@walkeros/cli 0.3.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +22 -0
  3. package/dist/__tests__/bundle/bundler.test.js +6 -0
  4. package/dist/__tests__/bundle/bundler.test.js.map +1 -1
  5. package/dist/__tests__/bundle/programmatic.test.js +4 -0
  6. package/dist/__tests__/bundle/programmatic.test.js.map +1 -1
  7. package/dist/__tests__/cli-e2e.test.js +1 -1
  8. package/dist/__tests__/cli-e2e.test.js.map +1 -1
  9. package/dist/__tests__/cli.test.js +6 -2
  10. package/dist/__tests__/cli.test.js.map +1 -1
  11. package/dist/__tests__/simulate/programmatic.test.js +1 -1
  12. package/dist/__tests__/simulate/programmatic.test.js.map +1 -1
  13. package/dist/__tests__/simulate/simulator.test.js +1 -1
  14. package/dist/__tests__/simulate/simulator.test.js.map +1 -1
  15. package/dist/commands/bundle/bundler.js +20 -20
  16. package/dist/commands/bundle/bundler.js.map +1 -1
  17. package/dist/commands/bundle/index.d.ts.map +1 -1
  18. package/dist/commands/bundle/index.js +2 -2
  19. package/dist/commands/bundle/index.js.map +1 -1
  20. package/dist/commands/bundle/template-engine.d.ts.map +1 -1
  21. package/dist/commands/bundle/template-engine.js +2 -0
  22. package/dist/commands/bundle/template-engine.js.map +1 -1
  23. package/dist/commands/run/index.d.ts.map +1 -1
  24. package/dist/commands/run/index.js +8 -3
  25. package/dist/commands/run/index.js.map +1 -1
  26. package/dist/commands/run/types.d.ts +8 -4
  27. package/dist/commands/run/types.d.ts.map +1 -1
  28. package/dist/commands/simulate/index.d.ts.map +1 -1
  29. package/dist/commands/simulate/index.js +6 -3
  30. package/dist/commands/simulate/index.js.map +1 -1
  31. package/dist/commands/simulate/jsdom-executor.d.ts +32 -0
  32. package/dist/commands/simulate/jsdom-executor.d.ts.map +1 -0
  33. package/dist/commands/simulate/jsdom-executor.js +160 -0
  34. package/dist/commands/simulate/jsdom-executor.js.map +1 -0
  35. package/dist/commands/simulate/simulator.d.ts +0 -4
  36. package/dist/commands/simulate/simulator.d.ts.map +1 -1
  37. package/dist/commands/simulate/simulator.js +22 -104
  38. package/dist/commands/simulate/simulator.js.map +1 -1
  39. package/dist/config/defaults.d.ts.map +1 -1
  40. package/dist/config/defaults.js +0 -1
  41. package/dist/config/defaults.js.map +1 -1
  42. package/dist/config/index.d.ts +1 -1
  43. package/dist/config/index.d.ts.map +1 -1
  44. package/dist/config/index.js +1 -1
  45. package/dist/config/index.js.map +1 -1
  46. package/dist/config/parser.d.ts.map +1 -1
  47. package/dist/config/parser.js +9 -0
  48. package/dist/config/parser.js.map +1 -1
  49. package/dist/config/utils.d.ts +80 -4
  50. package/dist/config/utils.d.ts.map +1 -1
  51. package/dist/config/utils.js +197 -7
  52. package/dist/config/utils.js.map +1 -1
  53. package/dist/core/docker.d.ts +8 -4
  54. package/dist/core/docker.d.ts.map +1 -1
  55. package/dist/core/docker.js +50 -15
  56. package/dist/core/docker.js.map +1 -1
  57. package/dist/index.d.ts +34 -16
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +123 -2017
  60. package/dist/index.js.map +1 -1
  61. package/dist/types/bundle.d.ts +26 -12
  62. package/dist/types/bundle.d.ts.map +1 -1
  63. package/examples/.npm-cache/content-v2/sha512/0d/2d/7581c288670eaf8538ddd9df145b78756ce3be0791c6e0b9cd33429b3bae894525b9bda287a3cedffbcdd2c7b3107bafc03f2b0367eea489eee1cc042abb +1 -0
  64. package/examples/.npm-cache/content-v2/sha512/12/20/bc4f5acca143809f7e07da1fdafb38137d93243de4d5b403e6e10b92d0d3a6e51eab24fe9dbc9d3ed1cd72e8f7a406085e99c422bb2c7d1166cf9f1f564e +0 -0
  65. package/examples/.npm-cache/content-v2/sha512/22/ee/fb2695b01871c1d36946bdcfb49f1b520a57200d0a0b221b1e7d5f047ab38a8b2ab0e5f0e25a00acde1f3f2f9d24430a18f1092d438bc1a9e9891cc45f75 +0 -0
  66. package/examples/.npm-cache/content-v2/sha512/24/89/da1ce6a61bca6de7e132f241a675c01c83738bf6b78af25b5cce01d3030361332b3fe938571e2b721f1555da9ddf930fdcf8c02f0471556071590e68cc09 +0 -0
  67. package/examples/.npm-cache/content-v2/sha512/47/fd/c6be997da99228c3e279b95d4a46d6913947078a178f54ac71795a159f3513b1483232f4c2d0a1f403178bf9f96bb19615de32a9e2133e949880c6bc15e2 +0 -0
  68. package/examples/.npm-cache/content-v2/sha512/4b/1c/c1cb7f8b32102071a89fef97158daa32080ebaedfbbd596880d2213d84e305abc76d2a95a412ded55c1c3d487adcb1ceff87fc2c85d7e2856ebd9d3f16f3 +0 -0
  69. package/examples/.npm-cache/content-v2/sha512/6e/53/ff864769671f44f39d8a3bf904cd646535b745cc4824a8bb3189193b474678049f43b5178ba15cad7f0289046105e70f1565afc84e907120b35a466690fd +1 -0
  70. package/examples/.npm-cache/content-v2/sha512/70/4c/4c8837d446965c5551b4ea527e95fa011744fb727581d82cf35bb5599ea0b57d18baa490f7af93ef9a16e8e45e5c0802737da20575f4056a4a5c9a3cd288 +0 -0
  71. package/examples/.npm-cache/content-v2/sha512/96/ad/05de3bbb12d7de8ea353f962bdaea7d2eb44f707f2973462a6635daf537c67b46cca7764fed7d464fe62152c3f783a07aba1ceb35e09ad446bff05a4b466 +1 -0
  72. package/examples/.npm-cache/content-v2/sha512/b5/20/52dde94e6cef7170f6089c64a4843e57be18be450d956f4e455905aed047ae6a368451c93035e6ac3ee59576b600f03f815afba0836b3a16e10a9aaca4ba +1 -0
  73. package/examples/.npm-cache/content-v2/sha512/c7/a9/d166a1c39f97df312c59261319ba1cf9aac178bda0a0cb697d5ddd78bd8dd38ef1bf40017bcc8633c2049896c2d70696d9bff9280851f270792ff38bb3a0 +1 -0
  74. package/examples/.npm-cache/content-v2/sha512/e0/d0/8c14083b633e6adbd3c6a93da5fc0f6bbd456c5512ef276920bedd8d85d551052adff992de977aff326616a211aaa2d6ddcc801149e9b7f914f566359b6a +1 -0
  75. package/examples/.npm-cache/content-v2/sha512/e7/c5/06ad3fd79ac4f1031fe0b16ea5e54e232ca397bbcd7592c679021cbfb027276099f8c848f3f7a7691f0102ad53aa64f9141e61d729b037a678bd60440d17 +1 -0
  76. package/examples/.npm-cache/content-v2/sha512/f3/28/d5d32329604ed7d471a4949105daa2cc98858cf24f45b0b97c41d0eb0d5a9fe7bf1f69c792161cc6693e4fc1b52e886ac41875ebfb8fe47fafe417ca3e6e +0 -0
  77. package/examples/.npm-cache/index-v5/04/5a/2b5d7a7c407d85d746baa0f5c9388a333e35a717a8a0a81943daa6cb1364 +3 -0
  78. package/examples/.npm-cache/index-v5/12/9a/eba560cbace295d8ee04cf283015377bd77b379e70968fb6bc407c7fc410 +15 -0
  79. package/examples/.npm-cache/index-v5/2f/a2/7b047564b0ee21ac835ec609e89153dd6549be554d098584d5bfd19fe043 +15 -0
  80. package/examples/.npm-cache/index-v5/32/8e/322d58dd8d1e000be248ada51385bf96288e56039de9feec1a4c6a467653 +3 -0
  81. package/examples/.npm-cache/index-v5/57/93/d1d7cd1402e3e26468db03f2870822bb2c9018a506cdfb3b405f38cd3e1c +3 -0
  82. package/examples/.npm-cache/index-v5/5d/f8/0a1f4fa7149e4ff33e09eb6aea41ac8d1730c868a5d3ace91f762698acff +3 -0
  83. package/examples/.npm-cache/index-v5/69/a4/a92c72d838259b051cdf8e0acfb2bc680b6d4cfc642314a7836c3f7b2c50 +15 -0
  84. package/examples/.npm-cache/index-v5/71/31/6da3423bb203f3de5eb16c942431073f89be2cfcb40058ec91dcb5ce0abc +15 -0
  85. package/examples/.npm-cache/index-v5/7b/94/72b6bffa050d9ef52a558dd220663695bc606f756be0dfa196ef4f3913ba +3 -0
  86. package/examples/.npm-cache/index-v5/85/9e/99e97fdd562517e56285337db91d1a8f2f416b8d631cf4d7d754fa671299 +15 -0
  87. package/examples/.npm-cache/index-v5/92/4c/9416ada81a9b3c679539fd1ab53f8de3d41ff268f35eba7a194389a85b06 +3 -0
  88. package/examples/.npm-cache/index-v5/c1/5a/13df76b218deed8a6ef12961116af5183db98c53fad1b922fd9edc075247 +3 -0
  89. package/examples/.npm-cache/index-v5/cb/11/253c55410a8ab7c4a9ea9d6e1bf8ef1450a581da64c478074dfd82c8bff6 +3 -0
  90. package/examples/.npm-cache/index-v5/d5/ae/b57fad3a62b5ba2dbdf24b042a9e7b70820f3db00e5a630f02e1fea020dc +3 -0
  91. package/examples/.npm-cache/index-v5/d6/32/2f620f83c7d14451de98de8298c2408e05a16cc0829bd16c891ac19d7a67 +3 -0
  92. package/examples/.npm-cache/index-v5/dd/b5/01dc7a3cd8b6a03a69aee9af500d51ae19cb0aa12631a4aafd152148b8e5 +15 -0
  93. package/examples/.npm-cache/index-v5/e0/cf/6b862c15d74630d3871cd813d305210ab741311deb10baf8813014e0bc30 +3 -0
  94. package/examples/.npm-cache/index-v5/e2/be/e880ccd35950a814d3c1dded34d3938ac61b15a195321dc51357f801aad4 +15 -0
  95. package/examples/.npm-cache/index-v5/e5/1f/f4affe0b392cd03288f23cc03abcb274ff11a2c8f8965299de681914abb2 +3 -0
  96. package/examples/.npm-cache/index-v5/f3/5b/9ebe450958ff0d7cc44ab0a00080cb8a3ff1389744b5eab5f97b68a6a6af +3 -0
  97. package/examples/.npm-cache/index-v5/fb/c1/0de405e902866d53e7c30cf36a97dc2578838622b261816f44dc377c9a80 +3 -0
  98. package/examples/README.md +330 -0
  99. package/examples/event.json +53 -0
  100. package/examples/flow-order-complete.json +68 -0
  101. package/examples/flow-simple.json +32 -0
  102. package/examples/flow.json +82 -0
  103. package/examples/server-collect.json +53 -0
  104. package/examples/server-collect.mjs +13370 -0
  105. package/examples/test.html +43 -0
  106. package/examples/web-serve.js +45 -0
  107. package/examples/web-serve.json +75 -0
  108. package/package.json +3 -2
  109. package/templates/server.hbs +2 -2
  110. package/templates/web.hbs +12 -11
