@accordproject/template-engine 2.8.0 → 3.0.1
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/README.md +26 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +95 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +95 -22
- package/dist/index.mjs.map +1 -1
- package/dist/worker.js +6 -4
- package/dist/worker.js.map +1 -1
- package/package.json +18 -10
package/dist/index.mjs
CHANGED
|
@@ -101,6 +101,16 @@ function integerDrafter(value, format) {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
function isDuration(value) {
|
|
105
|
+
return value != null && typeof value === "object" && "amount" in value && "unit" in value && typeof value.amount === "number" && typeof value.unit === "string";
|
|
106
|
+
}
|
|
107
|
+
function durationDrafter(value) {
|
|
108
|
+
if (!isDuration(value)) {
|
|
109
|
+
return "0 unknown";
|
|
110
|
+
}
|
|
111
|
+
return `${value.amount} ${value.unit}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
104
114
|
function longDrafter(value, format) {
|
|
105
115
|
if (format) {
|
|
106
116
|
return draftIntegerFormat(value, format);
|
|
@@ -189,6 +199,10 @@ function getDrafter(typeName) {
|
|
|
189
199
|
return longDrafter;
|
|
190
200
|
case "org.accordproject.money@0.3.0.MonetaryAmount":
|
|
191
201
|
return monetaryAmountDrafter;
|
|
202
|
+
case "org.accordproject.time@0.3.0.Duration":
|
|
203
|
+
return durationDrafter;
|
|
204
|
+
case "org.accordproject.time@0.3.0.Period":
|
|
205
|
+
return durationDrafter;
|
|
192
206
|
case "String":
|
|
193
207
|
return stringDrafter;
|
|
194
208
|
default:
|
|
@@ -679,7 +693,7 @@ function getJsonPath(rootData, currentNode, paths) {
|
|
|
679
693
|
}
|
|
680
694
|
}
|
|
681
695
|
}
|
|
682
|
-
if (currentNode.name !== "this") {
|
|
696
|
+
if (currentNode.name !== "this" && currentNode.name !== "top") {
|
|
683
697
|
withPath.push(`['${currentNode.name}']`);
|
|
684
698
|
}
|
|
685
699
|
return withPath.length > 0 ? `$${withPath.join("")}` : "$";
|
|
@@ -869,8 +883,17 @@ async function generateAgreement(modelManager, clauseLibrary, templateMark, data
|
|
|
869
883
|
}
|
|
870
884
|
} else if (CONDITIONAL_DEFINITION_RE.test(nodeClass)) {
|
|
871
885
|
if (context.condition) {
|
|
872
|
-
const
|
|
873
|
-
|
|
886
|
+
const key = this.path.join("/");
|
|
887
|
+
const resultStr = userCodeResults[key];
|
|
888
|
+
if (resultStr === void 0 || resultStr === null) {
|
|
889
|
+
context.isTrue = false;
|
|
890
|
+
} else {
|
|
891
|
+
try {
|
|
892
|
+
context.isTrue = !!JSON.parse(resultStr);
|
|
893
|
+
} catch (err) {
|
|
894
|
+
throw new Error(`Invalid JSON boolean result for condition '${key}': ${String(err)}`);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
874
897
|
} else {
|
|
875
898
|
const path = getJsonPath(templateMark, context, this.path);
|
|
876
899
|
const variableValues = jp.query(data, path, 1);
|
|
@@ -891,16 +914,27 @@ async function generateAgreement(modelManager, clauseLibrary, templateMark, data
|
|
|
891
914
|
} else if (CLAUSE_DEFINITION_RE.test(nodeClass)) {
|
|
892
915
|
const path = getJsonPath(templateMark, context, this.path);
|
|
893
916
|
const variableValues = jp.query(data, path, 1);
|
|
894
|
-
if (context.
|
|
895
|
-
delete context.nodes;
|
|
896
|
-
stopHere = true;
|
|
897
|
-
} else if (context.condition) {
|
|
917
|
+
if (context.condition) {
|
|
898
918
|
checkCode(context.condition);
|
|
899
|
-
const
|
|
919
|
+
const key = this.path.join("/");
|
|
920
|
+
const resultStr = userCodeResults[key];
|
|
921
|
+
let result = false;
|
|
922
|
+
if (resultStr === void 0 || resultStr === null) {
|
|
923
|
+
result = false;
|
|
924
|
+
} else {
|
|
925
|
+
try {
|
|
926
|
+
result = !!JSON.parse(resultStr);
|
|
927
|
+
} catch (err) {
|
|
928
|
+
throw new Error(`Invalid JSON boolean result for condition '${key}': ${String(err)}`);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
900
931
|
if (!result) {
|
|
901
932
|
delete context.nodes;
|
|
902
933
|
stopHere = true;
|
|
903
934
|
}
|
|
935
|
+
} else if (context.name !== "top" && (variableValues.length === 0 || variableValues[0] === void 0 || variableValues[0] === null)) {
|
|
936
|
+
delete context.nodes;
|
|
937
|
+
stopHere = true;
|
|
904
938
|
}
|
|
905
939
|
delete context.condition;
|
|
906
940
|
delete context.functionName;
|
|
@@ -949,18 +983,53 @@ class TemplateMarkInterpreter {
|
|
|
949
983
|
* @throws {Error} if the templateMark document is invalid
|
|
950
984
|
*/
|
|
951
985
|
checkTypes(templateMark) {
|
|
952
|
-
const modelManager = new ModelManager(
|
|
986
|
+
const modelManager = new ModelManager();
|
|
953
987
|
modelManager.addCTOModel(ConcertoMetaModel.MODEL, "concertometamodel.cto");
|
|
954
988
|
modelManager.addCTOModel(CommonMarkModel.MODEL, "commonmark.cto");
|
|
955
989
|
modelManager.addCTOModel(TemplateMarkModel.MODEL, "templatemark.cto");
|
|
956
990
|
const factory = new Factory(modelManager);
|
|
957
|
-
const serializer = new Serializer(factory, modelManager);
|
|
991
|
+
const serializer = new Serializer(factory, modelManager, {});
|
|
958
992
|
try {
|
|
959
|
-
serializer.fromJSON(templateMark);
|
|
960
|
-
return templateMark;
|
|
993
|
+
serializer.fromJSON(templateMark, {});
|
|
961
994
|
} catch (err) {
|
|
962
995
|
throw new Error(`Generated invalid agreement: ${err}: ${JSON.stringify(templateMark, null, 2)}`);
|
|
963
996
|
}
|
|
997
|
+
const errors = [];
|
|
998
|
+
const templateClass = this.templateClass;
|
|
999
|
+
const guardBlockPaths = /* @__PURE__ */ new Map();
|
|
1000
|
+
traverse(templateMark).forEach(function(node) {
|
|
1001
|
+
if (!node || typeof node !== "object" || !node.$class) return;
|
|
1002
|
+
const currentPath = this.path.join("/");
|
|
1003
|
+
if (OPTIONAL_DEFINITION_RE.test(node.$class) || CONDITIONAL_DEFINITION_RE.test(node.$class) || WITH_DEFINITION_RE.test(node.$class)) {
|
|
1004
|
+
guardBlockPaths.set(node.name, currentPath);
|
|
1005
|
+
}
|
|
1006
|
+
if (VARIABLE_DEFINITION_RE.test(node.$class) || ENUM_VARIABLE_DEFINITION_RE.test(node.$class) || FORMATTED_VARIABLE_DEFINITION_RE.test(node.$class)) {
|
|
1007
|
+
const propName = node.name;
|
|
1008
|
+
if (propName && propName !== "this") {
|
|
1009
|
+
try {
|
|
1010
|
+
const property = templateClass.getProperty(propName);
|
|
1011
|
+
if (property && property.isOptional()) {
|
|
1012
|
+
const guardPath = guardBlockPaths.get(propName);
|
|
1013
|
+
const isGuarded = guardPath !== void 0 && (currentPath === guardPath || currentPath.startsWith(guardPath + "/"));
|
|
1014
|
+
if (!isGuarded) {
|
|
1015
|
+
errors.push({
|
|
1016
|
+
propertyName: propName,
|
|
1017
|
+
message: `Optional property '${propName}' is used without a guard. Wrap it in {{#optional ${propName}}}...{{/optional}} or {{#if ${propName}}}...{{/if}}.`
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
} catch {
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
if (errors.length > 0) {
|
|
1027
|
+
const errorMessage = `Optional properties used without guards: ${errors.map((e) => e.propertyName).join(", ")}`;
|
|
1028
|
+
const error = new Error(errorMessage);
|
|
1029
|
+
error.errors = errors;
|
|
1030
|
+
throw error;
|
|
1031
|
+
}
|
|
1032
|
+
return templateMark;
|
|
964
1033
|
}
|
|
965
1034
|
/**
|
|
966
1035
|
* Compiles the code nodes containing TS to code nodes containing JS.
|
|
@@ -993,22 +1062,22 @@ class TemplateMarkInterpreter {
|
|
|
993
1062
|
return compiler.compile(templateMark);
|
|
994
1063
|
}
|
|
995
1064
|
validateCiceroMark(ciceroMark) {
|
|
996
|
-
const modelManager = new ModelManager(
|
|
1065
|
+
const modelManager = new ModelManager();
|
|
997
1066
|
modelManager.addCTOModel(ConcertoMetaModel.MODEL, "concertometamodel.cto");
|
|
998
1067
|
modelManager.addCTOModel(CommonMarkModel.MODEL, "commonmark.cto");
|
|
999
1068
|
modelManager.addCTOModel(CiceroMarkModel.MODEL, "ciceromark.cto");
|
|
1000
1069
|
const factory = new Factory(modelManager);
|
|
1001
|
-
const serializer = new Serializer(factory, modelManager);
|
|
1070
|
+
const serializer = new Serializer(factory, modelManager, {});
|
|
1002
1071
|
try {
|
|
1003
|
-
return serializer.fromJSON(ciceroMark);
|
|
1072
|
+
return serializer.fromJSON(ciceroMark, {});
|
|
1004
1073
|
} catch (err) {
|
|
1005
1074
|
throw new Error(`Generated invalid agreement: ${err}: ${JSON.stringify(ciceroMark, null, 2)}`);
|
|
1006
1075
|
}
|
|
1007
1076
|
}
|
|
1008
1077
|
async generate(templateMark, data, options) {
|
|
1009
1078
|
const factory = new Factory(this.modelManager);
|
|
1010
|
-
const serializer = new Serializer(factory, this.modelManager);
|
|
1011
|
-
const templateData = serializer.fromJSON(data);
|
|
1079
|
+
const serializer = new Serializer(factory, this.modelManager, {});
|
|
1080
|
+
const templateData = serializer.fromJSON(data, {});
|
|
1012
1081
|
if (templateData.getFullyQualifiedType() !== this.templateClass.getFullyQualifiedName()) {
|
|
1013
1082
|
throw new Error(`Template data must be of type '${this.templateClass.getFullyQualifiedName()}'.`);
|
|
1014
1083
|
}
|
|
@@ -1057,7 +1126,7 @@ class TemplateArchiveProcessor {
|
|
|
1057
1126
|
* @param {object} request - the request to send to the template logic
|
|
1058
1127
|
* @param {object} state - the current state of the template
|
|
1059
1128
|
* @param {[string]} currentTime - the current time, defaults to now
|
|
1060
|
-
* @param {[number]} utcOffset - the UTC
|
|
1129
|
+
* @param {[number]} utcOffset - the UTC offset, defaults to zero
|
|
1061
1130
|
* @returns {Promise} the response and any events
|
|
1062
1131
|
*/
|
|
1063
1132
|
async trigger(data, request, state, currentTime, utcOffset) {
|
|
@@ -1077,6 +1146,8 @@ class TemplateArchiveProcessor {
|
|
|
1077
1146
|
const result = compiler.compile(code);
|
|
1078
1147
|
compiledCode[tsFile.getIdentifier()] = result;
|
|
1079
1148
|
}
|
|
1149
|
+
const resolvedTime = currentTime ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1150
|
+
const resolvedOffset = utcOffset ?? 0;
|
|
1080
1151
|
const evaluator = new JavaScriptEvaluator();
|
|
1081
1152
|
const evalResponse = await evaluator.evalDangerously({
|
|
1082
1153
|
templateLogic: true,
|
|
@@ -1085,7 +1156,7 @@ class TemplateArchiveProcessor {
|
|
|
1085
1156
|
code: compiledCode["logic/logic.ts"].code,
|
|
1086
1157
|
// TODO DCS - how to find the code to run?
|
|
1087
1158
|
argumentNames: ["data", "request", "state"],
|
|
1088
|
-
arguments: [data, request, state,
|
|
1159
|
+
arguments: [data, request, state, resolvedTime, resolvedOffset]
|
|
1089
1160
|
});
|
|
1090
1161
|
if (evalResponse.result) {
|
|
1091
1162
|
return evalResponse.result;
|
|
@@ -1099,8 +1170,8 @@ class TemplateArchiveProcessor {
|
|
|
1099
1170
|
/**
|
|
1100
1171
|
* Init the logic of a template
|
|
1101
1172
|
* @param {[string]} currentTime - the current time, defaults to now
|
|
1102
|
-
* @param {[number]} utcOffset - the UTC
|
|
1103
|
-
* @returns {Promise} the
|
|
1173
|
+
* @param {[number]} utcOffset - the UTC offset, defaults to zero
|
|
1174
|
+
* @returns {Promise<InitResponse>} the new state
|
|
1104
1175
|
*/
|
|
1105
1176
|
async init(data, currentTime, utcOffset) {
|
|
1106
1177
|
const logicManager = this.template.getLogicManager();
|
|
@@ -1119,6 +1190,8 @@ class TemplateArchiveProcessor {
|
|
|
1119
1190
|
const result = compiler.compile(code);
|
|
1120
1191
|
compiledCode[tsFile.getIdentifier()] = result;
|
|
1121
1192
|
}
|
|
1193
|
+
const resolvedTime = currentTime ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1194
|
+
const resolvedOffset = utcOffset ?? 0;
|
|
1122
1195
|
const evaluator = new JavaScriptEvaluator();
|
|
1123
1196
|
const evalResponse = await evaluator.evalDangerously({
|
|
1124
1197
|
templateLogic: true,
|
|
@@ -1127,7 +1200,7 @@ class TemplateArchiveProcessor {
|
|
|
1127
1200
|
code: compiledCode["logic/logic.ts"].code,
|
|
1128
1201
|
// TODO DCS - how to find the code to run?
|
|
1129
1202
|
argumentNames: ["data"],
|
|
1130
|
-
arguments: [data,
|
|
1203
|
+
arguments: [data, resolvedTime, resolvedOffset]
|
|
1131
1204
|
});
|
|
1132
1205
|
if (evalResponse.result) {
|
|
1133
1206
|
return evalResponse.result;
|