@telorun/templating 0.5.0 → 0.7.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.
@@ -1,10 +1,8 @@
1
1
  import { Environment } from "@marcbachmann/cel-js";
2
2
  import { Stream } from "@telorun/sdk";
3
+ import { CEL_FUNCTIONS, type CelHandlers } from "./catalog.js";
3
4
 
4
- export interface CelHandlers {
5
- sha256: (s: string) => string;
6
- json: (value: unknown) => string;
7
- }
5
+ export type { CelHandlers } from "./catalog.js";
8
6
 
9
7
  const stub = (name: string) => () => {
10
8
  throw new Error(
@@ -15,13 +13,22 @@ const stub = (name: string) => () => {
15
13
 
16
14
  const STUB_HANDLERS: CelHandlers = {
17
15
  sha256: stub("sha256"),
16
+ md5: stub("md5"),
17
+ sha1: stub("sha1"),
18
+ sha512: stub("sha512"),
19
+ hmac: stub("hmac"),
20
+ base64Encode: stub("base64Encode"),
21
+ base64Decode: stub("base64Decode"),
18
22
  json: stub("json"),
19
23
  };
20
24
 
21
- /** Build a CEL `Environment` with Telo's stdlib of functions. Always registers the
22
- * same function signatures (so `env.check()` succeeds for type-inference) the
23
- * handlers govern what the function does when called at runtime. Analyzer-only
24
- * callers can omit handlers; runtime callers (kernel) must supply real ones.
25
+ /** Build a CEL `Environment` with Telo's stdlib. Every function comes from the
26
+ * single-source catalog (`CEL_FUNCTIONS`), so registration and the documented
27
+ * surface (`telo cel functions`) can never drift. Always registers the same
28
+ * signatures (so `env.check()` succeeds for type-inference); the host-injected
29
+ * handlers govern what `hostBacked` functions do at runtime. Analyzer-only
30
+ * callers can omit handlers (the stubs throw if such a function is evaluated);
31
+ * runtime callers (kernel) supply real ones.
25
32
  *
26
33
  * Also registers the `Stream` object type, backed by the `Stream` class from
27
34
  * `@telorun/sdk`. CEL's type-checker rejects values whose constructor isn't
@@ -31,20 +38,51 @@ const STUB_HANDLERS: CelHandlers = {
31
38
  * no fields, so terminal access (passing the value through CEL) succeeds but
32
39
  * member access raises a CEL error at runtime — matching the analyzer's
33
40
  * static check on `x-telo-stream`-marked properties. */
34
- export function buildCelEnvironment(handlers: CelHandlers = STUB_HANDLERS): Environment {
35
- return new Environment({ unlistedVariablesAreDyn: true, enableOptionalTypes: true })
36
- .registerFunction("join(list, string): string", (list: unknown[], sep: string) =>
37
- list.map(String).join(sep),
38
- )
39
- .registerFunction("keys(map): list", (map: unknown) => {
40
- if (map instanceof Map) return [...map.keys()];
41
- return Object.keys(map as Record<string, unknown>);
42
- })
43
- .registerFunction("values(map): list", (map: unknown) => {
44
- if (map instanceof Map) return [...map.values()];
45
- return Object.values(map as Record<string, unknown>);
46
- })
47
- .registerFunction("sha256(string): string", (s: string) => handlers.sha256(s))
48
- .registerFunction("json(dyn): string", (value: unknown) => handlers.json(value))
49
- .registerType("Stream", Stream as unknown as new (...args: unknown[]) => unknown);
41
+ /** Expand a documented signature that may contain `type?`-marked optional
42
+ * parameters into one cel-js registration signature per arity. For example,
43
+ * `"nowIso(string?): string"` produces `["nowIso(): string",
44
+ * "nowIso(string): string"]`. Required parameters must precede optional ones.
45
+ * Returns `[signature]` unchanged when no `?` is present or the signature
46
+ * cannot be parsed. */
47
+ export function deriveSignatures(signature: string): string[] {
48
+ const m = signature.match(/^(\w+)\((.*?)\):\s*(.+)$/);
49
+ if (!m) return [signature];
50
+ const name = m[1]!;
51
+ const paramsStr = m[2]!.trim();
52
+ const returnType = m[3]!.trim();
53
+ if (!paramsStr.includes("?")) return [signature];
54
+
55
+ const params = paramsStr.split(",").map((p) => p.trim());
56
+ const required: string[] = [];
57
+ const optional: string[] = [];
58
+ for (const p of params) {
59
+ if (p.endsWith("?")) {
60
+ optional.push(p.slice(0, -1));
61
+ } else {
62
+ if (optional.length > 0) return [signature];
63
+ required.push(p);
64
+ }
65
+ }
66
+ if (optional.length === 0) return [signature];
67
+
68
+ return Array.from({ length: optional.length + 1 }, (_, i) => {
69
+ const allParams = [...required, ...optional.slice(0, i)];
70
+ return `${name}(${allParams.join(", ")}): ${returnType}`;
71
+ });
72
+ }
73
+
74
+ export function buildCelEnvironment(handlers: Partial<CelHandlers> = {}): Environment {
75
+ const h: CelHandlers = { ...STUB_HANDLERS, ...handlers };
76
+ let env = new Environment({ unlistedVariablesAreDyn: true, enableOptionalTypes: true });
77
+ for (const fn of CEL_FUNCTIONS) {
78
+ const impl = fn.build(h);
79
+ // `register` lists one cel-js signature per arity (overloaded functions).
80
+ // When absent, `deriveSignatures` expands `type?` optional-param notation
81
+ // into one registration per arity — so `nowIso(string?): string` registers
82
+ // both `nowIso(): string` and `nowIso(string): string` automatically.
83
+ for (const sig of fn.register ?? deriveSignatures(fn.signature)) {
84
+ env = env.registerFunction(sig, impl);
85
+ }
86
+ }
87
+ return env.registerType("Stream", Stream as unknown as new (...args: unknown[]) => unknown);
50
88
  }
package/src/index.ts CHANGED
@@ -1,4 +1,11 @@
1
1
  export { buildCelEnvironment, type CelHandlers } from "./cel/environment.js";
2
+ export {
3
+ celFunctionCatalog,
4
+ CEL_FUNCTIONS,
5
+ type CelFunctionInfo,
6
+ type CelFunctionDoc,
7
+ type CelFunctionCategory,
8
+ } from "./cel/catalog.js";
2
9
  export {
3
10
  compileExpression,
4
11
  compileString,