@accordproject/template-engine 2.9.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +20 -8
- package/dist/index.js +140 -85
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +140 -85
- package/dist/index.mjs.map +1 -1
- package/dist/worker.js +6 -4
- package/dist/worker.js.map +1 -1
- package/package.json +11 -10
package/dist/index.mjs
CHANGED
|
@@ -508,17 +508,31 @@ class JavaScriptEvaluator {
|
|
|
508
508
|
const end = (/* @__PURE__ */ new Date()).getTime();
|
|
509
509
|
resolve({ result, elapsed: end - start });
|
|
510
510
|
}).catch((err) => {
|
|
511
|
-
console.
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
511
|
+
console.error(err);
|
|
512
|
+
if (err instanceof Error) {
|
|
513
|
+
reject({
|
|
514
|
+
message: err.message,
|
|
515
|
+
stack: err.stack
|
|
516
|
+
});
|
|
517
|
+
} else {
|
|
518
|
+
reject({
|
|
519
|
+
message: String(err)
|
|
520
|
+
});
|
|
521
|
+
}
|
|
515
522
|
});
|
|
516
523
|
}
|
|
517
524
|
} catch (err) {
|
|
518
|
-
console.
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
525
|
+
console.error(err);
|
|
526
|
+
if (err instanceof Error) {
|
|
527
|
+
reject({
|
|
528
|
+
message: err.message,
|
|
529
|
+
stack: err.stack
|
|
530
|
+
});
|
|
531
|
+
} else {
|
|
532
|
+
reject({
|
|
533
|
+
message: String(err)
|
|
534
|
+
});
|
|
535
|
+
}
|
|
522
536
|
}
|
|
523
537
|
});
|
|
524
538
|
}
|
|
@@ -540,6 +554,7 @@ class JavaScriptEvaluator {
|
|
|
540
554
|
};
|
|
541
555
|
if (this.queue.length >= this.options.maxQueueDepth) {
|
|
542
556
|
reject({ maxQueueDepthExceeded: true, elapsed: 0 });
|
|
557
|
+
return;
|
|
543
558
|
}
|
|
544
559
|
this.queue.push(workItem);
|
|
545
560
|
this.processQueue(options);
|
|
@@ -580,7 +595,7 @@ class JavaScriptEvaluator {
|
|
|
580
595
|
}
|
|
581
596
|
const workerPath = this.getWorkerPath();
|
|
582
597
|
const worker = child_process.fork(workerPath, { timeout: options.timeout, env: {} });
|
|
583
|
-
if (
|
|
598
|
+
if (worker.pid === void 0 || worker.pid === null) {
|
|
584
599
|
throw new Error("Failed to fork child process");
|
|
585
600
|
}
|
|
586
601
|
this.workers.push(worker);
|
|
@@ -589,7 +604,18 @@ class JavaScriptEvaluator {
|
|
|
589
604
|
worker.on("error", (err) => {
|
|
590
605
|
this.workers = this.workers.filter((w) => w.pid !== worker.pid);
|
|
591
606
|
const end = (/* @__PURE__ */ new Date()).getTime();
|
|
592
|
-
|
|
607
|
+
if (err instanceof Error) {
|
|
608
|
+
reject({
|
|
609
|
+
message: err.message,
|
|
610
|
+
stack: err.stack,
|
|
611
|
+
elapsed: end - start
|
|
612
|
+
});
|
|
613
|
+
} else {
|
|
614
|
+
reject({
|
|
615
|
+
message: String(err),
|
|
616
|
+
elapsed: end - start
|
|
617
|
+
});
|
|
618
|
+
}
|
|
593
619
|
});
|
|
594
620
|
worker.on("message", (msg) => {
|
|
595
621
|
result = msg;
|
|
@@ -693,7 +719,7 @@ function getJsonPath(rootData, currentNode, paths) {
|
|
|
693
719
|
}
|
|
694
720
|
}
|
|
695
721
|
}
|
|
696
|
-
if (currentNode.name !== "this") {
|
|
722
|
+
if (currentNode.name !== "this" && currentNode.name !== "top") {
|
|
697
723
|
withPath.push(`['${currentNode.name}']`);
|
|
698
724
|
}
|
|
699
725
|
return withPath.length > 0 ? `$${withPath.join("")}` : "$";
|
|
@@ -772,13 +798,17 @@ async function generateRecursiveBlocks(modelManager, clauseLibrary, templateMark
|
|
|
772
798
|
if (!Array.isArray(arrayData)) {
|
|
773
799
|
throw new Error(`Values found for path '${path}' in data ${data} is not an array: ${arrayData}.`);
|
|
774
800
|
} else {
|
|
801
|
+
const firstChild = context.nodes[0];
|
|
802
|
+
const hasListWrapper = firstChild && firstChild.$class === `${CommonMarkModel.NAMESPACE}.List` && Array.isArray(firstChild.nodes) && firstChild.nodes.length > 0;
|
|
803
|
+
const itemTemplate = hasListWrapper ? firstChild.nodes[0] : firstChild;
|
|
775
804
|
const nodes = [];
|
|
776
805
|
for (let n2 = 0; n2 < arrayData.length; n2++) {
|
|
777
806
|
const arrayItem = arrayData[n2];
|
|
778
|
-
const subResult = await generateAgreement(modelManager, clauseLibrary,
|
|
807
|
+
const subResult = await generateAgreement(modelManager, clauseLibrary, itemTemplate, arrayItem, options);
|
|
808
|
+
const childNodes = hasListWrapper ? subResult.nodes ? subResult.nodes : [] : [subResult];
|
|
779
809
|
nodes.push({
|
|
780
810
|
$class: childNodeClass,
|
|
781
|
-
nodes:
|
|
811
|
+
nodes: childNodes
|
|
782
812
|
});
|
|
783
813
|
}
|
|
784
814
|
result[thisPath.join("/")] = nodes;
|
|
@@ -883,8 +913,17 @@ async function generateAgreement(modelManager, clauseLibrary, templateMark, data
|
|
|
883
913
|
}
|
|
884
914
|
} else if (CONDITIONAL_DEFINITION_RE.test(nodeClass)) {
|
|
885
915
|
if (context.condition) {
|
|
886
|
-
const
|
|
887
|
-
|
|
916
|
+
const key = this.path.join("/");
|
|
917
|
+
const resultStr = userCodeResults[key];
|
|
918
|
+
if (resultStr === void 0 || resultStr === null) {
|
|
919
|
+
context.isTrue = false;
|
|
920
|
+
} else {
|
|
921
|
+
try {
|
|
922
|
+
context.isTrue = !!JSON.parse(resultStr);
|
|
923
|
+
} catch (err) {
|
|
924
|
+
throw new Error(`Invalid JSON boolean result for condition '${key}': ${String(err)}`);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
888
927
|
} else {
|
|
889
928
|
const path = getJsonPath(templateMark, context, this.path);
|
|
890
929
|
const variableValues = jp.query(data, path, 1);
|
|
@@ -905,16 +944,27 @@ async function generateAgreement(modelManager, clauseLibrary, templateMark, data
|
|
|
905
944
|
} else if (CLAUSE_DEFINITION_RE.test(nodeClass)) {
|
|
906
945
|
const path = getJsonPath(templateMark, context, this.path);
|
|
907
946
|
const variableValues = jp.query(data, path, 1);
|
|
908
|
-
if (context.
|
|
909
|
-
delete context.nodes;
|
|
910
|
-
stopHere = true;
|
|
911
|
-
} else if (context.condition) {
|
|
947
|
+
if (context.condition) {
|
|
912
948
|
checkCode(context.condition);
|
|
913
|
-
const
|
|
949
|
+
const key = this.path.join("/");
|
|
950
|
+
const resultStr = userCodeResults[key];
|
|
951
|
+
let result = false;
|
|
952
|
+
if (resultStr === void 0 || resultStr === null) {
|
|
953
|
+
result = false;
|
|
954
|
+
} else {
|
|
955
|
+
try {
|
|
956
|
+
result = !!JSON.parse(resultStr);
|
|
957
|
+
} catch (err) {
|
|
958
|
+
throw new Error(`Invalid JSON boolean result for condition '${key}': ${String(err)}`);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
914
961
|
if (!result) {
|
|
915
962
|
delete context.nodes;
|
|
916
963
|
stopHere = true;
|
|
917
964
|
}
|
|
965
|
+
} else if (context.name !== "top" && (variableValues.length === 0 || variableValues[0] === void 0 || variableValues[0] === null)) {
|
|
966
|
+
delete context.nodes;
|
|
967
|
+
stopHere = true;
|
|
918
968
|
}
|
|
919
969
|
delete context.condition;
|
|
920
970
|
delete context.functionName;
|
|
@@ -963,14 +1013,14 @@ class TemplateMarkInterpreter {
|
|
|
963
1013
|
* @throws {Error} if the templateMark document is invalid
|
|
964
1014
|
*/
|
|
965
1015
|
checkTypes(templateMark) {
|
|
966
|
-
const modelManager = new ModelManager(
|
|
1016
|
+
const modelManager = new ModelManager();
|
|
967
1017
|
modelManager.addCTOModel(ConcertoMetaModel.MODEL, "concertometamodel.cto");
|
|
968
1018
|
modelManager.addCTOModel(CommonMarkModel.MODEL, "commonmark.cto");
|
|
969
1019
|
modelManager.addCTOModel(TemplateMarkModel.MODEL, "templatemark.cto");
|
|
970
1020
|
const factory = new Factory(modelManager);
|
|
971
|
-
const serializer = new Serializer(factory, modelManager);
|
|
1021
|
+
const serializer = new Serializer(factory, modelManager, {});
|
|
972
1022
|
try {
|
|
973
|
-
serializer.fromJSON(templateMark);
|
|
1023
|
+
serializer.fromJSON(templateMark, {});
|
|
974
1024
|
} catch (err) {
|
|
975
1025
|
throw new Error(`Generated invalid agreement: ${err}: ${JSON.stringify(templateMark, null, 2)}`);
|
|
976
1026
|
}
|
|
@@ -1042,22 +1092,22 @@ class TemplateMarkInterpreter {
|
|
|
1042
1092
|
return compiler.compile(templateMark);
|
|
1043
1093
|
}
|
|
1044
1094
|
validateCiceroMark(ciceroMark) {
|
|
1045
|
-
const modelManager = new ModelManager(
|
|
1095
|
+
const modelManager = new ModelManager();
|
|
1046
1096
|
modelManager.addCTOModel(ConcertoMetaModel.MODEL, "concertometamodel.cto");
|
|
1047
1097
|
modelManager.addCTOModel(CommonMarkModel.MODEL, "commonmark.cto");
|
|
1048
1098
|
modelManager.addCTOModel(CiceroMarkModel.MODEL, "ciceromark.cto");
|
|
1049
1099
|
const factory = new Factory(modelManager);
|
|
1050
|
-
const serializer = new Serializer(factory, modelManager);
|
|
1100
|
+
const serializer = new Serializer(factory, modelManager, {});
|
|
1051
1101
|
try {
|
|
1052
|
-
return serializer.fromJSON(ciceroMark);
|
|
1102
|
+
return serializer.fromJSON(ciceroMark, {});
|
|
1053
1103
|
} catch (err) {
|
|
1054
1104
|
throw new Error(`Generated invalid agreement: ${err}: ${JSON.stringify(ciceroMark, null, 2)}`);
|
|
1055
1105
|
}
|
|
1056
1106
|
}
|
|
1057
1107
|
async generate(templateMark, data, options) {
|
|
1058
1108
|
const factory = new Factory(this.modelManager);
|
|
1059
|
-
const serializer = new Serializer(factory, this.modelManager);
|
|
1060
|
-
const templateData = serializer.fromJSON(data);
|
|
1109
|
+
const serializer = new Serializer(factory, this.modelManager, {});
|
|
1110
|
+
const templateData = serializer.fromJSON(data, {});
|
|
1061
1111
|
if (templateData.getFullyQualifiedType() !== this.templateClass.getFullyQualifiedName()) {
|
|
1062
1112
|
throw new Error(`Template data must be of type '${this.templateClass.getFullyQualifiedName()}'.`);
|
|
1063
1113
|
}
|
|
@@ -1102,14 +1152,14 @@ class TemplateArchiveProcessor {
|
|
|
1102
1152
|
return result;
|
|
1103
1153
|
}
|
|
1104
1154
|
/**
|
|
1105
|
-
*
|
|
1106
|
-
* @param {
|
|
1107
|
-
* @
|
|
1108
|
-
* @param {[string]} currentTime - the current time, defaults to now
|
|
1109
|
-
* @param {[number]} utcOffset - the UTC offer, defaults to zero
|
|
1110
|
-
* @returns {Promise} the response and any events
|
|
1155
|
+
* Compile the logic of a template
|
|
1156
|
+
* @param {boolean} [enableCompiledLogicCache] - whether to cache the compiled logic for future use
|
|
1157
|
+
* @returns {Promise<Record<string, TwoSlashReturn>>} the compiled code for each typescript file
|
|
1111
1158
|
*/
|
|
1112
|
-
async
|
|
1159
|
+
async compileLogic(enableCompiledLogicCache = false) {
|
|
1160
|
+
if (enableCompiledLogicCache && this.compiledLogicCache) {
|
|
1161
|
+
return this.compiledLogicCache;
|
|
1162
|
+
}
|
|
1113
1163
|
const logicManager = this.template.getLogicManager();
|
|
1114
1164
|
if (logicManager.getLanguage() === "typescript") {
|
|
1115
1165
|
const compiledCode = {};
|
|
@@ -1126,65 +1176,70 @@ class TemplateArchiveProcessor {
|
|
|
1126
1176
|
const result = compiler.compile(code);
|
|
1127
1177
|
compiledCode[tsFile.getIdentifier()] = result;
|
|
1128
1178
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
templateLogic: true,
|
|
1132
|
-
verbose: false,
|
|
1133
|
-
functionName: "trigger",
|
|
1134
|
-
code: compiledCode["logic/logic.ts"].code,
|
|
1135
|
-
// TODO DCS - how to find the code to run?
|
|
1136
|
-
argumentNames: ["data", "request", "state"],
|
|
1137
|
-
arguments: [data, request, state, currentTime, utcOffset]
|
|
1138
|
-
});
|
|
1139
|
-
if (evalResponse.result) {
|
|
1140
|
-
return evalResponse.result;
|
|
1141
|
-
} else {
|
|
1142
|
-
throw new Error("Trigger failed with message: " + evalResponse.message);
|
|
1179
|
+
if (enableCompiledLogicCache) {
|
|
1180
|
+
this.compiledLogicCache = compiledCode;
|
|
1143
1181
|
}
|
|
1182
|
+
return compiledCode;
|
|
1144
1183
|
} else {
|
|
1145
1184
|
throw new Error("Only TypeScript is supported at this time");
|
|
1146
1185
|
}
|
|
1147
1186
|
}
|
|
1148
1187
|
/**
|
|
1149
|
-
*
|
|
1188
|
+
* Trigger the logic of a template.
|
|
1189
|
+
* @param {object} data - the data for the template
|
|
1190
|
+
* @param {object} request - the request to send to the template logic
|
|
1191
|
+
* @param {object} state - the current state of the template
|
|
1150
1192
|
* @param {[string]} currentTime - the current time, defaults to now
|
|
1151
|
-
* @param {[number]} utcOffset - the UTC
|
|
1152
|
-
* @
|
|
1193
|
+
* @param {[number]} utcOffset - the UTC offset, defaults to zero
|
|
1194
|
+
* @param {boolean} [enableCompiledLogicCache] - whether to use the compiled logic cache
|
|
1195
|
+
* @returns {Promise<TriggerResponse>} the response and any events
|
|
1153
1196
|
*/
|
|
1154
|
-
async
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
}
|
|
1171
|
-
const evaluator = new JavaScriptEvaluator();
|
|
1172
|
-
const evalResponse = await evaluator.evalDangerously({
|
|
1173
|
-
templateLogic: true,
|
|
1174
|
-
verbose: false,
|
|
1175
|
-
functionName: "init",
|
|
1176
|
-
code: compiledCode["logic/logic.ts"].code,
|
|
1177
|
-
// TODO DCS - how to find the code to run?
|
|
1178
|
-
argumentNames: ["data"],
|
|
1179
|
-
arguments: [data, currentTime, utcOffset]
|
|
1180
|
-
});
|
|
1181
|
-
if (evalResponse.result) {
|
|
1182
|
-
return evalResponse.result;
|
|
1183
|
-
} else {
|
|
1184
|
-
throw new Error("Init failed with message: " + evalResponse.message);
|
|
1185
|
-
}
|
|
1197
|
+
async trigger(data, request, state, currentTime, utcOffset, enableCompiledLogicCache) {
|
|
1198
|
+
const compiledCode = await this.compileLogic(enableCompiledLogicCache);
|
|
1199
|
+
const resolvedTime = currentTime ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1200
|
+
const resolvedOffset = utcOffset ?? 0;
|
|
1201
|
+
const evaluator = new JavaScriptEvaluator();
|
|
1202
|
+
const evalResponse = await evaluator.evalDangerously({
|
|
1203
|
+
templateLogic: true,
|
|
1204
|
+
verbose: false,
|
|
1205
|
+
functionName: "trigger",
|
|
1206
|
+
code: compiledCode["logic/logic.ts"].code,
|
|
1207
|
+
// TODO DCS - how to find the code to run?
|
|
1208
|
+
argumentNames: ["data", "request", "state"],
|
|
1209
|
+
arguments: [data, request, state, resolvedTime, resolvedOffset]
|
|
1210
|
+
});
|
|
1211
|
+
if (evalResponse.result) {
|
|
1212
|
+
return evalResponse.result;
|
|
1186
1213
|
} else {
|
|
1187
|
-
throw new Error("
|
|
1214
|
+
throw new Error("Trigger failed with message: " + evalResponse.message);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Init the logic of a template.
|
|
1219
|
+
* @param {object} data - the data for the template
|
|
1220
|
+
* @param {[string]} currentTime - the current time, defaults to now
|
|
1221
|
+
* @param {[number]} utcOffset - the UTC offset, defaults to zero
|
|
1222
|
+
* @param {boolean} [enableCompiledLogicCache] - whether to use the compiled logic cache
|
|
1223
|
+
* @returns {Promise<InitResponse>} the new state
|
|
1224
|
+
*/
|
|
1225
|
+
async init(data, currentTime, utcOffset, enableCompiledLogicCache) {
|
|
1226
|
+
const compiledCode = await this.compileLogic(enableCompiledLogicCache);
|
|
1227
|
+
const resolvedTime = currentTime ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1228
|
+
const resolvedOffset = utcOffset ?? 0;
|
|
1229
|
+
const evaluator = new JavaScriptEvaluator();
|
|
1230
|
+
const evalResponse = await evaluator.evalDangerously({
|
|
1231
|
+
templateLogic: true,
|
|
1232
|
+
verbose: false,
|
|
1233
|
+
functionName: "init",
|
|
1234
|
+
code: compiledCode["logic/logic.ts"].code,
|
|
1235
|
+
// TODO DCS - how to find the code to run?
|
|
1236
|
+
argumentNames: ["data"],
|
|
1237
|
+
arguments: [data, resolvedTime, resolvedOffset]
|
|
1238
|
+
});
|
|
1239
|
+
if (evalResponse.result) {
|
|
1240
|
+
return evalResponse.result;
|
|
1241
|
+
} else {
|
|
1242
|
+
throw new Error("Init failed with message: " + evalResponse.message);
|
|
1188
1243
|
}
|
|
1189
1244
|
}
|
|
1190
1245
|
}
|