UNPKG

@accordproject/cicero-core

Version:

Cicero Core - Implementation of Accord Protocol Template Specification

275 lines (252 loc) • 12.3 kB
/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } var fs = require('fs'); var fsPath = require('path'); var slash = require('slash'); var JSZip = require('jszip'); var xregexp = require('xregexp'); var languageTagRegex = require('ietf-language-tag-regex'); var DefaultArchiveLoader = require('./loaders/defaultarchiveloader'); var Logger = require('@accordproject/ergo-compiler').Logger; var FileLoader = require('@accordproject/ergo-compiler').FileLoader; // Matches 'sample.md' or 'sample_TAG.md' where TAG is an IETF language tag (BCP 47) var IETF_REGEXP = languageTagRegex({ exact: false }).toString().slice(1, -2); var SAMPLE_FILE_REGEXP = xregexp('text[/\\\\]sample(_(' + IETF_REGEXP + '))?.md$'); /** * A utility class to create templates from data sources. * @class * @private * @abstract */ class TemplateLoader extends FileLoader { /** * Create a template from an archive. * @param {*} Template - the type to construct * @param {Buffer} buffer - the buffer to a Cicero Template Archive (cta) file * @param {object} options - additional options * @return {Promise<Template>} a Promise to the template */ static fromArchive(Template, buffer, options) { return _asyncToGenerator(function* () { var method = 'fromArchive'; var zip = yield JSZip.loadAsync(buffer); // const allFiles = await TemplateLoader.loadZipFilesContents(zip, /.*/); // console.log(allFiles); var ctoModelFiles = []; var ctoModelFileNames = []; var sampleTextFiles = {}; var readmeContents = yield TemplateLoader.loadZipFileContents(zip, 'README.md'); var logo = yield TemplateLoader.loadZipFileBuffer(zip, 'logo.png'); var sampleFiles = yield TemplateLoader.loadZipFilesContents(zip, SAMPLE_FILE_REGEXP); sampleFiles.forEach( /*#__PURE__*/function () { var _ref = _asyncToGenerator(function* (sampleFile) { var matches = sampleFile.name.match(SAMPLE_FILE_REGEXP); var locale = 'default'; // Locale match found if (matches !== null && matches[2]) { locale = matches[2]; } sampleTextFiles[locale] = sampleFile.contents; }); return function (_x) { return _ref.apply(this, arguments); }; }()); var requestContents = yield TemplateLoader.loadZipFileContents(zip, 'request.json', true); var packageJsonObject = yield TemplateLoader.loadZipFileContents(zip, 'package.json', true, true); var grammar = yield TemplateLoader.loadZipFileContents(zip, 'text/grammar.tem.md', false, false); var authorSignature = yield TemplateLoader.loadZipFileContents(zip, 'signature.json', true, false); Logger.debug(method, 'Looking for model files'); var ctoFiles = yield TemplateLoader.loadZipFilesContents(zip, /model[/\\].*\.cto$/); ctoFiles.forEach( /*#__PURE__*/function () { var _ref2 = _asyncToGenerator(function* (file) { ctoModelFileNames.push(file.name); ctoModelFiles.push(file.contents); }); return function (_x2) { return _ref2.apply(this, arguments); }; }()); // create the template var template = new (Function.prototype.bind.call(Template, null, packageJsonObject, readmeContents, sampleTextFiles, requestContents, logo, options, authorSignature))(); // add model files Logger.debug(method, 'Adding model files to model manager'); template.getModelManager().addAPModelFiles(ctoModelFiles, ctoModelFileNames, true); // Archives can always be loaded offline Logger.debug(method, 'Setting grammar'); if (!grammar) { throw new Error('A template must contain a grammar.tem.md file.'); } else { var templateKind = template.getMetadata().getTemplateType() !== 0 ? 'clause' : 'contract'; template.parserManager.setTemplate(grammar); template.parserManager.setTemplateKind(templateKind); template.parserManager.buildParser(); } // load and add the ergo files if (template.getMetadata().getRuntime() === 'ergo') { Logger.debug(method, 'Adding Ergo files to script manager'); var scriptFiles = yield TemplateLoader.loadZipFilesContents(zip, /logic[/\\].*\.ergo$/); scriptFiles.forEach(function (obj) { template.getLogicManager().addLogicFile(obj.contents, obj.name); }); } else { // load and add compiled JS files - we assume all runtimes are JS based (review!) Logger.debug(method, 'Adding JS files to script manager'); var _scriptFiles = yield TemplateLoader.loadZipFilesContents(zip, /logic[/\\].*\.js$/); _scriptFiles.forEach(function (obj) { template.getLogicManager().addLogicFile(obj.contents, obj.name); }); } TemplateLoader.registerFormulas(template.parserManager, template.getLogicManager()); // check the integrity of the model and logic of the template authorSignature ? template.validate({ verifySignature: true }) : template.validate(); return template; // Returns template })(); } /** * Create a template from an URL. * @param {*} Template - the type to construct * @param {String} url - the URL to a Cicero Template Archive (cta) file * @param {object} options - additional options * @return {Promise} a Promise to the template */ static fromUrl(Template, url, options) { return _asyncToGenerator(function* () { var loader = new DefaultArchiveLoader(); var buffer = yield loader.load(url, options); return TemplateLoader.fromArchive(Template, buffer, options); })(); } /** * Builds a Template from the contents of a directory. * The directory must include a package.json in the root (used to specify * the name, version and description of the template). * * @param {*} Template - the type to construct * @param {String} path to a local directory * @param {Object} [options] - an optional set of options to configure the instance. * @return {Promise<Template>} a Promise to the instantiated template */ static fromDirectory(Template, path) { var _arguments = arguments; return _asyncToGenerator(function* () { var options = _arguments.length > 2 && _arguments[2] !== undefined ? _arguments[2] : {}; var method = 'fromDirectory'; // grab the README.md var readmeContents = yield TemplateLoader.loadFileContents(path, 'README.md'); // grab the logo.png var logo = yield TemplateLoader.loadFileBuffer(path, 'logo.png'); // grab the request.json var requestJsonObject = yield TemplateLoader.loadFileContents(path, 'request.json', true); // grab the package.json var packageJsonObject = yield TemplateLoader.loadFileContents(path, 'package.json', true, true); // grab the sample files Logger.debug(method, 'Looking for sample files'); var sampleFiles = yield TemplateLoader.loadFilesContents(path, SAMPLE_FILE_REGEXP); var sampleTextFiles = {}; sampleFiles.forEach(file => { var matches = file.name.match(SAMPLE_FILE_REGEXP); var locale = 'default'; // Match found if (matches !== null && matches[2]) { locale = matches[2]; } Logger.debug(method, 'Using sample file locale', locale); sampleTextFiles[locale] = file.contents; }); // grab the signature.json var authorSignature = yield TemplateLoader.loadFileContents(path, 'signature.json', true, false); // create the template var template = new (Function.prototype.bind.call(Template, null, packageJsonObject, readmeContents, sampleTextFiles, requestJsonObject, logo, options, authorSignature))(); var modelFiles = []; var modelFileNames = []; var ctoFiles = yield TemplateLoader.loadFilesContents(path, /model[/\\].*\.cto$/); ctoFiles.forEach(file => { modelFileNames.push(slash(file.name)); modelFiles.push(file.contents); }); var externalModelFiles = yield template.getModelManager().addAPModelFiles(modelFiles, modelFileNames, options && options.offline); if (!options || !options.offline) { externalModelFiles.forEach(function (file) { fs.writeFileSync(path + '/model/' + file.name, file.content); }); } // load and add the template var grammar = yield TemplateLoader.loadFileContents(path, 'text/grammar.tem.md', false, false); if (!grammar) { throw new Error('A template must either contain a grammar.tem.md file.'); } else { var templateKind = template.getMetadata().getTemplateType() !== 0 ? 'clause' : 'contract'; template.parserManager.setTemplate(grammar); template.parserManager.setTemplateKind(templateKind); template.parserManager.buildParser(); Logger.debug(method, 'Loaded grammar.tem.md', grammar); } Logger.debug(method, 'Loaded grammar.tem.md'); // load and add the ergo files if (template.getMetadata().getRuntime() === 'ergo') { var ergoFiles = yield TemplateLoader.loadFilesContents(path, /logic[/\\].*\.ergo$/); ergoFiles.forEach(file => { var resolvedPath = slash(fsPath.resolve(path)); var resolvedFilePath = slash(fsPath.resolve(file.name)); var truncatedPath = resolvedFilePath.replace(resolvedPath + '/', ''); template.getLogicManager().addLogicFile(file.contents, truncatedPath); }); } else { // load and add compiled JS files - we assume all runtimes are JS based (review!) var jsFiles = yield TemplateLoader.loadFilesContents(path, /logic[/\\].*\.js$/); jsFiles.forEach(file => { var resolvedPath = slash(fsPath.resolve(path)); var resolvedFilePath = slash(fsPath.resolve(file.name)); var truncatedPath = resolvedFilePath.replace(resolvedPath + '/', ''); template.getLogicManager().addLogicFile(file.contents, truncatedPath); }); } TemplateLoader.registerFormulas(template.parserManager, template.getLogicManager()); // check the template authorSignature ? template.validate({ verifySignature: true }) : template.validate(); return template; })(); } /** * Prepare the text for parsing (normalizes new lines, etc) * @param {*} parserManager - the parser manager * @param {*} logicManager - the logic manager */ static registerFormulas(parserManager, logicManager) { var formulas = parserManager.getFormulas(); formulas.forEach(x => { logicManager.addTemplateFile(x.code, x.name); }); } /** * Prepare the text for parsing (normalizes new lines, etc) * @param {string} input - the text for the clause * @return {string} - the normalized text for the clause */ static normalizeText(input) { // we replace all \r and \n with \n var text = input.replace(/\r/gm, ''); return text; } } module.exports = TemplateLoader;