@telorun/cel 0.0.0-stage → 0.107.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 +146 -0
- package/dist/backend-runtime.d.ts.map +1 -0
- package/dist/backend-runtime.js +322 -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 +785 -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 +34 -0
- package/dist/cel-map-value.d.ts.map +1 -0
- package/dist/cel-map-value.js +74 -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 +159 -0
- package/dist/cel-value.d.ts.map +1 -0
- package/dist/cel-value.js +225 -0
- package/dist/check-diagnostic.d.ts +53 -0
- package/dist/check-diagnostic.d.ts.map +1 -0
- package/dist/check-diagnostic.js +66 -0
- package/dist/checker.d.ts +77 -0
- package/dist/checker.d.ts.map +1 -0
- package/dist/checker.js +721 -0
- package/dist/closure-backend.d.ts +21 -0
- package/dist/closure-backend.d.ts.map +1 -0
- package/dist/closure-backend.js +436 -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 +59 -0
- package/dist/duration-value.d.ts.map +1 -0
- package/dist/duration-value.js +135 -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 +283 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +459 -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 +78 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +189 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -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 +132 -0
- package/dist/js-emitter.d.ts.map +1 -0
- package/dist/js-emitter.js +562 -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 +33 -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 +52 -0
- package/dist/member-read.d.ts.map +1 -0
- package/dist/member-read.js +125 -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 +33 -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 +56 -0
- package/dist/runtime-library.d.ts.map +1 -0
- package/dist/runtime-library.js +545 -0
- package/dist/serializer.d.ts +24 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/serializer.js +240 -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 +53 -0
- package/dist/timestamp-value.d.ts.map +1 -0
- package/dist/timestamp-value.js +223 -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 +26 -0
- package/dist/type-expression.d.ts.map +1 -0
- package/dist/type-expression.js +134 -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 +454 -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 +86 -0
- package/src/cel-program.ts +103 -0
- package/src/cel-type.ts +359 -0
- package/src/cel-value.ts +353 -0
- package/src/check-diagnostic.ts +102 -0
- package/src/checker.ts +932 -0
- package/src/closure-backend.ts +502 -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 +145 -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 +740 -0
- package/src/function-catalog.ts +140 -0
- package/src/function-registry.ts +229 -0
- package/src/index.ts +386 -0
- package/src/integer-arithmetic.ts +64 -0
- package/src/js-emitter.ts +713 -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 +191 -0
- package/src/macro-shape.ts +66 -0
- package/src/member-read.ts +126 -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 +34 -0
- package/src/root-references.ts +126 -0
- package/src/runtime-library.ts +615 -0
- package/src/serializer.ts +246 -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 +294 -0
- package/src/tree-equality.ts +130 -0
- package/src/type-expression.ts +160 -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,615 @@
|
|
|
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
|
+
|
|
17
|
+
import type {
|
|
18
|
+
CelDuration,
|
|
19
|
+
CelMap,
|
|
20
|
+
CelOptional,
|
|
21
|
+
CelTimestamp,
|
|
22
|
+
CelValue,
|
|
23
|
+
} from "./cel-value.js";
|
|
24
|
+
import {
|
|
25
|
+
asyncValueRefused,
|
|
26
|
+
celError,
|
|
27
|
+
celNone,
|
|
28
|
+
celSome,
|
|
29
|
+
celTypeNameOf,
|
|
30
|
+
celTypeValue,
|
|
31
|
+
celUint,
|
|
32
|
+
isCelBytes,
|
|
33
|
+
isCelDuration,
|
|
34
|
+
isCelMap,
|
|
35
|
+
isCelRecord,
|
|
36
|
+
isCelUint,
|
|
37
|
+
type CelError,
|
|
38
|
+
type CelTypeValue,
|
|
39
|
+
} from "./cel-value.js";
|
|
40
|
+
import {
|
|
41
|
+
celDurationFromNanos,
|
|
42
|
+
durationField,
|
|
43
|
+
durationNanos,
|
|
44
|
+
durationOutOfRange,
|
|
45
|
+
formatDuration,
|
|
46
|
+
parseDuration,
|
|
47
|
+
type DurationField,
|
|
48
|
+
} from "./duration-value.js";
|
|
49
|
+
import {
|
|
50
|
+
intDivide,
|
|
51
|
+
intModulo,
|
|
52
|
+
intResult,
|
|
53
|
+
uintDivide,
|
|
54
|
+
uintModulo,
|
|
55
|
+
uintResult,
|
|
56
|
+
} from "./integer-arithmetic.js";
|
|
57
|
+
import { MAX_UINT } from "./lexer.js";
|
|
58
|
+
import { celLookup } from "./member-read.js";
|
|
59
|
+
import { celMatches } from "./regular-expression.js";
|
|
60
|
+
import type { CelSignature } from "./signature.js";
|
|
61
|
+
import { signatureKey } from "./signature.js";
|
|
62
|
+
import { googleTypeNames } from "./standard-library.js";
|
|
63
|
+
import type { SourceRange } from "./syntax-tree.js";
|
|
64
|
+
import {
|
|
65
|
+
celTimestamp,
|
|
66
|
+
formatTimestamp,
|
|
67
|
+
parseTimestamp,
|
|
68
|
+
timestampField,
|
|
69
|
+
timestampNanos,
|
|
70
|
+
type TimestampField,
|
|
71
|
+
} from "./timestamp-value.js";
|
|
72
|
+
import { celCompare, celEqual } from "./value-equality.js";
|
|
73
|
+
import { bytesToText, doubleText, textToBytes } from "./value-text.js";
|
|
74
|
+
|
|
75
|
+
/** What an implementation knows about the call it is answering. */
|
|
76
|
+
export interface CelCallContext {
|
|
77
|
+
/** The span of the call, carried into any error value it produces. */
|
|
78
|
+
readonly range?: SourceRange;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type CelImplementation = (args: readonly CelValue[], ctx: CelCallContext) => CelValue;
|
|
82
|
+
|
|
83
|
+
// --- reading arguments the dispatcher has already typed ---------------------
|
|
84
|
+
|
|
85
|
+
const text = (value: CelValue): string => value as string;
|
|
86
|
+
const integer = (value: CelValue): bigint => value as bigint;
|
|
87
|
+
const unsigned = (value: CelValue): bigint => (value as { value: bigint }).value;
|
|
88
|
+
const double = (value: CelValue): number => value as number;
|
|
89
|
+
const bytes = (value: CelValue): Uint8Array => value as Uint8Array;
|
|
90
|
+
const list = (value: CelValue): readonly CelValue[] => value as readonly CelValue[];
|
|
91
|
+
const instant = (value: CelValue): CelTimestamp => value as CelTimestamp;
|
|
92
|
+
const span = (value: CelValue): CelDuration => value as CelDuration;
|
|
93
|
+
const optional = (value: CelValue): CelOptional => value as CelOptional;
|
|
94
|
+
|
|
95
|
+
/** The code points of text — CEL counts and indexes a string by character. */
|
|
96
|
+
const characters = (value: string): string[] => [...value];
|
|
97
|
+
|
|
98
|
+
// --- conversions -----------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
/** The doubles at which an int64 ends in each direction: 2^63 and -2^63. */
|
|
101
|
+
const INT_RANGE_ABOVE = 9223372036854775808;
|
|
102
|
+
const INT_RANGE_BELOW = -9223372036854775808;
|
|
103
|
+
|
|
104
|
+
const DECIMAL_INT = /^[+-]?\d+$/;
|
|
105
|
+
const DECIMAL_UINT = /^\+?\d+$/;
|
|
106
|
+
const DECIMAL_DOUBLE = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
107
|
+
|
|
108
|
+
function conversionError(what: string, value: string, range?: SourceRange): CelError {
|
|
109
|
+
return celError("invalid_conversion", `${what} cannot convert ${value}`, range);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* `int(double)`. A double **at or beyond either int64 extreme** is refused, which is
|
|
114
|
+
* cel-spec's rule and the only one under which the two ends behave alike: the double nearest
|
|
115
|
+
* `-9223372036854775808` IS that number exactly, so an engine that refuses only the positive
|
|
116
|
+
* end accepts one extreme and refuses the other for no reason a reader could state.
|
|
117
|
+
*/
|
|
118
|
+
function intFromDouble(value: number, range?: SourceRange): bigint | CelError {
|
|
119
|
+
if (!Number.isFinite(value)) return conversionError("int()", doubleText(value), range);
|
|
120
|
+
if (value >= INT_RANGE_ABOVE || value <= INT_RANGE_BELOW) {
|
|
121
|
+
return celError("numeric_overflow", `integer overflow: ${doubleText(value)}`, range);
|
|
122
|
+
}
|
|
123
|
+
return BigInt(Math.trunc(value));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function uintFromDouble(value: number, range?: SourceRange): CelValue {
|
|
127
|
+
if (!Number.isFinite(value)) return conversionError("uint()", doubleText(value), range);
|
|
128
|
+
const whole = BigInt(Math.trunc(value));
|
|
129
|
+
if (whole < 0n || whole > MAX_UINT) {
|
|
130
|
+
return celError("numeric_overflow", `unsigned integer overflow: ${whole}`, range);
|
|
131
|
+
}
|
|
132
|
+
return celUint(whole);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function doubleFromText(value: string, range?: SourceRange): CelValue {
|
|
136
|
+
const trimmed = value;
|
|
137
|
+
if (/^[+-]?(?:Inf|Infinity)$/.test(trimmed)) {
|
|
138
|
+
return trimmed.startsWith("-") ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY;
|
|
139
|
+
}
|
|
140
|
+
if (trimmed === "NaN") return Number.NaN;
|
|
141
|
+
if (!DECIMAL_DOUBLE.test(trimmed)) return conversionError("double()", JSON.stringify(value), range);
|
|
142
|
+
return Number(trimmed);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const BOOL_WORDS: Readonly<Record<string, boolean>> = {
|
|
146
|
+
"1": true,
|
|
147
|
+
t: true,
|
|
148
|
+
true: true,
|
|
149
|
+
TRUE: true,
|
|
150
|
+
True: true,
|
|
151
|
+
"0": false,
|
|
152
|
+
f: false,
|
|
153
|
+
false: false,
|
|
154
|
+
FALSE: false,
|
|
155
|
+
False: false,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/** The name a type value carries. A null's type and an optional's are named apart. */
|
|
159
|
+
export function typeValueName(typeName: string): string {
|
|
160
|
+
if (typeName === "optional") return "optional_type";
|
|
161
|
+
return typeName;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** The type value of any value — `type()` must answer for anything, including `dyn`. */
|
|
165
|
+
export function celTypeValueOf(value: CelValue, range?: SourceRange): CelTypeValue | CelError {
|
|
166
|
+
const name = celTypeNameOf(value);
|
|
167
|
+
if (name === undefined) {
|
|
168
|
+
return celError("invalid_conversion", "this value is of no CEL type", range);
|
|
169
|
+
}
|
|
170
|
+
return celTypeValue(typeValueName(name));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// --- text and bytes --------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
const BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
176
|
+
|
|
177
|
+
/** Bytes as base64 with the standard alphabet and padding. */
|
|
178
|
+
export function base64Text(input: Uint8Array): string {
|
|
179
|
+
let out = "";
|
|
180
|
+
for (let at = 0; at < input.length; at += 3) {
|
|
181
|
+
const a = input[at]!;
|
|
182
|
+
const b = input[at + 1];
|
|
183
|
+
const c = input[at + 2];
|
|
184
|
+
out += BASE64[a >> 2]!;
|
|
185
|
+
out += BASE64[((a & 3) << 4) | ((b ?? 0) >> 4)]!;
|
|
186
|
+
out += b === undefined ? "=" : BASE64[((b & 15) << 2) | ((c ?? 0) >> 6)]!;
|
|
187
|
+
out += c === undefined ? "=" : BASE64[c & 63]!;
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function hexText(input: Uint8Array): string {
|
|
193
|
+
let out = "";
|
|
194
|
+
for (const byte of input) out += byte.toString(16).padStart(2, "0");
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* JSON as CEL values: an object is a map, an array a list, a number a double. Exported
|
|
200
|
+
* because the function catalog's `parseJson` answers the same question, and two readings
|
|
201
|
+
* of what JSON becomes would be two value domains.
|
|
202
|
+
*/
|
|
203
|
+
export function jsonAsCelValue(parsed: unknown): CelValue {
|
|
204
|
+
if (parsed === null || typeof parsed === "boolean" || typeof parsed === "number" || typeof parsed === "string") {
|
|
205
|
+
return parsed;
|
|
206
|
+
}
|
|
207
|
+
if (Array.isArray(parsed)) return parsed.map(jsonAsCelValue);
|
|
208
|
+
const out: Record<string, CelValue> = Object.create(null) as Record<string, CelValue>;
|
|
209
|
+
for (const [key, value] of Object.entries(parsed as object)) out[key] = jsonAsCelValue(value);
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** An index into a string, bounded by its length; an out-of-range one is refused. */
|
|
214
|
+
function boundedIndex(at: bigint, length: number, range?: SourceRange): number | CelError {
|
|
215
|
+
if (at < 0n || at > BigInt(length)) {
|
|
216
|
+
return celError("invalid_argument", `index ${at} is outside the string`, range);
|
|
217
|
+
}
|
|
218
|
+
return Number(at);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* A substring by **UTF-16 code unit**, as every index-taking member of this library is and
|
|
223
|
+
* as the declaration's own `spec: false` reason states — not by code point, which is how the
|
|
224
|
+
* strings extension reads the same call.
|
|
225
|
+
*/
|
|
226
|
+
function substring(value: string, from: bigint, to: bigint | undefined, range?: SourceRange): CelValue {
|
|
227
|
+
const start = boundedIndex(from, value.length, range);
|
|
228
|
+
if (typeof start !== "number") return start;
|
|
229
|
+
const end = to === undefined ? value.length : boundedIndex(to, value.length, range);
|
|
230
|
+
if (typeof end !== "number") return end;
|
|
231
|
+
if (start > end) {
|
|
232
|
+
return celError("invalid_argument", `the substring starts at ${from} and ends at ${to}`, range);
|
|
233
|
+
}
|
|
234
|
+
return value.slice(start, end);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* `split` with a limit, as the engine being replaced answers it: a negative limit takes
|
|
239
|
+
* every part, zero takes none, and a positive one keeps the remainder in the last part.
|
|
240
|
+
*/
|
|
241
|
+
function splitText(value: string, separator: string, limit?: bigint): CelValue {
|
|
242
|
+
if (limit === undefined || limit < 0n) return value.split(separator);
|
|
243
|
+
if (limit === 0n) return [];
|
|
244
|
+
const parts = value.split(separator);
|
|
245
|
+
const kept = Number(limit);
|
|
246
|
+
if (parts.length <= kept) return parts;
|
|
247
|
+
return [...parts.slice(0, kept - 1), parts.slice(kept - 1).join(separator)];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// --- the table -------------------------------------------------------------
|
|
251
|
+
|
|
252
|
+
function timestampGetter(field: TimestampField): CelImplementation {
|
|
253
|
+
return (args, ctx) =>
|
|
254
|
+
timestampField(instant(args[0]!), field, args.length > 1 ? text(args[1]!) : undefined, ctx.range);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function durationGetter(field: DurationField): CelImplementation {
|
|
258
|
+
return (args) => durationField(span(args[0]!), field);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const IMPLEMENTATIONS = new Map<string, CelImplementation>([
|
|
262
|
+
// conversions
|
|
263
|
+
["bool(bool)", (args) => args[0]!],
|
|
264
|
+
[
|
|
265
|
+
"bool(string)",
|
|
266
|
+
(args, ctx) => {
|
|
267
|
+
const held = BOOL_WORDS[text(args[0]!)];
|
|
268
|
+
return held === undefined ? conversionError("bool()", JSON.stringify(args[0]), ctx.range) : held;
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
["bytes(bytes)", (args) => args[0]!],
|
|
272
|
+
["bytes(string)", (args) => textToBytes(text(args[0]!))],
|
|
273
|
+
["double(double)", (args) => args[0]!],
|
|
274
|
+
["double(int)", (args) => Number(integer(args[0]!))],
|
|
275
|
+
["double(uint)", (args) => Number(unsigned(args[0]!))],
|
|
276
|
+
["double(string)", (args, ctx) => doubleFromText(text(args[0]!), ctx.range)],
|
|
277
|
+
["duration(string)", (args, ctx) => parseDuration(text(args[0]!), ctx.range)],
|
|
278
|
+
["duration(google.protobuf.Duration)", (args) => args[0]!],
|
|
279
|
+
["dyn(A)", (args) => args[0]!],
|
|
280
|
+
["int(int)", (args) => args[0]!],
|
|
281
|
+
["int(uint)", (args, ctx) => intResult(unsigned(args[0]!), ctx.range)],
|
|
282
|
+
["int(double)", (args, ctx) => intFromDouble(double(args[0]!), ctx.range)],
|
|
283
|
+
[
|
|
284
|
+
"int(string)",
|
|
285
|
+
(args, ctx) => {
|
|
286
|
+
const written = text(args[0]!);
|
|
287
|
+
if (!DECIMAL_INT.test(written)) return conversionError("int()", JSON.stringify(written), ctx.range);
|
|
288
|
+
return intResult(BigInt(written), ctx.range);
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
["int(google.protobuf.Timestamp)", (args) => instant(args[0]!).seconds],
|
|
292
|
+
// A duration's whole seconds, truncated toward zero — no row pins it, cel-spec does.
|
|
293
|
+
["int(google.protobuf.Duration)", (args) => durationField(span(args[0]!), "getSeconds")],
|
|
294
|
+
["matches(string, string)", (args, ctx) => celMatches(text(args[0]!), text(args[1]!), ctx.range)],
|
|
295
|
+
["size(string)", (args) => BigInt(characters(text(args[0]!)).length)],
|
|
296
|
+
["size(bytes)", (args) => BigInt(bytes(args[0]!).length)],
|
|
297
|
+
["size(list<A>)", (args) => BigInt(list(args[0]!).length)],
|
|
298
|
+
["size(map<K, V>)", (args) => BigInt(sizeOfMap(args[0]!))],
|
|
299
|
+
["string(string)", (args) => args[0]!],
|
|
300
|
+
["string(bool)", (args) => String(args[0]!)],
|
|
301
|
+
["string(int)", (args) => String(integer(args[0]!))],
|
|
302
|
+
["string(uint)", (args) => String(unsigned(args[0]!))],
|
|
303
|
+
["string(double)", (args) => doubleText(double(args[0]!))],
|
|
304
|
+
["string(bytes)", (args, ctx) => bytesToText(bytes(args[0]!), ctx.range)],
|
|
305
|
+
["string(google.protobuf.Timestamp)", (args) => formatTimestamp(instant(args[0]!))],
|
|
306
|
+
["string(google.protobuf.Duration)", (args) => formatDuration(span(args[0]!))],
|
|
307
|
+
["timestamp(string)", (args, ctx) => parseTimestamp(text(args[0]!), ctx.range)],
|
|
308
|
+
["timestamp(int)", (args, ctx) => celTimestamp(integer(args[0]!), 0, ctx.range)],
|
|
309
|
+
["timestamp(google.protobuf.Timestamp)", (args) => args[0]!],
|
|
310
|
+
["uint(uint)", (args) => args[0]!],
|
|
311
|
+
["uint(int)", (args, ctx) => uintResult(integer(args[0]!), ctx.range)],
|
|
312
|
+
["uint(double)", (args, ctx) => uintFromDouble(double(args[0]!), ctx.range)],
|
|
313
|
+
[
|
|
314
|
+
"uint(string)",
|
|
315
|
+
(args, ctx) => {
|
|
316
|
+
const written = text(args[0]!);
|
|
317
|
+
if (!DECIMAL_UINT.test(written)) return conversionError("uint()", JSON.stringify(written), ctx.range);
|
|
318
|
+
return uintResult(BigInt(written), ctx.range);
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
|
|
322
|
+
// string members
|
|
323
|
+
["string.size()", (args) => BigInt(characters(text(args[0]!)).length)],
|
|
324
|
+
["bytes.size()", (args) => BigInt(bytes(args[0]!).length)],
|
|
325
|
+
["list<A>.size()", (args) => BigInt(list(args[0]!).length)],
|
|
326
|
+
["map<K, V>.size()", (args) => BigInt(sizeOfMap(args[0]!))],
|
|
327
|
+
["string.startsWith(string)", (args) => text(args[0]!).startsWith(text(args[1]!))],
|
|
328
|
+
["string.endsWith(string)", (args) => text(args[0]!).endsWith(text(args[1]!))],
|
|
329
|
+
["string.contains(string)", (args) => text(args[0]!).includes(text(args[1]!))],
|
|
330
|
+
["string.matches(string)", (args, ctx) => celMatches(text(args[0]!), text(args[1]!), ctx.range)],
|
|
331
|
+
// Named for ASCII and not limited to it — see the declaration's own `spec: false` reason.
|
|
332
|
+
["string.lowerAscii()", (args) => text(args[0]!).toLowerCase()],
|
|
333
|
+
["string.upperAscii()", (args) => text(args[0]!).toUpperCase()],
|
|
334
|
+
["string.trim()", (args) => text(args[0]!).trim()],
|
|
335
|
+
["string.indexOf(string)", (args) => BigInt(text(args[0]!).indexOf(text(args[1]!)))],
|
|
336
|
+
[
|
|
337
|
+
"string.indexOf(string, int)",
|
|
338
|
+
(args, ctx) => {
|
|
339
|
+
const from = boundedIndex(integer(args[2]!), text(args[0]!).length, ctx.range);
|
|
340
|
+
return typeof from === "number" ? BigInt(text(args[0]!).indexOf(text(args[1]!), from)) : from;
|
|
341
|
+
},
|
|
342
|
+
],
|
|
343
|
+
["string.lastIndexOf(string)", (args) => BigInt(text(args[0]!).lastIndexOf(text(args[1]!)))],
|
|
344
|
+
[
|
|
345
|
+
"string.lastIndexOf(string, int)",
|
|
346
|
+
(args, ctx) => {
|
|
347
|
+
const from = boundedIndex(integer(args[2]!), text(args[0]!).length, ctx.range);
|
|
348
|
+
return typeof from === "number" ? BigInt(text(args[0]!).lastIndexOf(text(args[1]!), from)) : from;
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
["string.substring(int)", (args, ctx) => substring(text(args[0]!), integer(args[1]!), undefined, ctx.range)],
|
|
352
|
+
[
|
|
353
|
+
"string.substring(int, int)",
|
|
354
|
+
(args, ctx) => substring(text(args[0]!), integer(args[1]!), integer(args[2]!), ctx.range),
|
|
355
|
+
],
|
|
356
|
+
["string.split(string)", (args) => splitText(text(args[0]!), text(args[1]!))],
|
|
357
|
+
["string.split(string, int)", (args) => splitText(text(args[0]!), text(args[1]!), integer(args[2]!))],
|
|
358
|
+
["list<string>.join()", (args) => list(args[0]!).join("")],
|
|
359
|
+
["list<string>.join(string)", (args) => list(args[0]!).join(text(args[1]!))],
|
|
360
|
+
|
|
361
|
+
// bytes members, each a compatibility member rather than CEL's
|
|
362
|
+
["bytes.string()", (args, ctx) => bytesToText(bytes(args[0]!), ctx.range)],
|
|
363
|
+
["bytes.hex()", (args) => hexText(bytes(args[0]!))],
|
|
364
|
+
["bytes.base64()", (args) => base64Text(bytes(args[0]!))],
|
|
365
|
+
[
|
|
366
|
+
"bytes.json()",
|
|
367
|
+
(args, ctx) => {
|
|
368
|
+
const decoded = bytesToText(bytes(args[0]!), ctx.range);
|
|
369
|
+
if (typeof decoded !== "string") return decoded;
|
|
370
|
+
try {
|
|
371
|
+
return jsonAsCelValue(JSON.parse(decoded));
|
|
372
|
+
} catch (cause) {
|
|
373
|
+
return celError("invalid_conversion", `the bytes are not JSON: ${(cause as Error).message}`, ctx.range);
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
[
|
|
378
|
+
"bytes.at(int)",
|
|
379
|
+
(args, ctx) => {
|
|
380
|
+
const held = bytes(args[0]!);
|
|
381
|
+
const at = integer(args[1]!);
|
|
382
|
+
if (at < 0n || at >= BigInt(held.length)) {
|
|
383
|
+
return celError("index_out_of_range", `index out of range: ${at}`, ctx.range);
|
|
384
|
+
}
|
|
385
|
+
return BigInt(held[Number(at)]!);
|
|
386
|
+
},
|
|
387
|
+
],
|
|
388
|
+
|
|
389
|
+
// arithmetic
|
|
390
|
+
["!(bool)", (args) => !(args[0] as boolean)],
|
|
391
|
+
["-(int)", (args, ctx) => intResult(-integer(args[0]!), ctx.range)],
|
|
392
|
+
["-(double)", (args) => -double(args[0]!)],
|
|
393
|
+
["+(int, int)", (args, ctx) => intResult(integer(args[0]!) + integer(args[1]!), ctx.range)],
|
|
394
|
+
["+(uint, uint)", (args, ctx) => uintResult(unsigned(args[0]!) + unsigned(args[1]!), ctx.range)],
|
|
395
|
+
["+(double, double)", (args) => double(args[0]!) + double(args[1]!)],
|
|
396
|
+
["+(string, string)", (args) => text(args[0]!) + text(args[1]!)],
|
|
397
|
+
[
|
|
398
|
+
"+(bytes, bytes)",
|
|
399
|
+
(args) => {
|
|
400
|
+
const left = bytes(args[0]!);
|
|
401
|
+
const right = bytes(args[1]!);
|
|
402
|
+
const out = new Uint8Array(left.length + right.length);
|
|
403
|
+
out.set(left);
|
|
404
|
+
out.set(right, left.length);
|
|
405
|
+
return out;
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
["+(list<A>, list<A>)", (args) => [...list(args[0]!), ...list(args[1]!)]],
|
|
409
|
+
[
|
|
410
|
+
"+(google.protobuf.Timestamp, google.protobuf.Duration)",
|
|
411
|
+
(args, ctx) =>
|
|
412
|
+
durationOutOfRange(span(args[1]!), ctx.range) ??
|
|
413
|
+
celTimestamp(0n, timestampNanos(instant(args[0]!)) + durationNanos(span(args[1]!)), ctx.range),
|
|
414
|
+
],
|
|
415
|
+
[
|
|
416
|
+
"+(google.protobuf.Duration, google.protobuf.Timestamp)",
|
|
417
|
+
(args, ctx) =>
|
|
418
|
+
durationOutOfRange(span(args[0]!), ctx.range) ??
|
|
419
|
+
celTimestamp(0n, durationNanos(span(args[0]!)) + timestampNanos(instant(args[1]!)), ctx.range),
|
|
420
|
+
],
|
|
421
|
+
[
|
|
422
|
+
"+(google.protobuf.Duration, google.protobuf.Duration)",
|
|
423
|
+
(args, ctx) => celDurationFromNanos(durationNanos(span(args[0]!)) + durationNanos(span(args[1]!)), ctx.range),
|
|
424
|
+
],
|
|
425
|
+
["-(int, int)", (args, ctx) => intResult(integer(args[0]!) - integer(args[1]!), ctx.range)],
|
|
426
|
+
["-(uint, uint)", (args, ctx) => uintResult(unsigned(args[0]!) - unsigned(args[1]!), ctx.range)],
|
|
427
|
+
["-(double, double)", (args) => double(args[0]!) - double(args[1]!)],
|
|
428
|
+
[
|
|
429
|
+
"-(google.protobuf.Timestamp, google.protobuf.Timestamp)",
|
|
430
|
+
(args, ctx) =>
|
|
431
|
+
celDurationFromNanos(timestampNanos(instant(args[0]!)) - timestampNanos(instant(args[1]!)), ctx.range),
|
|
432
|
+
],
|
|
433
|
+
[
|
|
434
|
+
"-(google.protobuf.Timestamp, google.protobuf.Duration)",
|
|
435
|
+
(args, ctx) =>
|
|
436
|
+
durationOutOfRange(span(args[1]!), ctx.range) ??
|
|
437
|
+
celTimestamp(0n, timestampNanos(instant(args[0]!)) - durationNanos(span(args[1]!)), ctx.range),
|
|
438
|
+
],
|
|
439
|
+
[
|
|
440
|
+
"-(google.protobuf.Duration, google.protobuf.Duration)",
|
|
441
|
+
(args, ctx) => celDurationFromNanos(durationNanos(span(args[0]!)) - durationNanos(span(args[1]!)), ctx.range),
|
|
442
|
+
],
|
|
443
|
+
["*(int, int)", (args, ctx) => intResult(integer(args[0]!) * integer(args[1]!), ctx.range)],
|
|
444
|
+
["*(uint, uint)", (args, ctx) => uintResult(unsigned(args[0]!) * unsigned(args[1]!), ctx.range)],
|
|
445
|
+
["*(double, double)", (args) => double(args[0]!) * double(args[1]!)],
|
|
446
|
+
["/(int, int)", (args, ctx) => intDivide(integer(args[0]!), integer(args[1]!), ctx.range)],
|
|
447
|
+
["/(uint, uint)", (args, ctx) => uintDivide(unsigned(args[0]!), unsigned(args[1]!), ctx.range)],
|
|
448
|
+
// A double divided by zero is an infinity, as IEEE 754 says; only the integers refuse.
|
|
449
|
+
["/(double, double)", (args) => double(args[0]!) / double(args[1]!)],
|
|
450
|
+
["%(int, int)", (args, ctx) => intModulo(integer(args[0]!), integer(args[1]!), ctx.range)],
|
|
451
|
+
["%(uint, uint)", (args, ctx) => uintModulo(unsigned(args[0]!), unsigned(args[1]!), ctx.range)],
|
|
452
|
+
[
|
|
453
|
+
"in(A, list<A>)",
|
|
454
|
+
(args) => {
|
|
455
|
+
// Membership reads every element, so an element that must be awaited is a door too, and
|
|
456
|
+
// the refusal is TERMINAL as it is in a comprehension: answering `true` off the one
|
|
457
|
+
// element that is readable decides the question against a list this engine cannot read.
|
|
458
|
+
for (const held of list(args[1]!)) {
|
|
459
|
+
const refused = asyncValueRefused(held);
|
|
460
|
+
if (refused) return refused;
|
|
461
|
+
if (celEqual(held, args[0]!)) return true;
|
|
462
|
+
}
|
|
463
|
+
return false;
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
["in(K, map<K, V>)", (args) => typeof celLookup(args[1]!, args[0]!) !== "symbol"],
|
|
467
|
+
|
|
468
|
+
// the optional library
|
|
469
|
+
["optional<A>.hasValue()", (args) => optional(args[0]!).present],
|
|
470
|
+
[
|
|
471
|
+
"optional<A>.value()",
|
|
472
|
+
(args, ctx) => {
|
|
473
|
+
const held = optional(args[0]!);
|
|
474
|
+
return held.present
|
|
475
|
+
? (held.held as CelValue)
|
|
476
|
+
: celError("optional_value_missing", "the optional holds no value", ctx.range);
|
|
477
|
+
},
|
|
478
|
+
],
|
|
479
|
+
["optional<A>.or(optional<A>)", (args) => (optional(args[0]!).present ? args[0]! : args[1]!)],
|
|
480
|
+
[
|
|
481
|
+
"optional<A>.orValue(A)",
|
|
482
|
+
(args) => (optional(args[0]!).present ? (optional(args[0]!).held as CelValue) : args[1]!),
|
|
483
|
+
],
|
|
484
|
+
|
|
485
|
+
// timestamp and duration fields
|
|
486
|
+
["google.protobuf.Timestamp.getDate()", timestampGetter("getDate")],
|
|
487
|
+
["google.protobuf.Timestamp.getDate(string)", timestampGetter("getDate")],
|
|
488
|
+
["google.protobuf.Timestamp.getDayOfMonth()", timestampGetter("getDayOfMonth")],
|
|
489
|
+
["google.protobuf.Timestamp.getDayOfMonth(string)", timestampGetter("getDayOfMonth")],
|
|
490
|
+
["google.protobuf.Timestamp.getDayOfWeek()", timestampGetter("getDayOfWeek")],
|
|
491
|
+
["google.protobuf.Timestamp.getDayOfWeek(string)", timestampGetter("getDayOfWeek")],
|
|
492
|
+
["google.protobuf.Timestamp.getDayOfYear()", timestampGetter("getDayOfYear")],
|
|
493
|
+
["google.protobuf.Timestamp.getDayOfYear(string)", timestampGetter("getDayOfYear")],
|
|
494
|
+
["google.protobuf.Timestamp.getFullYear()", timestampGetter("getFullYear")],
|
|
495
|
+
["google.protobuf.Timestamp.getFullYear(string)", timestampGetter("getFullYear")],
|
|
496
|
+
["google.protobuf.Timestamp.getHours()", timestampGetter("getHours")],
|
|
497
|
+
["google.protobuf.Timestamp.getHours(string)", timestampGetter("getHours")],
|
|
498
|
+
["google.protobuf.Timestamp.getMilliseconds()", timestampGetter("getMilliseconds")],
|
|
499
|
+
["google.protobuf.Timestamp.getMilliseconds(string)", timestampGetter("getMilliseconds")],
|
|
500
|
+
["google.protobuf.Timestamp.getMinutes()", timestampGetter("getMinutes")],
|
|
501
|
+
["google.protobuf.Timestamp.getMinutes(string)", timestampGetter("getMinutes")],
|
|
502
|
+
["google.protobuf.Timestamp.getMonth()", timestampGetter("getMonth")],
|
|
503
|
+
["google.protobuf.Timestamp.getMonth(string)", timestampGetter("getMonth")],
|
|
504
|
+
["google.protobuf.Timestamp.getSeconds()", timestampGetter("getSeconds")],
|
|
505
|
+
["google.protobuf.Timestamp.getSeconds(string)", timestampGetter("getSeconds")],
|
|
506
|
+
["google.protobuf.Duration.getHours()", durationGetter("getHours")],
|
|
507
|
+
["google.protobuf.Duration.getMinutes()", durationGetter("getMinutes")],
|
|
508
|
+
["google.protobuf.Duration.getSeconds()", durationGetter("getSeconds")],
|
|
509
|
+
["google.protobuf.Duration.getMilliseconds()", durationGetter("getMilliseconds")],
|
|
510
|
+
]);
|
|
511
|
+
|
|
512
|
+
function sizeOfMap(value: CelValue): number {
|
|
513
|
+
if (isCelMap(value)) return (value as CelMap).entries.size;
|
|
514
|
+
if (isCelRecord(value)) return Object.keys(value).length;
|
|
515
|
+
return 0;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Equality and ordering are answered by **name**, over every type pair the library
|
|
520
|
+
* declares and every one a host adds: writing them per pair would be 40 entries that
|
|
521
|
+
* all call the same two functions, and a host registering a comparison over its own type
|
|
522
|
+
* would then have no implementation.
|
|
523
|
+
*/
|
|
524
|
+
const BY_NAME = new Map<string, CelImplementation>([
|
|
525
|
+
["==", (args) => celEqual(args[0]!, args[1]!)],
|
|
526
|
+
["!=", (args) => !celEqual(args[0]!, args[1]!)],
|
|
527
|
+
["<", ordering((compared) => compared < 0)],
|
|
528
|
+
["<=", ordering((compared) => compared <= 0)],
|
|
529
|
+
[">", ordering((compared) => compared > 0)],
|
|
530
|
+
[">=", ordering((compared) => compared >= 0)],
|
|
531
|
+
["type", (args, ctx) => celTypeValueOf(args[0]!, ctx.range)],
|
|
532
|
+
]);
|
|
533
|
+
|
|
534
|
+
function ordering(holds: (compared: number) => boolean): CelImplementation {
|
|
535
|
+
return (args, ctx) => {
|
|
536
|
+
const compared = celCompare(args[0]!, args[1]!);
|
|
537
|
+
// Values that do not order at all — unrelated types, or a NaN. NaN orders with
|
|
538
|
+
// nothing, including itself, so every comparison against it is false.
|
|
539
|
+
if (compared === undefined) {
|
|
540
|
+
if (isNotANumber(args[0]!) || isNotANumber(args[1]!)) return false;
|
|
541
|
+
return celError(
|
|
542
|
+
"no_matching_overload",
|
|
543
|
+
`${celTypeNameOf(args[0]!) ?? "this value"} and ${celTypeNameOf(args[1]!) ?? "this value"} do not order`,
|
|
544
|
+
ctx.range,
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
return holds(compared);
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function isNotANumber(value: CelValue): boolean {
|
|
552
|
+
return typeof value === "number" && Number.isNaN(value);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* The implementation of a registered signature, or nothing where the registration has
|
|
557
|
+
* none — a host's own function, which the host supplies itself.
|
|
558
|
+
*/
|
|
559
|
+
export function implementationOf(signature: CelSignature): CelImplementation | undefined {
|
|
560
|
+
return IMPLEMENTATIONS.get(signatureKey(signature)) ?? BY_NAME.get(signature.name);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/** Every dispatch key this file answers for, for a completeness check. */
|
|
564
|
+
export function implementedKeys(): readonly string[] {
|
|
565
|
+
return [...IMPLEMENTATIONS.keys()];
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* The values the library's own constants read as: each type name as a type value, and
|
|
570
|
+
* `google`, through which the two well-known type names are reached — the only spelling
|
|
571
|
+
* in which an expression can name the timestamp or duration type.
|
|
572
|
+
*/
|
|
573
|
+
export function standardConstantValues(optionalTypes: boolean): ReadonlyMap<string, CelValue> {
|
|
574
|
+
const values = new Map<string, CelValue>();
|
|
575
|
+
for (const name of ["bool", "bytes", "double", "int", "list", "map", "null_type", "string", "type", "uint"]) {
|
|
576
|
+
values.set(name, celTypeValue(name));
|
|
577
|
+
}
|
|
578
|
+
if (optionalTypes) values.set("optional_type", celTypeValue("optional_type"));
|
|
579
|
+
const google: Record<string, CelValue> = {};
|
|
580
|
+
for (const [namespace, names] of googleTypeNames()) {
|
|
581
|
+
const held: Record<string, CelValue> = {};
|
|
582
|
+
for (const name of names) held[name] = celTypeValue(`google.${namespace}.${name}`);
|
|
583
|
+
google[namespace] = held;
|
|
584
|
+
}
|
|
585
|
+
values.set("google", google);
|
|
586
|
+
return values;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* `optional.ofNonZeroValue(v)`: present unless the value is its type's zero — which is
|
|
591
|
+
* what makes it different from `optional.of`, and what three of the vectors' rows read.
|
|
592
|
+
*/
|
|
593
|
+
export function optionalOfNonZero(value: CelValue): CelOptional {
|
|
594
|
+
return isZeroValue(value) ? celNone() : celSome(value);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Whether a value is its type's zero. Every type that HAS one is listed; the types left out
|
|
599
|
+
* are left out as a decision, not an omission — **an instant, a type value, an optional and
|
|
600
|
+
* a host's named type have no zero in CEL**, because nothing in the language names one. An
|
|
601
|
+
* engine that picked the epoch as a timestamp's zero would make
|
|
602
|
+
* `optional.ofNonZeroValue(timestamp(0))` absent, which no row and no rule asks for.
|
|
603
|
+
*/
|
|
604
|
+
function isZeroValue(value: CelValue): boolean {
|
|
605
|
+
if (value === null || value === false || value === "") return true;
|
|
606
|
+
if (typeof value === "bigint") return value === 0n;
|
|
607
|
+
if (typeof value === "number") return value === 0;
|
|
608
|
+
if (isCelUint(value)) return value.value === 0n;
|
|
609
|
+
if (isCelBytes(value)) return value.length === 0;
|
|
610
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
611
|
+
if (isCelMap(value)) return value.entries.size === 0;
|
|
612
|
+
if (isCelRecord(value)) return Object.keys(value).length === 0;
|
|
613
|
+
if (isCelDuration(value)) return durationNanos(value) === 0n;
|
|
614
|
+
return false;
|
|
615
|
+
}
|