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