@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.
- package/LICENSE +202 -0
- package/README.md +446 -0
- package/dist/aws.d.ts +28 -0
- package/dist/aws.d.ts.map +1 -0
- package/dist/aws.js +104 -0
- package/dist/aws.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +132 -0
- package/dist/bin.js.map +1 -0
- package/dist/bridge/exportFiles.d.ts +16 -0
- package/dist/bridge/exportFiles.d.ts.map +1 -0
- package/dist/bridge/exportFiles.js +95 -0
- package/dist/bridge/exportFiles.js.map +1 -0
- package/dist/bridge/pair.d.ts +103 -0
- package/dist/bridge/pair.d.ts.map +1 -0
- package/dist/bridge/pair.js +219 -0
- package/dist/bridge/pair.js.map +1 -0
- package/dist/bridge/protocol.d.ts +187 -0
- package/dist/bridge/protocol.d.ts.map +1 -0
- package/dist/bridge/protocol.js +132 -0
- package/dist/bridge/protocol.js.map +1 -0
- package/dist/bridge/server.d.ts +95 -0
- package/dist/bridge/server.d.ts.map +1 -0
- package/dist/bridge/server.js +669 -0
- package/dist/bridge/server.js.map +1 -0
- package/dist/cdk-scaffold.d.ts +16 -0
- package/dist/cdk-scaffold.d.ts.map +1 -0
- package/dist/cdk-scaffold.js +28 -0
- package/dist/cdk-scaffold.js.map +1 -0
- package/dist/codegen.d.ts +5 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/codegen.js +27 -0
- package/dist/codegen.js.map +1 -0
- package/dist/diff.d.ts +33 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +225 -0
- package/dist/diff.js.map +1 -0
- package/dist/docs.d.ts +46 -0
- package/dist/docs.d.ts.map +1 -0
- package/dist/docs.js +223 -0
- package/dist/docs.js.map +1 -0
- package/dist/emit.d.ts +8 -0
- package/dist/emit.d.ts.map +1 -0
- package/dist/emit.js +55 -0
- package/dist/emit.js.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +25 -0
- package/dist/errors.js.map +1 -0
- package/dist/export.d.ts +27 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +126 -0
- package/dist/export.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/lint.d.ts +6 -0
- package/dist/lint.d.ts.map +1 -0
- package/dist/lint.js +31 -0
- package/dist/lint.js.map +1 -0
- package/dist/render.d.ts +6 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +49 -0
- package/dist/render.js.map +1 -0
- package/dist/run.d.ts +13 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +38 -0
- package/dist/run.js.map +1 -0
- package/dist/simulate.d.ts +35 -0
- package/dist/simulate.d.ts.map +1 -0
- package/dist/simulate.js +194 -0
- package/dist/simulate.js.map +1 -0
- package/dist/studio.d.ts +23 -0
- package/dist/studio.d.ts.map +1 -0
- package/dist/studio.js +120 -0
- package/dist/studio.js.map +1 -0
- package/dist/synth-resolve-hook.d.ts +19 -0
- package/dist/synth-resolve-hook.d.ts.map +1 -0
- package/dist/synth-resolve-hook.js +127 -0
- package/dist/synth-resolve-hook.js.map +1 -0
- package/dist/synth-runner.d.ts +12 -0
- package/dist/synth-runner.d.ts.map +1 -0
- package/dist/synth-runner.js +145 -0
- package/dist/synth-runner.js.map +1 -0
- package/dist/synth.d.ts +55 -0
- package/dist/synth.d.ts.map +1 -0
- package/dist/synth.js +327 -0
- package/dist/synth.js.map +1 -0
- package/dist/watch.d.ts +66 -0
- package/dist/watch.d.ts.map +1 -0
- package/dist/watch.js +293 -0
- package/dist/watch.js.map +1 -0
- package/package.json +85 -0
- package/schema/flowdoc-0.1.schema.json +669 -0
- package/schema/scenario-0.1.schema.json +318 -0
- package/src/aws.ts +149 -0
- package/src/bin.ts +179 -0
- package/src/bridge/exportFiles.ts +116 -0
- package/src/bridge/pair.ts +312 -0
- package/src/bridge/protocol.ts +289 -0
- package/src/bridge/server.ts +827 -0
- package/src/cdk-scaffold.ts +41 -0
- package/src/codegen.ts +36 -0
- package/src/diff.ts +279 -0
- package/src/docs.ts +249 -0
- package/src/emit.ts +69 -0
- package/src/errors.ts +27 -0
- package/src/export.ts +167 -0
- package/src/index.ts +27 -0
- package/src/lint.ts +42 -0
- package/src/render.ts +58 -0
- package/src/run.ts +42 -0
- package/src/simulate.ts +244 -0
- package/src/studio.ts +143 -0
- package/src/synth-resolve-hook.ts +152 -0
- package/src/synth-runner.ts +157 -0
- package/src/synth.ts +380 -0
- package/src/watch.ts +388 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 The flow-as-code Authors
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
// Module-resolution hook for the `flow-cli synth` child process. Two jobs,
|
|
6
|
+
// both aimed at the same thing: a `<name>.flow.ts` sitting in a plain folder
|
|
7
|
+
// of FlowDocs must synth, because that is where `flow-cli codegen` writes it
|
|
8
|
+
// and what `flow-cli studio` re-synths on every save.
|
|
9
|
+
//
|
|
10
|
+
// 1. TypeScript in the builder graph is ESM when nothing says otherwise.
|
|
11
|
+
// Without a package.json above it, the TypeScript loader compiles the file
|
|
12
|
+
// to CommonJS, so its `import { Flow } from "@flow-as-code/core"`
|
|
13
|
+
// becomes a `require` of an ESM-only package, which is not the module
|
|
14
|
+
// system codegen emits for.
|
|
15
|
+
// A directory that does declare a type keeps it: this only fills in a
|
|
16
|
+
// decision nobody made.
|
|
17
|
+
//
|
|
18
|
+
// "Nobody made a decision" is read off the filesystem rather than off the
|
|
19
|
+
// resolver's answer, because runtimes have answered differently for the same
|
|
20
|
+
// plain folder: Node 22 and later return a null `format` for the builder
|
|
21
|
+
// file, while Node 20 returned "commonjs". Trusting a nullish `format` alone
|
|
22
|
+
// therefore fixed the plain-folder case on 22 and left it broken on 20.
|
|
23
|
+
// Node 20 is no longer supported, but the answer is not a contract, so the
|
|
24
|
+
// walk stays: a TypeScript file with no package.json anywhere above it is
|
|
25
|
+
// ESM whatever the resolver said, and one with a package.json above it is
|
|
26
|
+
// left exactly as resolved.
|
|
27
|
+
//
|
|
28
|
+
// 2. @flow-as-code/core resolves from @flow-as-code/cli when it resolves from
|
|
29
|
+
// nowhere else. A folder of FlowDocs has no node_modules, and requiring the
|
|
30
|
+
// user to install @flow-as-code/core beside every such folder to edit a file
|
|
31
|
+
// the CLI generated is not a reasonable price. It is a hard dependency of
|
|
32
|
+
// @flow-as-code/cli, so resolving from the CLI's own module always finds the
|
|
33
|
+
// copy the CLI is running against.
|
|
34
|
+
//
|
|
35
|
+
// The fallback runs after the default resolver, never before it, so a copy
|
|
36
|
+
// installed next to the builder file still wins and version skew stays the
|
|
37
|
+
// user's choice. It is scoped to @flow-as-code/core alone: a builder file
|
|
38
|
+
// cannot use it to reach the rest of the CLI's dependency tree.
|
|
39
|
+
//
|
|
40
|
+
// Nothing about the sandbox changes. The child keeps its stripped environment,
|
|
41
|
+
// its pinned working directory, and the permission flags synth.ts passes; the
|
|
42
|
+
// fallback only reads a file the child could already read under
|
|
43
|
+
// `--allow-fs-read=*`. See SECURITY.md.
|
|
44
|
+
//
|
|
45
|
+
// Loaded on the module-customization hooks thread by `module.register`
|
|
46
|
+
// (https://nodejs.org/api/module.html#customization-hooks), which is why the
|
|
47
|
+
// fallback target arrives through `initialize` rather than a module-level
|
|
48
|
+
// import.
|
|
49
|
+
import { existsSync } from "node:fs";
|
|
50
|
+
import { dirname, join } from "node:path";
|
|
51
|
+
import { fileURLToPath } from "node:url";
|
|
52
|
+
/** Module URL whose resolution scope holds the CLI's own @flow-as-code/core. */
|
|
53
|
+
let fallbackParentURL;
|
|
54
|
+
/**
|
|
55
|
+
* @flow-as-code/core's package name, from PACKAGE_NAMES rather than a literal: the npm
|
|
56
|
+
* scope is written down in exactly one module (packages/core/src/package-names.ts)
|
|
57
|
+
* and this file runs on a thread that should not import it to find out.
|
|
58
|
+
*/
|
|
59
|
+
let flowCorePackage;
|
|
60
|
+
export function initialize(data) {
|
|
61
|
+
fallbackParentURL = data?.fallbackParentURL;
|
|
62
|
+
flowCorePackage = data?.flowCorePackage;
|
|
63
|
+
}
|
|
64
|
+
function isFlowCore(specifier) {
|
|
65
|
+
if (flowCorePackage === undefined)
|
|
66
|
+
return false;
|
|
67
|
+
return specifier === flowCorePackage || specifier.startsWith(`${flowCorePackage}/`);
|
|
68
|
+
}
|
|
69
|
+
function isTypeScript(url) {
|
|
70
|
+
return url.startsWith("file:") && (url.endsWith(".ts") || url.endsWith(".tsx"));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* True when no package.json exists in the file's directory or any directory
|
|
74
|
+
* above it, so nothing has declared a module type for it. This is the same walk
|
|
75
|
+
* node itself does to answer that question
|
|
76
|
+
* (https://nodejs.org/api/packages.html#determining-module-system); doing it
|
|
77
|
+
* here is what makes the answer the same whatever the resolver reports.
|
|
78
|
+
*/
|
|
79
|
+
function nothingDeclaredATypeFor(url) {
|
|
80
|
+
let dir;
|
|
81
|
+
try {
|
|
82
|
+
dir = dirname(fileURLToPath(url));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
for (;;) {
|
|
88
|
+
if (existsSync(join(dir, "package.json")))
|
|
89
|
+
return false;
|
|
90
|
+
const parent = dirname(dir);
|
|
91
|
+
if (parent === dir)
|
|
92
|
+
return true;
|
|
93
|
+
dir = parent;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export async function resolve(specifier, context, nextResolve) {
|
|
97
|
+
let resolution;
|
|
98
|
+
try {
|
|
99
|
+
resolution = await nextResolve(specifier, context);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
// Only a specifier that could not be found at all, and only
|
|
103
|
+
// @flow-as-code/core: an export map violation or a syntax error in the
|
|
104
|
+
// installed copy is the user's problem to see, not something to paper
|
|
105
|
+
// over with a second copy.
|
|
106
|
+
if (fallbackParentURL === undefined)
|
|
107
|
+
throw error;
|
|
108
|
+
if (!isFlowCore(specifier))
|
|
109
|
+
throw error;
|
|
110
|
+
if (error.code !== "ERR_MODULE_NOT_FOUND")
|
|
111
|
+
throw error;
|
|
112
|
+
resolution = await nextResolve(specifier, { ...context, parentURL: fallbackParentURL });
|
|
113
|
+
}
|
|
114
|
+
// A nullish `format` (Node 22 and later) or a defaulted "commonjs" (the
|
|
115
|
+
// answer Node 20 gave) on a TypeScript file with no package.json above it is
|
|
116
|
+
// the plain-folder case described above, and nothing else. Any other format
|
|
117
|
+
// is a decision somebody made and is left alone.
|
|
118
|
+
if (isTypeScript(resolution.url) &&
|
|
119
|
+
(resolution.format === null ||
|
|
120
|
+
resolution.format === undefined ||
|
|
121
|
+
resolution.format === "commonjs") &&
|
|
122
|
+
nothingDeclaredATypeFor(resolution.url)) {
|
|
123
|
+
return { ...resolution, format: "module" };
|
|
124
|
+
}
|
|
125
|
+
return resolution;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=synth-resolve-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synth-resolve-hook.js","sourceRoot":"","sources":["../src/synth-resolve-hook.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,2EAA2E;AAC3E,6EAA6E;AAC7E,6EAA6E;AAC7E,sDAAsD;AACtD,EAAE;AACF,yEAAyE;AACzE,8EAA8E;AAC9E,qEAAqE;AACrE,yEAAyE;AACzE,+BAA+B;AAC/B,yEAAyE;AACzE,2BAA2B;AAC3B,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,4EAA4E;AAC5E,gFAAgF;AAChF,2EAA2E;AAC3E,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,+BAA+B;AAC/B,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,gFAAgF;AAChF,6EAA6E;AAC7E,gFAAgF;AAChF,sCAAsC;AACtC,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,0EAA0E;AAC1E,gEAAgE;AAChE,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,gEAAgE;AAChE,wCAAwC;AACxC,EAAE;AACF,uEAAuE;AACvE,6EAA6E;AAC7E,0EAA0E;AAC1E,UAAU;AAEV,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,gFAAgF;AAChF,IAAI,iBAAqC,CAAC;AAE1C;;;;GAIG;AACH,IAAI,eAAmC,CAAC;AAOxC,MAAM,UAAU,UAAU,CAAC,IAA0B;IACnD,iBAAiB,GAAG,IAAI,EAAE,iBAAiB,CAAC;IAC5C,eAAe,GAAG,IAAI,EAAE,eAAe,CAAC;AAC1C,CAAC;AAgBD,SAAS,UAAU,CAAC,SAAiB;IACnC,IAAI,eAAe,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAChD,OAAO,SAAS,KAAK,eAAe,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,eAAe,GAAG,CAAC,CAAC;AACtF,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAC/B,OAAO,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AAClF,CAAC;AAED;;;;;;GAMG;AACH,SAAS,uBAAuB,CAAC,GAAW;IAC1C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,SAAS,CAAC;QACR,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QACxD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAChC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,SAAiB,EACjB,OAAuB,EACvB,WAAwB;IAExB,IAAI,UAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,4DAA4D;QAC5D,uEAAuE;QACvE,sEAAsE;QACtE,2BAA2B;QAC3B,IAAI,iBAAiB,KAAK,SAAS;YAAE,MAAM,KAAK,CAAC;QACjD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,MAAM,KAAK,CAAC;QACxC,IAAK,KAA+B,CAAC,IAAI,KAAK,sBAAsB;YAAE,MAAM,KAAK,CAAC;QAClF,UAAU,GAAG,MAAM,WAAW,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC1F,CAAC;IAED,wEAAwE;IACxE,6EAA6E;IAC7E,4EAA4E;IAC5E,iDAAiD;IACjD,IACE,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC;QAC5B,CAAC,UAAU,CAAC,MAAM,KAAK,IAAI;YACzB,UAAU,CAAC,MAAM,KAAK,SAAS;YAC/B,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;QACnC,uBAAuB,CAAC,UAAU,CAAC,GAAG,CAAC,EACvC,CAAC;QACD,OAAO,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC7C,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const SYNTH_ENVELOPE_SENTINEL = "__FLOW_CLI_SYNTH_ENVELOPE_V1__";
|
|
2
|
+
export interface SynthEnvelope {
|
|
3
|
+
flows?: {
|
|
4
|
+
name: string;
|
|
5
|
+
doc: unknown;
|
|
6
|
+
}[];
|
|
7
|
+
error?: {
|
|
8
|
+
message: string;
|
|
9
|
+
stack?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=synth-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synth-runner.d.ts","sourceRoot":"","sources":["../src/synth-runner.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,uBAAuB,mCAAmC,CAAC;AAExE,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IACzC,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 The flow-as-code Authors
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
// Child-process entry point for `flow-cli synth`. Never imported by the
|
|
6
|
+
// parent: it is spawned (see synth.ts) so that evaluating an untrusted
|
|
7
|
+
// builder file cannot touch the CLI process. Runs under tsx (--import), so
|
|
8
|
+
// this file works both compiled (dist/synth-runner.js) and straight from
|
|
9
|
+
// source in tests (src/synth-runner.ts).
|
|
10
|
+
//
|
|
11
|
+
// Contract with the parent: argv[2] is the absolute path of the builder file,
|
|
12
|
+
// argv[3] the path of the resolution hook module, and argv[4] the module URL
|
|
13
|
+
// that hook resolves @flow-as-code/core from when the builder file's own directory
|
|
14
|
+
// cannot. The runner prints exactly one envelope line to stdout, prefixed with
|
|
15
|
+
// SYNTH_ENVELOPE_SENTINEL, so user code writing to stdout during module
|
|
16
|
+
// evaluation cannot corrupt the channel. Everything else on stdout is ignored
|
|
17
|
+
// by the parent.
|
|
18
|
+
import { register } from "node:module";
|
|
19
|
+
import { pathToFileURL } from "node:url";
|
|
20
|
+
import { PACKAGE_NAMES, synth } from "@flow-as-code/core";
|
|
21
|
+
export const SYNTH_ENVELOPE_SENTINEL = "__FLOW_CLI_SYNTH_ENVELOPE_V1__";
|
|
22
|
+
/**
|
|
23
|
+
* Structural check instead of `instanceof Flow`: the builder file may resolve
|
|
24
|
+
* its own copy of @flow-as-code/core (dual-package or version skew), and a Flow from
|
|
25
|
+
* that copy is still a Flow for our purposes.
|
|
26
|
+
*/
|
|
27
|
+
function isFlowLike(value) {
|
|
28
|
+
if (typeof value !== "object" || value === null)
|
|
29
|
+
return false;
|
|
30
|
+
const v = value;
|
|
31
|
+
return (typeof v.name === "string" &&
|
|
32
|
+
(v.kind === "flow" || v.kind === "module") &&
|
|
33
|
+
typeof v.all === "function" &&
|
|
34
|
+
typeof v.startId === "function");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Convention: every exported Flow instance, plus the result of every exported
|
|
38
|
+
* zero-argument function that returns one. Functions that throw when called
|
|
39
|
+
* (including classes, which throw without `new`) are skipped; a throw during
|
|
40
|
+
* module evaluation itself is a hard error and is reported by main().
|
|
41
|
+
*/
|
|
42
|
+
function collectFlows(mod) {
|
|
43
|
+
const flows = [];
|
|
44
|
+
const seen = new Set();
|
|
45
|
+
const push = (f) => {
|
|
46
|
+
if (!seen.has(f)) {
|
|
47
|
+
seen.add(f);
|
|
48
|
+
flows.push(f);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
for (const value of Object.values(mod)) {
|
|
52
|
+
if (isFlowLike(value)) {
|
|
53
|
+
push(value);
|
|
54
|
+
}
|
|
55
|
+
else if (typeof value === "function" && value.length === 0) {
|
|
56
|
+
let result;
|
|
57
|
+
try {
|
|
58
|
+
result = value();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
continue; // not a flow factory (e.g. a class, or a helper that needs setup)
|
|
62
|
+
}
|
|
63
|
+
if (isFlowLike(result))
|
|
64
|
+
push(result);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return flows;
|
|
68
|
+
}
|
|
69
|
+
function emit(envelope) {
|
|
70
|
+
process.stdout.write("\n" + SYNTH_ENVELOPE_SENTINEL + JSON.stringify(envelope) + "\n");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Registers the @flow-as-code/core resolution fallback before the builder file is
|
|
74
|
+
* imported. See synth-resolve-hook.ts for what it does and why it is safe.
|
|
75
|
+
*
|
|
76
|
+
* `module.register` is the only registration API present on every Node this
|
|
77
|
+
* package supports (`registerHooks` landed in 22.15). Its deprecation warning
|
|
78
|
+
* on newer runtimes would otherwise reach the user inside a synth failure
|
|
79
|
+
* message, which is why it is silenced for the length of the call and no
|
|
80
|
+
* longer.
|
|
81
|
+
*/
|
|
82
|
+
function registerResolveFallback(hookPath, fallbackParentURL) {
|
|
83
|
+
const { noDeprecation } = process;
|
|
84
|
+
process.noDeprecation = true;
|
|
85
|
+
try {
|
|
86
|
+
register(pathToFileURL(hookPath).href, {
|
|
87
|
+
parentURL: import.meta.url,
|
|
88
|
+
data: { fallbackParentURL, flowCorePackage: PACKAGE_NAMES.core },
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
process.noDeprecation = noDeprecation;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function main() {
|
|
96
|
+
const sourcePath = process.argv[2];
|
|
97
|
+
if (sourcePath === undefined) {
|
|
98
|
+
emit({ error: { message: "synth-runner: missing source file argument" } });
|
|
99
|
+
process.exitCode = 1;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const hookPath = process.argv[3];
|
|
103
|
+
const fallbackParentURL = process.argv[4];
|
|
104
|
+
if (hookPath !== undefined && fallbackParentURL !== undefined) {
|
|
105
|
+
registerResolveFallback(hookPath, fallbackParentURL);
|
|
106
|
+
}
|
|
107
|
+
let mod;
|
|
108
|
+
try {
|
|
109
|
+
mod = (await import(pathToFileURL(sourcePath).href));
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
113
|
+
emit({ error: { message: err.message, stack: err.stack } });
|
|
114
|
+
process.exitCode = 1;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const flows = collectFlows(mod);
|
|
118
|
+
const byName = new Map();
|
|
119
|
+
for (const flow of flows) {
|
|
120
|
+
if (byName.has(flow.name)) {
|
|
121
|
+
emit({
|
|
122
|
+
error: {
|
|
123
|
+
message: `Two exported flows share the name "${flow.name}". Flow names must be unique per file because each becomes <name>.flowdoc.json.`,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
byName.set(flow.name, flow);
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
// meta is stamped by the parent (generator + sourceHash of the file it
|
|
133
|
+
// actually read), so the runner synths without one.
|
|
134
|
+
emit({
|
|
135
|
+
flows: flows.map((f) => ({ name: f.name, doc: synth(f, { includeMeta: false }) })),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
140
|
+
emit({ error: { message: err.message, stack: err.stack } });
|
|
141
|
+
process.exitCode = 1;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
void main();
|
|
145
|
+
//# sourceMappingURL=synth-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synth-runner.js","sourceRoot":"","sources":["../src/synth-runner.ts"],"names":[],"mappings":"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"}
|
package/dist/synth.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { FlowDoc } from "@flow-as-code/core";
|
|
2
|
+
/** Recorded in meta.generator of every FlowDoc this CLI writes. */
|
|
3
|
+
export declare const GENERATOR = "cli@0.0.1";
|
|
4
|
+
/** A synth failure with a message meant for the terminal. */
|
|
5
|
+
export declare class SynthError extends Error {
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The frames of a thrown value's stack that point at the user's own code.
|
|
9
|
+
*
|
|
10
|
+
* A syntax error in a builder file used to print the actionable line and then
|
|
11
|
+
* ten frames of esbuild and node stream internals under it
|
|
12
|
+
* (`at failureErrorWithLog (.../node_modules/esbuild/lib/main.js:1752:15)`,
|
|
13
|
+
* `at Pipe.onStreamRead (node:internal/stream_base_commons:191:23)`), which buries
|
|
14
|
+
* the file, line and column that the message above already names. The studio's
|
|
15
|
+
* badge shows only the first line for the same reason; the terminal now matches
|
|
16
|
+
* that intent.
|
|
17
|
+
*
|
|
18
|
+
* Frames inside node_modules and node's own internals are dropped, and so are
|
|
19
|
+
* the header lines the stack repeats from the message that is printed above it.
|
|
20
|
+
* A frame in the builder file or anywhere else the user wrote survives, because
|
|
21
|
+
* that is the one a runtime error in a flow needs. There is no verbose flag or
|
|
22
|
+
* env var in this CLI to hide the full stack behind, so what is dropped here is
|
|
23
|
+
* dropped: the retained frames plus the message carry the location.
|
|
24
|
+
*
|
|
25
|
+
* `builderPath` is the file being synthesized. Its own frames are kept even
|
|
26
|
+
* when it sits under a directory named node_modules, which a vendored or
|
|
27
|
+
* linked builder file legitimately does: the node_modules rule is about
|
|
28
|
+
* dependency code, and the file the user asked to run is never that. Without
|
|
29
|
+
* the exemption such a file reports a throw with no line or column at all.
|
|
30
|
+
*/
|
|
31
|
+
export declare function actionableFrames(stack: string | undefined, builderPath?: string): string[];
|
|
32
|
+
export interface SynthesizedFlow {
|
|
33
|
+
name: string;
|
|
34
|
+
doc: FlowDoc;
|
|
35
|
+
}
|
|
36
|
+
export interface SynthFileResult {
|
|
37
|
+
flows: SynthesizedFlow[];
|
|
38
|
+
/** `sha256:<hex>` of the builder file bytes, ready for meta.sourceHash. */
|
|
39
|
+
sourceHash: string;
|
|
40
|
+
}
|
|
41
|
+
export declare function sha256Hex(bytes: Buffer | string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Evaluates the builder file in the sandboxed child and returns its flows.
|
|
44
|
+
* Throws SynthError with an actionable message on any failure.
|
|
45
|
+
*/
|
|
46
|
+
export declare function synthFile(sourcePath: string): Promise<SynthFileResult>;
|
|
47
|
+
/** Serializes a doc with the meta this CLI stamps. Byte-stable. */
|
|
48
|
+
export declare function serializeWithMeta(doc: FlowDoc, sourceHash: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* The `flow-cli synth` command body: synth every flow the file exports and
|
|
51
|
+
* write <flow.name>.flowdoc.json into outDir (default: the source file's
|
|
52
|
+
* directory). Returns the written paths in write order.
|
|
53
|
+
*/
|
|
54
|
+
export declare function synthToFiles(sourcePath: string, outDir?: string): Promise<string[]>;
|
|
55
|
+
//# sourceMappingURL=synth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synth.d.ts","sourceRoot":"","sources":["../src/synth.ts"],"names":[],"mappings":"AAmDA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAGlD,mEAAmE;AACnE,eAAO,MAAM,SAAS,cAAc,CAAC;AAIrC,6DAA6D;AAC7D,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAoDxC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CA2B1F;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAExD;AAiFD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAoF5E;AAED,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAoBzF"}
|