@telorun/templating 0.8.0 → 0.9.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/README.md CHANGED
@@ -55,8 +55,8 @@ metadata:
55
55
  A complete feedback collection REST API — no code, pure YAML.
56
56
  Persists entries to SQLite and serves them over HTTP.
57
57
  imports:
58
- Http: std/http-server@0.9.0
59
- Sql: std/sql@0.8.0
58
+ Http: std/http-server@0.11.0
59
+ Sql: std/sql@0.9.0
60
60
  targets:
61
61
  - !ref Migrations
62
62
  - !ref Server
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/cel/catalog.ts"],"names":[],"mappings":"AAEA;;;0DAG0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAClC;AAED,MAAM,MAAM,mBAAmB,GAC3B,YAAY,GACZ,MAAM,GACN,MAAM,GACN,QAAQ,GACR,MAAM,GACN,YAAY,GACZ,MAAM,GACN,UAAU,GACV,SAAS,GACT,MAAM,CAAC;AAEX;;;qBAGqB;AACrB,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;0EACsE;IACtE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;yDAIqD;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;wBACoB;IACpB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;2EACuE;IACvE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;CACjE;AAED,wEAAwE;AACxE,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;AAyE5D,eAAO,MAAM,aAAa,EAAE,SAAS,cAAc,EAoZlD,CAAC;AAEF,oEAAoE;AACpE,wBAAgB,kBAAkB,IAAI,eAAe,EAAE,CAEtD"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/cel/catalog.ts"],"names":[],"mappings":"AAEA;;;0DAG0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAClC;AAED,MAAM,MAAM,mBAAmB,GAC3B,YAAY,GACZ,MAAM,GACN,MAAM,GACN,QAAQ,GACR,MAAM,GACN,YAAY,GACZ,MAAM,GACN,UAAU,GACV,SAAS,GACT,MAAM,CAAC;AAEX;;;qBAGqB;AACrB,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;0EACsE;IACtE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;yDAIqD;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;wBACoB;IACpB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;2EACuE;IACvE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;CACjE;AAED,wEAAwE;AACxE,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;AA8E5D,eAAO,MAAM,aAAa,EAAE,SAAS,cAAc,EAoelD,CAAC;AAEF,oEAAoE;AACpE,wBAAgB,kBAAkB,IAAI,eAAe,EAAE,CAEtD"}
@@ -14,6 +14,9 @@ const minMax = (list, isMin) => {
14
14
  }
15
15
  return best;
16
16
  };
