@objectstack/cli 9.5.1 → 9.6.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 (38) hide show
  1. package/dist/commands/compile.d.ts.map +1 -1
  2. package/dist/commands/compile.js +15 -0
  3. package/dist/commands/compile.js.map +1 -1
  4. package/dist/commands/dev.js +1 -1
  5. package/dist/commands/dev.js.map +1 -1
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +0 -2
  8. package/dist/commands/init.js.map +1 -1
  9. package/dist/commands/serve.d.ts.map +1 -1
  10. package/dist/commands/serve.js +53 -14
  11. package/dist/commands/serve.js.map +1 -1
  12. package/dist/commands/start.js +1 -1
  13. package/dist/commands/start.js.map +1 -1
  14. package/dist/utils/detect-free-identifiers.d.ts +13 -0
  15. package/dist/utils/detect-free-identifiers.d.ts.map +1 -0
  16. package/dist/utils/detect-free-identifiers.js +236 -0
  17. package/dist/utils/detect-free-identifiers.js.map +1 -0
  18. package/dist/utils/detect-free-identifiers.test.d.ts +2 -0
  19. package/dist/utils/detect-free-identifiers.test.d.ts.map +1 -0
  20. package/dist/utils/detect-free-identifiers.test.js +75 -0
  21. package/dist/utils/detect-free-identifiers.test.js.map +1 -0
  22. package/dist/utils/extract-hook-body.d.ts.map +1 -1
  23. package/dist/utils/extract-hook-body.js +20 -0
  24. package/dist/utils/extract-hook-body.js.map +1 -1
  25. package/dist/utils/lint-flow-patterns.d.ts +31 -0
  26. package/dist/utils/lint-flow-patterns.d.ts.map +1 -0
  27. package/dist/utils/lint-flow-patterns.js +109 -0
  28. package/dist/utils/lint-flow-patterns.js.map +1 -0
  29. package/dist/utils/lint-flow-patterns.test.d.ts +2 -0
  30. package/dist/utils/lint-flow-patterns.test.d.ts.map +1 -0
  31. package/dist/utils/lint-flow-patterns.test.js +80 -0
  32. package/dist/utils/lint-flow-patterns.test.js.map +1 -0
  33. package/dist/utils/validate-expressions.d.ts.map +1 -1
  34. package/dist/utils/validate-expressions.js +34 -0
  35. package/dist/utils/validate-expressions.js.map +1 -1
  36. package/dist/utils/validate-expressions.test.js +59 -0
  37. package/dist/utils/validate-expressions.test.js.map +1 -1
  38. package/package.json +54 -54
