@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.
- package/LICENSE +17 -0
- package/README.md +281 -2
- package/dist/activation.d.ts +27 -0
- package/dist/activation.d.ts.map +1 -0
- package/dist/activation.js +24 -0
- package/dist/backend-runtime.d.ts +184 -0
- package/dist/backend-runtime.d.ts.map +1 -0
- package/dist/backend-runtime.js +425 -0
- package/dist/bounded-cache.d.ts +21 -0
- package/dist/bounded-cache.d.ts.map +1 -0
- package/dist/bounded-cache.js +42 -0
- package/dist/catalog-runtime.d.ts +59 -0
- package/dist/catalog-runtime.d.ts.map +1 -0
- package/dist/catalog-runtime.js +786 -0
- package/dist/cel-expression.d.ts +32 -0
- package/dist/cel-expression.d.ts.map +1 -0
- package/dist/cel-expression.js +29 -0
- package/dist/cel-map-value.d.ts +47 -0
- package/dist/cel-map-value.d.ts.map +1 -0
- package/dist/cel-map-value.js +85 -0
- package/dist/cel-program.d.ts +44 -0
- package/dist/cel-program.d.ts.map +1 -0
- package/dist/cel-program.js +72 -0
- package/dist/cel-type.d.ts +131 -0
- package/dist/cel-type.d.ts.map +1 -0
- package/dist/cel-type.js +293 -0
- package/dist/cel-value.d.ts +166 -0
- package/dist/cel-value.d.ts.map +1 -0
- package/dist/cel-value.js +225 -0
- package/dist/check-diagnostic.d.ts +54 -0
- package/dist/check-diagnostic.d.ts.map +1 -0
- package/dist/check-diagnostic.js +66 -0
- package/dist/checker.d.ts +86 -0
- package/dist/checker.d.ts.map +1 -0
- package/dist/checker.js +806 -0
- package/dist/closure-backend.d.ts +21 -0
- package/dist/closure-backend.d.ts.map +1 -0
- package/dist/closure-backend.js +487 -0
- package/dist/comprehension-bindings.d.ts +33 -0
- package/dist/comprehension-bindings.d.ts.map +1 -0
- package/dist/comprehension-bindings.js +50 -0
- package/dist/comprehension-runtime.d.ts +44 -0
- package/dist/comprehension-runtime.d.ts.map +1 -0
- package/dist/comprehension-runtime.js +137 -0
- package/dist/declared-chain.d.ts +35 -0
- package/dist/declared-chain.d.ts.map +1 -0
- package/dist/declared-chain.js +36 -0
- package/dist/duration-value.d.ts +70 -0
- package/dist/duration-value.d.ts.map +1 -0
- package/dist/duration-value.js +149 -0
- package/dist/emitted-module.d.ts +207 -0
- package/dist/emitted-module.d.ts.map +1 -0
- package/dist/emitted-module.js +359 -0
- package/dist/engine-version.d.ts +3 -0
- package/dist/engine-version.d.ts.map +1 -0
- package/dist/engine-version.js +8 -0
- package/dist/environment-digest.d.ts +44 -0
- package/dist/environment-digest.d.ts.map +1 -0
- package/dist/environment-digest.js +98 -0
- package/dist/environment.d.ts +291 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +474 -0
- package/dist/function-catalog.d.ts +66 -0
- package/dist/function-catalog.d.ts.map +1 -0
- package/dist/function-catalog.js +77 -0
- package/dist/function-registry.d.ts +118 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +292 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +65 -0
- package/dist/integer-arithmetic.d.ts +27 -0
- package/dist/integer-arithmetic.d.ts.map +1 -0
- package/dist/integer-arithmetic.js +58 -0
- package/dist/js-emitter.d.ts +133 -0
- package/dist/js-emitter.d.ts.map +1 -0
- package/dist/js-emitter.js +568 -0
- package/dist/json-schema-type.d.ts +182 -0
- package/dist/json-schema-type.d.ts.map +1 -0
- package/dist/json-schema-type.js +487 -0
- package/dist/json-text-scan.d.ts +28 -0
- package/dist/json-text-scan.d.ts.map +1 -0
- package/dist/json-text-scan.js +159 -0
- package/dist/lexer.d.ts +103 -0
- package/dist/lexer.d.ts.map +1 -0
- package/dist/lexer.js +458 -0
- package/dist/macro-check.d.ts +41 -0
- package/dist/macro-check.d.ts.map +1 -0
- package/dist/macro-check.js +162 -0
- package/dist/macro-shape.d.ts +24 -0
- package/dist/macro-shape.d.ts.map +1 -0
- package/dist/macro-shape.js +55 -0
- package/dist/member-read.d.ts +89 -0
- package/dist/member-read.d.ts.map +1 -0
- package/dist/member-read.js +166 -0
- package/dist/namespace-resolution.d.ts +35 -0
- package/dist/namespace-resolution.d.ts.map +1 -0
- package/dist/namespace-resolution.js +160 -0
- package/dist/nominal-type.d.ts +63 -0
- package/dist/nominal-type.d.ts.map +1 -0
- package/dist/nominal-type.js +98 -0
- package/dist/nullable-access.d.ts +38 -0
- package/dist/nullable-access.d.ts.map +1 -0
- package/dist/nullable-access.js +93 -0
- package/dist/parse-limits.d.ts +26 -0
- package/dist/parse-limits.d.ts.map +1 -0
- package/dist/parse-limits.js +21 -0
- package/dist/parser.d.ts +48 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +503 -0
- package/dist/qualified-calls.d.ts +22 -0
- package/dist/qualified-calls.d.ts.map +1 -0
- package/dist/qualified-calls.js +27 -0
- package/dist/regular-expression.d.ts +48 -0
- package/dist/regular-expression.d.ts.map +1 -0
- package/dist/regular-expression.js +77 -0
- package/dist/reserved-words.d.ts +52 -0
- package/dist/reserved-words.d.ts.map +1 -0
- package/dist/reserved-words.js +77 -0
- package/dist/resolved-call.d.ts +46 -0
- package/dist/resolved-call.d.ts.map +1 -0
- package/dist/resolved-call.js +15 -0
- package/dist/root-references.d.ts +23 -0
- package/dist/root-references.d.ts.map +1 -0
- package/dist/root-references.js +120 -0
- package/dist/runtime-library.d.ts +67 -0
- package/dist/runtime-library.d.ts.map +1 -0
- package/dist/runtime-library.js +554 -0
- package/dist/serializer.d.ts +24 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/serializer.js +256 -0
- package/dist/sha256.d.ts +20 -0
- package/dist/sha256.d.ts.map +1 -0
- package/dist/sha256.js +103 -0
- package/dist/signature.d.ts +72 -0
- package/dist/signature.d.ts.map +1 -0
- package/dist/signature.js +61 -0
- package/dist/signatures/function-catalog.json +788 -0
- package/dist/signatures/standard-library.json +229 -0
- package/dist/standard-library.d.ts +41 -0
- package/dist/standard-library.d.ts.map +1 -0
- package/dist/standard-library.js +85 -0
- package/dist/syntax-diagnostic.d.ts +61 -0
- package/dist/syntax-diagnostic.d.ts.map +1 -0
- package/dist/syntax-diagnostic.js +28 -0
- package/dist/syntax-tree.d.ts +160 -0
- package/dist/syntax-tree.d.ts.map +1 -0
- package/dist/syntax-tree.js +58 -0
- package/dist/timestamp-value.d.ts +62 -0
- package/dist/timestamp-value.d.ts.map +1 -0
- package/dist/timestamp-value.js +238 -0
- package/dist/tree-equality.d.ts +15 -0
- package/dist/tree-equality.d.ts.map +1 -0
- package/dist/tree-equality.js +105 -0
- package/dist/type-expression.d.ts +42 -0
- package/dist/type-expression.d.ts.map +1 -0
- package/dist/type-expression.js +154 -0
- package/dist/value-equality.d.ts +38 -0
- package/dist/value-equality.d.ts.map +1 -0
- package/dist/value-equality.js +196 -0
- package/dist/value-text.d.ts +25 -0
- package/dist/value-text.d.ts.map +1 -0
- package/dist/value-text.js +44 -0
- package/dist/zoned-calendar.d.ts +61 -0
- package/dist/zoned-calendar.d.ts.map +1 -0
- package/dist/zoned-calendar.js +143 -0
- package/package.json +56 -3
- package/src/activation.ts +32 -0
- package/src/backend-runtime.ts +604 -0
- package/src/bounded-cache.ts +45 -0
- package/src/catalog-runtime.ts +921 -0
- package/src/cel-expression.ts +53 -0
- package/src/cel-map-value.ts +97 -0
- package/src/cel-program.ts +103 -0
- package/src/cel-type.ts +359 -0
- package/src/cel-value.ts +361 -0
- package/src/check-diagnostic.ts +104 -0
- package/src/checker.ts +1045 -0
- package/src/closure-backend.ts +547 -0
- package/src/comprehension-bindings.ts +66 -0
- package/src/comprehension-runtime.ts +157 -0
- package/src/declared-chain.ts +45 -0
- package/src/duration-value.ts +160 -0
- package/src/emitted-module.ts +494 -0
- package/src/engine-version.ts +9 -0
- package/src/environment-digest.ts +111 -0
- package/src/environment.ts +761 -0
- package/src/function-catalog.ts +140 -0
- package/src/function-registry.ts +341 -0
- package/src/index.ts +407 -0
- package/src/integer-arithmetic.ts +64 -0
- package/src/js-emitter.ts +721 -0
- package/src/json-schema-type.ts +664 -0
- package/src/json-text-scan.ts +163 -0
- package/src/lexer.ts +562 -0
- package/src/macro-check.ts +197 -0
- package/src/macro-shape.ts +66 -0
- package/src/member-read.ts +167 -0
- package/src/namespace-resolution.ts +167 -0
- package/src/nominal-type.ts +149 -0
- package/src/nullable-access.ts +95 -0
- package/src/parse-limits.ts +36 -0
- package/src/parser.ts +554 -0
- package/src/qualified-calls.ts +39 -0
- package/src/regular-expression.ts +101 -0
- package/src/reserved-words.ts +94 -0
- package/src/resolved-call.ts +47 -0
- package/src/root-references.ts +126 -0
- package/src/runtime-library.ts +639 -0
- package/src/serializer.ts +262 -0
- package/src/sha256.ts +112 -0
- package/src/signature.ts +127 -0
- package/src/signatures/function-catalog.json +788 -0
- package/src/signatures/standard-library.json +235 -0
- package/src/standard-library.ts +149 -0
- package/src/syntax-diagnostic.ts +72 -0
- package/src/syntax-tree.ts +229 -0
- package/src/timestamp-value.ts +310 -0
- package/src/tree-equality.ts +130 -0
- package/src/type-expression.ts +182 -0
- package/src/value-equality.ts +201 -0
- package/src/value-text.ts +45 -0
- package/src/zoned-calendar.ts +178 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What every operator and standard function **does** — once, for both backends.
|
|
3
|
+
*
|
|
4
|
+
* The declarations are data (`signatures/standard-library.json`); this is the behaviour
|
|
5
|
+
* behind them, keyed by the same dispatch key the registry resolves on. The closure
|
|
6
|
+
* backend calls through here and the emitter will too, so there is exactly one answer
|
|
7
|
+
* per operation and no second copy to drift: a semantics written inside a backend is a
|
|
8
|
+
* semantics each backend gets slightly wrong in its own way.
|
|
9
|
+
*
|
|
10
|
+
* An implementation takes its arguments **already evaluated and already free of
|
|
11
|
+
* errors** — the caller carries an error-valued operand through for the short-circuit
|
|
12
|
+
* rules — and returns a value, which may itself be an error value (an overflow, a
|
|
13
|
+
* missing key, a pattern RE2 refuses). Its return type is not thenable, so an
|
|
14
|
+
* implementation that answers asynchronously does not compile.
|
|
15
|
+
*/
|
|
16
|
+
import { asyncValueRefused, celError, celNone, celSome, celTypeNameOf, celTypeValue, celUint, isCelBytes, isCelDuration, isCelMap, isCelRecord, isCelUint, } from "./cel-value.js";
|
|
17
|
+
import { celDurationFromNanos, durationField, durationNanos, durationOutOfRange, formatDuration, parseDuration, } from "./duration-value.js";
|
|
18
|
+
import { intDivide, intModulo, intResult, uintDivide, uintModulo, uintResult, } from "./integer-arithmetic.js";
|
|
19
|
+
import { MAX_UINT } from "./lexer.js";
|
|
20
|
+
import { celLookup } from "./member-read.js";
|
|
21
|
+
import { celMatches } from "./regular-expression.js";
|
|
22
|
+
import { signatureKey } from "./signature.js";
|
|
23
|
+
import { googleTypeNames } from "./standard-library.js";
|
|
24
|
+
import { celTimestamp, formatTimestamp, parseTimestamp, timestampField, timestampNanos, } from "./timestamp-value.js";
|
|
25
|
+
import { celCompare, celEqual } from "./value-equality.js";
|
|
26
|
+
import { bytesToText, doubleText, textToBytes } from "./value-text.js";
|
|
27
|
+
/** The widest arity a registration declares, and so the number of direct parameters. */
|
|
28
|
+
export const CALL_SITE_DIRECT_ARITY = 4;
|
|
29
|
+
// --- reading arguments the dispatcher has already typed ---------------------
|
|
30
|
+
// Each takes `CelValue | undefined` because a parameter list is as wide as the widest
|
|
31
|
+
// overload; the dispatcher has already resolved on this argument's type, so the cast is
|
|
32
|
+
// what reading it means.
|
|
33
|
+
const text = (value) => value;
|
|
34
|
+
const integer = (value) => value;
|
|
35
|
+
const unsigned = (value) => value.value;
|
|
36
|
+
const double = (value) => value;
|
|
37
|
+
const bytes = (value) => value;
|
|
38
|
+
const list = (value) => value;
|
|
39
|
+
const instant = (value) => value;
|
|
40
|
+
const span = (value) => value;
|
|
41
|
+
const optional = (value) => value;
|
|
42
|
+
/** The argument itself, where what is read of it is that the dispatcher resolved on it. */
|
|
43
|
+
const value = (held) => held;
|
|
44
|
+
/** The code points of text — CEL counts and indexes a string by character. */
|
|
45
|
+
const characters = (value) => [...value];
|
|
46
|
+
// --- conversions -----------------------------------------------------------
|
|
47
|
+
/** The doubles at which an int64 ends in each direction: 2^63 and -2^63. */
|
|
48
|
+
const INT_RANGE_ABOVE = 9223372036854775808;
|
|
49
|
+
const INT_RANGE_BELOW = -9223372036854775808;
|
|
50
|
+
const DECIMAL_INT = /^[+-]?\d+$/;
|
|
51
|
+
const DECIMAL_UINT = /^\+?\d+$/;
|
|
52
|
+
const DECIMAL_DOUBLE = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
53
|
+
function conversionError(what, value, range) {
|
|
54
|
+
return celError("invalid_conversion", `${what} cannot convert ${value}`, range);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* `int(double)`. A double **at or beyond either int64 extreme** is refused, which is
|
|
58
|
+
* cel-spec's rule and the only one under which the two ends behave alike: the double nearest
|
|
59
|
+
* `-9223372036854775808` IS that number exactly, so an engine that refuses only the positive
|
|
60
|
+
* end accepts one extreme and refuses the other for no reason a reader could state.
|
|
61
|
+
*/
|
|
62
|
+
function intFromDouble(value, range) {
|
|
63
|
+
if (!Number.isFinite(value))
|
|
64
|
+
return conversionError("int()", doubleText(value), range);
|
|
65
|
+
if (value >= INT_RANGE_ABOVE || value <= INT_RANGE_BELOW) {
|
|
66
|
+
return celError("numeric_overflow", `integer overflow: ${doubleText(value)}`, range);
|
|
67
|
+
}
|
|
68
|
+
return BigInt(Math.trunc(value));
|
|
69
|
+
}
|
|
70
|
+
function uintFromDouble(value, range) {
|
|
71
|
+
if (!Number.isFinite(value))
|
|
72
|
+
return conversionError("uint()", doubleText(value), range);
|
|
73
|
+
const whole = BigInt(Math.trunc(value));
|
|
74
|
+
if (whole < 0n || whole > MAX_UINT) {
|
|
75
|
+
return celError("numeric_overflow", `unsigned integer overflow: ${whole}`, range);
|
|
76
|
+
}
|
|
77
|
+
return celUint(whole);
|
|
78
|
+
}
|
|
79
|
+
function doubleFromText(value, range) {
|
|
80
|
+
const trimmed = value;
|
|
81
|
+
if (/^[+-]?(?:Inf|Infinity)$/.test(trimmed)) {
|
|
82
|
+
return trimmed.startsWith("-") ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY;
|
|
83
|
+
}
|
|
84
|
+
if (trimmed === "NaN")
|
|
85
|
+
return Number.NaN;
|
|
86
|
+
if (!DECIMAL_DOUBLE.test(trimmed))
|
|
87
|
+
return conversionError("double()", JSON.stringify(value), range);
|
|
88
|
+
return Number(trimmed);
|
|
89
|
+
}
|
|
90
|
+
const BOOL_WORDS = {
|
|
91
|
+
"1": true,
|
|
92
|
+
t: true,
|
|
93
|
+
true: true,
|
|
94
|
+
TRUE: true,
|
|
95
|
+
True: true,
|
|
96
|
+
"0": false,
|
|
97
|
+
f: false,
|
|
98
|
+
false: false,
|
|
99
|
+
FALSE: false,
|
|
100
|
+
False: false,
|
|
101
|
+
};
|
|
102
|
+
/** The name a type value carries. A null's type and an optional's are named apart. */
|
|
103
|
+
export function typeValueName(typeName) {
|
|
104
|
+
if (typeName === "optional")
|
|
105
|
+
return "optional_type";
|
|
106
|
+
return typeName;
|
|
107
|
+
}
|
|
108
|
+
/** The type value of any value — `type()` must answer for anything, including `dyn`. */
|
|
109
|
+
export function celTypeValueOf(value, range) {
|
|
110
|
+
const name = celTypeNameOf(value);
|
|
111
|
+
if (name === undefined) {
|
|
112
|
+
return celError("invalid_conversion", "this value is of no CEL type", range);
|
|
113
|
+
}
|
|
114
|
+
return celTypeValue(typeValueName(name));
|
|
115
|
+
}
|
|
116
|
+
// --- text and bytes --------------------------------------------------------
|
|
117
|
+
const BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
118
|
+
/** Bytes as base64 with the standard alphabet and padding. */
|
|
119
|
+
export function base64Text(input) {
|
|
120
|
+
let out = "";
|
|
121
|
+
for (let at = 0; at < input.length; at += 3) {
|
|
122
|
+
const a = input[at];
|
|
123
|
+
const b = input[at + 1];
|
|
124
|
+
const c = input[at + 2];
|
|
125
|
+
out += BASE64[a >> 2];
|
|
126
|
+
out += BASE64[((a & 3) << 4) | ((b ?? 0) >> 4)];
|
|
127
|
+
out += b === undefined ? "=" : BASE64[((b & 15) << 2) | ((c ?? 0) >> 6)];
|
|
128
|
+
out += c === undefined ? "=" : BASE64[c & 63];
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
function hexText(input) {
|
|
133
|
+
let out = "";
|
|
134
|
+
for (const byte of input)
|
|
135
|
+
out += byte.toString(16).padStart(2, "0");
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* JSON as CEL values: an object is a map, an array a list, a number a double. Exported
|
|
140
|
+
* because the function catalog's `parseJson` answers the same question, and two readings
|
|
141
|
+
* of what JSON becomes would be two value domains.
|
|
142
|
+
*/
|
|
143
|
+
export function jsonAsCelValue(parsed) {
|
|
144
|
+
if (parsed === null || typeof parsed === "boolean" || typeof parsed === "number" || typeof parsed === "string") {
|
|
145
|
+
return parsed;
|
|
146
|
+
}
|
|
147
|
+
if (Array.isArray(parsed))
|
|
148
|
+
return parsed.map(jsonAsCelValue);
|
|
149
|
+
const out = Object.create(null);
|
|
150
|
+
for (const [key, value] of Object.entries(parsed))
|
|
151
|
+
out[key] = jsonAsCelValue(value);
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
/** An index into a string, bounded by its length; an out-of-range one is refused. */
|
|
155
|
+
function boundedIndex(at, length, range) {
|
|
156
|
+
if (at < 0n || at > BigInt(length)) {
|
|
157
|
+
return celError("invalid_argument", `index ${at} is outside the string`, range);
|
|
158
|
+
}
|
|
159
|
+
return Number(at);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* A substring by **UTF-16 code unit**, as every index-taking member of this library is and
|
|
163
|
+
* as the declaration's own `spec: false` reason states — not by code point, which is how the
|
|
164
|
+
* strings extension reads the same call.
|
|
165
|
+
*/
|
|
166
|
+
function substring(value, from, to, range) {
|
|
167
|
+
const start = boundedIndex(from, value.length, range);
|
|
168
|
+
if (typeof start !== "number")
|
|
169
|
+
return start;
|
|
170
|
+
const end = to === undefined ? value.length : boundedIndex(to, value.length, range);
|
|
171
|
+
if (typeof end !== "number")
|
|
172
|
+
return end;
|
|
173
|
+
if (start > end) {
|
|
174
|
+
return celError("invalid_argument", `the substring starts at ${from} and ends at ${to}`, range);
|
|
175
|
+
}
|
|
176
|
+
return value.slice(start, end);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* `split` with a limit, as the engine being replaced answers it: a negative limit takes
|
|
180
|
+
* every part, zero takes none, and a positive one keeps the remainder in the last part.
|
|
181
|
+
*/
|
|
182
|
+
function splitText(value, separator, limit) {
|
|
183
|
+
if (limit === undefined || limit < 0n)
|
|
184
|
+
return value.split(separator);
|
|
185
|
+
if (limit === 0n)
|
|
186
|
+
return [];
|
|
187
|
+
const parts = value.split(separator);
|
|
188
|
+
const kept = Number(limit);
|
|
189
|
+
if (parts.length <= kept)
|
|
190
|
+
return parts;
|
|
191
|
+
return [...parts.slice(0, kept - 1), parts.slice(kept - 1).join(separator)];
|
|
192
|
+
}
|
|
193
|
+
// --- the table -------------------------------------------------------------
|
|
194
|
+
function timestampGetter(field) {
|
|
195
|
+
// One implementation under both arities: the zone is absent where the call omits it, and
|
|
196
|
+
// no CEL value is `undefined`, so the absence is the argument's own answer.
|
|
197
|
+
return (ctx, a, b) => timestampField(instant(a), field, b === undefined ? undefined : text(b), ctx.range);
|
|
198
|
+
}
|
|
199
|
+
function durationGetter(field) {
|
|
200
|
+
return (ctx, a) => durationField(span(a), field);
|
|
201
|
+
}
|
|
202
|
+
const IMPLEMENTATIONS = new Map([
|
|
203
|
+
// conversions
|
|
204
|
+
["bool(bool)", (ctx, a) => value(a)],
|
|
205
|
+
[
|
|
206
|
+
"bool(string)",
|
|
207
|
+
(ctx, a) => {
|
|
208
|
+
const held = BOOL_WORDS[text(a)];
|
|
209
|
+
return held === undefined ? conversionError("bool()", JSON.stringify(a), ctx.range) : held;
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
["bytes(bytes)", (ctx, a) => value(a)],
|
|
213
|
+
["bytes(string)", (ctx, a) => textToBytes(text(a))],
|
|
214
|
+
["double(double)", (ctx, a) => value(a)],
|
|
215
|
+
["double(int)", (ctx, a) => Number(integer(a))],
|
|
216
|
+
["double(uint)", (ctx, a) => Number(unsigned(a))],
|
|
217
|
+
["double(string)", (ctx, a) => doubleFromText(text(a), ctx.range)],
|
|
218
|
+
["duration(string)", (ctx, a) => parseDuration(text(a), ctx.range)],
|
|
219
|
+
["duration(google.protobuf.Duration)", (ctx, a) => value(a)],
|
|
220
|
+
["dyn(A)", (ctx, a) => value(a)],
|
|
221
|
+
["int(int)", (ctx, a) => value(a)],
|
|
222
|
+
["int(uint)", (ctx, a) => intResult(unsigned(a), ctx.range)],
|
|
223
|
+
["int(double)", (ctx, a) => intFromDouble(double(a), ctx.range)],
|
|
224
|
+
[
|
|
225
|
+
"int(string)",
|
|
226
|
+
(ctx, a) => {
|
|
227
|
+
const written = text(a);
|
|
228
|
+
if (!DECIMAL_INT.test(written))
|
|
229
|
+
return conversionError("int()", JSON.stringify(written), ctx.range);
|
|
230
|
+
return intResult(BigInt(written), ctx.range);
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
["int(google.protobuf.Timestamp)", (ctx, a) => instant(a).seconds],
|
|
234
|
+
// A duration's whole seconds, truncated toward zero — no row pins it, cel-spec does.
|
|
235
|
+
["int(google.protobuf.Duration)", (ctx, a) => durationField(span(a), "getSeconds")],
|
|
236
|
+
["matches(string, string)", (ctx, a, b) => celMatches(text(a), text(b), ctx.range)],
|
|
237
|
+
["size(string)", (ctx, a) => BigInt(characters(text(a)).length)],
|
|
238
|
+
["size(bytes)", (ctx, a) => BigInt(bytes(a).length)],
|
|
239
|
+
["size(list<A>)", (ctx, a) => BigInt(list(a).length)],
|
|
240
|
+
["size(map<K, V>)", (ctx, a) => BigInt(sizeOfMap(a))],
|
|
241
|
+
["string(string)", (ctx, a) => value(a)],
|
|
242
|
+
["string(bool)", (ctx, a) => String(a)],
|
|
243
|
+
["string(int)", (ctx, a) => String(integer(a))],
|
|
244
|
+
["string(uint)", (ctx, a) => String(unsigned(a))],
|
|
245
|
+
["string(double)", (ctx, a) => doubleText(double(a))],
|
|
246
|
+
["string(bytes)", (ctx, a) => bytesToText(bytes(a), ctx.range)],
|
|
247
|
+
["string(google.protobuf.Timestamp)", (ctx, a) => formatTimestamp(instant(a))],
|
|
248
|
+
["string(google.protobuf.Duration)", (ctx, a) => formatDuration(span(a))],
|
|
249
|
+
["timestamp(string)", (ctx, a) => parseTimestamp(text(a), ctx.range)],
|
|
250
|
+
["timestamp(int)", (ctx, a) => celTimestamp(integer(a), 0, ctx.range)],
|
|
251
|
+
["timestamp(google.protobuf.Timestamp)", (ctx, a) => value(a)],
|
|
252
|
+
["uint(uint)", (ctx, a) => value(a)],
|
|
253
|
+
["uint(int)", (ctx, a) => uintResult(integer(a), ctx.range)],
|
|
254
|
+
["uint(double)", (ctx, a) => uintFromDouble(double(a), ctx.range)],
|
|
255
|
+
[
|
|
256
|
+
"uint(string)",
|
|
257
|
+
(ctx, a) => {
|
|
258
|
+
const written = text(a);
|
|
259
|
+
if (!DECIMAL_UINT.test(written))
|
|
260
|
+
return conversionError("uint()", JSON.stringify(written), ctx.range);
|
|
261
|
+
return uintResult(BigInt(written), ctx.range);
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
// string members
|
|
265
|
+
["string.size()", (ctx, a) => BigInt(characters(text(a)).length)],
|
|
266
|
+
["bytes.size()", (ctx, a) => BigInt(bytes(a).length)],
|
|
267
|
+
["list<A>.size()", (ctx, a) => BigInt(list(a).length)],
|
|
268
|
+
["map<K, V>.size()", (ctx, a) => BigInt(sizeOfMap(a))],
|
|
269
|
+
["string.startsWith(string)", (ctx, a, b) => text(a).startsWith(text(b))],
|
|
270
|
+
["string.endsWith(string)", (ctx, a, b) => text(a).endsWith(text(b))],
|
|
271
|
+
["string.contains(string)", (ctx, a, b) => text(a).includes(text(b))],
|
|
272
|
+
["string.matches(string)", (ctx, a, b) => celMatches(text(a), text(b), ctx.range)],
|
|
273
|
+
// Named for ASCII and not limited to it — see the declaration's own `spec: false` reason.
|
|
274
|
+
["string.lowerAscii()", (ctx, a) => text(a).toLowerCase()],
|
|
275
|
+
["string.upperAscii()", (ctx, a) => text(a).toUpperCase()],
|
|
276
|
+
["string.trim()", (ctx, a) => text(a).trim()],
|
|
277
|
+
["string.indexOf(string)", (ctx, a, b) => BigInt(text(a).indexOf(text(b)))],
|
|
278
|
+
[
|
|
279
|
+
"string.indexOf(string, int)",
|
|
280
|
+
(ctx, a, b, c) => {
|
|
281
|
+
const from = boundedIndex(integer(c), text(a).length, ctx.range);
|
|
282
|
+
return typeof from === "number" ? BigInt(text(a).indexOf(text(b), from)) : from;
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
["string.lastIndexOf(string)", (ctx, a, b) => BigInt(text(a).lastIndexOf(text(b)))],
|
|
286
|
+
[
|
|
287
|
+
"string.lastIndexOf(string, int)",
|
|
288
|
+
(ctx, a, b, c) => {
|
|
289
|
+
const from = boundedIndex(integer(c), text(a).length, ctx.range);
|
|
290
|
+
return typeof from === "number" ? BigInt(text(a).lastIndexOf(text(b), from)) : from;
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
["string.substring(int)", (ctx, a, b) => substring(text(a), integer(b), undefined, ctx.range)],
|
|
294
|
+
[
|
|
295
|
+
"string.substring(int, int)",
|
|
296
|
+
(ctx, a, b, c) => substring(text(a), integer(b), integer(c), ctx.range),
|
|
297
|
+
],
|
|
298
|
+
["string.split(string)", (ctx, a, b) => splitText(text(a), text(b))],
|
|
299
|
+
["string.split(string, int)", (ctx, a, b, c) => splitText(text(a), text(b), integer(c))],
|
|
300
|
+
["list<string>.join()", (ctx, a) => list(a).join("")],
|
|
301
|
+
["list<string>.join(string)", (ctx, a, b) => list(a).join(text(b))],
|
|
302
|
+
// bytes members, each a compatibility member rather than CEL's
|
|
303
|
+
["bytes.string()", (ctx, a) => bytesToText(bytes(a), ctx.range)],
|
|
304
|
+
["bytes.hex()", (ctx, a) => hexText(bytes(a))],
|
|
305
|
+
["bytes.base64()", (ctx, a) => base64Text(bytes(a))],
|
|
306
|
+
[
|
|
307
|
+
"bytes.json()",
|
|
308
|
+
(ctx, a) => {
|
|
309
|
+
const decoded = bytesToText(bytes(a), ctx.range);
|
|
310
|
+
if (typeof decoded !== "string")
|
|
311
|
+
return decoded;
|
|
312
|
+
try {
|
|
313
|
+
return jsonAsCelValue(JSON.parse(decoded));
|
|
314
|
+
}
|
|
315
|
+
catch (cause) {
|
|
316
|
+
return celError("invalid_conversion", `the bytes are not JSON: ${cause.message}`, ctx.range);
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
[
|
|
321
|
+
"bytes.at(int)",
|
|
322
|
+
(ctx, a, b) => {
|
|
323
|
+
const held = bytes(a);
|
|
324
|
+
const at = integer(b);
|
|
325
|
+
if (at < 0n || at >= BigInt(held.length)) {
|
|
326
|
+
return celError("index_out_of_range", `index out of range: ${at}`, ctx.range);
|
|
327
|
+
}
|
|
328
|
+
return BigInt(held[Number(at)]);
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
// arithmetic
|
|
332
|
+
["!(bool)", (ctx, a) => !a],
|
|
333
|
+
["-(int)", (ctx, a) => intResult(-integer(a), ctx.range)],
|
|
334
|
+
["-(double)", (ctx, a) => -double(a)],
|
|
335
|
+
["+(int, int)", (ctx, a, b) => intResult(integer(a) + integer(b), ctx.range)],
|
|
336
|
+
["+(uint, uint)", (ctx, a, b) => uintResult(unsigned(a) + unsigned(b), ctx.range)],
|
|
337
|
+
["+(double, double)", (ctx, a, b) => double(a) + double(b)],
|
|
338
|
+
["+(string, string)", (ctx, a, b) => text(a) + text(b)],
|
|
339
|
+
[
|
|
340
|
+
"+(bytes, bytes)",
|
|
341
|
+
(ctx, a, b) => {
|
|
342
|
+
const left = bytes(a);
|
|
343
|
+
const right = bytes(b);
|
|
344
|
+
const out = new Uint8Array(left.length + right.length);
|
|
345
|
+
out.set(left);
|
|
346
|
+
out.set(right, left.length);
|
|
347
|
+
return out;
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
["+(list<A>, list<A>)", (ctx, a, b) => [...list(a), ...list(b)]],
|
|
351
|
+
[
|
|
352
|
+
"+(google.protobuf.Timestamp, google.protobuf.Duration)",
|
|
353
|
+
(ctx, a, b) => durationOutOfRange(span(b), ctx.range) ??
|
|
354
|
+
celTimestamp(0n, timestampNanos(instant(a)) + durationNanos(span(b)), ctx.range),
|
|
355
|
+
],
|
|
356
|
+
[
|
|
357
|
+
"+(google.protobuf.Duration, google.protobuf.Timestamp)",
|
|
358
|
+
(ctx, a, b) => durationOutOfRange(span(a), ctx.range) ??
|
|
359
|
+
celTimestamp(0n, durationNanos(span(a)) + timestampNanos(instant(b)), ctx.range),
|
|
360
|
+
],
|
|
361
|
+
[
|
|
362
|
+
"+(google.protobuf.Duration, google.protobuf.Duration)",
|
|
363
|
+
(ctx, a, b) => celDurationFromNanos(durationNanos(span(a)) + durationNanos(span(b)), ctx.range),
|
|
364
|
+
],
|
|
365
|
+
["-(int, int)", (ctx, a, b) => intResult(integer(a) - integer(b), ctx.range)],
|
|
366
|
+
["-(uint, uint)", (ctx, a, b) => uintResult(unsigned(a) - unsigned(b), ctx.range)],
|
|
367
|
+
["-(double, double)", (ctx, a, b) => double(a) - double(b)],
|
|
368
|
+
[
|
|
369
|
+
"-(google.protobuf.Timestamp, google.protobuf.Timestamp)",
|
|
370
|
+
(ctx, a, b) => celDurationFromNanos(timestampNanos(instant(a)) - timestampNanos(instant(b)), ctx.range),
|
|
371
|
+
],
|
|
372
|
+
[
|
|
373
|
+
"-(google.protobuf.Timestamp, google.protobuf.Duration)",
|
|
374
|
+
(ctx, a, b) => durationOutOfRange(span(b), ctx.range) ??
|
|
375
|
+
celTimestamp(0n, timestampNanos(instant(a)) - durationNanos(span(b)), ctx.range),
|
|
376
|
+
],
|
|
377
|
+
[
|
|
378
|
+
"-(google.protobuf.Duration, google.protobuf.Duration)",
|
|
379
|
+
(ctx, a, b) => celDurationFromNanos(durationNanos(span(a)) - durationNanos(span(b)), ctx.range),
|
|
380
|
+
],
|
|
381
|
+
["*(int, int)", (ctx, a, b) => intResult(integer(a) * integer(b), ctx.range)],
|
|
382
|
+
["*(uint, uint)", (ctx, a, b) => uintResult(unsigned(a) * unsigned(b), ctx.range)],
|
|
383
|
+
["*(double, double)", (ctx, a, b) => double(a) * double(b)],
|
|
384
|
+
["/(int, int)", (ctx, a, b) => intDivide(integer(a), integer(b), ctx.range)],
|
|
385
|
+
["/(uint, uint)", (ctx, a, b) => uintDivide(unsigned(a), unsigned(b), ctx.range)],
|
|
386
|
+
// A double divided by zero is an infinity, as IEEE 754 says; only the integers refuse.
|
|
387
|
+
["/(double, double)", (ctx, a, b) => double(a) / double(b)],
|
|
388
|
+
["%(int, int)", (ctx, a, b) => intModulo(integer(a), integer(b), ctx.range)],
|
|
389
|
+
["%(uint, uint)", (ctx, a, b) => uintModulo(unsigned(a), unsigned(b), ctx.range)],
|
|
390
|
+
[
|
|
391
|
+
"in(A, list<A>)",
|
|
392
|
+
(ctx, a, b) => {
|
|
393
|
+
// Membership reads every element, so an element that must be awaited is a door too, and
|
|
394
|
+
// the refusal is TERMINAL as it is in a comprehension: answering `true` off the one
|
|
395
|
+
// element that is readable decides the question against a list this engine cannot read.
|
|
396
|
+
for (const held of list(b)) {
|
|
397
|
+
const refused = asyncValueRefused(held);
|
|
398
|
+
if (refused)
|
|
399
|
+
return refused;
|
|
400
|
+
if (celEqual(held, value(a)))
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
return false;
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
["in(K, map<K, V>)", (ctx, a, b) => typeof celLookup(value(b), value(a)) !== "symbol"],
|
|
407
|
+
// the optional library
|
|
408
|
+
["optional<A>.hasValue()", (ctx, a) => optional(a).present],
|
|
409
|
+
[
|
|
410
|
+
"optional<A>.value()",
|
|
411
|
+
(ctx, a) => {
|
|
412
|
+
const held = optional(a);
|
|
413
|
+
return held.present
|
|
414
|
+
? held.held
|
|
415
|
+
: celError("optional_value_missing", "the optional holds no value", ctx.range);
|
|
416
|
+
},
|
|
417
|
+
],
|
|
418
|
+
["optional<A>.or(optional<A>)", (ctx, a, b) => (optional(a).present ? value(a) : value(b))],
|
|
419
|
+
[
|
|
420
|
+
"optional<A>.orValue(A)",
|
|
421
|
+
(ctx, a, b) => (optional(a).present ? optional(a).held : value(b)),
|
|
422
|
+
],
|
|
423
|
+
// timestamp and duration fields
|
|
424
|
+
["google.protobuf.Timestamp.getDate()", timestampGetter("getDate")],
|
|
425
|
+
["google.protobuf.Timestamp.getDate(string)", timestampGetter("getDate")],
|
|
426
|
+
["google.protobuf.Timestamp.getDayOfMonth()", timestampGetter("getDayOfMonth")],
|
|
427
|
+
["google.protobuf.Timestamp.getDayOfMonth(string)", timestampGetter("getDayOfMonth")],
|
|
428
|
+
["google.protobuf.Timestamp.getDayOfWeek()", timestampGetter("getDayOfWeek")],
|
|
429
|
+
["google.protobuf.Timestamp.getDayOfWeek(string)", timestampGetter("getDayOfWeek")],
|
|
430
|
+
["google.protobuf.Timestamp.getDayOfYear()", timestampGetter("getDayOfYear")],
|
|
431
|
+
["google.protobuf.Timestamp.getDayOfYear(string)", timestampGetter("getDayOfYear")],
|
|
432
|
+
["google.protobuf.Timestamp.getFullYear()", timestampGetter("getFullYear")],
|
|
433
|
+
["google.protobuf.Timestamp.getFullYear(string)", timestampGetter("getFullYear")],
|
|
434
|
+
["google.protobuf.Timestamp.getHours()", timestampGetter("getHours")],
|
|
435
|
+
["google.protobuf.Timestamp.getHours(string)", timestampGetter("getHours")],
|
|
436
|
+
["google.protobuf.Timestamp.getMilliseconds()", timestampGetter("getMilliseconds")],
|
|
437
|
+
["google.protobuf.Timestamp.getMilliseconds(string)", timestampGetter("getMilliseconds")],
|
|
438
|
+
["google.protobuf.Timestamp.getMinutes()", timestampGetter("getMinutes")],
|
|
439
|
+
["google.protobuf.Timestamp.getMinutes(string)", timestampGetter("getMinutes")],
|
|
440
|
+
["google.protobuf.Timestamp.getMonth()", timestampGetter("getMonth")],
|
|
441
|
+
["google.protobuf.Timestamp.getMonth(string)", timestampGetter("getMonth")],
|
|
442
|
+
["google.protobuf.Timestamp.getSeconds()", timestampGetter("getSeconds")],
|
|
443
|
+
["google.protobuf.Timestamp.getSeconds(string)", timestampGetter("getSeconds")],
|
|
444
|
+
["google.protobuf.Duration.getHours()", durationGetter("getHours")],
|
|
445
|
+
["google.protobuf.Duration.getMinutes()", durationGetter("getMinutes")],
|
|
446
|
+
["google.protobuf.Duration.getSeconds()", durationGetter("getSeconds")],
|
|
447
|
+
["google.protobuf.Duration.getMilliseconds()", durationGetter("getMilliseconds")],
|
|
448
|
+
]);
|
|
449
|
+
function sizeOfMap(value) {
|
|
450
|
+
if (isCelMap(value))
|
|
451
|
+
return value.entries.size;
|
|
452
|
+
if (isCelRecord(value))
|
|
453
|
+
return Object.keys(value).length;
|
|
454
|
+
return 0;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Equality and ordering are answered by **name**, over every type pair the library
|
|
458
|
+
* declares and every one a host adds: writing them per pair would be 40 entries that
|
|
459
|
+
* all call the same two functions, and a host registering a comparison over its own type
|
|
460
|
+
* would then have no implementation.
|
|
461
|
+
*/
|
|
462
|
+
const BY_NAME = new Map([
|
|
463
|
+
["==", (ctx, a, b) => celEqual(value(a), value(b))],
|
|
464
|
+
["!=", (ctx, a, b) => !celEqual(value(a), value(b))],
|
|
465
|
+
["<", ordering((compared) => compared < 0)],
|
|
466
|
+
["<=", ordering((compared) => compared <= 0)],
|
|
467
|
+
[">", ordering((compared) => compared > 0)],
|
|
468
|
+
[">=", ordering((compared) => compared >= 0)],
|
|
469
|
+
["type", (ctx, a) => celTypeValueOf(value(a), ctx.range)],
|
|
470
|
+
]);
|
|
471
|
+
function ordering(holds) {
|
|
472
|
+
return (ctx, a, b) => {
|
|
473
|
+
const compared = celCompare(value(a), value(b));
|
|
474
|
+
// Values that do not order at all — unrelated types, or a NaN. NaN orders with
|
|
475
|
+
// nothing, including itself, so every comparison against it is false.
|
|
476
|
+
if (compared === undefined) {
|
|
477
|
+
if (isNotANumber(a) || isNotANumber(b))
|
|
478
|
+
return false;
|
|
479
|
+
return celError("no_matching_overload", `${celTypeNameOf(a) ?? "this value"} and ${celTypeNameOf(b) ?? "this value"} do not order`, ctx.range);
|
|
480
|
+
}
|
|
481
|
+
return holds(compared);
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function isNotANumber(value) {
|
|
485
|
+
return typeof value === "number" && Number.isNaN(value);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* The implementation of a registered signature, or nothing where the registration has
|
|
489
|
+
* none — a host's own function, which the host supplies itself.
|
|
490
|
+
*/
|
|
491
|
+
export function implementationOf(signature) {
|
|
492
|
+
return IMPLEMENTATIONS.get(signatureKey(signature)) ?? BY_NAME.get(signature.name);
|
|
493
|
+
}
|
|
494
|
+
/** Every dispatch key this file answers for, for a completeness check. */
|
|
495
|
+
export function implementedKeys() {
|
|
496
|
+
return [...IMPLEMENTATIONS.keys()];
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* The values the library's own constants read as: each type name as a type value, and
|
|
500
|
+
* `google`, through which the two well-known type names are reached — the only spelling
|
|
501
|
+
* in which an expression can name the timestamp or duration type.
|
|
502
|
+
*/
|
|
503
|
+
export function standardConstantValues(optionalTypes) {
|
|
504
|
+
const values = new Map();
|
|
505
|
+
for (const name of ["bool", "bytes", "double", "int", "list", "map", "null_type", "string", "type", "uint"]) {
|
|
506
|
+
values.set(name, celTypeValue(name));
|
|
507
|
+
}
|
|
508
|
+
if (optionalTypes)
|
|
509
|
+
values.set("optional_type", celTypeValue("optional_type"));
|
|
510
|
+
const google = {};
|
|
511
|
+
for (const [namespace, names] of googleTypeNames()) {
|
|
512
|
+
const held = {};
|
|
513
|
+
for (const name of names)
|
|
514
|
+
held[name] = celTypeValue(`google.${namespace}.${name}`);
|
|
515
|
+
google[namespace] = held;
|
|
516
|
+
}
|
|
517
|
+
values.set("google", google);
|
|
518
|
+
return values;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* `optional.ofNonZeroValue(v)`: present unless the value is its type's zero — which is
|
|
522
|
+
* what makes it different from `optional.of`, and what three of the vectors' rows read.
|
|
523
|
+
*/
|
|
524
|
+
export function optionalOfNonZero(held) {
|
|
525
|
+
return isZeroValue(held) ? celNone() : celSome(held);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Whether a value is its type's zero. Every type that HAS one is listed; the types left out
|
|
529
|
+
* are left out as a decision, not an omission — **an instant, a type value, an optional and
|
|
530
|
+
* a host's named type have no zero in CEL**, because nothing in the language names one. An
|
|
531
|
+
* engine that picked the epoch as a timestamp's zero would make
|
|
532
|
+
* `optional.ofNonZeroValue(timestamp(0))` absent, which no row and no rule asks for.
|
|
533
|
+
*/
|
|
534
|
+
function isZeroValue(value) {
|
|
535
|
+
if (value === null || value === false || value === "")
|
|
536
|
+
return true;
|
|
537
|
+
if (typeof value === "bigint")
|
|
538
|
+
return value === 0n;
|
|
539
|
+
if (typeof value === "number")
|
|
540
|
+
return value === 0;
|
|
541
|
+
if (isCelUint(value))
|
|
542
|
+
return value.value === 0n;
|
|
543
|
+
if (isCelBytes(value))
|
|
544
|
+
return value.length === 0;
|
|
545
|
+
if (Array.isArray(value))
|
|
546
|
+
return value.length === 0;
|
|
547
|
+
if (isCelMap(value))
|
|
548
|
+
return value.entries.size === 0;
|
|
549
|
+
if (isCelRecord(value))
|
|
550
|
+
return Object.keys(value).length === 0;
|
|
551
|
+
if (isCelDuration(value))
|
|
552
|
+
return durationNanos(value) === 0n;
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tree back to CEL source.
|
|
3
|
+
*
|
|
4
|
+
* The contract is round-trip: serializing any tree the parser produced yields text
|
|
5
|
+
* that parses to an **equal** tree (`tree-equality.ts`), and a `qcall` writes back
|
|
6
|
+
* as the `Alias.fn(x)` it was read from. That is what makes a tree, rather than the
|
|
7
|
+
* author's text, something a tool may hold and hand back — a rewrite, a quick fix,
|
|
8
|
+
* a stored expression.
|
|
9
|
+
*
|
|
10
|
+
* Parentheses are placed from precedence alone, never kept from the source: the
|
|
11
|
+
* tree records structure, and the source's own parentheses are not structure.
|
|
12
|
+
*
|
|
13
|
+
* It refuses rather than guesses. A tree that cannot be written as CEL — an
|
|
14
|
+
* `unparsed` hole left by error recovery, a `NaN` double, an integer outside its
|
|
15
|
+
* type's range, a name that is not a name — throws, because text that does not
|
|
16
|
+
* parse back would make every later answer about it wrong.
|
|
17
|
+
*/
|
|
18
|
+
import type { CelNode } from "./syntax-tree.js";
|
|
19
|
+
export declare class CelSerializeError extends Error {
|
|
20
|
+
constructor(message: string);
|
|
21
|
+
}
|
|
22
|
+
/** The CEL source of a tree. Throws on a tree that has none. */
|
|
23
|
+
export declare function serializeTree(root: CelNode): string;
|
|
24
|
+
//# sourceMappingURL=serializer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAc,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE5D,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAuOD,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAEnD"}
|