@shipfox/expression 1.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.
Files changed (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +117 -0
  3. package/dist/evaluator/errors.d.ts +8 -0
  4. package/dist/evaluator/errors.d.ts.map +1 -0
  5. package/dist/evaluator/errors.js +13 -0
  6. package/dist/evaluator/errors.js.map +1 -0
  7. package/dist/evaluator/evaluate-workflow-expression.d.ts +11 -0
  8. package/dist/evaluator/evaluate-workflow-expression.d.ts.map +1 -0
  9. package/dist/evaluator/evaluate-workflow-expression.js +30 -0
  10. package/dist/evaluator/evaluate-workflow-expression.js.map +1 -0
  11. package/dist/evaluator/index.d.ts +3 -0
  12. package/dist/evaluator/index.d.ts.map +1 -0
  13. package/dist/evaluator/index.js +4 -0
  14. package/dist/evaluator/index.js.map +1 -0
  15. package/dist/expression/create-workflow-expression.d.ts +7 -0
  16. package/dist/expression/create-workflow-expression.d.ts.map +1 -0
  17. package/dist/expression/create-workflow-expression.js +156 -0
  18. package/dist/expression/create-workflow-expression.js.map +1 -0
  19. package/dist/expression/errors.d.ts +12 -0
  20. package/dist/expression/errors.d.ts.map +1 -0
  21. package/dist/expression/errors.js +13 -0
  22. package/dist/expression/errors.js.map +1 -0
  23. package/dist/expression/index.d.ts +4 -0
  24. package/dist/expression/index.d.ts.map +1 -0
  25. package/dist/expression/index.js +4 -0
  26. package/dist/expression/index.js.map +1 -0
  27. package/dist/expression/workflow-expression.d.ts +33 -0
  28. package/dist/expression/workflow-expression.d.ts.map +1 -0
  29. package/dist/expression/workflow-expression.js +3 -0
  30. package/dist/expression/workflow-expression.js.map +1 -0
  31. package/dist/index.d.ts +27 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +25 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/outputs/index.d.ts +2 -0
  36. package/dist/outputs/index.d.ts.map +1 -0
  37. package/dist/outputs/index.js +3 -0
  38. package/dist/outputs/index.js.map +1 -0
  39. package/dist/outputs/output-declarations.d.ts +39 -0
  40. package/dist/outputs/output-declarations.d.ts.map +1 -0
  41. package/dist/outputs/output-declarations.js +295 -0
  42. package/dist/outputs/output-declarations.js.map +1 -0
  43. package/dist/plan/bare-reference.d.ts +2 -0
  44. package/dist/plan/bare-reference.d.ts.map +1 -0
  45. package/dist/plan/bare-reference.js +23 -0
  46. package/dist/plan/bare-reference.js.map +1 -0
  47. package/dist/plan/context-key-access.d.ts +25 -0
  48. package/dist/plan/context-key-access.d.ts.map +1 -0
  49. package/dist/plan/context-key-access.js +249 -0
  50. package/dist/plan/context-key-access.js.map +1 -0
  51. package/dist/plan/evaluate-planned-predicate.d.ts +14 -0
  52. package/dist/plan/evaluate-planned-predicate.d.ts.map +1 -0
  53. package/dist/plan/evaluate-planned-predicate.js +31 -0
  54. package/dist/plan/evaluate-planned-predicate.js.map +1 -0
  55. package/dist/plan/evaluation-trace.d.ts +44 -0
  56. package/dist/plan/evaluation-trace.d.ts.map +1 -0
  57. package/dist/plan/evaluation-trace.js +95 -0
  58. package/dist/plan/evaluation-trace.js.map +1 -0
  59. package/dist/plan/extract-exact-context-roots.d.ts +2 -0
  60. package/dist/plan/extract-exact-context-roots.d.ts.map +1 -0
  61. package/dist/plan/extract-exact-context-roots.js +114 -0
  62. package/dist/plan/extract-exact-context-roots.js.map +1 -0
  63. package/dist/plan/fill.d.ts +3 -0
  64. package/dist/plan/fill.d.ts.map +1 -0
  65. package/dist/plan/fill.js +8 -0
  66. package/dist/plan/fill.js.map +1 -0
  67. package/dist/plan/freeze-run-command.d.ts +17 -0
  68. package/dist/plan/freeze-run-command.d.ts.map +1 -0
  69. package/dist/plan/freeze-run-command.js +33 -0
  70. package/dist/plan/freeze-run-command.js.map +1 -0
  71. package/dist/plan/freeze.d.ts +48 -0
  72. package/dist/plan/freeze.d.ts.map +1 -0
  73. package/dist/plan/freeze.js +148 -0
  74. package/dist/plan/freeze.js.map +1 -0
  75. package/dist/plan/plan-field.d.ts +26 -0
  76. package/dist/plan/plan-field.d.ts.map +1 -0
  77. package/dist/plan/plan-field.js +63 -0
  78. package/dist/plan/plan-field.js.map +1 -0
  79. package/dist/plan/predicate-shape.d.ts +2 -0
  80. package/dist/plan/predicate-shape.d.ts.map +1 -0
  81. package/dist/plan/predicate-shape.js +45 -0
  82. package/dist/plan/predicate-shape.js.map +1 -0
  83. package/dist/plan/resolved-field.d.ts +17 -0
  84. package/dist/plan/resolved-field.d.ts.map +1 -0
  85. package/dist/plan/resolved-field.js +3 -0
  86. package/dist/plan/resolved-field.js.map +1 -0
  87. package/dist/plan/route-expression.d.ts +9 -0
  88. package/dist/plan/route-expression.d.ts.map +1 -0
  89. package/dist/plan/route-expression.js +46 -0
  90. package/dist/plan/route-expression.js.map +1 -0
  91. package/dist/plan/validate-server-evaluable.d.ts +14 -0
  92. package/dist/plan/validate-server-evaluable.d.ts.map +1 -0
  93. package/dist/plan/validate-server-evaluable.js +20 -0
  94. package/dist/plan/validate-server-evaluable.js.map +1 -0
  95. package/dist/resolver/coerce-workflow-value-to-string.d.ts +2 -0
  96. package/dist/resolver/coerce-workflow-value-to-string.d.ts.map +1 -0
  97. package/dist/resolver/coerce-workflow-value-to-string.js +16 -0
  98. package/dist/resolver/coerce-workflow-value-to-string.js.map +1 -0
  99. package/dist/resolver/errors.d.ts +10 -0
  100. package/dist/resolver/errors.d.ts.map +1 -0
  101. package/dist/resolver/errors.js +12 -0
  102. package/dist/resolver/errors.js.map +1 -0
  103. package/dist/run/hoist-run-command.d.ts +33 -0
  104. package/dist/run/hoist-run-command.d.ts.map +1 -0
  105. package/dist/run/hoist-run-command.js +79 -0
  106. package/dist/run/hoist-run-command.js.map +1 -0
  107. package/dist/run/shell-quoting.d.ts +24 -0
  108. package/dist/run/shell-quoting.d.ts.map +1 -0
  109. package/dist/run/shell-quoting.js +364 -0
  110. package/dist/run/shell-quoting.js.map +1 -0
  111. package/dist/template/errors.d.ts +14 -0
  112. package/dist/template/errors.d.ts.map +1 -0
  113. package/dist/template/errors.js +14 -0
  114. package/dist/template/errors.js.map +1 -0
  115. package/dist/template/extract-cel-context-roots.d.ts +2 -0
  116. package/dist/template/extract-cel-context-roots.d.ts.map +1 -0
  117. package/dist/template/extract-cel-context-roots.js +110 -0
  118. package/dist/template/extract-cel-context-roots.js.map +1 -0
  119. package/dist/template/extract-cel-untrusted-path-accesses.d.ts +5 -0
  120. package/dist/template/extract-cel-untrusted-path-accesses.d.ts.map +1 -0
  121. package/dist/template/extract-cel-untrusted-path-accesses.js +140 -0
  122. package/dist/template/extract-cel-untrusted-path-accesses.js.map +1 -0
  123. package/dist/template/parse-workflow-template.d.ts +3 -0
  124. package/dist/template/parse-workflow-template.d.ts.map +1 -0
  125. package/dist/template/parse-workflow-template.js +111 -0
  126. package/dist/template/parse-workflow-template.js.map +1 -0
  127. package/dist/template/scan-cel-token.d.ts +3 -0
  128. package/dist/template/scan-cel-token.d.ts.map +1 -0
  129. package/dist/template/scan-cel-token.js +55 -0
  130. package/dist/template/scan-cel-token.js.map +1 -0
  131. package/dist/template/template-segment.d.ts +12 -0
  132. package/dist/template/template-segment.d.ts.map +1 -0
  133. package/dist/template/template-segment.js +3 -0
  134. package/dist/template/template-segment.js.map +1 -0
  135. package/dist/tsconfig.test.tsbuildinfo +1 -0
  136. package/dist/workflow-context/workflow-context.d.ts +458 -0
  137. package/dist/workflow-context/workflow-context.d.ts.map +1 -0
  138. package/dist/workflow-context/workflow-context.js +664 -0
  139. package/dist/workflow-context/workflow-context.js.map +1 -0
  140. package/package.json +49 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/template/errors.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gCAAgC,8BAA8B,CAAC;AAE5E,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,QAAQ,CAAC,IAAI,+BAAoC;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,MAAM,EAAE;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAOtF"}
@@ -0,0 +1,14 @@
1
+ export const invalidWorkflowTemplateErrorCode = 'invalid-workflow-template';
2
+ export class InvalidWorkflowTemplateError extends Error {
3
+ constructor(params){
4
+ super('Invalid workflow template', {
5
+ cause: params.cause
6
+ }), this.code = invalidWorkflowTemplateErrorCode;
7
+ this.name = 'InvalidWorkflowTemplateError';
8
+ this.source = params.source;
9
+ this.reason = params.reason;
10
+ this.offset = params.offset;
11
+ }
12
+ }
13
+
14
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/template/errors.ts"],"sourcesContent":["export const invalidWorkflowTemplateErrorCode = 'invalid-workflow-template';\n\nexport class InvalidWorkflowTemplateError extends Error {\n readonly code = invalidWorkflowTemplateErrorCode;\n readonly source: string;\n readonly reason: string;\n readonly offset: number;\n\n constructor(params: {source: string; reason: string; offset: number; cause?: unknown}) {\n super('Invalid workflow template', {cause: params.cause});\n this.name = 'InvalidWorkflowTemplateError';\n this.source = params.source;\n this.reason = params.reason;\n this.offset = params.offset;\n }\n}\n"],"names":["invalidWorkflowTemplateErrorCode","InvalidWorkflowTemplateError","Error","params","cause","code","name","source","reason","offset"],"mappings":"AAAA,OAAO,MAAMA,mCAAmC,4BAA4B;AAE5E,OAAO,MAAMC,qCAAqCC;IAMhD,YAAYC,MAAyE,CAAE;QACrF,KAAK,CAAC,6BAA6B;YAACC,OAAOD,OAAOC,KAAK;QAAA,SANhDC,OAAOL;QAOd,IAAI,CAACM,IAAI,GAAG;QACZ,IAAI,CAACC,MAAM,GAAGJ,OAAOI,MAAM;QAC3B,IAAI,CAACC,MAAM,GAAGL,OAAOK,MAAM;QAC3B,IAAI,CAACC,MAAM,GAAGN,OAAOM,MAAM;IAC7B;AACF"}
@@ -0,0 +1,2 @@
1
+ export declare function extractCelContextRoots(source: string): string[];
2
+ //# sourceMappingURL=extract-cel-context-roots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-cel-context-roots.d.ts","sourceRoot":"","sources":["../../src/template/extract-cel-context-roots.ts"],"names":[],"mappings":"AAmBA,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAI/D"}
@@ -0,0 +1,110 @@
1
+ import { parse as parseCel } from '@marcbachmann/cel-js';
2
+ const binaryOperators = new Set([
3
+ '!=',
4
+ '==',
5
+ 'in',
6
+ '+',
7
+ '-',
8
+ '*',
9
+ '/',
10
+ '%',
11
+ '<',
12
+ '<=',
13
+ '>',
14
+ '>='
15
+ ]);
16
+ const comprehensionMethods = new Set([
17
+ 'all',
18
+ 'exists',
19
+ 'exists_one',
20
+ 'filter',
21
+ 'map'
22
+ ]);
23
+ export function extractCelContextRoots(source) {
24
+ const contextRoots = new Set();
25
+ collectContextRoots(parseCel(source).ast, contextRoots, new Set());
26
+ return [
27
+ ...contextRoots
28
+ ].sort();
29
+ }
30
+ function collectContextRoots(node, contextRoots, scopedIdentifiers) {
31
+ if (binaryOperators.has(node.op) || node.op === '||' || node.op === '&&') {
32
+ collectBinaryContextRoots(node.args, contextRoots, scopedIdentifiers);
33
+ return;
34
+ }
35
+ switch(node.op){
36
+ case 'id':
37
+ if (!scopedIdentifiers.has(node.args)) contextRoots.add(node.args);
38
+ return;
39
+ case 'value':
40
+ return;
41
+ case '.':
42
+ case '.?':
43
+ collectContextRoots(node.args[0], contextRoots, scopedIdentifiers);
44
+ return;
45
+ case '[]':
46
+ case '[?]':
47
+ collectBinaryContextRoots(node.args, contextRoots, scopedIdentifiers);
48
+ return;
49
+ case 'call':
50
+ for (const argument of node.args[1]){
51
+ collectContextRoots(argument, contextRoots, scopedIdentifiers);
52
+ }
53
+ return;
54
+ case 'rcall':
55
+ {
56
+ const [method, receiver, args] = node.args;
57
+ collectContextRoots(receiver, contextRoots, scopedIdentifiers);
58
+ const binding = bindComprehensionAlias(method, args, scopedIdentifiers);
59
+ for (const argument of args.slice(binding.skipArgs)){
60
+ collectContextRoots(argument, contextRoots, binding.scopedIdentifiers);
61
+ }
62
+ return;
63
+ }
64
+ case 'list':
65
+ for (const element of node.args){
66
+ collectContextRoots(element, contextRoots, scopedIdentifiers);
67
+ }
68
+ return;
69
+ case 'map':
70
+ for (const [key, value] of node.args){
71
+ if (key.op !== 'id') collectContextRoots(key, contextRoots, scopedIdentifiers);
72
+ collectContextRoots(value, contextRoots, scopedIdentifiers);
73
+ }
74
+ return;
75
+ case '?:':
76
+ collectContextRoots(node.args[0], contextRoots, scopedIdentifiers);
77
+ collectContextRoots(node.args[1], contextRoots, scopedIdentifiers);
78
+ collectContextRoots(node.args[2], contextRoots, scopedIdentifiers);
79
+ return;
80
+ case '!_':
81
+ case '-_':
82
+ collectContextRoots(node.args, contextRoots, scopedIdentifiers);
83
+ return;
84
+ }
85
+ throw new Error(`Unsupported CEL AST operator: ${node.op}`);
86
+ }
87
+ function collectBinaryContextRoots([left, right], contextRoots, scopedIdentifiers) {
88
+ collectContextRoots(left, contextRoots, scopedIdentifiers);
89
+ collectContextRoots(right, contextRoots, scopedIdentifiers);
90
+ }
91
+ function bindComprehensionAlias(method, args, scopedIdentifiers) {
92
+ if (!comprehensionMethods.has(method)) return {
93
+ scopedIdentifiers,
94
+ skipArgs: 0
95
+ };
96
+ const [alias] = args;
97
+ if (alias?.op !== 'id') return {
98
+ scopedIdentifiers,
99
+ skipArgs: 0
100
+ };
101
+ return {
102
+ scopedIdentifiers: new Set([
103
+ ...scopedIdentifiers,
104
+ alias.args
105
+ ]),
106
+ skipArgs: 1
107
+ };
108
+ }
109
+
110
+ //# sourceMappingURL=extract-cel-context-roots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/template/extract-cel-context-roots.ts"],"sourcesContent":["import {type ASTNode, type BinaryOperator, parse as parseCel} from '@marcbachmann/cel-js';\n\nconst binaryOperators = new Set<BinaryOperator>([\n '!=',\n '==',\n 'in',\n '+',\n '-',\n '*',\n '/',\n '%',\n '<',\n '<=',\n '>',\n '>=',\n]);\n\nconst comprehensionMethods = new Set(['all', 'exists', 'exists_one', 'filter', 'map']);\n\nexport function extractCelContextRoots(source: string): string[] {\n const contextRoots = new Set<string>();\n collectContextRoots(parseCel(source).ast, contextRoots, new Set());\n return [...contextRoots].sort();\n}\n\nfunction collectContextRoots(\n node: ASTNode,\n contextRoots: Set<string>,\n scopedIdentifiers: ReadonlySet<string>,\n): void {\n if (binaryOperators.has(node.op as BinaryOperator) || node.op === '||' || node.op === '&&') {\n collectBinaryContextRoots(node.args as [ASTNode, ASTNode], contextRoots, scopedIdentifiers);\n return;\n }\n\n switch (node.op) {\n case 'id':\n if (!scopedIdentifiers.has(node.args)) contextRoots.add(node.args);\n return;\n case 'value':\n return;\n case '.':\n case '.?':\n collectContextRoots(node.args[0], contextRoots, scopedIdentifiers);\n return;\n case '[]':\n case '[?]':\n collectBinaryContextRoots(node.args, contextRoots, scopedIdentifiers);\n return;\n case 'call':\n for (const argument of node.args[1]) {\n collectContextRoots(argument, contextRoots, scopedIdentifiers);\n }\n return;\n case 'rcall': {\n const [method, receiver, args] = node.args as [string, ASTNode, ASTNode[]];\n collectContextRoots(receiver, contextRoots, scopedIdentifiers);\n const binding = bindComprehensionAlias(method, args, scopedIdentifiers);\n for (const argument of args.slice(binding.skipArgs)) {\n collectContextRoots(argument, contextRoots, binding.scopedIdentifiers);\n }\n return;\n }\n case 'list':\n for (const element of node.args) {\n collectContextRoots(element, contextRoots, scopedIdentifiers);\n }\n return;\n case 'map':\n for (const [key, value] of node.args) {\n if (key.op !== 'id') collectContextRoots(key, contextRoots, scopedIdentifiers);\n collectContextRoots(value, contextRoots, scopedIdentifiers);\n }\n return;\n case '?:':\n collectContextRoots(node.args[0], contextRoots, scopedIdentifiers);\n collectContextRoots(node.args[1], contextRoots, scopedIdentifiers);\n collectContextRoots(node.args[2], contextRoots, scopedIdentifiers);\n return;\n case '!_':\n case '-_':\n collectContextRoots(node.args, contextRoots, scopedIdentifiers);\n return;\n }\n\n throw new Error(`Unsupported CEL AST operator: ${(node as {op: string}).op}`);\n}\n\nfunction collectBinaryContextRoots(\n [left, right]: [ASTNode, ASTNode],\n contextRoots: Set<string>,\n scopedIdentifiers: ReadonlySet<string>,\n): void {\n collectContextRoots(left, contextRoots, scopedIdentifiers);\n collectContextRoots(right, contextRoots, scopedIdentifiers);\n}\n\nfunction bindComprehensionAlias(\n method: string,\n args: readonly ASTNode[],\n scopedIdentifiers: ReadonlySet<string>,\n): {readonly scopedIdentifiers: ReadonlySet<string>; readonly skipArgs: number} {\n if (!comprehensionMethods.has(method)) return {scopedIdentifiers, skipArgs: 0};\n\n const [alias] = args;\n if (alias?.op !== 'id') return {scopedIdentifiers, skipArgs: 0};\n\n return {\n scopedIdentifiers: new Set([...scopedIdentifiers, alias.args]),\n skipArgs: 1,\n };\n}\n"],"names":["parse","parseCel","binaryOperators","Set","comprehensionMethods","extractCelContextRoots","source","contextRoots","collectContextRoots","ast","sort","node","scopedIdentifiers","has","op","collectBinaryContextRoots","args","add","argument","method","receiver","binding","bindComprehensionAlias","slice","skipArgs","element","key","value","Error","left","right","alias"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function extractCelUntrustedPathAccesses(params: {
2
+ source: string;
3
+ untrustedPathsByRoot: ReadonlyMap<string, readonly string[]>;
4
+ }): string[];
5
+ //# sourceMappingURL=extract-cel-untrusted-path-accesses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-cel-untrusted-path-accesses.d.ts","sourceRoot":"","sources":["../../src/template/extract-cel-untrusted-path-accesses.ts"],"names":[],"mappings":"AAiBA,wBAAgB,+BAA+B,CAAC,MAAM,EAAE;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;CAC9D,GAAG,MAAM,EAAE,CASX"}
@@ -0,0 +1,140 @@
1
+ import { parse as parseCel } from '@marcbachmann/cel-js';
2
+ const binaryOperators = new Set([
3
+ '!=',
4
+ '==',
5
+ 'in',
6
+ '+',
7
+ '-',
8
+ '*',
9
+ '/',
10
+ '%',
11
+ '<',
12
+ '<=',
13
+ '>',
14
+ '>='
15
+ ]);
16
+ export function extractCelUntrustedPathAccesses(params) {
17
+ const roots = new Set();
18
+ collectUntrustedPathAccesses(parseCel(params.source).ast, params.untrustedPathsByRoot, roots, new Map());
19
+ return [
20
+ ...roots
21
+ ].sort();
22
+ }
23
+ function collectUntrustedPathAccesses(node, untrustedPathsByRoot, roots, aliases) {
24
+ if (binaryOperators.has(node.op) || node.op === '||' || node.op === '&&') {
25
+ collectBinaryUntrustedPathAccesses(node.args, untrustedPathsByRoot, roots, aliases);
26
+ return;
27
+ }
28
+ switch(node.op){
29
+ case 'id':
30
+ case 'value':
31
+ return;
32
+ case '.':
33
+ case '.?':
34
+ {
35
+ const [target, field] = node.args;
36
+ collectUntrustedPathAccesses(target, untrustedPathsByRoot, roots, aliases);
37
+ const root = rootName(target, aliases);
38
+ if (root && untrustedPathsByRoot.get(root)?.includes(field)) roots.add(root);
39
+ return;
40
+ }
41
+ case '[]':
42
+ case '[?]':
43
+ {
44
+ const [target, key] = node.args;
45
+ collectUntrustedPathAccesses(target, untrustedPathsByRoot, roots, aliases);
46
+ collectUntrustedPathAccesses(key, untrustedPathsByRoot, roots, aliases);
47
+ const root = rootName(target, aliases);
48
+ if (!root || !untrustedPathsByRoot.has(root)) return;
49
+ const field = literalFieldName(key);
50
+ if (field !== undefined) {
51
+ if (untrustedPathsByRoot.get(root)?.includes(field)) roots.add(root);
52
+ return;
53
+ }
54
+ if (!isAllowedListIndex(target, root)) roots.add(root);
55
+ return;
56
+ }
57
+ case 'call':
58
+ for (const argument of node.args[1]){
59
+ collectUntrustedPathAccesses(argument, untrustedPathsByRoot, roots, aliases);
60
+ }
61
+ return;
62
+ case 'rcall':
63
+ {
64
+ const [method, receiver, args] = node.args;
65
+ collectUntrustedPathAccesses(receiver, untrustedPathsByRoot, roots, aliases);
66
+ const nextAliases = comprehensionAliases(method, receiver, args, aliases);
67
+ for (const argument of args){
68
+ collectUntrustedPathAccesses(argument, untrustedPathsByRoot, roots, nextAliases);
69
+ }
70
+ return;
71
+ }
72
+ case 'list':
73
+ for (const element of node.args){
74
+ collectUntrustedPathAccesses(element, untrustedPathsByRoot, roots, aliases);
75
+ }
76
+ return;
77
+ case 'map':
78
+ for (const [key, value] of node.args){
79
+ collectUntrustedPathAccesses(key, untrustedPathsByRoot, roots, aliases);
80
+ collectUntrustedPathAccesses(value, untrustedPathsByRoot, roots, aliases);
81
+ }
82
+ return;
83
+ case '?:':
84
+ collectUntrustedPathAccesses(node.args[0], untrustedPathsByRoot, roots, aliases);
85
+ collectUntrustedPathAccesses(node.args[1], untrustedPathsByRoot, roots, aliases);
86
+ collectUntrustedPathAccesses(node.args[2], untrustedPathsByRoot, roots, aliases);
87
+ return;
88
+ case '!_':
89
+ case '-_':
90
+ collectUntrustedPathAccesses(node.args, untrustedPathsByRoot, roots, aliases);
91
+ return;
92
+ }
93
+ throw new Error(`Unsupported CEL AST operator: ${node.op}`);
94
+ }
95
+ function collectBinaryUntrustedPathAccesses([left, right], untrustedPathsByRoot, roots, aliases) {
96
+ collectUntrustedPathAccesses(left, untrustedPathsByRoot, roots, aliases);
97
+ collectUntrustedPathAccesses(right, untrustedPathsByRoot, roots, aliases);
98
+ }
99
+ function rootName(node, aliases) {
100
+ switch(node.op){
101
+ case 'id':
102
+ return aliases.get(node.args) ?? node.args;
103
+ case '.':
104
+ case '.?':
105
+ return rootName(node.args[0], aliases);
106
+ case '[]':
107
+ case '[?]':
108
+ return rootName(node.args[0], aliases);
109
+ default:
110
+ return undefined;
111
+ }
112
+ }
113
+ function literalFieldName(node) {
114
+ return node.op === 'value' && typeof node.args === 'string' ? node.args : undefined;
115
+ }
116
+ function isAllowedListIndex(target, root) {
117
+ return root === 'executions' && target.op === 'id';
118
+ }
119
+ function comprehensionAliases(method, receiver, args, aliases) {
120
+ if (![
121
+ 'all',
122
+ 'exists',
123
+ 'exists_one',
124
+ 'filter',
125
+ 'map'
126
+ ].includes(method)) return aliases;
127
+ const [alias] = args;
128
+ if (alias?.op !== 'id') return aliases;
129
+ const root = rootName(receiver, aliases);
130
+ if (root !== 'executions') return aliases;
131
+ return new Map([
132
+ ...aliases,
133
+ [
134
+ alias.args,
135
+ root
136
+ ]
137
+ ]);
138
+ }
139
+
140
+ //# sourceMappingURL=extract-cel-untrusted-path-accesses.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/template/extract-cel-untrusted-path-accesses.ts"],"sourcesContent":["import {type ASTNode, type BinaryOperator, parse as parseCel} from '@marcbachmann/cel-js';\n\nconst binaryOperators = new Set<BinaryOperator>([\n '!=',\n '==',\n 'in',\n '+',\n '-',\n '*',\n '/',\n '%',\n '<',\n '<=',\n '>',\n '>=',\n]);\n\nexport function extractCelUntrustedPathAccesses(params: {\n source: string;\n untrustedPathsByRoot: ReadonlyMap<string, readonly string[]>;\n}): string[] {\n const roots = new Set<string>();\n collectUntrustedPathAccesses(\n parseCel(params.source).ast,\n params.untrustedPathsByRoot,\n roots,\n new Map(),\n );\n return [...roots].sort();\n}\n\nfunction collectUntrustedPathAccesses(\n node: ASTNode,\n untrustedPathsByRoot: ReadonlyMap<string, readonly string[]>,\n roots: Set<string>,\n aliases: ReadonlyMap<string, string>,\n): void {\n if (binaryOperators.has(node.op as BinaryOperator) || node.op === '||' || node.op === '&&') {\n collectBinaryUntrustedPathAccesses(\n node.args as [ASTNode, ASTNode],\n untrustedPathsByRoot,\n roots,\n aliases,\n );\n return;\n }\n\n switch (node.op) {\n case 'id':\n case 'value':\n return;\n case '.':\n case '.?': {\n const [target, field] = node.args as [ASTNode, string];\n collectUntrustedPathAccesses(target, untrustedPathsByRoot, roots, aliases);\n const root = rootName(target, aliases);\n if (root && untrustedPathsByRoot.get(root)?.includes(field)) roots.add(root);\n return;\n }\n case '[]':\n case '[?]': {\n const [target, key] = node.args as [ASTNode, ASTNode];\n collectUntrustedPathAccesses(target, untrustedPathsByRoot, roots, aliases);\n collectUntrustedPathAccesses(key, untrustedPathsByRoot, roots, aliases);\n const root = rootName(target, aliases);\n if (!root || !untrustedPathsByRoot.has(root)) return;\n\n const field = literalFieldName(key);\n if (field !== undefined) {\n if (untrustedPathsByRoot.get(root)?.includes(field)) roots.add(root);\n return;\n }\n\n if (!isAllowedListIndex(target, root)) roots.add(root);\n return;\n }\n case 'call':\n for (const argument of node.args[1]) {\n collectUntrustedPathAccesses(argument, untrustedPathsByRoot, roots, aliases);\n }\n return;\n case 'rcall': {\n const [method, receiver, args] = node.args as [string, ASTNode, ASTNode[]];\n collectUntrustedPathAccesses(receiver, untrustedPathsByRoot, roots, aliases);\n const nextAliases = comprehensionAliases(method, receiver, args, aliases);\n for (const argument of args) {\n collectUntrustedPathAccesses(argument, untrustedPathsByRoot, roots, nextAliases);\n }\n return;\n }\n case 'list':\n for (const element of node.args) {\n collectUntrustedPathAccesses(element, untrustedPathsByRoot, roots, aliases);\n }\n return;\n case 'map':\n for (const [key, value] of node.args) {\n collectUntrustedPathAccesses(key, untrustedPathsByRoot, roots, aliases);\n collectUntrustedPathAccesses(value, untrustedPathsByRoot, roots, aliases);\n }\n return;\n case '?:':\n collectUntrustedPathAccesses(node.args[0], untrustedPathsByRoot, roots, aliases);\n collectUntrustedPathAccesses(node.args[1], untrustedPathsByRoot, roots, aliases);\n collectUntrustedPathAccesses(node.args[2], untrustedPathsByRoot, roots, aliases);\n return;\n case '!_':\n case '-_':\n collectUntrustedPathAccesses(node.args, untrustedPathsByRoot, roots, aliases);\n return;\n }\n\n throw new Error(`Unsupported CEL AST operator: ${(node as {op: string}).op}`);\n}\n\nfunction collectBinaryUntrustedPathAccesses(\n [left, right]: [ASTNode, ASTNode],\n untrustedPathsByRoot: ReadonlyMap<string, readonly string[]>,\n roots: Set<string>,\n aliases: ReadonlyMap<string, string>,\n): void {\n collectUntrustedPathAccesses(left, untrustedPathsByRoot, roots, aliases);\n collectUntrustedPathAccesses(right, untrustedPathsByRoot, roots, aliases);\n}\n\nfunction rootName(node: ASTNode, aliases: ReadonlyMap<string, string>): string | undefined {\n switch (node.op) {\n case 'id':\n return aliases.get(node.args) ?? node.args;\n case '.':\n case '.?':\n return rootName(node.args[0], aliases);\n case '[]':\n case '[?]':\n return rootName(node.args[0], aliases);\n default:\n return undefined;\n }\n}\n\nfunction literalFieldName(node: ASTNode): string | undefined {\n return node.op === 'value' && typeof node.args === 'string' ? node.args : undefined;\n}\n\nfunction isAllowedListIndex(target: ASTNode, root: string): boolean {\n return root === 'executions' && target.op === 'id';\n}\n\nfunction comprehensionAliases(\n method: string,\n receiver: ASTNode,\n args: readonly ASTNode[],\n aliases: ReadonlyMap<string, string>,\n): ReadonlyMap<string, string> {\n if (!['all', 'exists', 'exists_one', 'filter', 'map'].includes(method)) return aliases;\n\n const [alias] = args;\n if (alias?.op !== 'id') return aliases;\n\n const root = rootName(receiver, aliases);\n if (root !== 'executions') return aliases;\n\n return new Map([...aliases, [alias.args, root]]);\n}\n"],"names":["parse","parseCel","binaryOperators","Set","extractCelUntrustedPathAccesses","params","roots","collectUntrustedPathAccesses","source","ast","untrustedPathsByRoot","Map","sort","node","aliases","has","op","collectBinaryUntrustedPathAccesses","args","target","field","root","rootName","get","includes","add","key","literalFieldName","undefined","isAllowedListIndex","argument","method","receiver","nextAliases","comprehensionAliases","element","value","Error","left","right","alias"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { WorkflowTemplateSegment } from './template-segment.js';
2
+ export declare function parseWorkflowTemplate(source: string): WorkflowTemplateSegment[];
3
+ //# sourceMappingURL=parse-workflow-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-workflow-template.d.ts","sourceRoot":"","sources":["../../src/template/parse-workflow-template.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,uBAAuB,CAAC;AAEnE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,uBAAuB,EAAE,CA+C/E"}
@@ -0,0 +1,111 @@
1
+ import { createWorkflowExpression } from '../expression/create-workflow-expression.js';
2
+ import { InvalidWorkflowExpressionError } from '../expression/errors.js';
3
+ import { InvalidWorkflowTemplateError } from './errors.js';
4
+ import { extractCelContextRoots } from './extract-cel-context-roots.js';
5
+ import { scanLineComment, scanStringLiteral } from './scan-cel-token.js';
6
+ export function parseWorkflowTemplate(source) {
7
+ const segments = [];
8
+ let literal = '';
9
+ let index = 0;
10
+ while(index < source.length){
11
+ if (source.startsWith('$${{', index)) {
12
+ literal += '${{';
13
+ index += 4;
14
+ continue;
15
+ }
16
+ if (!source.startsWith('${{', index)) {
17
+ literal += source[index];
18
+ index += 1;
19
+ continue;
20
+ }
21
+ if (literal.length > 0) {
22
+ segments.push({
23
+ kind: 'literal',
24
+ text: literal
25
+ });
26
+ literal = '';
27
+ }
28
+ const expressionStartIndex = index;
29
+ const closeIndex = findExpressionCloseIndex(source, expressionStartIndex);
30
+ if (closeIndex === null) {
31
+ throw new InvalidWorkflowTemplateError({
32
+ source,
33
+ offset: expressionStartIndex,
34
+ reason: 'unterminated opener'
35
+ });
36
+ }
37
+ const innerSource = source.slice(expressionStartIndex + 3, closeIndex);
38
+ const expression = createTemplateExpression(source, expressionStartIndex, innerSource);
39
+ const contextRoots = extractTemplateContextRoots(source, expressionStartIndex, expression.source);
40
+ segments.push({
41
+ kind: 'expr',
42
+ expression,
43
+ contextRoots
44
+ });
45
+ index = closeIndex + 2;
46
+ }
47
+ if (literal.length > 0) segments.push({
48
+ kind: 'literal',
49
+ text: literal
50
+ });
51
+ return segments;
52
+ }
53
+ function findExpressionCloseIndex(source, openerIndex) {
54
+ let index = openerIndex + 3;
55
+ let depth = 0;
56
+ while(index < source.length){
57
+ const stringEndIndex = scanStringLiteral(source, index);
58
+ if (stringEndIndex !== null) {
59
+ index = stringEndIndex;
60
+ continue;
61
+ }
62
+ const commentEndIndex = scanLineComment(source, index);
63
+ if (commentEndIndex !== null) {
64
+ index = commentEndIndex;
65
+ continue;
66
+ }
67
+ if (depth === 0 && source.startsWith('}}', index)) return index;
68
+ const char = source[index];
69
+ if (char === '(' || char === '[' || char === '{') {
70
+ depth += 1;
71
+ } else if ((char === ')' || char === ']' || char === '}') && depth > 0) {
72
+ depth -= 1;
73
+ }
74
+ index += 1;
75
+ }
76
+ return null;
77
+ }
78
+ function extractTemplateContextRoots(source, expressionStartIndex, expressionSource) {
79
+ try {
80
+ return extractCelContextRoots(expressionSource);
81
+ } catch (error) {
82
+ throw new InvalidWorkflowTemplateError({
83
+ source,
84
+ offset: expressionStartIndex,
85
+ reason: error instanceof Error ? error.message : 'Expression context roots could not be extracted.',
86
+ cause: error
87
+ });
88
+ }
89
+ }
90
+ function createTemplateExpression(source, expressionStartIndex, innerSource) {
91
+ try {
92
+ return createWorkflowExpression({
93
+ source: innerSource,
94
+ check: {
95
+ mode: 'syntax'
96
+ }
97
+ });
98
+ } catch (error) {
99
+ if (error instanceof InvalidWorkflowExpressionError) {
100
+ throw new InvalidWorkflowTemplateError({
101
+ source,
102
+ offset: expressionStartIndex,
103
+ reason: error.reason,
104
+ cause: error
105
+ });
106
+ }
107
+ throw error;
108
+ }
109
+ }
110
+
111
+ //# sourceMappingURL=parse-workflow-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/template/parse-workflow-template.ts"],"sourcesContent":["import {createWorkflowExpression} from '../expression/create-workflow-expression.js';\nimport {InvalidWorkflowExpressionError} from '../expression/errors.js';\nimport {InvalidWorkflowTemplateError} from './errors.js';\nimport {extractCelContextRoots} from './extract-cel-context-roots.js';\nimport {scanLineComment, scanStringLiteral} from './scan-cel-token.js';\nimport type {WorkflowTemplateSegment} from './template-segment.js';\n\nexport function parseWorkflowTemplate(source: string): WorkflowTemplateSegment[] {\n const segments: WorkflowTemplateSegment[] = [];\n let literal = '';\n let index = 0;\n\n while (index < source.length) {\n if (source.startsWith('$${{', index)) {\n literal += '${{';\n index += 4;\n continue;\n }\n\n if (!source.startsWith('${{', index)) {\n literal += source[index];\n index += 1;\n continue;\n }\n\n if (literal.length > 0) {\n segments.push({kind: 'literal', text: literal});\n literal = '';\n }\n\n const expressionStartIndex = index;\n const closeIndex = findExpressionCloseIndex(source, expressionStartIndex);\n if (closeIndex === null) {\n throw new InvalidWorkflowTemplateError({\n source,\n offset: expressionStartIndex,\n reason: 'unterminated opener',\n });\n }\n\n const innerSource = source.slice(expressionStartIndex + 3, closeIndex);\n const expression = createTemplateExpression(source, expressionStartIndex, innerSource);\n const contextRoots = extractTemplateContextRoots(\n source,\n expressionStartIndex,\n expression.source,\n );\n\n segments.push({kind: 'expr', expression, contextRoots});\n index = closeIndex + 2;\n }\n\n if (literal.length > 0) segments.push({kind: 'literal', text: literal});\n return segments;\n}\n\nfunction findExpressionCloseIndex(source: string, openerIndex: number): number | null {\n let index = openerIndex + 3;\n let depth = 0;\n\n while (index < source.length) {\n const stringEndIndex = scanStringLiteral(source, index);\n if (stringEndIndex !== null) {\n index = stringEndIndex;\n continue;\n }\n\n const commentEndIndex = scanLineComment(source, index);\n if (commentEndIndex !== null) {\n index = commentEndIndex;\n continue;\n }\n\n if (depth === 0 && source.startsWith('}}', index)) return index;\n\n const char = source[index];\n if (char === '(' || char === '[' || char === '{') {\n depth += 1;\n } else if ((char === ')' || char === ']' || char === '}') && depth > 0) {\n depth -= 1;\n }\n\n index += 1;\n }\n\n return null;\n}\n\nfunction extractTemplateContextRoots(\n source: string,\n expressionStartIndex: number,\n expressionSource: string,\n) {\n try {\n return extractCelContextRoots(expressionSource);\n } catch (error) {\n throw new InvalidWorkflowTemplateError({\n source,\n offset: expressionStartIndex,\n reason:\n error instanceof Error ? error.message : 'Expression context roots could not be extracted.',\n cause: error,\n });\n }\n}\n\nfunction createTemplateExpression(\n source: string,\n expressionStartIndex: number,\n innerSource: string,\n) {\n try {\n return createWorkflowExpression({\n source: innerSource,\n check: {mode: 'syntax'},\n });\n } catch (error) {\n if (error instanceof InvalidWorkflowExpressionError) {\n throw new InvalidWorkflowTemplateError({\n source,\n offset: expressionStartIndex,\n reason: error.reason,\n cause: error,\n });\n }\n\n throw error;\n }\n}\n"],"names":["createWorkflowExpression","InvalidWorkflowExpressionError","InvalidWorkflowTemplateError","extractCelContextRoots","scanLineComment","scanStringLiteral","parseWorkflowTemplate","source","segments","literal","index","length","startsWith","push","kind","text","expressionStartIndex","closeIndex","findExpressionCloseIndex","offset","reason","innerSource","slice","expression","createTemplateExpression","contextRoots","extractTemplateContextRoots","openerIndex","depth","stringEndIndex","commentEndIndex","char","expressionSource","error","Error","message","cause","check","mode"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function scanStringLiteral(source: string, index: number): number | null;
2
+ export declare function scanLineComment(source: string, index: number): number | null;
3
+ //# sourceMappingURL=scan-cel-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-cel-token.d.ts","sourceRoot":"","sources":["../../src/template/scan-cel-token.ts"],"names":[],"mappings":"AAQA,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiB9E;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK5E"}
@@ -0,0 +1,55 @@
1
+ const stringPrefixPatterns = [
2
+ {
3
+ prefix: 'r',
4
+ raw: true
5
+ },
6
+ {
7
+ prefix: 'R',
8
+ raw: true
9
+ },
10
+ {
11
+ prefix: 'b',
12
+ raw: false
13
+ },
14
+ {
15
+ prefix: 'B',
16
+ raw: false
17
+ },
18
+ {
19
+ prefix: '',
20
+ raw: false
21
+ }
22
+ ];
23
+ export function scanStringLiteral(source, index) {
24
+ for (const { prefix, raw } of stringPrefixPatterns){
25
+ if (!source.startsWith(prefix, index)) continue;
26
+ const quoteIndex = index + prefix.length;
27
+ const quote = source[quoteIndex];
28
+ if (quote !== '"' && quote !== "'") continue;
29
+ const tripleQuote = quote.repeat(3);
30
+ if (source.startsWith(tripleQuote, quoteIndex)) {
31
+ return scanQuotedString(source, quoteIndex + 3, tripleQuote, raw);
32
+ }
33
+ return scanQuotedString(source, quoteIndex + 1, quote, raw);
34
+ }
35
+ return null;
36
+ }
37
+ export function scanLineComment(source, index) {
38
+ if (!source.startsWith('//', index)) return null;
39
+ const newlineIndex = source.indexOf('\n', index + 2);
40
+ return newlineIndex === -1 ? source.length : newlineIndex;
41
+ }
42
+ function scanQuotedString(source, startIndex, delimiter, raw) {
43
+ let index = startIndex;
44
+ while(index < source.length){
45
+ if (!raw && source[index] === '\\') {
46
+ index += 2;
47
+ continue;
48
+ }
49
+ if (source.startsWith(delimiter, index)) return index + delimiter.length;
50
+ index += 1;
51
+ }
52
+ return source.length;
53
+ }
54
+
55
+ //# sourceMappingURL=scan-cel-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/template/scan-cel-token.ts"],"sourcesContent":["const stringPrefixPatterns = [\n {prefix: 'r', raw: true},\n {prefix: 'R', raw: true},\n {prefix: 'b', raw: false},\n {prefix: 'B', raw: false},\n {prefix: '', raw: false},\n] as const;\n\nexport function scanStringLiteral(source: string, index: number): number | null {\n for (const {prefix, raw} of stringPrefixPatterns) {\n if (!source.startsWith(prefix, index)) continue;\n\n const quoteIndex = index + prefix.length;\n const quote = source[quoteIndex];\n if (quote !== '\"' && quote !== \"'\") continue;\n\n const tripleQuote = quote.repeat(3);\n if (source.startsWith(tripleQuote, quoteIndex)) {\n return scanQuotedString(source, quoteIndex + 3, tripleQuote, raw);\n }\n\n return scanQuotedString(source, quoteIndex + 1, quote, raw);\n }\n\n return null;\n}\n\nexport function scanLineComment(source: string, index: number): number | null {\n if (!source.startsWith('//', index)) return null;\n\n const newlineIndex = source.indexOf('\\n', index + 2);\n return newlineIndex === -1 ? source.length : newlineIndex;\n}\n\nfunction scanQuotedString(\n source: string,\n startIndex: number,\n delimiter: string,\n raw: boolean,\n): number {\n let index = startIndex;\n while (index < source.length) {\n if (!raw && source[index] === '\\\\') {\n index += 2;\n continue;\n }\n\n if (source.startsWith(delimiter, index)) return index + delimiter.length;\n\n index += 1;\n }\n\n return source.length;\n}\n"],"names":["stringPrefixPatterns","prefix","raw","scanStringLiteral","source","index","startsWith","quoteIndex","length","quote","tripleQuote","repeat","scanQuotedString","scanLineComment","newlineIndex","indexOf","startIndex","delimiter"],"mappings":"AAAA,MAAMA,uBAAuB;IAC3B;QAACC,QAAQ;QAAKC,KAAK;IAAI;IACvB;QAACD,QAAQ;QAAKC,KAAK;IAAI;IACvB;QAACD,QAAQ;QAAKC,KAAK;IAAK;IACxB;QAACD,QAAQ;QAAKC,KAAK;IAAK;IACxB;QAACD,QAAQ;QAAIC,KAAK;IAAK;CACxB;AAED,OAAO,SAASC,kBAAkBC,MAAc,EAAEC,KAAa;IAC7D,KAAK,MAAM,EAACJ,MAAM,EAAEC,GAAG,EAAC,IAAIF,qBAAsB;QAChD,IAAI,CAACI,OAAOE,UAAU,CAACL,QAAQI,QAAQ;QAEvC,MAAME,aAAaF,QAAQJ,OAAOO,MAAM;QACxC,MAAMC,QAAQL,MAAM,CAACG,WAAW;QAChC,IAAIE,UAAU,OAAOA,UAAU,KAAK;QAEpC,MAAMC,cAAcD,MAAME,MAAM,CAAC;QACjC,IAAIP,OAAOE,UAAU,CAACI,aAAaH,aAAa;YAC9C,OAAOK,iBAAiBR,QAAQG,aAAa,GAAGG,aAAaR;QAC/D;QAEA,OAAOU,iBAAiBR,QAAQG,aAAa,GAAGE,OAAOP;IACzD;IAEA,OAAO;AACT;AAEA,OAAO,SAASW,gBAAgBT,MAAc,EAAEC,KAAa;IAC3D,IAAI,CAACD,OAAOE,UAAU,CAAC,MAAMD,QAAQ,OAAO;IAE5C,MAAMS,eAAeV,OAAOW,OAAO,CAAC,MAAMV,QAAQ;IAClD,OAAOS,iBAAiB,CAAC,IAAIV,OAAOI,MAAM,GAAGM;AAC/C;AAEA,SAASF,iBACPR,MAAc,EACdY,UAAkB,EAClBC,SAAiB,EACjBf,GAAY;IAEZ,IAAIG,QAAQW;IACZ,MAAOX,QAAQD,OAAOI,MAAM,CAAE;QAC5B,IAAI,CAACN,OAAOE,MAAM,CAACC,MAAM,KAAK,MAAM;YAClCA,SAAS;YACT;QACF;QAEA,IAAID,OAAOE,UAAU,CAACW,WAAWZ,QAAQ,OAAOA,QAAQY,UAAUT,MAAM;QAExEH,SAAS;IACX;IAEA,OAAOD,OAAOI,MAAM;AACtB"}
@@ -0,0 +1,12 @@
1
+ import type { WorkflowExpression } from '../expression/workflow-expression.js';
2
+ export interface WorkflowTemplateLiteralSegment {
3
+ readonly kind: 'literal';
4
+ readonly text: string;
5
+ }
6
+ export interface WorkflowTemplateExprSegment {
7
+ readonly kind: 'expr';
8
+ readonly expression: WorkflowExpression;
9
+ readonly contextRoots: readonly string[];
10
+ }
11
+ export type WorkflowTemplateSegment = WorkflowTemplateLiteralSegment | WorkflowTemplateExprSegment;
12
+ //# sourceMappingURL=template-segment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-segment.d.ts","sourceRoot":"","sources":["../../src/template/template-segment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,sCAAsC,CAAC;AAE7E,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,MAAM,MAAM,uBAAuB,GAAG,8BAA8B,GAAG,2BAA2B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=template-segment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/template/template-segment.ts"],"sourcesContent":["import type {WorkflowExpression} from '../expression/workflow-expression.js';\n\nexport interface WorkflowTemplateLiteralSegment {\n readonly kind: 'literal';\n readonly text: string;\n}\n\nexport interface WorkflowTemplateExprSegment {\n readonly kind: 'expr';\n readonly expression: WorkflowExpression;\n readonly contextRoots: readonly string[];\n}\n\nexport type WorkflowTemplateSegment = WorkflowTemplateLiteralSegment | WorkflowTemplateExprSegment;\n"],"names":[],"mappings":"AAaA,WAAmG"}