@@ -0,0 +1,75 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+ import { describe, it, expect } from 'vitest';
3
+ import { detectFreeIdentifiers } from './detect-free-identifiers.js';
4
+ /** Helper: stringify a real function so we test the exact `.toString()` path. */
5
+ const src = (fn) => String(fn);
6
+ describe('detectFreeIdentifiers (#1876 — body self-containment)', () => {
7
+ describe('flags module-scope references (would ReferenceError at runtime)', () => {
8
+ it('a helper call (arrow)', () => {
9
+ const r = detectFreeIdentifiers('(ctx) => { ctx.record.slug = slugify(ctx.record.name); }');
10
+ expect(r.unparsed).toBe(false);
11
+ expect(r.free).toEqual(['slugify']);
12
+ });
13
+ it('a top-level const (function expression)', () => {
14
+ const r = detectFreeIdentifiers('function h(ctx){ return TAX_RATE * ctx.amount; }');
15
+ expect(r.free).toEqual(['TAX_RATE']);
16
+ });
17
+ it('object-method shorthand form', () => {
18
+ const r = detectFreeIdentifiers('handler(ctx){ return fmt(ctx.x); }');
19
+ expect(r.free).toEqual(['fmt']);
20
+ });
21
+ it('an implicit-return arrow', () => {
22
+ const r = detectFreeIdentifiers('(ctx) => compute(ctx.a, ctx.b)');
23
+ expect(r.free).toEqual(['compute']);
24
+ });
25
+ it('multiple distinct free names, sorted & de-duped', () => {
26
+ const r = detectFreeIdentifiers('(ctx) => { a(ctx); b(ctx); a(ctx); return CONST; }');
27
+ expect(r.free).toEqual(['CONST', 'a', 'b']);
28
+ });
29
+ });
30
+ describe('does NOT flag self-contained handlers (false-positive guards)', () => {
31
+ const selfContained = [
32
+ ['member access only', '(ctx) => { ctx.record.slug = ctx.record.name.toLowerCase(); }'],
33
+ ['local const', '(ctx) => { const s = ctx.record.name; return s.trim(); }'],
34
+ ['globals (Math/JSON)', '(ctx) => { ctx.record.id = Math.round(JSON.parse(ctx.x).y); }'],
35
+ ['destructured params', '({ record, api }) => { record.x = record.y; return api; }'],
36
+ ['locally-declared helper', '(ctx) => { const f = (a) => a * 2; return f(ctx.n); }'],
37
+ ['object shorthand of a local', '(ctx) => { const a = ctx.a; return { a }; }'],
38
+ ['object literal keys', '(ctx) => ({ total: ctx.a, count: ctx.b })'],
39
+ ['for-of loop binding', '(ctx) => { let sum = 0; for (const x of ctx.items) { sum += x; } ctx.sum = sum; }'],
40
+ ['catch binding', '(ctx) => { try { ctx.run(); } catch (err) { ctx.log = err; } }'],
41
+ ['param default uses global', '({ x = Math.PI }) => x'],
42
+ ['returned object method closes over param', '(ctx) => ({ run() { return ctx.x; } })'],
43
+ ['element access with local key', '(ctx) => { const k = ctx.key; return ctx.data[k]; }'],
44
+ ['named function expression recursion', 'function fact(n){ return n <= 1 ? 1 : n * fact(n - 1); }'],
45
+ ['nested destructuring', '({ a: { b } }) => b + 1'],
46
+ ['rest params', '(...args) => args.length'],
47
+ ['typeof a local', '(ctx) => { const v = ctx.v; return typeof v; }'],
48
+ ];
49
+ for (const [label, source] of selfContained) {
50
+ it(label, () => {
51
+ const r = detectFreeIdentifiers(source);
52
+ expect(r.unparsed).toBe(false);
53
+ expect(r.free).toEqual([]);
54
+ });
55
+ }
56
+ });
57
+ describe('real compiled `.toString()` shapes', () => {
58
+ it('does not flag a self-contained closure', () => {
59
+ const handler = (ctx) => {
60
+ const name = String(ctx.record.name ?? '').trim();
61
+ ctx.record.slug = name.toLowerCase().replace(/\s+/g, '-');
62
+ };
63
+ expect(detectFreeIdentifiers(src(handler)).free).toEqual([]);
64
+ });
65
+ });
66
+ it('never invents free vars for non-handler junk (conservative — caller won\'t block)', () => {
67
+ // Whether or not TS error-recovers a node, the safe outcome is no free vars
68
+ // so extraction is never blocked on garbage. (peelToBlockBody rejects such
69
+ // input earlier in the real path anyway.)
70
+ expect(detectFreeIdentifiers('this is not a function').free).toEqual([]);
71
+ expect(detectFreeIdentifiers('').free).toEqual([]);
72
+ expect(detectFreeIdentifiers('{ not: valid').free).toEqual([]);
73
+ });
74
+ });
75
+ //# sourceMappingURL=detect-free-identifiers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-free-identifiers.test.js","sourceRoot":"","sources":["../../src/utils/detect-free-identifiers.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"extract-hook-body.d.ts","sourceRoot":"","sources":["../../src/utils/extract-hook-body.ts"],"names":[],"mappings":"AAgDA,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,YAAY,EAAE,KAAK,CAAC,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,aAAa,GAAG,KAAK,CAAC,CAAC;IACtF,4EAA4E;IAC5E,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,aAAa,CAmDpG"}
1
+ {"version":3,"file":"extract-hook-body.d.ts","sourceRoot":"","sources":["../../src/utils/extract-hook-body.ts"],"names":[],"mappings":"AAuDA,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,YAAY,EAAE,KAAK,CAAC,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,aAAa,GAAG,KAAK,CAAC,CAAC;IACtF,4EAA4E;IAC5E,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,aAAa,CAoEpG"}
@@ -20,7 +20,13 @@
20
20
  * `ctx.crypto.*` access patterns and add the matching capability tokens to
21
21
  * `body.capabilities` automatically. Authors can still override by setting
22
22
  * `// @capabilities api.read api.write` as the first line of the function.
