@flow-as-code/core 0.1.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 (164) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +30 -0
  3. package/SPEC.md +92 -0
  4. package/dist/actions.d.ts +87 -0
  5. package/dist/actions.d.ts.map +1 -0
  6. package/dist/actions.js +204 -0
  7. package/dist/actions.js.map +1 -0
  8. package/dist/aws.d.ts +27 -0
  9. package/dist/aws.d.ts.map +1 -0
  10. package/dist/aws.js +32 -0
  11. package/dist/aws.js.map +1 -0
  12. package/dist/blocks.d.ts +287 -0
  13. package/dist/blocks.d.ts.map +1 -0
  14. package/dist/blocks.js +352 -0
  15. package/dist/blocks.js.map +1 -0
  16. package/dist/codegen.d.ts +21 -0
  17. package/dist/codegen.d.ts.map +1 -0
  18. package/dist/codegen.js +1027 -0
  19. package/dist/codegen.js.map +1 -0
  20. package/dist/export.d.ts +256 -0
  21. package/dist/export.d.ts.map +1 -0
  22. package/dist/export.js +799 -0
  23. package/dist/export.js.map +1 -0
  24. package/dist/flow.d.ts +41 -0
  25. package/dist/flow.d.ts.map +1 -0
  26. package/dist/flow.js +85 -0
  27. package/dist/flow.js.map +1 -0
  28. package/dist/flowdoc.d.ts +111 -0
  29. package/dist/flowdoc.d.ts.map +1 -0
  30. package/dist/flowdoc.js +139 -0
  31. package/dist/flowdoc.js.map +1 -0
  32. package/dist/index.d.ts +16 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +26 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/layout.d.ts +11 -0
  37. package/dist/layout.d.ts.map +1 -0
  38. package/dist/layout.js +49 -0
  39. package/dist/layout.js.map +1 -0
  40. package/dist/lint/engine.d.ts +16 -0
  41. package/dist/lint/engine.d.ts.map +1 -0
  42. package/dist/lint/engine.js +37 -0
  43. package/dist/lint/engine.js.map +1 -0
  44. package/dist/lint/graph.d.ts +34 -0
  45. package/dist/lint/graph.d.ts.map +1 -0
  46. package/dist/lint/graph.js +79 -0
  47. package/dist/lint/graph.js.map +1 -0
  48. package/dist/lint/index.d.ts +7 -0
  49. package/dist/lint/index.d.ts.map +1 -0
  50. package/dist/lint/index.js +5 -0
  51. package/dist/lint/index.js.map +1 -0
  52. package/dist/lint/reporters.d.ts +6 -0
  53. package/dist/lint/reporters.d.ts.map +1 -0
  54. package/dist/lint/reporters.js +30 -0
  55. package/dist/lint/reporters.js.map +1 -0
  56. package/dist/lint/rules/action-allowed-in-flow-type.d.ts +11 -0
  57. package/dist/lint/rules/action-allowed-in-flow-type.d.ts.map +1 -0
  58. package/dist/lint/rules/action-allowed-in-flow-type.js +32 -0
  59. package/dist/lint/rules/action-allowed-in-flow-type.js.map +1 -0
  60. package/dist/lint/rules/action-count.d.ts +16 -0
  61. package/dist/lint/rules/action-count.d.ts.map +1 -0
  62. package/dist/lint/rules/action-count.js +32 -0
  63. package/dist/lint/rules/action-count.js.map +1 -0
  64. package/dist/lint/rules/error-branches.d.ts +10 -0
  65. package/dist/lint/rules/error-branches.d.ts.map +1 -0
  66. package/dist/lint/rules/error-branches.js +39 -0
  67. package/dist/lint/rules/error-branches.js.map +1 -0
  68. package/dist/lint/rules/index.d.ts +17 -0
  69. package/dist/lint/rules/index.d.ts.map +1 -0
  70. package/dist/lint/rules/index.js +36 -0
  71. package/dist/lint/rules/index.js.map +1 -0
  72. package/dist/lint/rules/module-depth-5.d.ts +14 -0
  73. package/dist/lint/rules/module-depth-5.d.ts.map +1 -0
  74. package/dist/lint/rules/module-depth-5.js +67 -0
  75. package/dist/lint/rules/module-depth-5.js.map +1 -0
  76. package/dist/lint/rules/no-literal-arn.d.ts +21 -0
  77. package/dist/lint/rules/no-literal-arn.d.ts.map +1 -0
  78. package/dist/lint/rules/no-literal-arn.js +58 -0
  79. package/dist/lint/rules/no-literal-arn.js.map +1 -0
  80. package/dist/lint/rules/no-unresolved-token.d.ts +8 -0
  81. package/dist/lint/rules/no-unresolved-token.d.ts.map +1 -0
  82. package/dist/lint/rules/no-unresolved-token.js +71 -0
  83. package/dist/lint/rules/no-unresolved-token.js.map +1 -0
  84. package/dist/lint/rules/prompt-length-3000.d.ts +16 -0
  85. package/dist/lint/rules/prompt-length-3000.d.ts.map +1 -0
  86. package/dist/lint/rules/prompt-length-3000.js +61 -0
  87. package/dist/lint/rules/prompt-length-3000.js.map +1 -0
  88. package/dist/lint/rules/reachable-blocks.d.ts +7 -0
  89. package/dist/lint/rules/reachable-blocks.d.ts.map +1 -0
  90. package/dist/lint/rules/reachable-blocks.js +45 -0
  91. package/dist/lint/rules/reachable-blocks.js.map +1 -0
  92. package/dist/lint/rules/recording-consent-before-record.d.ts +3 -0
  93. package/dist/lint/rules/recording-consent-before-record.d.ts.map +1 -0
  94. package/dist/lint/rules/recording-consent-before-record.js +87 -0
  95. package/dist/lint/rules/recording-consent-before-record.js.map +1 -0
  96. package/dist/lint/rules/terminal-blocks.d.ts +8 -0
  97. package/dist/lint/rules/terminal-blocks.d.ts.map +1 -0
  98. package/dist/lint/rules/terminal-blocks.js +40 -0
  99. package/dist/lint/rules/terminal-blocks.js.map +1 -0
  100. package/dist/lint/rules/unique-names.d.ts +7 -0
  101. package/dist/lint/rules/unique-names.d.ts.map +1 -0
  102. package/dist/lint/rules/unique-names.js +40 -0
  103. package/dist/lint/rules/unique-names.js.map +1 -0
  104. package/dist/lint/types.d.ts +30 -0
  105. package/dist/lint/types.d.ts.map +1 -0
  106. package/dist/lint/types.js +11 -0
  107. package/dist/lint/types.js.map +1 -0
  108. package/dist/materialize.d.ts +31 -0
  109. package/dist/materialize.d.ts.map +1 -0
  110. package/dist/materialize.js +192 -0
  111. package/dist/materialize.js.map +1 -0
  112. package/dist/package-names.d.ts +11 -0
  113. package/dist/package-names.d.ts.map +1 -0
  114. package/dist/package-names.js +25 -0
  115. package/dist/package-names.js.map +1 -0
  116. package/dist/refs.d.ts +35 -0
  117. package/dist/refs.d.ts.map +1 -0
  118. package/dist/refs.js +69 -0
  119. package/dist/refs.js.map +1 -0
  120. package/dist/serialize.d.ts +13 -0
  121. package/dist/serialize.d.ts.map +1 -0
  122. package/dist/serialize.js +86 -0
  123. package/dist/serialize.js.map +1 -0
  124. package/dist/simulate.d.ts +446 -0
  125. package/dist/simulate.d.ts.map +1 -0
  126. package/dist/simulate.js +1115 -0
  127. package/dist/simulate.js.map +1 -0
  128. package/dist/synth.d.ts +25 -0
  129. package/dist/synth.d.ts.map +1 -0
  130. package/dist/synth.js +93 -0
  131. package/dist/synth.js.map +1 -0
  132. package/package.json +73 -0
  133. package/src/actions.ts +221 -0
  134. package/src/aws.ts +55 -0
  135. package/src/blocks.ts +619 -0
  136. package/src/codegen.ts +1144 -0
  137. package/src/export.ts +1190 -0
  138. package/src/flow.ts +108 -0
  139. package/src/flowdoc.ts +261 -0
  140. package/src/index.ts +31 -0
  141. package/src/layout.ts +54 -0
  142. package/src/lint/engine.ts +56 -0
  143. package/src/lint/graph.ts +86 -0
  144. package/src/lint/index.ts +10 -0
  145. package/src/lint/reporters.ts +44 -0
  146. package/src/lint/rules/action-allowed-in-flow-type.ts +32 -0
  147. package/src/lint/rules/action-count.ts +35 -0
  148. package/src/lint/rules/error-branches.ts +45 -0
  149. package/src/lint/rules/index.ts +53 -0
  150. package/src/lint/rules/module-depth-5.ts +70 -0
  151. package/src/lint/rules/no-literal-arn.ts +64 -0
  152. package/src/lint/rules/no-unresolved-token.ts +80 -0
  153. package/src/lint/rules/prompt-length-3000.ts +66 -0
  154. package/src/lint/rules/reachable-blocks.ts +50 -0
  155. package/src/lint/rules/recording-consent-before-record.ts +90 -0
  156. package/src/lint/rules/terminal-blocks.ts +45 -0
  157. package/src/lint/rules/unique-names.ts +44 -0
  158. package/src/lint/types.ts +43 -0
  159. package/src/materialize.ts +230 -0
  160. package/src/package-names.ts +26 -0
  161. package/src/refs.ts +93 -0
  162. package/src/serialize.ts +97 -0
  163. package/src/simulate.ts +1610 -0
  164. package/src/synth.ts +110 -0
