@telorun/cel 0.107.0 → 0.109.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend-runtime.d.ts +46 -8
- package/dist/backend-runtime.d.ts.map +1 -1
- package/dist/backend-runtime.js +133 -30
- package/dist/catalog-runtime.d.ts.map +1 -1
- package/dist/catalog-runtime.js +110 -109
- package/dist/cel-map-value.d.ts +20 -7
- package/dist/cel-map-value.d.ts.map +1 -1
- package/dist/cel-map-value.js +29 -18
- package/dist/cel-value.d.ts +10 -3
- package/dist/cel-value.d.ts.map +1 -1
- package/dist/check-diagnostic.d.ts +3 -2
- package/dist/check-diagnostic.d.ts.map +1 -1
- package/dist/checker.d.ts +9 -0
- package/dist/checker.d.ts.map +1 -1
- package/dist/checker.js +98 -13
- package/dist/closure-backend.js +66 -15
- package/dist/duration-value.d.ts +11 -0
- package/dist/duration-value.d.ts.map +1 -1
- package/dist/duration-value.js +17 -3
- package/dist/emitted-module.d.ts +1 -1
- package/dist/emitted-module.js +1 -1
- package/dist/engine-version.d.ts +1 -1
- package/dist/engine-version.js +1 -1
- package/dist/environment.d.ts +8 -0
- package/dist/environment.d.ts.map +1 -1
- package/dist/environment.js +20 -5
- package/dist/function-registry.d.ts +40 -0
- package/dist/function-registry.d.ts.map +1 -1
- package/dist/function-registry.js +105 -2
- package/dist/index.d.ts +10 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -6
- package/dist/js-emitter.d.ts +3 -2
- package/dist/js-emitter.d.ts.map +1 -1
- package/dist/js-emitter.js +11 -5
- package/dist/macro-check.d.ts +8 -0
- package/dist/macro-check.d.ts.map +1 -1
- package/dist/macro-check.js +1 -1
- package/dist/member-read.d.ts +42 -5
- package/dist/member-read.d.ts.map +1 -1
- package/dist/member-read.js +47 -6
- package/dist/resolved-call.d.ts +13 -0
- package/dist/resolved-call.d.ts.map +1 -1
- package/dist/runtime-library.d.ts +13 -2
- package/dist/runtime-library.d.ts.map +1 -1
- package/dist/runtime-library.js +134 -125
- package/dist/serializer.d.ts.map +1 -1
- package/dist/serializer.js +20 -4
- package/dist/timestamp-value.d.ts +9 -0
- package/dist/timestamp-value.d.ts.map +1 -1
- package/dist/timestamp-value.js +15 -0
- package/dist/type-expression.d.ts +18 -2
- package/dist/type-expression.d.ts.map +1 -1
- package/dist/type-expression.js +23 -3
- package/dist/value-equality.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/backend-runtime.ts +180 -30
- package/src/catalog-runtime.ts +133 -133
- package/src/cel-map-value.ts +31 -20
- package/src/cel-value.ts +11 -3
- package/src/check-diagnostic.ts +4 -2
- package/src/checker.ts +123 -10
- package/src/closure-backend.ts +59 -14
- package/src/duration-value.ts +18 -3
- package/src/emitted-module.ts +1 -1
- package/src/engine-version.ts +1 -1
- package/src/environment.ts +26 -5
- package/src/function-registry.ts +114 -2
- package/src/index.ts +24 -3
- package/src/js-emitter.ts +13 -5
- package/src/macro-check.ts +7 -1
- package/src/member-read.ts +48 -7
- package/src/resolved-call.ts +13 -0
- package/src/runtime-library.ts +167 -143
- package/src/serializer.ts +19 -3
- package/src/timestamp-value.ts +16 -0
- package/src/type-expression.ts +25 -3
- package/src/value-equality.ts +3 -3
package/src/index.ts
CHANGED
|
@@ -143,7 +143,7 @@ export type {
|
|
|
143
143
|
VariableDefinition,
|
|
144
144
|
} from "./environment.js";
|
|
145
145
|
|
|
146
|
-
export { FunctionRegistry } from "./function-registry.js";
|
|
146
|
+
export { FunctionRegistry, UNKNOWN_FUNCTION_CANDIDATES } from "./function-registry.js";
|
|
147
147
|
export type { RegisteredFunction, Resolution, ResolutionFailure } from "./function-registry.js";
|
|
148
148
|
|
|
149
149
|
export {
|
|
@@ -225,7 +225,11 @@ export {
|
|
|
225
225
|
} from "./zoned-calendar.js";
|
|
226
226
|
export type { CivilTime } from "./zoned-calendar.js";
|
|
227
227
|
|
|
228
|
-
export {
|
|
228
|
+
export {
|
|
229
|
+
CelTypeExpressionError,
|
|
230
|
+
CelUnknownTypeNameError,
|
|
231
|
+
parseTypeExpression,
|
|
232
|
+
} from "./type-expression.js";
|
|
229
233
|
export type { NominalResolver } from "./type-expression.js";
|
|
230
234
|
|
|
231
235
|
// --- the value domain, the semantics and the closure backend ----------------
|
|
@@ -234,7 +238,10 @@ export type { CelActivation } from "./activation.js";
|
|
|
234
238
|
|
|
235
239
|
export { BoundedCache } from "./bounded-cache.js";
|
|
236
240
|
|
|
237
|
-
|
|
241
|
+
// `mapKeyIdentity` is deliberately NOT exported: what identifies an entry is the entries
|
|
242
|
+
// map's own business, and publishing the rule is what let a consumer grow a second copy of
|
|
243
|
+
// it. A host reads a map through the member-read seam and walks `entries` for the pairs.
|
|
244
|
+
export { celMapFromEntries, celMapKeys } from "./cel-map-value.js";
|
|
238
245
|
|
|
239
246
|
export { CelEvaluationError, programOfStep } from "./cel-program.js";
|
|
240
247
|
export type { CelProgram, EvaluateOptions } from "./cel-program.js";
|
|
@@ -295,6 +302,7 @@ export type {
|
|
|
295
302
|
CelEvaluationCode,
|
|
296
303
|
CelHostValue,
|
|
297
304
|
CelMap,
|
|
305
|
+
CelMapKey,
|
|
298
306
|
CelMapValueEntry,
|
|
299
307
|
CelOptional,
|
|
300
308
|
CelRecord,
|
|
@@ -314,6 +322,7 @@ export type {
|
|
|
314
322
|
CompileTarget,
|
|
315
323
|
EvaluationFrame,
|
|
316
324
|
NamespaceDispatch,
|
|
325
|
+
NamespaceImplementation,
|
|
317
326
|
} from "./backend-runtime.js";
|
|
318
327
|
|
|
319
328
|
export {
|
|
@@ -329,6 +338,7 @@ export {
|
|
|
329
338
|
celDurationFromNanos,
|
|
330
339
|
durationField,
|
|
331
340
|
durationNanos,
|
|
341
|
+
durationNanosFromText,
|
|
332
342
|
durationOutOfRange,
|
|
333
343
|
formatDuration,
|
|
334
344
|
MAX_DURATION_NANOS,
|
|
@@ -341,6 +351,7 @@ export {
|
|
|
341
351
|
celIterable,
|
|
342
352
|
celLookup,
|
|
343
353
|
celRead,
|
|
354
|
+
lookupAbsence,
|
|
344
355
|
lookupError,
|
|
345
356
|
MISSING,
|
|
346
357
|
OUT_OF_RANGE,
|
|
@@ -369,9 +380,12 @@ export {
|
|
|
369
380
|
typeValueName,
|
|
370
381
|
} from "./runtime-library.js";
|
|
371
382
|
export type { CelCallContext, CelImplementation } from "./runtime-library.js";
|
|
383
|
+
// The bound a host writing an implementation is held to, and what the refusal names.
|
|
384
|
+
export { CALL_SITE_DIRECT_ARITY } from "./runtime-library.js";
|
|
372
385
|
|
|
373
386
|
export {
|
|
374
387
|
celTimestamp,
|
|
388
|
+
celTimestampFromMillis,
|
|
375
389
|
formatTimestamp,
|
|
376
390
|
MAX_TIMESTAMP_SECONDS,
|
|
377
391
|
MIN_TIMESTAMP_SECONDS,
|
|
@@ -384,3 +398,10 @@ export {
|
|
|
384
398
|
export { celCompare, celEqual } from "./value-equality.js";
|
|
385
399
|
|
|
386
400
|
export { bytesToText, doubleText, textToBytes } from "./value-text.js";
|
|
401
|
+
|
|
402
|
+
export {
|
|
403
|
+
BINDING_FORMS,
|
|
404
|
+
namespaceMacroBinding,
|
|
405
|
+
receiverMacroBinding,
|
|
406
|
+
type ComprehensionBinding,
|
|
407
|
+
} from "./comprehension-bindings.js";
|
package/src/js-emitter.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
import { namespaceMacroBinding, receiverMacroBinding } from "./comprehension-bindings.js";
|
|
37
37
|
import { splitDeclaredChain } from "./declared-chain.js";
|
|
38
38
|
import { isMacroCall } from "./macro-check.js";
|
|
39
|
+
import { CALL_SITE_DIRECT_ARITY } from "./runtime-library.js";
|
|
39
40
|
import type { CelLiteral, CelNode, CelSelectNode, SourceRange } from "./syntax-tree.js";
|
|
40
41
|
|
|
41
42
|
/**
|
|
@@ -347,8 +348,8 @@ export class ModuleEmitter {
|
|
|
347
348
|
const kept = entry.optional ? fn.next() : undefined;
|
|
348
349
|
const written = kept
|
|
349
350
|
? `(${kept} = optionalEntry(${value}, ${at}), isCelError(${kept}) ? ${kept} : ` +
|
|
350
|
-
`(${kept}.present && ${out}.push(
|
|
351
|
-
: `(${out}.push(
|
|
351
|
+
`(${kept}.present && ${out}.push(${key}, ${kept}.held), ${body}))`
|
|
352
|
+
: `(${out}.push(${key}, ${value}), ${body})`;
|
|
352
353
|
body =
|
|
353
354
|
`(${key} = ${entry.key}, isCelError(${key}) ? ${key} : ` +
|
|
354
355
|
`(${value} = ${entry.value}, isCelError(${value}) ? ${value} : ${written}))`;
|
|
@@ -427,8 +428,9 @@ export class ModuleEmitter {
|
|
|
427
428
|
|
|
428
429
|
/**
|
|
429
430
|
* One dispatch: evaluate the arguments, carry the first error out, then hand the values to
|
|
430
|
-
* the site
|
|
431
|
-
* overloads it resolved — exactly as a compiled closure's does.
|
|
431
|
+
* the site **positionally**. The site is hoisted, so it is built once per loaded module and
|
|
432
|
+
* holds the overloads it resolved — exactly as a compiled closure's does. The arity is the
|
|
433
|
+
* dispatch key's, so the entry point is chosen here and no argument array is built.
|
|
432
434
|
*/
|
|
433
435
|
private call(
|
|
434
436
|
name: string,
|
|
@@ -440,7 +442,13 @@ export class ModuleEmitter {
|
|
|
440
442
|
const site = this.hoist(
|
|
441
443
|
`callSite(${textSource(name)}, ${textSource(form)}, ${this.range(range)})`,
|
|
442
444
|
);
|
|
443
|
-
|
|
445
|
+
// A call written wider than the bound takes the array form — the arity is the SOURCE's,
|
|
446
|
+
// so a width no signature can declare is still something an author may write.
|
|
447
|
+
return this.carrying(args, fn, (values) =>
|
|
448
|
+
values.length <= CALL_SITE_DIRECT_ARITY
|
|
449
|
+
? `${site}.call${values.length}(${values.join(", ")})`
|
|
450
|
+
: `${site}.call([${values.join(", ")}])`,
|
|
451
|
+
);
|
|
444
452
|
}
|
|
445
453
|
|
|
446
454
|
private qualifiedCall(
|
package/src/macro-check.ts
CHANGED
|
@@ -25,6 +25,12 @@ export interface MacroHost {
|
|
|
25
25
|
typeOf(node: CelNode): CelType;
|
|
26
26
|
/** The type of a subexpression with extra names in scope. */
|
|
27
27
|
typeOfBinding(node: CelNode, bindings: ReadonlyMap<string, CelType>): CelType;
|
|
28
|
+
/**
|
|
29
|
+
* The type of a select read as a question about PRESENCE — `has()`'s argument. It is
|
|
30
|
+
* the same reading `.?b` gets, which is what keeps the two forms of one question from
|
|
31
|
+
* answering differently about a union whose branches do not all hold the member.
|
|
32
|
+
*/
|
|
33
|
+
typeOfPresence(node: Extract<CelNode, { kind: "select" }>): CelType;
|
|
28
34
|
report(code: CelCheckCode, message: string, range: SourceRange): void;
|
|
29
35
|
}
|
|
30
36
|
|
|
@@ -99,7 +105,7 @@ function checkOptionalBinding(node: Extract<CelNode, { kind: "receiverCall" }>,
|
|
|
99
105
|
/** `has(a.b)` — a question about presence. Its shape is already validated. */
|
|
100
106
|
function checkHas(node: Extract<CelNode, { kind: "call" }>, host: MacroHost): CelType {
|
|
101
107
|
const argument = node.args[0]!;
|
|
102
|
-
if (argument.kind === "select") host.
|
|
108
|
+
if (argument.kind === "select") host.typeOfPresence(argument);
|
|
103
109
|
return BOOL;
|
|
104
110
|
}
|
|
105
111
|
|
package/src/member-read.ts
CHANGED
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
*
|
|
17
17
|
* A key the value does not hold is the `no_such_key` **error value**, which
|
|
18
18
|
* participates in short-circuit. Never `undefined` passed along.
|
|
19
|
+
*
|
|
20
|
+
* **What each of the four verdicts answers is written once here**, in `lookupAbsence`,
|
|
21
|
+
* and read by both presence callers — so the closure backend, the emitter and the
|
|
22
|
+
* `has()` macro cannot differ by construction.
|
|
19
23
|
*/
|
|
20
24
|
|
|
21
25
|
import { celMapKeys, mapKeyIdentity } from "./cel-map-value.js";
|
|
@@ -86,6 +90,38 @@ export function celRead(container: CelValue, key: CelValue, range?: SourceRange)
|
|
|
86
90
|
return lookupError(found, key, range);
|
|
87
91
|
}
|
|
88
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Whether a lookup that found nothing answers **absence** rather than an error. This is
|
|
95
|
+
* the seam's contract, in the one place both backends and the emitter read it: they all
|
|
96
|
+
* reach a member read through `backend-runtime.ts`, which reaches the four verdicts
|
|
97
|
+
* through here.
|
|
98
|
+
*
|
|
99
|
+
* - A **presence-shaped** read — `a.?b`, `a[?k]` and `has(a.b)` — answers absence for
|
|
100
|
+
* *missing*, *out of range* and *holds no members* alike, and the error only for an
|
|
101
|
+
* **unusable key**. It asks whether a member is THERE, and a value that cannot hold one
|
|
102
|
+
* has none to find. The authority is the optional library's own: it enters this engine
|
|
103
|
+
* from cel-go whole, and cel-go's attribute qualification answers "not found" for a
|
|
104
|
+
* receiver that is neither a mapper, a lister nor an indexer **whenever the read is a
|
|
105
|
+
* presence test**, erroring only otherwise — the error reading being an explicitly named
|
|
106
|
+
* opt-in (`EnableErrorOnBadPresenceTest`), which Telo does not carry, because a
|
|
107
|
+
* per-environment switch over what an expression MEANS would let the analyzer and a
|
|
108
|
+
* kernel disagree about one manifest.
|
|
109
|
+
* - A read **through a present optional, written in the ordinary form** — the `.c` of
|
|
110
|
+
* `a.?b.c` — is not a presence question about `c`. It answers absence for a key the held
|
|
111
|
+
* value does not hold (`optional.of({'c': {}}).c.missing` is absent, cel-spec's
|
|
112
|
+
* `optional_chaining_5`) and the error for a held value that holds no members at all
|
|
113
|
+
* (`{true: dyn(0)}[?true].absent` is cel-spec's error).
|
|
114
|
+
* - A **plain** read never asks: each of the four is the error `lookupError` words.
|
|
115
|
+
*
|
|
116
|
+
* An unusable key is the one refusal no form forgives: `[?3.1]` names an entry no
|
|
117
|
+
* container of that shape could hold, so it is a mistake in the READ rather than a member
|
|
118
|
+
* that happens to be absent.
|
|
119
|
+
*/
|
|
120
|
+
export function lookupAbsence(found: symbol, presence: boolean): boolean {
|
|
121
|
+
if (found === MISSING || found === OUT_OF_RANGE) return true;
|
|
122
|
+
return presence && found === UNSUPPORTED_CONTAINER;
|
|
123
|
+
}
|
|
124
|
+
|
|
89
125
|
/** The error a refused lookup is. */
|
|
90
126
|
export function lookupError(found: symbol, key: CelValue, range?: SourceRange): CelError {
|
|
91
127
|
if (found === MISSING) return celError("no_such_key", `no such key: ${describe(key)}`, range);
|
|
@@ -99,20 +135,25 @@ export function lookupError(found: symbol, key: CelValue, range?: SourceRange):
|
|
|
99
135
|
}
|
|
100
136
|
|
|
101
137
|
/**
|
|
102
|
-
* Whether a key is there — `has(a.b)`.
|
|
103
|
-
* `false`
|
|
104
|
-
*
|
|
138
|
+
* Whether a key is there — `has(a.b)`. The question is presence-shaped, so a value that
|
|
139
|
+
* holds no members answers `false` exactly as a missing key does: it has no member to
|
|
140
|
+
* find. Only an unusable key is a mistake in the question itself.
|
|
105
141
|
*/
|
|
106
142
|
export function celHas(container: CelValue, key: CelValue, range?: SourceRange): boolean | CelError {
|
|
107
143
|
const found = celLookup(container, key);
|
|
108
144
|
if (typeof found !== "symbol") return true;
|
|
109
|
-
if (found
|
|
145
|
+
if (lookupAbsence(found, true)) return false;
|
|
110
146
|
return lookupError(found, key, range);
|
|
111
147
|
}
|
|
112
148
|
|
|
113
|
-
/**
|
|
114
|
-
|
|
115
|
-
|
|
149
|
+
/**
|
|
150
|
+
* The elements a comprehension ranges over: a list's items, a map's keys. A list is handed
|
|
151
|
+
* over as it is — nothing in CEL mutates a value, and every comprehension reads its range —
|
|
152
|
+
* so copying it would cost the length of the list per comprehension to defend against a
|
|
153
|
+
* write no expression can perform.
|
|
154
|
+
*/
|
|
155
|
+
export function celIterable(container: CelValue, range?: SourceRange): readonly CelValue[] | CelError {
|
|
156
|
+
if (Array.isArray(container)) return container as readonly CelValue[];
|
|
116
157
|
if (isCelMap(container)) return celMapKeys(container);
|
|
117
158
|
if (isCelRecord(container)) return Object.keys(container);
|
|
118
159
|
return celError("unsupported_container", "a comprehension reads a list or a map", range);
|
package/src/resolved-call.ts
CHANGED
|
@@ -31,4 +31,17 @@ export interface ResolvedCall {
|
|
|
31
31
|
readonly deterministic?: boolean;
|
|
32
32
|
readonly hostBacked?: boolean;
|
|
33
33
|
readonly throws?: readonly string[];
|
|
34
|
+
/**
|
|
35
|
+
* Each argument's own type, for a NAMESPACED call only.
|
|
36
|
+
*
|
|
37
|
+
* The host judges such a call against its own, richer signature grammar — a JSON Schema
|
|
38
|
+
* per parameter, an optional trailing parameter — so it needs what was actually passed.
|
|
39
|
+
* It cannot re-derive it: checking an argument's subtree on its own loses whatever the
|
|
40
|
+
* expression bound around it, so an argument inside a comprehension (`xs.map(i,
|
|
41
|
+
* Billing.total(i))`) would type as `dyn` and the host's argument check would silently
|
|
42
|
+
* stop asking. Here the types come from the same pass that typed the call.
|
|
43
|
+
*
|
|
44
|
+
* Absent for a call the registry resolved: its parameter types are the signature's.
|
|
45
|
+
*/
|
|
46
|
+
readonly argumentTypes?: readonly string[];
|
|
34
47
|
}
|