@walkeros/cli 0.0.0-next-20251219153324
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/CHANGELOG.md +175 -0
- package/README.md +531 -0
- package/dist/__tests__/bundle/bundler-helpers.test.d.ts +2 -0
- package/dist/__tests__/bundle/bundler-helpers.test.d.ts.map +1 -0
- package/dist/__tests__/bundle/bundler-helpers.test.js +151 -0
- package/dist/__tests__/bundle/bundler-helpers.test.js.map +1 -0
- package/dist/__tests__/bundle/bundler.test.d.ts +2 -0
- package/dist/__tests__/bundle/bundler.test.d.ts.map +1 -0
- package/dist/__tests__/bundle/bundler.test.js +352 -0
- package/dist/__tests__/bundle/bundler.test.js.map +1 -0
- package/dist/__tests__/bundle/programmatic.test.d.ts +2 -0
- package/dist/__tests__/bundle/programmatic.test.d.ts.map +1 -0
- package/dist/__tests__/bundle/programmatic.test.js +148 -0
- package/dist/__tests__/bundle/programmatic.test.js.map +1 -0
- package/dist/__tests__/cli-e2e.test.d.ts +8 -0
- package/dist/__tests__/cli-e2e.test.d.ts.map +1 -0
- package/dist/__tests__/cli-e2e.test.js +145 -0
- package/dist/__tests__/cli-e2e.test.js.map +1 -0
- package/dist/__tests__/cli.test.d.ts +2 -0
- package/dist/__tests__/cli.test.d.ts.map +1 -0
- package/dist/__tests__/cli.test.js +179 -0
- package/dist/__tests__/cli.test.js.map +1 -0
- package/dist/__tests__/config-loader.test.d.ts +7 -0
- package/dist/__tests__/config-loader.test.d.ts.map +1 -0
- package/dist/__tests__/config-loader.test.js +414 -0
- package/dist/__tests__/config-loader.test.js.map +1 -0
- package/dist/__tests__/core/asset-resolver.test.d.ts +2 -0
- package/dist/__tests__/core/asset-resolver.test.d.ts.map +1 -0
- package/dist/__tests__/core/asset-resolver.test.js +14 -0
- package/dist/__tests__/core/asset-resolver.test.js.map +1 -0
- package/dist/__tests__/core/build-cache.test.d.ts +2 -0
- package/dist/__tests__/core/build-cache.test.d.ts.map +1 -0
- package/dist/__tests__/core/build-cache.test.js +55 -0
- package/dist/__tests__/core/build-cache.test.js.map +1 -0
- package/dist/__tests__/core/cache-utils.test.d.ts +2 -0
- package/dist/__tests__/core/cache-utils.test.d.ts.map +1 -0
- package/dist/__tests__/core/cache-utils.test.js +70 -0
- package/dist/__tests__/core/cache-utils.test.js.map +1 -0
- package/dist/__tests__/core/config.test.d.ts +2 -0
- package/dist/__tests__/core/config.test.d.ts.map +1 -0
- package/dist/__tests__/core/config.test.js +79 -0
- package/dist/__tests__/core/config.test.js.map +1 -0
- package/dist/__tests__/core/logger.test.d.ts +2 -0
- package/dist/__tests__/core/logger.test.d.ts.map +1 -0
- package/dist/__tests__/core/logger.test.js +53 -0
- package/dist/__tests__/core/logger.test.js.map +1 -0
- package/dist/__tests__/integration/bundle-run.integration.test.d.ts +8 -0
- package/dist/__tests__/integration/bundle-run.integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/bundle-run.integration.test.js +54 -0
- package/dist/__tests__/integration/bundle-run.integration.test.js.map +1 -0
- package/dist/__tests__/push/push.test.d.ts +7 -0
- package/dist/__tests__/push/push.test.d.ts.map +1 -0
- package/dist/__tests__/push/push.test.js +197 -0
- package/dist/__tests__/push/push.test.js.map +1 -0
- package/dist/__tests__/simulate/env-loader.test.d.ts +2 -0
- package/dist/__tests__/simulate/env-loader.test.d.ts.map +1 -0
- package/dist/__tests__/simulate/env-loader.test.js +47 -0
- package/dist/__tests__/simulate/env-loader.test.js.map +1 -0
- package/dist/__tests__/simulate/node-executor.test.d.ts +5 -0
- package/dist/__tests__/simulate/node-executor.test.d.ts.map +1 -0
- package/dist/__tests__/simulate/node-executor.test.js +25 -0
- package/dist/__tests__/simulate/node-executor.test.js.map +1 -0
- package/dist/__tests__/simulate/server-simulate.integration.test.d.ts +5 -0
- package/dist/__tests__/simulate/server-simulate.integration.test.d.ts.map +1 -0
- package/dist/__tests__/simulate/server-simulate.integration.test.js +58 -0
- package/dist/__tests__/simulate/server-simulate.integration.test.js.map +1 -0
- package/dist/__tests__/smoke/production.smoke.test.d.ts +8 -0
- package/dist/__tests__/smoke/production.smoke.test.d.ts.map +1 -0
- package/dist/__tests__/smoke/production.smoke.test.js +65 -0
- package/dist/__tests__/smoke/production.smoke.test.js.map +1 -0
- package/dist/commands/bundle/bundler.d.ts +32 -0
- package/dist/commands/bundle/bundler.d.ts.map +1 -0
- package/dist/commands/bundle/bundler.js +564 -0
- package/dist/commands/bundle/bundler.js.map +1 -0
- package/dist/commands/bundle/index.d.ts +59 -0
- package/dist/commands/bundle/index.d.ts.map +1 -0
- package/dist/commands/bundle/index.js +192 -0
- package/dist/commands/bundle/index.js.map +1 -0
- package/dist/commands/bundle/package-manager.d.ts +8 -0
- package/dist/commands/bundle/package-manager.d.ts.map +1 -0
- package/dist/commands/bundle/package-manager.js +208 -0
- package/dist/commands/bundle/package-manager.js.map +1 -0
- package/dist/commands/bundle/stats.d.ts +23 -0
- package/dist/commands/bundle/stats.d.ts.map +1 -0
- package/dist/commands/bundle/stats.js +52 -0
- package/dist/commands/bundle/stats.js.map +1 -0
- package/dist/commands/cache.d.ts +7 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +61 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/push/index.d.ts +7 -0
- package/dist/commands/push/index.d.ts.map +1 -0
- package/dist/commands/push/index.js +250 -0
- package/dist/commands/push/index.js.map +1 -0
- package/dist/commands/push/types.d.ts +22 -0
- package/dist/commands/push/types.d.ts.map +1 -0
- package/dist/commands/push/types.js +2 -0
- package/dist/commands/push/types.js.map +1 -0
- package/dist/commands/run/__tests__/run.integration.test.d.ts +7 -0
- package/dist/commands/run/__tests__/run.integration.test.d.ts.map +1 -0
- package/dist/commands/run/__tests__/run.integration.test.js +51 -0
- package/dist/commands/run/__tests__/run.integration.test.js.map +1 -0
- package/dist/commands/run/__tests__/validators.test.d.ts +2 -0
- package/dist/commands/run/__tests__/validators.test.d.ts.map +1 -0
- package/dist/commands/run/__tests__/validators.test.js +80 -0
- package/dist/commands/run/__tests__/validators.test.js.map +1 -0
- package/dist/commands/run/execution.d.ts +14 -0
- package/dist/commands/run/execution.d.ts.map +1 -0
- package/dist/commands/run/execution.js +41 -0
- package/dist/commands/run/execution.js.map +1 -0
- package/dist/commands/run/index.d.ts +38 -0
- package/dist/commands/run/index.d.ts.map +1 -0
- package/dist/commands/run/index.js +165 -0
- package/dist/commands/run/index.js.map +1 -0
- package/dist/commands/run/types.d.ts +65 -0
- package/dist/commands/run/types.d.ts.map +1 -0
- package/dist/commands/run/types.js +7 -0
- package/dist/commands/run/types.js.map +1 -0
- package/dist/commands/run/utils.d.ts +29 -0
- package/dist/commands/run/utils.d.ts.map +1 -0
- package/dist/commands/run/utils.js +52 -0
- package/dist/commands/run/utils.js.map +1 -0
- package/dist/commands/run/validators.d.ts +33 -0
- package/dist/commands/run/validators.d.ts.map +1 -0
- package/dist/commands/run/validators.js +58 -0
- package/dist/commands/run/validators.js.map +1 -0
- package/dist/commands/simulate/env-loader.d.ts +19 -0
- package/dist/commands/simulate/env-loader.d.ts.map +1 -0
- package/dist/commands/simulate/env-loader.js +46 -0
- package/dist/commands/simulate/env-loader.js.map +1 -0
- package/dist/commands/simulate/index.d.ts +48 -0
- package/dist/commands/simulate/index.d.ts.map +1 -0
- package/dist/commands/simulate/index.js +114 -0
- package/dist/commands/simulate/index.js.map +1 -0
- package/dist/commands/simulate/jsdom-executor.d.ts +37 -0
- package/dist/commands/simulate/jsdom-executor.d.ts.map +1 -0
- package/dist/commands/simulate/jsdom-executor.js +137 -0
- package/dist/commands/simulate/jsdom-executor.js.map +1 -0
- package/dist/commands/simulate/node-executor.d.ts +28 -0
- package/dist/commands/simulate/node-executor.d.ts.map +1 -0
- package/dist/commands/simulate/node-executor.js +94 -0
- package/dist/commands/simulate/node-executor.js.map +1 -0
- package/dist/commands/simulate/simulator.d.ts +14 -0
- package/dist/commands/simulate/simulator.d.ts.map +1 -0
- package/dist/commands/simulate/simulator.js +163 -0
- package/dist/commands/simulate/simulator.js.map +1 -0
- package/dist/commands/simulate/tracker.d.ts +30 -0
- package/dist/commands/simulate/tracker.d.ts.map +1 -0
- package/dist/commands/simulate/tracker.js +96 -0
- package/dist/commands/simulate/tracker.js.map +1 -0
- package/dist/commands/simulate/types.d.ts +20 -0
- package/dist/commands/simulate/types.d.ts.map +1 -0
- package/dist/commands/simulate/types.js +2 -0
- package/dist/commands/simulate/types.js.map +1 -0
- package/dist/config/build-defaults.d.ts +49 -0
- package/dist/config/build-defaults.d.ts.map +1 -0
- package/dist/config/build-defaults.js +68 -0
- package/dist/config/build-defaults.js.map +1 -0
- package/dist/config/index.d.ts +13 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +15 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +81 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +153 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/utils.d.ts +101 -0
- package/dist/config/utils.d.ts.map +1 -0
- package/dist/config/utils.js +235 -0
- package/dist/config/utils.js.map +1 -0
- package/dist/config/validators.d.ts +52 -0
- package/dist/config/validators.d.ts.map +1 -0
- package/dist/config/validators.js +85 -0
- package/dist/config/validators.js.map +1 -0
- package/dist/core/asset-resolver.d.ts +34 -0
- package/dist/core/asset-resolver.d.ts.map +1 -0
- package/dist/core/asset-resolver.js +70 -0
- package/dist/core/asset-resolver.js.map +1 -0
- package/dist/core/build-cache.d.ts +23 -0
- package/dist/core/build-cache.d.ts.map +1 -0
- package/dist/core/build-cache.js +44 -0
- package/dist/core/build-cache.js.map +1 -0
- package/dist/core/cache-utils.d.ts +27 -0
- package/dist/core/cache-utils.d.ts.map +1 -0
- package/dist/core/cache-utils.js +60 -0
- package/dist/core/cache-utils.js.map +1 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +8 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/local-packages.d.ts +19 -0
- package/dist/core/local-packages.d.ts.map +1 -0
- package/dist/core/local-packages.js +60 -0
- package/dist/core/local-packages.js.map +1 -0
- package/dist/core/logger.d.ts +28 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +88 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/output.d.ts +30 -0
- package/dist/core/output.d.ts.map +1 -0
- package/dist/core/output.js +46 -0
- package/dist/core/output.js.map +1 -0
- package/dist/core/timer.d.ts +14 -0
- package/dist/core/timer.d.ts.map +1 -0
- package/dist/core/timer.js +29 -0
- package/dist/core/timer.js.map +1 -0
- package/dist/core/tmp.d.ts +27 -0
- package/dist/core/tmp.d.ts.map +1 -0
- package/dist/core/tmp.js +36 -0
- package/dist/core/tmp.js.map +1 -0
- package/dist/core/utils.d.ts +10 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +12 -0
- package/dist/core/utils.js.map +1 -0
- package/dist/examples/README.md +355 -0
- package/dist/examples/event.json +53 -0
- package/dist/examples/flow-order-complete.json +67 -0
- package/dist/examples/flow-simple.json +31 -0
- package/dist/examples/flow.json +82 -0
- package/dist/examples/server-collect.json +60 -0
- package/dist/examples/server-collect.mjs +13540 -0
- package/dist/examples/test.html +43 -0
- package/dist/examples/web-serve.js +25503 -0
- package/dist/examples/web-serve.json +74 -0
- package/dist/index.d.ts +427 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2629 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/index.d.ts +10 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +10 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/main.d.ts +2 -0
- package/dist/runtime/main.d.ts.map +1 -0
- package/dist/runtime/main.js +269 -0
- package/dist/runtime/main.js.map +1 -0
- package/dist/runtime/runner.d.ts +20 -0
- package/dist/runtime/runner.d.ts.map +1 -0
- package/dist/runtime/runner.js +72 -0
- package/dist/runtime/runner.js.map +1 -0
- package/dist/runtime/serve.d.ts +19 -0
- package/dist/runtime/serve.d.ts.map +1 -0
- package/dist/runtime/serve.js +97 -0
- package/dist/runtime/serve.js.map +1 -0
- package/dist/schemas/index.d.ts +9 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +9 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/primitives.d.ts +37 -0
- package/dist/schemas/primitives.d.ts.map +1 -0
- package/dist/schemas/primitives.js +43 -0
- package/dist/schemas/primitives.js.map +1 -0
- package/dist/schemas/run.d.ts +23 -0
- package/dist/schemas/run.d.ts.map +1 -0
- package/dist/schemas/run.js +20 -0
- package/dist/schemas/run.js.map +1 -0
- package/dist/types/bundle.d.ts +141 -0
- package/dist/types/bundle.d.ts.map +1 -0
- package/dist/types/bundle.js +10 -0
- package/dist/types/bundle.js.map +1 -0
- package/dist/types/global.d.ts +38 -0
- package/dist/types/global.d.ts.map +1 -0
- package/dist/types/global.js +24 -0
- package/dist/types/global.js.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +27 -0
- package/dist/version.js.map +1 -0
- package/dist/walker.js +1 -0
- package/examples/README.md +355 -0
- package/examples/event.json +53 -0
- package/examples/flow-order-complete.json +67 -0
- package/examples/flow-simple.json +31 -0
- package/examples/flow.json +82 -0
- package/examples/server-collect.json +60 -0
- package/examples/server-collect.mjs +13540 -0
- package/examples/test.html +43 -0
- package/examples/web-serve.js +25503 -0
- package/examples/web-serve.json +74 -0
- package/package.json +62 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2629 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/version.ts
|
|
7
|
+
import { readFileSync } from "fs";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
var versionFilename = fileURLToPath(import.meta.url);
|
|
11
|
+
var versionDirname = dirname(versionFilename);
|
|
12
|
+
function findPackageJson() {
|
|
13
|
+
const paths = [
|
|
14
|
+
join(versionDirname, "../package.json"),
|
|
15
|
+
// dist/ or src/
|
|
16
|
+
join(versionDirname, "../../package.json")
|
|
17
|
+
// src/core/ (not used, but safe)
|
|
18
|
+
];
|
|
19
|
+
for (const p of paths) {
|
|
20
|
+
try {
|
|
21
|
+
return readFileSync(p, "utf-8");
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return JSON.stringify({ version: "0.0.0" });
|
|
26
|
+
}
|
|
27
|
+
var VERSION = JSON.parse(findPackageJson()).version;
|
|
28
|
+
|
|
29
|
+
// src/commands/bundle/index.ts
|
|
30
|
+
import path9 from "path";
|
|
31
|
+
|
|
32
|
+
// src/core/logger.ts
|
|
33
|
+
import chalk from "chalk";
|
|
34
|
+
function createLogger(options = {}) {
|
|
35
|
+
const { verbose = false, silent = false, json = false } = options;
|
|
36
|
+
const shouldLog = !silent && !json;
|
|
37
|
+
const shouldDebug = verbose && !silent && !json;
|
|
38
|
+
return {
|
|
39
|
+
log: (color, ...args) => {
|
|
40
|
+
if (shouldLog) {
|
|
41
|
+
const message = args.map((arg) => String(arg)).join(" ");
|
|
42
|
+
const colorMap = {
|
|
43
|
+
red: chalk.red,
|
|
44
|
+
green: chalk.green,
|
|
45
|
+
blue: chalk.blue,
|
|
46
|
+
yellow: chalk.yellow,
|
|
47
|
+
gray: chalk.gray,
|
|
48
|
+
grey: chalk.gray,
|
|
49
|
+
cyan: chalk.cyan,
|
|
50
|
+
magenta: chalk.magenta,
|
|
51
|
+
white: chalk.white,
|
|
52
|
+
black: chalk.black
|
|
53
|
+
};
|
|
54
|
+
const colorFn = colorMap[color];
|
|
55
|
+
const coloredMessage = colorFn ? colorFn(message) : message;
|
|
56
|
+
console.log(coloredMessage);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
info: (...args) => {
|
|
60
|
+
if (shouldLog) {
|
|
61
|
+
const message = args.map((arg) => String(arg)).join(" ");
|
|
62
|
+
console.log(chalk.blue(message));
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
success: (...args) => {
|
|
66
|
+
if (shouldLog) {
|
|
67
|
+
const message = args.map((arg) => String(arg)).join(" ");
|
|
68
|
+
console.log(chalk.green(message));
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
warning: (...args) => {
|
|
72
|
+
if (shouldLog) {
|
|
73
|
+
const message = args.map((arg) => String(arg)).join(" ");
|
|
74
|
+
console.log(chalk.yellow(message));
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
warn: (...args) => {
|
|
78
|
+
if (shouldLog) {
|
|
79
|
+
const message = args.map((arg) => String(arg)).join(" ");
|
|
80
|
+
console.log(chalk.yellow(message));
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
error: (...args) => {
|
|
84
|
+
if (!json) {
|
|
85
|
+
const message = args.map((arg) => String(arg)).join(" ");
|
|
86
|
+
console.error(chalk.red(message));
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
debug: (...args) => {
|
|
90
|
+
if (shouldDebug) {
|
|
91
|
+
const message = args.map((arg) => String(arg)).join(" ");
|
|
92
|
+
console.log(chalk.gray(message));
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
gray: (...args) => {
|
|
96
|
+
if (shouldLog) {
|
|
97
|
+
const message = args.map((arg) => String(arg)).join(" ");
|
|
98
|
+
console.log(chalk.gray(message));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function createCommandLogger(options) {
|
|
104
|
+
return createLogger({
|
|
105
|
+
verbose: options.verbose,
|
|
106
|
+
silent: options.silent ?? false,
|
|
107
|
+
json: options.json
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/core/timer.ts
|
|
112
|
+
function createTimer() {
|
|
113
|
+
let startTime = 0;
|
|
114
|
+
let endTime = 0;
|
|
115
|
+
return {
|
|
116
|
+
start() {
|
|
117
|
+
startTime = Date.now();
|
|
118
|
+
endTime = 0;
|
|
119
|
+
},
|
|
120
|
+
end() {
|
|
121
|
+
endTime = Date.now();
|
|
122
|
+
return endTime - startTime;
|
|
123
|
+
},
|
|
124
|
+
getElapsed() {
|
|
125
|
+
const currentTime = endTime || Date.now();
|
|
126
|
+
return currentTime - startTime;
|
|
127
|
+
},
|
|
128
|
+
format() {
|
|
129
|
+
const elapsed = this.getElapsed();
|
|
130
|
+
return (elapsed / 1e3).toFixed(2) + "s";
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/core/output.ts
|
|
136
|
+
function createJsonOutput(success, data, error, duration) {
|
|
137
|
+
return {
|
|
138
|
+
success,
|
|
139
|
+
...data && { data },
|
|
140
|
+
...error && { error },
|
|
141
|
+
...duration && { duration }
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function createSuccessOutput(data, duration) {
|
|
145
|
+
return createJsonOutput(true, data, void 0, duration);
|
|
146
|
+
}
|
|
147
|
+
function createErrorOutput(error, duration) {
|
|
148
|
+
return createJsonOutput(false, void 0, error, duration);
|
|
149
|
+
}
|
|
150
|
+
function formatBytes(bytes) {
|
|
151
|
+
return (bytes / 1024).toFixed(2);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/core/tmp.ts
|
|
155
|
+
import path from "path";
|
|
156
|
+
var DEFAULT_TMP_ROOT = ".tmp";
|
|
157
|
+
function getTmpPath(tmpDir, ...segments) {
|
|
158
|
+
const root = tmpDir || DEFAULT_TMP_ROOT;
|
|
159
|
+
const absoluteRoot = path.isAbsolute(root) ? root : path.resolve(root);
|
|
160
|
+
return path.join(absoluteRoot, ...segments);
|
|
161
|
+
}
|
|
162
|
+
function getDefaultTmpRoot() {
|
|
163
|
+
return DEFAULT_TMP_ROOT;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/core/asset-resolver.ts
|
|
167
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
168
|
+
import { existsSync } from "fs";
|
|
169
|
+
import path3 from "path";
|
|
170
|
+
|
|
171
|
+
// src/config/utils.ts
|
|
172
|
+
import fs from "fs-extra";
|
|
173
|
+
import path2 from "path";
|
|
174
|
+
function isUrl(str) {
|
|
175
|
+
try {
|
|
176
|
+
const url = new URL(str);
|
|
177
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
178
|
+
} catch {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async function downloadFromUrl(url) {
|
|
183
|
+
if (!isUrl(url)) {
|
|
184
|
+
throw new Error(`Invalid URL: ${url}`);
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const response = await fetch(url);
|
|
188
|
+
if (!response.ok) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`Failed to download ${url}: ${response.status} ${response.statusText}`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
const content = await response.text();
|
|
194
|
+
const downloadsDir = getTmpPath(void 0, "downloads");
|
|
195
|
+
await fs.ensureDir(downloadsDir);
|
|
196
|
+
const tempPath = path2.join(downloadsDir, "flow.json");
|
|
197
|
+
await fs.writeFile(tempPath, content, "utf-8");
|
|
198
|
+
return tempPath;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (error instanceof Error) {
|
|
201
|
+
throw new Error(`Failed to download from URL: ${error.message}`);
|
|
202
|
+
}
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async function loadJsonConfig(configPath) {
|
|
207
|
+
let absolutePath;
|
|
208
|
+
let isTemporary = false;
|
|
209
|
+
if (isUrl(configPath)) {
|
|
210
|
+
absolutePath = await downloadFromUrl(configPath);
|
|
211
|
+
isTemporary = true;
|
|
212
|
+
} else {
|
|
213
|
+
absolutePath = path2.resolve(configPath);
|
|
214
|
+
if (!await fs.pathExists(absolutePath)) {
|
|
215
|
+
throw new Error(`Configuration file not found: ${absolutePath}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
const rawConfig = await fs.readJson(absolutePath);
|
|
220
|
+
return rawConfig;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`Invalid JSON in config file: ${configPath}. ${error instanceof Error ? error.message : error}`
|
|
224
|
+
);
|
|
225
|
+
} finally {
|
|
226
|
+
if (isTemporary) {
|
|
227
|
+
try {
|
|
228
|
+
await fs.remove(absolutePath);
|
|
229
|
+
} catch {
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function loadJsonFromSource(source, options) {
|
|
235
|
+
const paramName = options?.name || "input";
|
|
236
|
+
if (!source || source.trim() === "") {
|
|
237
|
+
if (options?.required) {
|
|
238
|
+
throw new Error(`${paramName} is required`);
|
|
239
|
+
}
|
|
240
|
+
if (options?.fallback !== void 0) {
|
|
241
|
+
return options.fallback;
|
|
242
|
+
}
|
|
243
|
+
return {};
|
|
244
|
+
}
|
|
245
|
+
const trimmedSource = source.trim();
|
|
246
|
+
if (isUrl(trimmedSource)) {
|
|
247
|
+
try {
|
|
248
|
+
const tempPath = await downloadFromUrl(trimmedSource);
|
|
249
|
+
try {
|
|
250
|
+
const data = await fs.readJson(tempPath);
|
|
251
|
+
return data;
|
|
252
|
+
} finally {
|
|
253
|
+
try {
|
|
254
|
+
await fs.remove(tempPath);
|
|
255
|
+
} catch {
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
} catch (error) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
`Failed to load ${paramName} from URL ${trimmedSource}: ${getErrorMessage(error)}`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const resolvedPath = path2.resolve(trimmedSource);
|
|
265
|
+
if (await fs.pathExists(resolvedPath)) {
|
|
266
|
+
try {
|
|
267
|
+
const data = await fs.readJson(resolvedPath);
|
|
268
|
+
return data;
|
|
269
|
+
} catch (error) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
`Failed to parse ${paramName} from file ${trimmedSource}: ${getErrorMessage(error)}`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
const parsed = JSON.parse(trimmedSource);
|
|
277
|
+
return parsed;
|
|
278
|
+
} catch (jsonError) {
|
|
279
|
+
if (!trimmedSource.startsWith("{") && !trimmedSource.startsWith("[")) {
|
|
280
|
+
return { name: trimmedSource };
|
|
281
|
+
}
|
|
282
|
+
throw new Error(
|
|
283
|
+
`Failed to parse ${paramName}. Input appears to be JSON but contains errors: ${jsonError instanceof Error ? jsonError.message : String(jsonError)}`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/core/asset-resolver.ts
|
|
289
|
+
var cachedAssetDir;
|
|
290
|
+
function getAssetDir() {
|
|
291
|
+
if (cachedAssetDir) return cachedAssetDir;
|
|
292
|
+
const currentFile = fileURLToPath2(import.meta.url);
|
|
293
|
+
let dir = path3.dirname(currentFile);
|
|
294
|
+
while (dir !== path3.dirname(dir)) {
|
|
295
|
+
if (existsSync(path3.join(dir, "examples"))) {
|
|
296
|
+
cachedAssetDir = dir;
|
|
297
|
+
return dir;
|
|
298
|
+
}
|
|
299
|
+
dir = path3.dirname(dir);
|
|
300
|
+
}
|
|
301
|
+
cachedAssetDir = path3.dirname(currentFile);
|
|
302
|
+
return cachedAssetDir;
|
|
303
|
+
}
|
|
304
|
+
function resolveAsset(assetPath, assetType, baseDir) {
|
|
305
|
+
if (isUrl(assetPath)) {
|
|
306
|
+
return assetPath;
|
|
307
|
+
}
|
|
308
|
+
if (!assetPath.includes("/") && !assetPath.includes("\\")) {
|
|
309
|
+
const assetDir = getAssetDir();
|
|
310
|
+
return path3.join(assetDir, "examples", assetPath);
|
|
311
|
+
}
|
|
312
|
+
if (path3.isAbsolute(assetPath)) {
|
|
313
|
+
return assetPath;
|
|
314
|
+
}
|
|
315
|
+
return path3.resolve(baseDir || process.cwd(), assetPath);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// src/core/utils.ts
|
|
319
|
+
function getErrorMessage(error) {
|
|
320
|
+
return error instanceof Error ? error.message : String(error);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/core/local-packages.ts
|
|
324
|
+
import path4 from "path";
|
|
325
|
+
import fs2 from "fs-extra";
|
|
326
|
+
async function resolveLocalPackage(packageName, localPath, configDir, logger2) {
|
|
327
|
+
const absolutePath = path4.isAbsolute(localPath) ? localPath : path4.resolve(configDir, localPath);
|
|
328
|
+
if (!await fs2.pathExists(absolutePath)) {
|
|
329
|
+
throw new Error(
|
|
330
|
+
`Local package path not found: ${localPath} (resolved to ${absolutePath})`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
const pkgJsonPath = path4.join(absolutePath, "package.json");
|
|
334
|
+
if (!await fs2.pathExists(pkgJsonPath)) {
|
|
335
|
+
throw new Error(
|
|
336
|
+
`No package.json found at ${absolutePath}. Is this a valid package directory?`
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
const distPath = path4.join(absolutePath, "dist");
|
|
340
|
+
const hasDistFolder = await fs2.pathExists(distPath);
|
|
341
|
+
if (!hasDistFolder) {
|
|
342
|
+
logger2.warn(
|
|
343
|
+
`\u26A0\uFE0F ${packageName}: No dist/ folder found. Using package root.`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
name: packageName,
|
|
348
|
+
absolutePath,
|
|
349
|
+
distPath: hasDistFolder ? distPath : absolutePath,
|
|
350
|
+
hasDistFolder
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
async function copyLocalPackage(localPkg, targetDir, logger2) {
|
|
354
|
+
const packageDir = path4.join(targetDir, "node_modules", localPkg.name);
|
|
355
|
+
await fs2.ensureDir(path4.dirname(packageDir));
|
|
356
|
+
await fs2.copy(
|
|
357
|
+
path4.join(localPkg.absolutePath, "package.json"),
|
|
358
|
+
path4.join(packageDir, "package.json")
|
|
359
|
+
);
|
|
360
|
+
if (localPkg.hasDistFolder) {
|
|
361
|
+
await fs2.copy(localPkg.distPath, path4.join(packageDir, "dist"));
|
|
362
|
+
} else {
|
|
363
|
+
const entries = await fs2.readdir(localPkg.absolutePath);
|
|
364
|
+
for (const entry of entries) {
|
|
365
|
+
if (!["node_modules", ".turbo", ".git"].includes(entry)) {
|
|
366
|
+
await fs2.copy(
|
|
367
|
+
path4.join(localPkg.absolutePath, entry),
|
|
368
|
+
path4.join(packageDir, entry)
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
logger2.info(`\u{1F4E6} Using local: ${localPkg.name} from ${localPkg.absolutePath}`);
|
|
374
|
+
return packageDir;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/config/validators.ts
|
|
378
|
+
import { schemas } from "@walkeros/core/dev";
|
|
379
|
+
var { safeParseSetup } = schemas;
|
|
380
|
+
function isObject(value) {
|
|
381
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
|
|
382
|
+
}
|
|
383
|
+
function validateFlowSetup(data) {
|
|
384
|
+
const result = safeParseSetup(data);
|
|
385
|
+
if (!result.success) {
|
|
386
|
+
const errors = result.error.issues.map((issue) => {
|
|
387
|
+
const path14 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
|
|
388
|
+
return ` - ${path14}: ${issue.message}`;
|
|
389
|
+
}).join("\n");
|
|
390
|
+
throw new Error(`Invalid configuration:
|
|
391
|
+
${errors}`);
|
|
392
|
+
}
|
|
393
|
+
return result.data;
|
|
394
|
+
}
|
|
395
|
+
function getAvailableFlows(setup) {
|
|
396
|
+
return Object.keys(setup.flows);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// src/config/build-defaults.ts
|
|
400
|
+
var WEB_BUILD_DEFAULTS = {
|
|
401
|
+
format: "iife",
|
|
402
|
+
platform: "browser",
|
|
403
|
+
target: "es2020",
|
|
404
|
+
minify: true,
|
|
405
|
+
sourcemap: false,
|
|
406
|
+
cache: true,
|
|
407
|
+
windowCollector: "collector",
|
|
408
|
+
windowElb: "elb"
|
|
409
|
+
};
|
|
410
|
+
var SERVER_BUILD_DEFAULTS = {
|
|
411
|
+
format: "esm",
|
|
412
|
+
platform: "node",
|
|
413
|
+
target: "node20",
|
|
414
|
+
minify: true,
|
|
415
|
+
sourcemap: false,
|
|
416
|
+
cache: true
|
|
417
|
+
};
|
|
418
|
+
var DEFAULT_OUTPUT_PATHS = {
|
|
419
|
+
web: "./dist/walker.js",
|
|
420
|
+
server: "./dist/bundle.mjs"
|
|
421
|
+
};
|
|
422
|
+
function getBuildDefaults(platform) {
|
|
423
|
+
return platform === "web" ? WEB_BUILD_DEFAULTS : SERVER_BUILD_DEFAULTS;
|
|
424
|
+
}
|
|
425
|
+
function getDefaultOutput(platform) {
|
|
426
|
+
return DEFAULT_OUTPUT_PATHS[platform];
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/config/loader.ts
|
|
430
|
+
import path5 from "path";
|
|
431
|
+
import fs3 from "fs-extra";
|
|
432
|
+
import { getFlowConfig, getPlatform } from "@walkeros/core";
|
|
433
|
+
var DEFAULT_INCLUDE_FOLDER = "./shared";
|
|
434
|
+
function loadBundleConfig(rawConfig, options) {
|
|
435
|
+
const setup = validateFlowSetup(rawConfig);
|
|
436
|
+
const availableFlows = getAvailableFlows(setup);
|
|
437
|
+
const flowName = resolveFlow(setup, options.flowName, availableFlows);
|
|
438
|
+
const flowConfig = getFlowConfig(setup, flowName);
|
|
439
|
+
const platform = getPlatform(flowConfig);
|
|
440
|
+
if (!platform) {
|
|
441
|
+
throw new Error(
|
|
442
|
+
`Invalid configuration: flow "${flowName}" must have a "web" or "server" key.`
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
const buildDefaults = getBuildDefaults(platform);
|
|
446
|
+
const packages = flowConfig.packages || {};
|
|
447
|
+
const output = options.buildOverrides?.output || getDefaultOutput(platform);
|
|
448
|
+
const configDir = isUrl(options.configPath) ? process.cwd() : path5.dirname(options.configPath);
|
|
449
|
+
let includes = setup.include;
|
|
450
|
+
if (!includes) {
|
|
451
|
+
const defaultIncludePath = path5.resolve(configDir, DEFAULT_INCLUDE_FOLDER);
|
|
452
|
+
if (fs3.pathExistsSync(defaultIncludePath)) {
|
|
453
|
+
includes = [DEFAULT_INCLUDE_FOLDER];
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const buildOptions = {
|
|
457
|
+
...buildDefaults,
|
|
458
|
+
packages,
|
|
459
|
+
output,
|
|
460
|
+
include: includes,
|
|
461
|
+
configDir,
|
|
462
|
+
...options.buildOverrides
|
|
463
|
+
};
|
|
464
|
+
const isMultiFlow = availableFlows.length > 1;
|
|
465
|
+
if (isMultiFlow && options.logger) {
|
|
466
|
+
options.logger.info(
|
|
467
|
+
`\u{1F4E6} Using flow: ${flowName} (${availableFlows.length} total)`
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
flowConfig,
|
|
472
|
+
buildOptions,
|
|
473
|
+
flowName,
|
|
474
|
+
isMultiFlow,
|
|
475
|
+
availableFlows
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function resolveFlow(setup, requestedFlow, available) {
|
|
479
|
+
if (available.length === 1) {
|
|
480
|
+
return available[0];
|
|
481
|
+
}
|
|
482
|
+
if (!requestedFlow) {
|
|
483
|
+
throw new Error(
|
|
484
|
+
`Multiple flows found. Please specify a flow using --flow flag.
|
|
485
|
+
Available flows: ${available.join(", ")}`
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
if (!available.includes(requestedFlow)) {
|
|
489
|
+
throw new Error(
|
|
490
|
+
`Flow "${requestedFlow}" not found in configuration.
|
|
491
|
+
Available flows: ${available.join(", ")}`
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
return requestedFlow;
|
|
495
|
+
}
|
|
496
|
+
function loadAllFlows(rawConfig, options) {
|
|
497
|
+
const setup = validateFlowSetup(rawConfig);
|
|
498
|
+
const flows = getAvailableFlows(setup);
|
|
499
|
+
if (options.logger) {
|
|
500
|
+
options.logger.info(
|
|
501
|
+
`\u{1F4E6} Loading all ${flows.length} flows: ${flows.join(", ")}`
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
return flows.map(
|
|
505
|
+
(name) => loadBundleConfig(rawConfig, {
|
|
506
|
+
...options,
|
|
507
|
+
flowName: name
|
|
508
|
+
})
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// src/commands/bundle/bundler.ts
|
|
513
|
+
import esbuild from "esbuild";
|
|
514
|
+
import path8 from "path";
|
|
515
|
+
import fs6 from "fs-extra";
|
|
516
|
+
import { packageNameToVariable } from "@walkeros/core";
|
|
517
|
+
|
|
518
|
+
// src/commands/bundle/package-manager.ts
|
|
519
|
+
import pacote from "pacote";
|
|
520
|
+
import path6 from "path";
|
|
521
|
+
import fs4 from "fs-extra";
|
|
522
|
+
|
|
523
|
+
// src/core/cache-utils.ts
|
|
524
|
+
import { getHashServer } from "@walkeros/server-core";
|
|
525
|
+
var HASH_LENGTH = 12;
|
|
526
|
+
function isMutableVersion(version) {
|
|
527
|
+
return version === "latest" || version.includes("^") || version.includes("~") || version.includes("*") || version.includes("x");
|
|
528
|
+
}
|
|
529
|
+
function getTodayDate() {
|
|
530
|
+
return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
531
|
+
}
|
|
532
|
+
async function getPackageCacheKey(packageName, version, date) {
|
|
533
|
+
const safeName = packageName.replace(/\//g, "-").replace(/@/g, "");
|
|
534
|
+
if (isMutableVersion(version)) {
|
|
535
|
+
const dateStr = date ?? getTodayDate();
|
|
536
|
+
const input2 = `${safeName}@${version}:${dateStr}`;
|
|
537
|
+
return getHashServer(input2, HASH_LENGTH);
|
|
538
|
+
}
|
|
539
|
+
const input = `${safeName}@${version}`;
|
|
540
|
+
return getHashServer(input, HASH_LENGTH);
|
|
541
|
+
}
|
|
542
|
+
function normalizeJson(content) {
|
|
543
|
+
const parsed = JSON.parse(content);
|
|
544
|
+
return JSON.stringify(parsed);
|
|
545
|
+
}
|
|
546
|
+
async function getFlowConfigCacheKey(content, date) {
|
|
547
|
+
const dateStr = date ?? getTodayDate();
|
|
548
|
+
const normalized = normalizeJson(content);
|
|
549
|
+
const input = `${normalized}:${dateStr}`;
|
|
550
|
+
return getHashServer(input, HASH_LENGTH);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// src/commands/bundle/package-manager.ts
|
|
554
|
+
var PACKAGE_DOWNLOAD_TIMEOUT_MS = 6e4;
|
|
555
|
+
async function withTimeout(promise, ms, errorMessage) {
|
|
556
|
+
const timeout = new Promise(
|
|
557
|
+
(_, reject) => setTimeout(() => reject(new Error(errorMessage)), ms)
|
|
558
|
+
);
|
|
559
|
+
return Promise.race([promise, timeout]);
|
|
560
|
+
}
|
|
561
|
+
function getPackageDirectory(baseDir, packageName, version) {
|
|
562
|
+
return path6.join(baseDir, "node_modules", packageName);
|
|
563
|
+
}
|
|
564
|
+
async function getCachedPackagePath(pkg, tmpDir) {
|
|
565
|
+
const cacheDir = getTmpPath(tmpDir, "cache", "packages");
|
|
566
|
+
const cacheKey = await getPackageCacheKey(pkg.name, pkg.version);
|
|
567
|
+
return path6.join(cacheDir, cacheKey);
|
|
568
|
+
}
|
|
569
|
+
async function isPackageCached(pkg, tmpDir) {
|
|
570
|
+
const cachedPath = await getCachedPackagePath(pkg, tmpDir);
|
|
571
|
+
return fs4.pathExists(cachedPath);
|
|
572
|
+
}
|
|
573
|
+
function validateNoDuplicatePackages(packages) {
|
|
574
|
+
const packageMap = /* @__PURE__ */ new Map();
|
|
575
|
+
for (const pkg of packages) {
|
|
576
|
+
if (!packageMap.has(pkg.name)) {
|
|
577
|
+
packageMap.set(pkg.name, []);
|
|
578
|
+
}
|
|
579
|
+
packageMap.get(pkg.name).push(pkg.version);
|
|
580
|
+
}
|
|
581
|
+
const conflicts = [];
|
|
582
|
+
for (const [name, versions] of packageMap.entries()) {
|
|
583
|
+
const uniqueVersions = [...new Set(versions)];
|
|
584
|
+
if (uniqueVersions.length > 1) {
|
|
585
|
+
conflicts.push(`${name}: [${uniqueVersions.join(", ")}]`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (conflicts.length > 0) {
|
|
589
|
+
throw new Error(
|
|
590
|
+
`Version conflicts detected:
|
|
591
|
+
${conflicts.map((c) => ` - ${c}`).join("\n")}
|
|
592
|
+
|
|
593
|
+
Each package must use the same version across all declarations. Please update your configuration to use consistent versions.`
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
async function resolveDependencies(pkg, packageDir, logger2, visited = /* @__PURE__ */ new Set()) {
|
|
598
|
+
const dependencies = [];
|
|
599
|
+
const pkgKey = `${pkg.name}@${pkg.version}`;
|
|
600
|
+
if (visited.has(pkgKey)) {
|
|
601
|
+
return dependencies;
|
|
602
|
+
}
|
|
603
|
+
visited.add(pkgKey);
|
|
604
|
+
try {
|
|
605
|
+
const packageJsonPath = path6.join(packageDir, "package.json");
|
|
606
|
+
if (await fs4.pathExists(packageJsonPath)) {
|
|
607
|
+
const packageJson = await fs4.readJson(packageJsonPath);
|
|
608
|
+
const deps = {
|
|
609
|
+
...packageJson.dependencies,
|
|
610
|
+
...packageJson.peerDependencies
|
|
611
|
+
};
|
|
612
|
+
for (const [name, versionSpec] of Object.entries(deps)) {
|
|
613
|
+
if (typeof versionSpec === "string") {
|
|
614
|
+
dependencies.push({ name, version: versionSpec });
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
} catch (error) {
|
|
619
|
+
logger2.debug(`Failed to read dependencies for ${pkgKey}: ${error}`);
|
|
620
|
+
}
|
|
621
|
+
return dependencies;
|
|
622
|
+
}
|
|
623
|
+
async function downloadPackages(packages, targetDir, logger2, useCache = true, configDir) {
|
|
624
|
+
const packagePaths = /* @__PURE__ */ new Map();
|
|
625
|
+
const downloadQueue = [...packages];
|
|
626
|
+
const processed = /* @__PURE__ */ new Set();
|
|
627
|
+
const localPackageMap = /* @__PURE__ */ new Map();
|
|
628
|
+
for (const pkg of packages) {
|
|
629
|
+
if (pkg.path) {
|
|
630
|
+
localPackageMap.set(pkg.name, pkg.path);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
validateNoDuplicatePackages(packages);
|
|
634
|
+
await fs4.ensureDir(targetDir);
|
|
635
|
+
while (downloadQueue.length > 0) {
|
|
636
|
+
const pkg = downloadQueue.shift();
|
|
637
|
+
const pkgKey = `${pkg.name}@${pkg.version}`;
|
|
638
|
+
if (processed.has(pkgKey)) {
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
processed.add(pkgKey);
|
|
642
|
+
if (!pkg.path && localPackageMap.has(pkg.name)) {
|
|
643
|
+
pkg.path = localPackageMap.get(pkg.name);
|
|
644
|
+
}
|
|
645
|
+
if (pkg.path) {
|
|
646
|
+
const localPkg = await resolveLocalPackage(
|
|
647
|
+
pkg.name,
|
|
648
|
+
pkg.path,
|
|
649
|
+
configDir || process.cwd(),
|
|
650
|
+
logger2
|
|
651
|
+
);
|
|
652
|
+
const installedPath = await copyLocalPackage(localPkg, targetDir, logger2);
|
|
653
|
+
packagePaths.set(pkg.name, installedPath);
|
|
654
|
+
const deps = await resolveDependencies(pkg, installedPath, logger2);
|
|
655
|
+
for (const dep of deps) {
|
|
656
|
+
const depKey = `${dep.name}@${dep.version}`;
|
|
657
|
+
if (!processed.has(depKey)) {
|
|
658
|
+
downloadQueue.push(dep);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
const packageSpec = `${pkg.name}@${pkg.version}`;
|
|
664
|
+
const packageDir = getPackageDirectory(targetDir, pkg.name, pkg.version);
|
|
665
|
+
const cachedPath = await getCachedPackagePath(pkg);
|
|
666
|
+
if (useCache && await isPackageCached(pkg)) {
|
|
667
|
+
logger2.debug(`Using cached ${packageSpec}...`);
|
|
668
|
+
try {
|
|
669
|
+
await fs4.ensureDir(path6.dirname(packageDir));
|
|
670
|
+
await fs4.copy(cachedPath, packageDir);
|
|
671
|
+
packagePaths.set(pkg.name, packageDir);
|
|
672
|
+
const deps = await resolveDependencies(pkg, packageDir, logger2);
|
|
673
|
+
for (const dep of deps) {
|
|
674
|
+
const depKey = `${dep.name}@${dep.version}`;
|
|
675
|
+
if (!processed.has(depKey)) {
|
|
676
|
+
downloadQueue.push(dep);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
continue;
|
|
680
|
+
} catch (error) {
|
|
681
|
+
logger2.debug(
|
|
682
|
+
`Failed to use cache for ${packageSpec}, downloading fresh: ${error}`
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
logger2.debug(`Downloading ${packageSpec}...`);
|
|
687
|
+
try {
|
|
688
|
+
await fs4.ensureDir(path6.dirname(packageDir));
|
|
689
|
+
const cacheDir = process.env.NPM_CACHE_DIR || path6.join(process.cwd(), ".npm-cache");
|
|
690
|
+
await withTimeout(
|
|
691
|
+
pacote.extract(packageSpec, packageDir, {
|
|
692
|
+
// Force npm registry download, prevent workspace resolution
|
|
693
|
+
registry: "https://registry.npmjs.org",
|
|
694
|
+
// Force online fetching from registry (don't use cached workspace packages)
|
|
695
|
+
preferOnline: true,
|
|
696
|
+
// Cache for performance
|
|
697
|
+
cache: cacheDir,
|
|
698
|
+
// Don't resolve relative to workspace context
|
|
699
|
+
where: void 0
|
|
700
|
+
}),
|
|
701
|
+
PACKAGE_DOWNLOAD_TIMEOUT_MS,
|
|
702
|
+
`Package download timed out after ${PACKAGE_DOWNLOAD_TIMEOUT_MS / 1e3}s: ${packageSpec}`
|
|
703
|
+
);
|
|
704
|
+
if (useCache) {
|
|
705
|
+
try {
|
|
706
|
+
await fs4.ensureDir(path6.dirname(cachedPath));
|
|
707
|
+
await fs4.copy(packageDir, cachedPath);
|
|
708
|
+
logger2.debug(`Cached ${packageSpec} for future use`);
|
|
709
|
+
} catch (cacheError) {
|
|
710
|
+
logger2.debug(`Failed to cache ${packageSpec}: ${cacheError}`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
packagePaths.set(pkg.name, packageDir);
|
|
714
|
+
const deps = await resolveDependencies(pkg, packageDir, logger2);
|
|
715
|
+
for (const dep of deps) {
|
|
716
|
+
const depKey = `${dep.name}@${dep.version}`;
|
|
717
|
+
if (!processed.has(depKey)) {
|
|
718
|
+
downloadQueue.push(dep);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
} catch (error) {
|
|
722
|
+
throw new Error(`Failed to download ${packageSpec}: ${error}`);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
return packagePaths;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// src/core/build-cache.ts
|
|
729
|
+
import fs5 from "fs-extra";
|
|
730
|
+
import path7 from "path";
|
|
731
|
+
async function getBuildCachePath(configContent, tmpDir) {
|
|
732
|
+
const cacheDir = getTmpPath(tmpDir, "cache", "builds");
|
|
733
|
+
const cacheKey = await getFlowConfigCacheKey(configContent);
|
|
734
|
+
return path7.join(cacheDir, `${cacheKey}.js`);
|
|
735
|
+
}
|
|
736
|
+
async function isBuildCached(configContent, tmpDir) {
|
|
737
|
+
const cachePath = await getBuildCachePath(configContent, tmpDir);
|
|
738
|
+
return fs5.pathExists(cachePath);
|
|
739
|
+
}
|
|
740
|
+
async function cacheBuild(configContent, buildOutput, tmpDir) {
|
|
741
|
+
const cachePath = await getBuildCachePath(configContent, tmpDir);
|
|
742
|
+
await fs5.ensureDir(path7.dirname(cachePath));
|
|
743
|
+
await fs5.writeFile(cachePath, buildOutput, "utf-8");
|
|
744
|
+
}
|
|
745
|
+
async function getCachedBuild(configContent, tmpDir) {
|
|
746
|
+
const cachePath = await getBuildCachePath(configContent, tmpDir);
|
|
747
|
+
if (await fs5.pathExists(cachePath)) {
|
|
748
|
+
return await fs5.readFile(cachePath, "utf-8");
|
|
749
|
+
}
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// src/commands/bundle/bundler.ts
|
|
754
|
+
async function copyIncludes(includes, sourceDir, outputDir, logger2) {
|
|
755
|
+
for (const include of includes) {
|
|
756
|
+
const sourcePath = path8.resolve(sourceDir, include);
|
|
757
|
+
const folderName = path8.basename(include);
|
|
758
|
+
const destPath = path8.join(outputDir, folderName);
|
|
759
|
+
if (await fs6.pathExists(sourcePath)) {
|
|
760
|
+
await fs6.copy(sourcePath, destPath);
|
|
761
|
+
logger2.debug(`Copied ${include} to output`);
|
|
762
|
+
} else {
|
|
763
|
+
logger2.debug(`Include folder not found: ${include}`);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
function generateCacheKeyContent(flowConfig, buildOptions) {
|
|
768
|
+
const configForCache = {
|
|
769
|
+
flow: flowConfig,
|
|
770
|
+
build: {
|
|
771
|
+
...buildOptions,
|
|
772
|
+
// Exclude non-deterministic fields from cache key
|
|
773
|
+
tempDir: void 0,
|
|
774
|
+
output: void 0
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
return JSON.stringify(configForCache);
|
|
778
|
+
}
|
|
779
|
+
async function bundleCore(flowConfig, buildOptions, logger2, showStats = false) {
|
|
780
|
+
const bundleStartTime = Date.now();
|
|
781
|
+
const TEMP_DIR = buildOptions.tempDir || getTmpPath();
|
|
782
|
+
if (buildOptions.cache !== false) {
|
|
783
|
+
const configContent = generateCacheKeyContent(flowConfig, buildOptions);
|
|
784
|
+
const cached = await isBuildCached(configContent);
|
|
785
|
+
if (cached) {
|
|
786
|
+
const cachedBuild = await getCachedBuild(configContent);
|
|
787
|
+
if (cachedBuild) {
|
|
788
|
+
logger2.info("\u2728 Using cached build");
|
|
789
|
+
const outputPath = path8.resolve(buildOptions.output);
|
|
790
|
+
await fs6.ensureDir(path8.dirname(outputPath));
|
|
791
|
+
await fs6.writeFile(outputPath, cachedBuild);
|
|
792
|
+
logger2.gray(`Output: ${outputPath}`);
|
|
793
|
+
logger2.success("\u2705 Build completed (from cache)");
|
|
794
|
+
if (showStats) {
|
|
795
|
+
const stats = await fs6.stat(outputPath);
|
|
796
|
+
const packageStats = Object.entries(buildOptions.packages).map(
|
|
797
|
+
([name, pkg]) => ({
|
|
798
|
+
name: `${name}@${pkg.version || "latest"}`,
|
|
799
|
+
size: 0
|
|
800
|
+
// Size estimation not available for cached builds
|
|
801
|
+
})
|
|
802
|
+
);
|
|
803
|
+
return {
|
|
804
|
+
totalSize: stats.size,
|
|
805
|
+
packages: packageStats,
|
|
806
|
+
buildTime: Date.now() - bundleStartTime,
|
|
807
|
+
treeshakingEffective: true
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
try {
|
|
815
|
+
await fs6.ensureDir(TEMP_DIR);
|
|
816
|
+
logger2.info("\u{1F4E5} Downloading packages...");
|
|
817
|
+
const packagesArray = Object.entries(buildOptions.packages).map(
|
|
818
|
+
([name, packageConfig]) => ({
|
|
819
|
+
name,
|
|
820
|
+
version: packageConfig.version || "latest",
|
|
821
|
+
path: packageConfig.path
|
|
822
|
+
// Pass local path if defined
|
|
823
|
+
})
|
|
824
|
+
);
|
|
825
|
+
const packagePaths = await downloadPackages(
|
|
826
|
+
packagesArray,
|
|
827
|
+
TEMP_DIR,
|
|
828
|
+
logger2,
|
|
829
|
+
buildOptions.cache,
|
|
830
|
+
buildOptions.configDir
|
|
831
|
+
// For resolving relative local paths
|
|
832
|
+
);
|
|
833
|
+
for (const [pkgName, pkgPath] of packagePaths.entries()) {
|
|
834
|
+
if (pkgName.startsWith("@walkeros/")) {
|
|
835
|
+
const pkgJsonPath = path8.join(pkgPath, "package.json");
|
|
836
|
+
const pkgJson = await fs6.readJSON(pkgJsonPath);
|
|
837
|
+
if (!pkgJson.exports && pkgJson.module) {
|
|
838
|
+
pkgJson.exports = {
|
|
839
|
+
".": {
|
|
840
|
+
import: pkgJson.module,
|
|
841
|
+
require: pkgJson.main
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
await fs6.writeJSON(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
const packageJsonPath = path8.join(TEMP_DIR, "package.json");
|
|
849
|
+
await fs6.writeFile(
|
|
850
|
+
packageJsonPath,
|
|
851
|
+
JSON.stringify({ type: "module" }, null, 2)
|
|
852
|
+
);
|
|
853
|
+
logger2.info("\u{1F4DD} Creating entry point...");
|
|
854
|
+
const entryContent = await createEntryPoint(
|
|
855
|
+
flowConfig,
|
|
856
|
+
buildOptions,
|
|
857
|
+
packagePaths
|
|
858
|
+
);
|
|
859
|
+
const entryPath = path8.join(TEMP_DIR, "entry.js");
|
|
860
|
+
await fs6.writeFile(entryPath, entryContent);
|
|
861
|
+
logger2.info("\u26A1 Bundling with esbuild...");
|
|
862
|
+
const outputPath = path8.resolve(buildOptions.output);
|
|
863
|
+
await fs6.ensureDir(path8.dirname(outputPath));
|
|
864
|
+
const esbuildOptions = createEsbuildOptions(
|
|
865
|
+
buildOptions,
|
|
866
|
+
entryPath,
|
|
867
|
+
outputPath,
|
|
868
|
+
TEMP_DIR,
|
|
869
|
+
packagePaths,
|
|
870
|
+
logger2
|
|
871
|
+
);
|
|
872
|
+
try {
|
|
873
|
+
await esbuild.build(esbuildOptions);
|
|
874
|
+
} catch (buildError) {
|
|
875
|
+
throw createBuildError(
|
|
876
|
+
buildError,
|
|
877
|
+
buildOptions.code || ""
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
logger2.gray(`Output: ${outputPath}`);
|
|
881
|
+
if (buildOptions.cache !== false) {
|
|
882
|
+
const configContent = generateCacheKeyContent(flowConfig, buildOptions);
|
|
883
|
+
const buildOutput = await fs6.readFile(outputPath, "utf-8");
|
|
884
|
+
await cacheBuild(configContent, buildOutput);
|
|
885
|
+
logger2.debug("Build cached for future use");
|
|
886
|
+
}
|
|
887
|
+
let stats;
|
|
888
|
+
if (showStats) {
|
|
889
|
+
stats = await collectBundleStats(
|
|
890
|
+
outputPath,
|
|
891
|
+
buildOptions.packages,
|
|
892
|
+
bundleStartTime,
|
|
893
|
+
entryContent
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
if (buildOptions.include && buildOptions.include.length > 0) {
|
|
897
|
+
const outputDir = path8.dirname(outputPath);
|
|
898
|
+
await copyIncludes(
|
|
899
|
+
buildOptions.include,
|
|
900
|
+
buildOptions.configDir || process.cwd(),
|
|
901
|
+
outputDir,
|
|
902
|
+
logger2
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
return stats;
|
|
906
|
+
} catch (error) {
|
|
907
|
+
throw error;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
async function collectBundleStats(outputPath, packages, startTime, entryContent) {
|
|
911
|
+
const stats = await fs6.stat(outputPath);
|
|
912
|
+
const totalSize = stats.size;
|
|
913
|
+
const buildTime = Date.now() - startTime;
|
|
914
|
+
const packageStats = Object.entries(packages).map(([name, pkg]) => {
|
|
915
|
+
const importPattern = new RegExp(`from\\s+['"]${name}['"]`, "g");
|
|
916
|
+
const namedImportPattern = new RegExp(
|
|
917
|
+
`import\\s+\\{[^}]*\\}\\s+from\\s+['"]${name}['"]`,
|
|
918
|
+
"g"
|
|
919
|
+
);
|
|
920
|
+
const hasImports = importPattern.test(entryContent) || namedImportPattern.test(entryContent);
|
|
921
|
+
const packagesCount = Object.keys(packages).length;
|
|
922
|
+
const estimatedSize = hasImports ? Math.floor(totalSize / packagesCount) : 0;
|
|
923
|
+
return {
|
|
924
|
+
name: `${name}@${pkg.version || "latest"}`,
|
|
925
|
+
size: estimatedSize
|
|
926
|
+
};
|
|
927
|
+
});
|
|
928
|
+
const hasWildcardImports = /import\s+\*\s+as\s+\w+\s+from/.test(entryContent);
|
|
929
|
+
const treeshakingEffective = !hasWildcardImports;
|
|
930
|
+
return {
|
|
931
|
+
totalSize,
|
|
932
|
+
packages: packageStats,
|
|
933
|
+
buildTime,
|
|
934
|
+
treeshakingEffective
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
function createEsbuildOptions(buildOptions, entryPath, outputPath, tempDir, packagePaths, logger2) {
|
|
938
|
+
const alias = {};
|
|
939
|
+
const baseOptions = {
|
|
940
|
+
entryPoints: [entryPath],
|
|
941
|
+
bundle: true,
|
|
942
|
+
format: buildOptions.format,
|
|
943
|
+
platform: buildOptions.platform,
|
|
944
|
+
outfile: outputPath,
|
|
945
|
+
absWorkingDir: tempDir,
|
|
946
|
+
// Resolve modules from temp directory
|
|
947
|
+
// alias removed - not needed with absWorkingDir
|
|
948
|
+
mainFields: ["module", "main"],
|
|
949
|
+
// Prefer ESM over CJS
|
|
950
|
+
treeShaking: true,
|
|
951
|
+
logLevel: "error",
|
|
952
|
+
minify: buildOptions.minify,
|
|
953
|
+
sourcemap: buildOptions.sourcemap,
|
|
954
|
+
resolveExtensions: [".mjs", ".js", ".ts", ".json"],
|
|
955
|
+
// Prefer .mjs
|
|
956
|
+
// Enhanced minification options when minify is enabled
|
|
957
|
+
...buildOptions.minify && {
|
|
958
|
+
minifyWhitespace: buildOptions.minifyOptions?.whitespace ?? true,
|
|
959
|
+
minifyIdentifiers: buildOptions.minifyOptions?.identifiers ?? true,
|
|
960
|
+
minifySyntax: buildOptions.minifyOptions?.syntax ?? true,
|
|
961
|
+
legalComments: buildOptions.minifyOptions?.legalComments ?? "none",
|
|
962
|
+
keepNames: buildOptions.minifyOptions?.keepNames ?? false,
|
|
963
|
+
charset: "utf8"
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
if (buildOptions.platform === "browser") {
|
|
967
|
+
baseOptions.define = {
|
|
968
|
+
"process.env.NODE_ENV": '"production"',
|
|
969
|
+
global: "globalThis"
|
|
970
|
+
};
|
|
971
|
+
baseOptions.external = buildOptions.external || [];
|
|
972
|
+
} else if (buildOptions.platform === "node") {
|
|
973
|
+
const nodeBuiltins = [
|
|
974
|
+
"crypto",
|
|
975
|
+
"fs",
|
|
976
|
+
"path",
|
|
977
|
+
"os",
|
|
978
|
+
"util",
|
|
979
|
+
"stream",
|
|
980
|
+
"buffer",
|
|
981
|
+
"events",
|
|
982
|
+
"http",
|
|
983
|
+
"https",
|
|
984
|
+
"url",
|
|
985
|
+
"querystring",
|
|
986
|
+
"zlib"
|
|
987
|
+
];
|
|
988
|
+
const npmPackages = ["express", "express/*", "cors", "cors/*"];
|
|
989
|
+
baseOptions.external = buildOptions.external ? [...nodeBuiltins, ...npmPackages, ...buildOptions.external] : [...nodeBuiltins, ...npmPackages];
|
|
990
|
+
if (buildOptions.format === "esm") {
|
|
991
|
+
baseOptions.banner = {
|
|
992
|
+
js: `import { createRequire } from 'module';const require = createRequire(import.meta.url);`
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (buildOptions.target) {
|
|
997
|
+
baseOptions.target = buildOptions.target;
|
|
998
|
+
} else if (buildOptions.platform === "node") {
|
|
999
|
+
baseOptions.target = "node18";
|
|
1000
|
+
} else {
|
|
1001
|
+
baseOptions.target = "es2018";
|
|
1002
|
+
}
|
|
1003
|
+
return baseOptions;
|
|
1004
|
+
}
|
|
1005
|
+
function detectDestinationPackages(flowConfig) {
|
|
1006
|
+
const destinationPackages = /* @__PURE__ */ new Set();
|
|
1007
|
+
const destinations = flowConfig.destinations;
|
|
1008
|
+
if (destinations) {
|
|
1009
|
+
for (const [destKey, destConfig] of Object.entries(destinations)) {
|
|
1010
|
+
if (typeof destConfig === "object" && destConfig !== null && "package" in destConfig && typeof destConfig.package === "string") {
|
|
1011
|
+
destinationPackages.add(destConfig.package);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
return destinationPackages;
|
|
1016
|
+
}
|
|
1017
|
+
function detectSourcePackages(flowConfig) {
|
|
1018
|
+
const sourcePackages = /* @__PURE__ */ new Set();
|
|
1019
|
+
const sources = flowConfig.sources;
|
|
1020
|
+
if (sources) {
|
|
1021
|
+
for (const [sourceKey, sourceConfig] of Object.entries(sources)) {
|
|
1022
|
+
if (typeof sourceConfig === "object" && sourceConfig !== null && "package" in sourceConfig && typeof sourceConfig.package === "string") {
|
|
1023
|
+
sourcePackages.add(sourceConfig.package);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
return sourcePackages;
|
|
1028
|
+
}
|
|
1029
|
+
function detectExplicitCodeImports(flowConfig) {
|
|
1030
|
+
const explicitCodeImports = /* @__PURE__ */ new Map();
|
|
1031
|
+
const destinations = flowConfig.destinations;
|
|
1032
|
+
if (destinations) {
|
|
1033
|
+
for (const [destKey, destConfig] of Object.entries(destinations)) {
|
|
1034
|
+
if (typeof destConfig === "object" && destConfig !== null && "package" in destConfig && typeof destConfig.package === "string" && "code" in destConfig && typeof destConfig.code === "string") {
|
|
1035
|
+
const isAutoGenerated = destConfig.code.startsWith("_");
|
|
1036
|
+
if (!isAutoGenerated) {
|
|
1037
|
+
if (!explicitCodeImports.has(destConfig.package)) {
|
|
1038
|
+
explicitCodeImports.set(destConfig.package, /* @__PURE__ */ new Set());
|
|
1039
|
+
}
|
|
1040
|
+
explicitCodeImports.get(destConfig.package).add(destConfig.code);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
const sources = flowConfig.sources;
|
|
1046
|
+
if (sources) {
|
|
1047
|
+
for (const [sourceKey, sourceConfig] of Object.entries(sources)) {
|
|
1048
|
+
if (typeof sourceConfig === "object" && sourceConfig !== null && "package" in sourceConfig && typeof sourceConfig.package === "string" && "code" in sourceConfig && typeof sourceConfig.code === "string") {
|
|
1049
|
+
const isAutoGenerated = sourceConfig.code.startsWith("_");
|
|
1050
|
+
if (!isAutoGenerated) {
|
|
1051
|
+
if (!explicitCodeImports.has(sourceConfig.package)) {
|
|
1052
|
+
explicitCodeImports.set(sourceConfig.package, /* @__PURE__ */ new Set());
|
|
1053
|
+
}
|
|
1054
|
+
explicitCodeImports.get(sourceConfig.package).add(sourceConfig.code);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
return explicitCodeImports;
|
|
1060
|
+
}
|
|
1061
|
+
function generateImportStatements(packages, destinationPackages, sourcePackages, explicitCodeImports) {
|
|
1062
|
+
const importStatements = [];
|
|
1063
|
+
const examplesMappings = [];
|
|
1064
|
+
const usedPackages = /* @__PURE__ */ new Set([...destinationPackages, ...sourcePackages]);
|
|
1065
|
+
for (const [packageName, packageConfig] of Object.entries(packages)) {
|
|
1066
|
+
const isUsedByDestOrSource = usedPackages.has(packageName);
|
|
1067
|
+
const hasExplicitCode = explicitCodeImports.has(packageName);
|
|
1068
|
+
if (packageConfig.imports && packageConfig.imports.length > 0) {
|
|
1069
|
+
const uniqueImports = [...new Set(packageConfig.imports)];
|
|
1070
|
+
const defaultImports = [];
|
|
1071
|
+
const namedImports = [];
|
|
1072
|
+
for (const imp of uniqueImports) {
|
|
1073
|
+
if (imp.startsWith("default as ")) {
|
|
1074
|
+
defaultImports.push(imp.replace("default as ", ""));
|
|
1075
|
+
} else {
|
|
1076
|
+
namedImports.push(imp);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
if (defaultImports.length > 0) {
|
|
1080
|
+
for (const defaultImport of defaultImports) {
|
|
1081
|
+
importStatements.push(
|
|
1082
|
+
`import ${defaultImport} from '${packageName}';`
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
if (namedImports.length > 0) {
|
|
1087
|
+
const importList = namedImports.join(", ");
|
|
1088
|
+
importStatements.push(
|
|
1089
|
+
`import { ${importList} } from '${packageName}';`
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
const examplesImport = uniqueImports.find(
|
|
1093
|
+
(imp) => imp.includes("examples as ")
|
|
1094
|
+
);
|
|
1095
|
+
if (examplesImport) {
|
|
1096
|
+
const examplesVarName = examplesImport.split(" as ")[1];
|
|
1097
|
+
const destinationMatch = packageName.match(
|
|
1098
|
+
/@walkeros\/web-destination-(.+)$/
|
|
1099
|
+
);
|
|
1100
|
+
if (destinationMatch) {
|
|
1101
|
+
const destinationName = destinationMatch[1];
|
|
1102
|
+
examplesMappings.push(
|
|
1103
|
+
` ${destinationName}: typeof ${examplesVarName} !== 'undefined' ? ${examplesVarName} : undefined`
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
} else if (hasExplicitCode) {
|
|
1108
|
+
const codes = Array.from(explicitCodeImports.get(packageName));
|
|
1109
|
+
importStatements.push(
|
|
1110
|
+
`import { ${codes.join(", ")} } from '${packageName}';`
|
|
1111
|
+
);
|
|
1112
|
+
} else if (isUsedByDestOrSource) {
|
|
1113
|
+
const varName = packageNameToVariable(packageName);
|
|
1114
|
+
importStatements.push(`import ${varName} from '${packageName}';`);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return { importStatements, examplesMappings };
|
|
1118
|
+
}
|
|
1119
|
+
async function createEntryPoint(flowConfig, buildOptions, packagePaths) {
|
|
1120
|
+
const destinationPackages = detectDestinationPackages(flowConfig);
|
|
1121
|
+
const sourcePackages = detectSourcePackages(flowConfig);
|
|
1122
|
+
const explicitCodeImports = detectExplicitCodeImports(flowConfig);
|
|
1123
|
+
const { importStatements } = generateImportStatements(
|
|
1124
|
+
buildOptions.packages,
|
|
1125
|
+
destinationPackages,
|
|
1126
|
+
sourcePackages,
|
|
1127
|
+
explicitCodeImports
|
|
1128
|
+
);
|
|
1129
|
+
const importsCode = importStatements.join("\n");
|
|
1130
|
+
const hasFlow = destinationPackages.size > 0 || sourcePackages.size > 0;
|
|
1131
|
+
if (!hasFlow) {
|
|
1132
|
+
const userCode = buildOptions.code || "";
|
|
1133
|
+
return importsCode ? `${importsCode}
|
|
1134
|
+
|
|
1135
|
+
${userCode}` : userCode;
|
|
1136
|
+
}
|
|
1137
|
+
const configObject = buildConfigObject(flowConfig, explicitCodeImports);
|
|
1138
|
+
const wrappedCode = generatePlatformWrapper(
|
|
1139
|
+
configObject,
|
|
1140
|
+
buildOptions.code || "",
|
|
1141
|
+
buildOptions
|
|
1142
|
+
);
|
|
1143
|
+
return importsCode ? `${importsCode}
|
|
1144
|
+
|
|
1145
|
+
${wrappedCode}` : wrappedCode;
|
|
1146
|
+
}
|
|
1147
|
+
function createBuildError(buildError, code) {
|
|
1148
|
+
if (!buildError.errors || buildError.errors.length === 0) {
|
|
1149
|
+
return new Error(`Build failed: ${buildError.message || buildError}`);
|
|
1150
|
+
}
|
|
1151
|
+
const firstError = buildError.errors[0];
|
|
1152
|
+
const location = firstError.location;
|
|
1153
|
+
if (location && location.file && location.file.includes("entry.js")) {
|
|
1154
|
+
const line = location.line;
|
|
1155
|
+
const column = location.column;
|
|
1156
|
+
const codeLines = code.split("\n");
|
|
1157
|
+
const errorLine = codeLines[line - 1] || "";
|
|
1158
|
+
return new Error(
|
|
1159
|
+
`Code syntax error at line ${line}, column ${column}:
|
|
1160
|
+
${errorLine}
|
|
1161
|
+
${" ".repeat(column - 1)}^
|
|
1162
|
+
${firstError.text}`
|
|
1163
|
+
);
|
|
1164
|
+
}
|
|
1165
|
+
return new Error(
|
|
1166
|
+
`Build error: ${firstError.text}
|
|
1167
|
+
` + (location ? ` at ${location.file}:${location.line}:${location.column}` : "")
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
function buildConfigObject(flowConfig, explicitCodeImports) {
|
|
1171
|
+
const flowWithProps = flowConfig;
|
|
1172
|
+
const sources = flowWithProps.sources || {};
|
|
1173
|
+
const destinations = flowWithProps.destinations || {};
|
|
1174
|
+
const sourcesEntries = Object.entries(sources).map(([key, source]) => {
|
|
1175
|
+
const hasExplicitCode = source.code && explicitCodeImports.has(source.package);
|
|
1176
|
+
const codeVar = hasExplicitCode ? source.code : packageNameToVariable(source.package);
|
|
1177
|
+
const configStr = source.config ? processConfigValue(source.config) : "{}";
|
|
1178
|
+
const envStr = source.env ? `,
|
|
1179
|
+
env: ${processConfigValue(source.env)}` : "";
|
|
1180
|
+
return ` ${key}: {
|
|
1181
|
+
code: ${codeVar},
|
|
1182
|
+
config: ${configStr}${envStr}
|
|
1183
|
+
}`;
|
|
1184
|
+
});
|
|
1185
|
+
const destinationsEntries = Object.entries(destinations).map(
|
|
1186
|
+
([key, dest]) => {
|
|
1187
|
+
const hasExplicitCode = dest.code && explicitCodeImports.has(dest.package);
|
|
1188
|
+
const codeVar = hasExplicitCode ? dest.code : packageNameToVariable(dest.package);
|
|
1189
|
+
const configStr = dest.config ? processConfigValue(dest.config) : "{}";
|
|
1190
|
+
const envStr = dest.env ? `,
|
|
1191
|
+
env: ${processConfigValue(dest.env)}` : "";
|
|
1192
|
+
return ` ${key}: {
|
|
1193
|
+
code: ${codeVar},
|
|
1194
|
+
config: ${configStr}${envStr}
|
|
1195
|
+
}`;
|
|
1196
|
+
}
|
|
1197
|
+
);
|
|
1198
|
+
const collectorStr = flowWithProps.collector ? `,
|
|
1199
|
+
...${processConfigValue(flowWithProps.collector)}` : "";
|
|
1200
|
+
return `{
|
|
1201
|
+
sources: {
|
|
1202
|
+
${sourcesEntries.join(",\n")}
|
|
1203
|
+
},
|
|
1204
|
+
destinations: {
|
|
1205
|
+
${destinationsEntries.join(",\n")}
|
|
1206
|
+
}${collectorStr}
|
|
1207
|
+
}`;
|
|
1208
|
+
}
|
|
1209
|
+
function processConfigValue(value) {
|
|
1210
|
+
return JSON.stringify(value, null, 2);
|
|
1211
|
+
}
|
|
1212
|
+
function generatePlatformWrapper(configObject, userCode, buildOptions) {
|
|
1213
|
+
if (buildOptions.platform === "browser") {
|
|
1214
|
+
const windowAssignments = [];
|
|
1215
|
+
if (buildOptions.windowCollector) {
|
|
1216
|
+
windowAssignments.push(
|
|
1217
|
+
` if (typeof window !== 'undefined') window['${buildOptions.windowCollector}'] = collector;`
|
|
1218
|
+
);
|
|
1219
|
+
}
|
|
1220
|
+
if (buildOptions.windowElb) {
|
|
1221
|
+
windowAssignments.push(
|
|
1222
|
+
` if (typeof window !== 'undefined') window['${buildOptions.windowElb}'] = elb;`
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
const assignments = windowAssignments.length > 0 ? "\n" + windowAssignments.join("\n") : "";
|
|
1226
|
+
return `(async () => {
|
|
1227
|
+
const config = ${configObject};
|
|
1228
|
+
|
|
1229
|
+
${userCode}
|
|
1230
|
+
|
|
1231
|
+
const { collector, elb } = await startFlow(config);${assignments}
|
|
1232
|
+
})();`;
|
|
1233
|
+
} else {
|
|
1234
|
+
const codeSection = userCode ? `
|
|
1235
|
+
${userCode}
|
|
1236
|
+
` : "";
|
|
1237
|
+
return `export default async function(context = {}) {
|
|
1238
|
+
const config = ${configObject};${codeSection}
|
|
1239
|
+
return await startFlow(config);
|
|
1240
|
+
}`;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// src/commands/bundle/stats.ts
|
|
1245
|
+
function displayStats(stats, logger2) {
|
|
1246
|
+
logger2.info("\n\u{1F4CA} Bundle Statistics");
|
|
1247
|
+
logger2.info("\u2500".repeat(50));
|
|
1248
|
+
const sizeKB = formatBytes(stats.totalSize);
|
|
1249
|
+
logger2.info(`Total Size: ${sizeKB} KB`);
|
|
1250
|
+
const timeSeconds = (stats.buildTime / 1e3).toFixed(2);
|
|
1251
|
+
logger2.info(`Build Time: ${timeSeconds}s`);
|
|
1252
|
+
const treeshakingStatus = stats.treeshakingEffective ? "\u2705 Effective" : "\u26A0\uFE0F Not optimal (consider using named imports)";
|
|
1253
|
+
logger2.info(`Tree-shaking: ${treeshakingStatus}`);
|
|
1254
|
+
if (stats.packages.length > 0) {
|
|
1255
|
+
logger2.info(`
|
|
1256
|
+
Package Breakdown:`);
|
|
1257
|
+
stats.packages.forEach((pkg) => {
|
|
1258
|
+
if (pkg.size > 0) {
|
|
1259
|
+
const pkgSizeKB = formatBytes(pkg.size);
|
|
1260
|
+
logger2.info(` \u2022 ${pkg.name}: ${pkgSizeKB} KB`);
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
logger2.info("\u2500".repeat(50));
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// src/commands/bundle/index.ts
|
|
1268
|
+
async function bundleCommand(options) {
|
|
1269
|
+
const timer = createTimer();
|
|
1270
|
+
timer.start();
|
|
1271
|
+
const logger2 = createCommandLogger(options);
|
|
1272
|
+
if (options.dryRun) {
|
|
1273
|
+
logger2.info(
|
|
1274
|
+
`[DRY-RUN] Would execute bundle with config: ${options.config}`
|
|
1275
|
+
);
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
try {
|
|
1279
|
+
if (options.flow && options.all) {
|
|
1280
|
+
throw new Error("Cannot use both --flow and --all flags together");
|
|
1281
|
+
}
|
|
1282
|
+
logger2.info("\u{1F4E6} Reading configuration...");
|
|
1283
|
+
const configPath = resolveAsset(options.config, "config");
|
|
1284
|
+
const rawConfig = await loadJsonConfig(configPath);
|
|
1285
|
+
const configsToBundle = options.all ? loadAllFlows(rawConfig, { configPath, logger: logger2 }) : [
|
|
1286
|
+
loadBundleConfig(rawConfig, {
|
|
1287
|
+
configPath,
|
|
1288
|
+
flowName: options.flow,
|
|
1289
|
+
logger: logger2
|
|
1290
|
+
})
|
|
1291
|
+
];
|
|
1292
|
+
const results = [];
|
|
1293
|
+
for (const {
|
|
1294
|
+
flowConfig,
|
|
1295
|
+
buildOptions,
|
|
1296
|
+
flowName,
|
|
1297
|
+
isMultiFlow
|
|
1298
|
+
} of configsToBundle) {
|
|
1299
|
+
try {
|
|
1300
|
+
if (options.cache !== void 0) {
|
|
1301
|
+
buildOptions.cache = options.cache;
|
|
1302
|
+
}
|
|
1303
|
+
if (isMultiFlow || options.all) {
|
|
1304
|
+
logger2.info(`
|
|
1305
|
+
\u{1F527} Building flow: ${flowName}`);
|
|
1306
|
+
} else {
|
|
1307
|
+
logger2.info("\u{1F527} Starting bundle process...");
|
|
1308
|
+
}
|
|
1309
|
+
const shouldCollectStats = options.stats || options.json;
|
|
1310
|
+
const stats = await bundleCore(
|
|
1311
|
+
flowConfig,
|
|
1312
|
+
buildOptions,
|
|
1313
|
+
logger2,
|
|
1314
|
+
shouldCollectStats
|
|
1315
|
+
);
|
|
1316
|
+
results.push({
|
|
1317
|
+
flowName,
|
|
1318
|
+
success: true,
|
|
1319
|
+
stats
|
|
1320
|
+
});
|
|
1321
|
+
if (!options.json && !options.all && options.stats && stats) {
|
|
1322
|
+
displayStats(stats, logger2);
|
|
1323
|
+
}
|
|
1324
|
+
} catch (error) {
|
|
1325
|
+
const errorMessage = getErrorMessage(error);
|
|
1326
|
+
results.push({
|
|
1327
|
+
flowName,
|
|
1328
|
+
success: false,
|
|
1329
|
+
error: errorMessage
|
|
1330
|
+
});
|
|
1331
|
+
if (!options.all) {
|
|
1332
|
+
throw error;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
const duration = timer.end() / 1e3;
|
|
1337
|
+
const successCount = results.filter((r) => r.success).length;
|
|
1338
|
+
const failureCount = results.filter((r) => !r.success).length;
|
|
1339
|
+
if (options.json) {
|
|
1340
|
+
const outputLogger = createLogger({ silent: false, json: false });
|
|
1341
|
+
const output = failureCount === 0 ? createSuccessOutput(
|
|
1342
|
+
{
|
|
1343
|
+
flows: results,
|
|
1344
|
+
summary: {
|
|
1345
|
+
total: results.length,
|
|
1346
|
+
success: successCount,
|
|
1347
|
+
failed: failureCount
|
|
1348
|
+
}
|
|
1349
|
+
},
|
|
1350
|
+
duration
|
|
1351
|
+
) : createErrorOutput(
|
|
1352
|
+
`${failureCount} flow(s) failed to build`,
|
|
1353
|
+
duration
|
|
1354
|
+
);
|
|
1355
|
+
outputLogger.log("white", JSON.stringify(output, null, 2));
|
|
1356
|
+
} else {
|
|
1357
|
+
if (options.all) {
|
|
1358
|
+
logger2.info(`
|
|
1359
|
+
\u{1F4CA} Build Summary:`);
|
|
1360
|
+
logger2.info(` Total: ${results.length}`);
|
|
1361
|
+
logger2.success(` \u2705 Success: ${successCount}`);
|
|
1362
|
+
if (failureCount > 0) {
|
|
1363
|
+
logger2.error(` \u274C Failed: ${failureCount}`);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
if (failureCount === 0) {
|
|
1367
|
+
logger2.success(`
|
|
1368
|
+
\u2705 Bundle created successfully in ${timer.format()}`);
|
|
1369
|
+
} else {
|
|
1370
|
+
throw new Error(`${failureCount} flow(s) failed to build`);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
} catch (error) {
|
|
1374
|
+
const duration = timer.getElapsed() / 1e3;
|
|
1375
|
+
const errorMessage = getErrorMessage(error);
|
|
1376
|
+
if (options.json) {
|
|
1377
|
+
const outputLogger = createLogger({ silent: false, json: false });
|
|
1378
|
+
const output = createErrorOutput(errorMessage, duration);
|
|
1379
|
+
outputLogger.log("white", JSON.stringify(output, null, 2));
|
|
1380
|
+
} else {
|
|
1381
|
+
logger2.error("\u274C Bundle failed:");
|
|
1382
|
+
logger2.error(errorMessage);
|
|
1383
|
+
}
|
|
1384
|
+
process.exit(1);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
async function bundle(configOrPath, options = {}) {
|
|
1388
|
+
let rawConfig;
|
|
1389
|
+
let configPath = path9.resolve(process.cwd(), "walkeros.config.json");
|
|
1390
|
+
if (typeof configOrPath === "string") {
|
|
1391
|
+
configPath = resolveAsset(configOrPath, "config");
|
|
1392
|
+
rawConfig = await loadJsonConfig(configPath);
|
|
1393
|
+
} else {
|
|
1394
|
+
rawConfig = configOrPath;
|
|
1395
|
+
}
|
|
1396
|
+
const { flowConfig, buildOptions } = loadBundleConfig(rawConfig, {
|
|
1397
|
+
configPath,
|
|
1398
|
+
flowName: options.flowName,
|
|
1399
|
+
buildOverrides: options.buildOverrides
|
|
1400
|
+
});
|
|
1401
|
+
if (options.cache !== void 0) {
|
|
1402
|
+
buildOptions.cache = options.cache;
|
|
1403
|
+
}
|
|
1404
|
+
const logger2 = createCommandLogger(options);
|
|
1405
|
+
return await bundleCore(
|
|
1406
|
+
flowConfig,
|
|
1407
|
+
buildOptions,
|
|
1408
|
+
logger2,
|
|
1409
|
+
options.stats ?? false
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
// src/commands/simulate/simulator.ts
|
|
1414
|
+
import path10 from "path";
|
|
1415
|
+
import fs8 from "fs-extra";
|
|
1416
|
+
import { getPlatform as getPlatform2 } from "@walkeros/core";
|
|
1417
|
+
|
|
1418
|
+
// src/commands/simulate/tracker.ts
|
|
1419
|
+
var CallTracker = class {
|
|
1420
|
+
calls = /* @__PURE__ */ new Map();
|
|
1421
|
+
/**
|
|
1422
|
+
* Wrap a function to track its calls
|
|
1423
|
+
*/
|
|
1424
|
+
wrapFunction(name, fn) {
|
|
1425
|
+
const self = this;
|
|
1426
|
+
const targetFn = fn || (() => {
|
|
1427
|
+
});
|
|
1428
|
+
return new Proxy(targetFn, {
|
|
1429
|
+
apply(_target, thisArg, args) {
|
|
1430
|
+
self.logCall(name, args);
|
|
1431
|
+
return targetFn.apply(thisArg, args);
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Wrap an environment object, tracking specified paths
|
|
1437
|
+
*
|
|
1438
|
+
* @param env - Environment object (from destination's examples/env.ts)
|
|
1439
|
+
* @param paths - Paths to track (e.g., ['gtag:window.gtag', 'gtag:window.dataLayer.push'])
|
|
1440
|
+
*/
|
|
1441
|
+
wrapEnv(env, paths) {
|
|
1442
|
+
const wrapped = {};
|
|
1443
|
+
for (const [key, value] of Object.entries(env)) {
|
|
1444
|
+
if (typeof value === "object" && value !== null) {
|
|
1445
|
+
wrapped[key] = Array.isArray(value) ? [...value] : { ...value };
|
|
1446
|
+
} else {
|
|
1447
|
+
wrapped[key] = value;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
for (const fullPath of paths) {
|
|
1451
|
+
const [destKey, ...pathParts] = fullPath.split(":");
|
|
1452
|
+
const path14 = pathParts.join(":");
|
|
1453
|
+
if (!path14) continue;
|
|
1454
|
+
const cleanPath = path14.replace(/^call:/, "");
|
|
1455
|
+
const parts = cleanPath.split(".");
|
|
1456
|
+
let current = wrapped;
|
|
1457
|
+
let source = env;
|
|
1458
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1459
|
+
const part = parts[i];
|
|
1460
|
+
if (!current[part]) {
|
|
1461
|
+
current[part] = {};
|
|
1462
|
+
}
|
|
1463
|
+
current = current[part];
|
|
1464
|
+
source = source && typeof source[part] === "object" && source[part] !== null ? source[part] : void 0;
|
|
1465
|
+
}
|
|
1466
|
+
const finalKey = parts[parts.length - 1];
|
|
1467
|
+
const originalFn = source?.[finalKey];
|
|
1468
|
+
current[finalKey] = this.wrapFunction(
|
|
1469
|
+
`${destKey}:${cleanPath}`,
|
|
1470
|
+
typeof originalFn === "function" ? originalFn : void 0
|
|
1471
|
+
);
|
|
1472
|
+
}
|
|
1473
|
+
return wrapped;
|
|
1474
|
+
}
|
|
1475
|
+
logCall(fullPath, args) {
|
|
1476
|
+
const [destKey, ...pathParts] = fullPath.split(":");
|
|
1477
|
+
const apiPath = pathParts.join(":");
|
|
1478
|
+
if (!this.calls.has(destKey)) {
|
|
1479
|
+
this.calls.set(destKey, []);
|
|
1480
|
+
}
|
|
1481
|
+
this.calls.get(destKey).push({
|
|
1482
|
+
type: "call",
|
|
1483
|
+
path: apiPath,
|
|
1484
|
+
args,
|
|
1485
|
+
timestamp: Date.now()
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
getCalls() {
|
|
1489
|
+
return Object.fromEntries(this.calls);
|
|
1490
|
+
}
|
|
1491
|
+
reset() {
|
|
1492
|
+
this.calls.clear();
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
// src/commands/simulate/jsdom-executor.ts
|
|
1497
|
+
import { JSDOM, VirtualConsole } from "jsdom";
|
|
1498
|
+
import fs7 from "fs-extra";
|
|
1499
|
+
function buildSandboxFromEnvs(envs, destinations, tracker) {
|
|
1500
|
+
const baseBrowserMocks = {
|
|
1501
|
+
Image: class MockImage {
|
|
1502
|
+
src = "";
|
|
1503
|
+
onload = (() => {
|
|
1504
|
+
});
|
|
1505
|
+
onerror = (() => {
|
|
1506
|
+
});
|
|
1507
|
+
},
|
|
1508
|
+
fetch: async () => ({ ok: true, json: async () => ({}) }),
|
|
1509
|
+
location: { href: "http://localhost" },
|
|
1510
|
+
navigator: { userAgent: "Mozilla/5.0 (walkerOS Simulation)" }
|
|
1511
|
+
};
|
|
1512
|
+
const sandbox = {
|
|
1513
|
+
window: { ...baseBrowserMocks },
|
|
1514
|
+
document: {}
|
|
1515
|
+
};
|
|
1516
|
+
for (const [destKey, destConfig] of Object.entries(destinations)) {
|
|
1517
|
+
const destEnv = envs[destKey];
|
|
1518
|
+
if (!destEnv?.push) continue;
|
|
1519
|
+
const mockEnv = destEnv.push;
|
|
1520
|
+
const trackPaths = destEnv.simulation || [];
|
|
1521
|
+
const trackedEnv = tracker.wrapEnv(
|
|
1522
|
+
mockEnv,
|
|
1523
|
+
trackPaths.map((p) => `${destKey}:${p}`)
|
|
1524
|
+
);
|
|
1525
|
+
if (trackedEnv.window && typeof trackedEnv.window === "object") {
|
|
1526
|
+
Object.assign(sandbox.window, trackedEnv.window);
|
|
1527
|
+
}
|
|
1528
|
+
if (trackedEnv.document && typeof trackedEnv.document === "object") {
|
|
1529
|
+
Object.assign(sandbox.document, trackedEnv.document);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
return sandbox;
|
|
1533
|
+
}
|
|
1534
|
+
function waitForWindowProperty(window, prop, timeout = 5e3) {
|
|
1535
|
+
return new Promise((resolve3, reject) => {
|
|
1536
|
+
const start = Date.now();
|
|
1537
|
+
const check = () => {
|
|
1538
|
+
if (window[prop] !== void 0) {
|
|
1539
|
+
resolve3();
|
|
1540
|
+
} else if (Date.now() - start > timeout) {
|
|
1541
|
+
reject(
|
|
1542
|
+
new Error(
|
|
1543
|
+
`Timeout waiting for window.${prop}. IIFE may have failed to execute or assign to window.`
|
|
1544
|
+
)
|
|
1545
|
+
);
|
|
1546
|
+
} else {
|
|
1547
|
+
setImmediate(check);
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
|
+
check();
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
async function executeInJSDOM(bundlePath, destinations, event, tracker, envs, timeout = 1e4) {
|
|
1554
|
+
const start = Date.now();
|
|
1555
|
+
const virtualConsole = new VirtualConsole();
|
|
1556
|
+
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>", {
|
|
1557
|
+
url: "http://localhost",
|
|
1558
|
+
runScripts: "dangerously",
|
|
1559
|
+
// Allow script execution
|
|
1560
|
+
resources: "usable",
|
|
1561
|
+
virtualConsole
|
|
1562
|
+
});
|
|
1563
|
+
const { window } = dom;
|
|
1564
|
+
const sandbox = buildSandboxFromEnvs(envs, destinations, tracker);
|
|
1565
|
+
Object.assign(window, sandbox.window);
|
|
1566
|
+
Object.assign(window.document, sandbox.document);
|
|
1567
|
+
const bundleCode = await fs7.readFile(bundlePath, "utf8");
|
|
1568
|
+
try {
|
|
1569
|
+
window.eval(bundleCode);
|
|
1570
|
+
} catch (error) {
|
|
1571
|
+
throw new Error(`Bundle execution failed: ${getErrorMessage(error)}`);
|
|
1572
|
+
}
|
|
1573
|
+
try {
|
|
1574
|
+
await waitForWindowProperty(
|
|
1575
|
+
window,
|
|
1576
|
+
"collector",
|
|
1577
|
+
timeout
|
|
1578
|
+
);
|
|
1579
|
+
await waitForWindowProperty(
|
|
1580
|
+
window,
|
|
1581
|
+
"elb",
|
|
1582
|
+
timeout
|
|
1583
|
+
);
|
|
1584
|
+
} catch (error) {
|
|
1585
|
+
throw new Error(
|
|
1586
|
+
`Window property assignment failed: ${getErrorMessage(error)}`
|
|
1587
|
+
);
|
|
1588
|
+
}
|
|
1589
|
+
const { collector, elb } = window;
|
|
1590
|
+
let elbResult;
|
|
1591
|
+
try {
|
|
1592
|
+
elbResult = await elb(event.name, event.data);
|
|
1593
|
+
} catch (error) {
|
|
1594
|
+
throw new Error(`Event execution failed: ${getErrorMessage(error)}`);
|
|
1595
|
+
}
|
|
1596
|
+
return {
|
|
1597
|
+
collector,
|
|
1598
|
+
elb,
|
|
1599
|
+
elbResult,
|
|
1600
|
+
usage: tracker.getCalls(),
|
|
1601
|
+
duration: Date.now() - start
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
// src/commands/simulate/node-executor.ts
|
|
1606
|
+
import { pathToFileURL } from "url";
|
|
1607
|
+
function buildGlobalMocksFromEnvs(envs, destinations, tracker) {
|
|
1608
|
+
const globalMocks = {};
|
|
1609
|
+
for (const [destKey] of Object.entries(destinations)) {
|
|
1610
|
+
const destEnv = envs[destKey];
|
|
1611
|
+
if (!destEnv?.push) continue;
|
|
1612
|
+
const mockEnv = destEnv.push;
|
|
1613
|
+
const trackPaths = destEnv.simulation || [];
|
|
1614
|
+
const trackedEnv = tracker.wrapEnv(
|
|
1615
|
+
mockEnv,
|
|
1616
|
+
trackPaths.map((p) => `${destKey}:${p}`)
|
|
1617
|
+
);
|
|
1618
|
+
Object.assign(globalMocks, trackedEnv);
|
|
1619
|
+
}
|
|
1620
|
+
return globalMocks;
|
|
1621
|
+
}
|
|
1622
|
+
function injectGlobalMocks(mocks) {
|
|
1623
|
+
const originalValues = {};
|
|
1624
|
+
for (const [key, value] of Object.entries(mocks)) {
|
|
1625
|
+
originalValues[key] = globalThis[key];
|
|
1626
|
+
globalThis[key] = value;
|
|
1627
|
+
}
|
|
1628
|
+
return () => {
|
|
1629
|
+
for (const [key, value] of Object.entries(originalValues)) {
|
|
1630
|
+
if (value === void 0) {
|
|
1631
|
+
delete globalThis[key];
|
|
1632
|
+
} else {
|
|
1633
|
+
globalThis[key] = value;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1638
|
+
async function executeInNode(bundlePath, destinations, event, tracker, envs, timeout = 3e4) {
|
|
1639
|
+
const start = Date.now();
|
|
1640
|
+
const globalMocks = buildGlobalMocksFromEnvs(envs, destinations, tracker);
|
|
1641
|
+
const cleanupMocks = injectGlobalMocks(globalMocks);
|
|
1642
|
+
try {
|
|
1643
|
+
const executeWithTimeout = async () => {
|
|
1644
|
+
const importUrl = process.env.JEST_WORKER_ID ? bundlePath : `${pathToFileURL(bundlePath).href}?t=${Date.now()}`;
|
|
1645
|
+
const module = await import(importUrl);
|
|
1646
|
+
if (!module.default || typeof module.default !== "function") {
|
|
1647
|
+
throw new Error("Bundle does not export default factory function");
|
|
1648
|
+
}
|
|
1649
|
+
const result = await module.default();
|
|
1650
|
+
if (!result || !result.elb || typeof result.elb !== "function") {
|
|
1651
|
+
throw new Error(
|
|
1652
|
+
"Factory function did not return valid result with elb"
|
|
1653
|
+
);
|
|
1654
|
+
}
|
|
1655
|
+
const { collector, elb } = result;
|
|
1656
|
+
let elbResult;
|
|
1657
|
+
try {
|
|
1658
|
+
elbResult = await elb(event.name, event.data);
|
|
1659
|
+
} catch (error) {
|
|
1660
|
+
throw new Error(`Event execution failed: ${getErrorMessage(error)}`);
|
|
1661
|
+
}
|
|
1662
|
+
return {
|
|
1663
|
+
collector,
|
|
1664
|
+
elb,
|
|
1665
|
+
elbResult,
|
|
1666
|
+
usage: tracker.getCalls(),
|
|
1667
|
+
duration: Date.now() - start
|
|
1668
|
+
};
|
|
1669
|
+
};
|
|
1670
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1671
|
+
setTimeout(
|
|
1672
|
+
() => reject(new Error(`Server simulation timeout after ${timeout}ms`)),
|
|
1673
|
+
timeout
|
|
1674
|
+
);
|
|
1675
|
+
});
|
|
1676
|
+
return await Promise.race([executeWithTimeout(), timeoutPromise]);
|
|
1677
|
+
} catch (error) {
|
|
1678
|
+
throw new Error(`Node execution failed: ${getErrorMessage(error)}`);
|
|
1679
|
+
} finally {
|
|
1680
|
+
cleanupMocks();
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
// src/commands/simulate/env-loader.ts
|
|
1685
|
+
async function loadDestinationEnvs(destinations) {
|
|
1686
|
+
const envs = {};
|
|
1687
|
+
for (const [destKey, destConfig] of Object.entries(destinations)) {
|
|
1688
|
+
const typedConfig = destConfig;
|
|
1689
|
+
if (!typedConfig.package) {
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
try {
|
|
1693
|
+
const packageName = typedConfig.package;
|
|
1694
|
+
const isDemoPackage = packageName.includes("-demo");
|
|
1695
|
+
const importPath = isDemoPackage ? packageName : `${packageName}/dev`;
|
|
1696
|
+
const module = await import(importPath);
|
|
1697
|
+
const examplesModule = module.examples || module.default?.examples;
|
|
1698
|
+
const envModule = examplesModule?.env;
|
|
1699
|
+
if (envModule?.push) {
|
|
1700
|
+
envs[destKey] = {
|
|
1701
|
+
init: envModule.init,
|
|
1702
|
+
push: envModule.push,
|
|
1703
|
+
simulation: envModule.simulation || []
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
} catch (error) {
|
|
1707
|
+
console.warn(
|
|
1708
|
+
`Warning: Could not load env for destination "${destKey}": ${error instanceof Error ? error.message : String(error)}`
|
|
1709
|
+
);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
return envs;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// src/commands/simulate/simulator.ts
|
|
1716
|
+
function generateId() {
|
|
1717
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
1718
|
+
}
|
|
1719
|
+
async function simulateCore(configPath, event, options = {}) {
|
|
1720
|
+
const logger2 = createLogger({
|
|
1721
|
+
verbose: options.verbose || false,
|
|
1722
|
+
silent: options.silent || false,
|
|
1723
|
+
json: options.json || false
|
|
1724
|
+
});
|
|
1725
|
+
try {
|
|
1726
|
+
logger2.info("\u{1F3AF} Starting walkerOS simulation...");
|
|
1727
|
+
logger2.info("\u{1F4E6} Loading bundle configuration...");
|
|
1728
|
+
const fullConfigPath = path10.resolve(configPath);
|
|
1729
|
+
const rawConfig = await loadJsonConfig(fullConfigPath);
|
|
1730
|
+
loadBundleConfig(rawConfig, { configPath: fullConfigPath });
|
|
1731
|
+
logger2.info(`\u{1F680} Executing simulation with event: ${JSON.stringify(event)}`);
|
|
1732
|
+
const result = await executeSimulation(event, fullConfigPath);
|
|
1733
|
+
if (result.success) {
|
|
1734
|
+
logger2.info(`\u2705 Simulation completed successfully`);
|
|
1735
|
+
} else {
|
|
1736
|
+
logger2.error(`\u274C Simulation failed: ${result.error}`);
|
|
1737
|
+
}
|
|
1738
|
+
return result;
|
|
1739
|
+
} catch (error) {
|
|
1740
|
+
const errorMessage = getErrorMessage(error);
|
|
1741
|
+
logger2.error(`\u{1F4A5} Simulation error: ${errorMessage}`);
|
|
1742
|
+
return {
|
|
1743
|
+
success: false,
|
|
1744
|
+
error: errorMessage
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
function formatSimulationResult(result, options = {}) {
|
|
1749
|
+
if (options.json) {
|
|
1750
|
+
const output = {
|
|
1751
|
+
result: result.elbResult,
|
|
1752
|
+
usage: result.usage,
|
|
1753
|
+
duration: result.duration
|
|
1754
|
+
};
|
|
1755
|
+
return JSON.stringify(output, null, 2);
|
|
1756
|
+
}
|
|
1757
|
+
if (result.success) {
|
|
1758
|
+
return "\u2705 Simulation completed successfully";
|
|
1759
|
+
} else {
|
|
1760
|
+
return `\u274C Simulation failed: ${result.error}`;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
async function executeSimulation(event, configPath) {
|
|
1764
|
+
const startTime = Date.now();
|
|
1765
|
+
let bundlePath;
|
|
1766
|
+
const tempDir = getTmpPath();
|
|
1767
|
+
try {
|
|
1768
|
+
if (!isObject(event) || !("name" in event) || typeof event.name !== "string") {
|
|
1769
|
+
throw new Error(
|
|
1770
|
+
'Event must be an object with a "name" property of type string'
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
const typedEvent = event;
|
|
1774
|
+
await fs8.ensureDir(tempDir);
|
|
1775
|
+
const rawConfig = await loadJsonConfig(configPath);
|
|
1776
|
+
const { flowConfig, buildOptions } = loadBundleConfig(rawConfig, {
|
|
1777
|
+
configPath
|
|
1778
|
+
});
|
|
1779
|
+
const platform = getPlatform2(flowConfig);
|
|
1780
|
+
const tracker = new CallTracker();
|
|
1781
|
+
const tempOutput = path10.join(
|
|
1782
|
+
tempDir,
|
|
1783
|
+
`simulation-bundle-${generateId()}.${platform === "web" ? "js" : "mjs"}`
|
|
1784
|
+
);
|
|
1785
|
+
const destinations = flowConfig.destinations;
|
|
1786
|
+
const simulationBuildOptions = {
|
|
1787
|
+
...buildOptions,
|
|
1788
|
+
code: buildOptions.code || "",
|
|
1789
|
+
output: tempOutput,
|
|
1790
|
+
tempDir,
|
|
1791
|
+
...platform === "web" ? {
|
|
1792
|
+
format: "iife",
|
|
1793
|
+
platform: "browser",
|
|
1794
|
+
windowCollector: "collector",
|
|
1795
|
+
windowElb: "elb"
|
|
1796
|
+
} : {
|
|
1797
|
+
format: "esm",
|
|
1798
|
+
platform: "node"
|
|
1799
|
+
}
|
|
1800
|
+
};
|
|
1801
|
+
await bundleCore(
|
|
1802
|
+
flowConfig,
|
|
1803
|
+
simulationBuildOptions,
|
|
1804
|
+
createLogger({ silent: true }),
|
|
1805
|
+
false
|
|
1806
|
+
);
|
|
1807
|
+
bundlePath = tempOutput;
|
|
1808
|
+
const envs = await loadDestinationEnvs(destinations || {});
|
|
1809
|
+
let result;
|
|
1810
|
+
if (platform === "web") {
|
|
1811
|
+
result = await executeInJSDOM(
|
|
1812
|
+
tempOutput,
|
|
1813
|
+
destinations || {},
|
|
1814
|
+
typedEvent,
|
|
1815
|
+
tracker,
|
|
1816
|
+
envs,
|
|
1817
|
+
1e4
|
|
1818
|
+
);
|
|
1819
|
+
} else {
|
|
1820
|
+
result = await executeInNode(
|
|
1821
|
+
tempOutput,
|
|
1822
|
+
destinations || {},
|
|
1823
|
+
typedEvent,
|
|
1824
|
+
tracker,
|
|
1825
|
+
envs,
|
|
1826
|
+
3e4
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
const elbResult = result.elbResult;
|
|
1830
|
+
const usage = result.usage;
|
|
1831
|
+
const duration = Date.now() - startTime;
|
|
1832
|
+
return {
|
|
1833
|
+
success: true,
|
|
1834
|
+
elbResult,
|
|
1835
|
+
usage,
|
|
1836
|
+
duration,
|
|
1837
|
+
logs: []
|
|
1838
|
+
};
|
|
1839
|
+
} catch (error) {
|
|
1840
|
+
const duration = Date.now() - startTime;
|
|
1841
|
+
return {
|
|
1842
|
+
success: false,
|
|
1843
|
+
error: getErrorMessage(error),
|
|
1844
|
+
duration
|
|
1845
|
+
};
|
|
1846
|
+
} finally {
|
|
1847
|
+
if (tempDir) {
|
|
1848
|
+
await fs8.remove(tempDir).catch(() => {
|
|
1849
|
+
});
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
// src/commands/simulate/index.ts
|
|
1855
|
+
async function simulateCommand(options) {
|
|
1856
|
+
const logger2 = createCommandLogger(options);
|
|
1857
|
+
if (options.dryRun) {
|
|
1858
|
+
logger2.info(
|
|
1859
|
+
`[DRY-RUN] Would execute simulate with config: ${options.config}`
|
|
1860
|
+
);
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
const startTime = Date.now();
|
|
1864
|
+
try {
|
|
1865
|
+
const event = await loadJsonFromSource(options.event, {
|
|
1866
|
+
name: "event"
|
|
1867
|
+
});
|
|
1868
|
+
const result = await simulateCore(options.config, event, {
|
|
1869
|
+
json: options.json,
|
|
1870
|
+
verbose: options.verbose,
|
|
1871
|
+
silent: options.silent
|
|
1872
|
+
});
|
|
1873
|
+
const resultWithDuration = {
|
|
1874
|
+
...result,
|
|
1875
|
+
duration: (Date.now() - startTime) / 1e3
|
|
1876
|
+
};
|
|
1877
|
+
const outputLogger = createLogger({ silent: false, json: false });
|
|
1878
|
+
const output = formatSimulationResult(resultWithDuration, {
|
|
1879
|
+
json: options.json
|
|
1880
|
+
});
|
|
1881
|
+
outputLogger.log("white", output);
|
|
1882
|
+
if (!result.success) {
|
|
1883
|
+
process.exit(1);
|
|
1884
|
+
}
|
|
1885
|
+
} catch (error) {
|
|
1886
|
+
const errorMessage = getErrorMessage(error);
|
|
1887
|
+
if (options.json) {
|
|
1888
|
+
const outputLogger = createLogger({ silent: false, json: false });
|
|
1889
|
+
const errorOutput = JSON.stringify(
|
|
1890
|
+
{
|
|
1891
|
+
success: false,
|
|
1892
|
+
error: errorMessage,
|
|
1893
|
+
duration: (Date.now() - startTime) / 1e3
|
|
1894
|
+
},
|
|
1895
|
+
null,
|
|
1896
|
+
2
|
|
1897
|
+
);
|
|
1898
|
+
outputLogger.log("white", errorOutput);
|
|
1899
|
+
} else {
|
|
1900
|
+
const errorLogger = createLogger({ silent: false, json: false });
|
|
1901
|
+
errorLogger.error(`\u274C Simulate command failed: ${errorMessage}`);
|
|
1902
|
+
}
|
|
1903
|
+
process.exit(1);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
async function simulate(configOrPath, event, options = {}) {
|
|
1907
|
+
if (typeof configOrPath !== "string") {
|
|
1908
|
+
throw new Error(
|
|
1909
|
+
"simulate() currently only supports config file paths. Config object support will be added in a future version. Please provide a path to a configuration file."
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
return await simulateCore(configOrPath, event, {
|
|
1913
|
+
json: options.json ?? false,
|
|
1914
|
+
verbose: options.verbose ?? false
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
// src/commands/push/index.ts
|
|
1919
|
+
import path11 from "path";
|
|
1920
|
+
import { JSDOM as JSDOM2, VirtualConsole as VirtualConsole2 } from "jsdom";
|
|
1921
|
+
import fs9 from "fs-extra";
|
|
1922
|
+
import { getPlatform as getPlatform3 } from "@walkeros/core";
|
|
1923
|
+
import { schemas as schemas2 } from "@walkeros/core/dev";
|
|
1924
|
+
async function pushCommand(options) {
|
|
1925
|
+
const logger2 = createCommandLogger(options);
|
|
1926
|
+
const startTime = Date.now();
|
|
1927
|
+
try {
|
|
1928
|
+
logger2.info("\u{1F4E5} Loading event...");
|
|
1929
|
+
const event = await loadJsonFromSource(options.event, {
|
|
1930
|
+
name: "event"
|
|
1931
|
+
});
|
|
1932
|
+
const eventResult = schemas2.PartialEventSchema.safeParse(event);
|
|
1933
|
+
if (!eventResult.success) {
|
|
1934
|
+
const errors = eventResult.error.issues.map((issue) => `${String(issue.path.join("."))}: ${issue.message}`).join(", ");
|
|
1935
|
+
throw new Error(`Invalid event: ${errors}`);
|
|
1936
|
+
}
|
|
1937
|
+
const parsedEvent = eventResult.data;
|
|
1938
|
+
if (!parsedEvent.name) {
|
|
1939
|
+
throw new Error('Invalid event: Missing required "name" property');
|
|
1940
|
+
}
|
|
1941
|
+
const validatedEvent = {
|
|
1942
|
+
name: parsedEvent.name,
|
|
1943
|
+
data: parsedEvent.data || {}
|
|
1944
|
+
};
|
|
1945
|
+
if (!validatedEvent.name.includes(" ")) {
|
|
1946
|
+
logger2.warn(
|
|
1947
|
+
`Event name "${validatedEvent.name}" should follow "ENTITY ACTION" format (e.g., "page view")`
|
|
1948
|
+
);
|
|
1949
|
+
}
|
|
1950
|
+
logger2.info("\u{1F4E6} Loading flow configuration...");
|
|
1951
|
+
const configPath = path11.resolve(options.config);
|
|
1952
|
+
const rawConfig = await loadJsonConfig(configPath);
|
|
1953
|
+
const { flowConfig, buildOptions, flowName, isMultiFlow } = loadBundleConfig(rawConfig, {
|
|
1954
|
+
configPath: options.config,
|
|
1955
|
+
flowName: options.flow,
|
|
1956
|
+
logger: logger2
|
|
1957
|
+
});
|
|
1958
|
+
const platform = getPlatform3(flowConfig);
|
|
1959
|
+
logger2.info("\u{1F528} Bundling flow configuration...");
|
|
1960
|
+
const configDir = path11.dirname(configPath);
|
|
1961
|
+
const tempDir = path11.join(
|
|
1962
|
+
configDir,
|
|
1963
|
+
".tmp",
|
|
1964
|
+
`push-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
|
1965
|
+
);
|
|
1966
|
+
await fs9.ensureDir(tempDir);
|
|
1967
|
+
const tempPath = path11.join(
|
|
1968
|
+
tempDir,
|
|
1969
|
+
`bundle.${platform === "web" ? "js" : "mjs"}`
|
|
1970
|
+
);
|
|
1971
|
+
const pushBuildOptions = {
|
|
1972
|
+
...buildOptions,
|
|
1973
|
+
output: tempPath,
|
|
1974
|
+
// Web uses IIFE for browser-like execution, server uses ESM
|
|
1975
|
+
format: platform === "web" ? "iife" : "esm",
|
|
1976
|
+
platform: platform === "web" ? "browser" : "node",
|
|
1977
|
+
...platform === "web" && {
|
|
1978
|
+
windowCollector: "collector",
|
|
1979
|
+
windowElb: "elb"
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
await bundleCore(flowConfig, pushBuildOptions, logger2, false);
|
|
1983
|
+
logger2.debug(`Bundle created: ${tempPath}`);
|
|
1984
|
+
let result;
|
|
1985
|
+
if (platform === "web") {
|
|
1986
|
+
logger2.info("\u{1F310} Executing in web environment (JSDOM)...");
|
|
1987
|
+
result = await executeWebPush(tempPath, validatedEvent, logger2);
|
|
1988
|
+
} else if (platform === "server") {
|
|
1989
|
+
logger2.info("\u{1F5A5}\uFE0F Executing in server environment (Node.js)...");
|
|
1990
|
+
result = await executeServerPush(tempPath, validatedEvent, logger2);
|
|
1991
|
+
} else {
|
|
1992
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
1993
|
+
}
|
|
1994
|
+
const duration = Date.now() - startTime;
|
|
1995
|
+
if (options.json) {
|
|
1996
|
+
const outputLogger = createLogger({ silent: false, json: false });
|
|
1997
|
+
outputLogger.log(
|
|
1998
|
+
"white",
|
|
1999
|
+
JSON.stringify(
|
|
2000
|
+
{
|
|
2001
|
+
success: result.success,
|
|
2002
|
+
event: result.elbResult,
|
|
2003
|
+
duration
|
|
2004
|
+
},
|
|
2005
|
+
null,
|
|
2006
|
+
2
|
|
2007
|
+
)
|
|
2008
|
+
);
|
|
2009
|
+
} else {
|
|
2010
|
+
if (result.success) {
|
|
2011
|
+
logger2.success("\u2705 Event pushed successfully");
|
|
2012
|
+
if (result.elbResult && typeof result.elbResult === "object") {
|
|
2013
|
+
const pushResult = result.elbResult;
|
|
2014
|
+
if ("id" in pushResult && pushResult.id) {
|
|
2015
|
+
logger2.info(` Event ID: ${pushResult.id}`);
|
|
2016
|
+
}
|
|
2017
|
+
if ("entity" in pushResult && pushResult.entity) {
|
|
2018
|
+
logger2.info(` Entity: ${pushResult.entity}`);
|
|
2019
|
+
}
|
|
2020
|
+
if ("action" in pushResult && pushResult.action) {
|
|
2021
|
+
logger2.info(` Action: ${pushResult.action}`);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
logger2.info(` Duration: ${duration}ms`);
|
|
2025
|
+
} else {
|
|
2026
|
+
logger2.error(`\u274C Push failed: ${result.error}`);
|
|
2027
|
+
process.exit(1);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
try {
|
|
2031
|
+
await fs9.remove(tempDir);
|
|
2032
|
+
} catch {
|
|
2033
|
+
}
|
|
2034
|
+
} catch (error) {
|
|
2035
|
+
const duration = Date.now() - startTime;
|
|
2036
|
+
const errorMessage = getErrorMessage(error);
|
|
2037
|
+
if (options.json) {
|
|
2038
|
+
const outputLogger = createLogger({ silent: false, json: false });
|
|
2039
|
+
outputLogger.log(
|
|
2040
|
+
"white",
|
|
2041
|
+
JSON.stringify(
|
|
2042
|
+
{
|
|
2043
|
+
success: false,
|
|
2044
|
+
error: errorMessage,
|
|
2045
|
+
duration
|
|
2046
|
+
},
|
|
2047
|
+
null,
|
|
2048
|
+
2
|
|
2049
|
+
)
|
|
2050
|
+
);
|
|
2051
|
+
} else {
|
|
2052
|
+
logger2.error(`\u274C Push command failed: ${errorMessage}`);
|
|
2053
|
+
}
|
|
2054
|
+
process.exit(1);
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
async function executeWebPush(bundlePath, event, logger2) {
|
|
2058
|
+
const startTime = Date.now();
|
|
2059
|
+
try {
|
|
2060
|
+
const virtualConsole = new VirtualConsole2();
|
|
2061
|
+
const dom = new JSDOM2("<!DOCTYPE html><html><body></body></html>", {
|
|
2062
|
+
url: "http://localhost",
|
|
2063
|
+
runScripts: "dangerously",
|
|
2064
|
+
resources: "usable",
|
|
2065
|
+
virtualConsole
|
|
2066
|
+
});
|
|
2067
|
+
const { window } = dom;
|
|
2068
|
+
logger2.debug("Loading bundle...");
|
|
2069
|
+
const bundleCode = await fs9.readFile(bundlePath, "utf8");
|
|
2070
|
+
window.eval(bundleCode);
|
|
2071
|
+
logger2.debug("Waiting for elb...");
|
|
2072
|
+
await waitForWindowProperty2(
|
|
2073
|
+
window,
|
|
2074
|
+
"elb",
|
|
2075
|
+
5e3
|
|
2076
|
+
);
|
|
2077
|
+
const windowObj = window;
|
|
2078
|
+
const elb = windowObj.elb;
|
|
2079
|
+
logger2.info(`Pushing event: ${event.name}`);
|
|
2080
|
+
const elbResult = await elb(event.name, event.data);
|
|
2081
|
+
return {
|
|
2082
|
+
success: true,
|
|
2083
|
+
elbResult,
|
|
2084
|
+
duration: Date.now() - startTime
|
|
2085
|
+
};
|
|
2086
|
+
} catch (error) {
|
|
2087
|
+
return {
|
|
2088
|
+
success: false,
|
|
2089
|
+
duration: Date.now() - startTime,
|
|
2090
|
+
error: getErrorMessage(error)
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
async function executeServerPush(bundlePath, event, logger2, timeout = 6e4) {
|
|
2095
|
+
const startTime = Date.now();
|
|
2096
|
+
try {
|
|
2097
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
2098
|
+
setTimeout(
|
|
2099
|
+
() => reject(new Error(`Server push timeout after ${timeout}ms`)),
|
|
2100
|
+
timeout
|
|
2101
|
+
);
|
|
2102
|
+
});
|
|
2103
|
+
const executePromise = (async () => {
|
|
2104
|
+
logger2.debug("Importing bundle...");
|
|
2105
|
+
const flowModule = await import(bundlePath);
|
|
2106
|
+
if (!flowModule.default || typeof flowModule.default !== "function") {
|
|
2107
|
+
throw new Error("Bundle does not export default factory function");
|
|
2108
|
+
}
|
|
2109
|
+
logger2.debug("Calling factory function...");
|
|
2110
|
+
const result = await flowModule.default();
|
|
2111
|
+
if (!result || !result.elb || typeof result.elb !== "function") {
|
|
2112
|
+
throw new Error(
|
|
2113
|
+
"Factory function did not return valid result with elb"
|
|
2114
|
+
);
|
|
2115
|
+
}
|
|
2116
|
+
const { elb } = result;
|
|
2117
|
+
logger2.info(`Pushing event: ${event.name}`);
|
|
2118
|
+
const elbResult = await elb(event.name, event.data);
|
|
2119
|
+
return {
|
|
2120
|
+
success: true,
|
|
2121
|
+
elbResult,
|
|
2122
|
+
duration: Date.now() - startTime
|
|
2123
|
+
};
|
|
2124
|
+
})();
|
|
2125
|
+
return await Promise.race([executePromise, timeoutPromise]);
|
|
2126
|
+
} catch (error) {
|
|
2127
|
+
return {
|
|
2128
|
+
success: false,
|
|
2129
|
+
duration: Date.now() - startTime,
|
|
2130
|
+
error: getErrorMessage(error)
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
function waitForWindowProperty2(window, prop, timeout = 5e3) {
|
|
2135
|
+
return new Promise((resolve3, reject) => {
|
|
2136
|
+
const start = Date.now();
|
|
2137
|
+
const check = () => {
|
|
2138
|
+
if (window[prop] !== void 0) {
|
|
2139
|
+
resolve3();
|
|
2140
|
+
} else if (Date.now() - start > timeout) {
|
|
2141
|
+
reject(
|
|
2142
|
+
new Error(
|
|
2143
|
+
`Timeout waiting for window.${prop}. IIFE may have failed to execute.`
|
|
2144
|
+
)
|
|
2145
|
+
);
|
|
2146
|
+
} else {
|
|
2147
|
+
setImmediate(check);
|
|
2148
|
+
}
|
|
2149
|
+
};
|
|
2150
|
+
check();
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
// src/commands/run/index.ts
|
|
2155
|
+
import path13 from "path";
|
|
2156
|
+
|
|
2157
|
+
// src/commands/run/validators.ts
|
|
2158
|
+
import { existsSync as existsSync2 } from "fs";
|
|
2159
|
+
|
|
2160
|
+
// src/schemas/primitives.ts
|
|
2161
|
+
import { z } from "@walkeros/core/dev";
|
|
2162
|
+
var RunModeSchema = z.enum(["collect", "serve"]).describe("CLI run mode: collect events or serve HTTP");
|
|
2163
|
+
var PortSchema = z.number().int("Port must be an integer").min(1, "Port must be at least 1").max(65535, "Port must be at most 65535").describe("HTTP server port number");
|
|
2164
|
+
var FilePathSchema = z.string().min(1, "File path cannot be empty").describe("Path to configuration file");
|
|
2165
|
+
|
|
2166
|
+
// src/schemas/run.ts
|
|
2167
|
+
import { z as z2 } from "@walkeros/core/dev";
|
|
2168
|
+
var RunOptionsSchema = z2.object({
|
|
2169
|
+
mode: RunModeSchema,
|
|
2170
|
+
flow: FilePathSchema,
|
|
2171
|
+
port: PortSchema.default(8080),
|
|
2172
|
+
flowName: z2.string().optional().describe("Specific flow name to run")
|
|
2173
|
+
});
|
|
2174
|
+
|
|
2175
|
+
// src/commands/run/validators.ts
|
|
2176
|
+
function validateMode(mode) {
|
|
2177
|
+
const result = RunModeSchema.safeParse(mode);
|
|
2178
|
+
if (!result.success) {
|
|
2179
|
+
throw new Error(
|
|
2180
|
+
`Invalid mode: "${mode}"
|
|
2181
|
+
Valid modes: collect, serve
|
|
2182
|
+
Example: walkeros run collect ./flow.json`
|
|
2183
|
+
);
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
function validateFlowFile(filePath) {
|
|
2187
|
+
const absolutePath = resolveAsset(filePath, "bundle");
|
|
2188
|
+
if (!existsSync2(absolutePath)) {
|
|
2189
|
+
throw new Error(
|
|
2190
|
+
`Flow file not found: ${filePath}
|
|
2191
|
+
Resolved path: ${absolutePath}
|
|
2192
|
+
Make sure the file exists and the path is correct`
|
|
2193
|
+
);
|
|
2194
|
+
}
|
|
2195
|
+
return absolutePath;
|
|
2196
|
+
}
|
|
2197
|
+
function validatePort(port) {
|
|
2198
|
+
const result = PortSchema.safeParse(port);
|
|
2199
|
+
if (!result.success) {
|
|
2200
|
+
throw new Error(
|
|
2201
|
+
`Invalid port: ${port}
|
|
2202
|
+
Port must be an integer between 1 and 65535
|
|
2203
|
+
Example: --port 8080`
|
|
2204
|
+
);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
// src/commands/run/utils.ts
|
|
2209
|
+
import path12 from "path";
|
|
2210
|
+
import fs10 from "fs-extra";
|
|
2211
|
+
async function prepareBundleForRun(configPath, options) {
|
|
2212
|
+
const configDir = path12.dirname(path12.resolve(configPath));
|
|
2213
|
+
const tempDir = path12.join(
|
|
2214
|
+
configDir,
|
|
2215
|
+
".tmp",
|
|
2216
|
+
`run-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
|
2217
|
+
);
|
|
2218
|
+
await fs10.ensureDir(tempDir);
|
|
2219
|
+
const tempPath = path12.join(tempDir, "bundle.mjs");
|
|
2220
|
+
await bundle(configPath, {
|
|
2221
|
+
cache: true,
|
|
2222
|
+
verbose: options.verbose,
|
|
2223
|
+
silent: options.silent,
|
|
2224
|
+
buildOverrides: {
|
|
2225
|
+
output: tempPath,
|
|
2226
|
+
format: "esm",
|
|
2227
|
+
platform: "node"
|
|
2228
|
+
}
|
|
2229
|
+
});
|
|
2230
|
+
return tempPath;
|
|
2231
|
+
}
|
|
2232
|
+
function isPreBuiltConfig(configPath) {
|
|
2233
|
+
return configPath.endsWith(".mjs") || configPath.endsWith(".js") || configPath.endsWith(".cjs");
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
// src/commands/run/execution.ts
|
|
2237
|
+
import { createLogger as createLogger2, Level } from "@walkeros/core";
|
|
2238
|
+
|
|
2239
|
+
// src/runtime/runner.ts
|
|
2240
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2241
|
+
import { resolve, dirname as dirname2 } from "path";
|
|
2242
|
+
async function runFlow(file, config, logger2) {
|
|
2243
|
+
logger2.info(`Loading flow from ${file}`);
|
|
2244
|
+
try {
|
|
2245
|
+
const absolutePath = resolve(file);
|
|
2246
|
+
const flowDir = dirname2(absolutePath);
|
|
2247
|
+
process.chdir(flowDir);
|
|
2248
|
+
const fileUrl = pathToFileURL2(absolutePath).href;
|
|
2249
|
+
const module = await import(fileUrl);
|
|
2250
|
+
if (!module.default || typeof module.default !== "function") {
|
|
2251
|
+
logger2.throw(
|
|
2252
|
+
`Invalid flow bundle: ${file} must export a default function`
|
|
2253
|
+
);
|
|
2254
|
+
}
|
|
2255
|
+
const result = await module.default(config);
|
|
2256
|
+
if (!result || !result.collector) {
|
|
2257
|
+
logger2.throw(`Invalid flow bundle: ${file} must return { collector }`);
|
|
2258
|
+
}
|
|
2259
|
+
const { collector } = result;
|
|
2260
|
+
logger2.info("Flow running");
|
|
2261
|
+
if (config?.port) {
|
|
2262
|
+
logger2.info(`Port: ${config.port}`);
|
|
2263
|
+
}
|
|
2264
|
+
const shutdown = async (signal) => {
|
|
2265
|
+
logger2.info(`Received ${signal}, shutting down gracefully...`);
|
|
2266
|
+
try {
|
|
2267
|
+
if (collector.command) {
|
|
2268
|
+
await collector.command("shutdown");
|
|
2269
|
+
}
|
|
2270
|
+
logger2.info("Shutdown complete");
|
|
2271
|
+
process.exit(0);
|
|
2272
|
+
} catch (error) {
|
|
2273
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2274
|
+
logger2.error(`Error during shutdown: ${message}`);
|
|
2275
|
+
process.exit(1);
|
|
2276
|
+
}
|
|
2277
|
+
};
|
|
2278
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
2279
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
2280
|
+
await new Promise(() => {
|
|
2281
|
+
});
|
|
2282
|
+
} catch (error) {
|
|
2283
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2284
|
+
logger2.error(`Failed to run flow: ${message}`);
|
|
2285
|
+
if (error instanceof Error && error.stack) {
|
|
2286
|
+
logger2.debug("Stack trace:", { stack: error.stack });
|
|
2287
|
+
}
|
|
2288
|
+
throw error;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
// src/runtime/serve.ts
|
|
2293
|
+
import express from "express";
|
|
2294
|
+
import { resolve as resolve2 } from "path";
|
|
2295
|
+
async function runServeMode(config, logger2) {
|
|
2296
|
+
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : config?.port || 8080;
|
|
2297
|
+
const host = process.env.HOST || config?.host || "0.0.0.0";
|
|
2298
|
+
const file = resolve2(process.env.FILE || config?.file || "./dist/walker.js");
|
|
2299
|
+
const serveName = process.env.SERVE_NAME || config?.serveName || "walker.js";
|
|
2300
|
+
const servePath = process.env.SERVE_PATH || config?.servePath || "";
|
|
2301
|
+
const urlPath = servePath ? `/${servePath}/${serveName}` : `/${serveName}`;
|
|
2302
|
+
logger2.info("Starting single-file server...");
|
|
2303
|
+
logger2.info(`File: ${file}`);
|
|
2304
|
+
logger2.info(`URL: http://${host}:${port}${urlPath}`);
|
|
2305
|
+
try {
|
|
2306
|
+
const app = express();
|
|
2307
|
+
app.get("/health", (req, res) => {
|
|
2308
|
+
res.json({
|
|
2309
|
+
status: "ok",
|
|
2310
|
+
version: VERSION,
|
|
2311
|
+
timestamp: Date.now(),
|
|
2312
|
+
mode: "serve",
|
|
2313
|
+
file,
|
|
2314
|
+
url: urlPath
|
|
2315
|
+
});
|
|
2316
|
+
});
|
|
2317
|
+
app.get(urlPath, (req, res) => {
|
|
2318
|
+
res.type("application/javascript");
|
|
2319
|
+
res.sendFile(file, { dotfiles: "allow" }, (err) => {
|
|
2320
|
+
if (err && !res.headersSent) {
|
|
2321
|
+
const errCode = err.code;
|
|
2322
|
+
const errStatus = err.status || err.statusCode;
|
|
2323
|
+
if (errCode !== "ECONNABORTED") {
|
|
2324
|
+
logger2.error(
|
|
2325
|
+
`sendFile error for ${file}: ${err.message} (code: ${errCode}, status: ${errStatus})`
|
|
2326
|
+
);
|
|
2327
|
+
}
|
|
2328
|
+
if (errStatus === 404 || errCode === "ENOENT" || errCode === "EISDIR" || errCode === "ENOTDIR") {
|
|
2329
|
+
res.status(404).send("File not found");
|
|
2330
|
+
} else if (errCode !== "ECONNABORTED") {
|
|
2331
|
+
res.status(500).send("Internal server error");
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
});
|
|
2335
|
+
});
|
|
2336
|
+
const server = app.listen(port, host, () => {
|
|
2337
|
+
logger2.info(`Server listening on http://${host}:${port}`);
|
|
2338
|
+
logger2.info(`GET ${urlPath} - Bundle file`);
|
|
2339
|
+
logger2.info(`GET /health - Health check`);
|
|
2340
|
+
});
|
|
2341
|
+
const shutdownHandler = (signal) => {
|
|
2342
|
+
logger2.info(`Received ${signal}, shutting down...`);
|
|
2343
|
+
server.close(() => {
|
|
2344
|
+
logger2.info("Server closed");
|
|
2345
|
+
process.exit(0);
|
|
2346
|
+
});
|
|
2347
|
+
};
|
|
2348
|
+
process.on("SIGTERM", () => shutdownHandler("SIGTERM"));
|
|
2349
|
+
process.on("SIGINT", () => shutdownHandler("SIGINT"));
|
|
2350
|
+
await new Promise(() => {
|
|
2351
|
+
});
|
|
2352
|
+
} catch (error) {
|
|
2353
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2354
|
+
logger2.error(`Server failed: ${message}`);
|
|
2355
|
+
process.exit(1);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
// src/commands/run/execution.ts
|
|
2360
|
+
var logLevel = process.env.VERBOSE === "true" ? Level.DEBUG : Level.INFO;
|
|
2361
|
+
var logger = createLogger2({ level: logLevel });
|
|
2362
|
+
async function executeRunLocal(mode, flowPath, options) {
|
|
2363
|
+
switch (mode) {
|
|
2364
|
+
case "collect": {
|
|
2365
|
+
if (!flowPath) {
|
|
2366
|
+
throw new Error("Flow path is required for collect mode");
|
|
2367
|
+
}
|
|
2368
|
+
const config = {
|
|
2369
|
+
port: options.port,
|
|
2370
|
+
host: options.host
|
|
2371
|
+
};
|
|
2372
|
+
await runFlow(flowPath, config, logger.scope("runner"));
|
|
2373
|
+
break;
|
|
2374
|
+
}
|
|
2375
|
+
case "serve": {
|
|
2376
|
+
const config = {
|
|
2377
|
+
port: options.port,
|
|
2378
|
+
host: options.host,
|
|
2379
|
+
serveName: options.serveName,
|
|
2380
|
+
servePath: options.servePath,
|
|
2381
|
+
file: flowPath || void 0
|
|
2382
|
+
};
|
|
2383
|
+
await runServeMode(config, logger.scope("serve"));
|
|
2384
|
+
break;
|
|
2385
|
+
}
|
|
2386
|
+
default:
|
|
2387
|
+
throw new Error(`Unknown mode: ${mode}`);
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
// src/commands/run/index.ts
|
|
2392
|
+
async function runCommand(mode, options) {
|
|
2393
|
+
const timer = createTimer();
|
|
2394
|
+
timer.start();
|
|
2395
|
+
const logger2 = createCommandLogger(options);
|
|
2396
|
+
try {
|
|
2397
|
+
validateMode(mode);
|
|
2398
|
+
const configPath = validateFlowFile(options.config);
|
|
2399
|
+
if (options.port !== void 0) {
|
|
2400
|
+
validatePort(options.port);
|
|
2401
|
+
}
|
|
2402
|
+
const isPreBuilt = isPreBuiltConfig(configPath);
|
|
2403
|
+
let flowPath = null;
|
|
2404
|
+
if (mode === "collect") {
|
|
2405
|
+
if (isPreBuilt) {
|
|
2406
|
+
flowPath = path13.resolve(configPath);
|
|
2407
|
+
if (!options.json && !options.silent) {
|
|
2408
|
+
logger2.info(`\u{1F4E6} Using pre-built flow: ${path13.basename(flowPath)}`);
|
|
2409
|
+
}
|
|
2410
|
+
} else {
|
|
2411
|
+
if (!options.json && !options.silent) {
|
|
2412
|
+
logger2.info("\u{1F528} Building flow bundle...");
|
|
2413
|
+
}
|
|
2414
|
+
flowPath = await prepareBundleForRun(configPath, {
|
|
2415
|
+
verbose: options.verbose,
|
|
2416
|
+
silent: options.json || options.silent
|
|
2417
|
+
});
|
|
2418
|
+
if (!options.json && !options.silent) {
|
|
2419
|
+
logger2.success("\u2705 Bundle ready");
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
if (options.dryRun) {
|
|
2424
|
+
logger2.info(`[DRY-RUN] Would execute locally: run ${mode}`);
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
2427
|
+
if (!options.json && !options.silent) {
|
|
2428
|
+
const modeLabel = mode === "collect" ? "Collector" : "Server";
|
|
2429
|
+
logger2.info(`\u{1F5A5}\uFE0F Starting ${modeLabel} locally...`);
|
|
2430
|
+
}
|
|
2431
|
+
await executeRunLocal(mode, flowPath, {
|
|
2432
|
+
port: options.port,
|
|
2433
|
+
host: options.host,
|
|
2434
|
+
serveName: options.serveName,
|
|
2435
|
+
servePath: options.servePath
|
|
2436
|
+
});
|
|
2437
|
+
} catch (error) {
|
|
2438
|
+
const duration = timer.getElapsed() / 1e3;
|
|
2439
|
+
const errorMessage = getErrorMessage(error);
|
|
2440
|
+
if (options.json) {
|
|
2441
|
+
const output = {
|
|
2442
|
+
success: false,
|
|
2443
|
+
mode,
|
|
2444
|
+
error: errorMessage,
|
|
2445
|
+
duration
|
|
2446
|
+
};
|
|
2447
|
+
console.log(JSON.stringify(output, null, 2));
|
|
2448
|
+
} else {
|
|
2449
|
+
logger2.error("\u274C Run failed:");
|
|
2450
|
+
logger2.error(errorMessage);
|
|
2451
|
+
}
|
|
2452
|
+
process.exit(1);
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
async function run(mode, options) {
|
|
2456
|
+
const startTime = Date.now();
|
|
2457
|
+
try {
|
|
2458
|
+
validateMode(mode);
|
|
2459
|
+
let flowFile;
|
|
2460
|
+
if (typeof options.config === "string") {
|
|
2461
|
+
flowFile = validateFlowFile(options.config);
|
|
2462
|
+
} else {
|
|
2463
|
+
throw new Error("Programmatic run() requires config file path");
|
|
2464
|
+
}
|
|
2465
|
+
if (options.port !== void 0) {
|
|
2466
|
+
validatePort(options.port);
|
|
2467
|
+
}
|
|
2468
|
+
const isPreBuilt = isPreBuiltConfig(flowFile);
|
|
2469
|
+
let flowPath;
|
|
2470
|
+
if (isPreBuilt) {
|
|
2471
|
+
flowPath = path13.resolve(flowFile);
|
|
2472
|
+
} else {
|
|
2473
|
+
flowPath = await prepareBundleForRun(flowFile, {
|
|
2474
|
+
verbose: options.verbose,
|
|
2475
|
+
silent: true
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
await executeRunLocal(mode, flowPath, {
|
|
2479
|
+
port: options.port,
|
|
2480
|
+
host: options.host,
|
|
2481
|
+
serveName: options.serveName,
|
|
2482
|
+
servePath: options.servePath
|
|
2483
|
+
});
|
|
2484
|
+
return {
|
|
2485
|
+
success: true,
|
|
2486
|
+
exitCode: 0,
|
|
2487
|
+
duration: Date.now() - startTime
|
|
2488
|
+
};
|
|
2489
|
+
} catch (error) {
|
|
2490
|
+
return {
|
|
2491
|
+
success: false,
|
|
2492
|
+
exitCode: 1,
|
|
2493
|
+
duration: Date.now() - startTime,
|
|
2494
|
+
error: getErrorMessage(error)
|
|
2495
|
+
};
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
// src/commands/cache.ts
|
|
2500
|
+
import fs11 from "fs-extra";
|
|
2501
|
+
function registerCacheCommand(program2) {
|
|
2502
|
+
const cache = program2.command("cache").description("Manage the CLI cache");
|
|
2503
|
+
cache.command("clear").description("Clear all cached packages and builds").option("--packages", "Clear only package cache").option("--builds", "Clear only build cache").option("--tmp-dir <dir>", "Custom temp directory").action(async (options) => {
|
|
2504
|
+
const tmpDir = options.tmpDir;
|
|
2505
|
+
if (options.packages) {
|
|
2506
|
+
await fs11.remove(getTmpPath(tmpDir, "cache", "packages"));
|
|
2507
|
+
console.log("Package cache cleared");
|
|
2508
|
+
} else if (options.builds) {
|
|
2509
|
+
await fs11.remove(getTmpPath(tmpDir, "cache", "builds"));
|
|
2510
|
+
console.log("Build cache cleared");
|
|
2511
|
+
} else {
|
|
2512
|
+
await fs11.remove(getTmpPath(tmpDir, "cache"));
|
|
2513
|
+
console.log("All caches cleared");
|
|
2514
|
+
}
|
|
2515
|
+
});
|
|
2516
|
+
cache.command("info").description("Show cache statistics").option("--tmp-dir <dir>", "Custom temp directory").action(async (options) => {
|
|
2517
|
+
const tmpDir = options.tmpDir;
|
|
2518
|
+
const packagesDir = getTmpPath(tmpDir, "cache", "packages");
|
|
2519
|
+
const buildsDir = getTmpPath(tmpDir, "cache", "builds");
|
|
2520
|
+
const packageCount = await countEntries(packagesDir);
|
|
2521
|
+
const buildCount = await countEntries(buildsDir);
|
|
2522
|
+
console.log(`Cache directory: ${getTmpPath(tmpDir, "cache")}`);
|
|
2523
|
+
console.log(`Cached packages: ${packageCount}`);
|
|
2524
|
+
console.log(`Cached builds: ${buildCount}`);
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
function registerCleanCommand(program2) {
|
|
2528
|
+
program2.command("clean").description("Clear the entire temp directory (.tmp/)").option("--tmp-dir <dir>", "Custom temp directory").action(async (options) => {
|
|
2529
|
+
const tmpDir = options.tmpDir || getDefaultTmpRoot();
|
|
2530
|
+
await fs11.remove(tmpDir);
|
|
2531
|
+
console.log(`Temp directory cleared: ${tmpDir}`);
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2534
|
+
async function countEntries(dir) {
|
|
2535
|
+
if (!await fs11.pathExists(dir)) return 0;
|
|
2536
|
+
const entries = await fs11.readdir(dir);
|
|
2537
|
+
return entries.length;
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
// src/index.ts
|
|
2541
|
+
var program = new Command();
|
|
2542
|
+
program.name("walkeros").description("walkerOS CLI - Bundle and deploy walkerOS components").version(VERSION);
|
|
2543
|
+
program.hook("preAction", (thisCommand, actionCommand) => {
|
|
2544
|
+
const options = actionCommand.opts();
|
|
2545
|
+
if (!options.silent && !options.json) {
|
|
2546
|
+
console.log(`\u{1F680} walkerOS CLI v${VERSION}`);
|
|
2547
|
+
}
|
|
2548
|
+
});
|
|
2549
|
+
program.command("bundle [file]").description("Bundle NPM packages with custom code").option("-f, --flow <name>", "flow to build (for multi-flow configs)").option("--all", "build all flows (for multi-flow configs)").option("-s, --stats", "show bundle statistics").option("--json", "output statistics in JSON format (implies --stats)").option("--no-cache", "disable package caching and download fresh packages").option("-v, --verbose", "verbose output").option("--dry-run", "preview command without executing").option("--silent", "suppress output").action(async (file, options) => {
|
|
2550
|
+
await bundleCommand({
|
|
2551
|
+
config: file || "bundle.config.json",
|
|
2552
|
+
flow: options.flow,
|
|
2553
|
+
all: options.all,
|
|
2554
|
+
stats: options.stats,
|
|
2555
|
+
json: options.json,
|
|
2556
|
+
cache: options.cache,
|
|
2557
|
+
verbose: options.verbose,
|
|
2558
|
+
dryRun: options.dryRun,
|
|
2559
|
+
silent: options.silent
|
|
2560
|
+
});
|
|
2561
|
+
});
|
|
2562
|
+
program.command("simulate [file]").description("Simulate event processing and capture API calls").option(
|
|
2563
|
+
"-e, --event <source>",
|
|
2564
|
+
"Event to simulate (JSON string, file path, or URL)"
|
|
2565
|
+
).option("--json", "Output results as JSON").option("-v, --verbose", "Verbose output").option("--dry-run", "preview command without executing").option("--silent", "suppress output").action(async (file, options) => {
|
|
2566
|
+
await simulateCommand({
|
|
2567
|
+
config: file || "bundle.config.json",
|
|
2568
|
+
event: options.event,
|
|
2569
|
+
json: options.json,
|
|
2570
|
+
verbose: options.verbose,
|
|
2571
|
+
dryRun: options.dryRun,
|
|
2572
|
+
silent: options.silent
|
|
2573
|
+
});
|
|
2574
|
+
});
|
|
2575
|
+
program.command("push [file]").description("Push an event through the flow with real API execution").requiredOption(
|
|
2576
|
+
"-e, --event <source>",
|
|
2577
|
+
"Event to push (JSON string, file path, or URL)"
|
|
2578
|
+
).option("--flow <name>", "Flow name (for multi-flow configs)").option("--json", "Output results as JSON").option("-v, --verbose", "Verbose output").option("-s, --silent", "Suppress output").action(async (file, options) => {
|
|
2579
|
+
await pushCommand({
|
|
2580
|
+
config: file || "bundle.config.json",
|
|
2581
|
+
event: options.event,
|
|
2582
|
+
flow: options.flow,
|
|
2583
|
+
json: options.json,
|
|
2584
|
+
verbose: options.verbose,
|
|
2585
|
+
silent: options.silent
|
|
2586
|
+
});
|
|
2587
|
+
});
|
|
2588
|
+
var runCmd = program.command("run").description("Run walkerOS flows in collect or serve mode");
|
|
2589
|
+
runCmd.command("collect [file]").description(
|
|
2590
|
+
"Run collector mode (event collection endpoint). Defaults to server-collect.mjs if no file specified."
|
|
2591
|
+
).option("-p, --port <number>", "Port to listen on (default: 8080)", parseInt).option("-h, --host <address>", "Host address (default: 0.0.0.0)").option("--json", "Output results as JSON").option("-v, --verbose", "Verbose output").option("--dry-run", "preview command without executing").option("--silent", "suppress output").action(async (file, options) => {
|
|
2592
|
+
await runCommand("collect", {
|
|
2593
|
+
config: file || "server-collect.mjs",
|
|
2594
|
+
port: options.port,
|
|
2595
|
+
host: options.host,
|
|
2596
|
+
json: options.json,
|
|
2597
|
+
verbose: options.verbose,
|
|
2598
|
+
dryRun: options.dryRun,
|
|
2599
|
+
silent: options.silent
|
|
2600
|
+
});
|
|
2601
|
+
});
|
|
2602
|
+
runCmd.command("serve [file]").description(
|
|
2603
|
+
"Run serve mode (single-file server for browser bundles). Defaults to baked-in web-serve.js if no file specified."
|
|
2604
|
+
).option("-p, --port <number>", "Port to listen on (default: 8080)", parseInt).option("-h, --host <address>", "Host address (default: 0.0.0.0)").option("--name <filename>", "Filename in URL (default: walker.js)").option("--path <directory>", "URL directory path (e.g., libs/v1)").option("--json", "Output results as JSON").option("-v, --verbose", "Verbose output").option("--dry-run", "preview command without executing").option("--silent", "suppress output").action(async (file, options) => {
|
|
2605
|
+
await runCommand("serve", {
|
|
2606
|
+
config: file || "web-serve.js",
|
|
2607
|
+
port: options.port,
|
|
2608
|
+
host: options.host,
|
|
2609
|
+
serveName: options.name,
|
|
2610
|
+
servePath: options.path,
|
|
2611
|
+
json: options.json,
|
|
2612
|
+
verbose: options.verbose,
|
|
2613
|
+
dryRun: options.dryRun,
|
|
2614
|
+
silent: options.silent
|
|
2615
|
+
});
|
|
2616
|
+
});
|
|
2617
|
+
registerCacheCommand(program);
|
|
2618
|
+
registerCleanCommand(program);
|
|
2619
|
+
program.parse();
|
|
2620
|
+
export {
|
|
2621
|
+
bundle,
|
|
2622
|
+
bundleCommand,
|
|
2623
|
+
pushCommand,
|
|
2624
|
+
run,
|
|
2625
|
+
runCommand,
|
|
2626
|
+
simulate,
|
|
2627
|
+
simulateCommand
|
|
2628
|
+
};
|
|
2629
|
+
//# sourceMappingURL=index.js.map
|