package/dist/index.js CHANGED
@@ -1,2026 +1,132 @@
1
- #!/usr/bin/env node
2
-
3
- // src/index.ts
4
- import { Command } from "commander";
5
-
6
- // src/commands/bundle/index.ts
7
- import path7 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
-
81
- // src/core/timer.ts
82
- function createTimer() {
83
- let startTime = 0;
84
- let endTime = 0;
85
- return {
86
- start() {
87
- startTime = Date.now();
88
- endTime = 0;
89
- },
90
- end() {
91
- endTime = Date.now();
92
- return endTime - startTime;
93
- },
94
- getElapsed() {
95
- const currentTime = endTime || Date.now();
96
- return currentTime - startTime;
97
- },
98
- format() {
99
- const elapsed = this.getElapsed();
100
- return (elapsed / 1e3).toFixed(2) + "s";
101
- }
102
- };
103
- }
104
-
105
- // src/core/output.ts
106
- function createJsonOutput(success, data, error, duration) {
107
- return {
108
- success,
109
- ...data && { data },
110
- ...error && { error },
111
- ...duration && { duration }
112
- };
113
- }
114
- function createSuccessOutput(data, duration) {
115
- return createJsonOutput(true, data, void 0, duration);
116
- }
117
- function createErrorOutput(error, duration) {
118
- return createJsonOutput(false, void 0, error, duration);
119
- }
120
- function formatBytes(bytes) {
121
- return (bytes / 1024).toFixed(2);
122
- }
123
-
124
- // src/core/docker.ts
125
- import { spawn } from "child_process";
126
- import path from "path";
127
- var CLI_DOCKER_IMAGE = process.env.WALKEROS_CLI_DOCKER_IMAGE || "walkeros/cli:latest";
128
- var RUNTIME_DOCKER_IMAGE = process.env.WALKEROS_RUNTIME_DOCKER_IMAGE || "walkeros/docker:latest";
129
- function buildDockerCommand(command, args, options = {}, configFile) {
130
- const cwd = process.cwd();
131
- const cmd = ["docker", "run", "--rm"];
132
- if (configFile) {
133
- const configPath = path.resolve(cwd, configFile);
134
- cmd.push("-v", `${configPath}:/config/flow.json:ro`);
135
- args = args.map((arg) => arg === configFile ? "/config/flow.json" : arg);
136
- }
137
- cmd.push("-v", `${cwd}:/workspace`);
138
- cmd.push("-w", "/workspace");
139
- if (process.platform !== "win32") {
140
- try {
141
- const uid = process.getuid?.();
142
- const gid = process.getgid?.();
143
- if (uid !== void 0 && gid !== void 0) {
144
- cmd.push("--user", `${uid}:${gid}`);
145
- }
146
- } catch {
147
- }
148
- }
149
- if (options.verbose) {
150
- cmd.push("-e", "VERBOSE=true");
151
- }
152
- if (options.silent) {
153
- cmd.push("-e", "SILENT=true");
154
- }
155
- cmd.push(CLI_DOCKER_IMAGE);
156
- cmd.push(command, ...args);
157
- return cmd;
158
- }
159
- async function executeInDocker(command, args, options = {}, configFile) {
160
- const containerArgs = [...args, "--local"];
161
- const dockerCmd = buildDockerCommand(
162
- command,
163
- containerArgs,
164
- options,
165
- configFile
166
- );
167
- return new Promise((resolve, reject) => {
168
- const proc = spawn(dockerCmd[0], dockerCmd.slice(1), {
169
- stdio: options.silent ? "ignore" : "inherit",
170
- shell: false
171
- });
172
- proc.on("error", (error) => {
173
- reject(new Error(`Docker execution failed: ${error.message}`));
174
- });
175
- proc.on("exit", (code) => {
176
- if (code === 0) {
177
- resolve();
178
- } else {
179
- reject(new Error(`Docker command exited with code ${code}`));
180
- }
181
- });
182
- });
183
- }
184
- async function isDockerAvailable() {
185
- return new Promise((resolve) => {
186
- const proc = spawn("docker", ["--version"], {
187
- stdio: "ignore"
188
- });
189
- proc.on("error", () => resolve(false));
190
- proc.on("exit", (code) => resolve(code === 0));
191
- });
192
- }
193
- function buildDockerRunCommand(mode, flowPath, options = {}) {
194
- const cwd = process.cwd();
195
- const cmd = ["docker", "run", "--rm"];
196
- cmd.push("-e", `MODE=${mode}`);
197
- if (mode === "collect" && flowPath) {
198
- const absoluteFlowPath = path.resolve(cwd, flowPath);
199
- cmd.push("-v", `${absoluteFlowPath}:/app/flow.mjs:ro`);
200
- cmd.push("-e", "FLOW=/app/flow.mjs");
201
- }
202
- if (options.port !== void 0) {
203
- cmd.push("-p", `${options.port}:${options.port}`);
204
- cmd.push("-e", `PORT=${options.port}`);
205
- }
206
- if (options.host) {
207
- cmd.push("-e", `HOST=${options.host}`);
208
- }
209
- if (mode === "serve" && options.staticDir) {
210
- const absoluteStaticDir = path.resolve(cwd, options.staticDir);
211
- cmd.push("-v", `${absoluteStaticDir}:/app/dist:ro`);
212
- cmd.push("-e", "STATIC_DIR=/app/dist");
213
- }
214
- if (process.platform !== "win32") {
215
- try {
216
- const uid = process.getuid?.();
217
- const gid = process.getgid?.();
218
- if (uid !== void 0 && gid !== void 0) {
219
- cmd.push("--user", `${uid}:${gid}`);
220
- }
221
- } catch {
222
- }
223
- }
224
- cmd.push(RUNTIME_DOCKER_IMAGE);
225
- return cmd;
226
- }
227
- async function executeRunInDocker(mode, flowPath, options = {}) {
228
- const dockerCmd = buildDockerRunCommand(mode, flowPath, options);
229
- return new Promise((resolve, reject) => {
230
- const proc = spawn(dockerCmd[0], dockerCmd.slice(1), {
231
- stdio: options.silent ? "ignore" : "inherit",
232
- shell: false
233
- });
234
- proc.on("error", (error) => {
235
- reject(new Error(`Docker execution failed: ${error.message}`));
236
- });
237
- proc.on("exit", (code) => {
238
- if (code === 0) {
239
- resolve();
240
- } else {
241
- reject(new Error(`Docker command exited with code ${code}`));
242
- }
243
- });
244
- });
245
- }
246
-
247
- // src/core/execution.ts
248
- function getExecutionMode(options) {
249
- if (options.local || process.env.WALKEROS_CONTAINER === "true") {
250
- return "local";
251
- }
252
- return "docker";
253
- }
254
- async function executeCommand(localHandler, dockerCommand, dockerArgs, options, logger, configFile) {
255
- const mode = getExecutionMode(options);
256
- if (options.dryRun) {
257
- if (mode === "docker") {
258
- const cmd = `docker run walkeros/cli:latest ${dockerCommand} ${dockerArgs.join(" ")}`;
259
- logger?.info(`[DRY-RUN] Would execute: ${cmd}`);
260
- } else {
261
- logger?.info(
262
- `[DRY-RUN] Would execute locally: ${dockerCommand} ${dockerArgs.join(" ")}`
263
- );
264
- }
265
- return;
266
- }
267
- if (mode === "local") {
268
- if (logger && !options.silent) {
269
- logger.info("\u{1F5A5}\uFE0F Executing locally...");
270
- }
271
- await localHandler();
272
- } else {
273
- const dockerAvailable = await isDockerAvailable();
274
- if (!dockerAvailable) {
275
- throw new Error(
276
- "Docker is not available. Please install Docker or use --local flag to execute locally."
277
- );
278
- }
279
- if (logger && !options.silent) {
280
- logger.info("\u{1F433} Executing in Docker container...");
281
- }
282
- await executeInDocker(dockerCommand, dockerArgs, options, configFile);
283
- }
284
- }
285
-
286
- // src/core/asset-resolver.ts
287
- import { fileURLToPath } from "url";
288
- import path2 from "path";
289
- function getPackageRoot() {
290
- const currentFile = fileURLToPath(import.meta.url);
291
- if (currentFile.includes("/src/")) {
292
- const srcIndex = currentFile.indexOf("/src/");
293
- return currentFile.substring(0, srcIndex);
294
- }
295
- return path2.resolve(currentFile, "../..");
296
- }
297
- function resolveAsset(assetPath, assetType, baseDir) {
298
- const packageRoot = getPackageRoot();
299
- if (!assetPath.includes("/") && !assetPath.includes("\\")) {
300
- if (assetType === "template") {
301
- return path2.join(packageRoot, "templates", assetPath);
302
- }
303
- return path2.join(packageRoot, "examples", assetPath);
304
- }
305
- if (path2.isAbsolute(assetPath)) {
306
- return assetPath;
307
- }
308
- const resolveBase = baseDir || process.cwd();
309
- return path2.resolve(resolveBase, assetPath);
310
- }
311
-
312
- // src/config/validators.ts
313
- function isObject(value) {
314
- return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
315
- }
316
- function validatePlatform(platform) {
317
- return platform === "web" || platform === "server";
318
- }
319
- function isMultiEnvConfig(data) {
320
- return isObject(data) && "version" in data && data.version === 1 && "environments" in data && isObject(data.environments);
321
- }
322
- function isSingleEnvConfig(data) {
323
- return isObject(data) && "flow" in data && "build" in data && isObject(data.flow) && isObject(data.build) && "platform" in data.flow && validatePlatform(data.flow.platform);
324
- }
325
-
326
- // src/config/utils.ts
327
- import fs from "fs-extra";
328
- import path3 from "path";
329
- async function loadJsonConfig(configPath) {
330
- const absolutePath = path3.resolve(configPath);
331
- if (!await fs.pathExists(absolutePath)) {
332
- throw new Error(`Configuration file not found: ${absolutePath}`);
333
- }
334
- try {
335
- const rawConfig = await fs.readJson(absolutePath);
336
- return rawConfig;
337
- } catch (error) {
338
- throw new Error(
339
- `Invalid JSON in config file: ${configPath}. ${error instanceof Error ? error.message : error}`
340
- );
341
- }
342
- }
343
- function getTempDir(tempDir = ".tmp") {
344
- const randomId = Math.random().toString(36).substring(2, 11);
345
- const basePath = path3.isAbsolute(tempDir) ? tempDir : path3.join(process.cwd(), tempDir);
346
- return path3.join(basePath, `cli-${Date.now()}-${randomId}`);
347
- }
348
-
349
- // src/config/defaults.ts
350
- function getDefaultBuildOptions(platform, code, output) {
351
- const common = {
352
- code,
353
- packages: {},
354
- minify: false,
355
- sourcemap: false,
356
- cache: true
357
- };
358
- if (platform === "web") {
359
- return {
360
- ...common,
361
- platform: "browser",
362
- format: "iife",
363
- target: "es2020",
364
- output: output || "./dist/walker.js",
365
- globalName: "walkerOS"
366
- };
367
- }
368
- return {
369
- ...common,
370
- platform: "node",
371
- format: "esm",
372
- target: "node18",
373
- output: output || "./dist/bundle.js"
374
- };
375
- }
376
- function ensureBuildOptions(buildOptions, flowPlatform) {
377
- const defaults = getDefaultBuildOptions(
378
- flowPlatform,
379
- buildOptions.code || "",
380
- buildOptions.output
381
- );
382
- if (!buildOptions.output && !defaults.output) {
383
- throw new Error("BuildOptions.output is required");
384
- }
385
- return {
386
- ...defaults,
387
- ...buildOptions,
388
- code: buildOptions.code || defaults.code || "",
389
- output: buildOptions.output || defaults.output,
390
- packages: buildOptions.packages || defaults.packages
391
- };
392
- }
393
-
394
- // src/config/parser.ts
395
- import path4 from "path";
396
- function parseBundleConfig(data) {
397
- if (!isObject(data)) {
398
- throw new Error(`Invalid config: expected object, got ${typeof data}`);
399
- }
400
- if (!("flow" in data) || !isObject(data.flow)) {
401
- throw new Error(
402
- `Invalid config: missing "flow" field. Expected format: { flow: { platform: "web" | "server", ... }, build: { ... } }`
403
- );
404
- }
405
- if (!("build" in data) || !isObject(data.build)) {
406
- throw new Error(
407
- `Invalid config: missing "build" field. Expected format: { flow: { platform: "web" | "server", ... }, build: { ... } }`
408
- );
409
- }
410
- const flowData = data.flow;
411
- if (!("platform" in flowData) || flowData.platform !== "web" && flowData.platform !== "server") {
412
- throw new Error(
413
- `Invalid config: flow.platform must be "web" or "server", got "${flowData.platform}"`
414
- );
415
- }
416
- const buildData = data.build;
417
- if ("packages" in buildData && !isObject(buildData.packages)) {
418
- throw new Error(
419
- `Invalid config: build.packages must be an object, got ${typeof buildData.packages}`
420
- );
421
- }
422
- const config = data;
423
- return normalizeConfigs(config, "/unknown/path");
424
- }
425
- function normalizeConfigs(config, configPath) {
426
- const flowConfig = config.flow;
427
- const platform = flowConfig.platform;
428
- if (!validatePlatform(platform)) {
429
- throw new Error(
430
- `Invalid platform "${platform}". Must be "web" or "server".`
431
- );
432
- }
433
- const buildDefaults = platform === "web" ? {
434
- platform: "browser",
435
- format: "iife",
436
- target: "es2020",
437
- minify: false,
438
- sourcemap: false,
439
- tempDir: ".tmp",
440
- cache: true
441
- } : {
442
- platform: "node",
443
- format: "esm",
444
- target: "node20",
445
- minify: false,
446
- sourcemap: false,
447
- tempDir: ".tmp",
448
- cache: true
449
- };
450
- const buildConfig = {
451
- ...buildDefaults,
452
- ...config.build
453
- };
454
- if (buildConfig.template === void 0) {
455
- buildConfig.template = platform === "server" ? "server.hbs" : "web.hbs";
456
- }
457
- if (configPath && buildConfig.template && !path4.isAbsolute(buildConfig.template)) {
458
- if (buildConfig.template.startsWith("./") || buildConfig.template.startsWith("../")) {
459
- const configDir = path4.dirname(configPath);
460
- buildConfig.template = path4.resolve(configDir, buildConfig.template);
461
- }
462
- }
463
- const buildOptions = ensureBuildOptions(buildConfig, platform);
464
- return {
465
- flowConfig,
466
- buildOptions
467
- };
468
- }
469
-
470
- // src/config/loader.ts
471
- function loadBundleConfig(rawConfig, options) {
472
- if (isMultiEnvConfig(rawConfig)) {
473
- return loadMultiEnvironmentConfig(rawConfig, options);
474
- }
475
- if (isSingleEnvConfig(rawConfig)) {
476
- return loadSingleEnvironmentConfig(rawConfig, options);
477
- }
478
- const configType = isObject(rawConfig) ? "platform" in rawConfig ? `invalid platform value: "${rawConfig.platform}"` : 'missing "flow" and "build" fields' : `not an object (got ${typeof rawConfig})`;
479
- throw new Error(
480
- `Invalid configuration format at ${options.configPath}.
481
- Configuration ${configType}.
482
-
483
- Expected either:
484
- 1. Multi-environment: { version: 1, environments: { prod: { flow: {...}, build: {...} } } }
485
- 2. Single-environment: { flow: { platform: "web" | "server", ... }, build: { packages: {...}, ... } }`
486
- );
487
- }
488
- function loadMultiEnvironmentConfig(setup, options) {
489
- const availableEnvironments = Object.keys(setup.environments);
490
- if (!options.environment) {
491
- throw new Error(
492
- `Multi-environment configuration detected. Please specify an environment using --env flag.
493
- Available environments: ${availableEnvironments.join(", ")}`
494
- );
495
- }
496
- const selectedEnv = options.environment;
497
- if (!setup.environments[selectedEnv]) {
498
- throw new Error(
499
- `Environment "${selectedEnv}" not found in configuration.
500
- Available environments: ${availableEnvironments.join(", ")}`
501
- );
502
- }
503
- const envConfig = setup.environments[selectedEnv];
504
- const { flowConfig, buildOptions } = normalizeConfigs(
505
- envConfig,
506
- options.configPath
507
- );
508
- if (options.logger) {
509
- options.logger.info(
510
- `\u{1F4E6} Using environment: ${selectedEnv} (${availableEnvironments.length} total)`
511
- );
512
- }
513
- return {
514
- flowConfig,
515
- buildOptions,
516
- environment: selectedEnv,
517
- isMultiEnvironment: true,
518
- availableEnvironments
519
- };
520
- }
521
- function loadSingleEnvironmentConfig(config, options) {
522
- const { flowConfig, buildOptions } = normalizeConfigs(
523
- config,
524
- options.configPath
525
- );
526
- if (options.logger && options.environment) {
527
- options.logger.warn(
528
- `--env flag specified but configuration is single-environment. Ignoring flag.`
529
- );
530
- }
531
- return {
532
- flowConfig,
533
- buildOptions,
534
- environment: "default",
535
- isMultiEnvironment: false
536
- };
537
- }
538
- function loadAllEnvironments(rawConfig, options) {
539
- if (!isMultiEnvConfig(rawConfig)) {
540
- throw new Error(
541
- `--all flag requires a multi-environment configuration (Setup format).
542
- Your configuration appears to be single-environment.`
543
- );
544
- }
545
- const setup = rawConfig;
546
- const environments = Object.keys(setup.environments);
547
- if (options.logger) {
548
- options.logger.info(
549
- `\u{1F4E6} Loading all ${environments.length} environments: ${environments.join(", ")}`
550
- );
551
- }
552
- return environments.map(
553
- (envName) => loadMultiEnvironmentConfig(setup, {
554
- ...options,
555
- environment: envName
556
- })
557
- );
558
- }
559
-
560
- // src/commands/bundle/bundler.ts
561
- import esbuild from "esbuild";
562
- import path6 from "path";
563
- import fs4 from "fs-extra";
564
-
565
- // src/commands/bundle/package-manager.ts
566
- import pacote from "pacote";
567
- import path5 from "path";
568
- import fs2 from "fs-extra";
569
- function getPackageDirectory(baseDir, packageName, version) {
570
- return path5.join(baseDir, "node_modules", packageName);
571
- }
572
- function getCachedPackagePath(pkg, tempDir) {
573
- const cacheDir = path5.join(".tmp", "cache", "packages");
574
- const safeName = pkg.name.replace(/\//g, "-").replace(/@/g, "");
575
- return path5.join(cacheDir, `${safeName}-${pkg.version}`);
576
- }
577
- async function isPackageCached(pkg, tempDir) {
578
- const cachedPath = getCachedPackagePath(pkg, tempDir);
579
- return fs2.pathExists(cachedPath);
580
- }
581
- function validateNoDuplicatePackages(packages) {
582
- const packageMap = /* @__PURE__ */ new Map();
583
- for (const pkg of packages) {
584
- if (!packageMap.has(pkg.name)) {
585
- packageMap.set(pkg.name, []);
586
- }
587
- packageMap.get(pkg.name).push(pkg.version);
588
- }
589
- const conflicts = [];
590
- for (const [name, versions] of packageMap.entries()) {
591
- const uniqueVersions = [...new Set(versions)];
592
- if (uniqueVersions.length > 1) {
593
- conflicts.push(`${name}: [${uniqueVersions.join(", ")}]`);
594
- }
595
- }
596
- if (conflicts.length > 0) {
597
- throw new Error(
598
- `Version conflicts detected:
599
- ${conflicts.map((c) => ` - ${c}`).join("\n")}
600
-
601
- Each package must use the same version across all declarations. Please update your configuration to use consistent versions.`
602
- );
603
- }
604
- }
605
- async function resolveDependencies(pkg, packageDir, logger, visited = /* @__PURE__ */ new Set()) {
606
- const dependencies = [];
607
- const pkgKey = `${pkg.name}@${pkg.version}`;
608
- if (visited.has(pkgKey)) {
609
- return dependencies;
610
- }
611
- visited.add(pkgKey);
612
- try {
613
- const packageJsonPath = path5.join(packageDir, "package.json");
614
- if (await fs2.pathExists(packageJsonPath)) {
615
- const packageJson = await fs2.readJson(packageJsonPath);
616
- const deps = {
617
- ...packageJson.dependencies,
618
- ...packageJson.peerDependencies
619
- };
620
- for (const [name, versionSpec] of Object.entries(deps)) {
621
- if (typeof versionSpec === "string") {
622
- dependencies.push({ name, version: versionSpec });
623
- }
624
- }
625
- }
626
- } catch (error) {
627
- logger.debug(`Failed to read dependencies for ${pkgKey}: ${error}`);
628
- }
629
- return dependencies;
630
- }
631
- async function downloadPackages(packages, targetDir, logger, useCache = true) {
632
- const packagePaths = /* @__PURE__ */ new Map();
633
- const downloadQueue = [...packages];
634
- const processed = /* @__PURE__ */ new Set();
635
- validateNoDuplicatePackages(packages);
636
- await fs2.ensureDir(targetDir);
637
- while (downloadQueue.length > 0) {
638
- const pkg = downloadQueue.shift();
639
- const pkgKey = `${pkg.name}@${pkg.version}`;
640
- if (processed.has(pkgKey)) {
641
- continue;
642
- }
643
- processed.add(pkgKey);
644
- const packageSpec = `${pkg.name}@${pkg.version}`;
645
- const packageDir = getPackageDirectory(targetDir, pkg.name, pkg.version);
646
- const cachedPath = getCachedPackagePath(pkg, targetDir);
647
- if (useCache && await isPackageCached(pkg, targetDir)) {
648
- logger.debug(`Using cached ${packageSpec}...`);
649
- try {
650
- await fs2.ensureDir(path5.dirname(packageDir));
651
- await fs2.copy(cachedPath, packageDir);
652
- packagePaths.set(pkg.name, packageDir);
653
- const deps = await resolveDependencies(pkg, packageDir, logger);
654
- for (const dep of deps) {
655
- const depKey = `${dep.name}@${dep.version}`;
656
- if (!processed.has(depKey)) {
657
- downloadQueue.push(dep);
658
- }
659
- }
660
- continue;
661
- } catch (error) {
662
- logger.debug(
663
- `Failed to use cache for ${packageSpec}, downloading fresh: ${error}`
664
- );
665
- }
666
- }
667
- logger.debug(`Downloading ${packageSpec}...`);
668
- try {
669
- await fs2.ensureDir(path5.dirname(packageDir));
670
- const cacheDir = process.env.NPM_CACHE_DIR || path5.join(process.cwd(), ".npm-cache");
671
- await pacote.extract(packageSpec, packageDir, {
672
- // Force npm registry download, prevent workspace resolution
673
- registry: "https://registry.npmjs.org",
674
- // Force online fetching from registry (don't use cached workspace packages)
675
- preferOnline: true,
676
- // Cache for performance
677
- cache: cacheDir,
678
- // Don't resolve relative to workspace context
679
- where: void 0
680
- });
681
- if (useCache) {
682
- try {
683
- await fs2.ensureDir(path5.dirname(cachedPath));
684
- await fs2.copy(packageDir, cachedPath);
685
- logger.debug(`Cached ${packageSpec} for future use`);
686
- } catch (cacheError) {
687
- logger.debug(`Failed to cache ${packageSpec}: ${cacheError}`);
688
- }
689
- }
690
- packagePaths.set(pkg.name, packageDir);
691
- const deps = await resolveDependencies(pkg, packageDir, logger);
692
- for (const dep of deps) {
693
- const depKey = `${dep.name}@${dep.version}`;
694
- if (!processed.has(depKey)) {
695
- downloadQueue.push(dep);
696
- }
697
- }
698
- } catch (error) {
699
- throw new Error(`Failed to download ${packageSpec}: ${error}`);
700
- }
701
- }
702
- return packagePaths;
703
- }
704
-
705
- // src/commands/bundle/template-engine.ts
706
- import fs3 from "fs-extra";
707
- import Handlebars from "handlebars";
708
-
709
- // src/commands/bundle/serializer.ts
710
- function serializeToJS(value, options = {}) {
711
- const { indent = 2, singleQuotes = false } = options;
712
- const quote = singleQuotes ? "'" : '"';
713
- function serialize(val, currentIndent = 0) {
714
- if (val === null) return "null";
715
- if (val === void 0) return "undefined";
716
- if (typeof val === "boolean" || typeof val === "number") {
717
- return String(val);
718
- }
719
- if (typeof val === "string") {
720
- if (val.includes("=>")) {
721
- const arrowPatterns = [
722
- /^\s*\([^)]*\)\s*=>/,
723
- // (param) => or () =>
724
- /^\s*\w+\s*=>/,
725
- // param =>
726
- /^\s*\([^)]*\)\s*=>\s*\{/,
727
- // (param) => {
728
- /^\s*\w+\s*=>\s*\{/
729
- // param => {
730
- ];
731
- if (arrowPatterns.some((pattern) => pattern.test(val))) {
732
- return val;
733
- }
734
- }
735
- return quote + val.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"') + quote;
736
- }
737
- if (Array.isArray(val)) {
738
- if (val.length === 0) return "[]";
739
- const nextIndent = currentIndent + indent;
740
- const spacing = " ".repeat(nextIndent);
741
- const items = val.map((item) => spacing + serialize(item, nextIndent)).join(",\n");
742
- return `[
743
- ${items}
744
- ${" ".repeat(currentIndent)}]`;
745
- }
746
- if (isObject(val)) {
747
- const entries = Object.entries(val);
748
- if (entries.length === 0) return "{}";
749
- const nextIndent = currentIndent + indent;
750
- const spacing = " ".repeat(nextIndent);
751
- const props = entries.map(([key, value2]) => {
752
- const needsQuotes = /[^a-zA-Z0-9_$]/.test(key) || /^[0-9]/.test(key);
753
- const keyStr = needsQuotes ? quote + key + quote : key;
754
- return spacing + keyStr + ": " + serialize(value2, nextIndent);
755
- }).join(",\n");
756
- return `{
757
- ${props}
758
- ${" ".repeat(currentIndent)}}`;
759
- }
760
- return String(val);
761
- }
762
- return serialize(value);
763
- }
764
- function serializeConfig(config) {
765
- if (!config || Object.keys(config).length === 0) {
766
- return "{}";
767
- }
768
- return serializeToJS(config, { indent: 2, singleQuotes: true });
769
- }
770
- function processTemplateVariables(variables) {
771
- const processed = { ...variables };
772
- if (isObject(processed.sources)) {
773
- const sourcesObj = processed.sources;
774
- const processedSources = {};
775
- for (const [name, source] of Object.entries(sourcesObj)) {
776
- const typedSource = source;
777
- const { env: _, ...sourceWithoutEnv } = typedSource;
778
- processedSources[name] = {
779
- ...sourceWithoutEnv,
780
- config: isObject(typedSource.config) ? serializeConfig(typedSource.config) : typedSource.config,
781
- // Pass through string configs unchanged
782
- ...typedSource.env !== void 0 && { env: typedSource.env }
783
- };
784
- }
785
- processed.sources = processedSources;
786
- }
787
- if (isObject(processed.destinations)) {
788
- const destinationsObj = processed.destinations;
789
- const processedDestinations = {};
790
- for (const [name, dest] of Object.entries(destinationsObj)) {
791
- const typedDest = dest;
792
- const { env: _, ...destWithoutEnv } = typedDest;
793
- processedDestinations[name] = {
794
- ...destWithoutEnv,
795
- config: isObject(typedDest.config) ? serializeConfig(typedDest.config) : typedDest.config,
796
- ...typedDest.env !== void 0 && { env: typedDest.env }
797
- };
798
- }
799
- processed.destinations = processedDestinations;
800
- }
801
- if (isObject(processed.collector)) {
802
- processed.collector = serializeConfig(
803
- processed.collector
804
- );
805
- }
806
- return processed;
807
- }
808
-
809
- // src/commands/bundle/template-engine.ts
810
- var TemplateEngine = class {
811
- handlebars;
812
- constructor() {
813
- this.handlebars = Handlebars.create();
814
- }
815
- /**
816
- * Load template content from file path
817
- *
818
- * @param templatePath - Template path (bare name, relative, or absolute)
819
- */
820
- async loadTemplate(templatePath) {
821
- const resolvedPath = resolveAsset(templatePath, "template");
822
- if (!await fs3.pathExists(resolvedPath)) {
823
- throw new Error(`Template file not found: ${resolvedPath}`);
824
- }
825
- return await fs3.readFile(resolvedPath, "utf-8");
826
- }
827
- /**
828
- * Apply template with user code and variable substitution
829
- */
830
- applyTemplate(template, userCode, sources, destinations, collector, build) {
831
- const processedVariables = processTemplateVariables({
832
- sources,
833
- destinations,
834
- collector
835
- });
836
- const templateData = {
837
- CODE: userCode,
838
- build: build || {},
839
- ...processedVariables
840
- };
841
- const compiledTemplate = this.handlebars.compile(template);
842
- return compiledTemplate(templateData);
843
- }
844
- /**
845
- * Process template with user code
846
- */
847
- async process(templatePath, userCode, sources, destinations, collector, build) {
848
- const template = await this.loadTemplate(templatePath);
849
- return this.applyTemplate(
850
- template,
851
- userCode,
852
- sources,
853
- destinations,
854
- collector,
855
- build
856
- );
857
- }
858
- };
859
-
860
- // src/commands/bundle/bundler.ts
861
- async function bundleCore(flowConfig, buildOptions, logger, showStats = false) {
862
- const bundleStartTime = Date.now();
863
- const TEMP_DIR = buildOptions.tempDir ? path6.isAbsolute(buildOptions.tempDir) ? buildOptions.tempDir : path6.resolve(buildOptions.tempDir) : getTempDir();
864
- try {
865
- if (!buildOptions.tempDir) {
866
- await fs4.emptyDir(TEMP_DIR);
867
- }
868
- logger.debug("Cleaned temporary directory");
869
- logger.info("\u{1F4E5} Downloading packages...");
870
- const packagesArray = Object.entries(buildOptions.packages).map(
871
- ([name, packageConfig]) => ({
872
- name,
873
- version: packageConfig.version || "latest"
874
- })
875
- );
876
- const packagePaths = await downloadPackages(
877
- packagesArray,
878
- TEMP_DIR,
879
- logger,
880
- buildOptions.cache
881
- );
882
- for (const [pkgName, pkgPath] of packagePaths.entries()) {
883
- if (pkgName.startsWith("@walkeros/")) {
884
- const pkgJsonPath = path6.join(pkgPath, "package.json");
885
- const pkgJson = await fs4.readJSON(pkgJsonPath);
886
- if (!pkgJson.exports && pkgJson.module) {
887
- pkgJson.exports = {
888
- ".": {
889
- import: pkgJson.module,
890
- require: pkgJson.main
891
- }
892
- };
893
- await fs4.writeJSON(pkgJsonPath, pkgJson, { spaces: 2 });
894
- }
895
- }
896
- }
897
- const packageJsonPath = path6.join(TEMP_DIR, "package.json");
898
- await fs4.writeFile(
899
- packageJsonPath,
900
- JSON.stringify({ type: "module" }, null, 2)
901
- );
902
- logger.info("\u{1F4DD} Creating entry point...");
903
- const entryContent = await createEntryPoint(
904
- flowConfig,
905
- buildOptions,
906
- packagePaths
907
- );
908
- const entryPath = path6.join(TEMP_DIR, "entry.js");
909
- await fs4.writeFile(entryPath, entryContent);
910
- logger.info("\u26A1 Bundling with esbuild...");
911
- const outputPath = path6.resolve(buildOptions.output);
912
- await fs4.ensureDir(path6.dirname(outputPath));
913
- const esbuildOptions = createEsbuildOptions(
914
- buildOptions,
915
- entryPath,
916
- outputPath,
917
- TEMP_DIR,
918
- packagePaths,
919
- logger
920
- );
921
- try {
922
- await esbuild.build(esbuildOptions);
923
- } catch (buildError) {
924
- throw createBuildError(
925
- buildError,
926
- buildOptions.code || ""
927
- );
928
- }
929
- logger.gray(`Output: ${outputPath}`);
930
- let stats;
931
- if (showStats) {
932
- stats = await collectBundleStats(
933
- outputPath,
934
- buildOptions.packages,
935
- bundleStartTime,
936
- entryContent
937
- );
938
- }
939
- if (!buildOptions.tempDir) {
940
- await fs4.remove(TEMP_DIR);
941
- logger.debug("Cleaned up temporary files");
942
- }
943
- return stats;
944
- } catch (error) {
945
- if (!buildOptions.tempDir) {
946
- await fs4.remove(TEMP_DIR).catch(() => {
947
- });
948
- }
949
- throw error;
950
- }
951
- }
952
- async function collectBundleStats(outputPath, packages, startTime, entryContent) {
953
- const stats = await fs4.stat(outputPath);
954
- const totalSize = stats.size;
955
- const buildTime = Date.now() - startTime;
956
- const packageStats = Object.entries(packages).map(([name, pkg]) => {
957
- const importPattern = new RegExp(`from\\s+['"]${name}['"]`, "g");
958
- const namedImportPattern = new RegExp(
959
- `import\\s+\\{[^}]*\\}\\s+from\\s+['"]${name}['"]`,
960
- "g"
961
- );
962
- const hasImports = importPattern.test(entryContent) || namedImportPattern.test(entryContent);
963
- const packagesCount = Object.keys(packages).length;
964
- const estimatedSize = hasImports ? Math.floor(totalSize / packagesCount) : 0;
965
- return {
966
- name: `${name}@${pkg.version || "latest"}`,
967
- size: estimatedSize
968
- };
969
- });
970
- const hasWildcardImports = /import\s+\*\s+as\s+\w+\s+from/.test(entryContent);
971
- const treeshakingEffective = !hasWildcardImports;
972
- return {
973
- totalSize,
974
- packages: packageStats,
975
- buildTime,
976
- treeshakingEffective
977
- };
978
- }
979
- function createEsbuildOptions(buildOptions, entryPath, outputPath, tempDir, packagePaths, logger) {
980
- const alias = {};
981
- const baseOptions = {
982
- entryPoints: [entryPath],
983
- bundle: true,
984
- format: buildOptions.format,
985
- platform: buildOptions.platform,
986
- outfile: outputPath,
987
- absWorkingDir: tempDir,
988
- // Resolve modules from temp directory
989
- // alias removed - not needed with absWorkingDir
990
- mainFields: ["module", "main"],
991
- // Prefer ESM over CJS
992
- treeShaking: true,
993
- logLevel: "error",
994
- minify: buildOptions.minify,
995
- sourcemap: buildOptions.sourcemap,
996
- resolveExtensions: [".mjs", ".js", ".ts", ".json"],
997
- // Prefer .mjs
998
- // Enhanced minification options when minify is enabled
999
- ...buildOptions.minify && {
1000
- minifyWhitespace: buildOptions.minifyOptions?.whitespace ?? true,
1001
- minifyIdentifiers: buildOptions.minifyOptions?.identifiers ?? true,
1002
- minifySyntax: buildOptions.minifyOptions?.syntax ?? true,
1003
- legalComments: buildOptions.minifyOptions?.legalComments ?? "none",
1004
- keepNames: buildOptions.minifyOptions?.keepNames ?? false,
1005
- charset: "utf8"
1006
- }
1007
- };
1008
- if (buildOptions.platform === "browser") {
1009
- baseOptions.define = {
1010
- "process.env.NODE_ENV": '"production"',
1011
- global: "globalThis"
1012
- };
1013
- baseOptions.external = buildOptions.external || [];
1014
- } else if (buildOptions.platform === "node") {
1015
- const nodeBuiltins = [
1016
- "crypto",
1017
- "fs",
1018
- "path",
1019
- "os",
1020
- "util",
1021
- "stream",
1022
- "buffer",
1023
- "events",
1024
- "http",
1025
- "https",
1026
- "url",
1027
- "querystring",
1028
- "zlib"
1029
- ];
1030
- const npmPackages = ["express", "express/*", "cors", "cors/*"];
1031
- baseOptions.external = buildOptions.external ? [...nodeBuiltins, ...npmPackages, ...buildOptions.external] : [...nodeBuiltins, ...npmPackages];
1032
- }
1033
- if (buildOptions.target) {
1034
- baseOptions.target = buildOptions.target;
1035
- } else if (buildOptions.platform === "node") {
1036
- baseOptions.target = "node18";
1037
- } else {
1038
- baseOptions.target = "es2018";
1039
- }
1040
- if (buildOptions.globalName && buildOptions.format === "iife") {
1041
- baseOptions.globalName = buildOptions.globalName;
1042
- }
1043
- return baseOptions;
1044
- }
1045
- function packageNameToVariable(packageName) {
1046
- return packageName.replace("@", "_").replace(/[/-]/g, "_").split("_").map(
1047
- (part, i) => i === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)
1048
- ).join("");
1049
- }
1050
- async function createEntryPoint(flowConfig, buildOptions, packagePaths) {
1051
- const importStatements = [];
1052
- const examplesMappings = [];
1053
- const destinationPackages = /* @__PURE__ */ new Set();
1054
- const destinations = flowConfig.destinations;
1055
- if (destinations) {
1056
- for (const [destKey, destConfig] of Object.entries(destinations)) {
1057
- if (typeof destConfig === "object" && destConfig !== null && "package" in destConfig && typeof destConfig.package === "string") {
1058
- destinationPackages.add(destConfig.package);
1059
- }
1060
- }
1061
- }
1062
- for (const [packageName, packageConfig] of Object.entries(
1063
- buildOptions.packages
1064
- )) {
1065
- if (packageConfig.imports && packageConfig.imports.length > 0) {
1066
- const uniqueImports = [...new Set(packageConfig.imports)];
1067
- const defaultImports = [];
1068
- const namedImports = [];
1069
- for (const imp of uniqueImports) {
1070
- if (imp.startsWith("default as ")) {
1071
- defaultImports.push(imp.replace("default as ", ""));
1072
- } else {
1073
- namedImports.push(imp);
1074
- }
1075
- }
1076
- if (defaultImports.length > 0) {
1077
- for (const defaultImport of defaultImports) {
1078
- importStatements.push(
1079
- `import ${defaultImport} from '${packageName}';`
1080
- );
1081
- }
1082
- }
1083
- if (namedImports.length > 0) {
1084
- const importList = namedImports.join(", ");
1085
- importStatements.push(
1086
- `import { ${importList} } from '${packageName}';`
1087
- );
1088
- }
1089
- const examplesImport = uniqueImports.find(
1090
- (imp) => imp.includes("examples as ")
1091
- );
1092
- if (examplesImport) {
1093
- const examplesVarName = examplesImport.split(" as ")[1];
1094
- const destinationMatch = packageName.match(
1095
- /@walkeros\/web-destination-(.+)$/
1096
- );
1097
- if (destinationMatch) {
1098
- const destinationName = destinationMatch[1];
1099
- examplesMappings.push(
1100
- ` ${destinationName}: typeof ${examplesVarName} !== 'undefined' ? ${examplesVarName} : undefined`
1101
- );
1102
- }
1103
- }
1104
- } else {
1105
- const varName = packageNameToVariable(packageName);
1106
- importStatements.push(
1107
- `import * as ${varName} from '${packageName}'; // Consider specifying explicit imports`
1108
- );
1109
- }
1110
- if (destinationPackages.has(packageName)) {
1111
- const destinationMatch = packageName.match(
1112
- /@walkeros\/(?:(?:web|server)-)?destination-(.+)$/
1113
- );
1114
- if (destinationMatch) {
1115
- const destinationName = destinationMatch[1];
1116
- const examplesVarName = `${destinationName.replace(/-/g, "_")}_examples`;
1117
- const isDemoPackage = packageName.includes("-demo");
1118
- if (isDemoPackage) {
1119
- importStatements.push(
1120
- `import { examples as ${examplesVarName} } from '${packageName}';`
1121
- );
1122
- } else {
1123
- importStatements.push(
1124
- `import * as ${examplesVarName} from '${packageName}/examples';`
1125
- );
1126
- }
1127
- examplesMappings.push(` ${destinationName}: ${examplesVarName}`);
1128
- }
1129
- }
1130
- }
1131
- const examplesObject = examplesMappings.length > 0 ? `const examples = {
1132
- ${examplesMappings.join(",\n")}
1133
- };
1134
-
1135
- ` : "";
1136
- const importsCode = importStatements.join("\n");
1137
- let templatedCode;
1138
- if (buildOptions.template) {
1139
- const templateEngine = new TemplateEngine();
1140
- const flowWithProps = flowConfig;
1141
- templatedCode = await templateEngine.process(
1142
- buildOptions.template,
1143
- buildOptions.code || "",
1144
- // Pass user code as parameter (empty if undefined)
1145
- flowWithProps.sources || {},
1146
- flowWithProps.destinations || {},
1147
- flowWithProps.collector || {},
1148
- buildOptions
1149
- // Pass build config to template
1150
- );
1151
- } else {
1152
- templatedCode = buildOptions.code || "";
1153
- }
1154
- let wrappedCode = templatedCode;
1155
- const hasExport = /^\s*export\s/m.test(templatedCode);
1156
- if (!hasExport) {
1157
- if (buildOptions.format === "esm") {
1158
- wrappedCode = `export default ${templatedCode}`;
1159
- } else if (buildOptions.platform === "browser" && buildOptions.globalName) {
1160
- wrappedCode = `window['${buildOptions.globalName}'] = ${templatedCode}`;
1161
- }
1162
- }
1163
- let finalCode = importsCode ? `${importsCode}
1164
-
1165
- ${examplesObject}${wrappedCode}` : `${examplesObject}${wrappedCode}`;
1166
- if (examplesObject && buildOptions.format === "esm") {
1167
- finalCode += `
1168
-
1169
- export { examples };`;
1170
- }
1171
- return finalCode;
1172
- }
1173
- function createBuildError(buildError, code) {
1174
- if (!buildError.errors || buildError.errors.length === 0) {
1175
- return new Error(`Build failed: ${buildError.message || buildError}`);
1176
- }
1177
- const firstError = buildError.errors[0];
1178
- const location = firstError.location;
1179
- if (location && location.file && location.file.includes("entry.js")) {
1180
- const line = location.line;
1181
- const column = location.column;
1182
- const codeLines = code.split("\n");
1183
- const errorLine = codeLines[line - 1] || "";
1184
- return new Error(
1185
- `Code syntax error at line ${line}, column ${column}:
1186
- ${errorLine}
1187
- ${" ".repeat(column - 1)}^
1188
- ${firstError.text}`
1189
- );
1190
- }
1191
- return new Error(
1192
- `Build error: ${firstError.text}
1193
- ` + (location ? ` at ${location.file}:${location.line}:${location.column}` : "")
1194
- );
1195
- }
1196
-
1197
- // src/commands/bundle/stats.ts
1198
- function displayStats(stats, logger) {
1199
- logger.info("\n\u{1F4CA} Bundle Statistics");
1200
- logger.info("\u2500".repeat(50));
1201
- const sizeKB = formatBytes(stats.totalSize);
1202
- logger.info(`Total Size: ${sizeKB} KB`);
1203
- const timeSeconds = (stats.buildTime / 1e3).toFixed(2);
1204
- logger.info(`Build Time: ${timeSeconds}s`);
1205
- const treeshakingStatus = stats.treeshakingEffective ? "\u2705 Effective" : "\u26A0\uFE0F Not optimal (consider using named imports)";
1206
- logger.info(`Tree-shaking: ${treeshakingStatus}`);
1207
- if (stats.packages.length > 0) {
1208
- logger.info(`
1209
- Package Breakdown:`);
1210
- stats.packages.forEach((pkg) => {
1211
- if (pkg.size > 0) {
1212
- const pkgSizeKB = formatBytes(pkg.size);
1213
- logger.info(` \u2022 ${pkg.name}: ${pkgSizeKB} KB`);
1214
- }
1215
- });
1216
- }
1217
- logger.info("\u2500".repeat(50));
1218
- }
1219
-
1220
- // src/commands/bundle/index.ts
1221
- async function bundleCommand(options) {
1222
- const timer = createTimer();
1223
- timer.start();
1224
- const logger = createLogger({
1225
- verbose: options.verbose,
1226
- silent: options.silent ?? false,
1227
- json: options.json
1228
- });
1229
- const dockerArgs = [options.config];
1230
- if (options.env) dockerArgs.push("--env", options.env);
1231
- if (options.all) dockerArgs.push("--all");
1232
- if (options.stats) dockerArgs.push("--stats");
1233
- if (options.json) dockerArgs.push("--json");
1234
- if (options.cache === false) dockerArgs.push("--no-cache");
1235
- if (options.verbose) dockerArgs.push("--verbose");
1236
- if (options.silent) dockerArgs.push("--silent");
1237
- await executeCommand(
1238
- async () => {
1239
- try {
1240
- if (options.env && options.all) {
1241
- throw new Error("Cannot use both --env and --all flags together");
1242
- }
1243
- logger.info("\u{1F4E6} Reading configuration...");
1244
- const configPath = path7.resolve(options.config);
1245
- const rawConfig = await loadJsonConfig(configPath);
1246
- const configsToBundle = options.all ? loadAllEnvironments(rawConfig, { configPath, logger }) : [
1247
- loadBundleConfig(rawConfig, {
1248
- configPath,
1249
- environment: options.env,
1250
- logger
1251
- })
1252
- ];
1253
- const results = [];
1254
- for (const {
1255
- flowConfig,
1256
- buildOptions,
1257
- environment,
1258
- isMultiEnvironment
1259
- } of configsToBundle) {
1260
- try {
1261
- if (options.cache !== void 0) {
1262
- buildOptions.cache = options.cache;
1263
- }
1264
- if (isMultiEnvironment || options.all) {
1265
- logger.info(`
1266
- \u{1F527} Building environment: ${environment}`);
1267
- } else {
1268
- logger.info("\u{1F527} Starting bundle process...");
1269
- }
1270
- const shouldCollectStats = options.stats || options.json;
1271
- const stats = await bundleCore(
1272
- flowConfig,
1273
- buildOptions,
1274
- logger,
1275
- shouldCollectStats
1276
- );
1277
- results.push({
1278
- environment,
1279
- success: true,
1280
- stats
1281
- });
1282
- if (!options.json && !options.all && options.stats && stats) {
1283
- displayStats(stats, logger);
1284
- }
1285
- } catch (error) {
1286
- const errorMessage = error instanceof Error ? error.message : String(error);
1287
- results.push({
1288
- environment,
1289
- success: false,
1290
- error: errorMessage
1291
- });
1292
- if (!options.all) {
1293
- throw error;
1294
- }
1295
- }
1296
- }
1297
- const duration = timer.end() / 1e3;
1298
- const successCount = results.filter((r) => r.success).length;
1299
- const failureCount = results.filter((r) => !r.success).length;
1300
- if (options.json) {
1301
- const outputLogger = createLogger({ silent: false, json: false });
1302
- const output = failureCount === 0 ? createSuccessOutput(
1303
- {
1304
- environments: results,
1305
- summary: {
1306
- total: results.length,
1307
- success: successCount,
1308
- failed: failureCount
1309
- }
1310
- },
1311
- duration
1312
- ) : createErrorOutput(
1313
- `${failureCount} environment(s) failed to build`,
1314
- duration
1315
- );
1316
- outputLogger.log("white", JSON.stringify(output, null, 2));
1317
- } else {
1318
- if (options.all) {
1319
- logger.info(`
1320
- \u{1F4CA} Build Summary:`);
1321
- logger.info(` Total: ${results.length}`);
1322
- logger.success(` \u2705 Success: ${successCount}`);
1323
- if (failureCount > 0) {
1324
- logger.error(` \u274C Failed: ${failureCount}`);
1325
- }
1326
- }
1327
- if (failureCount === 0) {
1328
- logger.success(
1329
- `
1330
- \u2705 Bundle created successfully in ${timer.format()}`
1331
- );
1332
- } else {
1333
- throw new Error(`${failureCount} environment(s) failed to build`);
1334
- }
1335
- }
1336
- } catch (error) {
1337
- const duration = timer.getElapsed() / 1e3;
1338
- const errorMessage = error instanceof Error ? error.message : String(error);
1339
- if (options.json) {
1340
- const outputLogger = createLogger({ silent: false, json: false });
1341
- const output = createErrorOutput(errorMessage, duration);
1342
- outputLogger.log("white", JSON.stringify(output, null, 2));
1343
- } else {
1344
- logger.error("\u274C Bundle failed:");
1345
- logger.error(errorMessage);
1346
- }
1347
- process.exit(1);
1348
- }
1349
- },
1350
- "bundle",
1351
- dockerArgs,
1352
- options,
1353
- logger,
1354
- options.config
1355
- );
1356
- }
1357
- async function bundle(configOrPath, options = {}) {
1358
- let rawConfig;
1359
- if (typeof configOrPath === "string") {
1360
- rawConfig = await loadJsonConfig(configOrPath);
1361
- } else {
1362
- rawConfig = configOrPath;
1363
- }
1364
- const { flowConfig, buildOptions } = parseBundleConfig(rawConfig);
1365
- if (options.cache !== void 0) {
1366
- buildOptions.cache = options.cache;
1367
- }
1368
- const logger = createLogger({
1369
- silent: options.silent ?? false,
1370
- verbose: options.verbose ?? false
1371
- });
1372
- return await bundleCore(
1373
- flowConfig,
1374
- buildOptions,
1375
- logger,
1376
- options.stats ?? false
1377
- );
1378
- }
1379
-
1380
- // src/commands/simulate/simulator.ts
1381
- import path8 from "path";
1382
- import fs5 from "fs-extra";
1383
-
1384
- // src/commands/simulate/tracker.ts
1385
- var CallTracker = class {
1386
- calls = /* @__PURE__ */ new Map();
1387
- /**
1388
- * Wrap a function to track its calls
1389
- */
1390
- wrapFunction(name, fn) {
1391
- const self = this;
1392
- const targetFn = fn || (() => {
1393
- });
1394
- return new Proxy(targetFn, {
1395
- apply(_target, thisArg, args) {
1396
- self.logCall(name, args);
1397
- return targetFn.apply(thisArg, args);
1398
- }
1
+ import { Command } from 'commander';
2
+ import { readFileSync } from 'fs';
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname, join } from 'path';
5
+ import { bundleCommand } from './commands/bundle';
6
+ import { simulateCommand } from './commands/simulate';
7
+ import { runCommand } from './commands/run';
8
+ // Get package version dynamically
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+ const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
12
+ const VERSION = packageJson.version;
13
+ // === CLI Commands ===
14
+ // Export CLI command handlers
15
+ export { bundleCommand, simulateCommand, runCommand };
16
+ // === Programmatic API ===
17
+ // High-level functions for library usage
18
+ export { bundle } from './commands/bundle';
19
+ export { simulate } from './commands/simulate';
20
+ export { run } from './commands/run';
21
+ const program = new Command();
22
+ program
23
+ .name('walkeros')
24
+ .description('walkerOS CLI - Bundle and deploy walkerOS components')
25
+ .version(VERSION);
26
+ // Bundle command
27
+ program
28
+ .command('bundle [file]')
29
+ .description('Bundle NPM packages with custom code')
30
+ .option('-e, --env <name>', 'environment to build (for multi-environment configs)')
31
+ .option('--all', 'build all environments (for multi-environment configs)')
32
+ .option('-s, --stats', 'show bundle statistics')
33
+ .option('--json', 'output statistics in JSON format (implies --stats)')
34
+ .option('--no-cache', 'disable package caching and download fresh packages')
35
+ .option('-v, --verbose', 'verbose output')
36
+ .option('--local', 'execute in local Node.js instead of Docker')
37
+ .option('--dry-run', 'preview command without executing')
38
+ .option('--silent', 'suppress output')
39
+ .action(async (file, options) => {
40
+ await bundleCommand({
41
+ config: file || 'bundle.config.json',
42
+ env: options.env,
43
+ all: options.all,
44
+ stats: options.stats,
45
+ json: options.json,
46
+ cache: options.cache,
47
+ verbose: options.verbose,
48
+ local: options.local,
49
+ dryRun: options.dryRun,
50
+ silent: options.silent,
1399
51
  });
1400
- }
1401
- /**
1402
- * Wrap an environment object, tracking specified paths
1403
- *
1404
- * @param env - Environment object (from destination's examples/env.ts)
1405
- * @param paths - Paths to track (e.g., ['gtag:window.gtag', 'gtag:window.dataLayer.push'])
1406
- */
1407
- wrapEnv(env, paths) {
1408
- const wrapped = {};
1409
- for (const [key, value] of Object.entries(env)) {
1410
- if (typeof value === "object" && value !== null) {
1411
- wrapped[key] = Array.isArray(value) ? [...value] : { ...value };
1412
- } else {
1413
- wrapped[key] = value;
1414
- }
1415
- }
1416
- for (const fullPath of paths) {
1417
- const [destKey, ...pathParts] = fullPath.split(":");
1418
- const path10 = pathParts.join(":");
1419
- if (!path10) continue;
1420
- const cleanPath = path10.replace(/^call:/, "");
1421
- const parts = cleanPath.split(".");
1422
- let current = wrapped;
1423
- let source = env;
1424
- for (let i = 0; i < parts.length - 1; i++) {
1425
- const part = parts[i];
1426
- if (!current[part]) {
1427
- current[part] = {};
1428
- }
1429
- current = current[part];
1430
- source = source && typeof source[part] === "object" && source[part] !== null ? source[part] : void 0;
1431
- }
1432
- const finalKey = parts[parts.length - 1];
1433
- const originalFn = source?.[finalKey];
1434
- current[finalKey] = this.wrapFunction(
1435
- `${destKey}:${cleanPath}`,
1436
- typeof originalFn === "function" ? originalFn : void 0
1437
- );
1438
- }
1439
- return wrapped;
1440
- }
1441
- logCall(fullPath, args) {
1442
- const [destKey, ...pathParts] = fullPath.split(":");
1443
- const apiPath = pathParts.join(":");
1444
- if (!this.calls.has(destKey)) {
1445
- this.calls.set(destKey, []);
1446
- }
1447
- this.calls.get(destKey).push({
1448
- type: "call",
1449
- path: apiPath,
1450
- args,
1451
- timestamp: Date.now()
52
+ });
53
+ // Simulate command
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,
1452
72
  });
1453
- }
1454
- getCalls() {
1455
- return Object.fromEntries(this.calls);
1456
- }
1457
- reset() {
1458
- this.calls.clear();
1459
- }
1460
- };
1461
-
1462
- // src/commands/simulate/simulator.ts
1463
- function generateId() {
1464
- return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
1465
- }
1466
- async function simulateCore(configPath, event, options = {}) {
1467
- const logger = createLogger({
1468
- verbose: options.verbose || false,
1469
- silent: options.silent || false,
1470
- json: options.json || false
1471
- });
1472
- try {
1473
- logger.info("\u{1F3AF} Starting walkerOS simulation...");
1474
- logger.info("\u{1F4E6} Loading bundle configuration...");
1475
- const fullConfigPath = path8.resolve(configPath);
1476
- const rawConfig = await loadJsonConfig(fullConfigPath);
1477
- parseBundleConfig(rawConfig);
1478
- logger.info(`\u{1F680} Executing simulation with event: ${JSON.stringify(event)}`);
1479
- const result = await executeSimulation(event, fullConfigPath);
1480
- if (result.success) {
1481
- logger.info(`\u2705 Simulation completed successfully`);
1482
- } else {
1483
- logger.error(`\u274C Simulation failed: ${result.error}`);
1484
- }
1485
- return result;
1486
- } catch (error) {
1487
- const errorMessage = error instanceof Error ? error.message : String(error);
1488
- logger.error(`\u{1F4A5} Simulation error: ${errorMessage}`);
1489
- return {
1490
- success: false,
1491
- error: errorMessage
1492
- };
1493
- }
1494
- }
1495
- function parseEventInput(eventString = "") {
1496
- if (!eventString) {
1497
- return {};
1498
- }
1499
- try {
1500
- const parsed = JSON.parse(eventString);
1501
- return isObject(parsed) ? parsed : {};
1502
- } catch {
1503
- return { name: eventString };
1504
- }
1505
- }
1506
- function formatSimulationResult(result, options = {}) {
1507
- if (options.json) {
1508
- const output = {
1509
- result: result.elbResult,
1510
- usage: result.usage,
1511
- duration: result.duration
1512
- };
1513
- return JSON.stringify(output, null, 2);
1514
- }
1515
- if (result.success) {
1516
- return "\u2705 Simulation completed successfully";
1517
- } else {
1518
- return `\u274C Simulation failed: ${result.error}`;
1519
- }
1520
- }
1521
- async function executeSimulation(event, configPath) {
1522
- const startTime = Date.now();
1523
- let bundlePath;
1524
- const tempDir = getTempDir();
1525
- try {
1526
- await fs5.ensureDir(tempDir);
1527
- const rawConfig = await loadJsonConfig(configPath);
1528
- const { flowConfig, buildOptions } = parseBundleConfig(rawConfig);
1529
- const packagesArray = Object.entries(buildOptions.packages).map(
1530
- ([name, packageConfig]) => ({
1531
- name,
1532
- version: (typeof packageConfig === "object" && packageConfig !== null && "version" in packageConfig && typeof packageConfig.version === "string" ? packageConfig.version : void 0) || "latest"
1533
- })
1534
- );
1535
- const packagePaths = await downloadPackages(
1536
- packagesArray,
1537
- tempDir,
1538
- // downloadPackages will add 'node_modules' subdirectory itself
1539
- createLogger({ silent: true }),
1540
- buildOptions.cache
1541
- );
1542
- const tracker = new CallTracker();
1543
- const envSetupCode = [];
1544
- const destinations = flowConfig.destinations;
1545
- if (destinations) {
1546
- for (const [key, dest] of Object.entries(destinations)) {
1547
- const destName = key.replace(/-/g, "_");
1548
- envSetupCode.push(`
1549
- // Inject tracked env for destination '${key}' using examples from bundle
1550
- if (examples && examples['${key}'] && examples['${key}'].env) {
1551
- const mockEnv = examples['${key}'].env.push;
1552
- const trackPaths = examples['${key}'].env.simulation || [];
1553
- if (mockEnv) {
1554
- const wrappedPaths = trackPaths.map((p) => '${key}:' + p);
1555
- const trackedEnv = __simulationTracker.wrapEnv(mockEnv, wrappedPaths);
1556
- if (config.destinations && config.destinations['${key}']) {
1557
- config.destinations['${key}'].env = trackedEnv;
1558
- }
1559
- }
1560
- }
1561
- `);
1562
- }
1563
- }
1564
- const modifiedCode = `
1565
- // Inject tracked envs into destination configs
1566
- ${envSetupCode.join("\n")}
1567
-
1568
- ${buildOptions.code || ""}
1569
- `;
1570
- const tempOutput = path8.join(
1571
- tempDir,
1572
- `simulation-bundle-${generateId()}.mjs`
1573
- );
1574
- const simulationBuildOptions = {
1575
- ...buildOptions,
1576
- code: modifiedCode,
1577
- output: tempOutput,
1578
- tempDir,
1579
- // Use same temp dir for bundle
1580
- format: "esm",
1581
- // Force node platform for simulation since we're running in Node.js
1582
- platform: "node"
1583
- };
1584
- await bundleCore(
1585
- flowConfig,
1586
- simulationBuildOptions,
1587
- createLogger({ silent: true }),
1588
- false
1589
- );
1590
- bundlePath = tempOutput;
1591
- const globalWithSim = globalThis;
1592
- if (!globalWithSim.window) {
1593
- globalWithSim.window = {};
1594
- }
1595
- if (!globalWithSim.document) {
1596
- globalWithSim.document = {};
1597
- }
1598
- const timestamp = Date.now();
1599
- const moduleUrl = `file://${bundlePath}?t=${timestamp}`;
1600
- const module = await import(moduleUrl);
1601
- const importedExamples = module.examples;
1602
- const destinations2 = flowConfig.destinations;
1603
- if (importedExamples && destinations2) {
1604
- for (const [key, dest] of Object.entries(destinations2)) {
1605
- const destEnv = importedExamples[key]?.env?.push;
1606
- if (destEnv) {
1607
- if (destEnv.window && typeof globalWithSim.window === "object" && globalWithSim.window !== null) {
1608
- Object.assign(globalWithSim.window, destEnv.window);
1609
- }
1610
- if (destEnv.document && typeof globalWithSim.document === "object" && globalWithSim.document !== null) {
1611
- Object.assign(globalWithSim.document, destEnv.document);
1612
- }
1613
- }
1614
- }
1615
- }
1616
- const flowResult = await module.default({ tracker });
1617
- if (!flowResult || typeof flowResult.elb !== "function") {
1618
- throw new Error(
1619
- "Bundle did not export valid flow object with elb function"
1620
- );
1621
- }
1622
- const { elb } = flowResult;
1623
- const elbResult = await elb(event);
1624
- const usage = tracker.getCalls();
1625
- const duration = Date.now() - startTime;
1626
- return {
1627
- success: true,
1628
- elbResult,
1629
- usage,
1630
- duration,
1631
- logs: []
1632
- };
1633
- } catch (error) {
1634
- const duration = Date.now() - startTime;
1635
- return {
1636
- success: false,
1637
- error: error instanceof Error ? error.message : String(error),
1638
- duration
1639
- };
1640
- } finally {
1641
- if (tempDir) {
1642
- await fs5.remove(tempDir).catch(() => {
1643
- });
1644
- }
1645
- const globalWithSim = globalThis;
1646
- delete globalWithSim.window;
1647
- delete globalWithSim.document;
1648
- }
1649
- }
1650
-
1651
- // src/commands/simulate/index.ts
1652
- async function simulateCommand(options) {
1653
- const logger = createLogger({
1654
- verbose: options.verbose,
1655
- silent: options.silent ?? false,
1656
- json: options.json
1657
- });
1658
- const dockerArgs = [options.config];
1659
- if (options.event) dockerArgs.push("--event", options.event);
1660
- if (options.json) dockerArgs.push("--json");
1661
- if (options.verbose) dockerArgs.push("--verbose");
1662
- if (options.silent) dockerArgs.push("--silent");
1663
- await executeCommand(
1664
- async () => {
1665
- const startTime = Date.now();
1666
- try {
1667
- const event = parseEventInput(options.event);
1668
- const result = await simulateCore(options.config, event, {
1669
- json: options.json,
1670
- verbose: options.verbose,
1671
- silent: options.silent
1672
- });
1673
- const resultWithDuration = {
1674
- ...result,
1675
- duration: (Date.now() - startTime) / 1e3
1676
- };
1677
- const outputLogger = createLogger({ silent: false, json: false });
1678
- const output = formatSimulationResult(resultWithDuration, {
1679
- json: options.json
1680
- });
1681
- outputLogger.log("white", output);
1682
- if (!result.success) {
1683
- process.exit(1);
1684
- }
1685
- } catch (error) {
1686
- const errorMessage = error instanceof Error ? error.message : String(error);
1687
- if (options.json) {
1688
- const outputLogger = createLogger({ silent: false, json: false });
1689
- const errorOutput = JSON.stringify(
1690
- {
1691
- success: false,
1692
- error: errorMessage,
1693
- duration: (Date.now() - startTime) / 1e3
1694
- },
1695
- null,
1696
- 2
1697
- );
1698
- outputLogger.log("white", errorOutput);
1699
- } else {
1700
- const errorLogger = createLogger({ silent: false, json: false });
1701
- errorLogger.error(`\u274C Simulate command failed: ${errorMessage}`);
1702
- }
1703
- process.exit(1);
1704
- }
1705
- },
1706
- "simulate",
1707
- dockerArgs,
1708
- options,
1709
- logger,
1710
- options.config
1711
- );
1712
- }
1713
- async function simulate(configOrPath, event, options = {}) {
1714
- if (typeof configOrPath !== "string") {
1715
- throw new Error(
1716
- "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."
1717
- );
1718
- }
1719
- return await simulateCore(configOrPath, event, {
1720
- json: options.json ?? false,
1721
- verbose: options.verbose ?? false
1722
- });
1723
- }
1724
-
1725
- // src/commands/run/index.ts
1726
- import path9 from "path";
1727
- import os from "os";
1728
- import {
1729
- runFlow,
1730
- runServeMode
1731
- } from "@walkeros/docker";
1732
-
1733
- // src/commands/run/validators.ts
1734
- import { existsSync } from "fs";
1735
- var VALID_MODES = ["collect", "serve"];
1736
- function validateMode(mode) {
1737
- if (!VALID_MODES.includes(mode)) {
1738
- throw new Error(
1739
- `Invalid mode: "${mode}"
1740
- Valid modes: ${VALID_MODES.join(", ")}
1741
- Example: walkeros run collect ./flow.json`
1742
- );
1743
- }
1744
- }
1745
- function validateFlowFile(filePath) {
1746
- const absolutePath = resolveAsset(filePath, "bundle");
1747
- if (!existsSync(absolutePath)) {
1748
- throw new Error(
1749
- `Flow file not found: ${filePath}
1750
- Resolved path: ${absolutePath}
1751
- Make sure the file exists and the path is correct`
1752
- );
1753
- }
1754
- return absolutePath;
1755
- }
1756
- function validatePort(port) {
1757
- if (!Number.isInteger(port) || port < 1 || port > 65535) {
1758
- throw new Error(
1759
- `Invalid port: ${port}
1760
- Port must be an integer between 1 and 65535
1761
- Example: --port 8080`
1762
- );
1763
- }
1764
- }
1765
-
1766
- // src/commands/run/index.ts
1767
- async function runCommand(mode, options) {
1768
- const timer = createTimer();
1769
- timer.start();
1770
- const logger = createLogger({
1771
- verbose: options.verbose,
1772
- silent: options.silent ?? false,
1773
- json: options.json
1774
- });
1775
- try {
1776
- validateMode(mode);
1777
- const configPath = validateFlowFile(options.config);
1778
- if (options.port !== void 0) {
1779
- validatePort(options.port);
1780
- }
1781
- const isPreBuilt = configPath.endsWith(".mjs") || configPath.endsWith(".js") || configPath.endsWith(".cjs");
1782
- let flowPath = null;
1783
- if (mode === "collect") {
1784
- if (isPreBuilt) {
1785
- flowPath = path9.resolve(configPath);
1786
- if (!options.json && !options.silent) {
1787
- logger.info(`\u{1F4E6} Using pre-built flow: ${path9.basename(flowPath)}`);
1788
- }
1789
- } else {
1790
- if (!options.json && !options.silent) {
1791
- logger.info("\u{1F528} Building flow bundle...");
1792
- }
1793
- const rawConfig = await loadJsonConfig(configPath);
1794
- const tempPath = path9.join(
1795
- os.tmpdir(),
1796
- `walkeros-${Date.now()}-${Math.random().toString(36).slice(2, 9)}.mjs`
1797
- );
1798
- const existingBuild = typeof rawConfig === "object" && rawConfig !== null && "build" in rawConfig && typeof rawConfig.build === "object" ? rawConfig.build : {};
1799
- const configWithOutput = {
1800
- ...rawConfig,
1801
- build: {
1802
- ...existingBuild,
1803
- output: tempPath
1804
- }
1805
- };
1806
- await bundle(configWithOutput, {
1807
- cache: true,
1808
- verbose: options.verbose,
1809
- silent: options.json || options.silent
1810
- });
1811
- flowPath = tempPath;
1812
- if (!options.json && !options.silent) {
1813
- logger.success("\u2705 Bundle ready");
1814
- }
1815
- }
1816
- }
1817
- const executionMode = getExecutionMode(options);
1818
- if (executionMode === "docker") {
1819
- const dockerAvailable = await isDockerAvailable();
1820
- if (!dockerAvailable) {
1821
- throw new Error(
1822
- "Docker is not available. Please install Docker or use --local flag to execute locally."
1823
- );
1824
- }
1825
- if (!options.json && !options.silent) {
1826
- logger.info("\u{1F433} Executing in production runtime container...");
1827
- }
1828
- await executeRunInDocker(mode, flowPath, {
73
+ });
74
+ // Run command with subcommands
75
+ const runCmd = program
76
+ .command('run')
77
+ .description('Run walkerOS flows in collect or serve mode');
78
+ // Run collect subcommand
79
+ runCmd
80
+ .command('collect [file]')
81
+ .description('Run collector mode (event collection endpoint). Defaults to server-collect.mjs if no file specified.')
82
+ .option('-p, --port <number>', 'Port to listen on (default: 8080)', parseInt)
83
+ .option('-h, --host <address>', 'Host address (default: 0.0.0.0)')
84
+ .option('--json', 'Output results as JSON')
85
+ .option('-v, --verbose', 'Verbose output')
86
+ .option('--local', 'execute in local Node.js instead of Docker')
87
+ .option('--dry-run', 'preview command without executing')
88
+ .option('--silent', 'suppress output')
89
+ .action(async (file, options) => {
90
+ await runCommand('collect', {
91
+ config: file || 'server-collect.mjs',
1829
92
  port: options.port,
1830
93
  host: options.host,
1831
- staticDir: options.staticDir,
1832
- silent: options.silent
1833
- });
1834
- } else {
1835
- if (!options.json && !options.silent) {
1836
- const modeLabel = mode === "collect" ? "Collector" : "Server";
1837
- logger.info(`\u{1F5A5}\uFE0F Starting ${modeLabel} locally...`);
1838
- }
1839
- switch (mode) {
1840
- case "collect": {
1841
- if (!flowPath) {
1842
- throw new Error("Flow path is required for collect mode");
1843
- }
1844
- const config = {
1845
- port: options.port,
1846
- host: options.host
1847
- };
1848
- await runFlow(flowPath, config);
1849
- break;
1850
- }
1851
- case "serve": {
1852
- const config = {
1853
- port: options.port,
1854
- host: options.host,
1855
- staticDir: options.staticDir
1856
- };
1857
- await runServeMode(config);
1858
- break;
1859
- }
1860
- default:
1861
- throw new Error(`Unknown mode: ${mode}`);
1862
- }
1863
- }
1864
- } catch (error) {
1865
- const duration = timer.getElapsed() / 1e3;
1866
- const errorMessage = error instanceof Error ? error.message : String(error);
1867
- if (options.json) {
1868
- const output = {
1869
- success: false,
1870
- mode,
1871
- error: errorMessage,
1872
- duration
1873
- };
1874
- console.log(JSON.stringify(output, null, 2));
1875
- } else {
1876
- logger.error("\u274C Run failed:");
1877
- logger.error(errorMessage);
1878
- }
1879
- process.exit(1);
1880
- }
1881
- }
1882
- async function run(mode, options) {
1883
- const startTime = Date.now();
1884
- try {
1885
- validateMode(mode);
1886
- let flowFile;
1887
- if (typeof options.config === "string") {
1888
- flowFile = validateFlowFile(options.config);
1889
- } else {
1890
- throw new Error("Programmatic run() requires config file path");
1891
- }
1892
- if (options.port !== void 0) {
1893
- validatePort(options.port);
1894
- }
1895
- const isPreBuilt = flowFile.endsWith(".mjs") || flowFile.endsWith(".js") || flowFile.endsWith(".cjs");
1896
- let flowPath;
1897
- if (isPreBuilt) {
1898
- flowPath = path9.resolve(flowFile);
1899
- } else {
1900
- const rawConfig = await loadJsonConfig(flowFile);
1901
- const tempPath = path9.join(
1902
- os.tmpdir(),
1903
- `walkeros-${Date.now()}-${Math.random().toString(36).slice(2, 9)}.mjs`
1904
- );
1905
- const existingBuild = typeof rawConfig === "object" && rawConfig !== null && "build" in rawConfig && typeof rawConfig.build === "object" ? rawConfig.build : {};
1906
- const configWithOutput = {
1907
- ...rawConfig,
1908
- build: {
1909
- ...existingBuild,
1910
- output: tempPath
1911
- }
1912
- };
1913
- await bundle(configWithOutput, {
1914
- cache: true,
94
+ json: options.json,
1915
95
  verbose: options.verbose,
1916
- silent: true
1917
- });
1918
- flowPath = tempPath;
1919
- }
1920
- switch (mode) {
1921
- case "collect": {
1922
- const config = {
1923
- port: options.port,
1924
- host: options.host
1925
- };
1926
- await runFlow(flowPath, config);
1927
- break;
1928
- }
1929
- case "serve": {
1930
- const config = {
1931
- port: options.port,
1932
- host: options.host,
1933
- staticDir: options.staticDir
1934
- };
1935
- await runServeMode(config);
1936
- break;
1937
- }
1938
- default:
1939
- throw new Error(`Unknown mode: ${mode}`);
1940
- }
1941
- return {
1942
- success: true,
1943
- exitCode: 0,
1944
- duration: Date.now() - startTime
1945
- };
1946
- } catch (error) {
1947
- return {
1948
- success: false,
1949
- exitCode: 1,
1950
- duration: Date.now() - startTime,
1951
- error: error instanceof Error ? error.message : String(error)
1952
- };
1953
- }
1954
- }
1955
-
1956
- // src/index.ts
1957
- var program = new Command();
1958
- program.name("walkeros").description("walkerOS CLI - Bundle and deploy walkerOS components").version("0.3.4");
1959
- program.command("bundle [file]").description("Bundle NPM packages with custom code").option(
1960
- "-e, --env <name>",
1961
- "environment to build (for multi-environment configs)"
1962
- ).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) => {
1963
- await bundleCommand({
1964
- config: file || "bundle.config.json",
1965
- env: options.env,
1966
- all: options.all,
1967
- stats: options.stats,
1968
- json: options.json,
1969
- cache: options.cache,
1970
- verbose: options.verbose,
1971
- local: options.local,
1972
- dryRun: options.dryRun,
1973
- silent: options.silent
1974
- });
1975
- });
1976
- program.command("simulate [file]").description("Simulate event processing and capture API calls").option("-e, --event <json>", "Event to simulate (JSON string)").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) => {
1977
- await simulateCommand({
1978
- config: file || "bundle.config.json",
1979
- event: options.event,
1980
- json: options.json,
1981
- verbose: options.verbose,
1982
- local: options.local,
1983
- dryRun: options.dryRun,
1984
- silent: options.silent
1985
- });
1986
- });
1987
- var runCmd = program.command("run").description("Run walkerOS flows in collect or serve mode");
1988
- runCmd.command("collect [file]").description(
1989
- "Run collector mode (event collection endpoint). Defaults to server-collect.mjs if no file specified."
1990
- ).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) => {
1991
- await runCommand("collect", {
1992
- config: file || "server-collect.mjs",
1993
- port: options.port,
1994
- host: options.host,
1995
- json: options.json,
1996
- verbose: options.verbose,
1997
- local: options.local,
1998
- dryRun: options.dryRun,
1999
- silent: options.silent
2000
- });
96
+ local: options.local,
97
+ dryRun: options.dryRun,
98
+ silent: options.silent,
99
+ });
2001
100
  });
