@flow-as-code/cli 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 (120) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +446 -0
  3. package/dist/aws.d.ts +28 -0
  4. package/dist/aws.d.ts.map +1 -0
  5. package/dist/aws.js +104 -0
  6. package/dist/aws.js.map +1 -0
  7. package/dist/bin.d.ts +3 -0
  8. package/dist/bin.d.ts.map +1 -0
  9. package/dist/bin.js +132 -0
  10. package/dist/bin.js.map +1 -0
  11. package/dist/bridge/exportFiles.d.ts +16 -0
  12. package/dist/bridge/exportFiles.d.ts.map +1 -0
  13. package/dist/bridge/exportFiles.js +95 -0
  14. package/dist/bridge/exportFiles.js.map +1 -0
  15. package/dist/bridge/pair.d.ts +103 -0
  16. package/dist/bridge/pair.d.ts.map +1 -0
  17. package/dist/bridge/pair.js +219 -0
  18. package/dist/bridge/pair.js.map +1 -0
  19. package/dist/bridge/protocol.d.ts +187 -0
  20. package/dist/bridge/protocol.d.ts.map +1 -0
  21. package/dist/bridge/protocol.js +132 -0
  22. package/dist/bridge/protocol.js.map +1 -0
  23. package/dist/bridge/server.d.ts +95 -0
  24. package/dist/bridge/server.d.ts.map +1 -0
  25. package/dist/bridge/server.js +669 -0
  26. package/dist/bridge/server.js.map +1 -0
  27. package/dist/cdk-scaffold.d.ts +16 -0
  28. package/dist/cdk-scaffold.d.ts.map +1 -0
  29. package/dist/cdk-scaffold.js +28 -0
  30. package/dist/cdk-scaffold.js.map +1 -0
  31. package/dist/codegen.d.ts +5 -0
  32. package/dist/codegen.d.ts.map +1 -0
  33. package/dist/codegen.js +27 -0
  34. package/dist/codegen.js.map +1 -0
  35. package/dist/diff.d.ts +33 -0
  36. package/dist/diff.d.ts.map +1 -0
  37. package/dist/diff.js +225 -0
  38. package/dist/diff.js.map +1 -0
  39. package/dist/docs.d.ts +46 -0
  40. package/dist/docs.d.ts.map +1 -0
  41. package/dist/docs.js +223 -0
  42. package/dist/docs.js.map +1 -0
  43. package/dist/emit.d.ts +8 -0
  44. package/dist/emit.d.ts.map +1 -0
  45. package/dist/emit.js +55 -0
  46. package/dist/emit.js.map +1 -0
  47. package/dist/errors.d.ts +12 -0
  48. package/dist/errors.d.ts.map +1 -0
  49. package/dist/errors.js +25 -0
  50. package/dist/errors.js.map +1 -0
  51. package/dist/export.d.ts +27 -0
  52. package/dist/export.d.ts.map +1 -0
  53. package/dist/export.js +126 -0
  54. package/dist/export.js.map +1 -0
  55. package/dist/index.d.ts +4 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +23 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/lint.d.ts +6 -0
  60. package/dist/lint.d.ts.map +1 -0
  61. package/dist/lint.js +31 -0
  62. package/dist/lint.js.map +1 -0
  63. package/dist/render.d.ts +6 -0
  64. package/dist/render.d.ts.map +1 -0
  65. package/dist/render.js +49 -0
  66. package/dist/render.js.map +1 -0
  67. package/dist/run.d.ts +13 -0
  68. package/dist/run.d.ts.map +1 -0
  69. package/dist/run.js +38 -0
  70. package/dist/run.js.map +1 -0
  71. package/dist/simulate.d.ts +35 -0
  72. package/dist/simulate.d.ts.map +1 -0
  73. package/dist/simulate.js +194 -0
  74. package/dist/simulate.js.map +1 -0
  75. package/dist/studio.d.ts +23 -0
  76. package/dist/studio.d.ts.map +1 -0
  77. package/dist/studio.js +120 -0
  78. package/dist/studio.js.map +1 -0
  79. package/dist/synth-resolve-hook.d.ts +19 -0
  80. package/dist/synth-resolve-hook.d.ts.map +1 -0
  81. package/dist/synth-resolve-hook.js +127 -0
  82. package/dist/synth-resolve-hook.js.map +1 -0
  83. package/dist/synth-runner.d.ts +12 -0
  84. package/dist/synth-runner.d.ts.map +1 -0
  85. package/dist/synth-runner.js +145 -0
  86. package/dist/synth-runner.js.map +1 -0
  87. package/dist/synth.d.ts +55 -0
  88. package/dist/synth.d.ts.map +1 -0
  89. package/dist/synth.js +327 -0
  90. package/dist/synth.js.map +1 -0
  91. package/dist/watch.d.ts +66 -0
  92. package/dist/watch.d.ts.map +1 -0
  93. package/dist/watch.js +293 -0
  94. package/dist/watch.js.map +1 -0
  95. package/package.json +85 -0
  96. package/schema/flowdoc-0.1.schema.json +669 -0
  97. package/schema/scenario-0.1.schema.json +318 -0
  98. package/src/aws.ts +149 -0
  99. package/src/bin.ts +179 -0
  100. package/src/bridge/exportFiles.ts +116 -0
  101. package/src/bridge/pair.ts +312 -0
  102. package/src/bridge/protocol.ts +289 -0
  103. package/src/bridge/server.ts +827 -0
  104. package/src/cdk-scaffold.ts +41 -0
  105. package/src/codegen.ts +36 -0
  106. package/src/diff.ts +279 -0
  107. package/src/docs.ts +249 -0
  108. package/src/emit.ts +69 -0
  109. package/src/errors.ts +27 -0
  110. package/src/export.ts +167 -0
  111. package/src/index.ts +27 -0
  112. package/src/lint.ts +42 -0
  113. package/src/render.ts +58 -0
  114. package/src/run.ts +42 -0
  115. package/src/simulate.ts +244 -0
  116. package/src/studio.ts +143 -0
  117. package/src/synth-resolve-hook.ts +152 -0
  118. package/src/synth-runner.ts +157 -0
  119. package/src/synth.ts +380 -0
  120. package/src/watch.ts +388 -0
