@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
package/src/cel-value.ts
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CEL value domain, and how a value says what it is.
|
|
3
|
+
*
|
|
4
|
+
* **Identity is a string type key under `Symbol.for("telo.cel.value")`, never
|
|
5
|
+
* `instanceof`.** Two copies of this engine loaded independently are two sets of
|
|
6
|
+
* classes, so a constructor check would make a `uint` built by one copy not a uint to
|
|
7
|
+
* the other — `+`, `type()` and every overload would refuse it. A symbol from the
|
|
8
|
+
* global registry is the same symbol in both, so the key is readable across copies,
|
|
9
|
+
* and because a symbol-keyed member cannot appear in parsed JSON an inbound request
|
|
10
|
+
* body cannot forge a duration. A plain object carrying a *string*-keyed look-alike
|
|
11
|
+
* (`{"telo.cel.value": "uint"}`) is therefore data: it reads as a map, as it must.
|
|
12
|
+
*
|
|
13
|
+
* What is **plain** carries no key, because the host platform already represents it
|
|
14
|
+
* one way and only one: `null`, a boolean, a string, a double (`number`), an int
|
|
15
|
+
* (`bigint`), bytes (`Uint8Array`), a list (an array) and a map whose keys are all
|
|
16
|
+
* strings (a plain object, which is how a host hands one over). What has no faithful
|
|
17
|
+
* plain form carries the key: a `uint` (a `bigint` is already an int), a timestamp and
|
|
18
|
+
* a duration (both nanosecond-precise, which no host date type holds), a type value,
|
|
19
|
+
* an optional, a map with typed keys, and a CEL error. A host's own named type
|
|
20
|
+
* registers its own key, checked against this set at registration.
|
|
21
|
+
*
|
|
22
|
+
* The keys are a **closed set** (`CEL_VALUE_KEYS`); a new one is a change here.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import type { CelLiteral, SourceRange } from "./syntax-tree.js";
|
|
26
|
+
|
|
27
|
+
export const CEL_VALUE_TYPE: unique symbol = Symbol.for("telo.cel.value");
|
|
28
|
+
|
|
29
|
+
/** The type keys this engine owns. A host-registered type may not reuse one. */
|
|
30
|
+
export const CEL_VALUE_KEYS = [
|
|
31
|
+
"uint",
|
|
32
|
+
"google.protobuf.Timestamp",
|
|
33
|
+
"google.protobuf.Duration",
|
|
34
|
+
"type",
|
|
35
|
+
"optional",
|
|
36
|
+
"map",
|
|
37
|
+
"error",
|
|
38
|
+
] as const;
|
|
39
|
+
|
|
40
|
+
export type CelValueKey = (typeof CEL_VALUE_KEYS)[number];
|
|
41
|
+
|
|
42
|
+
interface Branded<Key extends string> {
|
|
43
|
+
readonly [CEL_VALUE_TYPE]: Key;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** An unsigned 64-bit integer. A `bigint` alone is already CEL's `int`. */
|
|
47
|
+
export interface CelUint extends Branded<"uint"> {
|
|
48
|
+
readonly value: bigint;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* An instant: whole seconds since the epoch plus nanoseconds in `[0, 1e9)`, so a
|
|
53
|
+
* negative instant is still `seconds` floored and `nanos` non-negative. Nanosecond
|
|
54
|
+
* precision is required of the domain — `string(timestamp('…999999999Z'))` is a row.
|
|
55
|
+
*/
|
|
56
|
+
export interface CelTimestamp extends Branded<"google.protobuf.Timestamp"> {
|
|
57
|
+
readonly seconds: bigint;
|
|
58
|
+
readonly nanos: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A span: seconds and nanoseconds of the **same sign**, `|nanos| < 1e9`. */
|
|
62
|
+
export interface CelDuration extends Branded<"google.protobuf.Duration"> {
|
|
63
|
+
readonly seconds: bigint;
|
|
64
|
+
readonly nanos: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A type as a value — what `type(x)` answers and what `int` denotes. */
|
|
68
|
+
export interface CelTypeValue extends Branded<"type"> {
|
|
69
|
+
readonly name: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** `optional.of(v)` and `optional.none()`. */
|
|
73
|
+
export interface CelOptional extends Branded<"optional"> {
|
|
74
|
+
readonly present: boolean;
|
|
75
|
+
readonly held?: CelValue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** One entry of a map, keyed by the canonical text of its key (`cel-map-value.ts`). */
|
|
79
|
+
export interface CelMapValueEntry {
|
|
80
|
+
readonly key: CelValue;
|
|
81
|
+
readonly value: CelValue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A map. Entries live in a `Map` keyed by the canonical text of each key, so the map
|
|
86
|
+
* holds int, uint, bool and string keys alike and **no key is ever a property name**:
|
|
87
|
+
* `__proto__`, `constructor` and `prototype` are data here, as they are in any map.
|
|
88
|
+
*/
|
|
89
|
+
export interface CelMap extends Branded<"map"> {
|
|
90
|
+
readonly entries: ReadonlyMap<string, CelMapValueEntry>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Every evaluation failure this engine names. A code is never derived from a message. */
|
|
94
|
+
export const CEL_EVALUATION_CODES = [
|
|
95
|
+
/** A map or a record does not hold the key read. */
|
|
96
|
+
"no_such_key",
|
|
97
|
+
/** A name nothing in the activation holds, at any prefix. */
|
|
98
|
+
"no_such_variable",
|
|
99
|
+
/** A list index below zero or past the last element. */
|
|
100
|
+
"index_out_of_range",
|
|
101
|
+
/** No registered overload takes the values a call was handed. */
|
|
102
|
+
"no_matching_overload",
|
|
103
|
+
/** A select, an index or an iteration over a value that holds no members. */
|
|
104
|
+
"unsupported_container",
|
|
105
|
+
/** A map key, or an index, of a type no map is keyed by. */
|
|
106
|
+
"unsupported_key_type",
|
|
107
|
+
/** A map literal or a comprehension building two entries with one key. */
|
|
108
|
+
"duplicate_map_key",
|
|
109
|
+
/** An int or uint result outside its own range. */
|
|
110
|
+
"numeric_overflow",
|
|
111
|
+
"division_by_zero",
|
|
112
|
+
"modulo_by_zero",
|
|
113
|
+
/** A conversion the value cannot make: `int('x')`, `timestamp` out of range. */
|
|
114
|
+
"invalid_conversion",
|
|
115
|
+
/** An argument a function refuses: an unknown time zone, a negative index. */
|
|
116
|
+
"invalid_argument",
|
|
117
|
+
/** A pattern RE2 cannot parse. */
|
|
118
|
+
"invalid_regular_expression",
|
|
119
|
+
/** `value()` on an optional that holds nothing. */
|
|
120
|
+
"optional_value_missing",
|
|
121
|
+
/** A namespaced call nothing bound an implementation for. */
|
|
122
|
+
"unbound_function",
|
|
123
|
+
/** A thenable reached evaluation. Evaluation is synchronous on both backends. */
|
|
124
|
+
"async_value_unsupported",
|
|
125
|
+
] as const;
|
|
126
|
+
|
|
127
|
+
export type CelEvaluationCode = (typeof CEL_EVALUATION_CODES)[number];
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* A failure as a **value**. It is not thrown: `false && <error>` is `false`, so every
|
|
131
|
+
* operator, comprehension step and conditional carries an error-valued operand
|
|
132
|
+
* through, and only the top of an evaluation turns a surviving one into a throw
|
|
133
|
+
* (`cel-program.ts`). Throwing from the operator that found it would make the
|
|
134
|
+
* short-circuit rules unimplementable.
|
|
135
|
+
*/
|
|
136
|
+
export interface CelError extends Branded<"error"> {
|
|
137
|
+
readonly code: CelEvaluationCode;
|
|
138
|
+
readonly message: string;
|
|
139
|
+
readonly range?: SourceRange;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** A value of a host-registered named type: its own key, outside `CEL_VALUE_KEYS`. */
|
|
143
|
+
export interface CelHostValue {
|
|
144
|
+
readonly [CEL_VALUE_TYPE]: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** A map whose keys are all strings, as a host hands one over. */
|
|
148
|
+
export interface CelRecord {
|
|
149
|
+
readonly [key: string]: CelValue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export type CelValue =
|
|
153
|
+
| null
|
|
154
|
+
| boolean
|
|
155
|
+
| string
|
|
156
|
+
| number
|
|
157
|
+
| bigint
|
|
158
|
+
| Uint8Array
|
|
159
|
+
| readonly CelValue[]
|
|
160
|
+
| CelUint
|
|
161
|
+
| CelTimestamp
|
|
162
|
+
| CelDuration
|
|
163
|
+
| CelTypeValue
|
|
164
|
+
| CelOptional
|
|
165
|
+
| CelMap
|
|
166
|
+
| CelError
|
|
167
|
+
| CelHostValue
|
|
168
|
+
| CelRecord;
|
|
169
|
+
|
|
170
|
+
// --- constructors ----------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
export function celUint(value: bigint): CelUint {
|
|
173
|
+
return { [CEL_VALUE_TYPE]: "uint", value };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function celTypeValue(name: string): CelTypeValue {
|
|
177
|
+
return { [CEL_VALUE_TYPE]: "type", name };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const NONE: CelOptional = { [CEL_VALUE_TYPE]: "optional", present: false };
|
|
181
|
+
|
|
182
|
+
export function celNone(): CelOptional {
|
|
183
|
+
return NONE;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function celSome(held: CelValue): CelOptional {
|
|
187
|
+
return { [CEL_VALUE_TYPE]: "optional", present: true, held };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function celError(
|
|
191
|
+
code: CelEvaluationCode,
|
|
192
|
+
message: string,
|
|
193
|
+
range?: SourceRange,
|
|
194
|
+
): CelError {
|
|
195
|
+
return { [CEL_VALUE_TYPE]: "error", code, message, ...(range ? { range } : {}) };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// --- reading what a value is ----------------------------------------------
|
|
199
|
+
|
|
200
|
+
/** The type key a value carries, or nothing when it carries none. */
|
|
201
|
+
function brandOf(value: object): string | undefined {
|
|
202
|
+
const key = (value as { [CEL_VALUE_TYPE]?: unknown })[CEL_VALUE_TYPE];
|
|
203
|
+
return typeof key === "string" ? key : undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function isCelError(value: unknown): value is CelError {
|
|
207
|
+
return typeof value === "object" && value !== null && brandOf(value) === "error";
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function isCelOptional(value: unknown): value is CelOptional {
|
|
211
|
+
return typeof value === "object" && value !== null && brandOf(value) === "optional";
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function isCelUint(value: unknown): value is CelUint {
|
|
215
|
+
return typeof value === "object" && value !== null && brandOf(value) === "uint";
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function isCelMap(value: unknown): value is CelMap {
|
|
219
|
+
return typeof value === "object" && value !== null && brandOf(value) === "map";
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function isCelTimestamp(value: unknown): value is CelTimestamp {
|
|
223
|
+
return typeof value === "object" && value !== null && brandOf(value) === "google.protobuf.Timestamp";
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function isCelDuration(value: unknown): value is CelDuration {
|
|
227
|
+
return typeof value === "object" && value !== null && brandOf(value) === "google.protobuf.Duration";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function isCelTypeValue(value: unknown): value is CelTypeValue {
|
|
231
|
+
return typeof value === "object" && value !== null && brandOf(value) === "type";
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Whether a value is bytes. `Object.prototype.toString` rather than `instanceof`, so a
|
|
236
|
+
* buffer built in another realm is still bytes — the same reason the brand is a
|
|
237
|
+
* registered symbol.
|
|
238
|
+
*/
|
|
239
|
+
export function isCelBytes(value: unknown): value is Uint8Array {
|
|
240
|
+
return (
|
|
241
|
+
typeof value === "object" &&
|
|
242
|
+
value !== null &&
|
|
243
|
+
Object.prototype.toString.call(value) === "[object Uint8Array]"
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Whether a value is a map a host handed over: a plain object, not a branded one.
|
|
249
|
+
*
|
|
250
|
+
* The prototype decides, which is why nothing else needs checking: an array, a buffer and
|
|
251
|
+
* a class instance all have a prototype of their own, so none of them is a record. It is
|
|
252
|
+
* also the cheapest test there is, and this runs on every member read.
|
|
253
|
+
*/
|
|
254
|
+
export function isCelRecord(value: unknown): value is CelRecord {
|
|
255
|
+
if (typeof value !== "object" || value === null) return false;
|
|
256
|
+
const prototype = Object.getPrototypeOf(value) as object | null;
|
|
257
|
+
if (prototype !== Object.prototype && prototype !== null) return false;
|
|
258
|
+
return brandOf(value) === undefined;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* The name of a value's CEL type, or nothing when the value is of no CEL type. It is
|
|
263
|
+
* what `type()` answers and what an overload is dispatched on, so it must answer for
|
|
264
|
+
* **any** value — `type()` has no `dyn` overload to fall back on.
|
|
265
|
+
*/
|
|
266
|
+
export function celTypeNameOf(value: unknown): string | undefined {
|
|
267
|
+
switch (typeof value) {
|
|
268
|
+
case "boolean":
|
|
269
|
+
return "bool";
|
|
270
|
+
case "string":
|
|
271
|
+
return "string";
|
|
272
|
+
case "number":
|
|
273
|
+
return "double";
|
|
274
|
+
case "bigint":
|
|
275
|
+
return "int";
|
|
276
|
+
case "object":
|
|
277
|
+
break;
|
|
278
|
+
default:
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
if (value === null) return "null_type";
|
|
282
|
+
const branded = brandOf(value as object);
|
|
283
|
+
if (branded !== undefined) return branded === "error" ? undefined : branded;
|
|
284
|
+
// Ordered by what an expression meets most: a host's map, then a list, then bytes,
|
|
285
|
+
// whose test is the expensive one.
|
|
286
|
+
const prototype = Object.getPrototypeOf(value) as object | null;
|
|
287
|
+
if (prototype === Object.prototype || prototype === null) return "map";
|
|
288
|
+
if (Array.isArray(value)) return "list";
|
|
289
|
+
return isCelBytes(value) ? "bytes" : undefined;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* A thenable reaching evaluation is refused, never passed along: evaluation is
|
|
294
|
+
* synchronous, and a promise flowing through an expression would be an invocation in
|
|
295
|
+
* disguise — invisible to a journal, absent from a trace, and unrepresentable as a
|
|
296
|
+
* step.
|
|
297
|
+
*
|
|
298
|
+
* The check is made at every **door** a host value comes through — an activation read, a
|
|
299
|
+
* registered implementation's result, a member read, an element entering a comprehension
|
|
300
|
+
* body, the value a name is bound to — and the list of them is in the package guide, which
|
|
301
|
+
* is where completeness is argued. `asyncValueRefused` is the one error they all answer
|
|
302
|
+
* with, so the code and the wording cannot drift between doors.
|
|
303
|
+
*/
|
|
304
|
+
export function isThenable(value: unknown): boolean {
|
|
305
|
+
return (
|
|
306
|
+
typeof value === "object" &&
|
|
307
|
+
value !== null &&
|
|
308
|
+
typeof (value as { then?: unknown }).then === "function"
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* The refusal a value that must be awaited becomes, or nothing where it is an ordinary
|
|
314
|
+
* value.
|
|
315
|
+
*
|
|
316
|
+
* **A container is NOT walked**, and that is deliberate rather than a gap: a thenable inside
|
|
317
|
+
* a host list or map is refused when it becomes a value the engine reasons about — read by
|
|
318
|
+
* index, read as a member, or bound into a body — so every element costs one `typeof` when it
|
|
319
|
+
* is actually used, instead of every read costing a walk of what it returned.
|
|
320
|
+
*/
|
|
321
|
+
export function asyncValueRefused(value: unknown, range?: SourceRange): CelError | undefined {
|
|
322
|
+
if (!isThenable(value)) return undefined;
|
|
323
|
+
return celError(
|
|
324
|
+
"async_value_unsupported",
|
|
325
|
+
"a value that must be awaited reached evaluation, and CEL evaluates synchronously",
|
|
326
|
+
range,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* The value a literal node denotes. A tagged literal is not a bare host value — `1` and
|
|
332
|
+
* `1u` are different expressions — so the one reading of a tag lives here, where both
|
|
333
|
+
* backends and the checker's literal-argument guard reach it rather than each keeping a
|
|
334
|
+
* switch of its own.
|
|
335
|
+
*/
|
|
336
|
+
export function literalValue(literal: CelLiteral): CelValue {
|
|
337
|
+
switch (literal.type) {
|
|
338
|
+
case "int":
|
|
339
|
+
return literal.value;
|
|
340
|
+
case "uint":
|
|
341
|
+
return celUint(literal.value);
|
|
342
|
+
case "double":
|
|
343
|
+
return literal.value;
|
|
344
|
+
case "string":
|
|
345
|
+
return literal.value;
|
|
346
|
+
case "bytes":
|
|
347
|
+
return literal.value;
|
|
348
|
+
case "bool":
|
|
349
|
+
return literal.value;
|
|
350
|
+
case "null":
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the checker says, and the fact that **the checker says it**.
|
|
3
|
+
*
|
|
4
|
+
* Every verdict here carries its own code and the range of the text it is about. That
|
|
5
|
+
* is the whole point: an engine that reports one sentence for unrelated mistakes forces
|
|
6
|
+
* whoever consumes it to re-derive the cause afterwards from a registry it has to walk
|
|
7
|
+
* again — a classifier that can only ever explain, never decide, and that must stay
|
|
8
|
+
* silent on anything it cannot account for. The component that already knows decides
|
|
9
|
+
* here, once, and nothing downstream reads a message string.
|
|
10
|
+
*
|
|
11
|
+
* A **fix is a whole-source replacement**, never a sub-range splice. A CEL expression
|
|
12
|
+
* is usually one YAML scalar, an edit to it is applied by replacing that scalar, and a
|
|
13
|
+
* sub-range offset means something different the moment the scalar is re-indented.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { SourceRange } from "./syntax-tree.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Every verdict this engine decides. The type follows the list, so a code can only be
|
|
20
|
+
* added in one place and a host reading the vocabulary reads the same set the compiler
|
|
21
|
+
* holds every diagnostic to.
|
|
22
|
+
*/
|
|
23
|
+
export const CEL_CHECK_CODES = [
|
|
24
|
+
/** The source could not be read at all (the front end's verdict, carried through). */
|
|
25
|
+
"CEL_SYNTAX_ERROR",
|
|
26
|
+
/** A type is not the one the expression needs here. */
|
|
27
|
+
"CEL_TYPE_ERROR",
|
|
28
|
+
/** A name nothing declares. */
|
|
29
|
+
"CEL_UNKNOWN_IDENTIFIER",
|
|
30
|
+
/** A member a declared type does not hold. */
|
|
31
|
+
"CEL_UNKNOWN_FIELD",
|
|
32
|
+
/** A function nothing registers. */
|
|
33
|
+
"CEL_UNKNOWN_FUNCTION",
|
|
34
|
+
/** A registered function called in the other form — `x.f()` for `f(x)`, or back. */
|
|
35
|
+
"CEL_WRONG_CALL_FORM",
|
|
36
|
+
/** A named type whose type argument differs from the one wanted. */
|
|
37
|
+
"CEL_TYPE_ARGUMENT_MISMATCH",
|
|
38
|
+
/** A dereference of something that may be null, with no guard proving it is not. */
|
|
39
|
+
"CEL_NULLABLE_ACCESS",
|
|
40
|
+
/** An argument a construct refuses on sight — a macro's variable that is not a name. */
|
|
41
|
+
"CEL_INVALID_ARGUMENT",
|
|
42
|
+
/** A namespaced call naming a function the namespace does not declare. */
|
|
43
|
+
"FUNCTION_UNRESOLVED",
|
|
44
|
+
/** A namespaced call with the wrong number of arguments. */
|
|
45
|
+
"FUNCTION_ARITY_MISMATCH",
|
|
46
|
+
/** A namespaced call whose argument is not the declared type. */
|
|
47
|
+
"FUNCTION_ARGUMENT_MISMATCH",
|
|
48
|
+
] as const;
|
|
49
|
+
|
|
50
|
+
export type CelCheckCode = (typeof CEL_CHECK_CODES)[number];
|
|
51
|
+
|
|
52
|
+
export interface CelDiagnosticFix {
|
|
53
|
+
/** The whole expression, corrected. */
|
|
54
|
+
readonly replacement: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface CelCheckDiagnostic {
|
|
58
|
+
readonly code: CelCheckCode;
|
|
59
|
+
readonly message: string;
|
|
60
|
+
readonly range: SourceRange;
|
|
61
|
+
readonly fix?: CelDiagnosticFix;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* An engine error: the caller used the engine wrongly, so nothing is checked or
|
|
66
|
+
* compiled. It is not a verdict about the expression — `unreadable_expression` is the
|
|
67
|
+
* refusal to compile a tree the front end already reported a syntax diagnostic for,
|
|
68
|
+
* which is a question the caller should have asked before compiling, and
|
|
69
|
+
* `emitted_module_rejected` is the refusal to run a loaded module whose integrity header
|
|
70
|
+
* is not the one the emission asked for.
|
|
71
|
+
*/
|
|
72
|
+
export type CelEngineErrorCode =
|
|
73
|
+
| "namespaces_mismatch"
|
|
74
|
+
| "unreadable_expression"
|
|
75
|
+
| "emitted_module_rejected";
|
|
76
|
+
|
|
77
|
+
export class CelEngineError extends Error {
|
|
78
|
+
readonly code: CelEngineErrorCode;
|
|
79
|
+
|
|
80
|
+
constructor(code: CelEngineErrorCode, message: string) {
|
|
81
|
+
super(message);
|
|
82
|
+
this.name = "CelEngineError";
|
|
83
|
+
this.code = code;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Collects diagnostics in discovery order, which is source order for one pass. */
|
|
88
|
+
export class DiagnosticList {
|
|
89
|
+
private readonly held: CelCheckDiagnostic[] = [];
|
|
90
|
+
|
|
91
|
+
add(diagnostic: CelCheckDiagnostic): void {
|
|
92
|
+
this.held.push(diagnostic);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get length(): number {
|
|
96
|
+
return this.held.length;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
list(): readonly CelCheckDiagnostic[] {
|
|
100
|
+
return this.held;
|
|
101
|
+
}
|
|
102
|
+
}
|