@@ -0,0 +1,192 @@
1
+ /*
2
+ * Copyright 2026 The flow-as-code Authors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ // Materialization: FlowDoc in, deployable Flow language content out.
6
+ //
7
+ // Two backends resolve `${cdref:...}` tokens (SPEC.md, Materialization):
8
+ // - materializeWithMap: strict lookup in a token -> value map; every missing
9
+ // token is reported at once in a MaterializeError.
10
+ // - materializeWithBinder: the binder returns opaque strings (CDK or
11
+ // CloudFormation tokens). They pass through byte-for-byte with no
12
+ // validation and no arn-shape checks, because intrinsics are resolved by
13
+ // CloudFormation long after this code runs.
14
+ //
15
+ // Both backends emit content ONLY: `layout`, `refs`, and `meta` are tool
16
+ // metadata and are dropped. Before dropping, `layout` is projected into
17
+ // `content.Metadata` so the flow lays out correctly in the Connect console
18
+ // (docs/01-flowdoc-spec.md). The Metadata shape follows the Flow language
19
+ // example, which uses EntryPointPosition and ActionMetadata.<id>.Position:
20
+ // https://docs.aws.amazon.com/connect/latest/devguide/flow-language-example.html
21
+ import { InvalidFlowDocError, assertFlowDoc } from "./flowdoc.js";
22
+ import { autoLayout } from "./layout.js";
23
+ import { collectRefs, parseToken } from "./refs.js";
24
+ import { canonicalAction, ordered, sortKeys } from "./serialize.js";
25
+ /**
26
+ * Strict map materialization failed: one or more tokens in the document have
27
+ * no entry in the resource map. `missingTokens` is sorted and complete, so a
28
+ * caller fixes the map once, not one missing key per run.
29
+ */
30
+ export class MaterializeError extends Error {
31
+ missingTokens;
32
+ constructor(missingTokens, reason) {
33
+ super(reason === undefined
34
+ ? `Cannot materialize: ${String(missingTokens.length)} unmapped token(s): ` +
35
+ missingTokens.join(", ")
36
+ : `Cannot materialize: ${reason} Offending token(s): ${missingTokens.join(", ")}`);
37
+ this.name = "MaterializeError";
38
+ this.missingTokens = missingTokens;
39
+ }
40
+ }
41
+ /**
42
+ * A reference token occupies an entire field value and is never interpolated
43
+ * into a longer string (FlowDoc invariant 4), so replacement only ever swaps
44
+ * whole string values. Binder output is inserted exactly as returned.
45
+ */
46
+ function resolveDeep(value, resolve) {
47
+ if (typeof value === "string") {
48
+ const entry = parseToken(value);
49
+ return entry === undefined ? value : resolve(entry);
50
+ }
51
+ if (Array.isArray(value))
52
+ return value.map((v) => resolveDeep(v, resolve));
53
+ if (value !== null && typeof value === "object") {
54
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [
55
+ k,
56
+ resolveDeep(v, resolve),
57
+ ]));
58
+ }
59
+ return value;
60
+ }
61
+ /** How far left of the StartAction block the console entry point marker sits. */
62
+ const ENTRY_POINT_OFFSET_X = 100;
63
+ /**
64
+ * Projects `layout` into the Metadata shape the console reads. Positions come
65
+ * from the document's layout; any action without one gets the same
66
+ * deterministic auto-layout synth would have assigned. Keys an imported
67
+ * document may already carry in content.Metadata are preserved; only the
68
+ * position keys are overwritten, because `layout` is the single source of
69
+ * truth for position (docs/01-flowdoc-spec.md).
70
+ */
71
+ function projectLayout(doc, resolve) {
72
+ const actions = doc.content.Actions;
73
+ const layout = doc.layout ?? {};
74
+ const missing = actions.some((a) => layout[a.Identifier] === undefined);
75
+ const auto = missing ? autoLayout(actions) : {};
76
+ // Pre-existing Metadata (an imported document, say) is carried through, and
77
+ // it is RESOLVED like everything else: the strictness check scans the whole
78
+ // content, so substitution must cover the whole content too, or a token
79
+ // could survive into deployable output.
80
+ const existing = resolveDeep(doc.content.Metadata ?? {}, resolve);
81
+ const existingRaw = existing.ActionMetadata;
82
+ const existingActionMetadata = existingRaw !== null && typeof existingRaw === "object" && !Array.isArray(existingRaw)
83
+ ? existingRaw
84
+ : {};
85
+ // Start from what was already there (entries for unknown ids survive; never
86
+ // drop content), then overlay Position per action: layout is the single
87
+ // source of truth for position, but sibling keys inside an entry are kept.
88
+ const actionMetadata = { ...existingActionMetadata };
89
+ const positions = {};
90
+ for (const a of actions) {
91
+ const p = layout[a.Identifier] ?? auto[a.Identifier] ?? { x: 0, y: 0 };
92
+ positions[a.Identifier] = p;
93
+ const prior = actionMetadata[a.Identifier];
94
+ const priorEntry = prior !== null && typeof prior === "object" && !Array.isArray(prior)
95
+ ? prior
96
+ : {};
97
+ actionMetadata[a.Identifier] = { ...priorEntry, Position: { x: p.x, y: p.y } };
98
+ }
99
+ // The example places the entry point marker left of the first block
100
+ // (EntryPointPosition x=88 vs first Action x=270).
101
+ const start = positions[doc.content.StartAction] ?? { x: 0, y: 0 };
102
+ const entryPointPosition = { x: Math.max(start.x - ENTRY_POINT_OFFSET_X, 0), y: start.y };
103
+ return {
104
+ ...existing,
105
+ EntryPointPosition: entryPointPosition,
106
+ ActionMetadata: actionMetadata,
107
+ };
108
+ }
109
+ function materialize(doc, resolve) {
110
+ // A module requires a top-level Settings in its deployable content; Connect
111
+ // rejects the create without it. Default to {} when the doc does not carry
112
+ // one; resolve tokens in it for consistency with the rest of the content.
113
+ const settings = doc.kind === "module" || doc.content.Settings !== undefined
114
+ ? resolveDeep(doc.content.Settings ?? {}, resolve)
115
+ : undefined;
116
+ return {
117
+ Version: doc.content.Version,
118
+ StartAction: doc.content.StartAction,
119
+ ...(settings === undefined ? {} : { Settings: settings }),
120
+ Metadata: projectLayout(doc, resolve),
121
+ Actions: doc.content.Actions.map((a) => resolveDeep(a, resolve)),
122
+ };
123
+ }
124
+ /**
125
+ * Strict materialization against a token -> value map. Every token in the
126
+ * content must have an entry; otherwise a MaterializeError lists ALL missing
127
+ * tokens (sorted), not just the first. Mapped values are not validated: at
128
+ * this point literal ARNs are the goal, not a mistake.
129
+ */
130
+ export function materializeWithMap(doc, resourceMap) {
131
+ assertFlowDoc(doc, "materializeWithMap");
132
+ if (typeof resourceMap !== "object" || resourceMap === null || Array.isArray(resourceMap)) {
133
+ throw new InvalidFlowDocError("materializeWithMap expects a token to value map object as its second argument.");
134
+ }
135
+ const missing = collectRefs(doc.content)
136
+ .map((e) => e.token)
137
+ .filter((t) => !Object.hasOwn(resourceMap, t));
138
+ if (missing.length > 0)
139
+ throw new MaterializeError(missing);
140
+ const content = materialize(doc, (entry) => resourceMap[entry.token]);
141
+ // Completeness is not enough. collectRefs finds a token anywhere in a string
142
+ // while substitution only replaces a whole-value token, so an interpolated
143
+ // token used to satisfy the check above and then survive into deployable
144
+ // output as literal text that Connect would read aloud. Refusing here means
145
+ // render and emit, which do not run lint, cannot ship one either.
146
+ const leaked = [...new Set(JSON.stringify(content).match(/\$\{cdref:[^}"]*\}/g) ?? [])].sort();
147
+ if (leaked.length > 0) {
148
+ throw new MaterializeError(leaked, "Token(s) survived materialization because they are embedded in a longer string. " +
149
+ "A reference must be the entire field value (FlowDoc invariant 4).");
150
+ }
151
+ return content;
152
+ }
153
+ /**
154
+ * Binder materialization for IaC backends (@flow-as-code/cdk). The binder returns an
155
+ * opaque string per reference, typically a CDK token that CloudFormation later
156
+ * resolves to an ARN. Output passes through exactly as returned, with no
157
+ * validation and no arn-shape lint (SPEC.md, Materialization).
158
+ */
159
+ export function materializeWithBinder(doc, binder) {
160
+ assertFlowDoc(doc, "materializeWithBinder");
161
+ if (typeof binder !== "function") {
162
+ throw new InvalidFlowDocError("materializeWithBinder expects a binder function as its second argument.");
163
+ }
164
+ return materialize(doc, binder);
165
+ }
166
+ // --- Deterministic serialization of deployable content -----------------------
167
+ // Mirrors serialize.ts: fixed first-class key order, alphabetical for the
168
+ // rest, two-space indent, trailing newline. Byte-identical for the same
169
+ // content regardless of insertion order.
170
+ /**
171
+ * Canonical JSON text of deployable content, two-space indented, newline
172
+ * terminated. Metadata leads with EntryPointPosition then ActionMetadata,
173
+ * matching the Flow language example; everything nested is key-sorted.
174
+ */
175
+ export function serializeContent(content) {
176
+ const canonical = ordered({
177
+ Version: content.Version,
178
+ StartAction: content.StartAction,
179
+ ...(content.Settings === undefined ? {} : { Settings: sortKeys(content.Settings) }),
180
+ ...(content.Metadata === undefined
181
+ ? {}
182
+ : {
183
+ Metadata: ordered(sortKeys(content.Metadata), [
184
+ "EntryPointPosition",
185
+ "ActionMetadata",
186
+ ]),
187
+ }),
188
+ Actions: content.Actions.map(canonicalAction),
189
+ }, ["Version", "StartAction", "Settings", "Metadata", "Actions"]);
190
+ return JSON.stringify(canonical, null, 2) + "\n";
191
+ }
192
+ //# sourceMappingURL=materialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materialize.js","sourceRoot":"","sources":["../src/materialize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qEAAqE;AACrE,EAAE;AACF,yEAAyE;AACzE,+EAA+E;AAC/E,uDAAuD;AACvD,uEAAuE;AACvE,sEAAsE;AACtE,6EAA6E;AAC7E,gDAAgD;AAChD,EAAE;AACF,yEAAyE;AACzE,wEAAwE;AACxE,2EAA2E;AAC3E,0EAA0E;AAC1E,2EAA2E;AAC3E,iFAAiF;AAGjF,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEpE;;;;GAIG;AACH,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,aAAa,CAAoB;IAE1C,YAAY,aAAgC,EAAE,MAAe;QAC3D,KAAK,CACH,MAAM,KAAK,SAAS;YAClB,CAAC,CAAC,uBAAuB,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,sBAAsB;gBACvE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;YAC5B,CAAC,CAAC,uBAAuB,MAAM,wBAAwB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACpF,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACrC,CAAC;CACF;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,KAAc,EAAE,OAAoC;IACvE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAChC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3E,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;YAC/D,CAAC;YACD,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC;SACxB,CAAC,CACH,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,iFAAiF;AACjF,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC;;;;;;;GAOG;AACH,SAAS,aAAa,CACpB,GAAY,EACZ,OAAoC;IAEpC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;IACpC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;IAChC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,SAAS,CAAC,CAAC;IACxE,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEhD,4EAA4E;IAC5E,4EAA4E;IAC5E,wEAAwE;IACxE,wCAAwC;IACxC,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,EAAE,OAAO,CAA4B,CAAC;IAC7F,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC;IAC5C,MAAM,sBAAsB,GAC1B,WAAW,KAAK,IAAI,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;QACpF,CAAC,CAAE,WAAuC;QAC1C,CAAC,CAAC,EAAE,CAAC;IAET,4EAA4E;IAC5E,wEAAwE;IACxE,2EAA2E;IAC3E,MAAM,cAAc,GAA4B,EAAE,GAAG,sBAAsB,EAAE,CAAC;IAC9E,MAAM,SAAS,GAA0B,EAAE,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;QACvE,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QAC3C,MAAM,UAAU,GACd,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAClE,CAAC,CAAE,KAAiC;YACpC,CAAC,CAAC,EAAE,CAAC;QACT,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,UAAU,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACjF,CAAC;IAED,oEAAoE;IACpE,mDAAmD;IACnD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;IACnE,MAAM,kBAAkB,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,oBAAoB,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;IAE1F,OAAO;QACL,GAAG,QAAQ;QACX,kBAAkB,EAAE,kBAAkB;QACtC,cAAc,EAAE,cAAc;KAC/B,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,GAAY,EAAE,OAAoC;IACrE,4EAA4E;IAC5E,2EAA2E;IAC3E,0EAA0E;IAC1E,MAAM,QAAQ,GACZ,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS;QACzD,CAAC,CAAE,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,EAAE,OAAO,CAA6B;QAC/E,CAAC,CAAC,SAAS,CAAC;IAChB,OAAO;QACL,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO;QAC5B,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,WAAW;QACpC,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;QACzD,QAAQ,EAAE,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC;QACrC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,OAAO,CAAe,CAAC;KAC/E,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAY,EAAE,WAAmC;IAClF,aAAa,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;IACzC,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC1F,MAAM,IAAI,mBAAmB,CAC3B,gFAAgF,CACjF,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC;SACrC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;SACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;IACjD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAE5D,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAW,CAAC,CAAC;IAEhF,6EAA6E;IAC7E,2EAA2E;IAC3E,yEAAyE;IACzE,4EAA4E;IAC5E,kEAAkE;IAClE,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/F,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,gBAAgB,CACxB,MAAM,EACN,kFAAkF;YAChF,mEAAmE,CACtE,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAY,EACZ,MAAiC;IAEjC,aAAa,CAAC,GAAG,EAAE,uBAAuB,CAAC,CAAC;IAC5C,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,mBAAmB,CAC3B,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAClC,CAAC;AAED,gFAAgF;AAChF,0EAA0E;AAC1E,wEAAwE;AACxE,yCAAyC;AAEzC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAoB;IACnD,MAAM,SAAS,GAAG,OAAO,CACvB;QACE,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnF,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS;YAChC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAA4B,EAAE;oBACvE,oBAAoB;oBACpB,gBAAgB;iBACjB,CAAC;aACH,CAAC;QACN,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;KAC9C,EACD,CAAC,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,CAC9D,CAAC;IACF,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AACnD,CAAC"}
@@ -0,0 +1,11 @@
1
+ /** The npm scope every package publishes under. */
2
+ export declare const PACKAGE_SCOPE = "@flow-as-code";
3
+ /** Published package names, keyed by their short names. */
4
+ export declare const PACKAGE_NAMES: {
5
+ readonly core: "@flow-as-code/core";
6
+ readonly cdk: "@flow-as-code/cdk";
7
+ readonly cli: "@flow-as-code/cli";
8
+ readonly tf: "@flow-as-code/tf";
9
+ readonly studio: "@flow-as-code/studio";
10
+ };
11
+ //# sourceMappingURL=package-names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-names.d.ts","sourceRoot":"","sources":["../src/package-names.ts"],"names":[],"mappings":"AAeA,mDAAmD;AACnD,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C,2DAA2D;AAC3D,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAC"}
@@ -0,0 +1,25 @@
1
+ /*
2
+ * Copyright 2026 The flow-as-code Authors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ // The one place the npm package names are written down as values.
6
+ //
7
+ // Everything that embeds a package name in its output, rather than importing
8
+ // it, reads it from here: codegen's default import, the cdk stack scaffold,
9
+ // the tf "generated by" banner, and the CLI's studio asset resolution. A
10
+ // rename is then one edit to PACKAGE_SCOPE plus the package.json files, and a
11
+ // test in core keeps this file honest against those manifests.
12
+ //
13
+ // The hosted demo build (docs/05-hosted-demo.md) aliases this module to a
14
+ // neutral placeholder so no package name reaches the deployed artifact.
15
+ /** The npm scope every package publishes under. */
16
+ export const PACKAGE_SCOPE = "@flow-as-code";
17
+ /** Published package names, keyed by their short names. */
18
+ export const PACKAGE_NAMES = {
19
+ core: `${PACKAGE_SCOPE}/core`,
20
+ cdk: `${PACKAGE_SCOPE}/cdk`,
21
+ cli: `${PACKAGE_SCOPE}/cli`,
22
+ tf: `${PACKAGE_SCOPE}/tf`,
23
+ studio: `${PACKAGE_SCOPE}/studio`,
24
+ };
25
+ //# sourceMappingURL=package-names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-names.js","sourceRoot":"","sources":["../src/package-names.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,kEAAkE;AAClE,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAC5E,yEAAyE;AACzE,8EAA8E;AAC9E,+DAA+D;AAC/D,EAAE;AACF,0EAA0E;AAC1E,wEAAwE;AAExE,mDAAmD;AACnD,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAE7C,2DAA2D;AAC3D,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,GAAG,aAAa,OAAO;IAC7B,GAAG,EAAE,GAAG,aAAa,MAAM;IAC3B,GAAG,EAAE,GAAG,aAAa,MAAM;IAC3B,EAAE,EAAE,GAAG,aAAa,KAAK;IACzB,MAAM,EAAE,GAAG,aAAa,SAAS;CACzB,CAAC"}
package/dist/refs.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ import type { RefEntry, RefType } from "./flowdoc.js";
2
+ /**
3
+ * A reference token, branded with the kind of resource it points at, so a
4
+ * queue cannot be passed where a Lambda is expected.
5
+ */
6
+ export type Ref<T extends RefType = RefType> = string & {
7
+ readonly __ref: T;
8
+ };
9
+ /** A single JSONPath identifier, the dynamic alternative to a reference. */
10
+ export type JsonPath = string & {
11
+ readonly __jsonPath: true;
12
+ };
13
+ /** A value legal in a reference-bearing parameter. */
14
+ export type RefValue<T extends RefType> = Ref<T> | JsonPath;
15
+ export declare const TOKEN_PATTERN: RegExp;
16
+ export declare const Refs: {
17
+ readonly queue: (name: string) => Ref<"queue">;
18
+ readonly hours: (name: string) => Ref<"hours">;
19
+ readonly lambda: (name: string) => Ref<"lambda">;
20
+ readonly lex: (name: string) => Ref<"lex">;
21
+ readonly prompt: (name: string) => Ref<"prompt">;
22
+ readonly flow: (name: string) => Ref<"flow">;
23
+ /** Module references carry an alias so a flow can pin which version it invokes. */
24
+ readonly module: (name: string, alias: string) => Ref<"module">;
25
+ };
26
+ /** Wraps a JSONPath expression for use in a reference-bearing parameter. */
27
+ export declare function jsonPath(path: string): JsonPath;
28
+ export declare function isToken(value: unknown): value is Ref;
29
+ export declare function parseToken(value: string): RefEntry | undefined;
30
+ /**
31
+ * Derived index of every token in a document, sorted by token so the output is
32
+ * stable. Regenerated on save; never hand-maintained.
33
+ */
34
+ export declare function collectRefs(value: unknown): RefEntry[];
35
+ //# sourceMappingURL=refs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refs.d.ts","sourceRoot":"","sources":["../src/refs.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGtD;;;GAGG;AACH,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,MAAM,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AAE9E,4EAA4E;AAC5E,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAA;CAAE,CAAC;AAE9D,sDAAsD;AACtD,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC;AAE5D,eAAO,MAAM,aAAa,QAC8F,CAAC;AAsBzH,eAAO,MAAM,IAAI;2BACD,MAAM,KAAG,GAAG,CAAC,OAAO,CAAC;2BACrB,MAAM,KAAG,GAAG,CAAC,OAAO,CAAC;4BACpB,MAAM,KAAG,GAAG,CAAC,QAAQ,CAAC;yBACzB,MAAM,KAAG,GAAG,CAAC,KAAK,CAAC;4BAChB,MAAM,KAAG,GAAG,CAAC,QAAQ,CAAC;0BACxB,MAAM,KAAG,GAAG,CAAC,MAAM,CAAC;IACjC,mFAAmF;4BACpE,MAAM,SAAS,MAAM,KAAG,GAAG,CAAC,QAAQ,CAAC;CAC5C,CAAC;AAEX,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAO/C;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,GAAG,CAEpD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAO9D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,EAAE,CAOtD"}
package/dist/refs.js ADDED
@@ -0,0 +1,69 @@
1
+ /*
2
+ * Copyright 2026 The flow-as-code Authors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ // Reference tokens. A reference occupies an entire field value and is never
6
+ // interpolated into a longer string: Connect requires the fields that hold
7
+ // references to be "either fully static or a single valid JSONPath
8
+ // identifier". See conformance/flow-language/actions.md.
9
+ import { SLUG_PATTERN } from "./flowdoc.js";
10
+ export const TOKEN_PATTERN = /^\$\{cdref:(queue|hours|lambda|lex|prompt|flow|module):([a-z0-9]+(?:-[a-z0-9]+)*)(?:@([a-z0-9]+(?:-[a-z0-9]+)*))?\}$/;
11
+ /** Matches tokens anywhere in a string, for building the refs index. */
12
+ const TOKEN_SCAN = /\$\{cdref:[a-z]+:[^}]+\}/g;
13
+ const JSONPATH_PATTERN = /^\$\.[A-Za-z0-9_$.[\]'-]+$/;
14
+ function assertSlug(kind, value) {
15
+ if (!SLUG_PATTERN.test(value)) {
16
+ throw new Error(`Invalid ${kind} "${value}". Names must be lowercase words separated by single hyphens.`);
17
+ }
18
+ }
19
+ function token(type, name, alias) {
20
+ assertSlug(`${type} name`, name);
21
+ if (alias !== undefined)
22
+ assertSlug("module alias", alias);
23
+ const suffix = alias === undefined ? "" : `@${alias}`;
24
+ return `\${cdref:${type}:${name}${suffix}}`;
25
+ }
26
+ export const Refs = {
27
+ queue: (name) => token("queue", name),
28
+ hours: (name) => token("hours", name),
29
+ lambda: (name) => token("lambda", name),
30
+ lex: (name) => token("lex", name),
31
+ prompt: (name) => token("prompt", name),
32
+ flow: (name) => token("flow", name),
33
+ /** Module references carry an alias so a flow can pin which version it invokes. */
34
+ module: (name, alias) => token("module", name, alias),
35
+ };
36
+ /** Wraps a JSONPath expression for use in a reference-bearing parameter. */
37
+ export function jsonPath(path) {
38
+ if (!JSONPATH_PATTERN.test(path)) {
39
+ throw new Error(`Invalid JSONPath "${path}". Expected a single identifier such as $.Attributes.queueId.`);
40
+ }
41
+ return path;
42
+ }
43
+ export function isToken(value) {
44
+ return typeof value === "string" && TOKEN_PATTERN.test(value);
45
+ }
46
+ export function parseToken(value) {
47
+ const m = TOKEN_PATTERN.exec(value);
48
+ if (!m)
49
+ return undefined;
50
+ const [, type, name, alias] = m;
51
+ const entry = { token: value, type: type, name: name };
52
+ if (alias !== undefined)
53
+ entry.alias = alias;
54
+ return entry;
55
+ }
56
+ /**
57
+ * Derived index of every token in a document, sorted by token so the output is
58
+ * stable. Regenerated on save; never hand-maintained.
59
+ */
60
+ export function collectRefs(value) {
61
+ const found = new Map();
62
+ for (const raw of JSON.stringify(value ?? null).match(TOKEN_SCAN) ?? []) {
63
+ const entry = parseToken(raw);
64
+ if (entry)
65
+ found.set(entry.token, entry);
66
+ }
67
+ return [...found.values()].sort((a, b) => (a.token < b.token ? -1 : a.token > b.token ? 1 : 0));
68
+ }
69
+ //# sourceMappingURL=refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refs.js","sourceRoot":"","sources":["../src/refs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,4EAA4E;AAC5E,2EAA2E;AAC3E,mEAAmE;AACnE,yDAAyD;AAGzD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAc5C,MAAM,CAAC,MAAM,aAAa,GACxB,sHAAsH,CAAC;AAEzH,wEAAwE;AACxE,MAAM,UAAU,GAAG,2BAA2B,CAAC;AAE/C,MAAM,gBAAgB,GAAG,4BAA4B,CAAC;AAEtD,SAAS,UAAU,CAAC,IAAY,EAAE,KAAa;IAC7C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,WAAW,IAAI,KAAK,KAAK,+DAA+D,CACzF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,KAAK,CAAoB,IAAO,EAAE,IAAY,EAAE,KAAc;IACrE,UAAU,CAAC,GAAG,IAAI,OAAO,EAAE,IAAI,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,SAAS;QAAE,UAAU,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC;IACtD,OAAO,YAAY,IAAI,IAAI,IAAI,GAAG,MAAM,GAAa,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,KAAK,EAAE,CAAC,IAAY,EAAgB,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC;IAC3D,KAAK,EAAE,CAAC,IAAY,EAAgB,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC;IAC3D,MAAM,EAAE,CAAC,IAAY,EAAiB,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC;IAC9D,GAAG,EAAE,CAAC,IAAY,EAAc,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC;IACrD,MAAM,EAAE,CAAC,IAAY,EAAiB,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC;IAC9D,IAAI,EAAE,CAAC,IAAY,EAAe,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC;IACxD,mFAAmF;IACnF,MAAM,EAAE,CAAC,IAAY,EAAE,KAAa,EAAiB,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC;CAC5E,CAAC;AAEX,4EAA4E;AAC5E,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,qBAAqB,IAAI,+DAA+D,CACzF,CAAC;IACJ,CAAC;IACD,OAAO,IAAgB,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzB,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,KAAK,GAAa,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAe,EAAE,IAAI,EAAE,IAAK,EAAE,CAAC;IAC7E,IAAI,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;IAC7C,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;QACxE,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,KAAK;YAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClG,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { FlowAction, FlowDoc } from "./flowdoc.js";
2
+ /** Recursively sorts object keys. Arrays keep their order, which is meaningful. */
3
+ export declare function sortKeys(value: unknown): unknown;
4
+ /** Emits keys in the given order, then anything else alphabetically. */
5
+ export declare function ordered<T extends Record<string, unknown>>(value: T, keys: (keyof T)[]): T;
6
+ export declare function canonicalAction(a: FlowAction): FlowAction;
7
+ /**
8
+ * Canonical in-memory form. Key order below is the byte order of the output.
9
+ */
10
+ export declare function canonicalize(doc: FlowDoc): FlowDoc;
11
+ /** Canonical JSON text, two-space indented, newline terminated. */
12
+ export declare function serialize(doc: FlowDoc): string;
13
+ //# sourceMappingURL=serialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAe,MAAM,cAAc,CAAC;AAErE,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOhD;AAED,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CASzF;AA0BD,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,GAAG,UAAU,CAQzD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAsBlD;AAED,mEAAmE;AACnE,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAE9C"}
@@ -0,0 +1,86 @@
1
+ /*
2
+ * Copyright 2026 The flow-as-code Authors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ // Deterministic serialization. Invariant 1 of the FlowDoc spec: same inputs,
6
+ // byte-identical output. Two authorings of the same flow must produce the same
7
+ // bytes, so key order is fixed rather than insertion-dependent.
8
+ /** Recursively sorts object keys. Arrays keep their order, which is meaningful. */
9
+ export function sortKeys(value) {
10
+ if (Array.isArray(value))
11
+ return value.map(sortKeys);
12
+ if (value === null || typeof value !== "object")
13
+ return value;
14
+ const entries = Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
15
+ return Object.fromEntries(entries.map(([k, v]) => [k, sortKeys(v)]));
16
+ }
17
+ /** Emits keys in the given order, then anything else alphabetically. */
18
+ export function ordered(value, keys) {
19
+ const out = {};
20
+ for (const k of keys) {
21
+ if (value[k] !== undefined)
22
+ out[k] = value[k];
23
+ }
24
+ for (const k of Object.keys(value).sort()) {
25
+ if (!keys.includes(k) && value[k] !== undefined)
26
+ out[k] = value[k];
27
+ }
28
+ return out;
29
+ }
30
+ function canonicalTransitions(t) {
31
+ // The arrays' ELEMENTS need canonicalizing too. Ordering only the top level
32
+ // copied Errors and Conditions by reference, so { ErrorType, NextAction } and
33
+ // { NextAction, ErrorType } serialized to different bytes: two authorings of
34
+ // the same flow produced different output, and downstream a different
35
+ // CloudFormation logical id.
36
+ const canonical = { ...t };
37
+ if (t.Errors !== undefined) {
38
+ canonical.Errors = t.Errors.map((e) => ordered({ ...e }, ["ErrorType", "NextAction"]));
39
+ }
40
+ if (t.Conditions !== undefined) {
41
+ canonical.Conditions = t.Conditions.map((c) => ordered({
42
+ NextAction: c.NextAction,
43
+ Condition: ordered({ ...c.Condition }, ["Operator", "Operands"]),
44
+ }, ["NextAction", "Condition"]));
45
+ }
46
+ return ordered(canonical, ["NextAction", "Errors", "Conditions"]);
47
+ }
48
+ export function canonicalAction(a) {
49
+ return {
50
+ Identifier: a.Identifier,
51
+ Type: a.Type,
52
+ // Parameters are opaque to us for GenericBlocks, so sort for stability.
53
+ Parameters: sortKeys(a.Parameters),
54
+ Transitions: canonicalTransitions(a.Transitions),
55
+ };
56
+ }
57
+ /**
58
+ * Canonical in-memory form. Key order below is the byte order of the output.
59
+ */
60
+ export function canonicalize(doc) {
61
+ const out = {
62
+ flowdoc: doc.flowdoc,
63
+ kind: doc.kind,
64
+ name: doc.name,
65
+ connectType: doc.connectType,
66
+ content: ordered({
67
+ Version: doc.content.Version,
68
+ StartAction: doc.content.StartAction,
69
+ ...(doc.content.Settings === undefined ? {} : { Settings: sortKeys(doc.content.Settings) }),
70
+ ...(doc.content.Metadata === undefined ? {} : { Metadata: sortKeys(doc.content.Metadata) }),
71
+ Actions: doc.content.Actions.map(canonicalAction),
72
+ }, ["Version", "StartAction", "Settings", "Metadata", "Actions"]),
73
+ };
74
+ if (doc.layout !== undefined)
75
+ out.layout = sortKeys(doc.layout);
76
+ if (doc.refs !== undefined)
77
+ out.refs = doc.refs.map((r) => ordered({ ...r }, ["token", "type", "name", "alias"]));
78
+ if (doc.meta !== undefined)
79
+ out.meta = sortKeys(doc.meta);
80
+ return out;
81
+ }
82
+ /** Canonical JSON text, two-space indented, newline terminated. */
83
+ export function serialize(doc) {
84
+ return JSON.stringify(canonicalize(doc), null, 2) + "\n";
85
+ }
86
+ //# sourceMappingURL=serialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.js","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"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"}