package/dist/render.js ADDED
@@ -0,0 +1,49 @@
1
+ /*
2
+ * Copyright 2026 The flow-as-code Authors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ // `flow-cli render <dir-or-file> --resources <map.json>`: standalone
6
+ // materialization. FlowDoc in, deployable Flow language JSON out, with every
7
+ // `${cdref:...}` token replaced from the resource map.
8
+ //
9
+ // This is the one path where literal ARNs are the goal rather than a mistake
10
+ // (packages/core/SPEC.md, Materialization), so the map's values are not
11
+ // linted. What is enforced is completeness: @flow-as-code/core's MaterializeError
12
+ // carries every unmapped token, and all of them are printed, for every
13
+ // document, before the command exits 1. One run, one round of map edits.
14
+ import { mkdirSync, writeFileSync } from "node:fs";
15
+ import { join, resolve } from "node:path";
16
+ import { MaterializeError, materializeWithMap, serializeContent } from "@flow-as-code/core";
17
+ import { defaultOutDir, loadDocs, readStringMap } from "./docs.js";
18
+ import { CliError } from "./errors.js";
19
+ export function runRender(target, options) {
20
+ const docs = loadDocs(target);
21
+ const resourceMap = readStringMap(options.resources, "resource map");
22
+ const outDir = resolve(options.out ?? defaultOutDir(target));
23
+ const problems = [];
24
+ const rendered = [];
25
+ for (const { path, doc } of docs) {
26
+ try {
27
+ rendered.push({
28
+ path: join(outDir, `${doc.name}.json`),
29
+ text: serializeContent(materializeWithMap(doc, resourceMap)),
30
+ });
31
+ }
32
+ catch (error) {
33
+ if (!(error instanceof MaterializeError))
34
+ throw error;
35
+ problems.push(`${path}: ${String(error.missingTokens.length)} unmapped token(s):`);
36
+ for (const token of error.missingTokens)
37
+ problems.push(` ${token}`);
38
+ }
39
+ }
40
+ if (problems.length > 0) {
41
+ throw new CliError(`Cannot render:\n ${problems.join("\n ")}\n` +
42
+ `Add the missing key(s) to ${resolve(options.resources)}.`);
43
+ }
44
+ mkdirSync(outDir, { recursive: true });
45
+ for (const { path, text } of rendered)
46
+ writeFileSync(path, text, "utf8");
47
+ return rendered.map((r) => r.path);
48
+ }
49
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qEAAqE;AACrE,6EAA6E;AAC7E,uDAAuD;AACvD,EAAE;AACF,6EAA6E;AAC7E,wEAAwE;AACxE,kFAAkF;AAClF,uEAAuE;AACvE,yEAAyE;AAEzE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE5F,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAOvC,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,OAAsB;IAC9D,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9B,MAAM,WAAW,GAAG,aAAa,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IAE7D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAqC,EAAE,CAAC;IACtD,KAAK,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,OAAO,CAAC;gBACtC,IAAI,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;aAC7D,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,gBAAgB,CAAC;gBAAE,MAAM,KAAK,CAAC;YACtD,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;YACnF,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,aAAa;gBAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,QAAQ,CAChB,qBAAqB,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI;YAC5C,6BAA6B,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAC7D,CAAC;IACJ,CAAC;IAED,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,QAAQ;QAAE,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACzE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC"}
package/dist/run.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { CommanderError } from "commander";
2
+ /** Wraps a command body so it reports failures as a message plus an exit code. */
3
+ export declare function action<A extends unknown[]>(body: (...args: A) => void | Promise<void>): (...args: A) => Promise<void>;
4
+ /**
5
+ * For a command's `exitOverride`: commander's own usage errors (a required
6
+ * option not given, an unknown option, a missing argument) exit with `code`
7
+ * instead of its default 1, so a command that reserves 1 for a result can
8
+ * keep bad arguments apart from it. Commander has written the message to
9
+ * stderr by the time this runs; help and version exit 0 and are left alone.
10
+ * https://github.com/tj/commander.js#override-exit-and-output-handling
11
+ */
12
+ export declare function usageErrorsExit(code: number): (error: CommanderError) => void;
13
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAIhD,kFAAkF;AAClF,wBAAgB,MAAM,CAAC,CAAC,SAAS,OAAO,EAAE,EACxC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACzC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAS/B;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAI7E"}
package/dist/run.js ADDED
@@ -0,0 +1,38 @@
1
+ /*
2
+ * Copyright 2026 The flow-as-code Authors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ // The one place a command's failure becomes an exit code.
6
+ //
7
+ // Every action is wrapped, so no command can throw past commander: a CliError
8
+ // prints its own message and its own code, anything else prints its message and
9
+ // exits 1. Nothing prints a stack trace, because a stack trace is never the
10
+ // answer to "which file is wrong".
11
+ import { CliError, messageOf } from "./errors.js";
12
+ /** Wraps a command body so it reports failures as a message plus an exit code. */
13
+ export function action(body) {
14
+ return async (...args) => {
15
+ try {
16
+ await body(...args);
17
+ }
18
+ catch (error) {
19
+ console.error(messageOf(error));
20
+ process.exitCode = error instanceof CliError ? error.exitCode : 1;
21
+ }
22
+ };
23
+ }
24
+ /**
25
+ * For a command's `exitOverride`: commander's own usage errors (a required
26
+ * option not given, an unknown option, a missing argument) exit with `code`
27
+ * instead of its default 1, so a command that reserves 1 for a result can
28
+ * keep bad arguments apart from it. Commander has written the message to
29
+ * stderr by the time this runs; help and version exit 0 and are left alone.
30
+ * https://github.com/tj/commander.js#override-exit-and-output-handling
31
+ */
32
+ export function usageErrorsExit(code) {
33
+ return (error) => {
34
+ if (error.exitCode !== 0)
35
+ process.exit(code);
36
+ };
37
+ }
38
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,0DAA0D;AAC1D,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAC5E,mCAAmC;AAInC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAElD,kFAAkF;AAClF,MAAM,UAAU,MAAM,CACpB,IAA0C;IAE1C,OAAO,KAAK,EAAE,GAAG,IAAO,EAAiB,EAAE;QACzC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;YAChC,OAAO,CAAC,QAAQ,GAAG,KAAK,YAAY,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,CAAC,KAAK,EAAE,EAAE;QACf,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { RunOptions, Scenario, SimulationRun } from "@flow-as-code/core";
2
+ import { type LiveClients } from "./aws.js";
3
+ /** Absolute path of the packaged scenario schema (`../schema` from src or dist). */
4
+ export declare const SCENARIO_SCHEMA_PATH: string;
5
+ export interface SimulateOptions {
6
+ instance: string;
7
+ resourceMap?: string;
8
+ format?: string;
9
+ out?: string;
10
+ }
11
+ export interface LoadedScenario {
12
+ /** Absolute path the scenario was read from. */
13
+ path: string;
14
+ scenario: Scenario;
15
+ }
16
+ /**
17
+ * Schema violations for a value that claims to be a scenario, then the
18
+ * cross-field findings for one that passes the schema. Empty when it is valid.
19
+ */
20
+ export declare function scenarioProblems(value: unknown): string[];
21
+ /**
22
+ * Absolute paths of the scenario files a `<scenarios>` argument names. A file
23
+ * contributes itself. A directory contributes every `scenario.json` and
24
+ * `*.scenario.json` in it and in its immediate subdirectories (the layout of
25
+ * conformance/simulate/, one `<case>/scenario.json` per case), sorted by path.
26
+ */
27
+ export declare function resolveScenarioPaths(target: string): string[];
28
+ /** Reads and validates every scenario a `<scenarios>` argument names. */
29
+ export declare function loadScenarios(target: string): LoadedScenario[];
30
+ /**
31
+ * Runs the suite. `timing` exists for the tests, which must not wait on real
32
+ * poll intervals; it is the subset of RunOptions that changes no behaviour.
33
+ */
34
+ export declare function runSimulate(target: string, options: SimulateOptions, clients?: LiveClients, timing?: Pick<RunOptions, "now" | "sleep" | "pollIntervalMs">): Promise<SimulationRun>;
35
+ //# sourceMappingURL=simulate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulate.d.ts","sourceRoot":"","sources":["../src/simulate.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAkB,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAY9F,OAAO,EAAE,KAAK,WAAW,EAAiC,MAAM,UAAU,CAAC;AAI3E,oFAAoF;AACpF,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AAWF,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAYD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAczD;AAMD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CA2B7D;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,CA+B9D;AAWD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,WAAyB,EAClC,MAAM,GAAE,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,OAAO,GAAG,gBAAgB,CAAM,GAChE,OAAO,CAAC,aAAa,CAAC,CAmDxB"}
@@ -0,0 +1,194 @@
1
+ /*
2
+ * Copyright 2026 The flow-as-code Authors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ // `flow-cli simulate <scenarios> --instance <arn> [--resource-map <file>] [--format junit|json] [--out <file>]`.
6
+ //
7
+ // Runs a scenario suite through @flow-as-code/core's runScenarios, which owns the
8
+ // TestCase lifecycle (create published, execute, poll, collect, delete) and the
9
+ // documented limits: 5 concurrent, 100 in flight including the running 5,
10
+ // 5 minutes per scenario.
11
+ // https://docs.aws.amazon.com/connect/latest/adminguide/testing-simulation-execute-test-cases.html
12
+ //
13
+ // Everything that can fail offline fails before the instance is touched, and
14
+ // all at once: every scenario file is schema-validated against the packaged
15
+ // byte copy of conformance/schema/scenario-0.1.schema.json, then checked by
16
+ // @flow-as-code/core's validateScenario for the cross-field rules the schema cannot
17
+ // express, and every ${cdref:...} token is resolved against --resource-map. A
18
+ // suite with one broken scenario creates no test case.
19
+ //
20
+ // The report goes to --out or stdout; failures also go to stderr, one block per
21
+ // scenario that did not pass, so a CI log says why without opening the XML.
22
+ // Exit 0 means every scenario PASSED. Anything else (FAILED, TIMED_OUT,
23
+ // ERRORED, STOPPED) exits 1.
24
+ import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
25
+ import { join, resolve } from "node:path";
26
+ import { fileURLToPath } from "node:url";
27
+ import { compileScenario, jsonReport, junitReport, resolveScenario, runScenarios, validateScenario, } from "@flow-as-code/core";
28
+ import { Ajv2020 } from "ajv/dist/2020.js";
29
+ import { parseInstanceArn, SDK_CLIENTS } from "./aws.js";
30
+ import { readStringMap } from "./docs.js";
31
+ import { CliError, messageOf } from "./errors.js";
32
+ /** Absolute path of the packaged scenario schema (`../schema` from src or dist). */
33
+ export const SCENARIO_SCHEMA_PATH = fileURLToPath(new URL("../schema/scenario-0.1.schema.json", import.meta.url));
34
+ /** The file names a directory argument picks up, matching conformance/simulate/. */
35
+ const SCENARIO_FILE = "scenario.json";
36
+ const SCENARIO_SUFFIX = ".scenario.json";
37
+ const FORMATS = new Set(["junit", "json"]);
38
+ /** Errors reported for one scenario before the rest are elided. */
39
+ const MAX_SCHEMA_ERRORS = 10;
40
+ let compiled;
41
+ function scenarioValidator() {
42
+ if (compiled === undefined) {
43
+ const schema = JSON.parse(readFileSync(SCENARIO_SCHEMA_PATH, "utf8"));
44
+ compiled = new Ajv2020({ allErrors: true, strict: false }).compile(schema);
45
+ }
46
+ return compiled;
47
+ }
48
+ /**
49
+ * Schema violations for a value that claims to be a scenario, then the
50
+ * cross-field findings for one that passes the schema. Empty when it is valid.
51
+ */
52
+ export function scenarioProblems(value) {
53
+ const validate = scenarioValidator();
54
+ if (!validate(value)) {
55
+ const errors = validate.errors ?? [];
56
+ const shown = errors.slice(0, MAX_SCHEMA_ERRORS).map((error) => {
57
+ const where = error.instancePath === "" ? "(root)" : error.instancePath;
58
+ return `${where} ${error.message ?? "is invalid"}`;
59
+ });
60
+ if (errors.length > MAX_SCHEMA_ERRORS) {
61
+ shown.push(`... and ${String(errors.length - MAX_SCHEMA_ERRORS)} more`);
62
+ }
63
+ return shown;
64
+ }
65
+ return validateScenario(value).map((finding) => `${finding.path}: ${finding.message}`);
66
+ }
67
+ function isScenarioFile(name) {
68
+ return name === SCENARIO_FILE || name.endsWith(SCENARIO_SUFFIX);
69
+ }
70
+ /**
71
+ * Absolute paths of the scenario files a `<scenarios>` argument names. A file
72
+ * contributes itself. A directory contributes every `scenario.json` and
73
+ * `*.scenario.json` in it and in its immediate subdirectories (the layout of
74
+ * conformance/simulate/, one `<case>/scenario.json` per case), sorted by path.
75
+ */
76
+ export function resolveScenarioPaths(target) {
77
+ const abs = resolve(target);
78
+ let stat;
79
+ try {
80
+ stat = statSync(abs);
81
+ }
82
+ catch {
83
+ throw new CliError(`No such file or directory: ${abs}`);
84
+ }
85
+ if (!stat.isDirectory())
86
+ return [abs];
87
+ const found = [];
88
+ for (const entry of readdirSync(abs, { withFileTypes: true })) {
89
+ const path = join(abs, entry.name);
90
+ if (entry.isDirectory()) {
91
+ for (const inner of readdirSync(path)) {
92
+ if (isScenarioFile(inner))
93
+ found.push(join(path, inner));
94
+ }
95
+ }
96
+ else if (isScenarioFile(entry.name)) {
97
+ found.push(path);
98
+ }
99
+ }
100
+ if (found.length === 0) {
101
+ throw new CliError(`No ${SCENARIO_FILE} or *${SCENARIO_SUFFIX} files in ${abs} or its subdirectories`);
102
+ }
103
+ return found.sort();
104
+ }
105
+ /** Reads and validates every scenario a `<scenarios>` argument names. */
106
+ export function loadScenarios(target) {
107
+ const problems = [];
108
+ const loaded = [];
109
+ for (const path of resolveScenarioPaths(target)) {
110
+ let raw;
111
+ try {
112
+ raw = readFileSync(path, "utf8");
113
+ }
114
+ catch (error) {
115
+ problems.push(`${path}: ${messageOf(error)}`);
116
+ continue;
117
+ }
118
+ let parsed;
119
+ try {
120
+ parsed = JSON.parse(raw);
121
+ }
122
+ catch (error) {
123
+ problems.push(`${path}: invalid JSON: ${messageOf(error)}`);
124
+ continue;
125
+ }
126
+ const found = scenarioProblems(parsed);
127
+ if (found.length > 0) {
128
+ for (const problem of found)
129
+ problems.push(`${path}: ${problem}`);
130
+ continue;
131
+ }
132
+ loaded.push({ path, scenario: parsed });
133
+ }
134
+ if (problems.length > 0) {
135
+ throw new CliError(`${String(problems.length)} problem(s):\n - ${problems.join("\n - ")}`);
136
+ }
137
+ return loaded;
138
+ }
139
+ /** The lines stderr gets for one scenario that did not pass. */
140
+ function describeFailure(result) {
141
+ const lines = [
142
+ `${result.status}: ${result.name}${result.message === undefined ? "" : `: ${result.message}`}`,
143
+ ];
144
+ for (const detail of result.details ?? [])
145
+ lines.push(` ${detail}`);
146
+ return lines.join("\n");
147
+ }
148
+ /**
149
+ * Runs the suite. `timing` exists for the tests, which must not wait on real
150
+ * poll intervals; it is the subset of RunOptions that changes no behaviour.
151
+ */
152
+ export async function runSimulate(target, options, clients = SDK_CLIENTS, timing = {}) {
153
+ const format = options.format ?? "junit";
154
+ if (!FORMATS.has(format)) {
155
+ throw new CliError(`Unknown --format "${format}". Use "junit" or "json".`);
156
+ }
157
+ const instance = parseInstanceArn(options.instance);
158
+ const scenarios = loadScenarios(target);
159
+ const resourceMap = options.resourceMap === undefined ? {} : readStringMap(options.resourceMap, "resource map");
160
+ // Every unresolved token in the suite, before a single test case exists.
161
+ const unresolved = [];
162
+ for (const { path, scenario } of scenarios) {
163
+ try {
164
+ resolveScenario(compileScenario(scenario), resourceMap);
165
+ }
166
+ catch (error) {
167
+ unresolved.push(`${path}: ${messageOf(error)}`);
168
+ }
169
+ }
170
+ if (unresolved.length > 0) {
171
+ throw new CliError(`${String(unresolved.length)} scenario(s) cannot be resolved` +
172
+ `${options.resourceMap === undefined ? " (no --resource-map given)" : ""}:\n - ` +
173
+ unresolved.join("\n - "));
174
+ }
175
+ const client = await clients.test(instance);
176
+ const run = await runScenarios(scenarios.map((s) => s.scenario), client, { resourceMap, ...timing });
177
+ const report = format === "json" ? jsonReport(run) : junitReport(run);
178
+ if (options.out === undefined) {
179
+ process.stdout.write(report);
180
+ }
181
+ else {
182
+ const out = resolve(options.out);
183
+ writeFileSync(out, report, "utf8");
184
+ console.log(out);
185
+ }
186
+ const notPassed = run.results.filter((result) => result.status !== "PASSED");
187
+ for (const result of notPassed)
188
+ console.error(describeFailure(result));
189
+ if (notPassed.length > 0) {
190
+ throw new CliError(`${String(notPassed.length)} of ${String(run.results.length)} scenario(s) did not pass`);
191
+ }
192
+ return run;
193
+ }
194
+ //# sourceMappingURL=simulate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulate.js","sourceRoot":"","sources":["../src/simulate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { BridgeEvent } from "./bridge/protocol.js";
2
+ import { type StudioServer } from "./bridge/server.js";
3
+ /**
4
+ * Absolute path of the built studio assets. Throws a CliError naming the fix
5
+ * when the package is missing or has not been built.
6
+ */
7
+ export declare function resolveStudioAssets(): string;
8
+ export interface StudioOptions {
9
+ /** Commander passes strings; 0 or absent means "pick a free port". */
10
+ port?: string;
11
+ /** Test seam: skip the asset directory (API only). */
12
+ assetsDir?: string;
13
+ }
14
+ /** One line per bridge event, so the terminal shows the sync happening. */
15
+ export declare function eventLine(event: BridgeEvent, dir: string): string;
16
+ /**
17
+ * Starts the studio server and returns it. The caller keeps the process alive;
18
+ * the command body below waits for a signal.
19
+ */
20
+ export declare function runStudio(dir: string | undefined, options?: StudioOptions): Promise<StudioServer>;
21
+ /** The command body: run until the terminal interrupts it. */
22
+ export declare function studioCommand(dir: string | undefined, options: StudioOptions): Promise<void>;
23
+ //# sourceMappingURL=studio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"studio.d.ts","sourceRoot":"","sources":["../src/studio.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAO1E;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CA+B5C;AAED,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAUjE;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,GAAE,aAAkB,yBAiCnF;AAED,8DAA8D;AAC9D,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC,CASf"}
package/dist/studio.js ADDED
@@ -0,0 +1,120 @@
1
+ /*
2
+ * Copyright 2026 The flow-as-code Authors
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ // `flow-cli studio [dir]`: serve the local visual editor over a directory.
6
+ //
7
+ // The command is thin on purpose. Everything it does is start the bridge in
8
+ // src/bridge/server.ts, point it at the studio's built assets, and print the
9
+ // URL; the interesting behaviour is testable there without a subprocess. It
10
+ // asks the bridge for `ensurePairs`, which writes the missing `<name>.flow.ts`
11
+ // for every document in the directory so that a directory of FlowDocs alone
12
+ // can start the edit-the-builder-file loop, and prints what that wrote.
13
+ //
14
+ // The assets are resolved through the studio package's own exports rather than
15
+ // a relative path, because @flow-as-code/cli is published and installed on its
16
+ // own: in a user's node_modules the studio is a sibling package, not a sibling
17
+ // directory. A studio that is installed but not built is the common local
18
+ // failure and gets its own message naming the command that fixes it.
19
+ import { existsSync } from "node:fs";
20
+ import { createRequire } from "node:module";
21
+ import { basename, dirname, relative, resolve } from "node:path";
22
+ import { PACKAGE_NAMES } from "@flow-as-code/core";
23
+ import { startStudioServer } from "./bridge/server.js";
24
+ import { CliError } from "./errors.js";
25
+ /** The studio's published entry point, resolved through its package exports. */
26
+ const STUDIO_INDEX = `${PACKAGE_NAMES.studio}/dist/index.html`;
27
+ const STUDIO_PACKAGE = `${PACKAGE_NAMES.studio}/package.json`;
28
+ /**
29
+ * Absolute path of the built studio assets. Throws a CliError naming the fix
30
+ * when the package is missing or has not been built.
31
+ */
32
+ export function resolveStudioAssets() {
33
+ const require = createRequire(import.meta.url);
34
+ let packageJson;
35
+ try {
36
+ packageJson = require.resolve(STUDIO_PACKAGE);
37
+ }
38
+ catch {
39
+ throw new CliError(`flow-cli studio needs ${PACKAGE_NAMES.studio}, which is not installed. ` +
40
+ `It ships as a dependency of ${PACKAGE_NAMES.cli}, so this usually means a partial install: ` +
41
+ `run "npm install".`);
42
+ }
43
+ const dist = resolve(dirname(packageJson), "dist");
44
+ if (!existsSync(resolve(dist, "index.html"))) {
45
+ throw new CliError(`The studio has not been built: ${resolve(dist, "index.html")} does not exist. ` +
46
+ `Run "npm run build --workspace ${PACKAGE_NAMES.studio}" (or "npm run build" ` +
47
+ `at the repository root) and try again.`);
48
+ }
49
+ // Resolving the entry through the exports map as well, so a package whose
50
+ // exports stop publishing dist/ fails here rather than serving nothing.
51
+ try {
52
+ require.resolve(STUDIO_INDEX);
53
+ }
54
+ catch {
55
+ throw new CliError(`${PACKAGE_NAMES.studio} does not export ${STUDIO_INDEX.split("/").pop() ?? "dist"}; ` +
56
+ `this flow-cli is not compatible with the installed studio.`);
57
+ }
58
+ return dist;
59
+ }
60
+ /** One line per bridge event, so the terminal shows the sync happening. */
61
+ export function eventLine(event, dir) {
62
+ const where = (path) => relative(dir, path) || path;
63
+ switch (event.kind) {
64
+ case "synced":
65
+ return `synced ${event.name}`;
66
+ case "conflict":
67
+ return `conflict ${event.name}: ${event.reason}`;
68
+ case "error":
69
+ return `error ${where(event.path)}: ${event.message}`;
70
+ }
71
+ }
72
+ /**
73
+ * Starts the studio server and returns it. The caller keeps the process alive;
74
+ * the command body below waits for a signal.
75
+ */
76
+ export async function runStudio(dir, options = {}) {
77
+ const target = resolve(dir ?? process.cwd());
78
+ const port = options.port === undefined ? 0 : Number(options.port);
79
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
80
+ throw new CliError(`--port must be a whole number between 0 and 65535, not "${options.port}".`);
81
+ }
82
+ const assetsDir = options.assetsDir ?? resolveStudioAssets();
83
+ let server;
84
+ try {
85
+ server = await startStudioServer({
86
+ dir: target,
87
+ assetsDir,
88
+ port,
89
+ // A directory of FlowDocs with no builder files beside them cannot start
90
+ // the loop this command exists for, because there is nothing to edit.
91
+ ensurePairs: true,
92
+ onEvent: (event) => console.log(eventLine(event, target)),
93
+ });
94
+ }
95
+ catch (error) {
96
+ throw new CliError(error instanceof Error ? error.message : String(error));
97
+ }
98
+ for (const { tsPath, name } of server.prepared.generated) {
99
+ console.log(`wrote ${basename(tsPath)} from ${name}.flowdoc.json`);
100
+ }
101
+ for (const { name, message } of server.prepared.problems) {
102
+ console.log(`skipped ${name}.flowdoc.json: ${message}`);
103
+ }
104
+ console.log(`flow-cli studio serving ${target}`);
105
+ console.log(` open ${server.url}`);
106
+ console.log(` the bridge listens on ${server.host} only; press Ctrl+C to stop`);
107
+ return server;
108
+ }
109
+ /** The command body: run until the terminal interrupts it. */
110
+ export async function studioCommand(dir, options) {
111
+ const server = await runStudio(dir, options);
112
+ await new Promise((resolveP) => {
113
+ const stop = () => {
114
+ void server.close().then(() => resolveP());
115
+ };
116
+ process.once("SIGINT", stop);
117
+ process.once("SIGTERM", stop);
118
+ });
119
+ }
120
+ //# sourceMappingURL=studio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"studio.js","sourceRoot":"","sources":["../src/studio.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export interface HookData {
2
+ fallbackParentURL?: string;
3
+ flowCorePackage?: string;
4
+ }
5
+ export declare function initialize(data: HookData | undefined): void;
6
+ interface ResolveContext {
7
+ parentURL?: string;
8
+ conditions?: readonly string[];
9
+ importAttributes?: Record<string, string>;
10
+ }
11
+ interface Resolution {
12
+ url: string;
13
+ format?: string | null;
14
+ shortCircuit?: boolean;
15
+ }
16
+ type NextResolve = (specifier: string, context: ResolveContext) => Promise<Resolution>;
17
+ export declare function resolve(specifier: string, context: ResolveContext, nextResolve: NextResolve): Promise<Resolution>;
18
+ export {};
19
+ //# sourceMappingURL=synth-resolve-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synth-resolve-hook.d.ts","sourceRoot":"","sources":["../src/synth-resolve-hook.ts"],"names":[],"mappings":"AA+DA,MAAM,WAAW,QAAQ;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CAG3D;AAED,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAED,UAAU,UAAU;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,KAAK,WAAW,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAiCvF,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,UAAU,CAAC,CA6BrB"}