@uipath/maestro-builder-sdk 6.17.6 → 6.18.1

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 (67) hide show
  1. package/dist/bindings-v2.js +15 -17
  2. package/dist/bpmn/bpmn-sdk.js +3 -2
  3. package/dist/bpmn/check.js +3 -2
  4. package/dist/bpmn/decompile.js +123 -137
  5. package/dist/bpmn/intsvc.js +7 -2
  6. package/dist/bpmn/serialize.js +1 -1
  7. package/dist/bpmn/typed-node.js +5 -4
  8. package/dist/case/check-cli.js +18 -4
  9. package/dist/case/check.d.ts +26 -1
  10. package/dist/case/check.js +80 -2
  11. package/dist/case/cli-library.d.ts +14 -0
  12. package/dist/case/cli-library.js +31 -0
  13. package/dist/case/compile-cli.js +12 -12
  14. package/dist/case/decompile.js +103 -109
  15. package/dist/case/entry-points.js +2 -1
  16. package/dist/case/merge.js +3 -5
  17. package/dist/case/serialize.js +1 -1
  18. package/dist/check.js +26 -10
  19. package/dist/cli/commands/case.js +10 -1
  20. package/dist/core/ceql.js +5 -2
  21. package/dist/core/connector-inputs.js +4 -4
  22. package/dist/core/event-checks.js +17 -2
  23. package/dist/core/event-filters.d.ts +38 -4
  24. package/dist/core/event-filters.js +63 -7
  25. package/dist/core/expr.d.ts +1 -1
  26. package/dist/core/expr.js +3 -3
  27. package/dist/core/json-file.d.ts +2 -0
  28. package/dist/core/json-file.js +4 -0
  29. package/dist/core/library.js +2 -1
  30. package/dist/core/node-envelope.d.ts +1 -0
  31. package/dist/core/node-envelope.js +1 -1
  32. package/dist/core/own-entry.d.ts +9 -0
  33. package/dist/core/own-entry.js +11 -0
  34. package/dist/core/ts-literal.d.ts +22 -0
  35. package/dist/core/ts-literal.js +37 -0
  36. package/dist/core/typed-raw-node.js +4 -3
  37. package/dist/decompile.js +238 -255
  38. package/dist/merge.js +10 -12
  39. package/dist/registry/compat/pystr.d.ts +2 -0
  40. package/dist/registry/compat/pystr.js +35 -0
  41. package/dist/registry/compat/pyvalue.d.ts +16 -0
  42. package/dist/registry/compat/pyvalue.js +35 -0
  43. package/dist/registry/generators/markdown/pyvalue.d.ts +1 -4
  44. package/dist/registry/generators/markdown/pyvalue.js +2 -30
  45. package/dist/registry/generators/markdown/sections/inputs.js +3 -2
  46. package/dist/registry/generators/type-tree.d.ts +1 -7
  47. package/dist/registry/generators/type-tree.js +5 -22
  48. package/dist/registry/library-build/build.js +6 -6
  49. package/dist/registry/library-build/canonical.js +25 -25
  50. package/dist/registry/library-build/context.js +5 -5
  51. package/dist/registry/library-build/enrich.js +18 -18
  52. package/dist/registry/library-build/events.js +25 -24
  53. package/dist/registry/library-build/generic-expand.js +21 -20
  54. package/dist/registry/library-build/plan.js +8 -8
  55. package/dist/registry/library-build/py.d.ts +3 -10
  56. package/dist/registry/library-build/py.js +9 -52
  57. package/dist/registry/library-build/schema-action.js +6 -6
  58. package/dist/registry/prepare/event.js +2 -2
  59. package/dist/registry/prepare/source-import.js +2 -9
  60. package/dist/registry/prepare/source-labels.js +2 -9
  61. package/dist/registry/prepare/support.d.ts +2 -6
  62. package/dist/registry/prepare/support.js +3 -58
  63. package/dist/registry/pull-cli.js +6 -14
  64. package/dist/registry/search-cli.js +2 -2
  65. package/dist/schema-version.js +0 -1
  66. package/dist/serialize.js +86 -76
  67. package/package.json +2 -2
package/dist/decompile.js CHANGED
@@ -36,6 +36,7 @@ import { fileURLToPath } from 'node:url';
36
36
  import { inferScriptShape } from './core/script-return.js';
37
37
  import { DEFAULT_TRIGGER_ID } from './core/expr.js';
38
38
  import { commentText } from './core/comment-text.js';
39
+ import { tsLiteral, tsNumber, tsText } from './core/ts-literal.js';
39
40
  import { parseIxpProjectType } from './core/actions.js';
40
41
  import { readFlowGlobals } from './flow-variables.js';
41
42
  import { missingBindingLabels } from './core/binding-messages.js';
@@ -127,11 +128,11 @@ function verClause(node) {
127
128
  const shipped = [...CORE_DEFINITION_KEYS]
128
129
  .filter((k) => k.startsWith(`${node.type}@`))
129
130
  .map((k) => k.slice(node.type.length + 1));
130
- warnOnce(`node ${str(node.id)} pins ${node.type}@${tv}, but this SDK bundles ${node.type}@${shipped.join(', ') || '<none>'} only; `
131
- + `\`compile\` will refuse the emitted { version: ${str(tv)} } (versions resolve exactly, flow-builder-sdk#71). `
131
+ warnOnce(`node ${tsLiteral(node.id)} pins ${node.type}@${tv}, but this SDK bundles ${node.type}@${shipped.join(', ') || '<none>'} only; `
132
+ + `\`compile\` will refuse the emitted { version: ${tsLiteral(tv)} } (versions resolve exactly, flow-builder-sdk#71). `
132
133
  + `The source keeps the file's pair; to recompile, drop that { version } and the node takes the SDK's pinned version.`);
133
134
  }
134
- return `version: ${str(tv)}`;
135
+ return `version: ${tsLiteral(tv)}`;
135
136
  }