17
+ /** Ensure a regex replaces every match: append the global flag unless the
18
+ * caller already passed it. */
19
+ const withGlobal = (flags) => flags && flags.includes("g") ? flags : `${flags ?? ""}g`;
17
20
  const sortList = (list) => [...list].sort((a, b) => {
18
21
  if (typeof a === "number" || typeof a === "bigint") {
19
22
  const d = num(a) - num(b);
@@ -130,6 +133,24 @@ export const CEL_FUNCTIONS = [
130
133
  hostBacked: false,
131
134
  build: () => (list) => sortList(list),
132
135
  },
136
+ {
137
+ name: "range",
138
+ signature: "range(int): list<int>",
139
+ category: "collection",
140
+ summary: "Integers [0, n-1] (empty for n <= 0); materializes indices for an unknown-length list.",
141
+ deterministic: true,
142
+ hostBacked: false,
143
+ build: () => (n) => Array.from({ length: Math.max(0, Math.trunc(num(n))) }, (_unused, i) => BigInt(i)),
144
+ },
145
+ {
146
+ name: "enumerate",
147
+ signature: "enumerate(list): list",
148
+ category: "collection",
149
+ summary: "Pair each element with its zero-based position as {index, value}.",
150
+ deterministic: true,
151
+ hostBacked: false,
152
+ build: () => (list) => list.map((value, i) => ({ index: BigInt(i), value })),
153
+ },
133
154
  // Strings
134
155
  {
135
156
  name: "lower",
@@ -176,6 +197,63 @@ export const CEL_FUNCTIONS = [
176
197
  hostBacked: false,
177
198
  build: () => (s, sep) => s.split(sep),
178
199
  },
200
+ {
201
+ name: "regexReplace",
202
+ signature: "regexReplace(string, string, string, string?): string",
203
+ category: "string",
204
+ summary: "Replace every regex match with a replacement ($1 backrefs); flags like 'i', 'm', 's'.",
205
+ deterministic: true,
206
+ hostBacked: false,
207
+ build: () => (s, pattern, replacement, flags) => s.replace(new RegExp(pattern, withGlobal(flags)), replacement),
208
+ },
209
+ {
210
+ name: "regexExtract",
211
+ signature: "regexExtract(string, string): string",
212
+ category: "string",
213
+ summary: "First whole match of a regex, or '' when there is none.",
214
+ deterministic: true,
215
+ hostBacked: false,
216
+ build: () => (s, pattern) => s.match(new RegExp(pattern))?.[0] ?? "",
217
+ },
218
+ {
219
+ name: "regexExtractAll",
220
+ signature: "regexExtractAll(string, string): list<string>",
221
+ category: "string",
222
+ summary: "Every whole match of a regex, in order.",
223
+ deterministic: true,
224
+ hostBacked: false,
225
+ build: () => (s, pattern) => [...s.matchAll(new RegExp(pattern, "g"))].map((m) => m[0]),
226
+ },
227
+ {
228
+ name: "regexGroups",
229
+ signature: "regexGroups(string, string): list<string>",
230
+ category: "string",
231
+ summary: "Capture groups of the first regex match (empty list when there is no match).",
232
+ deterministic: true,
233
+ hostBacked: false,
234
+ build: () => (s, pattern) => {
235
+ const m = s.match(new RegExp(pattern));
236
+ return m ? m.slice(1).map((g) => g ?? "") : [];
237
+ },
238
+ },
239
+ {
240
+ name: "trimPrefix",
241
+ signature: "trimPrefix(string, string): string",
242
+ category: "string",
243
+ summary: "Strip a leading prefix if present.",
244
+ deterministic: true,
245
+ hostBacked: false,
246
+ build: () => (s, prefix) => (s.startsWith(prefix) ? s.slice(prefix.length) : s),
247
+ },
248
+ {
249
+ name: "trimSuffix",
250
+ signature: "trimSuffix(string, string): string",
251
+ category: "string",
252
+ summary: "Strip a trailing suffix if present.",
253
+ deterministic: true,
254
+ hostBacked: false,
255
+ build: () => (s, suffix) => suffix && s.endsWith(suffix) ? s.slice(0, s.length - suffix.length) : s,
256
+ },
179
257
  // Math
180
258
  {
181
259
  name: "abs",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/templating",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Telo Templating - Engine registry and shared CEL core for Telo manifests.",
5
5
  "keywords": [
6
6
  "telo",
@@ -44,7 +44,7 @@
44
44
  "@types/uuid": "^10.0.0",
45
45
  "typescript": "^5.0.0",
46
46
  "vitest": "^2.1.8",
47
- "@telorun/sdk": "0.23.0"
47
+ "@telorun/sdk": "0.26.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@telorun/sdk": "*"
@@ -73,6 +73,11 @@ const minMax = (list: unknown[], isMin: boolean): unknown => {
73
73
  return best;
74
74
  };
75
75
 
76
+ /** Ensure a regex replaces every match: append the global flag unless the
77
+ * caller already passed it. */
78
+ const withGlobal = (flags?: string): string =>
79
+ flags && flags.includes("g") ? flags : `${flags ?? ""}g`;
80
+
76
81
  const sortList = (list: unknown[]): unknown[] =>
77
82
  [...list].sort((a, b) => {
78
83
  if (typeof a === "number" || typeof a === "bigint") {
@@ -195,6 +200,26 @@ export const CEL_FUNCTIONS: readonly CelFunctionDoc[] = [
195
200
  hostBacked: false,
196
201
  build: () => (list: unknown[]) => sortList(list),
197
202
  },
203
+ {
204
+ name: "range",
205
+ signature: "range(int): list<int>",
206
+ category: "collection",
207
+ summary: "Integers [0, n-1] (empty for n <= 0); materializes indices for an unknown-length list.",
208
+ deterministic: true,
209
+ hostBacked: false,
210
+ build: () => (n: unknown) =>
211
+ Array.from({ length: Math.max(0, Math.trunc(num(n))) }, (_unused, i) => BigInt(i)),
212
+ },
213
+ {
214
+ name: "enumerate",
215
+ signature: "enumerate(list): list",
216
+ category: "collection",
217
+ summary: "Pair each element with its zero-based position as {index, value}.",
218
+ deterministic: true,
219
+ hostBacked: false,
220
+ build: () => (list: unknown[]) =>
221
+ list.map((value, i) => ({ index: BigInt(i), value })),
222
+ },
198
223
  // Strings
199
224
  {
200
225
  name: "lower",
@@ -241,6 +266,66 @@ export const CEL_FUNCTIONS: readonly CelFunctionDoc[] = [
241
266
  hostBacked: false,
242
267
  build: () => (s: string, sep: string) => s.split(sep),
243
268
  },
269
+ {
270
+ name: "regexReplace",
271
+ signature: "regexReplace(string, string, string, string?): string",
272
+ category: "string",
273
+ summary: "Replace every regex match with a replacement ($1 backrefs); flags like 'i', 'm', 's'.",
274
+ deterministic: true,
275
+ hostBacked: false,
276
+ build: () => (s: string, pattern: string, replacement: string, flags?: string) =>
277
+ s.replace(new RegExp(pattern, withGlobal(flags)), replacement),
278
+ },
279
+ {
280
+ name: "regexExtract",
281
+ signature: "regexExtract(string, string): string",
282
+ category: "string",
283
+ summary: "First whole match of a regex, or '' when there is none.",
284
+ deterministic: true,
285
+ hostBacked: false,
286
+ build: () => (s: string, pattern: string) => s.match(new RegExp(pattern))?.[0] ?? "",
287
+ },
288
+ {
289
+ name: "regexExtractAll",
290
+ signature: "regexExtractAll(string, string): list<string>",
291
+ category: "string",
292
+ summary: "Every whole match of a regex, in order.",
293
+ deterministic: true,
294
+ hostBacked: false,
295
+ build: () => (s: string, pattern: string) =>
296
+ [...s.matchAll(new RegExp(pattern, "g"))].map((m) => m[0]),
297
+ },
298
+ {
299
+ name: "regexGroups",
300
+ signature: "regexGroups(string, string): list<string>",
301
+ category: "string",
302
+ summary: "Capture groups of the first regex match (empty list when there is no match).",
303
+ deterministic: true,
304
+ hostBacked: false,
305
+ build: () => (s: string, pattern: string) => {
306
+ const m = s.match(new RegExp(pattern));
307
+ return m ? m.slice(1).map((g) => g ?? "") : [];
308
+ },
309
+ },
310
+ {
311
+ name: "trimPrefix",
312
+ signature: "trimPrefix(string, string): string",
313
+ category: "string",
314
+ summary: "Strip a leading prefix if present.",
315
+ deterministic: true,
316
+ hostBacked: false,
317
+ build: () => (s: string, prefix: string) => (s.startsWith(prefix) ? s.slice(prefix.length) : s),
318
+ },
319
+ {
320
+ name: "trimSuffix",
321
+ signature: "trimSuffix(string, string): string",
322
+ category: "string",
323
+ summary: "Strip a trailing suffix if present.",
324
+ deterministic: true,
325
+ hostBacked: false,
326
+ build: () => (s: string, suffix: string) =>
327
+ suffix && s.endsWith(suffix) ? s.slice(0, s.length - suffix.length) : s,
328
+ },
244
329
  // Math
245
330
  {
246
331
  name: "abs",