2002
- runCmd.command("serve [file]").description(
2003
- "Run serve mode (static file server for browser bundles). Defaults to web-serve.mjs if no file specified."
2004
- ).option("-p, --port <number>", "Port to listen on (default: 8080)", parseInt).option("-h, --host <address>", "Host address (default: 0.0.0.0)").option("--static-dir <dir>", "Static directory for serve mode").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) => {
2005
- await runCommand("serve", {
2006
- config: file || "web-serve.mjs",
2007
- port: options.port,
2008
- host: options.host,
2009
- staticDir: options.staticDir,
2010
- json: options.json,
2011
- verbose: options.verbose,
2012
- local: options.local,
2013
- dryRun: options.dryRun,
2014
- silent: options.silent
2015
- });
101
+ // Run serve subcommand
102
+ runCmd
103
+ .command('serve [file]')
104
+ .description('Run serve mode (single-file server for browser bundles). Defaults to baked-in web-serve.js if no file specified.')
105
+ .option('-p, --port <number>', 'Port to listen on (default: 8080)', parseInt)
106
+ .option('-h, --host <address>', 'Host address (default: 0.0.0.0)')
107
+ .option('--name <filename>', 'Filename in URL (default: walker.js)')
108
+ .option('--path <directory>', 'URL directory path (e.g., libs/v1)')
109
+ .option('--json', 'Output results as JSON')
110
+ .option('-v, --verbose', 'Verbose output')
111
+ .option('--local', 'execute in local Node.js instead of Docker')
112
+ .option('--dry-run', 'preview command without executing')
113
+ .option('--silent', 'suppress output')
114
+ .action(async (file, options) => {
115
+ await runCommand('serve', {
116
+ config: file || 'web-serve.js',
117
+ port: options.port,
118
+ host: options.host,
119
+ serveName: options.name,
120
+ servePath: options.path,
121
+ json: options.json,
122
+ verbose: options.verbose,
123
+ local: options.local,
124
+ dryRun: options.dryRun,
125
+ silent: options.silent,
126
+ });
2016
127
  });
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)
2017
131
  program.parse();
2018
- export {
2019
- bundle,
2020
- bundleCommand,
2021
- run,
2022
- runCommand,
2023
- simulate,
2024
- simulateCommand
2025
- };
2026
132
  //# sourceMappingURL=index.js.map