@flow-as-code/tf 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.
package/dist/emit.js ADDED
@@ -0,0 +1,350 @@
1
+ /*
2
+ * Copyright 2026 The flow-as-code Authors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ // The Terraform/OpenTofu emitter. FlowDoc in, HCL plus .tftpl out, per
6
+ // docs/03-tf-emitter.md. One-way in v1: there is no HCL to FlowDoc direction.
7
+ //
8
+ // `emitTf` is pure: it takes documents and returns a path -> content map, so the
9
+ // goldens in conformance/emit-tf compare bytes with no filesystem involved.
10
+ // `writeTf` (write.ts) is the thin wrapper that puts them on disk.
11
+ //
12
+ // Verified provider arguments (checked 2026-08-31 against the provider docs and
13
+ // against `tofu validate` with hashicorp/aws 6.62.0 and hashicorp/awscc 1.99.0,
14
+ // see src/validate.test.ts):
15
+ // https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_contact_flow
16
+ // https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_contact_flow_module
17
+ // https://registry.terraform.io/providers/hashicorp/awscc/latest/docs/resources/connect_contact_flow_module_version
18
+ // https://registry.terraform.io/providers/hashicorp/awscc/latest/docs/resources/connect_contact_flow_module_alias
19
+ import { PACKAGE_NAMES, SLUG_PATTERN, collectRefs, parseToken, } from "@flow-as-code/core";
20
+ import { arg, blank, block, comment, objectArg, quote, renderFile } from "./hcl.js";
21
+ import { renderTemplate } from "./template.js";
22
+ /**
23
+ * The oldest core version the emitted configuration promises to work on, per
24
+ * docs/03-tf-emitter.md ("emit nothing Terraform 1.8+/OpenTofu 1.7+ do not both
25
+ * support"). It lands in every `versions.tf.example` as `required_version`, and
26
+ * the emit-tf CI job is asserted to run the gated suite against it, so the
27
+ * promise and the thing that checks the promise cannot drift apart.
28
+ */
29
+ export const CORE_VERSION_FLOOR = "1.7.0";
30
+ /** Emission refused. Every problem found is listed, not just the first. */
31
+ export class EmitTfError extends Error {
32
+ problems;
33
+ constructor(problems) {
34
+ super(`Cannot emit terraform:\n - ${problems.join("\n - ")}`);
35
+ this.name = "EmitTfError";
36
+ this.problems = problems;
37
+ }
38
+ }
39
+ const DEFAULT_INSTANCE_ID_EXPRESSION = "var.connect_instance_id";
40
+ const INSTANCE_ID_VARIABLE = "connect_instance_id";
41
+ const GENERATED_BY = [
42
+ `Generated by ${PACKAGE_NAMES.tf}. Do not edit by hand: edit the`,
43
+ "FlowDoc and re-emit. See docs/03-tf-emitter.md.",
44
+ ];
45
+ /** Slug to HCL identifier: hyphens are the only character needing a change. */
46
+ /**
47
+ * A slug as a Terraform identifier. Slugs may begin with a digit (@flow-as-code/core's
48
+ * SLUG_PATTERN allows it) but Terraform identifiers may not: OpenTofu rejects
49
+ * `resource "aws_connect_contact_flow" "2fa_line"` with "Invalid resource
50
+ * name", so a leading digit is prefixed. Deterministic and collision-free
51
+ * against other slugs, since no slug can contain an underscore.
52
+ */
53
+ const ident = (slug) => {
54
+ const underscored = slug.replaceAll("-", "_");
55
+ return /^[0-9]/.test(underscored) ? `_${underscored}` : underscored;
56
+ };
57
+ /** `<type>_<name>_arn`, with the module alias folded in. Stable by contract. */
58
+ function variableName(entry) {
59
+ const alias = entry.alias === undefined ? "" : `_${ident(entry.alias)}`;
60
+ return `${entry.type}_${ident(entry.name)}${alias}_arn`;
61
+ }
62
+ /** The bare identifier emitted when no address is known. Fails `validate`. */
63
+ const placeholder = (variable) => `TODO_MISSING_ADDRESS_${variable.replace(/_arn$/, "")}`;
64
+ /** `queue:front-desk`, `module:survey@prod`: the token without its wrapper. */
65
+ const refKey = (entry) => `${entry.type}:${entry.name}${entry.alias === undefined ? "" : `@${entry.alias}`}`;
66
+ const resourceType = (doc) => doc.kind === "module" ? "aws_connect_contact_flow_module" : "aws_connect_contact_flow";
67
+ /**
68
+ * Name of the locals entry a document's content is rendered with. Documents
69
+ * that reference something this set emits get their own, because putting those
70
+ * addresses in the shared map would make the map depend on resources the map
71
+ * itself feeds: terraform and tofu both refuse that as a dependency cycle
72
+ * (proved by the module-set case in src/validate.test.ts). Everything else
73
+ * shares `local.flow_refs`.
74
+ */
75
+ const REFS_LOCAL = "flow_refs";
76
+ const refsLocalFor = (doc) => `${doc.kind === "module" ? "module" : "flow"}_refs_${ident(doc.name)}`;
77
+ /** A flow and a module may share a name, so a document key carries both. */
78
+ const docKey = (doc) => `${doc.kind}:${doc.name}`;
79
+ const byString = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
80
+ /**
81
+ * An address expression is HCL the emitter pastes into `flow_refs.tf`
82
+ * unevaluated, so it is checked for the things that would corrupt the file or
83
+ * the deploy. Literal ARNs are rejected outright: references stay references
84
+ * (CLAUDE.md, docs/01-flowdoc-spec.md).
85
+ */
86
+ function checkExpression(label, value, problems) {
87
+ if (value.trim() === "") {
88
+ problems.push(`${label} is empty`);
89
+ return;
90
+ }
91
+ if (/arn:aws/i.test(value)) {
92
+ problems.push(`${label} is a literal ARN (${value}); map to a terraform address instead`);
93
+ }
94
+ if (/[\n\r]/.test(value))
95
+ problems.push(`${label} spans multiple lines (${JSON.stringify(value)})`);
96
+ if (/#|\/\/|\/\*/.test(value))
97
+ problems.push(`${label} contains a comment marker (${value})`);
98
+ }
99
+ /** Documents in emission order, with the structural problems reported first. */
100
+ function checkDocs(docs) {
101
+ const problems = [];
102
+ if (docs.length === 0)
103
+ problems.push("no documents to emit");
104
+ const seen = new Map();
105
+ for (const doc of docs) {
106
+ if (!SLUG_PATTERN.test(doc.name)) {
107
+ problems.push(`document name "${doc.name}" is not a slug`);
108
+ continue;
109
+ }
110
+ // `type` accepts every ConnectType except MODULE, and the module resource
111
+ // takes no `type` at all, so kind and connectType have to agree.
112
+ if (doc.kind === "module" && doc.connectType !== "MODULE") {
113
+ problems.push(`module "${doc.name}" has connectType ${doc.connectType}, expected MODULE`);
114
+ }
115
+ if (doc.kind === "flow" && doc.connectType === "MODULE") {
116
+ problems.push(`flow "${doc.name}" has connectType MODULE; emit it as kind "module"`);
117
+ }
118
+ const address = `${resourceType(doc)}.${ident(doc.name)}`;
119
+ const prior = seen.get(address);
120
+ if (prior !== undefined)
121
+ problems.push(`two documents both emit ${address}`);
122
+ else
123
+ seen.set(address, doc.name);
124
+ }
125
+ if (problems.length > 0)
126
+ throw new EmitTfError(problems);
127
+ // Kind breaks the tie, because a flow and a module may share a name and the
128
+ // order must not depend on the order the caller passed them in.
129
+ return [...docs].sort((a, b) => byString(a.name, b.name) || byString(a.kind, b.kind));
130
+ }
131
+ /**
132
+ * Every reference in the set, resolved. Documents in the set resolve their own
133
+ * references: a `${cdref:module:x@prod}` whose module x is emitted here points
134
+ * at the alias resource emitted with it, and a `${cdref:flow:y}` whose flow y is
135
+ * emitted here points at that flow. Everything else comes from the address map,
136
+ * and what the map does not cover gets a placeholder that fails `validate`.
137
+ */
138
+ function resolveRefs(docs, addressMap, problems) {
139
+ const flows = new Set(docs.filter((d) => d.kind === "flow").map((d) => d.name));
140
+ const modules = new Set(docs.filter((d) => d.kind === "module").map((d) => d.name));
141
+ const entries = new Map();
142
+ for (const doc of docs) {
143
+ for (const entry of collectRefs(doc.content))
144
+ entries.set(entry.token, entry);
145
+ }
146
+ const byVariable = new Map();
147
+ const resolved = [];
148
+ for (const entry of [...entries.values()].sort((a, b) => byString(a.token, b.token))) {
149
+ const variable = variableName(entry);
150
+ const clash = byVariable.get(variable);
151
+ if (clash !== undefined) {
152
+ problems.push(`${entry.token} and ${clash} both map to local.flow_refs.${variable}`);
153
+ continue;
154
+ }
155
+ byVariable.set(variable, entry.token);
156
+ const key = refKey(entry);
157
+ const supplied = addressMap[entry.token] ?? addressMap[key] ?? addressMap[variable];
158
+ if (supplied !== undefined)
159
+ checkExpression(`address for ${entry.token}`, supplied, problems);
160
+ let inSet;
161
+ if (entry.type === "module" && entry.alias !== undefined && modules.has(entry.name)) {
162
+ inSet = `awscc_connect_contact_flow_module_alias.${ident(entry.name)}_${ident(entry.alias)}.contact_flow_module_alias_arn`;
163
+ }
164
+ else if (entry.type === "flow" && flows.has(entry.name)) {
165
+ inSet = `aws_connect_contact_flow.${ident(entry.name)}.arn`;
166
+ }
167
+ if (inSet !== undefined) {
168
+ const shadowed = supplied === undefined ? undefined : key;
169
+ resolved.push({
170
+ entry,
171
+ variable,
172
+ expression: inSet,
173
+ source: "set",
174
+ ...(shadowed ? { shadowed } : {}),
175
+ });
176
+ }
177
+ else if (supplied !== undefined) {
178
+ resolved.push({ entry, variable, expression: supplied, source: "map" });
179
+ }
180
+ else {
181
+ resolved.push({ entry, variable, expression: placeholder(variable), source: "missing" });
182
+ }
183
+ }
184
+ return resolved;
185
+ }
186
+ /** Aliases each emitted module needs, derived from the references to it. */
187
+ function aliasesByModule(docs) {
188
+ const modules = new Set(docs.filter((d) => d.kind === "module").map((d) => d.name));
189
+ const aliases = new Map();
190
+ for (const name of modules)
191
+ aliases.set(name, new Set());
192
+ for (const doc of docs) {
193
+ for (const entry of collectRefs(doc.content)) {
194
+ if (entry.type !== "module" || entry.alias === undefined)
195
+ continue;
196
+ aliases.get(entry.name)?.add(entry.alias);
197
+ }
198
+ }
199
+ return new Map([...aliases].map(([name, set]) => [name, [...set].sort(byString)]));
200
+ }
201
+ const AWS_FLOW_DOC = "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_contact_flow";
202
+ const AWS_MODULE_DOC = "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_contact_flow_module";
203
+ const AWSCC_VERSION_DOC = "https://registry.terraform.io/providers/hashicorp/awscc/latest/docs/resources/connect_contact_flow_module_version";
204
+ const AWSCC_ALIAS_DOC = "https://registry.terraform.io/providers/hashicorp/awscc/latest/docs/resources/connect_contact_flow_module_alias";
205
+ const CREATE_FLOW_VERSION_DOC = "https://docs.aws.amazon.com/connect/latest/APIReference/API_CreateContactFlowVersion.html";
206
+ function flowsTf(docs, instanceId, inSetByDoc) {
207
+ const aliases = aliasesByModule(docs);
208
+ const hasModule = docs.some((d) => d.kind === "module");
209
+ const lines = GENERATED_BY.map(comment);
210
+ lines.push(comment(""), comment("Flow content is rendered from flows/<name>.flow.tftpl with the"), comment("addresses in flow_refs.tf. Provider, credential, and backend"), comment("configuration is yours; versions.tf.example lists what this needs."), comment(""), comment(AWS_FLOW_DOC), comment(AWS_MODULE_DOC));
211
+ if (hasModule) {
212
+ lines.push(comment(""), comment("Modules carry versions and aliases; full flows do not."), comment('CreateContactFlowVersion "only supports creating versions for'), comment('flows of type Campaign", so a flow updates its content in place'), comment("while a module pins content behind an alias."), comment(CREATE_FLOW_VERSION_DOC));
213
+ }
214
+ for (const doc of docs) {
215
+ const refs = (inSetByDoc.get(docKey(doc)) ?? []).length > 0 ? refsLocalFor(doc) : REFS_LOCAL;
216
+ const template = `templatefile("\${path.module}/flows/${doc.name}.flow.tftpl", local.${refs})`;
217
+ const body = [arg("instance_id", instanceId), arg("name", quote(doc.name))];
218
+ // `type` exists on the flow resource only; the module resource has none.
219
+ if (doc.kind === "flow")
220
+ body.push(arg("type", quote(doc.connectType)));
221
+ body.push(arg("content", template));
222
+ lines.push(blank(), block("resource", [resourceType(doc), ident(doc.name)], body));
223
+ if (doc.kind !== "module")
224
+ continue;
225
+ const self = `aws_connect_contact_flow_module.${ident(doc.name)}.arn`;
226
+ lines.push(blank(), comment("Snapshot of the module content as it stands when this resource is"), comment("created. Replace it after a content change so the aliases below"), comment("serve the new content."), comment(AWSCC_VERSION_DOC), block("resource", ["awscc_connect_contact_flow_module_version", ident(doc.name)], [arg("contact_flow_module_id", self)]));
227
+ for (const alias of aliases.get(doc.name) ?? []) {
228
+ lines.push(blank(), comment(AWSCC_ALIAS_DOC), block("resource", ["awscc_connect_contact_flow_module_alias", `${ident(doc.name)}_${ident(alias)}`], [
229
+ arg("contact_flow_module_id", self),
230
+ arg("contact_flow_module_version", `awscc_connect_contact_flow_module_version.${ident(doc.name)}.version`),
231
+ arg("name", quote(alias)),
232
+ ]));
233
+ }
234
+ }
235
+ return renderFile(lines);
236
+ }
237
+ function flowRefsTf(docs, resolved, inSetByDoc) {
238
+ const lines = GENERATED_BY.map(comment);
239
+ lines.push(comment(""), comment("One entry per reference token in the emitted flow set, named"), comment("<type>_<name>_arn with hyphens as underscores and the module alias"), comment("folded in. Values are terraform addresses, never literal ARNs."));
240
+ const shared = [];
241
+ for (const ref of resolved) {
242
+ // Addresses of resources this set emits are deliberately left out of the
243
+ // shared map; see refsLocalFor and the per-document entries below.
244
+ if (ref.source === "set")
245
+ continue;
246
+ if (ref.source === "missing") {
247
+ shared.push(comment(`TODO: no terraform address for ${ref.entry.token}.`), comment(`Add one to the address map under key "${refKey(ref.entry)}"`), comment("and re-emit. Until then this placeholder is an undeclared"), comment("reference, so `terraform validate` and `tofu validate` fail."));
248
+ }
249
+ shared.push(arg(ref.variable, ref.expression));
250
+ }
251
+ if (shared.length === 0) {
252
+ shared.push(comment("No reference resolves through this map."));
253
+ }
254
+ const body = [objectArg(REFS_LOCAL, shared)];
255
+ for (const doc of docs) {
256
+ const own = inSetByDoc.get(docKey(doc)) ?? [];
257
+ if (own.length === 0)
258
+ continue;
259
+ const entries = [];
260
+ for (const ref of own) {
261
+ if (ref.shadowed !== undefined) {
262
+ entries.push(comment(`Address map entry "${ref.shadowed}" ignored: this set emits`), comment("the resource itself, so the reference resolves to it."));
263
+ }
264
+ entries.push(arg(ref.variable, ref.expression));
265
+ }
266
+ body.push(blank(), comment(`${doc.name} references a resource this set emits. Its addresses`), comment("go in a local of their own: in the shared map they would make"), comment("that map depend on resources rendered from it, which both tools"), comment("report as a dependency cycle."), objectArg(refsLocalFor(doc), entries, {
267
+ open: `merge(local.${REFS_LOCAL}, {`,
268
+ close: "})",
269
+ }));
270
+ }
271
+ lines.push(blank(), block("locals", [], body));
272
+ return renderFile(lines);
273
+ }
274
+ function variablesTf() {
275
+ const lines = GENERATED_BY.map(comment);
276
+ lines.push(blank(), block("variable", [INSTANCE_ID_VARIABLE], [
277
+ arg("description", quote("Identifier of the Amazon Connect instance that holds these flows.")),
278
+ arg("type", "string"),
279
+ ]));
280
+ return renderFile(lines);
281
+ }
282
+ function versionsExample(needsAwscc) {
283
+ const providers = [
284
+ objectArg("aws", [arg("source", quote("hashicorp/aws")), arg("version", quote(">= 5.0"))]),
285
+ ];
286
+ if (needsAwscc) {
287
+ providers.push(blank(), objectArg("awscc", [
288
+ arg("source", quote("hashicorp/awscc")),
289
+ arg("version", quote(">= 1.74")),
290
+ ]));
291
+ }
292
+ const lines = GENERATED_BY.map(comment);
293
+ lines.push(comment(""), comment("Example only. This file is not loaded by terraform or tofu, which"), comment("read *.tf; copy what you need into your own configuration. No"), comment("provider credentials and no backend configuration are emitted."), comment(""), comment("The version constraint below is what both tools understand:"), comment("this output uses nothing newer than Terraform 1.8 / OpenTofu 1.7."));
294
+ if (needsAwscc) {
295
+ lines.push(comment(""), comment("awscc is needed for the module version and alias resources; it"), comment("carries them and the aws provider does not."));
296
+ }
297
+ lines.push(blank(), block("terraform", [], [
298
+ arg("required_version", quote(`>= ${CORE_VERSION_FLOOR}`)),
299
+ blank(),
300
+ block("required_providers", [], providers),
301
+ ]));
302
+ return renderFile(lines);
303
+ }
304
+ /**
305
+ * Emits a Terraform/OpenTofu configuration for a set of FlowDocs. Pure and
306
+ * deterministic: the same documents and options produce byte-identical files,
307
+ * and paths come back sorted.
308
+ */
309
+ export function emitTf(docs, options = {}) {
310
+ const ordered = checkDocs(docs);
311
+ const problems = [];
312
+ const instanceId = options.instanceIdExpression ?? DEFAULT_INSTANCE_ID_EXPRESSION;
313
+ if (options.instanceIdExpression !== undefined) {
314
+ checkExpression("instanceIdExpression", options.instanceIdExpression, problems);
315
+ }
316
+ const addressMap = options.addressMap ?? {};
317
+ for (const key of Object.keys(addressMap)) {
318
+ // A key in token form has to be a token we could actually have produced.
319
+ if (key.startsWith("${") && parseToken(key) === undefined) {
320
+ problems.push(`address map key "${key}" looks like a token but does not parse as one`);
321
+ }
322
+ }
323
+ const resolved = resolveRefs(ordered, addressMap, problems);
324
+ if (problems.length > 0)
325
+ throw new EmitTfError(problems);
326
+ // Which of each document's own references resolve to resources this set
327
+ // emits. They are what the per-document locals hold; see refsLocalFor.
328
+ const bySet = new Map(resolved.filter((r) => r.source === "set").map((r) => [r.entry.token, r]));
329
+ const inSetByDoc = new Map(ordered.map((doc) => [
330
+ docKey(doc),
331
+ collectRefs(doc.content)
332
+ .map((entry) => bySet.get(entry.token))
333
+ .filter((r) => r !== undefined),
334
+ ]));
335
+ const variables = new Map(resolved.map((r) => [r.entry.token, r.variable]));
336
+ const files = {
337
+ "flow_refs.tf": flowRefsTf(ordered, resolved, inSetByDoc),
338
+ "flows.tf": flowsTf(ordered, instanceId, inSetByDoc),
339
+ "versions.tf.example": versionsExample(ordered.some((d) => d.kind === "module")),
340
+ };
341
+ for (const doc of ordered) {
342
+ files[`flows/${doc.name}.flow.tftpl`] = renderTemplate(doc, variables);
343
+ }
344
+ if (options.instanceIdExpression === undefined)
345
+ files["variables.tf"] = variablesTf();
346
+ return {
347
+ files: Object.fromEntries(Object.entries(files).sort(([a], [b]) => byString(a, b))),
348
+ };
349
+ }
350
+ //# sourceMappingURL=emit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit.js","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"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"}
package/dist/hcl.d.ts ADDED
@@ -0,0 +1,49 @@
1
+ /** One line of an HCL body. */
2
+ export type HclLine = {
3
+ kind: "arg";
4
+ key: string;
5
+ value: string;
6
+ } | {
7
+ kind: "objectArg";
8
+ key: string;
9
+ body: HclLine[];
10
+ open?: string;
11
+ close?: string;
12
+ } | {
13
+ kind: "block";
14
+ type: string;
15
+ labels: string[];
16
+ body: HclLine[];
17
+ } | {
18
+ kind: "comment";
19
+ text: string;
20
+ } | {
21
+ kind: "blank";
22
+ };
23
+ export declare const arg: (key: string, value: string) => HclLine;
24
+ export declare const comment: (text: string) => HclLine;
25
+ export declare const blank: () => HclLine;
26
+ export declare const block: (type: string, labels: string[], body: HclLine[]) => HclLine;
27
+ /**
28
+ * `key = { ... }`: an object value, not a block. `open` and `close` wrap the
29
+ * body in something else, such as a `merge(...)` call around the braces.
30
+ */
31
+ export declare const objectArg: (key: string, body: HclLine[], wrap?: {
32
+ open: string;
33
+ close: string;
34
+ }) => HclLine;
35
+ /**
36
+ * A double-quoted HCL string with no template introducer left live. A quoted
37
+ * string in .tf is itself a template, so `${` and `%{` are escaped here too.
38
+ * The `$` replacements are functions because a string replacement would give
39
+ * `$` its substitution meaning and undo the escape.
40
+ */
41
+ export declare function quote(value: string): string;
42
+ /**
43
+ * Renders body lines at the given indent, aligning the `=` within each run of
44
+ * consecutive argument lines the way fmt does.
45
+ */
46
+ export declare function render(lines: readonly HclLine[], indent?: string): string[];
47
+ /** A whole file: rendered lines plus the trailing newline fmt insists on. */
48
+ export declare function renderFile(lines: readonly HclLine[]): string;
49
+ //# sourceMappingURL=hcl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hcl.d.ts","sourceRoot":"","sources":["../src/hcl.ts"],"names":[],"mappings":"AAaA,+BAA+B;AAC/B,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAClF;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,OAAO,EAAE,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtB,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,OAAwC,CAAC;AAC1F,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,OAAsC,CAAC;AAC9E,eAAO,MAAM,KAAK,QAAO,OAA8B,CAAC;AACxD,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,EAAE,QAAQ,MAAM,EAAE,EAAE,MAAM,OAAO,EAAE,KAAG,OAKtE,CAAC;AACH;;;GAGG;AACH,eAAO,MAAM,SAAS,GACpB,KAAK,MAAM,EACX,MAAM,OAAO,EAAE,EACf,OAAO;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,KACrC,OAAsD,CAAC;AAE1D;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAU3C;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE,CAuCvE;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAE5D"}
package/dist/hcl.js ADDED
@@ -0,0 +1,95 @@
1
+ /*
2
+ * Copyright 2026 The flow-as-code Authors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ // A very small HCL writer. It writes the handful of shapes this emitter needs
6
+ // (comments, `key = expression` arguments, and nested blocks) in the layout
7
+ // `terraform fmt` / `tofu fmt` produce, so emitted files are fmt-clean and stay
8
+ // byte-stable across runs.
9
+ //
10
+ // fmt aligns the `=` of a maximal run of consecutive argument lines, and a
11
+ // comment or a blank line ends the run. That rule is reproduced in `render`
12
+ // below and is asserted against a real `tofu fmt` in the gated validate tests.
13
+ export const arg = (key, value) => ({ kind: "arg", key, value });
14
+ export const comment = (text) => ({ kind: "comment", text });
15
+ export const blank = () => ({ kind: "blank" });
16
+ export const block = (type, labels, body) => ({
17
+ kind: "block",
18
+ type,
19
+ labels,
20
+ body,
21
+ });
22
+ /**
23
+ * `key = { ... }`: an object value, not a block. `open` and `close` wrap the
24
+ * body in something else, such as a `merge(...)` call around the braces.
25
+ */
26
+ export const objectArg = (key, body, wrap) => ({ kind: "objectArg", key, body, ...wrap });
27
+ /**
28
+ * A double-quoted HCL string with no template introducer left live. A quoted
29
+ * string in .tf is itself a template, so `${` and `%{` are escaped here too.
30
+ * The `$` replacements are functions because a string replacement would give
31
+ * `$` its substitution meaning and undo the escape.
32
+ */
33
+ export function quote(value) {
34
+ const escaped = value
35
+ .replaceAll("\\", "\\\\")
36
+ .replaceAll('"', '\\"')
37
+ .replaceAll("\n", "\\n")
38
+ .replaceAll("\r", "\\r")
39
+ .replaceAll("\t", "\\t")
40
+ .replaceAll("${", () => "$${")
41
+ .replaceAll("%{", () => "%%{");
42
+ return `"${escaped}"`;
43
+ }
44
+ /**
45
+ * Renders body lines at the given indent, aligning the `=` within each run of
46
+ * consecutive argument lines the way fmt does.
47
+ */
48
+ export function render(lines, indent = "") {
49
+ const out = [];
50
+ for (let i = 0; i < lines.length; i += 1) {
51
+ const line = lines[i];
52
+ if (line === undefined)
53
+ continue;
54
+ if (line.kind === "blank") {
55
+ out.push("");
56
+ continue;
57
+ }
58
+ if (line.kind === "comment") {
59
+ out.push(`${indent}# ${line.text}`.trimEnd());
60
+ continue;
61
+ }
62
+ if (line.kind === "block") {
63
+ const labels = line.labels.map((l) => ` ${quote(l)}`).join("");
64
+ out.push(`${indent}${line.type}${labels} {`);
65
+ out.push(...render(line.body, `${indent} `));
66
+ out.push(`${indent}}`);
67
+ continue;
68
+ }
69
+ if (line.kind === "objectArg") {
70
+ out.push(`${indent}${line.key} = ${line.open ?? "{"}`);
71
+ out.push(...render(line.body, `${indent} `));
72
+ out.push(`${indent}${line.close ?? "}"}`);
73
+ continue;
74
+ }
75
+ // An argument: gather the whole run so the `=` columns line up.
76
+ const run = [];
77
+ let j = i;
78
+ for (; j < lines.length; j += 1) {
79
+ const next = lines[j];
80
+ if (next === undefined || next.kind !== "arg")
81
+ break;
82
+ run.push({ key: next.key, value: next.value });
83
+ }
84
+ const width = Math.max(...run.map((a) => a.key.length));
85
+ for (const a of run)
86
+ out.push(`${indent}${a.key.padEnd(width)} = ${a.value}`);
87
+ i = j - 1;
88
+ }
89
+ return out;
90
+ }
91
+ /** A whole file: rendered lines plus the trailing newline fmt insists on. */
92
+ export function renderFile(lines) {
93
+ return `${render(lines).join("\n").replace(/\n+$/, "")}\n`;
94
+ }
95
+ //# sourceMappingURL=hcl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hcl.js","sourceRoot":"","sources":["../src/hcl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { EmitTfError, type EmitTfOptions, type EmitTfResult, emitTf } from "./emit.js";
2
+ export { escapeTemplateText, renderTemplate } from "./template.js";
3
+ export { writeTf } from "./write.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ /*
2
+ * Copyright 2026 The flow-as-code Authors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ // Public surface of @flow-as-code/tf. FlowDoc in, Terraform/OpenTofu out, per
6
+ // docs/03-tf-emitter.md. One-way in v1: HCL back to FlowDoc is a provider-era
7
+ // feature and is not attempted here.
8
+ export { EmitTfError, emitTf } from "./emit.js";
9
+ export { escapeTemplateText, renderTemplate } from "./template.js";
10
+ export { writeTf } from "./write.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,8EAA8E;AAC9E,8EAA8E;AAC9E,qCAAqC;AAErC,OAAO,EAAE,WAAW,EAAyC,MAAM,EAAE,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { type FlowDoc } from "@flow-as-code/core";
2
+ /**
3
+ * Doubles the leading character of every HCL template introducer so the text
4
+ * renders literally. `${` becomes `$${` and `%{` becomes `%%{`.
5
+ *
6
+ * The two patterns are disjoint and the expansion of one can never produce the
7
+ * other, so a single pass in either order is correct. It is also correct for
8
+ * already-doubled input: HCL scans left to right and takes the longest match,
9
+ * so escaping `$${` to `$$${` renders back to `$${` (a literal `$` followed by
10
+ * the escape for a literal `${`).
11
+ */
12
+ export declare function escapeTemplateText(text: string): string;
13
+ /**
14
+ * A sentinel prefix that does not occur in `haystack`. Authored text is allowed
15
+ * to contain anything, sentinel lookalikes included, so the prefix grows until
16
+ * it is absent from the document being rendered. It terminates: every extra
17
+ * character makes the prefix longer, and a string cannot contain a substring
18
+ * longer than itself.
19
+ */
20
+ export declare function sentinelPrefix(haystack: string): string;
21
+ /**
22
+ * The `.tftpl` body for one document: deployable content JSON with every
23
+ * `${cdref:...}` token replaced by a `${var}` interpolation naming the entry in
24
+ * `local.flow_refs`, and every other template introducer escaped.
25
+ *
26
+ * `variableNames` maps token to template variable name and must cover every
27
+ * token in the document; @flow-as-code/core's strict materializer raises a
28
+ * MaterializeError listing all missing tokens at once if it does not.
29
+ */
30
+ export declare function renderTemplate(doc: FlowDoc, variableNames: ReadonlyMap<string, string>): string;
31
+ //# sourceMappingURL=template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,KAAK,OAAO,EAAwC,MAAM,oBAAoB,CAAC;AAExF;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMvD;AAKD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAqB/F"}