136
137
  function verArg(node) {
137
138
  const clause = verClause(node);
@@ -148,7 +149,7 @@ function triggerOptParts(node) {
148
149
  parts.push(ver);
149
150
  const label = node.display?.label;
150
151
  if (typeof label === 'string' && label !== definitionLabel(node))
151
- parts.push(`label: ${str(label)}`);
152
+ parts.push(`label: ${tsLiteral(label)}`);
152
153
  return parts;
153
154
  }
154
155
  /** `triggerOptParts` as the `.trigger(spec, { ... })` options argument. */
@@ -176,7 +177,7 @@ function optsArgWith(node, ctx, extra) {
176
177
  // compile. Equal-to-default is omitted so ordinary flows stay quiet.
177
178
  const label = node.display?.label;
178
179
  if (typeof label === 'string' && label !== node.id)
179
- parts.push(`label: ${str(label)}`);
180
+ parts.push(`label: ${tsLiteral(label)}`);
180
181
  const updates = ctx.flow.variables?.variableUpdates?.[node.id] ?? [];
181
182
  if (updates.length) {
182
183
  const entries = updates.map((u) => `${key(u.variableId)}: ${emitVal(u.expression, ctx.imp, ctx.inputNames)}`);
@@ -233,27 +234,13 @@ function isTrigger(type) {
233
234
  return type.startsWith('core.trigger.') || type.startsWith('uipath.connector.event.') || type.startsWith('uipath.connector.trigger.');
234
235
  }
235
236
  // ─── Injection-safe source emitters (ported verbatim from case/decompile.ts) ──
236
- /**
237
- * A JS/TS string literal (double-quoted, correctly escaped). `JSON.stringify`
238
- * escapes quotes, backslashes and control chars — so untrusted `.flow` values
239
- * (labels, expressions, code) can never break out of the literal. We also escape
240
- * U+2028/U+2029 (legal in JSON, historically illegal in JS string literals).
241
- * Non-string input still serializes to inert JSON (never executable).
242
- */
243
- function str(s) {
244
- return JSON.stringify(s).replace(/[\u2028\u2029]/g, (c) => '\\u' + c.charCodeAt(0).toString(16));
245
- }
246
237
  /** A boolean literal, coerced so an untrusted non-boolean can never reach source verbatim. */
247
238
  function boolLit(v) {
248
239
  return v ? 'true' : 'false';
249
240
  }
250
241
  /** An object key: a bare identifier when legal, otherwise a quoted string. */
251
242
  function key(k) {
252
- return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k) ? k : str(k);
253
- }
254
- /** An arbitrary JSON-serializable value as a TS literal (used for var defaults). */
255
- function literal(v) {
256
- return JSON.stringify(v);
243
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k) ? k : tsLiteral(k);
257
244
  }
258
245
  /** Render an options object from present entries; '' when nothing is present. */
259
246
  function opts(entries) {
@@ -283,7 +270,7 @@ class Imports {
283
270
  }
284
271
  line(specifier) {
285
272
  const names = [...this.used].sort();
286
- return `import { ${names.join(', ')} } from ${str(specifier)};`;
273
+ return `import { ${names.join(', ')} } from ${tsLiteral(specifier)};`;
287
274
  }
288
275
  }
289
276
  // ─── Expression inversion ─────────────────────────────────────────────────────
@@ -344,7 +331,7 @@ function emitRef(jsBody, imp, inputNames) {
344
331
  const PATH = '[A-Za-z_]\\w*(?:\\.[A-Za-z_]\\w*|\\[\\d+\\])*';
345
332
  // !!$vars.<step>.output → ran('step')
346
333
  if ((m = body.match(/^!!\$vars\.([A-Za-z_]\w*)\.output$/)))
347
- return `${imp.use('ran')}(${str(m[1])})`;
334
+ return `${imp.use('ran')}(${tsLiteral(m[1])})`;
348
335
  // $vars.<step>.output[.<path>] → out('step'[, 'path'])
349
336
  if ((m = body.match(new RegExp(`^\\$vars\\.([A-Za-z_]\\w*)\\.output(?:\\.(${PATH}))?$`)))) {
350
337
  // A flow INPUT is published as the trigger's output, so
@@ -352,20 +339,20 @@ function emitRef(jsBody, imp, inputNames) {
352
339
  // Recover the author's spelling rather than the mechanical `out('start', …)`;
353
340
  // both re-serialize identically, but only one reads like the source.
354
341
  if (m[1] === currentTriggerId && m[2] !== undefined && inputNames.has(m[2])) {
355
- return `${imp.use('input')}(${str(m[2])})`;
342
+ return `${imp.use('input')}(${tsLiteral(m[2])})`;
356
343
  }
357
- return m[2] === undefined ? `${imp.use('out')}(${str(m[1])})` : `${imp.use('out')}(${str(m[1])}, ${str(m[2])})`;
344
+ return m[2] === undefined ? `${imp.use('out')}(${tsLiteral(m[1])})` : `${imp.use('out')}(${tsLiteral(m[1])}, ${tsLiteral(m[2])})`;
358
345
  }
359
346
  // $vars.<step>.error[.<field>] → err('step'[, 'field'])
360
347
  if ((m = body.match(new RegExp(`^\\$vars\\.([A-Za-z_]\\w*)\\.error(?:\\.(${PATH}))?$`)))) {
361
- return m[2] === undefined ? `${imp.use('err')}(${str(m[1])})` : `${imp.use('err')}(${str(m[1])}, ${str(m[2])})`;
348
+ return m[2] === undefined ? `${imp.use('err')}(${tsLiteral(m[1])})` : `${imp.use('err')}(${tsLiteral(m[1])}, ${tsLiteral(m[2])})`;
362
349
  }
363
350
  // $vars.<name> → input('name') for a declared input, else v('name').
364
351
  // The bare spelling predates flow inputs moving to `<trigger>.output.<name>`;
365
352
  // still read it so an older artifact decompiles.
366
353
  if ((m = body.match(/^\$vars\.([A-Za-z_]\w*)$/))) {
367
354
  const name = m[1];
368
- return inputNames.has(name) ? `${imp.use('input')}(${str(name)})` : `${imp.use('v')}(${str(name)})`;
355
+ return inputNames.has(name) ? `${imp.use('input')}(${tsLiteral(name)})` : `${imp.use('v')}(${tsLiteral(name)})`;
369
356
  }
370
357
  // Composite / unrecognized: a raw JS expression template.
371
358
  return `${imp.use('js')}${jsTemplate(body)}`;
@@ -380,14 +367,17 @@ function emitRef(jsBody, imp, inputNames) {
380
367
  * builder takes `Expr | string | unknown` (bare values pass through `toExpr`).
381
368
  */
382
369
  function emitVal(v, imp, inputNames) {
370
+ // a present-but-undefined value stays the TS expression `undefined` (the behaviour this branch shipped; opts() drops an entry whose value is undefined before it reaches here)
371
+ if (v === undefined)
372
+ return 'undefined';
383
373
  const raw = unwrapSource(v);
384
374
  if (raw !== undefined && !Array.isArray(v)) {
385
375
  if (raw.startsWith('=js:'))
386
376
  return emitRef(raw.slice(4), imp, inputNames);
387
- return str(raw); // a literal string (valid as Expr via toExpr, or as a raw string input)
377
+ return tsLiteral(raw); // a literal string (valid as Expr via toExpr, or as a raw string input)
388
378
  }
389
379
  // A raw JSON literal (number / boolean / null / array / object).
390
- return literal(v);
380
+ return tsLiteral(v);
391
381
  }
392
382
  /** A bare (unprefixed) JS expression, as decision/switch nodes store it. */
393
383
  function emitBareExpr(raw, imp, inputNames) {
@@ -530,7 +520,7 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
530
520
  // infers from the body first (see `scriptOutputs`), so a script whose type
531
521
  // is already plain in its code round-trips without it — emitting one there
532
522
  // would add an argument the author never wrote to every such step.
533
- return `${imp.use('script')}(${opts([['code', str(code)], ...scriptReturnsOpt(node, code)])})`;
523
+ return `${imp.use('script')}(${opts([['code', tsLiteral(code)], ...scriptReturnsOpt(node, code)])})`;
534
524
  }
535
525
  case T.delay: {
536
526
  // serialize's `delayInputs` writes `{timerType, timerPreset, timerValue?}`
@@ -538,18 +528,18 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
538
528
  // wait. Invert all three spellings.
539
529
  if (inputs.timerType === 'timeDate') {
540
530
  const until = typeof inputs.timerDate === 'string' ? inputs.timerDate : '';
541
- return `${imp.use('delay')}(${opts([['until', str(until)]])})`;
531
+ return `${imp.use('delay')}(${opts([['until', tsLiteral(until)]])})`;
542
532
  }
543
533
  const duration = inputs.timerPreset === 'custom'
544
534
  ? (typeof inputs.timerValue === 'string' ? inputs.timerValue : '')
545
535
  : (typeof inputs.timerPreset === 'string' ? inputs.timerPreset : '');
546
- return `${imp.use('delay')}(${opts([['duration', str(duration)]])})`;
536
+ return `${imp.use('delay')}(${opts([['duration', tsLiteral(duration)]])})`;
547
537
  }
548
538
  case T.documentClassify:
549
539
  case T.dynamicExtract: {
550
540
  const fr = unwrapSource(inputs.fileRef) ?? '';
551
- const fileSrc = fr.startsWith('=js:') ? emitBareExpr(fr.slice(4), imp, inputNames) : str(fr);
552
- const pageRange = typeof inputs.pageRange === 'string' && inputs.pageRange !== '' ? str(inputs.pageRange) : undefined;
541
+ const fileSrc = fr.startsWith('=js:') ? emitBareExpr(fr.slice(4), imp, inputNames) : tsLiteral(fr);
542
+ const pageRange = typeof inputs.pageRange === 'string' && inputs.pageRange !== '' ? tsLiteral(inputs.pageRange) : undefined;
553
543
  if (node.type === T.documentClassify) {
554
544
  const split = unwrapPlain(inputs.splitPages);
555
545
  return `${imp.use('documentClassify')}(${opts([
@@ -558,7 +548,7 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
558
548
  ['splitPages', split === true ? 'true' : undefined],
559
549
  ])})`;
560
550
  }
561
- const schema = inputs.schemaDocument !== undefined ? literal(inputs.schemaDocument) : undefined;
551
+ const schema = inputs.schemaDocument !== undefined ? tsLiteral(inputs.schemaDocument) : undefined;
562
552
  return `${imp.use('dynamicExtract')}(${opts([
563
553
  ['fileRef', fileSrc],
564
554
  ['schema', schema],
@@ -570,9 +560,9 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
570
560
  case T.dataFabricUpdate:
571
561
  case T.dataFabricDelete: {
572
562
  const cfg = (inputs.entityConfig ?? {});
573
- const entity = str(String(cfg.entityName ?? ''));
574
- const folder = typeof cfg._folderKey === 'string' ? str(cfg._folderKey) : undefined;
575
- const resourceKey = typeof cfg._resourceKey === 'string' ? str(cfg._resourceKey) : undefined;
563
+ const entity = tsText(cfg.entityName);
564
+ const folder = typeof cfg._folderKey === 'string' ? tsLiteral(cfg._folderKey) : undefined;
565
+ const resourceKey = typeof cfg._resourceKey === 'string' ? tsLiteral(cfg._resourceKey) : undefined;
576
566
  const val = (v) => {
577
567
  // 1.3 let `recordId` and a create's field value be a `{ type:
578
568
  // 'jsExpression', expression }` object as well as a `=js:` string. A
@@ -582,14 +572,14 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
582
572
  return emitBareExpr(String(obj.expression ?? ''), imp, inputNames);
583
573
  }
584
574
  const raw = typeof v === 'string' ? v : String(v ?? '');
585
- return raw.startsWith('=js:') ? emitBareExpr(raw.slice(4), imp, inputNames) : str(raw);
575
+ return raw.startsWith('=js:') ? emitBareExpr(raw.slice(4), imp, inputNames) : tsLiteral(raw);
586
576
  };
587
577
  const fieldObject = (rows) => {
588
578
  const list = Array.isArray(rows) ? rows : [];
589
579
  return `{ ${list.map((r) => `${key(String(r.field ?? ''))}: ${val(r.value)}`).join(', ')} }`;
590
580
  };
591
581
  const record = cfg.recordSource === 'fromRead'
592
- ? `{ fromRead: ${str(String(cfg.readEntityNodeId ?? ''))} }`
582
+ ? `{ fromRead: ${tsText(cfg.readEntityNodeId)} }`
593
583
  : `{ byId: ${val(cfg.recordId)} }`;
594
584
  if (node.type === T.dataFabricRead) {
595
585
  // The array is what the platform serializer reads and what compile
@@ -597,8 +587,8 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
597
587
  // here so a canvas-authored file still decompiles. Both flatten to the
598
588
  // builder's row list, where `or: true` marks each new OR alternative.
599
589
  const filterRow = (r, or = false) => opts([
600
- ['field', str(String(r.field ?? ''))],
601
- ['operator', r.operator !== undefined && r.operator !== '=' ? str(String(r.operator)) : undefined],
590
+ ['field', tsText(r.field)],
591
+ ['operator', r.operator !== undefined && r.operator !== '=' ? tsLiteral(String(r.operator)) : undefined],
602
592
  ['value', val(r.value)],
603
593
  ['or', or ? 'true' : undefined],
604
594
  ]);
@@ -621,15 +611,15 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
621
611
  // `resultMode` only exists from 1.4, and recovering it re-selects 1.4 on
622
612
  // the way back out — so a 1.0 node stays a 1.0 node through the round trip.
623
613
  const mode = cfg.resultMode === 'single' || cfg.resultMode === 'multiple'
624
- ? str(cfg.resultMode) : undefined;
614
+ ? tsLiteral(cfg.resultMode) : undefined;
625
615
  const sort = cfg._sort && typeof cfg._sort === 'object'
626
616
  ? opts([
627
- ['field', str(String(cfg._sort.field ?? ''))],
628
- ['direction', cfg._sort.direction === 'desc' ? str('desc') : undefined],
617
+ ['field', tsText(cfg._sort.field)],
618
+ ['direction', cfg._sort.direction === 'desc' ? tsLiteral('desc') : undefined],
629
619
  ])
630
620
  : undefined;
631
621
  const fields = Array.isArray(cfg._selectedFieldNames) && cfg._selectedFieldNames.length
632
- ? `[${cfg._selectedFieldNames.map((f) => str(String(f))).join(', ')}]`
622
+ ? `[${cfg._selectedFieldNames.map((f) => tsLiteral(String(f))).join(', ')}]`
633
623
  : undefined;
634
624
  return `${imp.use('dataFabricRead')}(${opts([
635
625
  ['entity', entity], ['filters', filters], ['resultMode', mode],
@@ -661,7 +651,7 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
661
651
  const raw = typeof x === 'string' ? x : String(x?.expression ?? x ?? '');
662
652
  return raw.startsWith('=js:') ? emitBareExpr(raw.slice(4), imp, inputNames)
663
653
  : raw.startsWith('$vars.') ? emitBareExpr(raw, imp, inputNames)
664
- : str(raw);
654
+ : tsLiteral(raw);
665
655
  };
666
656
  if (node.type === T.createOutgoingCall) {
667
657
  return `${imp.use('createOutgoingCall')}(${opts([['from', v(i.from)], ['to', v(i.to)]])})`;
@@ -690,16 +680,16 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
690
680
  // value, so it must not come back as one.
691
681
  if (raw === undefined || raw === '')
692
682
  return undefined;
693
- return raw.startsWith('=js:') ? emitBareExpr(raw.slice(4), imp, inputNames) : str(raw);
683
+ return raw.startsWith('=js:') ? emitBareExpr(raw.slice(4), imp, inputNames) : tsLiteral(raw);
694
684
  };
695
685
  const priority = unwrapPlain(inputs.priority);
696
686
  return `${imp.use('queueItem')}(${opts([
697
- ['queue', str(String(q.name ?? ''))],
698
- ['folderPath', str(String(q.folderPath ?? ''))],
699
- ['key', str(String(q.key ?? ''))],
687
+ ['queue', tsText(q.name)],
688
+ ['folderPath', tsText(q.folderPath)],
689
+ ['key', tsText(q.key)],
700
690
  ['item', rows.length ? objLit(rows) : '{}'],
701
691
  // `Normal` is the definition's default — only a departure was authored.
702
- ['priority', typeof priority === 'string' && priority !== 'Normal' ? str(priority) : undefined],
692
+ ['priority', typeof priority === 'string' && priority !== 'Normal' ? tsLiteral(priority) : undefined],
703
693
  ['reference', optStr('reference')],
704
694
  ['deferDate', optStr('deferDate')],
705
695
  ['dueDate', optStr('dueDate')],
@@ -741,7 +731,7 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
741
731
  const raw = typeof x === 'string' ? x : String(x?.expression ?? x ?? '');
742
732
  return raw.startsWith('=js:') ? emitBareExpr(raw.slice(4), imp, inputNames)
743
733
  : raw.startsWith('$vars.') ? emitBareExpr(raw, imp, inputNames)
744
- : str(raw);
734
+ : tsLiteral(raw);
745
735
  };
746
736
  const num = (x) => {
747
737
  if (x === undefined || x === null)
@@ -813,16 +803,16 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
813
803
  const model = (i.model ?? {});
814
804
  const nested = (k) => typeof model[k] === 'string' && model[k] !== '' ? model[k] : undefined;
815
805
  return `${imp.use('ixpExtract')}(${opts([
816
- ['project', str(node.type)],
806
+ ['project', tsLiteral(node.type)],
817
807
  // `modelName`/`projectName` are one value in two fields; either recovers it.
818
- ['modelName', str(plain('modelName') ?? plain('projectName') ?? nested('modelName') ?? '')],
819
- ['name', str(nested('modelDisplayName') ?? plain('modelName') ?? '')],
820
- ['folderName', str(plain('folderName') ?? nested('folderName') ?? '')],
821
- ['fileRef', fr.startsWith('=js:') ? emitBareExpr(fr.slice(4), imp, inputNames) : str(fr)],
822
- ['pageRange', plain('pageRange') !== undefined ? str(plain('pageRange')) : undefined],
823
- ['versionTag', plain('versionTag') !== undefined ? str(plain('versionTag')) : undefined],
808
+ ['modelName', tsLiteral(plain('modelName') ?? plain('projectName') ?? nested('modelName') ?? '')],
809
+ ['name', tsLiteral(nested('modelDisplayName') ?? plain('modelName') ?? '')],
810
+ ['folderName', tsLiteral(plain('folderName') ?? nested('folderName') ?? '')],
811
+ ['fileRef', fr.startsWith('=js:') ? emitBareExpr(fr.slice(4), imp, inputNames) : tsLiteral(fr)],
812
+ ['pageRange', plain('pageRange') !== undefined ? tsLiteral(plain('pageRange')) : undefined],
813
+ ['versionTag', plain('versionTag') !== undefined ? tsLiteral(plain('versionTag')) : undefined],
824
814
  ['description', plain('description') ?? nested('description') !== undefined
825
- ? str((plain('description') ?? nested('description'))) : undefined],
815
+ ? tsLiteral((plain('description') ?? nested('description'))) : undefined],
826
816
  ])})`;
827
817
  }
828
818
  // Every family this SDK AUTHORS is recovered above — a guard test asserts
@@ -866,23 +856,16 @@ function scriptReturnsOpt(node, code) {
866
856
  // A field map is the richer statement, so it wins when there is one.
867
857
  const entries = Object.entries(props)
868
858
  .filter(([, v]) => typeof v?.type === 'string')
869
- .map(([k, v]) => [k, str(v.type)]);
859
+ .map(([k, v]) => [k, tsLiteral(v.type)]);
870
860
  if (entries.length > 0)
871
861
  return [['returns', opts(entries)]];
872
862
  }
873
- return [['returns', str(declared)]];
863
+ return [['returns', tsLiteral(declared)]];
874
864
  }
875
865
  /** Key-sorted copy, so a property map compares by content and not by order. */
876
866
  function sortKeys(o) {
877
867
  return Object.fromEntries(Object.entries(o).sort(([a], [b]) => a.localeCompare(b)));
878
868
  }
879
- /** A validated numeric literal — never emit a raw JSON value that could carry code. */
880
- function numLit(v) {
881
- const n = Number(v);
882
- if (!Number.isFinite(n))
883
- throw new Error(`decompile: expected a number, got ${JSON.stringify(v)}.`);
884
- return String(n);
885
- }
886
869
  /**
887
870
  * Invert an HTTP node. The plain node (`core.action.http`) keeps its request at
888
871
  * the top level (`url` / `method` / `headers` / `queryParams` / `body` /
@@ -907,28 +890,28 @@ function emitHttp(node, managed, imp, inputNames) {
907
890
  const headersSrc = emitValMap(headers, imp, inputNames) || undefined;
908
891
  const querySrc = emitValMap(query, imp, inputNames) || undefined;
909
892
  const contentType = !managed && typeof inputs.contentType === 'string' && inputs.contentType !== 'application/json'
910
- ? str(inputs.contentType) : undefined;
911
- const timeout = typeof inputs.timeout === 'string' && inputs.timeout !== 'PT15M' ? str(inputs.timeout) : undefined;
912
- const retry = inputs.retryCount !== undefined && Number(inputs.retryCount) !== 0 ? numLit(inputs.retryCount) : undefined;
893
+ ? tsLiteral(inputs.contentType) : undefined;
894
+ const timeout = typeof inputs.timeout === 'string' && inputs.timeout !== 'PT15M' ? tsLiteral(inputs.timeout) : undefined;
895
+ const retry = inputs.retryCount !== undefined && Number(inputs.retryCount) !== 0 ? tsNumber(inputs.retryCount) : undefined;
913
896
  const branchRows = Array.isArray(inputs.branches) ? inputs.branches : [];
914
897
  const branchesSrc = branchRows.length
915
898
  ? `[${branchRows.map((b) => {
916
899
  const cond = unwrapSource(b.conditionExpression) ?? '';
917
900
  const expr = emitBareExpr(cond.startsWith('=js:') ? cond.slice(4) : cond, imp, inputNames);
918
- return `{ name: ${str(String(b.name ?? b.id ?? ''))}, condition: ${expr} }`;
901
+ return `{ name: ${tsText(b.name ?? b.id)}, condition: ${expr} }`;
919
902
  }).join(', ')}]`
920
903
  : undefined;
921
904
  const connectionValue = detail.connectionId ?? detail.connectionResourceId ?? req.connection;
922
905
  const connection = managed && typeof connectionValue === 'string' && connectionValue !== 'ImplicitConnection'
923
- ? str(connectionValue) : undefined;
906
+ ? tsLiteral(connectionValue) : undefined;
924
907
  const folder = managed && typeof detail.connectionFolderKey === 'string' && detail.connectionFolderKey.length > 0
925
- ? str(detail.connectionFolderKey) : undefined;
908
+ ? tsLiteral(detail.connectionFolderKey) : undefined;
926
909
  const targetConnector = managed && typeof req.targetConnector === 'string'
927
910
  && req.targetConnector !== 'uipath-uipath-http'
928
- ? str(req.targetConnector) : undefined;
911
+ ? tsLiteral(req.targetConnector) : undefined;
929
912
  const spec = opts([
930
913
  ['url', emitVal(req.url ?? '', imp, inputNames)],
931
- ['method', method !== 'GET' ? str(method) : undefined],
914
+ ['method', method !== 'GET' ? tsLiteral(method) : undefined],
932
915
  ['headers', headersSrc],
933
916
  ['query', querySrc],
934
917
  ['body', bodySrc],
@@ -964,43 +947,43 @@ function emitTransform(node, imp, inputNames) {
964
947
  const cfg = op?.config ?? {};
965
948
  if (op?.type === 'filter') {
966
949
  const filters = (cfg.filters ?? []).map((f) => objLit([
967
- ['field', str(f.field)],
968
- ['condition', str(f.condition)],
969
- ...(f.value !== undefined ? [['value', literal(f.value)]] : []),
950
+ ['field', tsLiteral(f.field)],
951
+ ['condition', tsLiteral(f.condition)],
952
+ ...(f.value !== undefined ? [['value', tsLiteral(f.value)]] : []),
970
953
  ]));
971
954
  return objLit([
972
- ['type', str('filter')],
973
- ...(cfg.operation !== undefined ? [['operation', str(cfg.operation)]] : []),
955
+ ['type', tsLiteral('filter')],
956
+ ...(cfg.operation !== undefined ? [['operation', tsLiteral(cfg.operation)]] : []),
974
957
  ['filters', `[${filters.join(', ')}]`],
975
958
  ]);
976
959
  }
977
960
  if (op?.type === 'map') {
978
961
  const mappings = (cfg.mappings ?? []).map((m) => objLit([
979
- ['field', str(m.field)],
980
- ...(m.transformation && m.transformation !== 'copy' ? [['transformation', str(m.transformation)]] : []),
981
- ...(m.renameTo ? [['renameTo', str(m.renameTo)]] : []),
962
+ ['field', tsLiteral(m.field)],
963
+ ...(m.transformation && m.transformation !== 'copy' ? [['transformation', tsLiteral(m.transformation)]] : []),
964
+ ...(m.renameTo ? [['renameTo', tsLiteral(m.renameTo)]] : []),
982
965
  ]));
983
966
  return objLit([
984
- ['type', str('map')],
967
+ ['type', tsLiteral('map')],
985
968
  ...(cfg.keepOriginalFields ? [['keepOriginalFields', boolLit(cfg.keepOriginalFields)]] : []),
986
969
  ['mappings', `[${mappings.join(', ')}]`],
987
970
  ]);
988
971
  }
989
972
  // groupBy
990
973
  const aggs = (cfg.aggregations ?? []).map((a) => objLit([
991
- ['operation', str(a.operation)],
992
- ['alias', str(a.alias)],
993
- ...(a.field ? [['field', str(a.field)]] : []),
974
+ ['operation', tsLiteral(a.operation)],
975
+ ['alias', tsLiteral(a.alias)],
976
+ ...(a.field ? [['field', tsLiteral(a.field)]] : []),
994
977
  ]));
995
978
  return objLit([
996
- ['type', str('groupBy')],
997
- ['groupByField', str(cfg.groupByField)],
979
+ ['type', tsLiteral('groupBy')],
980
+ ['groupByField', tsLiteral(cfg.groupByField)],
998
981
  ['aggregations', `[${aggs.join(', ')}]`],
999
982
  ]);
1000
983
  });
1001
984
  const spec = opts([
1002
985
  ['collection', collection],
1003
- ['variant', variant ? str(variant) : undefined],
986
+ ['variant', variant ? tsLiteral(variant) : undefined],
1004
987
  ['operations', `[${opSrcs.join(', ')}]`],
1005
988
  ]);
1006
989
  return `${imp.use('transform')}(${spec})`;
@@ -1180,7 +1163,7 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1180
1163
  };
1181
1164
  const promptSrc = (v) => {
1182
1165
  const raw = plain(v);
1183
- return raw === undefined ? undefined : str(unrender(raw));
1166
+ return raw === undefined ? undefined : tsLiteral(unrender(raw));
1184
1167
  };
1185
1168
  const inputRows = refs
1186
1169
  // Emit in the file's own order, not the length-sorted one.
@@ -1192,7 +1175,7 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1192
1175
  });
1193
1176
  const outVars = (Array.isArray(i.agentOutputVariables) ? i.agentOutputVariables : []);
1194
1177
  const returns = outVars.length
1195
- ? objLit(outVars.map((v) => [String(v.id ?? ''), str(String(v.type ?? 'string'))]))
1178
+ ? objLit(outVars.map((v) => [String(v.id ?? ''), tsLiteral(String(v.type ?? 'string'))]))
1196
1179
  : undefined;
1197
1180
  // The resources, from the agent's own artifact edges.
1198
1181
  const resource = (port) => (graph?.outEdges(node.id) ?? [])
@@ -1212,12 +1195,12 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1212
1195
  const props = v?.properties;
1213
1196
  if (!props || Object.keys(props).length === 0)
1214
1197
  return undefined;
1215
- return objLit(Object.entries(props).map(([k, x]) => [k, str(String(x?.type ?? 'string'))]));
1198
+ return objLit(Object.entries(props).map(([k, x]) => [k, tsLiteral(String(x?.type ?? 'string'))]));
1216
1199
  };
1217
1200
  return opts([
1218
- ['kind', str('clientside')],
1219
- ['name', str(rPlain(t, 'name') ?? '')],
1220
- ['description', rPlain(t, 'description') !== undefined ? str(rPlain(t, 'description')) : undefined],
1201
+ ['kind', tsLiteral('clientside')],
1202
+ ['name', tsLiteral(rPlain(t, 'name') ?? '')],
1203
+ ['description', rPlain(t, 'description') !== undefined ? tsLiteral(rPlain(t, 'description')) : undefined],
1221
1204
  ['inputs', schemaTypes(ti.inputSchema)],
1222
1205
  ['returns', schemaTypes(ti.outputSchema)],
1223
1206
  ]);
@@ -1229,10 +1212,10 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1229
1212
  if (!row || row.mode !== 'text-builder')
1230
1213
  return undefined;
1231
1214
  const v = row.textValue;
1232
- return typeof v === 'number' ? String(v) : typeof v === 'string' ? str(v) : JSON.stringify(v);
1215
+ return typeof v === 'number' ? String(v) : typeof v === 'string' ? tsLiteral(v) : JSON.stringify(v);
1233
1216
  };
1234
1217
  return opts([
1235
- ['kind', str('httpRequest')],
1218
+ ['kind', tsLiteral('httpRequest')],
1236
1219
  ['url', fixed('url')], ['method', fixed('method')],
1237
1220
  ['headers', fixed('headers')], ['params', fixed('params')],
1238
1221
  ['body', fixed('body')], ['timeout', fixed('timeout')],
@@ -1240,37 +1223,37 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1240
1223
  }
1241
1224
  if (type.startsWith('uipath.agent.resource.tool.mcp.')) {
1242
1225
  return opts([
1243
- ['kind', str('mcp')],
1244
- ['name', str(rPlain(t, 'name') ?? '')],
1245
- ['key', str(rPlain(t, 'referenceKey') ?? '')],
1226
+ ['kind', tsLiteral('mcp')],
1227
+ ['name', tsLiteral(rPlain(t, 'name') ?? '')],
1228
+ ['key', tsLiteral(rPlain(t, 'referenceKey') ?? '')],
1246
1229
  // The mapper's field name for the canonical server slug.
1247
- ['slug', rPlain(t, 'serverUrl') !== undefined ? str(rPlain(t, 'serverUrl')) : undefined],
1248
- ['folderPath', rPlain(t, 'folderPath') !== undefined ? str(rPlain(t, 'folderPath')) : undefined],
1249
- ['mcpType', rPlain(t, 'mcpType') !== undefined ? str(rPlain(t, 'mcpType')) : undefined],
1230
+ ['slug', rPlain(t, 'serverUrl') !== undefined ? tsLiteral(rPlain(t, 'serverUrl')) : undefined],
1231
+ ['folderPath', rPlain(t, 'folderPath') !== undefined ? tsLiteral(rPlain(t, 'folderPath')) : undefined],
1232
+ ['mcpType', rPlain(t, 'mcpType') !== undefined ? tsLiteral(rPlain(t, 'mcpType')) : undefined],
1250
1233
  ]);
1251
1234
  }
1252
1235
  if (type.startsWith('uipath.agent.resource.tool.a2a.')) {
1253
1236
  return opts([
1254
- ['kind', str('a2a')],
1255
- ['name', str(rPlain(t, 'name') ?? '')],
1256
- ['key', str(rPlain(t, 'referenceKey') ?? '')],
1257
- ['slug', str(rPlain(t, 'slug') ?? '')],
1258
- ['folderPath', rPlain(t, 'folderPath') !== undefined ? str(rPlain(t, 'folderPath')) : undefined],
1237
+ ['kind', tsLiteral('a2a')],
1238
+ ['name', tsLiteral(rPlain(t, 'name') ?? '')],
1239
+ ['key', tsLiteral(rPlain(t, 'referenceKey') ?? '')],
1240
+ ['slug', tsLiteral(rPlain(t, 'slug') ?? '')],
1241
+ ['folderPath', rPlain(t, 'folderPath') !== undefined ? tsLiteral(rPlain(t, 'folderPath')) : undefined],
1259
1242
  ]);
1260
1243
  }
1261
1244
  if (type.startsWith('uipath.agent.resource.tool.builtin.')) {
1262
1245
  return opts([
1263
- ['kind', str('builtin')],
1264
- ['tool', str(type.slice('uipath.agent.resource.tool.builtin.'.length))],
1265
- ['description', rPlain(t, 'description') !== undefined ? str(rPlain(t, 'description')) : undefined],
1246
+ ['kind', tsLiteral('builtin')],
1247
+ ['tool', tsLiteral(type.slice('uipath.agent.resource.tool.builtin.'.length))],
1248
+ ['description', rPlain(t, 'description') !== undefined ? tsLiteral(rPlain(t, 'description')) : undefined],
1266
1249
  ]);
1267
1250
  }
1268
1251
  if (type.startsWith('uipath.agent.resource.tool.ixp.')) {
1269
1252
  const tail = type.slice('uipath.agent.resource.tool.ixp.'.length);
1270
1253
  return opts([
1271
- ['kind', str('ixp')],
1272
- ['projectId', str(tail.slice(tail.indexOf('.') + 1))],
1273
- ['name', str(rPlain(t, 'name') ?? '')],
1254
+ ['kind', tsLiteral('ixp')],
1255
+ ['projectId', tsLiteral(tail.slice(tail.indexOf('.') + 1))],
1256
+ ['name', tsLiteral(rPlain(t, 'name') ?? '')],
1274
1257
  ]);
1275
1258
  }
1276
1259
  // The per-instance resource kinds (process / api / flow / maestro / agent /
@@ -1292,13 +1275,13 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1292
1275
  return typeof row?.default === 'string' ? row.default : undefined;
1293
1276
  };
1294
1277
  return opts([
1295
- ['kind', str(ns[1])],
1296
- ['key', str(type.slice(ns[0].length))],
1297
- ['name', str(bound('name') ?? rPlain(t, 'name') ?? '')],
1298
- ['folderPath', str(bound('folderPath') ?? '')],
1278
+ ['kind', tsLiteral(ns[1])],
1279
+ ['key', tsLiteral(type.slice(ns[0].length))],
1280
+ ['name', tsLiteral(bound('name') ?? rPlain(t, 'name') ?? '')],
1281
+ ['folderPath', tsLiteral(bound('folderPath') ?? '')],
1299
1282
  ]);
1300
1283
  }
1301
- return opts([['kind', str('builtin')], ['tool', str('summarize')]]); // unreachable in practice
1284
+ return opts([['kind', tsLiteral('builtin')], ['tool', tsLiteral('summarize')]]); // unreachable in practice
1302
1285
  });
1303
1286
  const contextSrc = resource('context').map((c) => {
1304
1287
  // `query` is a MODE TRIPLE, not a string: the author's value lands in
@@ -1310,17 +1293,17 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1310
1293
  : typeof q === 'string' && q !== '' ? q
1311
1294
  : undefined;
1312
1295
  return opts([
1313
- ['name', str(rPlain(c, 'indexName') ?? String(c.display?.label ?? ''))],
1314
- ['id', str(rPlain(c, 'indexId') ?? '')],
1315
- ['folderPath', rPlain(c, 'folderPath') !== undefined ? str(rPlain(c, 'folderPath')) : undefined],
1316
- ['folderKey', rPlain(c, 'folderKey') !== undefined ? str(rPlain(c, 'folderKey')) : undefined],
1317
- ['query', queryText !== undefined ? str(queryText) : undefined],
1296
+ ['name', tsLiteral(rPlain(c, 'indexName') ?? String(c.display?.label ?? ''))],
1297
+ ['id', tsLiteral(rPlain(c, 'indexId') ?? '')],
1298
+ ['folderPath', rPlain(c, 'folderPath') !== undefined ? tsLiteral(rPlain(c, 'folderPath')) : undefined],
1299
+ ['folderKey', rPlain(c, 'folderKey') !== undefined ? tsLiteral(rPlain(c, 'folderKey')) : undefined],
1300
+ ['query', queryText !== undefined ? tsLiteral(queryText) : undefined],
1318
1301
  ]);
1319
1302
  });
1320
1303
  const escalationSrc = resource('escalation').map((e) => {
1321
1304
  const ei = rInputs(e);
1322
1305
  const recipients = Array.isArray(ei.recipients)
1323
- ? ei.recipients.map((r) => str(String(r.value ?? '')))
1306
+ ? ei.recipients.map((r) => tsText(r.value))
1324
1307
  : [];
1325
1308
  const outcomeNames = Object.keys((ei.outcomeMapping ?? {}));
1326
1309
  const isQuickForm = e.type === 'uipath.agent.resource.escalation.quick-form';
@@ -1330,24 +1313,24 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1330
1313
  const app = ei.app;
1331
1314
  const extra = (ei._additionalProps ?? {});
1332
1315
  return opts([
1333
- ['variant', isQuickForm ? str('quick-form') : undefined],
1334
- ['name', str(rPlain(e, 'name') ?? '')],
1335
- ['description', rPlain(e, 'description') !== undefined ? str(rPlain(e, 'description')) : undefined],
1316
+ ['variant', isQuickForm ? tsLiteral('quick-form') : undefined],
1317
+ ['name', tsLiteral(rPlain(e, 'name') ?? '')],
1318
+ ['description', rPlain(e, 'description') !== undefined ? tsLiteral(rPlain(e, 'description')) : undefined],
1336
1319
  ['fields', rows.length ? `[${rows.map((f) => opts([
1337
- ['id', str(String(f.id ?? ''))],
1338
- ['type', str(String(f.type ?? 'text'))],
1339
- ['direction', str(String(f.direction ?? 'output'))],
1320
+ ['id', tsText(f.id)],
1321
+ ['type', tsLiteral(String(f.type ?? 'text'))],
1322
+ ['direction', tsLiteral(String(f.direction ?? 'output'))],
1340
1323
  ])).join(', ')}]` : undefined],
1341
1324
  ['app', app === undefined ? undefined : opts([
1342
- ['key', str(String(app.key ?? ''))],
1343
- ['name', str(String(app.name ?? ''))],
1344
- ['folderPath', typeof app.folderPath === 'string' && app.folderPath !== '' ? str(app.folderPath) : undefined],
1325
+ ['key', tsText(app.key)],
1326
+ ['name', tsText(app.name)],
1327
+ ['folderPath', typeof app.folderPath === 'string' && app.folderPath !== '' ? tsLiteral(app.folderPath) : undefined],
1345
1328
  ])],
1346
1329
  ['recipients', recipients.length ? `[${recipients.join(', ')}]` : undefined],
1347
- ['outcomes', outcomeNames.length ? `[${outcomeNames.map((o) => str(o)).join(', ')}]` : undefined],
1348
- ['taskTitle', typeof extra.taskTitle === 'string' && extra.taskTitle !== '' ? str(extra.taskTitle) : undefined],
1330
+ ['outcomes', outcomeNames.length ? `[${outcomeNames.map((o) => tsLiteral(o)).join(', ')}]` : undefined],
1331
+ ['taskTitle', typeof extra.taskTitle === 'string' && extra.taskTitle !== '' ? tsLiteral(extra.taskTitle) : undefined],
1349
1332
  // 'medium' is the platform default.
1350
- ['priority', typeof extra.priority === 'string' && extra.priority !== 'medium' ? str(extra.priority) : undefined],
1333
+ ['priority', typeof extra.priority === 'string' && extra.priority !== 'medium' ? tsLiteral(extra.priority) : undefined],
1351
1334
  ]);
1352
1335
  });
1353
1336
  const mem = resource('memory')[0];
@@ -1357,15 +1340,15 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1357
1340
  const id = tail.slice(tail.indexOf('.') + 1);
1358
1341
  const mi = rInputs(mem);
1359
1342
  return opts([
1360
- ['name', str(rPlain(mem, 'name') ?? '')],
1361
- ['id', str(id)],
1362
- ['description', rPlain(mem, 'description') !== undefined ? str(rPlain(mem, 'description')) : undefined],
1343
+ ['name', tsLiteral(rPlain(mem, 'name') ?? '')],
1344
+ ['id', tsLiteral(id)],
1345
+ ['description', rPlain(mem, 'description') !== undefined ? tsLiteral(rPlain(mem, 'description')) : undefined],
1363
1346
  // The platform defaults: few-shot on, similarity 0, k 3, hybrid search.
1364
1347
  ['dynamicFewShotLearning', unwrapPlain(mi.dynamicFewShotLearning) === false ? 'false' : undefined],
1365
1348
  ['semanticSimilarity', num(mi.semanticSimilarity) !== undefined && num(mi.semanticSimilarity) !== '0' ? num(mi.semanticSimilarity) : undefined],
1366
1349
  ['kValue', num(mi.kValue) !== undefined && num(mi.kValue) !== '3' ? num(mi.kValue) : undefined],
1367
1350
  ['searchMode', rPlain(mem, 'searchMode') !== undefined && rPlain(mem, 'searchMode') !== 'hybrid'
1368
- ? str(rPlain(mem, 'searchMode')) : undefined],
1351
+ ? tsLiteral(rPlain(mem, 'searchMode')) : undefined],
1369
1352
  ]);
1370
1353
  })();
1371
1354
  const guardrails = Array.isArray(i.guardrails) && i.guardrails.length > 0
@@ -1375,22 +1358,22 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1375
1358
  if (node.type === T.voiceAgent) {
1376
1359
  const v = (i.voice ?? {});
1377
1360
  const voiceSrc = Object.keys(v).length === 0 ? undefined : opts([
1378
- ['model', typeof v.model === 'string' ? str(v.model) : undefined],
1379
- ['persona', typeof v.persona === 'string' ? str(v.persona) : undefined],
1361
+ ['model', typeof v.model === 'string' ? tsLiteral(v.model) : undefined],
1362
+ ['persona', typeof v.persona === 'string' ? tsLiteral(v.persona) : undefined],
1380
1363
  ['temperature', num(v.temperature)],
1381
1364
  ['maxTokens', num(v.maxTokens)],
1382
1365
  ]);
1383
1366
  return `${imp.use('voiceAgent')}(${opts([
1384
- ['systemPrompt', promptSrc(i.systemPrompt) ?? str('')],
1367
+ ['systemPrompt', promptSrc(i.systemPrompt) ?? tsLiteral('')],
1385
1368
  ['inputs', inputRows.length ? objLit(inputRows) : undefined],
1386
1369
  ['callContext', (() => {
1387
1370
  const raw = plain(i.callContext) ?? '';
1388
1371
  const js = raw.startsWith('=js:') ? raw.slice(4) : raw;
1389
- return js.startsWith('$vars.') ? emitBareExpr(js, imp, inputNames) : str(js);
1372
+ return js.startsWith('$vars.') ? emitBareExpr(js, imp, inputNames) : tsLiteral(js);
1390
1373
  })()],
1391
1374
  ['voice', voiceSrc],
1392
1375
  ['maxIterations', num(i.maxIterations)],
1393
- ['source', str(plain(i.source) ?? '')],
1376
+ ['source', tsLiteral(plain(i.source) ?? '')],
1394
1377
  ])})`;
1395
1378
  }
1396
1379
  if (node.type === T.conversationalAgent) {
@@ -1407,7 +1390,7 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1407
1390
  // `out(...)` and anything composite to a `js` template. Any other string
1408
1391
  // is literal text, as the product reads it (flow-schema
1409
1392
  // `coerceToExpressionValue`), even when it looks like `$vars.…`.
1410
- return raw.startsWith('=js:') ? emitRef(raw.slice(4), imp, inputNames) : str(raw);
1393
+ return raw.startsWith('=js:') ? emitRef(raw.slice(4), imp, inputNames) : tsLiteral(raw);
1411
1394
  };
1412
1395
  // `'simple'` mode DERIVES the four runtime fields from `context`, so only
1413
1396
  // the context binding is the author's; recovering the derived ones would
@@ -1415,7 +1398,7 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1415
1398
  const mode = typeof st.mode === 'string' ? st.mode : 'simple';
1416
1399
  const settingsSrc = mode === 'custom'
1417
1400
  ? opts([
1418
- ['mode', str('custom')],
1401
+ ['mode', tsLiteral('custom')],
1419
1402
  ['conversationId', settingExpr('conversationId')],
1420
1403
  ['exchangeId', settingExpr('exchangeId')],
1421
1404
  ['messages', settingExpr('messages')],
@@ -1423,8 +1406,8 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1423
1406
  ])
1424
1407
  : opts([['context', settingExpr('context')]]);
1425
1408
  return `${imp.use('conversationalAgent')}(${opts([
1426
- ['model', str(plain(i.model) ?? '')],
1427
- ['systemPrompt', promptSrc(i.systemPrompt) ?? str('')],
1409
+ ['model', tsLiteral(plain(i.model) ?? '')],
1410
+ ['systemPrompt', promptSrc(i.systemPrompt) ?? tsLiteral('')],
1428
1411
  // `settings` is required on ConversationalAgentInputs, so an empty one
1429
1412
  // stays as `{}`, which check then reports as CONV_AGENT_NO_TURN.
1430
1413
  ['settings', settingsSrc || '{}'],
@@ -1432,19 +1415,19 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1432
1415
  ['maxTokenPerResponse', num(i.maxTokenPerResponse)],
1433
1416
  ['maxIterations', num(i.maxIterations)],
1434
1417
  ['guardrails', guardrails],
1435
- ['source', str(plain(i.source) ?? '')],
1418
+ ['source', tsLiteral(plain(i.source) ?? '')],
1436
1419
  ])})`;
1437
1420
  }
1438
1421
  return `${imp.use('inlineAgent')}(${opts([
1439
- ['model', str(plain(i.model) ?? '')],
1440
- ['systemPrompt', promptSrc(i.systemPrompt) ?? str('')],
1441
- ['userPrompt', promptSrc(i.userPrompt) ?? str('')],
1422
+ ['model', tsLiteral(plain(i.model) ?? '')],
1423
+ ['systemPrompt', promptSrc(i.systemPrompt) ?? tsLiteral('')],
1424
+ ['userPrompt', promptSrc(i.userPrompt) ?? tsLiteral('')],
1442
1425
  ['inputs', inputRows.length ? objLit(inputRows) : undefined],
1443
1426
  ['returns', returns],
1444
1427
  ['temperature', num(i.temperature)],
1445
1428
  ['maxTokenPerResponse', num(i.maxTokenPerResponse)],
1446
1429
  ['maxIterations', num(i.maxIterations)],
1447
- ['mode', plain(i.mode) !== undefined ? str(plain(i.mode)) : undefined],
1430
+ ['mode', plain(i.mode) !== undefined ? tsLiteral(plain(i.mode)) : undefined],
1448
1431
  ['guardrails', guardrails],
1449
1432
  ['context', list(contextSrc)],
1450
1433
  ['tools', list(toolSrc)],
@@ -1453,7 +1436,7 @@ function emitAgentCluster(node, imp, inputNames, graph) {
1453
1436
  // The agent's directory uuid is derived from the flow and step ids, but a
1454
1437
  // decompiled flow must keep the ORIGINAL — the sidecar lives at
1455
1438
  // `<source>/agent.json`, so a re-derived one would point at nothing.
1456
- ['source', str(plain(i.source) ?? '')],
1439
+ ['source', tsLiteral(plain(i.source) ?? '')],
1457
1440
  ])})`;
1458
1441
  }
1459
1442
  /**
@@ -1496,11 +1479,11 @@ function emitHitl(node, imp, inputNames, graph) {
1496
1479
  const id = String(f.id ?? '');
1497
1480
  const direction = String(f.direction ?? 'output');
1498
1481
  return opts([
1499
- ['id', str(id)],
1482
+ ['id', tsLiteral(id)],
1500
1483
  // serialize defaults a row's label to its id.
1501
- ['label', typeof f.label === 'string' && f.label !== id ? str(f.label) : undefined],
1502
- ['type', str(String(f.type ?? 'text'))],
1503
- ['direction', str(direction)],
1484
+ ['label', typeof f.label === 'string' && f.label !== id ? tsLiteral(f.label) : undefined],
1485
+ ['type', tsLiteral(String(f.type ?? 'text'))],
1486
+ ['direction', tsLiteral(direction)],
1504
1487
  ['value', direction === 'input' || direction === 'inOut' ? bindingExpr(f.binding) : undefined],
1505
1488
  ['required', f.required === true ? 'true' : undefined],
1506
1489
  ]);
@@ -1509,7 +1492,7 @@ function emitHitl(node, imp, inputNames, graph) {
1509
1492
  const outcomes = outcomeRows.map((o) => {
1510
1493
  const name = String(o.name ?? '');
1511
1494
  // A bare string is the shorthand; only `action: 'End'` needs the object.
1512
- return o.action === 'End' ? opts([['name', str(name)], ['action', str('End')]]) : str(name);
1495
+ return o.action === 'End' ? opts([['name', tsLiteral(name)], ['action', tsLiteral('End')]]) : tsLiteral(name);
1513
1496
  });
1514
1497
  // Delivery: only an override comes back. The definition's own default is what
1515
1498
  // serialize emits when the author wrote nothing.
@@ -1521,11 +1504,11 @@ function emitHitl(node, imp, inputNames, graph) {
1521
1504
  const assignee = (rec.assignee ?? {});
1522
1505
  const channels = Array.isArray(rec.channels) ? rec.channels : undefined;
1523
1506
  recipientSrc = opts([
1524
- ['channels', channels ? `[${channels.map((c) => str(c)).join(', ')}]` : undefined],
1507
+ ['channels', channels ? `[${channels.map((c) => tsLiteral(c)).join(', ')}]` : undefined],
1525
1508
  ['assignee', opts([
1526
- ['type', str(String(assignee.type ?? 'user'))],
1527
- ['value', typeof assignee.value === 'string' ? str(assignee.value) : undefined],
1528
- ['displayName', typeof assignee.displayName === 'string' ? str(assignee.displayName) : undefined],
1509
+ ['type', tsLiteral(String(assignee.type ?? 'user'))],
1510
+ ['value', typeof assignee.value === 'string' ? tsLiteral(assignee.value) : undefined],
1511
+ ['displayName', typeof assignee.displayName === 'string' ? tsLiteral(assignee.displayName) : undefined],
1529
1512
  ])],
1530
1513
  ]);
1531
1514
  }
@@ -1540,15 +1523,15 @@ function emitHitl(node, imp, inputNames, graph) {
1540
1523
  const bindings = (i.appInputBindings ?? {});
1541
1524
  const appInputs = Object.keys(bindings).length === 0 ? undefined : objLit(Object.entries(bindings).map(([k, v]) => {
1542
1525
  const raw = String(v ?? '');
1543
- return [k, raw.startsWith('=') ? emitBareExpr(`$${raw.slice(1)}`, imp, inputNames) : str(raw)];
1526
+ return [k, raw.startsWith('=') ? emitBareExpr(`$${raw.slice(1)}`, imp, inputNames) : tsLiteral(raw)];
1544
1527
  }));
1545
1528
  const appSrc = app === undefined ? undefined : opts([
1546
1529
  // `displayName` is serialize's echo of `name`; only `name` is authored.
1547
- ['name', str(String(app.name ?? ''))],
1548
- ['key', str(String(app.key ?? ''))],
1549
- ['folderPath', typeof app.folderPath === 'string' && app.folderPath !== '' ? str(app.folderPath) : undefined],
1530
+ ['name', tsText(app.name)],
1531
+ ['key', tsText(app.key)],
1532
+ ['folderPath', typeof app.folderPath === 'string' && app.folderPath !== '' ? tsLiteral(app.folderPath) : undefined],
1550
1533
  ['appSystemName', app.appSystemName === null ? 'null'
1551
- : typeof app.appSystemName === 'string' ? str(app.appSystemName) : undefined],
1534
+ : typeof app.appSystemName === 'string' ? tsLiteral(app.appSystemName) : undefined],
1552
1535
  ['inputs', appInputs],
1553
1536
  ]);
1554
1537
  // The validation station's own inputs ride as PLAIN `$vars.…` paths.
@@ -1557,15 +1540,15 @@ function emitHitl(node, imp, inputNames, graph) {
1557
1540
  if (raw === undefined)
1558
1541
  return undefined;
1559
1542
  const js = raw.startsWith('=js:') ? raw.slice(4) : raw;
1560
- return js.startsWith('$vars.') ? emitBareExpr(js, imp, inputNames) : str(js);
1543
+ return js.startsWith('$vars.') ? emitBareExpr(js, imp, inputNames) : tsLiteral(js);
1561
1544
  };
1562
1545
  const render = plain(i.validationRenderType);
1563
1546
  const documentSrc = node.type !== T.hitlDocValidation ? undefined : opts([
1564
- ['extractionResult', stationExpr(i.extractionResult) ?? str('')],
1565
- ['storageBucket', plain(i.storageBucket) !== undefined ? str(plain(i.storageBucket)) : undefined],
1566
- ['documentId', plain(i.documentId) !== undefined ? str(plain(i.documentId)) : undefined],
1547
+ ['extractionResult', stationExpr(i.extractionResult) ?? tsLiteral('')],
1548
+ ['storageBucket', plain(i.storageBucket) !== undefined ? tsLiteral(plain(i.storageBucket)) : undefined],
1549
+ ['documentId', plain(i.documentId) !== undefined ? tsLiteral(plain(i.documentId)) : undefined],
1567
1550
  // 'standard' is the definition's default.
1568
- ['render', render !== undefined && render !== 'standard' ? str(render) : undefined],
1551
+ ['render', render !== undefined && render !== 'standard' ? tsLiteral(render) : undefined],
1569
1552
  ['taxonomy', stationExpr(i.taxonomy)],
1570
1553
  ]);
1571
1554
  // On the base node the VERSION says it: 1.1 routes per outcome, 1.2 adds the
@@ -1596,7 +1579,7 @@ function emitHitl(node, imp, inputNames, graph) {
1596
1579
  const fanOut = !outcomePorts && derivesHandles && !versionRoutesHitl(node) && outcomeIdsOfNode(node).length > 1
1597
1580
  && (legacyCompleted || graph?.convergedFanOut(node.id) === true);
1598
1581
  if (legacyCompleted) {
1599
- warnOnce(`human task ${str(node.id)} leaves on the legacy 'completed' handle, which Studio Web no longer draws `
1582
+ warnOnce(`human task ${tsLiteral(node.id)} leaves on the legacy 'completed' handle, which Studio Web no longer draws `
1600
1583
  + `once a task has outcomes (it reads the edge as the primary outcome only). Recompiling emits one `
1601
1584
  + `outcome-<id> edge per outcome to the same step, so every outcome continues — what the edge meant `
1602
1585
  + `before flow-workbench #3512.`);
@@ -1604,11 +1587,11 @@ function emitHitl(node, imp, inputNames, graph) {
1604
1587
  const title = plain(i.title);
1605
1588
  const priority = plain(i.priority);
1606
1589
  return `${imp.use('hitl')}(${opts([
1607
- ['variant', variant !== undefined ? str(variant) : undefined],
1590
+ ['variant', variant !== undefined ? tsLiteral(variant) : undefined],
1608
1591
  // serialize defaults the title to the step's own name.
1609
- ['title', title !== undefined && title !== node.id ? str(title) : undefined],
1610
- ['priority', priority !== undefined && priority !== 'Low' ? str(priority) : undefined],
1611
- ['labels', plain(i.labels) !== undefined ? str(plain(i.labels)) : undefined],
1592
+ ['title', title !== undefined && title !== node.id ? tsLiteral(title) : undefined],
1593
+ ['priority', priority !== undefined && priority !== 'Low' ? tsLiteral(priority) : undefined],
1594
+ ['labels', plain(i.labels) !== undefined ? tsLiteral(plain(i.labels)) : undefined],
1612
1595
  ['recipient', recipientSrc],
1613
1596
  ['app', appSrc],
1614
1597
  ['document', documentSrc],
@@ -1645,7 +1628,7 @@ function unboundMarker(node, labels, key, action) {
1645
1628
  if (missing.length === 0)
1646
1629
  return '';
1647
1630
  const names = missing.join(' and ');
1648
- warnOnce(`node ${str(node.id)} (${node.type}) has no ${missing.join(' or ')} in the .flow (an unbound draft). `
1631
+ warnOnce(`node ${tsLiteral(node.id)} (${node.type}) has no ${missing.join(' or ')} in the .flow (an unbound draft). `
1649
1632
  + `The source leaves ${missing.length > 1 ? 'them' : 'it'} out, and compile refuses the node (BINDING_MISSING) `
1650
1633
  + `until it names both labels: run \`${prepareCommand(key, action ?? '<action>')}\`, then pass `
1651
1634
  + `\`connection\` and \`folder\`.`);
@@ -1685,8 +1668,8 @@ function emitEventSubscription(node, imp, inputNames) {
1685
1668
  .map((f) => eventFilterFromTreeLeaf(f))
1686
1669
  .filter((f) => f !== undefined)
1687
1670
  .map((f) => opts([
1688
- ['field', str(f.field)],
1689
- [f.operator, typeof f.value === 'string' ? str(f.value) : JSON.stringify(f.value)],
1671
+ ['field', tsLiteral(f.field)],
1672
+ [f.operator, typeof f.value === 'string' ? tsLiteral(f.value) : JSON.stringify(f.value)],
1690
1673
  ]));
1691
1674
  }
1692
1675
  catch {
@@ -1701,13 +1684,13 @@ function emitEventSubscription(node, imp, inputNames) {
1701
1684
  const conn = typeof detail?.connectionId === 'string' ? detail.connectionId : undefined;
1702
1685
  const folder = typeof detail?.connectionFolderKey === 'string' ? detail.connectionFolderKey : undefined;
1703
1686
  return opts([
1704
- ['connector', str(connector)],
1705
- ['event', str(event)],
1687
+ ['connector', tsLiteral(connector)],
1688
+ ['event', tsLiteral(event)],
1706
1689
  ['where', whereRows.length ? objLit(whereRows) : undefined],
1707
- ['object', object !== undefined ? str(object) : undefined],
1690
+ ['object', object !== undefined ? tsLiteral(object) : undefined],
1708
1691
  ['filters', filterRows.length ? `[${filterRows.join(', ')}]` : undefined],
1709
- ['connection', conn !== undefined ? str(conn) : undefined],
1710
- ['folder', folder !== undefined ? str(folder) : undefined],
1692
+ ['connection', conn !== undefined ? tsLiteral(conn) : undefined],
1693
+ ['folder', folder !== undefined ? tsLiteral(folder) : undefined],
1711
1694
  ]) + unboundMarker(node, { connection: conn, folder }, connector, event);
1712
1695
  }
1713
1696
  /**
@@ -1780,18 +1763,18 @@ function emitPublishedResource(node, imp, inputNames) {
1780
1763
  .map(([k, v]) => [k, emitVal(v, imp, inputNames)]);
1781
1764
  const schema = def?.outputDefinition?.output?.schema?.properties;
1782
1765
  const returns = schema && Object.keys(schema).length > 0
1783
- ? objLit(Object.entries(schema).map(([k, v]) => [k, str(String(v?.type ?? 'string'))]))
1766
+ ? objLit(Object.entries(schema).map(([k, v]) => [k, tsLiteral(String(v?.type ?? 'string'))]))
1784
1767
  : undefined;
1785
1768
  return `${imp.use(family.factory)}(${opts([
1786
- ['key', str(key)],
1787
- ['name', name !== undefined ? str(name) : undefined],
1788
- ['location', inSolution ? str('in-solution') : undefined],
1789
- ['folderPath', !inSolution && folderPath !== undefined ? str(folderPath) : undefined],
1790
- ['projectId', inSolution && projectId !== undefined ? str(projectId) : undefined],
1769
+ ['key', tsLiteral(key)],
1770
+ ['name', name !== undefined ? tsLiteral(name) : undefined],
1771
+ ['location', inSolution ? tsLiteral('in-solution') : undefined],
1772
+ ['folderPath', !inSolution && folderPath !== undefined ? tsLiteral(folderPath) : undefined],
1773
+ ['projectId', inSolution && projectId !== undefined ? tsLiteral(projectId) : undefined],
1791
1774
  ['inputs', inputRows.length ? objLit(inputRows) : undefined],
1792
1775
  ['returns', returns],
1793
- ['form', family.form !== undefined ? str(family.form) : undefined],
1794
- ['completion', fireAndForget ? str('fire-and-forget') : undefined],
1776
+ ['form', family.form !== undefined ? tsLiteral(family.form) : undefined],
1777
+ ['completion', fireAndForget ? tsLiteral('fire-and-forget') : undefined],
1795
1778
  ])})`;
1796
1779
  }
1797
1780
  function emitConnector(node, imp, inputNames, o) {
@@ -1820,12 +1803,12 @@ function emitConnector(node, imp, inputNames, o) {
1820
1803
  const folder = typeof detail.connectionFolderKey === 'string' ? detail.connectionFolderKey : undefined;
1821
1804
  const object = connectorObjectName(detail.configuration);
1822
1805
  const optsSrc = opts([
1823
- ['connection', connection !== undefined ? str(connection) : undefined],
1824
- ['folder', folder !== undefined ? str(folder) : undefined],
1825
- ['object', object !== undefined ? str(object) : undefined],
1806
+ ['connection', connection !== undefined ? tsLiteral(connection) : undefined],
1807
+ ['folder', folder !== undefined ? tsLiteral(folder) : undefined],
1808
+ ['object', object !== undefined ? tsLiteral(object) : undefined],
1826
1809
  ]);
1827
1810
  const inputsSrc = objLit(flat) || '{}';
1828
- const args = [str(split.key), str(split.action), inputsSrc, ...(optsSrc ? [optsSrc] : [])];
1811
+ const args = [tsLiteral(split.key), tsLiteral(split.action), inputsSrc, ...(optsSrc ? [optsSrc] : [])];
1829
1812
  return `${imp.use('connector')}(${args.join(', ')})`
1830
1813
  + unboundMarker(node, { connection, folder }, split.key, split.action);
1831
1814
  }
@@ -1847,8 +1830,8 @@ function unsupportedSpec(node, imp, o) {
1847
1830
  const inputs = node.inputs ?? {};
1848
1831
  const inputsSrc = Object.keys(inputs).length > 0 ? JSON.stringify(inputs, null, 2) : undefined;
1849
1832
  return `${imp.use('rawNode')}(${opts([
1850
- ['nodeType', str(node.type)],
1851
- ['version', str(String(node.typeVersion ?? ''))],
1833
+ ['nodeType', tsLiteral(node.type)],
1834
+ ['version', tsText(node.typeVersion)],
1852
1835
  ['manifest', constName],
1853
1836
  ['inputs', inputsSrc],
1854
1837
  ])})`;
@@ -1879,7 +1862,7 @@ const TYPE_NAMES = new Set([
1879
1862
  function typeExpr(type, imp) {
1880
1863
  if (TYPE_NAMES.has(type))
1881
1864
  return `${imp.use('types')}.${type}`;
1882
- return str(type);
1865
+ return tsLiteral(type);
1883
1866
  }
1884
1867
  /**
1885
1868
  * A global carrying metadata decompiles to the rich VarSpec spelling
@@ -1889,15 +1872,15 @@ function typeExpr(type, imp) {
1889
1872
  function declExpr(g, withDefault, imp, shared) {
1890
1873
  const meta = [];
1891
1874
  if (withDefault && g.defaultValue !== undefined)
1892
- meta.push(`default: ${literal(g.defaultValue)}`);
1875
+ meta.push(`default: ${tsLiteral(g.defaultValue)}`);
1893
1876
  if (g.description !== undefined)
1894
- meta.push(`description: ${str(g.description)}`);
1877
+ meta.push(`description: ${tsLiteral(g.description)}`);
1895
1878
  if (g.subType !== undefined)
1896
- meta.push(`subType: ${str(g.subType)}`);
1879
+ meta.push(`subType: ${tsLiteral(g.subType)}`);
1897
1880
  if (g.schema !== undefined)
1898
- meta.push(`schema: ${literal(g.schema)}`);
1881
+ meta.push(`schema: ${tsLiteral(g.schema)}`);
1899
1882
  if (shared !== undefined)
1900
- meta.push(`shared: ${str(shared)}`);
1883
+ meta.push(`shared: ${tsLiteral(shared)}`);
1901
1884
  if (meta.length === 0)
1902
1885
  return typeExpr(g.type, imp);
1903
1886
  return `{ type: ${typeExpr(g.type, imp)}, ${meta.join(', ')} }`;
@@ -1954,8 +1937,8 @@ function emitVariables(e, depth, flow, imp, epTriggers = new Set(), shared) {
1954
1937
  e.line(depth, `.output(${shape(outputs, true)})`);
1955
1938
  for (const g of inouts) {
1956
1939
  const rich = declExpr(g, false, imp);
1957
- const def = g.defaultValue !== undefined ? `, ${literal(g.defaultValue)}` : '';
1958
- e.line(depth, `.var(${str(g.name ?? g.id)}, ${rich}${def})`);
1940
+ const def = g.defaultValue !== undefined ? `, ${tsLiteral(g.defaultValue)}` : '';
1941
+ e.line(depth, `.var(${tsLiteral(g.name ?? g.id)}, ${rich}${def})`);
1959
1942
  }
1960
1943
  }
1961
1944
  // ─── Trigger ──────────────────────────────────────────────────────────────────
@@ -1971,7 +1954,7 @@ function triggerFactory(t, imp, inputNames) {
1971
1954
  if (t.type === T.triggerManual)
1972
1955
  return `${imp.use('manual')}()`;
1973
1956
  if (t.type === T.triggerScheduled)
1974
- return `${imp.use('scheduled')}(${opts([['every', str(scheduleEvery(t))]])})`;
1957
+ return `${imp.use('scheduled')}(${opts([['every', tsLiteral(scheduleEvery(t))]])})`;
1975
1958
  // The form schema is DERIVED from the root's inputs at compile time, so
1976
1959
  // nothing of it needs recovering — the factory takes no arguments.
1977
1960
  if (t.type === T.triggerForm)
@@ -1991,7 +1974,7 @@ function emitTrigger(e, depth, trigger, imp, o, inputNames = new Set()) {
1991
1974
  // output — so a renamed one has to survive the round trip. `start` is the
1992
1975
  // default and stays implicit.
1993
1976
  if (trigger.id && trigger.id !== DEFAULT_TRIGGER_ID)
1994
- e.line(depth, `.triggerId(${str(trigger.id)})`);
1977
+ e.line(depth, `.triggerId(${tsLiteral(trigger.id)})`);
1995
1978
  const factory = triggerFactory(trigger, imp, inputNames);
1996
1979
  if (factory !== undefined) {
1997
1980
  e.line(depth, `.trigger(${factory}${triggerOptsArg(trigger)})`);
@@ -2182,14 +2165,14 @@ function isIdentityEndSource(raw, name) {
2182
2165
  function returnOptsArg(node) {
2183
2166
  const parts = [];
2184
2167
  if (node.id !== 'end')
2185
- parts.push(`name: ${str(node.id)}`);
2168
+ parts.push(`name: ${tsLiteral(node.id)}`);
2186
2169
  const ver = verClause(node);
2187
2170
  if (ver)
2188
2171
  parts.push(ver);
2189
2172
  // The serializer labels an unlabelled End `End`, so that needs no carrying.
2190
2173
  const label = node.display?.label;
2191
2174
  if (typeof label === 'string' && label !== 'End')
2192
- parts.push(`label: ${str(label)}`);
2175
+ parts.push(`label: ${tsLiteral(label)}`);
2193
2176
  return parts.length ? `, { ${parts.join(', ')} }` : '';
2194
2177
  }
2195
2178
  /**
@@ -2228,7 +2211,7 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2228
2211
  while (cursor !== undefined && cursor !== stop && cursor !== EXIT) {
2229
2212
  // A ref forward: this path re-enters a step the main path reaches later.
2230
2213
  if (refInto?.has(cursor)) {
2231
- segs.push(`.stepToRef(${str(cursor)})`);
2214
+ segs.push(`.stepToRef(${tsLiteral(cursor)})`);
2232
2215
  return segs;
2233
2216
  }
2234
2217
  const node = ctx.graph.byId.get(cursor);
@@ -2240,7 +2223,7 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2240
2223
  // terminates are excluded: they are not named steps, so there is nothing a ref
2241
2224
  // could resolve against, and each path emitting its own is already correct.
2242
2225
  if (ctx.emitted.has(cursor) && node.type !== T.end && node.type !== T.terminate) {
2243
- segs.push(`.stepToRef(${str(cursor)})`);
2226
+ segs.push(`.stepToRef(${tsLiteral(cursor)})`);
2244
2227
  return segs;
2245
2228
  }
2246
2229
  ctx.emitted.add(cursor);
@@ -2249,7 +2232,7 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2249
2232
  return segs;
2250
2233
  }
2251
2234
  if (node.type === T.terminate) {
2252
- segs.push(`.terminate(${str(node.id)}, undefined${optsArg(node, ctx)})`);
2235
+ segs.push(`.terminate(${tsLiteral(node.id)}, undefined${optsArg(node, ctx)})`);
2253
2236
  return segs;
2254
2237
  }
2255
2238
  if (node.type === T.merge) {
@@ -2265,10 +2248,10 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2265
2248
  const elseSegs = region(portTarget(ctx.graph, node.id, 'false'), r, scope, ipdom, ctx, refInto);
2266
2249
  const labels = armLabels(node);
2267
2250
  if (labels.trueLabel)
2268
- thenSegs.unshift(`.label(${str(labels.trueLabel)})`);
2251
+ thenSegs.unshift(`.label(${tsLiteral(labels.trueLabel)})`);
2269
2252
  if (labels.falseLabel)
2270
- elseSegs.unshift(`.label(${str(labels.falseLabel)})`);
2271
- segs.push(`.branch(${str(node.id)}, ${cond},\n${reindent(armCb('t', thenSegs), ' ')},\n${reindent(armCb('e', elseSegs), ' ')}${verArg(node)})`);
2253
+ elseSegs.unshift(`.label(${tsLiteral(labels.falseLabel)})`);
2254
+ segs.push(`.branch(${tsLiteral(node.id)}, ${cond},\n${reindent(armCb('t', thenSegs), ' ')},\n${reindent(armCb('e', elseSegs), ' ')}${verArg(node)})`);
2272
2255
  cursor = r === EXIT ? undefined : r;
2273
2256
  continue;
2274
2257
  }
@@ -2312,14 +2295,14 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2312
2295
  const cond = typeof condRaw === 'string' && condRaw.length
2313
2296
  ? emitBareExpr(condRaw.startsWith('=js:') ? condRaw.slice(4) : condRaw, ctx.imp, ctx.inputNames)
2314
2297
  : `${ctx.imp.use('lit')}(true)`;
2315
- segs.push(`.doWhile(${str(node.id)}, ${cond}, ${armCb('b', bodySegs)}${opts})`);
2298
+ segs.push(`.doWhile(${tsLiteral(node.id)}, ${cond}, ${armCb('b', bodySegs)}${opts})`);
2316
2299
  }
2317
2300
  else {
2318
2301
  const collRaw = unwrapSource(inputs.collection);
2319
2302
  const collection = collRaw !== undefined
2320
2303
  ? emitBareExpr(collRaw.startsWith('=js:') ? collRaw.slice(4) : collRaw, ctx.imp, ctx.inputNames)
2321
2304
  : `${ctx.imp.use('lit')}([])`;
2322
- segs.push(`.loop(${str(node.id)}, ${collection}, ${armCb('b', bodySegs)}${opts})`);
2305
+ segs.push(`.loop(${tsLiteral(node.id)}, ${collection}, ${armCb('b', bodySegs)}${opts})`);
2323
2306
  }
2324
2307
  // A container can carry an error handler too — `core.logic.loop` declares
2325
2308
  // the handle, and `.onError()` after `.loop(...)` authors it. Without this
@@ -2333,7 +2316,7 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2333
2316
  continue;
2334
2317
  }
2335
2318
  if (node.type === T.subflow) {
2336
- segs.push(`.step(${str(node.id)}, ${emitSubflow(node, ctx)}${optsArg(node, ctx)})`);
2319
+ segs.push(`.step(${tsLiteral(node.id)}, ${emitSubflow(node, ctx)}${optsArg(node, ctx)})`);
2337
2320
  cursor = scopeSucc(ctx.graph, node.id, scope)[0];
2338
2321
  continue;
2339
2322
  }
@@ -2351,14 +2334,14 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2351
2334
  const r = ipdom.get(node.id) ?? stop;
2352
2335
  const armSrcs = exitPorts.map(({ value, port }) => {
2353
2336
  const armSegs = region(portTarget(ctx.graph, node.id, port), r, scope, ipdom, ctx, refInto);
2354
- return `{ value: ${str(value)}, body: ${armCb('b', armSegs)} }`;
2337
+ return `{ value: ${tsLiteral(value)}, body: ${armCb('b', armSegs)} }`;
2355
2338
  });
2356
2339
  const armList = `[\n${armSrcs.map((a) => reindent(a, ' ')).join(',\n')},\n]`;
2357
- segs.push(`.stepSwitch(${str(node.id)}, ${spec}, ${armList}${optsArg(node, ctx)})`);
2340
+ segs.push(`.stepSwitch(${tsLiteral(node.id)}, ${spec}, ${armList}${optsArg(node, ctx)})`);
2358
2341
  cursor = r === EXIT ? undefined : r;
2359
2342
  continue;
2360
2343
  }
2361
- segs.push(`.step(${str(node.id)}, ${spec}${optsArg(node, ctx)})`);
2344
+ segs.push(`.step(${tsLiteral(node.id)}, ${spec}${optsArg(node, ctx)})`);
2362
2345
  // An error handler on this step (source port `error` + `errorHandlingEnabled`).
2363
2346
  const succ = scopeSucc(ctx.graph, node.id, scope);
2364
2347
  const handler = errorHandlerSeg(node.id, succ, scope, ipdom, ctx);
@@ -2372,7 +2355,7 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2372
2355
  const branchSegs = region(be.targetNodeId, EXIT, scope, ipdom, ctx, mainReach);
2373
2356
  if (branchSegs.length === 0)
2374
2357
  branchSegs.push('.return({})');
2375
- segs.push(`.stepToList(${str(be.sourcePort)}, ${armCb('p', branchSegs)})`);
2358
+ segs.push(`.stepToList(${tsLiteral(be.sourcePort)}, ${armCb('p', branchSegs)})`);
2376
2359
  }
2377
2360
  // A per-outcome human task routes each NON-primary outcome from its own
2378
2361
  // `outcome-<slug>` exit; the primary one continues the main path, so it is
@@ -2387,7 +2370,7 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2387
2370
  const outSegs = region(oe.targetNodeId, EXIT, scope, ipdom, ctx, mainReach);
2388
2371
  if (outSegs.length === 0)
2389
2372
  outSegs.push('.return({})');
2390
- segs.push(`.stepToList(${str(oe.sourcePort)}, ${armCb('p', outSegs)})`);
2373
+ segs.push(`.stepToList(${tsLiteral(oe.sourcePort)}, ${armCb('p', outSegs)})`);
2391
2374
  }
2392
2375
  }
2393
2376
  // A body edge into the enclosing container's break handle: the path exits
@@ -2409,7 +2392,7 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2409
2392
  const join = ipdom.get(node.id);
2410
2393
  if (join && join !== EXIT && ctx.graph.byId.get(join)?.type === T.merge) {
2411
2394
  const arms = succ.map((t, i) => armCb(`a${i}`, region(t, join, scope, ipdom, ctx, refInto)));
2412
- segs.push(`.parallel(${str(join)}, [\n${arms.map((a) => reindent(a, ' ')).join(',\n')},\n]${verArg(ctx.graph.byId.get(join))})`);
2395
+ segs.push(`.parallel(${tsLiteral(join)}, [\n${arms.map((a) => reindent(a, ' ')).join(',\n')},\n]${verArg(ctx.graph.byId.get(join))})`);
2413
2396
  cursor = portTarget(ctx.graph, join, 'output');
2414
2397
  continue;
2415
2398
  }
@@ -2533,12 +2516,12 @@ function emitSwitch(node, ipdom, scope, ctx, stop, refInto) {
2533
2516
  if (m) {
2534
2517
  valueLiteral = m[2].trim();
2535
2518
  try {
2536
- value = literal(JSON.parse(valueLiteral));
2519
+ value = tsLiteral(JSON.parse(valueLiteral));
2537
2520
  }
2538
2521
  catch {
2539
2522
  // A non-JSON literal (e.g. single-quoted) — strip matching quotes if any.
2540
2523
  const q = valueLiteral.match(/^'(.*)'$/);
2541
- value = q ? str(q[1]) : str(valueLiteral);
2524
+ value = q ? tsLiteral(q[1]) : tsLiteral(valueLiteral);
2542
2525
  }
2543
2526
  }
2544
2527
  else {
@@ -2546,7 +2529,7 @@ function emitSwitch(node, ipdom, scope, ctx, stop, refInto) {
2546
2529
  }
2547
2530
  const armSegs = region(portTarget(ctx.graph, node.id, `case-${c.id}`), r, scope, ipdom, ctx, refInto);
2548
2531
  const defaultLabel = `Case ${valueLiteral}`;
2549
- const labelPart = c.label && c.label !== defaultLabel ? `, label: ${str(c.label)}` : '';
2532
+ const labelPart = c.label && c.label !== defaultLabel ? `, label: ${tsLiteral(c.label)}` : '';
2550
2533
  caseSrcs.push(`{ value: ${value}${labelPart}, body: ${armCb('b', armSegs)} }`);
2551
2534
  }
2552
2535
  const on = emitBareExpr(disc ?? '', ctx.imp, ctx.inputNames);
@@ -2563,7 +2546,7 @@ function emitSwitch(node, ipdom, scope, ctx, stop, refInto) {
2563
2546
  ? `\n/* TODO: this switch's case expressions are not a uniform \`<discriminant> === <value>\`; the recovered on/value are best-effort and need review. */`
2564
2547
  : '';
2565
2548
  const ver = verArg(node);
2566
- return `.switch(${str(node.id)}, ${on}, ${caseList}${defArm}${ver && !defArm ? ', undefined' : ''}${ver})${warn}`;
2549
+ return `.switch(${tsLiteral(node.id)}, ${on}, ${caseList}${defArm}${ver && !defArm ? ', undefined' : ''}${ver})${warn}`;
2567
2550
  }
2568
2551
  /** The manifest-less annotation node type — the only annotation `.flow` has. */
2569
2552
  const STICKY_NOTE_TYPE = 'stickyNote';
@@ -2587,14 +2570,14 @@ function stickyNotesSeg(nodes, flow) {
2587
2570
  const ui = { ...(n.ui ?? {}), ...(hoisted[n.id] ?? {}) };
2588
2571
  const pos = ui.position ?? { x: 0, y: 0 };
2589
2572
  const fields = [
2590
- `id: ${str(n.id)}`,
2591
- `content: ${str(typeof i.content === 'string' ? i.content : '')}`,
2573
+ `id: ${tsLiteral(n.id)}`,
2574
+ `content: ${tsLiteral(typeof i.content === 'string' ? i.content : '')}`,
2592
2575
  ];
2593
2576
  if (typeof i.color === 'string' && i.color !== 'yellow')
2594
- fields.push(`color: ${str(i.color)}`);
2595
- fields.push(`x: ${numLit(pos.x)}`, `y: ${numLit(pos.y)}`);
2577
+ fields.push(`color: ${tsLiteral(i.color)}`);
2578
+ fields.push(`x: ${tsNumber(pos.x)}`, `y: ${tsNumber(pos.y)}`);
2596
2579
  if (ui.size && typeof ui.size.width === 'number' && typeof ui.size.height === 'number') {
2597
- fields.push(`width: ${numLit(ui.size.width)}`, `height: ${numLit(ui.size.height)}`);
2580
+ fields.push(`width: ${tsNumber(ui.size.width)}`, `height: ${tsNumber(ui.size.height)}`);
2598
2581
  }
2599
2582
  return ` { ${fields.join(', ')} },`;
2600
2583
  });
@@ -2644,9 +2627,9 @@ function layoutSeg(flow, nodes, edges) {
2644
2627
  const pos = ui.position;
2645
2628
  if (!pos || typeof pos.x !== 'number' || typeof pos.y !== 'number')
2646
2629
  continue;
2647
- const fields = [`position: { x: ${numLit(pos.x)}, y: ${numLit(pos.y)} }`];
2630
+ const fields = [`position: { x: ${tsNumber(pos.x)}, y: ${tsNumber(pos.y)} }`];
2648
2631
  if (ui.size && typeof ui.size.width === 'number' && typeof ui.size.height === 'number') {
2649
- fields.push(`size: { width: ${numLit(ui.size.width)}, height: ${numLit(ui.size.height)} }`);
2632
+ fields.push(`size: { width: ${tsNumber(ui.size.width)}, height: ${tsNumber(ui.size.height)} }`);
2650
2633
  }
2651
2634
  if (typeof ui.collapsed === 'boolean')
2652
2635
  fields.push(`collapsed: ${boolLit(ui.collapsed)}`);
@@ -2670,7 +2653,7 @@ function layoutSeg(flow, nodes, edges) {
2670
2653
  continue;
2671
2654
  const rendered = pts
2672
2655
  .filter((p) => p && typeof p.x === 'number' && typeof p.y === 'number')
2673
- .map((p) => `{ x: ${numLit(p.x)}, y: ${numLit(p.y)}${typeof p.id === 'string' ? `, id: ${str(p.id)}` : ''} }`);
2656
+ .map((p) => `{ x: ${tsNumber(p.x)}, y: ${tsNumber(p.y)}${typeof p.id === 'string' ? `, id: ${tsLiteral(p.id)}` : ''} }`);
2674
2657
  if (rendered.length)
2675
2658
  parts.push(`${which}: [${rendered.join(', ')}]`);
2676
2659
  }
@@ -2679,7 +2662,7 @@ function layoutSeg(flow, nodes, edges) {
2679
2662
  const pair = `${e.sourceNodeId}->${e.targetNodeId}`;
2680
2663
  if ((pairCount.get(pair) ?? 0) !== 1)
2681
2664
  continue; // ambiguous — see the note above
2682
- edgeEntries.push(`${str(pair)}: { ${parts.join(', ')} }`);
2665
+ edgeEntries.push(`${tsLiteral(pair)}: { ${parts.join(', ')} }`);
2683
2666
  }
2684
2667
  if (!nodeEntries.length && !edgeEntries.length)
2685
2668
  return undefined;
@@ -2724,7 +2707,7 @@ function emitChildFlow(name, child, ctx) {
2724
2707
  emitted: new Set(),
2725
2708
  o: ctx.o,
2726
2709
  };
2727
- const parts = [`${ctx.imp.use('flow')}(${str(name)})`];
2710
+ const parts = [`${ctx.imp.use('flow')}(${tsLiteral(name)})`];
2728
2711
  // Variables.
2729
2712
  const shape = (dir) => `{ ${globals.filter((g) => g.direction === dir).map((g) => `${key(g.name ?? g.id)}: ${declExpr(g, dir !== 'inout', ctx.imp)}`).join(', ')} }`;
2730
2713
  if (globals.some((g) => g.direction === 'in'))
@@ -2732,8 +2715,8 @@ function emitChildFlow(name, child, ctx) {
2732
2715
  if (globals.some((g) => g.direction === 'out'))
2733
2716
  parts.push(`.output(${shape('out')})`);
2734
2717
  for (const g of globals.filter((g) => g.direction === 'inout')) {
2735
- const def = g.defaultValue !== undefined ? `, ${literal(g.defaultValue)}` : '';
2736
- parts.push(`.var(${str(g.name ?? g.id)}, ${declExpr(g, false, ctx.imp)}${def})`);
2718
+ const def = g.defaultValue !== undefined ? `, ${tsLiteral(g.defaultValue)}` : '';
2719
+ parts.push(`.var(${tsLiteral(g.name ?? g.id)}, ${declExpr(g, false, ctx.imp)}${def})`);
2737
2720
  }
2738
2721
  // Body: the child's trigger is its own manual Start; walk from its successor.
2739
2722
  const trigger = (child.nodes ?? []).find((n) => isTrigger(n.type) && !(child.edges ?? []).some((e) => e.targetNodeId === n.id));
@@ -2787,7 +2770,7 @@ export function decompile(flow, options = {}) {
2787
2770
  (flow.nodes ?? []).find((n) => String(n.type ?? '').startsWith('core.trigger.'))?.id ??
2788
2771
  DEFAULT_TRIGGER_ID;
2789
2772
  const e = new Emit();
2790
- e.line(0, `export default ${imp.use('flow')}(${str(slugId(flow))})`);
2773
+ e.line(0, `export default ${imp.use('flow')}(${tsLiteral(slugId(flow))})`);
2791
2774
  const d = 1;
2792
2775
  // The file-format version FIRST, mirroring the order the file itself carries
2793
2776
  // it (`id`, `version`, `name`) — and emitted even when it equals this SDK's
@@ -2805,9 +2788,9 @@ export function decompile(flow, options = {}) {
2805
2788
  e.line(d, `// TODO: ${commentText(schema.problem)}`);
2806
2789
  }
2807
2790
  if (flow.name !== undefined && flow.name !== flow.id)
2808
- e.line(d, `.name(${str(flow.name)})`);
2791
+ e.line(d, `.name(${tsLiteral(flow.name)})`);
2809
2792
  if (typeof flow.description === 'string') {
2810
- e.line(d, `.description(${str(flow.description)})`);
2793
+ e.line(d, `.description(${tsLiteral(flow.description)})`);
2811
2794
  }
2812
2795
  // Trigger nodes. One root is the default: the isDefaultEntryPoint carrier
2813
2796
  // when the file marks one (a multi-root flow always does), else `start`.
@@ -2830,7 +2813,7 @@ export function decompile(flow, options = {}) {
2830
2813
  const detached = detachedTopLevelNodes(flow.nodes ?? [], graph, triggerNodes, trigger);
2831
2814
  const detachedIds = new Set(detached.map((n) => n.id));
2832
2815
  if (detached.length > 0) {
2833
- const listing = detached.map((n) => `${str(n.id)} (${n.type})`).join(', ');
2816
+ const listing = detached.map((n) => `${tsLiteral(n.id)} (${n.type})`).join(', ');
2834
2817
  const message = `${detached.length} node(s) are not reachable from any trigger — no edge leads to them: ${listing}. `
2835
2818
  + 'The builder has no detached-node construct, so this source omits them and recompiling drops them. '
2836
2819
  + 'Connect each one with an edge in the .flow first, or edit that node\'s JSON directly.';
@@ -2838,14 +2821,14 @@ export function decompile(flow, options = {}) {
2838
2821
  throw new Error(`decompile: ${message}`);
2839
2822
  warnOnce(message);
2840
2823
  for (const n of detached) {
2841
- e.line(d, `// TODO: node ${commentText(str(n.id))} (${commentText(n.type)}) is not reachable from any trigger and is NOT in this source; recompiling drops it. Connect it in the .flow or edit its JSON directly.`);
2824
+ e.line(d, `// TODO: node ${commentText(tsLiteral(n.id))} (${commentText(n.type)}) is not reachable from any trigger and is NOT in this source; recompiling drops it. Connect it in the .flow or edit its JSON directly.`);
2842
2825
  }
2843
2826
  }
2844
2827
  // `{ shared }` input copies on the roots (#924). Any other trigger-level
2845
2828
  // update has no builder spelling: say so instead of dropping it.
2846
2829
  const shared = sharedCopies(flow, triggerNodes);
2847
2830
  if (shared.unrecognized.length) {
2848
- const listing = shared.unrecognized.map((u) => `${str(u.variableId)} on ${str(u.trigger)}`).join(', ');
2831
+ const listing = shared.unrecognized.map((u) => `${tsLiteral(u.variableId)} on ${tsLiteral(u.trigger)}`).join(', ');
2849
2832
  const message = `${shared.unrecognized.length} variable update(s) on a trigger node are not a { shared } input copy: ${listing}. `
2850
2833
  + 'The builder writes a trigger-level update only for { type, shared: \'<var>\' } (the row `<var> ← $vars.<trigger>.output.<input>`), '
2851
2834
  + 'so this source omits them and recompiling drops them.';
@@ -2946,5 +2929,5 @@ function entryPointSeg(t, scope, ipdom, ctx) {
2946
2929
  const prefixSegs = first !== undefined ? region(first, EXIT, scope, ipdom, ctx) : [];
2947
2930
  const optArgText = optParts.length ? `, { ${optParts.join(', ')} }` : (prefixSegs.length ? ', undefined' : '');
2948
2931
  const prefix = prefixSegs.length ? `, ${armCb('b', prefixSegs)}` : '';
2949
- return `.entryPoint(${str(t.id)}, ${factory}${optArgText}${prefix})`;
2932
+ return `.entryPoint(${tsLiteral(t.id)}, ${factory}${optArgText}${prefix})`;
2950
2933
  }