@telorun/cel 0.0.0-stage → 0.108.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 (223) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +281 -2
  3. package/dist/activation.d.ts +27 -0
  4. package/dist/activation.d.ts.map +1 -0
  5. package/dist/activation.js +24 -0
  6. package/dist/backend-runtime.d.ts +184 -0
  7. package/dist/backend-runtime.d.ts.map +1 -0
  8. package/dist/backend-runtime.js +425 -0
  9. package/dist/bounded-cache.d.ts +21 -0
  10. package/dist/bounded-cache.d.ts.map +1 -0
  11. package/dist/bounded-cache.js +42 -0
  12. package/dist/catalog-runtime.d.ts +59 -0
  13. package/dist/catalog-runtime.d.ts.map +1 -0
  14. package/dist/catalog-runtime.js +786 -0
  15. package/dist/cel-expression.d.ts +32 -0
  16. package/dist/cel-expression.d.ts.map +1 -0
  17. package/dist/cel-expression.js +29 -0
  18. package/dist/cel-map-value.d.ts +47 -0
  19. package/dist/cel-map-value.d.ts.map +1 -0
  20. package/dist/cel-map-value.js +85 -0
  21. package/dist/cel-program.d.ts +44 -0
  22. package/dist/cel-program.d.ts.map +1 -0
  23. package/dist/cel-program.js +72 -0
  24. package/dist/cel-type.d.ts +131 -0
  25. package/dist/cel-type.d.ts.map +1 -0
  26. package/dist/cel-type.js +293 -0
  27. package/dist/cel-value.d.ts +166 -0
  28. package/dist/cel-value.d.ts.map +1 -0
  29. package/dist/cel-value.js +225 -0
  30. package/dist/check-diagnostic.d.ts +54 -0
  31. package/dist/check-diagnostic.d.ts.map +1 -0
  32. package/dist/check-diagnostic.js +66 -0
  33. package/dist/checker.d.ts +86 -0
  34. package/dist/checker.d.ts.map +1 -0
  35. package/dist/checker.js +806 -0
  36. package/dist/closure-backend.d.ts +21 -0
  37. package/dist/closure-backend.d.ts.map +1 -0
  38. package/dist/closure-backend.js +487 -0
  39. package/dist/comprehension-bindings.d.ts +33 -0
  40. package/dist/comprehension-bindings.d.ts.map +1 -0
  41. package/dist/comprehension-bindings.js +50 -0
  42. package/dist/comprehension-runtime.d.ts +44 -0
  43. package/dist/comprehension-runtime.d.ts.map +1 -0
  44. package/dist/comprehension-runtime.js +137 -0
  45. package/dist/declared-chain.d.ts +35 -0
  46. package/dist/declared-chain.d.ts.map +1 -0
  47. package/dist/declared-chain.js +36 -0
  48. package/dist/duration-value.d.ts +70 -0
  49. package/dist/duration-value.d.ts.map +1 -0
  50. package/dist/duration-value.js +149 -0
  51. package/dist/emitted-module.d.ts +207 -0
  52. package/dist/emitted-module.d.ts.map +1 -0
  53. package/dist/emitted-module.js +359 -0
  54. package/dist/engine-version.d.ts +3 -0
  55. package/dist/engine-version.d.ts.map +1 -0
  56. package/dist/engine-version.js +8 -0
  57. package/dist/environment-digest.d.ts +44 -0
  58. package/dist/environment-digest.d.ts.map +1 -0
  59. package/dist/environment-digest.js +98 -0
  60. package/dist/environment.d.ts +291 -0
  61. package/dist/environment.d.ts.map +1 -0
  62. package/dist/environment.js +474 -0
  63. package/dist/function-catalog.d.ts +66 -0
  64. package/dist/function-catalog.d.ts.map +1 -0
  65. package/dist/function-catalog.js +77 -0
  66. package/dist/function-registry.d.ts +118 -0
  67. package/dist/function-registry.d.ts.map +1 -0
  68. package/dist/function-registry.js +292 -0
  69. package/dist/index.d.ts +93 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +65 -0
  72. package/dist/integer-arithmetic.d.ts +27 -0
  73. package/dist/integer-arithmetic.d.ts.map +1 -0
  74. package/dist/integer-arithmetic.js +58 -0
  75. package/dist/js-emitter.d.ts +133 -0
  76. package/dist/js-emitter.d.ts.map +1 -0
  77. package/dist/js-emitter.js +568 -0
  78. package/dist/json-schema-type.d.ts +182 -0
  79. package/dist/json-schema-type.d.ts.map +1 -0
  80. package/dist/json-schema-type.js +487 -0
  81. package/dist/json-text-scan.d.ts +28 -0
  82. package/dist/json-text-scan.d.ts.map +1 -0
  83. package/dist/json-text-scan.js +159 -0
  84. package/dist/lexer.d.ts +103 -0
  85. package/dist/lexer.d.ts.map +1 -0
  86. package/dist/lexer.js +458 -0
  87. package/dist/macro-check.d.ts +41 -0
  88. package/dist/macro-check.d.ts.map +1 -0
  89. package/dist/macro-check.js +162 -0
  90. package/dist/macro-shape.d.ts +24 -0
  91. package/dist/macro-shape.d.ts.map +1 -0
  92. package/dist/macro-shape.js +55 -0
  93. package/dist/member-read.d.ts +89 -0
  94. package/dist/member-read.d.ts.map +1 -0
  95. package/dist/member-read.js +166 -0
  96. package/dist/namespace-resolution.d.ts +35 -0
  97. package/dist/namespace-resolution.d.ts.map +1 -0
  98. package/dist/namespace-resolution.js +160 -0
  99. package/dist/nominal-type.d.ts +63 -0
  100. package/dist/nominal-type.d.ts.map +1 -0
  101. package/dist/nominal-type.js +98 -0
  102. package/dist/nullable-access.d.ts +38 -0
  103. package/dist/nullable-access.d.ts.map +1 -0
  104. package/dist/nullable-access.js +93 -0
  105. package/dist/parse-limits.d.ts +26 -0
  106. package/dist/parse-limits.d.ts.map +1 -0
  107. package/dist/parse-limits.js +21 -0
  108. package/dist/parser.d.ts +48 -0
  109. package/dist/parser.d.ts.map +1 -0
  110. package/dist/parser.js +503 -0
  111. package/dist/qualified-calls.d.ts +22 -0
  112. package/dist/qualified-calls.d.ts.map +1 -0
  113. package/dist/qualified-calls.js +27 -0
  114. package/dist/regular-expression.d.ts +48 -0
  115. package/dist/regular-expression.d.ts.map +1 -0
  116. package/dist/regular-expression.js +77 -0
  117. package/dist/reserved-words.d.ts +52 -0
  118. package/dist/reserved-words.d.ts.map +1 -0
  119. package/dist/reserved-words.js +77 -0
  120. package/dist/resolved-call.d.ts +46 -0
  121. package/dist/resolved-call.d.ts.map +1 -0
  122. package/dist/resolved-call.js +15 -0
  123. package/dist/root-references.d.ts +23 -0
  124. package/dist/root-references.d.ts.map +1 -0
  125. package/dist/root-references.js +120 -0
  126. package/dist/runtime-library.d.ts +67 -0
  127. package/dist/runtime-library.d.ts.map +1 -0
  128. package/dist/runtime-library.js +554 -0
  129. package/dist/serializer.d.ts +24 -0
  130. package/dist/serializer.d.ts.map +1 -0
  131. package/dist/serializer.js +256 -0
  132. package/dist/sha256.d.ts +20 -0
  133. package/dist/sha256.d.ts.map +1 -0
  134. package/dist/sha256.js +103 -0
  135. package/dist/signature.d.ts +72 -0
  136. package/dist/signature.d.ts.map +1 -0
  137. package/dist/signature.js +61 -0
  138. package/dist/signatures/function-catalog.json +788 -0
  139. package/dist/signatures/standard-library.json +229 -0
  140. package/dist/standard-library.d.ts +41 -0
  141. package/dist/standard-library.d.ts.map +1 -0
  142. package/dist/standard-library.js +85 -0
  143. package/dist/syntax-diagnostic.d.ts +61 -0
  144. package/dist/syntax-diagnostic.d.ts.map +1 -0
  145. package/dist/syntax-diagnostic.js +28 -0
  146. package/dist/syntax-tree.d.ts +160 -0
  147. package/dist/syntax-tree.d.ts.map +1 -0
  148. package/dist/syntax-tree.js +58 -0
  149. package/dist/timestamp-value.d.ts +62 -0
  150. package/dist/timestamp-value.d.ts.map +1 -0
  151. package/dist/timestamp-value.js +238 -0
  152. package/dist/tree-equality.d.ts +15 -0
  153. package/dist/tree-equality.d.ts.map +1 -0
  154. package/dist/tree-equality.js +105 -0
  155. package/dist/type-expression.d.ts +42 -0
  156. package/dist/type-expression.d.ts.map +1 -0
  157. package/dist/type-expression.js +154 -0
  158. package/dist/value-equality.d.ts +38 -0
  159. package/dist/value-equality.d.ts.map +1 -0
  160. package/dist/value-equality.js +196 -0
  161. package/dist/value-text.d.ts +25 -0
  162. package/dist/value-text.d.ts.map +1 -0
  163. package/dist/value-text.js +44 -0
  164. package/dist/zoned-calendar.d.ts +61 -0
  165. package/dist/zoned-calendar.d.ts.map +1 -0
  166. package/dist/zoned-calendar.js +143 -0
  167. package/package.json +56 -3
  168. package/src/activation.ts +32 -0
  169. package/src/backend-runtime.ts +604 -0
  170. package/src/bounded-cache.ts +45 -0
  171. package/src/catalog-runtime.ts +921 -0
  172. package/src/cel-expression.ts +53 -0
  173. package/src/cel-map-value.ts +97 -0
  174. package/src/cel-program.ts +103 -0
  175. package/src/cel-type.ts +359 -0
  176. package/src/cel-value.ts +361 -0
  177. package/src/check-diagnostic.ts +104 -0
  178. package/src/checker.ts +1045 -0
  179. package/src/closure-backend.ts +547 -0
  180. package/src/comprehension-bindings.ts +66 -0
  181. package/src/comprehension-runtime.ts +157 -0
  182. package/src/declared-chain.ts +45 -0
  183. package/src/duration-value.ts +160 -0
  184. package/src/emitted-module.ts +494 -0
  185. package/src/engine-version.ts +9 -0
  186. package/src/environment-digest.ts +111 -0
  187. package/src/environment.ts +761 -0
  188. package/src/function-catalog.ts +140 -0
  189. package/src/function-registry.ts +341 -0
  190. package/src/index.ts +407 -0
  191. package/src/integer-arithmetic.ts +64 -0
  192. package/src/js-emitter.ts +721 -0
  193. package/src/json-schema-type.ts +664 -0
  194. package/src/json-text-scan.ts +163 -0
  195. package/src/lexer.ts +562 -0
  196. package/src/macro-check.ts +197 -0
  197. package/src/macro-shape.ts +66 -0
  198. package/src/member-read.ts +167 -0
  199. package/src/namespace-resolution.ts +167 -0
  200. package/src/nominal-type.ts +149 -0
  201. package/src/nullable-access.ts +95 -0
  202. package/src/parse-limits.ts +36 -0
  203. package/src/parser.ts +554 -0
  204. package/src/qualified-calls.ts +39 -0
  205. package/src/regular-expression.ts +101 -0
  206. package/src/reserved-words.ts +94 -0
  207. package/src/resolved-call.ts +47 -0
  208. package/src/root-references.ts +126 -0
  209. package/src/runtime-library.ts +639 -0
  210. package/src/serializer.ts +262 -0
  211. package/src/sha256.ts +112 -0
  212. package/src/signature.ts +127 -0
  213. package/src/signatures/function-catalog.json +788 -0
  214. package/src/signatures/standard-library.json +235 -0
  215. package/src/standard-library.ts +149 -0
  216. package/src/syntax-diagnostic.ts +72 -0
  217. package/src/syntax-tree.ts +229 -0
  218. package/src/timestamp-value.ts +310 -0
  219. package/src/tree-equality.ts +130 -0
  220. package/src/type-expression.ts +182 -0
  221. package/src/value-equality.ts +201 -0
  222. package/src/value-text.ts +45 -0
  223. package/src/zoned-calendar.ts +178 -0