23
+ *
24
+ * Self-containment (#1876): a handler that references a module-scope identifier
25
+ * (helper, import, top-level const) cannot be shipped body-only — the reference
26
+ * would `ReferenceError` at runtime. {@link detectFreeIdentifiers} finds those;
27
+ * extraction throws so the caller falls back to bundling the real closure.
23
28
  */
29
+ import { detectFreeIdentifiers } from './detect-free-identifiers.js';
24
30
  const FORBIDDEN_PATTERNS = [
25
31
  { rx: /\bimport\s*[\(\*\{]/, reason: 'dynamic `import()` and ES imports are not allowed in hook/action bodies — declare a Connector recipe instead' },
26
32
  { rx: /\brequire\s*\(/, reason: '`require()` is not allowed in hook/action bodies' },
@@ -61,6 +67,20 @@ export function extractHookBody(fn, originLabel) {
61
67
  `--- offending body source ---\n${block.source.slice(0, 400)}${block.source.length > 400 ? '…' : ''}`);
62
68
  }
63
69
  }
70
+ // #1876 — a handler that references a MODULE-SCOPE identifier (a helper, an
71
+ // imported binding, a top-level const) is NOT self-contained: shipping it as
72
+ // a metadata-only `body` would throw `ReferenceError` at runtime (the
73
+ // reference ships without its definition). Detect those free identifiers and
74
+ // throw — the caller catches this and keeps the handler in the BUNDLED form,
75
+ // where esbuild carries the real closure along. The whole `fn` source (params
76
+ // included) is analyzed so parameters are correctly in scope.
77
+ const { free, unparsed } = detectFreeIdentifiers(raw);
78
+ if (!unparsed && free.length > 0) {
79
+ throw new Error(`[hook-body-extract] ${originLabel}: handler references identifier(s) not in scope at runtime: ` +
80
+ `${free.join(', ')}. Module-scope helpers/imports aren't shipped with a metadata-only body, so ` +
81
+ `this handler will be BUNDLED instead (no behavior change). To make it body-only, inline the ` +
82
+ `helper(s) into the handler or move the logic behind \`ctx\` (e.g. \`ctx.api\`).`);
83
+ }
64
84
  // Infer capabilities from API surface usage.
65
85
  const inferred = new Set();
66
86
  for (const { rx, cap } of CAPABILITY_PATTERNS) {
@@ -1 +1 @@
1
- {"version":3,"file":"extract-hook-body.js","sourceRoot":"","sources":["../../src/utils/extract-hook-body.ts"],"names":[],"mappings":"AAAA,yEAAyE;AAEzE;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,kBAAkB,GAA0C;IAChE,EAAE,EAAE,EAAE,qBAAqB,EAAE,MAAM,EAAE,8GAA8G,EAAE;IACrJ,EAAE,EAAE,EAAE,gBAAgB,EAAE,MAAM,EAAE,kDAAkD,EAAE;IACpF,EAAE,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,qFAAqF,EAAE;IACrH,EAAE,EAAE,EAAE,gBAAgB,EAAE,MAAM,EAAE,uDAAuD,EAAE;IACzF,EAAE,EAAE,EAAE,mBAAmB,EAAE,MAAM,EAAE,0DAA0D,EAAE;IAC/F,EAAE,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,+CAA+C,EAAE;IAC9E,EAAE,EAAE,EAAE,uBAAuB,EAAE,MAAM,EAAE,uDAAuD,EAAE;CACjG,CAAC;AAEF,MAAM,mBAAmB,GAAiG;IACxH,6DAA6D;IAC7D,+DAA+D;IAC/D,qEAAqE;IACrE,mEAAmE;IACnE,uEAAuE;IACvE,EAAE,EAAE,EAAE,yEAAyE,EAAE,GAAG,EAAE,UAAU,EAAE;IAClG,EAAE,EAAE,EAAE,mFAAmF,EAAE,GAAG,EAAE,WAAW,EAAE;IAC7G,EAAE,EAAE,EAAE,2BAA2B,EAAE,GAAG,EAAE,aAAa,EAAE;IACvD,EAAE,EAAE,EAAE,qBAAqB,EAAE,GAAG,EAAE,aAAa,EAAE;IACjD,EAAE,EAAE,EAAE,uCAAuC,EAAE,GAAG,EAAE,KAAK,EAAE;CAC5D,CAAC;AAWF;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,EAAgC,EAAE,WAAmB;IACnF,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;IAEvB,wEAAwE;IACxE,wEAAwE;IACxE,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,mDAAmD,WAAW,IAAI;YAChE,oFAAoF,CACvF,CAAC;IACJ,CAAC;IAED,oEAAoE;IACpE,KAAK,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,kBAAkB,EAAE,CAAC;QAChD,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,uBAAuB,WAAW,KAAK,MAAM,IAAI;gBAC/C,kCAAkC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACxG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyC,CAAC;IAClE,KAAK,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,mBAAmB,EAAE,CAAC;QAC9C,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;IAED,sEAAsE;IACtE,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACxF,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7D,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,IACE,CAAC,KAAK,UAAU;gBAChB,CAAC,KAAK,WAAW;gBACjB,CAAC,KAAK,aAAa;gBACnB,CAAC,KAAK,aAAa;gBACnB,CAAC,KAAK,KAAK,EACX,CAAC;gBACD,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,YAAY,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,EAAE;QAClC,YAAY,EAAE,KAAK,CAAC,YAAY;KACjC,CAAC;AACJ,CAAC;AAOD;;;GAGG;AACH,SAAS,eAAe,CAAC,GAAW;IAClC,oEAAoE;IACpE,wEAAwE;IACxE,sEAAsE;IACtE,oDAAoD;IACpD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;QAClD,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YAC5C,IAAI,IAAI;gBAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QAC/D,CAAC;aAAM,CAAC;YACN,gDAAgD;YAChD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YAC3C,OAAO,EAAE,MAAM,EAAE,WAAW,IAAI,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,kDAAkD;IAClD,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IACzC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACf,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACzC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YAC1D,IAAI,IAAI;gBAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QAC1D,IAAI,IAAI;YAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAC/D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAS;IAClC,8DAA8D;IAC9D,iEAAiE;IACjE,4EAA4E;IAC5E,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC;IACrB,OAAO,CAAC,GAAG,GAAG,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YACnD,IAAI,OAAO,GAAG,CAAC;gBAAE,OAAO,CAAC,CAAC,CAAC;YAC3B,IAAI,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;YACpB,OAAO,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAAE,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG;gBAAE,OAAO,CAAC,CAAC;YAC/C,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;YAChB,SAAS;QACX,CAAC;QACD,CAAC,EAAE,CAAC;IACN,CAAC;IACD,OAAO,CAAC,CAAC,CAAC;AACZ,CAAC;AAED,SAAS,kBAAkB,CAAC,CAAS,EAAE,KAAa,EAAE,IAAY,EAAE,KAAa;IAC/E,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACf,IAAI,CAAC,KAAK,IAAI;YAAE,KAAK,EAAE,CAAC;aACnB,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC;YACrB,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAO,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC,CAAC;AACZ,CAAC;AAED,SAAS,aAAa,CAAC,CAAS,EAAE,IAAY,EAAE,KAAa;IAC3D,MAAM,GAAG,GAAG,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAClD,IAAI,GAAG,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"extract-hook-body.js","sourceRoot":"","sources":["../../src/utils/extract-hook-body.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Build-time lint for flow authoring ANTI-PATTERNS — metadata that is valid
3
+ * (passes schema + expression checks) but is semantically a footgun at runtime.
4
+ * These are emitted as WARNINGS: they guide the author (very often an AI
5
+ * generating templates) toward the robust pattern without failing the build on
6
+ * a technically-legal construct.
7
+ *
8
+ * #1874 — time-relative rules via record-change date-EQUALITY. A start-node
9
+ * trigger condition like `end_date == daysFromNow(60)` on a `record-*` trigger
10
+ * only fires if the record happens to be written on that exact day; the robust
11
+ * shape is a daily SCHEDULE trigger + a range query. We flag the equality form
12
+ * specifically (range operators `>=`/`<=` are not flagged — they're the building
13
+ * block of the correct pattern), keeping false positives near zero.
14
+ */
15
+ export interface FlowLintFinding {
16
+ where: string;
17
+ message: string;
18
+ hint: string;
19
+ rule: string;
20
+ }
21
+ type AnyRec = Record<string, unknown>;
22
+ export declare const FLOW_TIME_RELATIVE_ANTIPATTERN = "flow-time-relative-antipattern";
23
+ export declare const FLOW_DOUBLE_BRACE_INTERP = "flow-double-brace-interpolation";
24
+ export declare const FLOW_BARE_DOLLAR_REF = "flow-bare-dollar-reference";
25
+ /**
26
+ * Lint every flow's start node for known authoring anti-patterns. Returns a
27
+ * (possibly empty) list of advisory findings — never throws, never fails a build.
28
+ */
29
+ export declare function lintFlowPatterns(stack: AnyRec): FlowLintFinding[];
30
+ export {};
31
+ //# sourceMappingURL=lint-flow-patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint-flow-patterns.d.ts","sourceRoot":"","sources":["../../src/utils/lint-flow-patterns.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAsBtC,eAAO,MAAM,8BAA8B,mCAAmC,CAAC;AAC/E,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAC1E,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AA0BjE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAsDjE"}
@@ -0,0 +1,109 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+ function asArray(v) {
3
+ if (Array.isArray(v))
4
+ return v;
5
+ if (v && typeof v === 'object')
6
+ return Object.entries(v).map(([name, def]) => ({ name, ...def }));
7
+ return [];
8
+ }
9
+ /** Extract the raw predicate source from a `condition` (string or Expression envelope). */
10
+ function conditionSource(raw) {
11
+ if (typeof raw === 'string')
12
+ return raw;
13
+ if (raw && typeof raw === 'object' && typeof raw.source === 'string')
14
+ return raw.source;
15
+ return '';
16
+ }
17
+ const TIME_FNS = 'daysFromNow|daysAgo|today|now|date|datetime';
18
+ // A time function adjacent to an equality operator, either side:
19
+ // `end_date == daysFromNow(60)` / `today() != record.start`
20
+ const DATE_EQ = new RegExp(`(?:(?:${TIME_FNS})\\s*\\([^)]*\\)\\s*(?:==|!=))|(?:(?:==|!=)\\s*(?:${TIME_FNS})\\s*\\()`);
21
+ export const FLOW_TIME_RELATIVE_ANTIPATTERN = 'flow-time-relative-antipattern';
22
+ export const FLOW_DOUBLE_BRACE_INTERP = 'flow-double-brace-interpolation';
23
+ export const FLOW_BARE_DOLLAR_REF = 'flow-bare-dollar-reference';
24
+ // Flow node VALUES interpolate with SINGLE braces (`{var}` / `{rec.field}` /
25
+ // `{$User.Id}`). Two wrong-syntax mistakes AI/human authors carry over from the
26
+ // *formula* template dialect (`{{ path }}`) or other platforms:
27
+ // - `{{ai_reply}}` — double-brace (verified: no flow node uses `{{ }}`).
28
+ // - `$source.id` — a `$`-prefixed reference written bare (resolves as a
29
+ // literal string), instead of `{source.id}`.
30
+ const DOUBLE_BRACE = /\{\{\s*[\w$][\w$.\s]*\}\}/;
31
+ // A `$Ident.field` not immediately inside a `{` (so `{$User.Id}` is NOT flagged).
32
+ // Require a letter/_ after `$` so currency like `$5.00` is never matched.
33
+ const BARE_DOLLAR_REF = /(?:^|[^{])\$[A-Za-z_]\w*\.[A-Za-z_]/;
34
+ /** Config keys whose string values are CEL predicates, not interpolated templates. */
35
+ const CEL_KEYS = new Set(['condition', 'expression', 'conditions']);
36
+ /** Collect every interpolated-template string value in a node config (skips CEL keys). */
37
+ function collectTemplateStrings(value, key, out) {
38
+ if (key && CEL_KEYS.has(key))
39
+ return;
40
+ if (typeof value === 'string') {
41
+ out.push(value);
42
+ return;
43
+ }
44
+ if (Array.isArray(value)) {
45
+ for (const v of value)
46
+ collectTemplateStrings(v, key, out);
47
+ return;
48
+ }
49
+ if (value && typeof value === 'object') {
50
+ for (const [k, v] of Object.entries(value))
51
+ collectTemplateStrings(v, k, out);
52
+ }
53
+ }
54
+ /**
55
+ * Lint every flow's start node for known authoring anti-patterns. Returns a
56
+ * (possibly empty) list of advisory findings — never throws, never fails a build.
57
+ */
58
+ export function lintFlowPatterns(stack) {
59
+ const findings = [];
60
+ for (const flow of asArray(stack.flows)) {
61
+ const flowName = typeof flow.name === 'string' ? flow.name : '(unnamed flow)';
62
+ const nodes = Array.isArray(flow.nodes) ? flow.nodes : [];
63
+ // (a) #1874 — date-equality time condition on a record-change start node.
64
+ const start = nodes.find((n) => n.type === 'start');
65
+ const startCfg = (start?.config ?? {});
66
+ const triggerType = typeof startCfg.triggerType === 'string' ? startCfg.triggerType : '';
67
+ if (triggerType.startsWith('record-')) {
68
+ const src = conditionSource(startCfg.condition).trim();
69
+ if (src && DATE_EQ.test(src)) {
70
+ findings.push({
71
+ where: `flow '${flowName}' · start condition`,
72
+ message: `record-change trigger uses a date-EQUALITY time condition (\`${src}\`) — it only fires if the ` +
73
+ `record happens to be written on that exact day, so unattended "N days before" rules never run.`,
74
+ hint: `Use a SCHEDULE trigger (daily cron) + a range query instead — e.g. a scheduled flow whose ` +
75
+ `get_record filters \`end_date\` BETWEEN {TODAY()} and {TODAY()+N}. (#1874)`,
76
+ rule: FLOW_TIME_RELATIVE_ANTIPATTERN,
77
+ });
78
+ }
79
+ }
80
+ // (b) #1315 — wrong interpolation syntax in any node's template values. Flow
81
+ // node values use SINGLE braces; double-brace `{{ }}` and bare `$ref.x`
82
+ // are carried over from the formula template dialect / other platforms.
83
+ for (const node of nodes) {
84
+ const strings = [];
85
+ collectTemplateStrings(node.config, undefined, strings);
86
+ const nodeWhere = `flow '${flowName}' · node '${node.id}' (${node.type})`;
87
+ for (const str of strings) {
88
+ if (DOUBLE_BRACE.test(str)) {
89
+ findings.push({
90
+ where: nodeWhere,
91
+ message: `double-brace interpolation \`${str.trim().slice(0, 80)}\` — flow node values use SINGLE braces.`,
92
+ hint: `Use \`{var}\` (e.g. \`{record.title}\`). Double-brace \`{{ }}\` is the formula/template-field dialect, not flow node values. (#1315)`,
93
+ rule: FLOW_DOUBLE_BRACE_INTERP,
94
+ });
95
+ }
96
+ if (BARE_DOLLAR_REF.test(str)) {
97
+ findings.push({
98
+ where: nodeWhere,
99
+ message: `\`${str.trim().slice(0, 80)}\` looks like a reference written as a literal — a bare \`$ref.field\` is NOT interpolated.`,
100
+ hint: `Wrap it and bind a variable: \`{source.id}\` (or \`{$User.Id}\` for the current user). (#1315)`,
101
+ rule: FLOW_BARE_DOLLAR_REF,
102
+ });
103
+ }
104
+ }
105
+ }
106
+ }
107
+ return findings;
108
+ }
109
+ //# sourceMappingURL=lint-flow-patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint-flow-patterns.js","sourceRoot":"","sources":["../../src/utils/lint-flow-patterns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=lint-flow-patterns.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint-flow-patterns.test.d.ts","sourceRoot":"","sources":["../../src/utils/lint-flow-patterns.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,80 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+ import { describe, it, expect } from 'vitest';
3
+ import { lintFlowPatterns, FLOW_TIME_RELATIVE_ANTIPATTERN, FLOW_DOUBLE_BRACE_INTERP, FLOW_BARE_DOLLAR_REF, } from './lint-flow-patterns.js';
4
+ const flow = (condition, triggerType = 'record-after-update') => ({
5
+ flows: [{
6
+ name: 'renewal_alert',
7
+ nodes: [{ id: 'start', type: 'start', config: { objectName: 'contract', triggerType, condition } }],
8
+ edges: [],
9
+ }],
10
+ });
11
+ describe('lintFlowPatterns — time-relative anti-pattern (#1874)', () => {
12
+ it('flags record-change date-EQUALITY against a time function', () => {
13
+ const fnds = lintFlowPatterns(flow('end_date == daysFromNow(60)'));
14
+ expect(fnds).toHaveLength(1);
15
+ expect(fnds[0].rule).toBe(FLOW_TIME_RELATIVE_ANTIPATTERN);
16
+ expect(fnds[0].where).toContain("renewal_alert");
17
+ expect(fnds[0].hint).toMatch(/schedule/i);
18
+ });
19
+ it('flags the function-on-the-left form too', () => {
20
+ expect(lintFlowPatterns(flow('today() != record.start_date'))).toHaveLength(1);
21
+ });
22
+ it('flags an Expression-envelope condition', () => {
23
+ expect(lintFlowPatterns(flow({ dialect: 'cel', source: 'record.due == daysFromNow(7)' }))).toHaveLength(1);
24
+ });
25
+ describe('does NOT flag (false-positive guards)', () => {
26
+ it('a RANGE comparison (the correct building block)', () => {
27
+ expect(lintFlowPatterns(flow('end_date <= daysFromNow(60)'))).toHaveLength(0);
28
+ expect(lintFlowPatterns(flow('end_date >= daysFromNow(7) && end_date <= daysFromNow(30)'))).toHaveLength(0);
29
+ });
30
+ it('equality on a non-time field', () => {
31
+ expect(lintFlowPatterns(flow('status == "expired"'))).toHaveLength(0);
32
+ });
33
+ it('a SCHEDULE trigger (only record-* triggers are linted)', () => {
34
+ expect(lintFlowPatterns(flow('end_date == daysFromNow(60)', 'schedule'))).toHaveLength(0);
35
+ });
36
+ it('no condition', () => {
37
+ expect(lintFlowPatterns(flow(undefined))).toHaveLength(0);
38
+ });
39
+ });
40
+ });
41
+ /** A flow with a create_record node carrying `config`. */
42
+ const nodeFlow = (config) => ({
43
+ flows: [{
44
+ name: 'mk',
45
+ nodes: [
46
+ { id: 'start', type: 'start', config: {} },
47
+ { id: 'create', type: 'create_record', config },
48
+ ],
49
+ edges: [],
50
+ }],
51
+ });
52
+ const rules = (s) => lintFlowPatterns(s).map((f) => f.rule);
53
+ describe('lintFlowPatterns — wrong interpolation syntax (#1315)', () => {
54
+ it('flags double-brace interpolation in a node value', () => {
55
+ expect(rules(nodeFlow({ objectName: 'm', fields: { body: '{{ai_reply}}' } })))
56
+ .toContain(FLOW_DOUBLE_BRACE_INTERP);
57
+ });
58
+ it('flags a bare $ref.field written as a literal', () => {
59
+ expect(rules(nodeFlow({ objectName: 'm', fields: { ticket: '$source.id' } })))
60
+ .toContain(FLOW_BARE_DOLLAR_REF);
61
+ });
62
+ describe('does NOT flag (false-positive guards)', () => {
63
+ it('correct single-brace interpolation', () => {
64
+ expect(rules(nodeFlow({ objectName: 'm', fields: { body: '{ai_reply}', t: 'Hi {record.name}' } }))).toEqual([]);
65
+ });
66
+ it('a braced $User reference', () => {
67
+ expect(rules(nodeFlow({ objectName: 'm', fields: { owner: '{$User.Id}' } }))).toEqual([]);
68
+ });
69
+ it('a currency literal', () => {
70
+ expect(rules(nodeFlow({ objectName: 'm', fields: { price: '$5.00', label: 'Total $5' } }))).toEqual([]);
71
+ });
72
+ it('a CEL condition (skipped — not a template value)', () => {
73
+ expect(rules({ flows: [{ name: 'd', nodes: [
74
+ { id: 'start', type: 'start', config: {} },
75
+ { id: 'dec', type: 'decision', config: { condition: 'record.amount > 100' } },
76
+ ], edges: [] }] })).toEqual([]);
77
+ });
78
+ });
79
+ });
80
+ //# sourceMappingURL=lint-flow-patterns.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint-flow-patterns.test.js","sourceRoot":"","sources":["../../src/utils/lint-flow-patterns.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"validate-expressions.d.ts","sourceRoot":"","sources":["../../src/utils/validate-expressions.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA0BtC;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CA2DnE"}
1
+ {"version":3,"file":"validate-expressions.d.ts","sourceRoot":"","sources":["../../src/utils/validate-expressions.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA0BtC;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CA+FnE"}
@@ -67,6 +67,40 @@ export function validateStackExpressions(stack) {
67
67
  for (const node of nodes) {
68
68
  const cfg = (node.config ?? {});
69
69
  check(`flow '${flowName}' · node '${node.id}' (${node.type}) condition`, cfg.condition, objectName);
70
+ // #1870 — a `script` node must declare a callable target (`actionType` or
71
+ // `function`). A node with neither is a silent no-op that otherwise passes
72
+ // build. (Function *existence* isn't checkable here — functions are code,
73
+ // not serialized into the artifact — so this is a structural check; the
74
+ // runtime verifies the named function is actually registered.)
75
+ if (node.type === 'script') {
76
+ // `function` is canonical; `functionName` is an accepted alias.
77
+ const fn = (typeof cfg.function === 'string' ? cfg.function.trim() : '') ||
78
+ (typeof cfg.functionName === 'string' ? cfg.functionName.trim() : '');
79
+ const action = typeof cfg.actionType === 'string' ? cfg.actionType.trim() : '';
80
+ // Inline `config.script` (a JS body) is also a declared form — the
81
+ // built-in runtime doesn't execute it (warned at run time), but the node
82
+ // is not the empty no-op this check targets, so don't flag it.
83
+ const inline = typeof cfg.script === 'string' ? cfg.script.trim() : '';
84
+ if (!fn && !action && !inline) {
85
+ issues.push({
86
+ where: `flow '${flowName}' · node '${node.id}' (script) callable`,
87
+ message: `script node declares neither \`actionType\` nor \`function\` — it would do nothing at runtime. ` +
88
+ `Name a built-in action (e.g. \`actionType: 'email'\`) or a registered function ` +
89
+ `(\`function: 'my_fn'\`, registered via \`defineStack({ functions })\`).`,
90
+ source: JSON.stringify({ id: node.id, type: node.type, config: cfg }),
91
+ });
92
+ }
93
+ else if (action === 'invoke_function' && !fn) {
94
+ // `actionType: 'invoke_function'` is a marker that names no callable on
95
+ // its own — the function name must be in `function`/`functionName`.
96
+ issues.push({
97
+ where: `flow '${flowName}' · node '${node.id}' (script) callable`,
98
+ message: `script node uses \`actionType: 'invoke_function'\` but no \`function\` (or \`functionName\`) — ` +
99
+ `it names no callable. Set \`function: 'my_fn'\` and register it via \`defineStack({ functions })\`.`,
100
+ source: JSON.stringify({ id: node.id, type: node.type, config: cfg }),
101
+ });
102
+ }
103
+ }
70
104
  }
71
105
  for (const edge of edges) {
72
106
  check(`flow '${flowName}' · edge '${edge.id}' (${edge.source}→${edge.target}) condition`, edge.condition, objectName);
@@ -1 +1 @@
1
- {"version":3,"file":"validate-expressions.js","sourceRoot":"","sources":["../../src/utils/validate-expressions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate-expressions.js","sourceRoot":"","sources":["../../src/utils/validate-expressions.ts"],"names":[],"mappings":"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"}
@@ -61,5 +61,64 @@ describe('validateStackExpressions (ADR-0032 build-time)', () => {
61
61
  expect(issues).toHaveLength(1);
62
62
  expect(issues[0].where).toContain("validation 'r1'");
63
63
  });
64
+ // #1870 — a `script` node that names no callable is a silent no-op.
65
+ it('flags a script node that declares neither actionType nor function (#1870)', () => {
66
+ const issues = validateStackExpressions({
67
+ flows: [{
68
+ name: 'helpdesk_flow',
69
+ nodes: [
70
+ { id: 'start', type: 'start', config: {} },
71
+ { id: 'triage', type: 'script', config: { actionType: undefined } },
72
+ ],
73
+ edges: [],
74
+ }],
75
+ });
76
+ expect(issues).toHaveLength(1);
77
+ expect(issues[0].where).toContain("node 'triage' (script) callable");
78
+ expect(issues[0].message).toMatch(/neither .*actionType.* nor .*function/);
79
+ });
80
+ it('accepts a script node that names a built-in action or a function (#1870)', () => {
81
+ const issues = validateStackExpressions({
82
+ flows: [{
83
+ name: 'helpdesk_flow',
84
+ nodes: [
85
+ { id: 'start', type: 'start', config: {} },
86
+ { id: 'mail', type: 'script', config: { actionType: 'email' } },
87
+ { id: 'triage', type: 'script', config: { function: 'helpdesk.aiTriageStub' } },
88
+ { id: 'inline', type: 'script', config: { script: 'variables.x = 1;' } },
89
+ ],
90
+ edges: [],
91
+ }],
92
+ });
93
+ expect(issues).toHaveLength(0);
94
+ });
95
+ // #1870 DX — `functionName` is an accepted alias for `function`.
96
+ it('accepts a script node that names a callable via the functionName alias', () => {
97
+ const issues = validateStackExpressions({
98
+ flows: [{
99
+ name: 'helpdesk_flow',
100
+ nodes: [
101
+ { id: 'start', type: 'start', config: {} },
102
+ { id: 'triage', type: 'script', config: { actionType: 'invoke_function', functionName: 'helpdesk.aiTriageStub' } },
103
+ ],
104
+ edges: [],
105
+ }],
106
+ });
107
+ expect(issues).toHaveLength(0);
108
+ });
109
+ it('flags actionType invoke_function with no function/functionName', () => {
110
+ const issues = validateStackExpressions({
111
+ flows: [{
112
+ name: 'helpdesk_flow',
113
+ nodes: [
114
+ { id: 'start', type: 'start', config: {} },
115
+ { id: 'triage', type: 'script', config: { actionType: 'invoke_function', inputs: { x: 1 } } },
116
+ ],
117
+ edges: [],
118
+ }],
119
+ });
120
+ expect(issues).toHaveLength(1);
121
+ expect(issues[0].message).toMatch(/invoke_function.*no .*function/i);
122
+ });
64
123
  });
65
124
  //# sourceMappingURL=validate-expressions.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-expressions.test.js","sourceRoot":"","sources":["../../src/utils/validate-expressions.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate-expressions.test.js","sourceRoot":"","sources":["../../src/utils/validate-expressions.test.ts"],"names":[],"mappings":"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"}