@telorun/cel 0.0.0-stage → 0.108.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +281 -2
  3. package/dist/activation.d.ts +27 -0
  4. package/dist/activation.d.ts.map +1 -0
  5. package/dist/activation.js +24 -0
  6. package/dist/backend-runtime.d.ts +184 -0
  7. package/dist/backend-runtime.d.ts.map +1 -0
  8. package/dist/backend-runtime.js +425 -0
  9. package/dist/bounded-cache.d.ts +21 -0
  10. package/dist/bounded-cache.d.ts.map +1 -0
  11. package/dist/bounded-cache.js +42 -0
  12. package/dist/catalog-runtime.d.ts +59 -0
  13. package/dist/catalog-runtime.d.ts.map +1 -0
  14. package/dist/catalog-runtime.js +786 -0
  15. package/dist/cel-expression.d.ts +32 -0
  16. package/dist/cel-expression.d.ts.map +1 -0
  17. package/dist/cel-expression.js +29 -0
  18. package/dist/cel-map-value.d.ts +47 -0
  19. package/dist/cel-map-value.d.ts.map +1 -0
  20. package/dist/cel-map-value.js +85 -0
  21. package/dist/cel-program.d.ts +44 -0
  22. package/dist/cel-program.d.ts.map +1 -0
  23. package/dist/cel-program.js +72 -0
  24. package/dist/cel-type.d.ts +131 -0
  25. package/dist/cel-type.d.ts.map +1 -0
  26. package/dist/cel-type.js +293 -0
  27. package/dist/cel-value.d.ts +166 -0
  28. package/dist/cel-value.d.ts.map +1 -0
  29. package/dist/cel-value.js +225 -0
  30. package/dist/check-diagnostic.d.ts +54 -0
  31. package/dist/check-diagnostic.d.ts.map +1 -0
  32. package/dist/check-diagnostic.js +66 -0
  33. package/dist/checker.d.ts +86 -0
  34. package/dist/checker.d.ts.map +1 -0
  35. package/dist/checker.js +806 -0
  36. package/dist/closure-backend.d.ts +21 -0
  37. package/dist/closure-backend.d.ts.map +1 -0
  38. package/dist/closure-backend.js +487 -0
  39. package/dist/comprehension-bindings.d.ts +33 -0
  40. package/dist/comprehension-bindings.d.ts.map +1 -0
  41. package/dist/comprehension-bindings.js +50 -0
  42. package/dist/comprehension-runtime.d.ts +44 -0
  43. package/dist/comprehension-runtime.d.ts.map +1 -0
  44. package/dist/comprehension-runtime.js +137 -0
  45. package/dist/declared-chain.d.ts +35 -0
  46. package/dist/declared-chain.d.ts.map +1 -0
  47. package/dist/declared-chain.js +36 -0
  48. package/dist/duration-value.d.ts +70 -0
  49. package/dist/duration-value.d.ts.map +1 -0
  50. package/dist/duration-value.js +149 -0
  51. package/dist/emitted-module.d.ts +207 -0
  52. package/dist/emitted-module.d.ts.map +1 -0
  53. package/dist/emitted-module.js +359 -0
  54. package/dist/engine-version.d.ts +3 -0
  55. package/dist/engine-version.d.ts.map +1 -0
  56. package/dist/engine-version.js +8 -0
  57. package/dist/environment-digest.d.ts +44 -0
  58. package/dist/environment-digest.d.ts.map +1 -0
  59. package/dist/environment-digest.js +98 -0
  60. package/dist/environment.d.ts +291 -0
  61. package/dist/environment.d.ts.map +1 -0
  62. package/dist/environment.js +474 -0
  63. package/dist/function-catalog.d.ts +66 -0
  64. package/dist/function-catalog.d.ts.map +1 -0
  65. package/dist/function-catalog.js +77 -0
  66. package/dist/function-registry.d.ts +118 -0
  67. package/dist/function-registry.d.ts.map +1 -0
  68. package/dist/function-registry.js +292 -0
  69. package/dist/index.d.ts +93 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +65 -0
  72. package/dist/integer-arithmetic.d.ts +27 -0
  73. package/dist/integer-arithmetic.d.ts.map +1 -0
  74. package/dist/integer-arithmetic.js +58 -0
  75. package/dist/js-emitter.d.ts +133 -0
  76. package/dist/js-emitter.d.ts.map +1 -0
  77. package/dist/js-emitter.js +568 -0
  78. package/dist/json-schema-type.d.ts +182 -0
  79. package/dist/json-schema-type.d.ts.map +1 -0
  80. package/dist/json-schema-type.js +487 -0
  81. package/dist/json-text-scan.d.ts +28 -0
  82. package/dist/json-text-scan.d.ts.map +1 -0
  83. package/dist/json-text-scan.js +159 -0
  84. package/dist/lexer.d.ts +103 -0
  85. package/dist/lexer.d.ts.map +1 -0
  86. package/dist/lexer.js +458 -0
  87. package/dist/macro-check.d.ts +41 -0
  88. package/dist/macro-check.d.ts.map +1 -0
  89. package/dist/macro-check.js +162 -0
  90. package/dist/macro-shape.d.ts +24 -0
  91. package/dist/macro-shape.d.ts.map +1 -0
  92. package/dist/macro-shape.js +55 -0
  93. package/dist/member-read.d.ts +89 -0
  94. package/dist/member-read.d.ts.map +1 -0
  95. package/dist/member-read.js +166 -0
  96. package/dist/namespace-resolution.d.ts +35 -0
  97. package/dist/namespace-resolution.d.ts.map +1 -0
  98. package/dist/namespace-resolution.js +160 -0
  99. package/dist/nominal-type.d.ts +63 -0
  100. package/dist/nominal-type.d.ts.map +1 -0
  101. package/dist/nominal-type.js +98 -0
  102. package/dist/nullable-access.d.ts +38 -0
  103. package/dist/nullable-access.d.ts.map +1 -0
  104. package/dist/nullable-access.js +93 -0
  105. package/dist/parse-limits.d.ts +26 -0
  106. package/dist/parse-limits.d.ts.map +1 -0
  107. package/dist/parse-limits.js +21 -0
  108. package/dist/parser.d.ts +48 -0
  109. package/dist/parser.d.ts.map +1 -0
  110. package/dist/parser.js +503 -0
  111. package/dist/qualified-calls.d.ts +22 -0
  112. package/dist/qualified-calls.d.ts.map +1 -0
  113. package/dist/qualified-calls.js +27 -0
  114. package/dist/regular-expression.d.ts +48 -0
  115. package/dist/regular-expression.d.ts.map +1 -0
  116. package/dist/regular-expression.js +77 -0
  117. package/dist/reserved-words.d.ts +52 -0
  118. package/dist/reserved-words.d.ts.map +1 -0
  119. package/dist/reserved-words.js +77 -0
  120. package/dist/resolved-call.d.ts +46 -0
  121. package/dist/resolved-call.d.ts.map +1 -0
  122. package/dist/resolved-call.js +15 -0
  123. package/dist/root-references.d.ts +23 -0
  124. package/dist/root-references.d.ts.map +1 -0
  125. package/dist/root-references.js +120 -0
  126. package/dist/runtime-library.d.ts +67 -0
  127. package/dist/runtime-library.d.ts.map +1 -0
  128. package/dist/runtime-library.js +554 -0
  129. package/dist/serializer.d.ts +24 -0
  130. package/dist/serializer.d.ts.map +1 -0
  131. package/dist/serializer.js +256 -0
  132. package/dist/sha256.d.ts +20 -0
  133. package/dist/sha256.d.ts.map +1 -0
  134. package/dist/sha256.js +103 -0
  135. package/dist/signature.d.ts +72 -0
  136. package/dist/signature.d.ts.map +1 -0
  137. package/dist/signature.js +61 -0
  138. package/dist/signatures/function-catalog.json +788 -0
  139. package/dist/signatures/standard-library.json +229 -0
  140. package/dist/standard-library.d.ts +41 -0
  141. package/dist/standard-library.d.ts.map +1 -0
  142. package/dist/standard-library.js +85 -0
  143. package/dist/syntax-diagnostic.d.ts +61 -0
  144. package/dist/syntax-diagnostic.d.ts.map +1 -0
  145. package/dist/syntax-diagnostic.js +28 -0
  146. package/dist/syntax-tree.d.ts +160 -0
  147. package/dist/syntax-tree.d.ts.map +1 -0
  148. package/dist/syntax-tree.js +58 -0
  149. package/dist/timestamp-value.d.ts +62 -0
  150. package/dist/timestamp-value.d.ts.map +1 -0
  151. package/dist/timestamp-value.js +238 -0
  152. package/dist/tree-equality.d.ts +15 -0
  153. package/dist/tree-equality.d.ts.map +1 -0
  154. package/dist/tree-equality.js +105 -0
  155. package/dist/type-expression.d.ts +42 -0
  156. package/dist/type-expression.d.ts.map +1 -0
  157. package/dist/type-expression.js +154 -0
  158. package/dist/value-equality.d.ts +38 -0
  159. package/dist/value-equality.d.ts.map +1 -0
  160. package/dist/value-equality.js +196 -0
  161. package/dist/value-text.d.ts +25 -0
  162. package/dist/value-text.d.ts.map +1 -0
  163. package/dist/value-text.js +44 -0
  164. package/dist/zoned-calendar.d.ts +61 -0
  165. package/dist/zoned-calendar.d.ts.map +1 -0
  166. package/dist/zoned-calendar.js +143 -0
  167. package/package.json +56 -3
  168. package/src/activation.ts +32 -0
  169. package/src/backend-runtime.ts +604 -0
  170. package/src/bounded-cache.ts +45 -0
  171. package/src/catalog-runtime.ts +921 -0
  172. package/src/cel-expression.ts +53 -0
  173. package/src/cel-map-value.ts +97 -0
  174. package/src/cel-program.ts +103 -0
  175. package/src/cel-type.ts +359 -0
  176. package/src/cel-value.ts +361 -0
  177. package/src/check-diagnostic.ts +104 -0
  178. package/src/checker.ts +1045 -0
  179. package/src/closure-backend.ts +547 -0
  180. package/src/comprehension-bindings.ts +66 -0
  181. package/src/comprehension-runtime.ts +157 -0
  182. package/src/declared-chain.ts +45 -0
  183. package/src/duration-value.ts +160 -0
  184. package/src/emitted-module.ts +494 -0
  185. package/src/engine-version.ts +9 -0
  186. package/src/environment-digest.ts +111 -0
  187. package/src/environment.ts +761 -0
  188. package/src/function-catalog.ts +140 -0
  189. package/src/function-registry.ts +341 -0
  190. package/src/index.ts +407 -0
  191. package/src/integer-arithmetic.ts +64 -0
  192. package/src/js-emitter.ts +721 -0
  193. package/src/json-schema-type.ts +664 -0
  194. package/src/json-text-scan.ts +163 -0
  195. package/src/lexer.ts +562 -0
  196. package/src/macro-check.ts +197 -0
  197. package/src/macro-shape.ts +66 -0
  198. package/src/member-read.ts +167 -0
  199. package/src/namespace-resolution.ts +167 -0
  200. package/src/nominal-type.ts +149 -0
  201. package/src/nullable-access.ts +95 -0
  202. package/src/parse-limits.ts +36 -0
  203. package/src/parser.ts +554 -0
  204. package/src/qualified-calls.ts +39 -0
  205. package/src/regular-expression.ts +101 -0
  206. package/src/reserved-words.ts +94 -0
  207. package/src/resolved-call.ts +47 -0
  208. package/src/root-references.ts +126 -0
  209. package/src/runtime-library.ts +639 -0
  210. package/src/serializer.ts +262 -0
  211. package/src/sha256.ts +112 -0
  212. package/src/signature.ts +127 -0
  213. package/src/signatures/function-catalog.json +788 -0
  214. package/src/signatures/standard-library.json +235 -0
  215. package/src/standard-library.ts +149 -0
  216. package/src/syntax-diagnostic.ts +72 -0
  217. package/src/syntax-tree.ts +229 -0
  218. package/src/timestamp-value.ts +310 -0
  219. package/src/tree-equality.ts +130 -0
  220. package/src/type-expression.ts +182 -0
  221. package/src/value-equality.ts +201 -0
  222. package/src/value-text.ts +45 -0
  223. package/src/zoned-calendar.ts +178 -0
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The canonical CEL syntax tree.
3
+ *
4
+ * Every node carries `range`, the half-open `[start, end)` span of UTF-16 code
5
+ * units it covers in the source it was parsed from, so a diagnostic, a hover or a
6
+ * rename can point at the text the author wrote. A tree built by hand carries
7
+ * ranges too — the serializer and the queries read them, and a node with none
8
+ * would make either answer silently wrong.
9
+ *
10
+ * Literals hold their decoded value in a tagged form rather than as a bare
11
+ * JavaScript value: CEL's `int` and `uint` are both arbitrary-magnitude integers,
12
+ * `1` and `1u` are different expressions, and a backend must be able to tell them
13
+ * apart without consulting a runtime value class.
14
+ *
15
+ * `qcall` is the one node the parser never produces — see `namespace-resolution.ts`.
16
+ */
17
+ /** Every child node, in source order. The single reader of a node's shape for traversal. */
18
+ export function childNodes(node) {
19
+ switch (node.kind) {
20
+ case "literal":
21
+ case "ident":
22
+ case "unparsed":
23
+ return [];
24
+ case "list":
25
+ return node.elements.map((element) => element.value);
26
+ case "map":
27
+ return node.entries.flatMap((entry) => [entry.key, entry.value]);
28
+ case "select":
29
+ return [node.operand];
30
+ case "index":
31
+ return [node.operand, node.index];
32
+ case "call":
33
+ return node.args;
34
+ case "receiverCall":
35
+ return [node.receiver, ...node.args];
36
+ case "qcall":
37
+ return node.args;
38
+ case "unary":
39
+ return [node.operand];
40
+ case "binary":
41
+ return [node.left, node.right];
42
+ case "conditional":
43
+ return [node.condition, node.whenTrue, node.whenFalse];
44
+ }
45
+ }
46
+ /** Depth-first pre-order walk over the whole tree. */
47
+ export function* walkTree(root) {
48
+ yield root;
49
+ for (const child of childNodes(root))
50
+ yield* walkTree(child);
51
+ }
52
+ /** Whether any node of the tree is an unparsed hole. */
53
+ export function hasUnparsed(root) {
54
+ for (const node of walkTree(root))
55
+ if (node.kind === "unparsed")
56
+ return true;
57
+ return false;
58
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Instants: reading one, writing one, and reading a field of one in a time zone.
3
+ *
4
+ * A timestamp is whole seconds plus nanoseconds in `[0, 1e9)`, because cel-spec's own
5
+ * rows carry nanosecond instants (`string(timestamp('9999-12-31T23:59:59.999999999Z'))`)
6
+ * and no host date type holds them. The range is cel-spec's: `0001-01-01T00:00:00Z`
7
+ * through `9999-12-31T23:59:59.999999999Z`, and an instant outside it is an error
8
+ * wherever it would be built — which is exactly the answer the vectors call
9
+ * domain-keeping where the engine being replaced returns a date beyond year 9999.
10
+ *
11
+ * **A zone is named, never computed here.** A getter takes an IANA name or a fixed
12
+ * `±HH:MM` offset; the rules behind a name are the host's data, which no release of this
13
+ * engine fixes, so the only thing this file decides is how a name is applied.
14
+ */
15
+ import { type CelError, type CelTimestamp } from "./cel-value.js";
16
+ import type { SourceRange } from "./syntax-tree.js";
17
+ /** `0001-01-01T00:00:00Z` and `9999-12-31T23:59:59Z`, in seconds since the epoch. */
18
+ export declare const MIN_TIMESTAMP_SECONDS = -62135596800n;
19
+ export declare const MAX_TIMESTAMP_SECONDS = 253402300799n;
20
+ /** An instant from seconds and nanoseconds of any sign, normalized, or the range error. */
21
+ export declare function celTimestamp(seconds: bigint, nanos?: bigint | number, range?: SourceRange): CelTimestamp | CelError;
22
+ /**
23
+ * An instant from epoch MILLISECONDS — the mirror of `celDurationFromNanos` for a host
24
+ * clock reading (`Date.now()`, a driver's epoch-millis column), or the range error.
25
+ *
26
+ * It exists so that a host never has to reach for its own date type to say "now": the
27
+ * instance type is seconds plus nanos, and a millisecond reading is the one other shape
28
+ * a host actually holds. A fractional reading keeps its sub-millisecond part.
29
+ */
30
+ export declare function celTimestampFromMillis(millis: number, range?: SourceRange): CelTimestamp | CelError;
31
+ /** The instant's nanoseconds since the epoch — what arithmetic and ordering compare. */
32
+ export declare function timestampNanos(value: CelTimestamp): bigint;
33
+ /** An instant from RFC 3339 text, with any offset and any fractional precision. */
34
+ export declare function parseTimestamp(text: string, range?: SourceRange): CelTimestamp | CelError;
35
+ /**
36
+ * RFC 3339 in UTC, with the fraction written to the precision it carries and omitted
37
+ * when the instant is whole — cel-spec's own spelling (`2009-02-13T23:31:30Z`).
38
+ */
39
+ export declare function formatTimestamp(value: CelTimestamp): string;
40
+ export interface CivilFields {
41
+ readonly year: number;
42
+ readonly month: number;
43
+ readonly day: number;
44
+ readonly hour: number;
45
+ readonly minute: number;
46
+ readonly second: number;
47
+ }
48
+ /**
49
+ * The civil fields of an instant in a zone. A zone is an IANA name, resolved by the
50
+ * host's own tz database, or a fixed `±HH:MM` offset, which is arithmetic; an unsigned
51
+ * offset is read as ahead of UTC, as cel-spec's own rows read it.
52
+ */
53
+ export declare function zonedFields(value: CelTimestamp, zone: string | undefined, range?: SourceRange): CivilFields | CelError;
54
+ /** Which field of an instant a getter answers. The names are cel-spec's. */
55
+ export type TimestampField = "getFullYear" | "getMonth" | "getDayOfYear" | "getDayOfMonth" | "getDate" | "getDayOfWeek" | "getHours" | "getMinutes" | "getSeconds" | "getMilliseconds";
56
+ /**
57
+ * A field of an instant. `getMonth`, `getDayOfMonth` and `getDayOfYear` are **zero
58
+ * based** and `getDate` is one based, which is cel-spec's own split; `getDayOfWeek`
59
+ * counts from Sunday.
60
+ */
61
+ export declare function timestampField(value: CelTimestamp, field: TimestampField, zone?: string, range?: SourceRange): bigint | CelError;
62
+ //# sourceMappingURL=timestamp-value.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timestamp-value.d.ts","sourceRoot":"","sources":["../src/timestamp-value.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAIL,KAAK,QAAQ,EACb,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,qFAAqF;AACrF,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AACnD,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AAInD,2FAA2F;AAC3F,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAM,GAAG,MAAU,EAC1B,KAAK,CAAC,EAAE,WAAW,GAClB,YAAY,GAAG,QAAQ,CAYzB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,YAAY,GAAG,QAAQ,CAMnG;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAE1D;AAKD,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,YAAY,GAAG,QAAQ,CAgCzF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAK3D;AAaD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAgED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,KAAK,CAAC,EAAE,WAAW,GAClB,WAAW,GAAG,QAAQ,CAYxB;AAsCD,4EAA4E;AAC5E,MAAM,MAAM,cAAc,GACtB,aAAa,GACb,UAAU,GACV,cAAc,GACd,eAAe,GACf,SAAS,GACT,cAAc,GACd,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,iBAAiB,CAAC;AAEtB;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,cAAc,EACrB,IAAI,CAAC,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,WAAW,GAClB,MAAM,GAAG,QAAQ,CA0BnB"}
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Instants: reading one, writing one, and reading a field of one in a time zone.
3
+ *
4
+ * A timestamp is whole seconds plus nanoseconds in `[0, 1e9)`, because cel-spec's own
5
+ * rows carry nanosecond instants (`string(timestamp('9999-12-31T23:59:59.999999999Z'))`)
6
+ * and no host date type holds them. The range is cel-spec's: `0001-01-01T00:00:00Z`
7
+ * through `9999-12-31T23:59:59.999999999Z`, and an instant outside it is an error
8
+ * wherever it would be built — which is exactly the answer the vectors call
9
+ * domain-keeping where the engine being replaced returns a date beyond year 9999.
10
+ *
11
+ * **A zone is named, never computed here.** A getter takes an IANA name or a fixed
12
+ * `±HH:MM` offset; the rules behind a name are the host's data, which no release of this
13
+ * engine fixes, so the only thing this file decides is how a name is applied.
14
+ */
15
+ import { celError, CEL_VALUE_TYPE, isCelError, } from "./cel-value.js";
16
+ /** `0001-01-01T00:00:00Z` and `9999-12-31T23:59:59Z`, in seconds since the epoch. */
17
+ export const MIN_TIMESTAMP_SECONDS = -62135596800n;
18
+ export const MAX_TIMESTAMP_SECONDS = 253402300799n;
19
+ const NANOS_PER_SECOND = 1000000000n;
20
+ /** An instant from seconds and nanoseconds of any sign, normalized, or the range error. */
21
+ export function celTimestamp(seconds, nanos = 0, range) {
22
+ const total = BigInt(nanos);
23
+ let whole = seconds + total / NANOS_PER_SECOND;
24
+ let rest = total % NANOS_PER_SECOND;
25
+ if (rest < 0n) {
26
+ whole -= 1n;
27
+ rest += NANOS_PER_SECOND;
28
+ }
29
+ if (whole < MIN_TIMESTAMP_SECONDS || whole > MAX_TIMESTAMP_SECONDS) {
30
+ return celError("invalid_conversion", "timestamp out of range", range);
31
+ }
32
+ return { [CEL_VALUE_TYPE]: "google.protobuf.Timestamp", seconds: whole, nanos: Number(rest) };
33
+ }
34
+ /**
35
+ * An instant from epoch MILLISECONDS — the mirror of `celDurationFromNanos` for a host
36
+ * clock reading (`Date.now()`, a driver's epoch-millis column), or the range error.
37
+ *
38
+ * It exists so that a host never has to reach for its own date type to say "now": the
39
+ * instance type is seconds plus nanos, and a millisecond reading is the one other shape
40
+ * a host actually holds. A fractional reading keeps its sub-millisecond part.
41
+ */
42
+ export function celTimestampFromMillis(millis, range) {
43
+ if (!Number.isFinite(millis)) {
44
+ return celError("invalid_conversion", "timestamp out of range", range);
45
+ }
46
+ const whole = Math.floor(millis / 1000);
47
+ return celTimestamp(BigInt(whole), Math.round((millis - whole * 1000) * 1_000_000), range);
48
+ }
49
+ /** The instant's nanoseconds since the epoch — what arithmetic and ordering compare. */
50
+ export function timestampNanos(value) {
51
+ return value.seconds * NANOS_PER_SECOND + BigInt(value.nanos);
52
+ }
53
+ const RFC_3339 = /^(\d{4})-(\d{2})-(\d{2})[Tt ](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(?:[Zz]|([+-])(\d{2}):(\d{2}))$/;
54
+ /** An instant from RFC 3339 text, with any offset and any fractional precision. */
55
+ export function parseTimestamp(text, range) {
56
+ const parts = RFC_3339.exec(text);
57
+ if (!parts) {
58
+ return celError("invalid_conversion", `${JSON.stringify(text)} is not an RFC 3339 instant`, range);
59
+ }
60
+ const [, year, month, day, hour, minute, second, fraction, sign, offsetHours, offsetMinutes] = parts;
61
+ const fields = {
62
+ year: Number(year),
63
+ month: Number(month),
64
+ day: Number(day),
65
+ hour: Number(hour),
66
+ minute: Number(minute),
67
+ second: Number(second),
68
+ };
69
+ if (fields.month < 1 ||
70
+ fields.month > 12 ||
71
+ fields.day < 1 ||
72
+ fields.day > daysInMonth(fields.year, fields.month) ||
73
+ fields.hour > 23 ||
74
+ fields.minute > 59 ||
75
+ fields.second > 59) {
76
+ return celError("invalid_conversion", `${JSON.stringify(text)} is not an instant`, range);
77
+ }
78
+ let seconds = secondsFromFields(fields);
79
+ if (sign) {
80
+ const offset = BigInt(Number(offsetHours) * 3600 + Number(offsetMinutes) * 60);
81
+ seconds += sign === "-" ? offset : -offset;
82
+ }
83
+ const nanos = fraction ? Number(`${fraction}000000000`.slice(0, 9)) : 0;
84
+ return celTimestamp(seconds, nanos, range);
85
+ }
86
+ /**
87
+ * RFC 3339 in UTC, with the fraction written to the precision it carries and omitted
88
+ * when the instant is whole — cel-spec's own spelling (`2009-02-13T23:31:30Z`).
89
+ */
90
+ export function formatTimestamp(value) {
91
+ const fields = utcFields(value.seconds);
92
+ const date = `${pad(fields.year, 4)}-${pad(fields.month, 2)}-${pad(fields.day, 2)}`;
93
+ const time = `${pad(fields.hour, 2)}:${pad(fields.minute, 2)}:${pad(fields.second, 2)}`;
94
+ return `${date}T${time}${fractionText(value.nanos)}Z`;
95
+ }
96
+ function fractionText(nanos) {
97
+ if (nanos === 0)
98
+ return "";
99
+ return `.${pad(nanos, 9).replace(/0+$/, "")}`;
100
+ }
101
+ function pad(value, width) {
102
+ return String(value).padStart(width, "0");
103
+ }
104
+ function isLeapYear(year) {
105
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
106
+ }
107
+ const MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
108
+ function daysInMonth(year, month) {
109
+ return month === 2 && isLeapYear(year) ? 29 : MONTH_DAYS[month - 1];
110
+ }
111
+ /** Days from the epoch to a civil date, by Howard Hinnant's `days_from_civil`. */
112
+ function daysFromCivil(year, month, day) {
113
+ const shifted = month <= 2 ? year - 1 : year;
114
+ const era = Math.floor(shifted / 400);
115
+ const yearOfEra = shifted - era * 400;
116
+ const dayOfYear = Math.floor((153 * (month + (month > 2 ? -3 : 9)) + 2) / 5) + day - 1;
117
+ const dayOfEra = yearOfEra * 365 + Math.floor(yearOfEra / 4) - Math.floor(yearOfEra / 100) + dayOfYear;
118
+ return era * 146097 + dayOfEra - 719468;
119
+ }
120
+ /** The civil date a day count names, the inverse of `daysFromCivil`. */
121
+ function civilFromDays(days) {
122
+ const shifted = days + 719468;
123
+ const era = Math.floor(shifted / 146097);
124
+ const dayOfEra = shifted - era * 146097;
125
+ const yearOfEra = Math.floor((dayOfEra - Math.floor(dayOfEra / 1460) + Math.floor(dayOfEra / 36524) - Math.floor(dayOfEra / 146096)) / 365);
126
+ const year = yearOfEra + era * 400;
127
+ const dayOfYear = dayOfEra - (365 * yearOfEra + Math.floor(yearOfEra / 4) - Math.floor(yearOfEra / 100));
128
+ const monthPrime = Math.floor((5 * dayOfYear + 2) / 153);
129
+ const day = dayOfYear - Math.floor((153 * monthPrime + 2) / 5) + 1;
130
+ const month = monthPrime + (monthPrime < 10 ? 3 : -9);
131
+ return { year: month <= 2 ? year + 1 : year, month, day };
132
+ }
133
+ function secondsFromFields(fields) {
134
+ const days = daysFromCivil(fields.year, fields.month, fields.day);
135
+ return BigInt(days) * 86400n + BigInt(fields.hour * 3600 + fields.minute * 60 + fields.second);
136
+ }
137
+ function utcFields(seconds) {
138
+ const days = Number(floorDiv(seconds, 86400n));
139
+ const rest = Number(seconds - BigInt(days) * 86400n);
140
+ const date = civilFromDays(days);
141
+ return {
142
+ ...date,
143
+ hour: Math.floor(rest / 3600),
144
+ minute: Math.floor((rest % 3600) / 60),
145
+ second: rest % 60,
146
+ };
147
+ }
148
+ function floorDiv(value, by) {
149
+ const quotient = value / by;
150
+ return value % by < 0n ? quotient - 1n : quotient;
151
+ }
152
+ // --- zones -----------------------------------------------------------------
153
+ const FIXED_OFFSET = /^([+-]?)(\d{2}):(\d{2})$/;
154
+ /**
155
+ * The civil fields of an instant in a zone. A zone is an IANA name, resolved by the
156
+ * host's own tz database, or a fixed `±HH:MM` offset, which is arithmetic; an unsigned
157
+ * offset is read as ahead of UTC, as cel-spec's own rows read it.
158
+ */
159
+ export function zonedFields(value, zone, range) {
160
+ if (zone === undefined || zone === "UTC")
161
+ return utcFields(value.seconds);
162
+ const fixed = FIXED_OFFSET.exec(zone);
163
+ if (fixed) {
164
+ const [, sign, hours, minutes] = fixed;
165
+ if (Number(hours) > 23 || Number(minutes) > 59) {
166
+ return celError("invalid_argument", `${JSON.stringify(zone)} is not a time zone`, range);
167
+ }
168
+ const offset = BigInt(Number(hours) * 3600 + Number(minutes) * 60);
169
+ return utcFields(value.seconds + (sign === "-" ? -offset : offset));
170
+ }
171
+ return namedZoneFields(value, zone, range);
172
+ }
173
+ /** The host's answer for an IANA zone. Its rules are the host's data, not this engine's. */
174
+ function namedZoneFields(value, zone, range) {
175
+ let parts;
176
+ try {
177
+ const format = new Intl.DateTimeFormat("en-US", {
178
+ timeZone: zone,
179
+ year: "numeric",
180
+ month: "2-digit",
181
+ day: "2-digit",
182
+ hour: "2-digit",
183
+ minute: "2-digit",
184
+ second: "2-digit",
185
+ hourCycle: "h23",
186
+ era: "short",
187
+ });
188
+ parts = format.formatToParts(new Date(Number(value.seconds) * 1000));
189
+ }
190
+ catch {
191
+ return celError("invalid_argument", `${JSON.stringify(zone)} is not a time zone`, range);
192
+ }
193
+ const read = (type) => Number(parts.find((part) => part.type === type)?.value ?? Number.NaN);
194
+ const era = parts.find((part) => part.type === "era")?.value;
195
+ const year = read("year");
196
+ return {
197
+ year: era === "BC" ? 1 - year : year,
198
+ month: read("month"),
199
+ day: read("day"),
200
+ hour: read("hour"),
201
+ minute: read("minute"),
202
+ second: read("second"),
203
+ };
204
+ }
205
+ /**
206
+ * A field of an instant. `getMonth`, `getDayOfMonth` and `getDayOfYear` are **zero
207
+ * based** and `getDate` is one based, which is cel-spec's own split; `getDayOfWeek`
208
+ * counts from Sunday.
209
+ */
210
+ export function timestampField(value, field, zone, range) {
211
+ if (field === "getMilliseconds")
212
+ return BigInt(Math.floor(value.nanos / 1_000_000));
213
+ const civil = zonedFields(value, zone, range);
214
+ if (isCelError(civil))
215
+ return civil;
216
+ switch (field) {
217
+ case "getFullYear":
218
+ return BigInt(civil.year);
219
+ case "getMonth":
220
+ return BigInt(civil.month - 1);
221
+ case "getDayOfMonth":
222
+ return BigInt(civil.day - 1);
223
+ case "getDate":
224
+ return BigInt(civil.day);
225
+ case "getDayOfYear":
226
+ return BigInt(daysFromCivil(civil.year, civil.month, civil.day) - daysFromCivil(civil.year, 1, 1));
227
+ case "getDayOfWeek": {
228
+ const days = daysFromCivil(civil.year, civil.month, civil.day);
229
+ return BigInt(((days % 7) + 11) % 7);
230
+ }
231
+ case "getHours":
232
+ return BigInt(civil.hour);
233
+ case "getMinutes":
234
+ return BigInt(civil.minute);
235
+ case "getSeconds":
236
+ return BigInt(civil.second);
237
+ }
238
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Whether two trees are the same expression.
3
+ *
4
+ * Ranges are **not** compared: two trees of the same expression written with
5
+ * different spacing or parentheses hold different offsets, and the round-trip
6
+ * contract (`serializer.ts`) is exactly that serializing and re-parsing gives back
7
+ * the same expression — not the same text.
8
+ *
9
+ * Numbers compare by identity rather than by `==`, so `-0.0` is not `0.0` and a
10
+ * `NaN` equals itself: both are distinct literals a round-trip must preserve.
11
+ */
12
+ import type { CelNode } from "./syntax-tree.js";
13
+ /** Whether the two trees are the same expression, their ranges aside. */
14
+ export declare function treesEqual(left: CelNode, right: CelNode): boolean;
15
+ //# sourceMappingURL=tree-equality.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-equality.d.ts","sourceRoot":"","sources":["../src/tree-equality.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAc,OAAO,EAAE,MAAM,kBAAkB,CAAC;AA0B5D,yEAAyE;AACzE,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CA0FjE"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Whether two trees are the same expression.
3
+ *
4
+ * Ranges are **not** compared: two trees of the same expression written with
5
+ * different spacing or parentheses hold different offsets, and the round-trip
6
+ * contract (`serializer.ts`) is exactly that serializing and re-parsing gives back
7
+ * the same expression — not the same text.
8
+ *
9
+ * Numbers compare by identity rather than by `==`, so `-0.0` is not `0.0` and a
10
+ * `NaN` equals itself: both are distinct literals a round-trip must preserve.
11
+ */
12
+ function literalsEqual(left, right) {
13
+ if (left.type !== right.type)
14
+ return false;
15
+ switch (left.type) {
16
+ case "null":
17
+ return true;
18
+ case "int":
19
+ case "uint":
20
+ return left.value === right.value;
21
+ case "double":
22
+ return Object.is(left.value, right.value);
23
+ case "bool":
24
+ case "string":
25
+ return left.value === right.value;
26
+ case "bytes": {
27
+ const other = right.value;
28
+ return left.value.length === other.length && left.value.every((byte, at) => byte === other[at]);
29
+ }
30
+ }
31
+ }
32
+ function listsEqual(left, right) {
33
+ return left.length === right.length && left.every((node, at) => treesEqual(node, right[at]));
34
+ }
35
+ /** Whether the two trees are the same expression, their ranges aside. */
36
+ export function treesEqual(left, right) {
37
+ if (left.kind !== right.kind)
38
+ return false;
39
+ switch (left.kind) {
40
+ case "literal":
41
+ return literalsEqual(left.literal, right.literal);
42
+ case "ident": {
43
+ const other = right;
44
+ // Absolute changes what the name resolves against, so it is part of the expression;
45
+ // whether a member was written between backticks is not.
46
+ return left.name === other.name && left.absolute === other.absolute;
47
+ }
48
+ case "unparsed":
49
+ return true;
50
+ case "list": {
51
+ const other = right.elements;
52
+ return (left.elements.length === other.length &&
53
+ left.elements.every((element, at) => element.optional === other[at].optional && treesEqual(element.value, other[at].value)));
54
+ }
55
+ case "map": {
56
+ const other = right.entries;
57
+ return (left.entries.length === other.length &&
58
+ left.entries.every((entry, at) => entry.optional === other[at].optional &&
59
+ treesEqual(entry.key, other[at].key) &&
60
+ treesEqual(entry.value, other[at].value)));
61
+ }
62
+ case "select": {
63
+ const other = right;
64
+ return (left.field === other.field &&
65
+ left.optional === other.optional &&
66
+ treesEqual(left.operand, other.operand));
67
+ }
68
+ case "index": {
69
+ const other = right;
70
+ return (left.optional === other.optional &&
71
+ treesEqual(left.operand, other.operand) &&
72
+ treesEqual(left.index, other.index));
73
+ }
74
+ case "call": {
75
+ const other = right;
76
+ return left.name === other.name && listsEqual(left.args, other.args);
77
+ }
78
+ case "receiverCall": {
79
+ const other = right;
80
+ return (left.name === other.name &&
81
+ treesEqual(left.receiver, other.receiver) &&
82
+ listsEqual(left.args, other.args));
83
+ }
84
+ case "qcall": {
85
+ const other = right;
86
+ return (left.namespace === other.namespace && left.name === other.name && listsEqual(left.args, other.args));
87
+ }
88
+ case "unary": {
89
+ const other = right;
90
+ return left.operator === other.operator && treesEqual(left.operand, other.operand);
91
+ }
92
+ case "binary": {
93
+ const other = right;
94
+ return (left.operator === other.operator &&
95
+ treesEqual(left.left, other.left) &&
96
+ treesEqual(left.right, other.right));
97
+ }
98
+ case "conditional": {
99
+ const other = right;
100
+ return (treesEqual(left.condition, other.condition) &&
101
+ treesEqual(left.whenTrue, other.whenTrue) &&
102
+ treesEqual(left.whenFalse, other.whenFalse));
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Types as text: `list<int>`, `map<string, int>`, `optional<T>`, `Money<string>`.
3
+ *
4
+ * A host declares a variable's or a signature's type as a string, and the signature
5
+ * data a port reads is text, so one grammar reads both. It is deliberately the same
6
+ * spelling `formatType` writes, so a type that round-trips through text is the type
7
+ * it started as — which is what lets the definition listing and the signature data
8
+ * be compared without a second parser.
9
+ *
10
+ * A bare capital letter is a type **parameter**, which is how a signature says "the
11
+ * same type here and there". A name the host registered as a nominal type resolves
12
+ * to it; any other name is refused rather than guessed — as a
13
+ * {@link CelUnknownTypeNameError}, which a caller holding the name's source may turn
14
+ * into a verdict — because a misspelled type in a signature would otherwise register a
15
+ * function nothing can call.
16
+ */
17
+ import type { CelType } from "./cel-type.js";
18
+ export declare class CelTypeExpressionError extends Error {
19
+ constructor(message: string);
20
+ }
21
+ /**
22
+ * A well-formed type expression naming a type nothing is registered under.
23
+ *
24
+ * It is **not** a `CelTypeExpressionError`, and the split is the whole point: that error
25
+ * is about text this grammar cannot read, while this one is about a name the HOST and its
26
+ * own registry disagree about. A caller that holds where the name was written can turn it
27
+ * into a ranged verdict instead of a crash — which is what a namespace declaration built
28
+ * out of a host's data does — and a caller that cannot still lets it fly.
29
+ */
30
+ export declare class CelUnknownTypeNameError extends Error {
31
+ /** The name nothing is registered under. */
32
+ readonly typeName: string;
33
+ constructor(typeName: string);
34
+ }
35
+ /** What a name that is neither a primitive nor a parameter resolves to. */
36
+ export type NominalResolver = (name: string, args: readonly CelType[]) => CelType | undefined;
37
+ /**
38
+ * Reads one type expression. `resolveNominal` is consulted for every name the
39
+ * language itself does not define.
40
+ */
41
+ export declare function parseTypeExpression(text: string, resolveNominal?: NominalResolver): CelType;
42
+ //# sourceMappingURL=type-expression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-expression.d.ts","sourceRoot":"","sources":["../src/type-expression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,eAAe,CAAC;AAoB5D,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;GAQG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM;CAK7B;AAmBD,2EAA2E;AAC3E,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,KAAK,OAAO,GAAG,SAAS,CAAC;AAE9F;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,eAAe,GAAG,OAAO,CAK3F"}