@@ -0,0 +1,474 @@
1
+ /**
2
+ * An environment: what names, functions and types an expression is read against.
3
+ *
4
+ * **Everything is registered the same way, including the standard library.** There is
5
+ * no privileged door: a host can replace `duration(string)` with its own, or remove a
6
+ * standard function so a call to it is refused, because the library came in through the
7
+ * same `registerFunction` a host uses. An engine with a built-in library behind a
8
+ * private registration is an engine whose library cannot be changed — which is the
9
+ * concrete thing this package exists to fix.
10
+ *
11
+ * `clone()` inherits everything and then diverges: registering or removing on the clone
12
+ * leaves the parent as it was. That is what lets one base environment be built once and
13
+ * specialised per site.
14
+ *
15
+ * **Two seams, and only two, let a host's vocabulary in**: `registerType`, for a named
16
+ * type over a base with its own operators and members, and `resolveSchemaType`, asked at
17
+ * every node of a JSON Schema before the structural rules. Nothing in this package knows
18
+ * any host's type names.
19
+ */
20
+ import { BoundedCache } from "./bounded-cache.js";
21
+ import { parseExpression, resolvedUnder } from "./cel-expression.js";
22
+ import { compileExpression } from "./cel-program.js";
23
+ import { emitModule, emitterRuntime, storedEmittedModule } from "./emitted-module.js";
24
+ import { environmentDigest } from "./environment-digest.js";
25
+ import { CEL_VALUE_KEYS } from "./cel-value.js";
26
+ import { DYN, formatType } from "./cel-type.js";
27
+ import { CelEngineError } from "./check-diagnostic.js";
28
+ import { checkExpression } from "./checker.js";
29
+ import { FunctionRegistry } from "./function-registry.js";
30
+ import { fieldMapType, schemaType } from "./json-schema-type.js";
31
+ import { normalizeNamespaces } from "./namespace-resolution.js";
32
+ import { buildRegisteredType, CelTypeRegistrationError, nominalOperators, nominalSignatures, } from "./nominal-type.js";
33
+ import { formatSignature, parseSignature } from "./signature.js";
34
+ import { registerStandardLibrary, standardConstants } from "./standard-library.js";
35
+ import { standardConstantValues } from "./runtime-library.js";
36
+ import { parseTypeExpression } from "./type-expression.js";
37
+ /** How many compiled expressions an environment keeps when nothing says otherwise. */
38
+ export const DEFAULT_COMPILED_CACHE_CAPACITY = 256;
39
+ export class CelEnvironment {
40
+ registry;
41
+ variables;
42
+ types;
43
+ namespaceFunctions;
44
+ /** Namespaces that declare only part of what they reach; see {@link NamespaceOptions.open}. */
45
+ openNamespaces;
46
+ constants;
47
+ /** Bounded: a source text is a key an author's input decides. */
48
+ compiled;
49
+ options;
50
+ constructor(options = {}, inherited) {
51
+ this.options = {
52
+ unlistedVariablesAreDyn: false,
53
+ homogeneousAggregateLiterals: false,
54
+ enableOptionalTypes: false,
55
+ ...(inherited ? inherited.options : {}),
56
+ ...options,
57
+ };
58
+ this.registry = new FunctionRegistry(inherited?.registry);
59
+ this.variables = new Map(inherited?.variables);
60
+ this.types = new Map(inherited?.types);
61
+ this.namespaceFunctions = new Map();
62
+ for (const [namespace, functions] of inherited?.namespaceFunctions ?? []) {
63
+ this.namespaceFunctions.set(namespace, new Map(functions));
64
+ }
65
+ this.openNamespaces = new Set(inherited?.openNamespaces);
66
+ this.constants = new Map(inherited?.constants);
67
+ this.compiled = new BoundedCache(this.options.compiledCacheCapacity ?? DEFAULT_COMPILED_CACHE_CAPACITY);
68
+ if (inherited)
69
+ return;
70
+ if (this.options.standardLibrary ?? true) {
71
+ for (const [name, value] of standardConstantValues(this.options.enableOptionalTypes)) {
72
+ this.constants.set(name, value);
73
+ }
74
+ registerStandardLibrary(this.registry, {
75
+ optionalTypes: this.options.enableOptionalTypes,
76
+ resolveNominal: this.nominalResolver,
77
+ });
78
+ for (const constant of standardConstants(this.options.enableOptionalTypes)) {
79
+ this.variables.set(constant.name, {
80
+ type: constant.type,
81
+ constant: true,
82
+ ...(constant.description === undefined ? {} : { description: constant.description }),
83
+ });
84
+ }
85
+ }
86
+ }
87
+ /** A new environment inheriting everything, which may then diverge. */
88
+ clone(options = {}) {
89
+ return new CelEnvironment(options, this);
90
+ }
91
+ // --- registration -------------------------------------------------------
92
+ /**
93
+ * Registers a function, **replacing** any registration answering the same call.
94
+ *
95
+ * Every registration forgets what this environment compiled: a program holds the
96
+ * overloads its call sites resolved to, so serving a cached one after the library
97
+ * changed would run the function that was replaced — the precise failure the
98
+ * replaceable library exists to avoid.
99
+ */
100
+ registerFunction(signature, metadata = {}) {
101
+ this.registry.register(this.signatureOf(signature), metadata);
102
+ this.compiled.clear();
103
+ return this;
104
+ }
105
+ /** Removes the one registration answering that call. Answers whether it was there. */
106
+ removeFunction(signature) {
107
+ const removed = this.registry.remove(this.signatureOf(signature));
108
+ this.compiled.clear();
109
+ return removed;
110
+ }
111
+ /** Removes every registration of a name, so a call to it is unknown. */
112
+ removeFunctionsNamed(name) {
113
+ const removed = this.registry.removeName(name);
114
+ this.compiled.clear();
115
+ return removed;
116
+ }
117
+ /** Registers an operator over the types given, replacing one over the same types. */
118
+ registerOperator(operator, parameters, returns, metadata = {}) {
119
+ this.registry.register({
120
+ name: operator,
121
+ form: "global",
122
+ parameters: parameters.map((text) => this.readType(text)),
123
+ returns: this.readType(returns),
124
+ }, metadata);
125
+ this.compiled.clear();
126
+ return this;
127
+ }
128
+ registerVariable(name, declaration, metadata = {}) {
129
+ this.variables.set(name, {
130
+ ...this.typeOf(declaration),
131
+ constant: false,
132
+ ...(metadata.description === undefined ? {} : { description: metadata.description }),
133
+ });
134
+ // A declared name changes which NAME a dotted chain reads: `splitDeclaredChain` is
135
+ // decided at compile time from the declarations, so a program compiled before this
136
+ // call resolved the chain against the activation instead. Serving it afterwards is
137
+ // the check/run divergence that split exists to prevent, arriving through the cache.
138
+ this.compiled.clear();
139
+ return this;
140
+ }
141
+ /** A name whose value never changes; it reads exactly as a variable does. */
142
+ registerConstant(name, declaration, metadata = {}) {
143
+ this.variables.set(name, {
144
+ ...this.typeOf(declaration),
145
+ constant: true,
146
+ ...(metadata.description === undefined ? {} : { description: metadata.description }),
147
+ });
148
+ this.compiled.clear();
149
+ return this;
150
+ }
151
+ hasVariable(name) {
152
+ return this.variables.has(name);
153
+ }
154
+ /**
155
+ * Every schema-typed registration whose schema this engine could not judge whole, with
156
+ * each node by JSON Pointer. Empty is every node judged, which is the normal answer.
157
+ */
158
+ schemaReports() {
159
+ const reports = [];
160
+ for (const [name, held] of [...this.variables.entries()].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)) {
161
+ const unjudged = held.unjudged ?? [];
162
+ const recursive = held.recursive ?? [];
163
+ if (unjudged.length === 0 && recursive.length === 0)
164
+ continue;
165
+ reports.push({ name, unjudged, recursive });
166
+ }
167
+ return reports;
168
+ }
169
+ /** Registers a named type, with every operator, conversion and member it declares. */
170
+ registerType(definition) {
171
+ // A value of a named type carries its name under the engine's own value key, so a
172
+ // name the engine already uses there would make two types one value.
173
+ if (CEL_VALUE_KEYS.includes(definition.name)) {
174
+ throw new CelTypeRegistrationError(`${JSON.stringify(definition.name)} is the type key of a value this engine builds`);
175
+ }
176
+ const registered = buildRegisteredType(definition, this.nominalResolver);
177
+ this.types.set(definition.name, registered);
178
+ const resolver = (name, args) => name === "Self" ? { ...registered.self, args: args.length > 0 ? args : registered.self.args } : this.nominalResolver(name, args);
179
+ for (const signature of nominalSignatures(definition)) {
180
+ this.registry.register(parseSignature(signature, resolver), {
181
+ deterministic: true,
182
+ origin: `type:${definition.name}`,
183
+ });
184
+ }
185
+ for (const operator of nominalOperators(definition)) {
186
+ this.registry.register({
187
+ name: operator.operator,
188
+ form: "global",
189
+ parameters: operator.parameters.map((text) => parseTypeExpression(text, resolver)),
190
+ returns: parseTypeExpression(operator.returns, resolver),
191
+ }, { deterministic: true, origin: `type:${definition.name}` });
192
+ }
193
+ this.compiled.clear();
194
+ return this;
195
+ }
196
+ /**
197
+ * Registers a namespace and the functions it declares. A namespace is a name that
198
+ * denotes a module rather than a value, which is what turns `Alias.fn(x)` into a
199
+ * qualified call rather than a method on something.
200
+ */
201
+ registerNamespace(name, functions = [], options = {}) {
202
+ normalizeNamespaces([name]);
203
+ const declared = new Map();
204
+ for (const declaration of functions) {
205
+ const entry = typeof declaration === "string" ? { signature: declaration } : declaration;
206
+ const flags = {
207
+ ...(entry.deterministic === undefined ? {} : { deterministic: entry.deterministic }),
208
+ ...(entry.hostBacked === undefined ? {} : { hostBacked: entry.hostBacked }),
209
+ ...(entry.throws === undefined ? {} : { throws: entry.throws }),
210
+ };
211
+ // A name this environment registers no type under is RECORDED and read as `dyn`,
212
+ // never thrown: a namespace declaration is a host's own data — a module function's
213
+ // declared result out of a manifest — so a typo there would otherwise be a crash
214
+ // with no line, and the consumer that knows where it was written is the one that
215
+ // can anchor the diagnostic. The checker reports it at each call.
216
+ const unregistered = [];
217
+ const resolver = (named, args) => {
218
+ const resolved = this.nominalResolver(named, args);
219
+ if (resolved)
220
+ return resolved;
221
+ if (!unregistered.includes(named))
222
+ unregistered.push(named);
223
+ return DYN;
224
+ };
225
+ const recorded = () => (unregistered.length > 0 ? { unregisteredTypes: [...unregistered] } : {});
226
+ if (entry.signature !== undefined) {
227
+ const signature = parseSignature(entry.signature, resolver);
228
+ if (signature.form !== "global") {
229
+ throw new CelTypeRegistrationError(`a namespaced function is declared without a receiver: ${JSON.stringify(entry.signature)}`);
230
+ }
231
+ declared.set(signature.name, {
232
+ name: signature.name,
233
+ returns: signature.returns,
234
+ parameters: signature.parameters,
235
+ signature: formatSignature(signature),
236
+ ...recorded(),
237
+ ...flags,
238
+ });
239
+ continue;
240
+ }
241
+ declared.set(entry.name, {
242
+ name: entry.name,
243
+ returns: typeof entry.returns === "string" ? parseTypeExpression(entry.returns, resolver) : entry.returns,
244
+ ...recorded(),
245
+ ...flags,
246
+ });
247
+ }
248
+ this.namespaceFunctions.set(name, declared);
249
+ if (options.open === true)
250
+ this.openNamespaces.add(name);
251
+ else
252
+ this.openNamespaces.delete(name);
253
+ this.compiled.clear();
254
+ return this;
255
+ }
256
+ /** Every namespace registered, in canonical order. */
257
+ namespaces() {
258
+ return [...this.namespaceFunctions.keys()].sort();
259
+ }
260
+ // --- reading ------------------------------------------------------------
261
+ /** Reads an expression against this environment's namespaces. */
262
+ parse(source) {
263
+ return parseExpression(source, {
264
+ namespaces: this.namespaces(),
265
+ optionalSyntax: this.options.enableOptionalTypes,
266
+ ...(this.options.limits ? { limits: this.options.limits } : {}),
267
+ });
268
+ }
269
+ /**
270
+ * Checks an expression. A tree resolved under a different namespace set is **refused**
271
+ * rather than checked: its qualified calls are not the ones this environment would
272
+ * have found, so every answer about it would be about a different expression.
273
+ */
274
+ check(source) {
275
+ const expression = typeof source === "string" ? this.parse(source) : source;
276
+ if (typeof source !== "string" && !resolvedUnder(expression, this.namespaces())) {
277
+ throw new CelEngineError("namespaces_mismatch", `the expression was resolved under [${expression.namespaces.join(", ")}] and this environment has [${this.namespaces().join(", ")}]`);
278
+ }
279
+ return checkExpression(expression, {
280
+ registry: this.registry,
281
+ variable: (name) => this.variables.get(name)?.type,
282
+ declaredVariableNames: () => [...this.variables.keys()],
283
+ namespaceFunction: (namespace, name) => this.namespaceFunctions.get(namespace)?.get(name),
284
+ namespaceIsOpen: (namespace) => this.openNamespaces.has(namespace),
285
+ options: {
286
+ unlistedVariablesAreDyn: this.options.unlistedVariablesAreDyn,
287
+ homogeneousAggregateLiterals: this.options.homogeneousAggregateLiterals,
288
+ enableOptionalTypes: this.options.enableOptionalTypes,
289
+ },
290
+ });
291
+ }
292
+ /**
293
+ * Compiles an expression into a program. Nothing is type-checked here — the checker
294
+ * decides every verdict, and a consumer that wants one asks for it — so compiling
295
+ * refuses only what cannot be compiled at all: a source that did not read whole.
296
+ *
297
+ * A compile from text is memoized in a bounded cache; a tree handed over directly is
298
+ * compiled each time, because a tree has no identity to key on.
299
+ */
300
+ compile(source) {
301
+ if (typeof source !== "string")
302
+ return this.compileExpressionNow(source);
303
+ const held = this.compiled.get(source);
304
+ if (held)
305
+ return held;
306
+ const program = this.compileExpressionNow(this.parse(source));
307
+ this.compiled.set(source, program);
308
+ return program;
309
+ }
310
+ /** Compiles and runs an expression against an activation. */
311
+ evaluate(source, activation, options) {
312
+ return this.compile(source).evaluate(activation, options);
313
+ }
314
+ // --- the emitter ---------------------------------------------------------
315
+ /**
316
+ * The JavaScript module for a set of expressions, emitted now: its text, its key and its
317
+ * integrity header. The same expressions in the same order against the same environment
318
+ * emit byte-identically, so a host may compare two emissions instead of trusting one.
319
+ *
320
+ * The host stores and loads the text; this package touches no filesystem and exports no
321
+ * loader. `emitterRuntime()` is what the loaded module's factory takes, and
322
+ * `programsFromEmittedModule` verifies its header before anything runs.
323
+ */
324
+ emit(sources) {
325
+ return emitModule(this.target(), this.digest(), this.expressionsOf(sources));
326
+ }
327
+ /**
328
+ * The module for a set of expressions, read from the store where it holds a copy whose
329
+ * header matches and emitted and written where it does not — so a corrupted, stale or
330
+ * foreign stored copy causes a recompile rather than a run.
331
+ */
332
+ emittedModule(sources, store) {
333
+ // The trees are handed over as a thunk: a hit reads a header and a digest, and parsing
334
+ // every expression to find out whether it needed to is work with no answer attached.
335
+ return storedEmittedModule(this.target(), this.digest(), sources, () => this.expressionsOf(sources), store);
336
+ }
337
+ /** The runtime support library an emitted module's factory is handed. */
338
+ emitterRuntime() {
339
+ return emitterRuntime(this.target());
340
+ }
341
+ /**
342
+ * This environment's digest: the order-independent hash of its resolved listing, which
343
+ * the key and the header are both over. A host that caches by key never needs it; a host
344
+ * explaining a recompile does.
345
+ */
346
+ digest() {
347
+ return environmentDigest(this);
348
+ }
349
+ expressionsOf(sources) {
350
+ return sources.map((source) => ({ source, root: this.readyExpression(this.parse(source)).root }));
351
+ }
352
+ /** What the backends need of this environment, built one way for both of them. */
353
+ target() {
354
+ return {
355
+ registry: this.registry,
356
+ constants: this.constants,
357
+ nominalArity: (name) => this.types.get(name)?.parameters.length,
358
+ // The same declarations the checker splits a dotted chain on.
359
+ declares: (name) => this.variables.has(name),
360
+ };
361
+ }
362
+ /**
363
+ * An expression a backend may compile: read whole, and resolved under this
364
+ * environment's own namespaces. Nothing is type-checked — the checker decides every
365
+ * verdict and a consumer that wants one asks for it.
366
+ */
367
+ readyExpression(expression) {
368
+ if (expression.diagnostics.length > 0) {
369
+ throw new CelEngineError("unreadable_expression", `${JSON.stringify(expression.source)} could not be read: ${expression.diagnostics[0].message}`);
370
+ }
371
+ if (!resolvedUnder(expression, this.namespaces())) {
372
+ throw new CelEngineError("namespaces_mismatch", `the expression was resolved under [${expression.namespaces.join(", ")}] and this environment has [${this.namespaces().join(", ")}]`);
373
+ }
374
+ return expression;
375
+ }
376
+ compileExpressionNow(expression) {
377
+ return compileExpression(this.readyExpression(expression), this.target());
378
+ }
379
+ /**
380
+ * Whether a value of this type can be converted to text by the environment's own
381
+ * `string()`. A consumer that renders a value into text asks this before it tries;
382
+ * `dyn` passes, because only the runtime knows what it holds.
383
+ */
384
+ convertsToString(type) {
385
+ if (type.kind === "dyn")
386
+ return true;
387
+ return "resolved" in this.registry.resolve("string", "global", [type]);
388
+ }
389
+ /** Every registration, for a listing a host prints or compares. */
390
+ definitions() {
391
+ return {
392
+ variables: [...this.variables.entries()]
393
+ .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
394
+ .map(([name, held]) => ({
395
+ name,
396
+ type: held.type,
397
+ typeName: formatType(held.type, true),
398
+ constant: held.constant,
399
+ ...(held.description === undefined ? {} : { description: held.description }),
400
+ })),
401
+ functions: this.registry.list().map((entry) => ({
402
+ name: entry.signature.name,
403
+ signature: formatSignature(entry.signature),
404
+ receiverType: entry.signature.receiver ? formatType(entry.signature.receiver) : null,
405
+ parameters: entry.signature.parameters.map((type) => formatType(type)),
406
+ returns: formatType(entry.signature.returns),
407
+ deterministic: entry.metadata.deterministic ?? true,
408
+ hostBacked: entry.metadata.hostBacked ?? false,
409
+ ...(entry.metadata.throws === undefined ? {} : { throws: entry.metadata.throws }),
410
+ ...(entry.metadata.description === undefined ? {} : { description: entry.metadata.description }),
411
+ ...(entry.metadata.origin === undefined ? {} : { origin: entry.metadata.origin }),
412
+ })),
413
+ types: [...this.types.values()].map((registered) => ({
414
+ name: registered.definition.name,
415
+ base: formatType(registered.base),
416
+ parameters: registered.parameters,
417
+ ...(registered.definition.description === undefined
418
+ ? {}
419
+ : { description: registered.definition.description }),
420
+ })),
421
+ namespaces: this.namespaces().map((name) => ({
422
+ name,
423
+ open: this.openNamespaces.has(name),
424
+ // A declaration whose parameter list is withheld has no signature text, so it is
425
+ // listed by what it DOES declare — its name and its result. Printing an invented
426
+ // empty parameter list would read as a function of no arguments.
427
+ functions: [...this.namespaceFunctions.get(name).values()].map((held) => held.signature ?? `${held.name}(…): ${formatType(held.returns)}`),
428
+ })),
429
+ };
430
+ }
431
+ // --- types --------------------------------------------------------------
432
+ nominalResolver = (name, args) => {
433
+ const registered = this.types.get(name);
434
+ if (!registered)
435
+ return undefined;
436
+ if (args.length === 0)
437
+ return registered.self;
438
+ if (args.length !== registered.parameters.length) {
439
+ throw new CelTypeRegistrationError(`${name} takes ${registered.parameters.length} type argument${registered.parameters.length === 1 ? "" : "s"}`);
440
+ }
441
+ return { ...registered.self, args };
442
+ };
443
+ readType(text) {
444
+ return parseTypeExpression(text, this.nominalResolver);
445
+ }
446
+ signatureOf(signature) {
447
+ return typeof signature === "string" ? parseSignature(signature, this.nominalResolver) : signature;
448
+ }
449
+ /** The type a declaration names: a type expression, a field map, or a schema. */
450
+ typeOf(declaration) {
451
+ if (typeof declaration === "string")
452
+ return { type: this.readType(declaration) };
453
+ if ("kind" in declaration)
454
+ return { type: declaration };
455
+ if ("fields" in declaration) {
456
+ return { type: fieldMapType(declaration.fields, (text) => this.readType(text)) };
457
+ }
458
+ if ("schema" in declaration) {
459
+ const converted = schemaType({
460
+ node: declaration.schema,
461
+ ...(declaration.document === undefined ? {} : { root: declaration.document }),
462
+ }, {
463
+ ...(this.options.resolveSchemaType ? { resolveSchemaType: this.options.resolveSchemaType } : {}),
464
+ lookupNamedType: (name, args) => this.nominalResolver(name, args.map((text) => this.readType(text))),
465
+ });
466
+ return {
467
+ type: converted.type,
468
+ ...(converted.unjudged.length === 0 ? {} : { unjudged: converted.unjudged }),
469
+ ...(converted.recursive.length === 0 ? {} : { recursive: converted.recursive }),
470
+ };
471
+ }
472
+ throw new CelTypeRegistrationError("a declaration is a type expression, a field map or a schema");
473
+ }
474
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Loading the function catalog into an environment.
3
+ *
4
+ * The catalog is the **dialect**: the functions a manifest may call beside CEL's own
5
+ * library. Like the standard library it is **data** (`signatures/function-catalog.json`)
6
+ * read through the same registration surface a host uses — here literally the public one,
7
+ * `CelEnvironment.registerFunction`, with no privileged door of any kind. A host may
8
+ * register the catalog, leave it out, replace one of its signatures or remove a name, and
9
+ * nothing in the engine behaves differently for it.
10
+ *
11
+ * **One surface, read by everything that lists the catalog.** `functionCatalog()` answers
12
+ * each function with its display signature, its category, its summary and its two flags,
13
+ * which is exactly what a `functions` listing prints; a consumer never reconstructs that
14
+ * from registrations.
15
+ *
16
+ * What a function DOES is beside it, per runtime — `catalog-runtime.ts`, keyed by the same
17
+ * dispatch key the registry resolves on. A declared signature with no behaviour there is
18
+ * refused at registration rather than at evaluation, which is the failure class the two
19
+ * artifacts exist to remove.
20
+ */
21
+ import type { CelCatalogHandlers } from "./catalog-runtime.js";
22
+ import type { CelEnvironment } from "./environment.js";
23
+ export declare const FUNCTION_CATALOG_GENERATION: number;
24
+ /** The categories the catalog groups its functions under, for a grouped listing. */
25
+ export declare function catalogCategories(): readonly string[];
26
+ /** One function of the catalog, as a listing prints it. */
27
+ export interface CatalogFunction {
28
+ readonly name: string;
29
+ /**
30
+ * The signature a human reads, which is not always one registration: an optional
31
+ * parameter is written `fn(string?): string` and registers one signature per arity.
32
+ */
33
+ readonly signature: string;
34
+ /** Every signature the function registers, in registration order. */
35
+ readonly signatures: readonly string[];
36
+ readonly category: string;
37
+ readonly summary: string;
38
+ /** False where two calls with the same arguments may answer differently. */
39
+ readonly deterministic: boolean;
40
+ /** Whether the implementation is the host's rather than the engine's. */
41
+ readonly hostBacked: boolean;
42
+ /** Whether the function refuses an argument written as a literal, at check. */
43
+ readonly checksLiteralArguments: boolean;
44
+ }
45
+ /** The catalog, as the one surface a listing reads. */
46
+ export declare function functionCatalog(): readonly CatalogFunction[];
47
+ /** Every signature the catalog registers, for a validator and for a docs listing. */
48
+ export declare function catalogSignatures(): readonly string[];
49
+ export interface RegisterCatalogOptions {
50
+ /**
51
+ * The implementations of the nine host-backed functions. Each may be left out — the
52
+ * registration is still made, so an expression calling it still type-checks, and
53
+ * evaluating one answers an `unbound_function` error naming the function. That is what
54
+ * a consumer which only ever CHECKS (an analyzer, an editor) needs.
55
+ */
56
+ readonly handlers?: Partial<CelCatalogHandlers>;
57
+ }
58
+ /**
59
+ * Registers the catalog onto an environment, through the public registration surface.
60
+ *
61
+ * A signature the runtime table does not answer for is refused here: a declaration with
62
+ * no behaviour type-checks and then fails at evaluation, which is precisely the failure
63
+ * the declaration/behaviour split exists to catch at registration instead.
64
+ */
65
+ export declare function registerFunctionCatalog(environment: CelEnvironment, options?: RegisterCatalogOptions): void;
66
+ //# sourceMappingURL=function-catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"function-catalog.d.ts","sourceRoot":"","sources":["../src/function-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAwBvD,eAAO,MAAM,2BAA2B,QAAkB,CAAC;AAE3D,oFAAoF;AACpF,wBAAgB,iBAAiB,IAAI,SAAS,MAAM,EAAE,CAErD;AAED,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4EAA4E;IAC5E,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;CAC1C;AAED,uDAAuD;AACvD,wBAAgB,eAAe,IAAI,SAAS,eAAe,EAAE,CAW5D;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,IAAI,SAAS,MAAM,EAAE,CAErD;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACjD;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,cAAc,EAC3B,OAAO,GAAE,sBAA2B,GACnC,IAAI,CAyBN"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Loading the function catalog into an environment.
3
+ *
4
+ * The catalog is the **dialect**: the functions a manifest may call beside CEL's own
5
+ * library. Like the standard library it is **data** (`signatures/function-catalog.json`)
6
+ * read through the same registration surface a host uses — here literally the public one,
7
+ * `CelEnvironment.registerFunction`, with no privileged door of any kind. A host may
8
+ * register the catalog, leave it out, replace one of its signatures or remove a name, and
9
+ * nothing in the engine behaves differently for it.
10
+ *
11
+ * **One surface, read by everything that lists the catalog.** `functionCatalog()` answers
12
+ * each function with its display signature, its category, its summary and its two flags,
13
+ * which is exactly what a `functions` listing prints; a consumer never reconstructs that
14
+ * from registrations.
15
+ *
16
+ * What a function DOES is beside it, per runtime — `catalog-runtime.ts`, keyed by the same
17
+ * dispatch key the registry resolves on. A declared signature with no behaviour there is
18
+ * refused at registration rather than at evaluation, which is the failure class the two
19
+ * artifacts exist to remove.
20
+ */
21
+ import data from "./signatures/function-catalog.json" with { type: "json" };
22
+ import { catalogImplementation, catalogLiteralCheck } from "./catalog-runtime.js";
23
+ import { parseSignature, signatureKey } from "./signature.js";
24
+ const DATA = data;
25
+ export const FUNCTION_CATALOG_GENERATION = DATA.generation;
26
+ /** The categories the catalog groups its functions under, for a grouped listing. */
27
+ export function catalogCategories() {
28
+ return DATA.categories;
29
+ }
30
+ /** The catalog, as the one surface a listing reads. */
31
+ export function functionCatalog() {
32
+ return DATA.functions.map((entry) => ({
33
+ name: entry.name,
34
+ signature: entry.signature,
35
+ signatures: entry.signatures,
36
+ category: entry.category,
37
+ summary: entry.summary,
38
+ deterministic: entry.deterministic,
39
+ hostBacked: entry.hostBacked,
40
+ checksLiteralArguments: entry.checksLiteralArguments ?? false,
41
+ }));
42
+ }
43
+ /** Every signature the catalog registers, for a validator and for a docs listing. */
44
+ export function catalogSignatures() {
45
+ return DATA.functions.flatMap((entry) => entry.signatures);
46
+ }
47
+ /**
48
+ * Registers the catalog onto an environment, through the public registration surface.
49
+ *
50
+ * A signature the runtime table does not answer for is refused here: a declaration with
51
+ * no behaviour type-checks and then fails at evaluation, which is precisely the failure
52
+ * the declaration/behaviour split exists to catch at registration instead.
53
+ */
54
+ export function registerFunctionCatalog(environment, options = {}) {
55
+ const handlers = options.handlers ?? {};
56
+ for (const entry of DATA.functions) {
57
+ const check = entry.checksLiteralArguments ? catalogLiteralCheck(entry.name) : undefined;
58
+ if (entry.checksLiteralArguments && check === undefined) {
59
+ throw new Error(`the catalog declares that ${entry.name} checks its literal arguments, and no guard answers for it`);
60
+ }
61
+ for (const signature of entry.signatures) {
62
+ const key = signatureKey(parseSignature(signature));
63
+ const implementation = catalogImplementation(key, handlers);
64
+ if (implementation === undefined) {
65
+ throw new Error(`the catalog declares ${signature} and nothing implements ${key}`);
66
+ }
67
+ environment.registerFunction(signature, {
68
+ implementation,
69
+ deterministic: entry.deterministic,
70
+ hostBacked: entry.hostBacked,
71
+ description: entry.summary,
72
+ origin: "function-catalog",
73
+ ...(check === undefined ? {} : { checkArguments: check }),
74
+ });
75
+ }
76
+ }
77
+ }