@walkeros/cli 3.1.0 → 3.2.0-next-1775064795590

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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __esm = (fn2, res) => function __init() {
4
- return fn2 && (res = (0, fn2[__getOwnPropNames(fn2)[0]])(fn2 = 0)), res;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
5
  };
6
6
  var __export = (target, all) => {
7
7
  for (var name in all)
@@ -205,26 +205,26 @@ function mergeAuthHeaders(token, headers) {
205
205
  if (token) normalized.Authorization = `Bearer ${token}`;
206
206
  return normalized;
207
207
  }
208
- async function apiFetch(path15, init) {
208
+ async function apiFetch(path19, init) {
209
209
  const baseUrl = resolveAppUrl();
210
210
  const token = resolveToken()?.token;
211
- return fetch(`${baseUrl}${path15}`, {
211
+ return fetch(`${baseUrl}${path19}`, {
212
212
  ...init,
213
213
  headers: mergeAuthHeaders(token, init?.headers)
214
214
  });
215
215
  }
216
- async function publicFetch(path15, init) {
216
+ async function publicFetch(path19, init) {
217
217
  const baseUrl = resolveAppUrl();
218
- return fetch(`${baseUrl}${path15}`, init);
218
+ return fetch(`${baseUrl}${path19}`, init);
219
219
  }
220
- async function deployFetch(path15, init) {
220
+ async function deployFetch(path19, init) {
221
221
  const baseUrl = resolveAppUrl();
222
222
  const token = resolveDeployToken() ?? resolveToken()?.token;
223
223
  if (!token)
224
224
  throw new Error(
225
225
  "No authentication token available. Set WALKEROS_DEPLOY_TOKEN or run walkeros auth login."
226
226
  );
227
- return fetch(`${baseUrl}${path15}`, {
227
+ return fetch(`${baseUrl}${path19}`, {
228
228
  ...init,
229
229
  headers: mergeAuthHeaders(token, init?.headers)
230
230
  });
@@ -237,7 +237,6 @@ var init_http = __esm({
237
237
  });
238
238
 
239
239
  // src/config/utils.ts
240
- import crypto from "crypto";
241
240
  import fs2 from "fs-extra";
242
241
  import path3 from "path";
243
242
  function isUrl(str) {
@@ -248,132 +247,79 @@ function isUrl(str) {
248
247
  return false;
249
248
  }
250
249
  }
251
- async function downloadFromUrl(url) {
252
- if (!isUrl(url)) {
253
- throw new Error(`Invalid URL: ${url}`);
254
- }
255
- try {
256
- const token = resolveToken()?.token;
257
- const response = await fetch(url, {
258
- headers: mergeAuthHeaders(token)
259
- });
260
- if (!response.ok) {
261
- throw new Error(
262
- `Failed to download ${url}: ${response.status} ${response.statusText}`
263
- );
264
- }
265
- const content = await response.text();
266
- const downloadsDir = getTmpPath(void 0, "downloads");
267
- await fs2.ensureDir(downloadsDir);
268
- const downloadId = crypto.randomUUID().slice(0, 8);
269
- const tempPath = path3.join(downloadsDir, `flow-${downloadId}.json`);
270
- await fs2.writeFile(tempPath, content, "utf-8");
271
- return tempPath;
272
- } catch (error) {
273
- if (error instanceof Error) {
274
- throw new Error(`Failed to download from URL: ${error.message}`);
275
- }
276
- throw error;
250
+ async function fetchContentString(url) {
251
+ const token = resolveToken()?.token;
252
+ const response = await fetch(url, {
253
+ headers: mergeAuthHeaders(token)
254
+ });
255
+ if (!response.ok) {
256
+ throw new Error(
257
+ `Failed to fetch ${url}: ${response.status} ${response.statusText}`
258
+ );
277
259
  }
260
+ return response.text();
278
261
  }
279
- async function loadJsonConfig(configPath) {
280
- const trimmed = configPath.trim();
262
+ async function resolveContent(input) {
263
+ const trimmed = input.trim();
281
264
  if (isUrl(trimmed)) {
282
- const absolutePath2 = await downloadFromUrl(trimmed);
283
- try {
284
- const rawConfig = await fs2.readJson(absolutePath2);
285
- return rawConfig;
286
- } catch (error) {
287
- throw new Error(
288
- `Invalid JSON in config file: ${configPath}. ${error instanceof Error ? error.message : error}`
289
- );
290
- } finally {
291
- try {
292
- await fs2.remove(absolutePath2);
293
- } catch {
294
- }
295
- }
265
+ return fetchContentString(trimmed);
296
266
  }
297
267
  const absolutePath = path3.resolve(trimmed);
298
268
  if (await fs2.pathExists(absolutePath)) {
299
- try {
300
- const rawConfig = await fs2.readJson(absolutePath);
301
- return rawConfig;
302
- } catch (error) {
303
- throw new Error(
304
- `Invalid JSON in config file: ${configPath}. ${error instanceof Error ? error.message : error}`
305
- );
306
- }
269
+ return fs2.readFile(absolutePath, "utf-8");
307
270
  }
308
271
  if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
309
- try {
310
- return JSON.parse(trimmed);
311
- } catch (jsonError) {
272
+ return trimmed;
273
+ }
274
+ throw new Error(`Configuration file not found: ${absolutePath}`);
275
+ }
276
+ async function loadConfig(input, options) {
277
+ const json = options?.json !== false;
278
+ if (!json) {
279
+ return resolveContent(input);
280
+ }
281
+ const trimmed = input.trim();
282
+ try {
283
+ const content = await resolveContent(trimmed);
284
+ return JSON.parse(content);
285
+ } catch (error) {
286
+ const errorMessage = error instanceof Error ? error.message : String(error);
287
+ if (errorMessage.includes("not found") || errorMessage.includes("Failed to fetch")) {
288
+ throw error;
289
+ }
290
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
312
291
  throw new Error(
313
- `Input appears to be JSON but contains errors: ${jsonError instanceof Error ? jsonError.message : String(jsonError)}`
292
+ `Input appears to be JSON but contains errors: ${errorMessage}`
314
293
  );
315
294
  }
295
+ throw new Error(`Invalid JSON in config file: ${input}. ${errorMessage}`);
316
296
  }
317
- throw new Error(`Configuration file not found: ${absolutePath}`);
297
+ }
298
+ async function loadJsonConfig(configPath) {
299
+ return loadConfig(configPath, { json: true });
318
300
  }
319
301
  async function loadJsonFromSource(source, options) {
320
302
  const paramName = options?.name || "input";
321
303
  if (!source || source.trim() === "") {
322
- if (options?.required) {
323
- throw new Error(`${paramName} is required`);
324
- }
325
- if (options?.fallback !== void 0) {
326
- return options.fallback;
327
- }
304
+ if (options?.required) throw new Error(`${paramName} is required`);
305
+ if (options?.fallback !== void 0) return options.fallback;
328
306
  return {};
329
307
  }
330
- const trimmedSource = source.trim();
331
- if (isUrl(trimmedSource)) {
332
- try {
333
- const tempPath = await downloadFromUrl(trimmedSource);
334
- try {
335
- const data = await fs2.readJson(tempPath);
336
- return data;
337
- } finally {
338
- try {
339
- await fs2.remove(tempPath);
340
- } catch {
341
- }
342
- }
343
- } catch (error) {
344
- throw new Error(
345
- `Failed to load ${paramName} from URL ${trimmedSource}: ${getErrorMessage(error)}`
346
- );
347
- }
348
- }
349
- const resolvedPath = path3.resolve(trimmedSource);
350
- if (await fs2.pathExists(resolvedPath)) {
351
- try {
352
- const data = await fs2.readJson(resolvedPath);
353
- return data;
354
- } catch (error) {
355
- throw new Error(
356
- `Failed to parse ${paramName} from file ${trimmedSource}: ${getErrorMessage(error)}`
357
- );
358
- }
359
- }
360
308
  try {
361
- const parsed = JSON.parse(trimmedSource);
362
- return parsed;
363
- } catch (jsonError) {
364
- if (!trimmedSource.startsWith("{") && !trimmedSource.startsWith("[")) {
365
- return { name: trimmedSource };
309
+ return await loadJsonConfig(source);
310
+ } catch (error) {
311
+ const trimmed = source.trim();
312
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
313
+ return { name: trimmed };
366
314
  }
367
315
  throw new Error(
368
- `Failed to parse ${paramName}. Input appears to be JSON but contains errors: ${jsonError instanceof Error ? jsonError.message : String(jsonError)}`
316
+ `Failed to parse ${paramName}. ${error instanceof Error ? error.message : String(error)}`
369
317
  );
370
318
  }
371
319
  }
372
320
  var init_utils = __esm({
373
321
  "src/config/utils.ts"() {
374
322
  "use strict";
375
- init_core();
376
- init_tmp();
377
323
  init_http();
378
324
  init_config_file();
379
325
  }
@@ -433,33 +379,82 @@ import path5 from "path";
433
379
  import fs3 from "fs-extra";
434
380
  async function resolveLocalPackage(packageName, localPath, configDir, logger) {
435
381
  const absolutePath = path5.isAbsolute(localPath) ? localPath : path5.resolve(configDir, localPath);
436
- if (!await fs3.pathExists(absolutePath)) {
382
+ const stat = await fs3.stat(absolutePath).catch(() => null);
383
+ if (stat?.isFile()) {
384
+ return { name: packageName, absolutePath, type: "file" };
385
+ }
386
+ if (!stat) {
387
+ for (const ext of [".ts", ".mjs", ".js", ".json"]) {
388
+ const withExt = absolutePath + ext;
389
+ if (await fs3.pathExists(withExt)) {
390
+ return { name: packageName, absolutePath: withExt, type: "file" };
391
+ }
392
+ }
437
393
  throw new Error(
438
394
  `Local package path not found: ${localPath} (resolved to ${absolutePath})`
439
395
  );
440
396
  }
441
- const pkgJsonPath = path5.join(absolutePath, "package.json");
442
- if (!await fs3.pathExists(pkgJsonPath)) {
443
- throw new Error(
444
- `No package.json found at ${absolutePath}. Is this a valid package directory?`
445
- );
446
- }
447
- const distPath = path5.join(absolutePath, "dist");
448
- const hasDistFolder = await fs3.pathExists(distPath);
449
- if (!hasDistFolder) {
450
- logger.warn(
451
- `\u26A0\uFE0F ${packageName}: No dist/ folder found. Using package root.`
397
+ if (stat.isDirectory()) {
398
+ const hasPkgJson = await fs3.pathExists(
399
+ path5.join(absolutePath, "package.json")
452
400
  );
401
+ if (hasPkgJson) {
402
+ const distPath = path5.join(absolutePath, "dist");
403
+ const hasDistFolder = await fs3.pathExists(distPath);
404
+ if (!hasDistFolder) {
405
+ logger.warn(
406
+ `\u26A0\uFE0F ${packageName}: No dist/ folder found. Using package root.`
407
+ );
408
+ }
409
+ return {
410
+ name: packageName,
411
+ absolutePath,
412
+ type: "package",
413
+ distPath: hasDistFolder ? distPath : absolutePath,
414
+ hasDistFolder
415
+ };
416
+ }
417
+ return { name: packageName, absolutePath, type: "directory" };
453
418
  }
454
- return {
455
- name: packageName,
456
- absolutePath,
457
- distPath: hasDistFolder ? distPath : absolutePath,
458
- hasDistFolder
459
- };
419
+ throw new Error(
420
+ `Local package path not found: ${localPath} (resolved to ${absolutePath})`
421
+ );
460
422
  }
461
423
  async function copyLocalPackage(localPkg, targetDir, logger) {
462
424
  const packageDir = path5.join(targetDir, "node_modules", localPkg.name);
425
+ if (localPkg.type === "file") {
426
+ await fs3.ensureDir(packageDir);
427
+ const ext = path5.extname(localPkg.absolutePath);
428
+ await fs3.copy(localPkg.absolutePath, path5.join(packageDir, `index${ext}`));
429
+ await fs3.writeJson(path5.join(packageDir, "package.json"), {
430
+ name: localPkg.name,
431
+ main: `./index${ext}`
432
+ });
433
+ logger.info(
434
+ `\u{1F4E6} Using local file: ${localPkg.name} from ${localPkg.absolutePath}`
435
+ );
436
+ return packageDir;
437
+ }
438
+ if (localPkg.type === "directory") {
439
+ await fs3.ensureDir(path5.dirname(packageDir));
440
+ const entries = await fs3.readdir(localPkg.absolutePath);
441
+ for (const entry of entries) {
442
+ if (!["node_modules", ".turbo", ".git"].includes(entry)) {
443
+ await fs3.copy(
444
+ path5.join(localPkg.absolutePath, entry),
445
+ path5.join(packageDir, entry)
446
+ );
447
+ }
448
+ }
449
+ await fs3.writeJson(path5.join(packageDir, "package.json"), {
450
+ name: localPkg.name,
451
+ main: "./index.ts"
452
+ });
453
+ logger.info(
454
+ `\u{1F4E6} Using local dir: ${localPkg.name} from ${localPkg.absolutePath}`
455
+ );
456
+ return packageDir;
457
+ }
463
458
  await fs3.ensureDir(path5.dirname(packageDir));
464
459
  await fs3.copy(
465
460
  path5.join(localPkg.absolutePath, "package.json"),
@@ -505,14 +500,11 @@ function detectPlatformFromPath(inputPath) {
505
500
  }
506
501
  async function loadContent(inputPath) {
507
502
  if (isUrl(inputPath)) {
508
- const token = resolveToken()?.token;
509
- const response = await fetch(inputPath, {
510
- headers: mergeAuthHeaders(token)
511
- });
512
- if (!response.ok) {
513
- throw new Error(`Failed to fetch ${inputPath}: ${response.status}`);
514
- }
515
- return response.text();
503
+ return fetchContentString(inputPath);
504
+ }
505
+ const trimmed = inputPath.trim();
506
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
507
+ return trimmed;
516
508
  }
517
509
  return fs4.readFile(inputPath, "utf8");
518
510
  }
@@ -520,12 +512,12 @@ var init_input_detector = __esm({
520
512
  "src/core/input-detector.ts"() {
521
513
  "use strict";
522
514
  init_utils();
523
- init_http();
524
- init_config_file();
525
515
  }
526
516
  });
527
517
 
528
518
  // src/core/stdin.ts
519
+ import fs5 from "fs-extra";
520
+ import path6 from "path";
529
521
  function isStdinPiped() {
530
522
  return !process.stdin.isTTY;
531
523
  }
@@ -540,9 +532,17 @@ async function readStdin() {
540
532
  }
541
533
  return content;
542
534
  }
535
+ async function readStdinToTempFile(label) {
536
+ const content = await readStdin();
537
+ const tmpPath = getTmpPath(void 0, `stdin-${label}.json`);
538
+ await fs5.ensureDir(path6.dirname(tmpPath));
539
+ await fs5.writeFile(tmpPath, content, "utf-8");
540
+ return tmpPath;
541
+ }
543
542
  var init_stdin = __esm({
544
543
  "src/core/stdin.ts"() {
545
544
  "use strict";
545
+ init_tmp();
546
546
  }
547
547
  });
548
548
 
@@ -599,6 +599,129 @@ var init_sse = __esm({
599
599
  }
600
600
  });
601
601
 
602
+ // src/core/event-validation.ts
603
+ import { schemas } from "@walkeros/core/dev";
604
+ function validateEvent(input, level = "strict") {
605
+ const errors = [];
606
+ const warnings = [];
607
+ const details = {};
608
+ if (typeof input !== "object" || input === null || Array.isArray(input)) {
609
+ errors.push({
610
+ path: "root",
611
+ message: "Event must be an object",
612
+ code: "NOT_AN_OBJECT"
613
+ });
614
+ return { valid: false, errors, warnings, details };
615
+ }
616
+ const event = input;
617
+ if (!("name" in event) || event.name === void 0) {
618
+ errors.push({
619
+ path: "name",
620
+ message: "Event must have a name field",
621
+ code: "MISSING_EVENT_NAME"
622
+ });
623
+ } else if (typeof event.name !== "string" || event.name.trim() === "") {
624
+ errors.push({
625
+ path: "name",
626
+ message: "Event name cannot be empty",
627
+ value: event.name,
628
+ code: "EMPTY_EVENT_NAME"
629
+ });
630
+ } else {
631
+ const name = event.name;
632
+ if (!name.includes(" ")) {
633
+ if (level === "strict") {
634
+ errors.push({
635
+ path: "name",
636
+ message: 'Event name must be "entity action" format with space (e.g., "page view")',
637
+ value: name,
638
+ code: "INVALID_EVENT_NAME"
639
+ });
640
+ details.entity = null;
641
+ details.action = null;
642
+ } else if (level === "standard") {
643
+ warnings.push({
644
+ path: "name",
645
+ message: `Event name "${name}" should follow "ENTITY ACTION" format (e.g., "page view")`
646
+ });
647
+ details.entity = null;
648
+ details.action = null;
649
+ }
650
+ } else {
651
+ const parts = name.trim().split(/\s+/);
652
+ const action = parts.pop();
653
+ const entity = parts.join(" ");
654
+ details.entity = entity;
655
+ details.action = action;
656
+ }
657
+ }
658
+ if (level === "minimal") {
659
+ return { valid: errors.length === 0, errors, warnings, details };
660
+ }
661
+ const zodResult = PartialEventSchema.safeParse(input);
662
+ if (!zodResult.success) {
663
+ for (const issue of zodResult.error.issues) {
664
+ const issuePath = issue.path.join(".");
665
+ if (issuePath === "name") continue;
666
+ errors.push({
667
+ path: issuePath || "root",
668
+ message: issue.message,
669
+ code: "SCHEMA_VALIDATION"
670
+ });
671
+ }
672
+ }
673
+ if (level === "strict") {
674
+ if (!event.consent) {
675
+ warnings.push({
676
+ path: "consent",
677
+ message: "No consent object provided",
678
+ suggestion: "Consider adding a consent object for GDPR/privacy compliance"
679
+ });
680
+ }
681
+ details.hasConsent = !!event.consent;
682
+ details.hasData = !!event.data;
683
+ details.hasContext = !!event.context;
684
+ }
685
+ return { valid: errors.length === 0, errors, warnings, details };
686
+ }
687
+ var PartialEventSchema;
688
+ var init_event_validation = __esm({
689
+ "src/core/event-validation.ts"() {
690
+ "use strict";
691
+ ({ PartialEventSchema } = schemas);
692
+ }
693
+ });
694
+
695
+ // src/core/package-path.ts
696
+ import fs6 from "fs";
697
+ import path7 from "path";
698
+ function resolvePackageImportPath(packageName, packages, configDir, subpath) {
699
+ const entry = packages?.[packageName];
700
+ if (entry?.path) {
701
+ const resolved = path7.isAbsolute(entry.path) ? entry.path : path7.resolve(configDir, entry.path);
702
+ if (!subpath) return resolved;
703
+ try {
704
+ const pkgJson = JSON.parse(
705
+ fs6.readFileSync(path7.join(resolved, "package.json"), "utf8")
706
+ );
707
+ const exportKey = `.${subpath.startsWith("/") ? subpath : `/${subpath}`}`;
708
+ const exp = pkgJson.exports?.[exportKey];
709
+ if (exp) {
710
+ const target = typeof exp === "string" ? exp : exp.import || exp.require || exp.default;
711
+ if (target) return path7.join(resolved, target);
712
+ }
713
+ } catch {
714
+ }
715
+ return path7.join(resolved, subpath.replace(/^\//, ""));
716
+ }
717
+ return subpath ? `${packageName}${subpath}` : packageName;
718
+ }
719
+ var init_package_path = __esm({
720
+ "src/core/package-path.ts"() {
721
+ "use strict";
722
+ }
723
+ });
724
+
602
725
  // src/core/index.ts
603
726
  var init_core = __esm({
604
727
  "src/core/index.ts"() {
@@ -615,20 +738,19 @@ var init_core = __esm({
615
738
  init_auth();
616
739
  init_http();
617
740
  init_sse();
741
+ init_event_validation();
742
+ init_package_path();
618
743
  }
619
744
  });
620
745
 
621
746
  // src/config/validators.ts
622
- import { schemas } from "@walkeros/core/dev";
623
- function isObject(value) {
624
- return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
625
- }
747
+ import { schemas as schemas2 } from "@walkeros/core/dev";
626
748
  function validateFlowConfig(data) {
627
749
  const result = safeParseConfig(data);
628
750
  if (!result.success) {
629
751
  const errors = result.error.issues.map((issue) => {
630
- const path15 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
631
- return ` - ${path15}: ${issue.message}`;
752
+ const path19 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
753
+ return ` - ${path19}: ${issue.message}`;
632
754
  }).join("\n");
633
755
  throw new Error(`Invalid configuration:
634
756
  ${errors}`);
@@ -642,7 +764,7 @@ var safeParseConfig;
642
764
  var init_validators = __esm({
643
765
  "src/config/validators.ts"() {
644
766
  "use strict";
645
- ({ safeParseConfig } = schemas);
767
+ ({ safeParseConfig } = schemas2);
646
768
  }
647
769
  });
648
770
 
@@ -664,7 +786,7 @@ var init_build_defaults = __esm({
664
786
  minify: true,
665
787
  sourcemap: false,
666
788
  cache: true,
667
- windowCollector: "collector",
789
+ windowCollector: "walkerOS",
668
790
  windowElb: "elb"
669
791
  };
670
792
  SERVER_BUILD_DEFAULTS = {
@@ -683,8 +805,8 @@ var init_build_defaults = __esm({
683
805
  });
684
806
 
685
807
  // src/config/loader.ts
686
- import path6 from "path";
687
- import fs5 from "fs-extra";
808
+ import path8 from "path";
809
+ import fs7 from "fs-extra";
688
810
  import { getFlowSettings, getPlatform } from "@walkeros/core";
689
811
  function loadBundleConfig(rawConfig, options) {
690
812
  const config = validateFlowConfig(rawConfig);
@@ -703,11 +825,11 @@ function loadBundleConfig(rawConfig, options) {
703
825
  const buildDefaults = getBuildDefaults(platform);
704
826
  const packages = flowSettings.packages || {};
705
827
  const output = options.buildOverrides?.output || getDefaultOutput(platform);
706
- const configDir = isUrl(options.configPath) ? process.cwd() : path6.dirname(options.configPath);
828
+ const configDir = isUrl(options.configPath) ? process.cwd() : path8.dirname(options.configPath);
707
829
  let includes = config.include;
708
830
  if (!includes) {
709
- const defaultIncludePath = path6.resolve(configDir, DEFAULT_INCLUDE_FOLDER);
710
- if (fs5.pathExistsSync(defaultIncludePath)) {
831
+ const defaultIncludePath = path8.resolve(configDir, DEFAULT_INCLUDE_FOLDER);
832
+ if (fs7.pathExistsSync(defaultIncludePath)) {
711
833
  includes = [DEFAULT_INCLUDE_FOLDER];
712
834
  }
713
835
  }
@@ -792,6 +914,43 @@ var init_config = __esm({
792
914
  }
793
915
  });
794
916
 
917
+ // src/commands/bundle/config-classifier.ts
918
+ function containsCodeMarkers(value) {
919
+ if (typeof value === "string") {
920
+ return value.startsWith("$code:") || value.startsWith("$store:") || value.includes("__WALKEROS_ENV:");
921
+ }
922
+ if (Array.isArray(value)) {
923
+ return value.some(containsCodeMarkers);
924
+ }
925
+ if (value !== null && typeof value === "object") {
926
+ return Object.values(value).some(
927
+ containsCodeMarkers
928
+ );
929
+ }
930
+ return false;
931
+ }
932
+ function classifyStepProperties(step) {
933
+ const codeProps = {};
934
+ const dataProps = {};
935
+ for (const [key, value] of Object.entries(step)) {
936
+ if (key === "code") {
937
+ codeProps[key] = value;
938
+ continue;
939
+ }
940
+ if (containsCodeMarkers(value)) {
941
+ codeProps[key] = value;
942
+ } else {
943
+ dataProps[key] = value;
944
+ }
945
+ }
946
+ return { codeProps, dataProps };
947
+ }
948
+ var init_config_classifier = __esm({
949
+ "src/commands/bundle/config-classifier.ts"() {
950
+ "use strict";
951
+ }
952
+ });
953
+
795
954
  // src/core/cache-utils.ts
796
955
  import { getHashServer } from "@walkeros/server-core";
797
956
  import semver from "semver";
@@ -832,8 +991,8 @@ var init_cache_utils = __esm({
832
991
 
833
992
  // src/commands/bundle/package-manager.ts
834
993
  import pacote from "pacote";
835
- import path7 from "path";
836
- import fs6 from "fs-extra";
994
+ import path9 from "path";
995
+ import fs8 from "fs-extra";
837
996
  import semver2 from "semver";
838
997
  async function withTimeout(promise, ms, errorMessage) {
839
998
  let timer;
@@ -847,7 +1006,7 @@ async function withTimeout(promise, ms, errorMessage) {
847
1006
  }
848
1007
  }
849
1008
  function getPackageDirectory(baseDir, packageName) {
850
- return path7.join(baseDir, "node_modules", packageName);
1009
+ return path9.join(baseDir, "node_modules", packageName);
851
1010
  }
852
1011
  async function collectAllSpecs(packages, logger, configDir) {
853
1012
  const allSpecs = /* @__PURE__ */ new Map();
@@ -873,7 +1032,47 @@ async function collectAllSpecs(packages, logger, configDir) {
873
1032
  optional: item.optional,
874
1033
  localPath: item.localPath
875
1034
  });
876
- if (item.localPath) continue;
1035
+ if (item.localPath) {
1036
+ const resolvedPath = path9.isAbsolute(item.localPath) ? item.localPath : path9.resolve(configDir || process.cwd(), item.localPath);
1037
+ const candidatePath = path9.join(resolvedPath, "package.json");
1038
+ const hasPkgJson = await fs8.pathExists(candidatePath);
1039
+ if (hasPkgJson) {
1040
+ try {
1041
+ const pkgJson = await fs8.readJson(candidatePath);
1042
+ const deps2 = pkgJson.dependencies || {};
1043
+ for (const [depName, depSpec] of Object.entries(deps2)) {
1044
+ if (typeof depSpec === "string") {
1045
+ queue.push({
1046
+ name: depName,
1047
+ spec: depSpec,
1048
+ source: "dependency",
1049
+ from: item.name,
1050
+ optional: false
1051
+ });
1052
+ }
1053
+ }
1054
+ const peerDeps2 = pkgJson.peerDependencies || {};
1055
+ const peerMeta2 = pkgJson.peerDependenciesMeta || {};
1056
+ for (const [depName, depSpec] of Object.entries(peerDeps2)) {
1057
+ if (typeof depSpec === "string") {
1058
+ const isOptional = peerMeta2[depName]?.optional === true;
1059
+ queue.push({
1060
+ name: depName,
1061
+ spec: depSpec,
1062
+ source: "peerDependency",
1063
+ from: item.name,
1064
+ optional: isOptional
1065
+ });
1066
+ }
1067
+ }
1068
+ } catch (error) {
1069
+ logger.debug(
1070
+ `Failed to read package.json for local package ${item.name}: ${error}`
1071
+ );
1072
+ }
1073
+ }
1074
+ continue;
1075
+ }
877
1076
  let manifest;
878
1077
  try {
879
1078
  manifest = await withTimeout(
@@ -920,7 +1119,7 @@ async function collectAllSpecs(packages, logger, configDir) {
920
1119
  function resolveVersionConflicts(allSpecs, logger) {
921
1120
  const resolved = /* @__PURE__ */ new Map();
922
1121
  for (const [name, specs] of allSpecs) {
923
- const localSpec = specs.find((s2) => s2.localPath);
1122
+ const localSpec = specs.find((s) => s.localPath);
924
1123
  if (localSpec) {
925
1124
  resolved.set(name, {
926
1125
  name,
@@ -929,13 +1128,13 @@ function resolveVersionConflicts(allSpecs, logger) {
929
1128
  });
930
1129
  continue;
931
1130
  }
932
- const nonPeerSpecs = specs.filter((s2) => s2.source !== "peerDependency");
933
- const peerSpecs = specs.filter((s2) => s2.source === "peerDependency");
1131
+ const nonPeerSpecs = specs.filter((s) => s.source !== "peerDependency");
1132
+ const peerSpecs = specs.filter((s) => s.source === "peerDependency");
934
1133
  let activeSpecs;
935
1134
  if (nonPeerSpecs.length > 0) {
936
1135
  activeSpecs = nonPeerSpecs;
937
1136
  } else {
938
- const requiredPeers = peerSpecs.filter((s2) => !s2.optional);
1137
+ const requiredPeers = peerSpecs.filter((s) => !s.optional);
939
1138
  if (requiredPeers.length === 0) {
940
1139
  logger.debug(`Skipping optional peer dependency: ${name}`);
941
1140
  continue;
@@ -945,19 +1144,19 @@ function resolveVersionConflicts(allSpecs, logger) {
945
1144
  activeSpecs.sort(
946
1145
  (a2, b2) => SOURCE_PRIORITY[a2.source] - SOURCE_PRIORITY[b2.source]
947
1146
  );
948
- const directSpecs = activeSpecs.filter((s2) => s2.source === "direct");
949
- const directExact = directSpecs.find((s2) => semver2.valid(s2.spec) !== null);
1147
+ const directSpecs = activeSpecs.filter((s) => s.source === "direct");
1148
+ const directExact = directSpecs.find((s) => semver2.valid(s.spec) !== null);
950
1149
  let chosenVersion;
951
1150
  if (directExact) {
952
1151
  chosenVersion = directExact.spec;
953
1152
  } else if (directSpecs.length > 0) {
954
1153
  chosenVersion = directSpecs[0].spec;
955
1154
  } else {
956
- const exactVersions = activeSpecs.filter((s2) => semver2.valid(s2.spec) !== null).map((s2) => s2.spec);
1155
+ const exactVersions = activeSpecs.filter((s) => semver2.valid(s.spec) !== null).map((s) => s.spec);
957
1156
  const uniqueExact = [...new Set(exactVersions)];
958
1157
  if (uniqueExact.length > 1) {
959
1158
  throw new Error(
960
- `Version conflict for ${name}: ${uniqueExact.join(" vs ")} (from ${activeSpecs.map((s2) => `${s2.spec} via ${s2.from}`).join(", ")})`
1159
+ `Version conflict for ${name}: ${uniqueExact.join(" vs ")} (from ${activeSpecs.map((s) => `${s.spec} via ${s.from}`).join(", ")})`
961
1160
  );
962
1161
  } else if (uniqueExact.length === 1) {
963
1162
  chosenVersion = uniqueExact[0];
@@ -1004,7 +1203,7 @@ async function downloadPackages(packages, targetDir, logger, useCache = true, co
1004
1203
  logger.debug("Resolving dependencies");
1005
1204
  const allSpecs = await collectAllSpecs(packages, logger, configDir);
1006
1205
  const resolved = resolveVersionConflicts(allSpecs, logger);
1007
- await fs6.ensureDir(targetDir);
1206
+ await fs8.ensureDir(targetDir);
1008
1207
  const localPackageMap = /* @__PURE__ */ new Map();
1009
1208
  for (const pkg of packages) {
1010
1209
  if (pkg.path) localPackageMap.set(pkg.name, pkg.path);
@@ -1033,8 +1232,8 @@ async function downloadPackages(packages, targetDir, logger, useCache = true, co
1033
1232
  logger.debug(`Downloading ${packageSpec} (cached)`);
1034
1233
  }
1035
1234
  try {
1036
- await fs6.ensureDir(path7.dirname(packageDir));
1037
- await fs6.copy(cachedPath, packageDir);
1235
+ await fs8.ensureDir(path9.dirname(packageDir));
1236
+ await fs8.copy(cachedPath, packageDir);
1038
1237
  packagePaths.set(name, packageDir);
1039
1238
  continue;
1040
1239
  } catch {
@@ -1042,7 +1241,7 @@ async function downloadPackages(packages, targetDir, logger, useCache = true, co
1042
1241
  }
1043
1242
  }
1044
1243
  try {
1045
- await fs6.ensureDir(path7.dirname(packageDir));
1244
+ await fs8.ensureDir(path9.dirname(packageDir));
1046
1245
  const cacheDir = process.env.NPM_CACHE_DIR || getTmpPath(tmpDir, "cache", "npm");
1047
1246
  await withTimeout(
1048
1247
  pacote.extract(packageSpec, packageDir, {
@@ -1057,8 +1256,8 @@ async function downloadPackages(packages, targetDir, logger, useCache = true, co
1057
1256
  }
1058
1257
  if (useCache) {
1059
1258
  try {
1060
- await fs6.ensureDir(path7.dirname(cachedPath));
1061
- await fs6.copy(packageDir, cachedPath);
1259
+ await fs8.ensureDir(path9.dirname(cachedPath));
1260
+ await fs8.copy(packageDir, cachedPath);
1062
1261
  } catch {
1063
1262
  }
1064
1263
  }
@@ -1072,11 +1271,11 @@ async function downloadPackages(packages, targetDir, logger, useCache = true, co
1072
1271
  async function getCachedPackagePath(pkg, tmpDir) {
1073
1272
  const cacheDir = getTmpPath(tmpDir, "cache", "packages");
1074
1273
  const cacheKey = await getPackageCacheKey(pkg.name, pkg.version);
1075
- return path7.join(cacheDir, cacheKey);
1274
+ return path9.join(cacheDir, cacheKey);
1076
1275
  }
1077
1276
  async function isPackageCached(pkg, tmpDir) {
1078
1277
  const cachedPath = await getCachedPackagePath(pkg, tmpDir);
1079
- return fs6.pathExists(cachedPath);
1278
+ return fs8.pathExists(cachedPath);
1080
1279
  }
1081
1280
  function validateNoDuplicatePackages(packages) {
1082
1281
  const packageMap = /* @__PURE__ */ new Map();
@@ -1122,29 +1321,57 @@ var init_package_manager = __esm({
1122
1321
  });
1123
1322
 
1124
1323
  // src/core/build-cache.ts
1125
- import fs7 from "fs-extra";
1126
- import path8 from "path";
1324
+ import fs9 from "fs-extra";
1325
+ import path10 from "path";
1326
+ import { getHashServer as getHashServer2 } from "@walkeros/server-core";
1127
1327
  async function getBuildCachePath(configContent, tmpDir) {
1128
1328
  const cacheDir = getTmpPath(tmpDir, "cache", "builds");
1129
1329
  const cacheKey = await getFlowSettingsCacheKey(configContent);
1130
- return path8.join(cacheDir, `${cacheKey}.js`);
1330
+ return path10.join(cacheDir, `${cacheKey}.js`);
1131
1331
  }
1132
1332
  async function isBuildCached(configContent, tmpDir) {
1133
1333
  const cachePath = await getBuildCachePath(configContent, tmpDir);
1134
- return fs7.pathExists(cachePath);
1334
+ return fs9.pathExists(cachePath);
1135
1335
  }
1136
1336
  async function cacheBuild(configContent, buildOutput, tmpDir) {
1137
1337
  const cachePath = await getBuildCachePath(configContent, tmpDir);
1138
- await fs7.ensureDir(path8.dirname(cachePath));
1139
- await fs7.writeFile(cachePath, buildOutput, "utf-8");
1338
+ await fs9.ensureDir(path10.dirname(cachePath));
1339
+ await fs9.writeFile(cachePath, buildOutput, "utf-8");
1140
1340
  }
1141
1341
  async function getCachedBuild(configContent, tmpDir) {
1142
1342
  const cachePath = await getBuildCachePath(configContent, tmpDir);
1143
- if (await fs7.pathExists(cachePath)) {
1144
- return await fs7.readFile(cachePath, "utf-8");
1343
+ if (await fs9.pathExists(cachePath)) {
1344
+ return await fs9.readFile(cachePath, "utf-8");
1345
+ }
1346
+ return null;
1347
+ }
1348
+ async function getCodeCachePath(codeContent, tmpDir) {
1349
+ const cacheDir = getTmpPath(tmpDir, "cache", "code");
1350
+ const cacheKey = await getHashServer2(codeContent, 12);
1351
+ return path10.join(cacheDir, `${cacheKey}.js`);
1352
+ }
1353
+ async function cacheCode(codeContent, codeOutput, tmpDir) {
1354
+ const cachePath = await getCodeCachePath(codeContent, tmpDir);
1355
+ await fs9.ensureDir(path10.dirname(cachePath));
1356
+ await fs9.writeFile(cachePath, codeOutput, "utf-8");
1357
+ }
1358
+ async function getCachedCode(codeContent, tmpDir) {
1359
+ const cachePath = await getCodeCachePath(codeContent, tmpDir);
1360
+ if (await fs9.pathExists(cachePath)) {
1361
+ return fs9.readFile(cachePath, "utf-8");
1145
1362
  }
1146
1363
  return null;
1147
1364
  }
1365
+ async function ensureCodeOnDisk(codeContent, compiledCode, tmpDir) {
1366
+ const cacheDir = getTmpPath(tmpDir, "cache", "code");
1367
+ const cacheKey = await getHashServer2(codeContent, 12);
1368
+ const cachePath = path10.join(cacheDir, `${cacheKey}.mjs`);
1369
+ if (!await fs9.pathExists(cachePath)) {
1370
+ await fs9.ensureDir(path10.dirname(cachePath));
1371
+ await fs9.writeFile(cachePath, compiledCode, "utf-8");
1372
+ }
1373
+ return cachePath;
1374
+ }
1148
1375
  var init_build_cache = __esm({
1149
1376
  "src/core/build-cache.ts"() {
1150
1377
  "use strict";
@@ -1154,19 +1381,23 @@ var init_build_cache = __esm({
1154
1381
  });
1155
1382
 
1156
1383
  // src/commands/bundle/bundler.ts
1157
- import crypto2 from "crypto";
1384
+ import crypto from "crypto";
1158
1385
  import esbuild from "esbuild";
1159
1386
  import { builtinModules } from "module";
1160
- import path9 from "path";
1161
- import fs8 from "fs-extra";
1387
+ import path11 from "path";
1388
+ import fs10 from "fs-extra";
1162
1389
  import { packageNameToVariable, ENV_MARKER_PREFIX } from "@walkeros/core";
1163
1390
  function isInlineCode(code) {
1164
1391
  return code !== null && typeof code === "object" && !Array.isArray(code) && "push" in code;
1165
1392
  }
1393
+ function hasCodeReference(code) {
1394
+ return isInlineCode(code) || typeof code === "string";
1395
+ }
1166
1396
  function validateReference(type, name, ref) {
1167
1397
  const hasPackage = !!ref.package;
1168
- const hasCode = isInlineCode(ref.code);
1169
- if (hasPackage && hasCode) {
1398
+ const hasInlineCode = isInlineCode(ref.code);
1399
+ const hasCode = hasCodeReference(ref.code);
1400
+ if (hasPackage && hasInlineCode) {
1170
1401
  throw new Error(
1171
1402
  `${type} "${name}": Cannot specify both package and code. Use one or the other.`
1172
1403
  );
@@ -1207,18 +1438,18 @@ function generateInlineCode(inline, config, env, chain, chainPropertyName, isDes
1207
1438
  }
1208
1439
  async function copyIncludes(includes, sourceDir, outputDir, logger) {
1209
1440
  for (const include of includes) {
1210
- const sourcePath = path9.resolve(sourceDir, include);
1211
- const folderName = path9.basename(include);
1212
- const destPath = path9.join(outputDir, folderName);
1213
- const resolvedOutput = path9.resolve(outputDir);
1214
- const resolvedSource = path9.resolve(sourcePath);
1215
- if (resolvedSource === resolvedOutput || resolvedOutput.startsWith(resolvedSource + path9.sep) || resolvedSource.startsWith(resolvedOutput + path9.sep)) {
1441
+ const sourcePath = path11.resolve(sourceDir, include);
1442
+ const folderName = path11.basename(include);
1443
+ const destPath = path11.join(outputDir, folderName);
1444
+ const resolvedOutput = path11.resolve(outputDir);
1445
+ const resolvedSource = path11.resolve(sourcePath);
1446
+ if (resolvedSource === resolvedOutput || resolvedOutput.startsWith(resolvedSource + path11.sep) || resolvedSource.startsWith(resolvedOutput + path11.sep)) {
1216
1447
  throw new Error(
1217
1448
  `Circular include detected: "${include}" resolves to "${resolvedSource}" which overlaps with output directory "${resolvedOutput}"`
1218
1449
  );
1219
1450
  }
1220
- if (await fs8.pathExists(sourcePath)) {
1221
- await fs8.copy(sourcePath, destPath);
1451
+ if (await fs10.pathExists(sourcePath)) {
1452
+ await fs10.copy(sourcePath, destPath);
1222
1453
  logger.debug(`Copied ${include} to output`);
1223
1454
  } else {
1224
1455
  logger.warn(`Include folder not found: ${include}`);
@@ -1279,7 +1510,7 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1279
1510
  if (hasDeprecatedFeatures) {
1280
1511
  logger.warn("Skipping deprecated code: true entries from bundle.");
1281
1512
  }
1282
- const buildId = crypto2.randomUUID();
1513
+ const buildId = crypto.randomUUID();
1283
1514
  const TEMP_DIR = buildOptions.tempDir || getTmpPath(void 0, `walkeros-build-${buildId}`);
1284
1515
  const CACHE_DIR = buildOptions.tempDir || getTmpPath();
1285
1516
  if (buildOptions.cache !== false) {
@@ -1289,14 +1520,14 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1289
1520
  const cachedBuild = await getCachedBuild(configContent, CACHE_DIR);
1290
1521
  if (cachedBuild) {
1291
1522
  logger.debug("Using cached build");
1292
- const outputPath = path9.resolve(buildOptions.output);
1293
- await fs8.ensureDir(path9.dirname(outputPath));
1294
- await fs8.writeFile(outputPath, cachedBuild);
1295
- const stats = await fs8.stat(outputPath);
1523
+ const outputPath = path11.resolve(buildOptions.output);
1524
+ await fs10.ensureDir(path11.dirname(outputPath));
1525
+ await fs10.writeFile(outputPath, cachedBuild);
1526
+ const stats = await fs10.stat(outputPath);
1296
1527
  const sizeKB = (stats.size / 1024).toFixed(1);
1297
1528
  logger.info(`Output: ${outputPath} (${sizeKB} KB, cached)`);
1298
1529
  if (showStats) {
1299
- const stats2 = await fs8.stat(outputPath);
1530
+ const stats2 = await fs10.stat(outputPath);
1300
1531
  const packageStats = Object.entries(buildOptions.packages).map(
1301
1532
  ([name, pkg]) => ({
1302
1533
  name: `${name}@${pkg.version || "latest"}`,
@@ -1319,7 +1550,7 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1319
1550
  }
1320
1551
  }
1321
1552
  try {
1322
- await fs8.ensureDir(TEMP_DIR);
1553
+ await fs10.ensureDir(TEMP_DIR);
1323
1554
  const hasSourcesOrDests = Object.keys(
1324
1555
  flowSettings.sources || {}
1325
1556
  ).length > 0 || Object.keys(
@@ -1328,9 +1559,28 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1328
1559
  if (hasSourcesOrDests && !buildOptions.packages["@walkeros/collector"]) {
1329
1560
  buildOptions.packages["@walkeros/collector"] = {};
1330
1561
  }
1331
- const stepPackages = collectStepPackages(flowSettings);
1562
+ const stepPackages = collectAllStepPackages(flowSettings);
1332
1563
  for (const pkg of stepPackages) {
1333
- if (!buildOptions.packages[pkg]) {
1564
+ const isLocalPath = pkg.startsWith(".") || pkg.startsWith("/");
1565
+ if (isLocalPath) {
1566
+ const varName = packageNameToVariable(pkg);
1567
+ if (!buildOptions.packages[varName]) {
1568
+ buildOptions.packages[varName] = {
1569
+ path: pkg,
1570
+ imports: [`default as ${varName}`]
1571
+ };
1572
+ }
1573
+ for (const section of ["sources", "destinations", "transformers", "stores"]) {
1574
+ const steps = flowSettings[section];
1575
+ if (!steps) continue;
1576
+ for (const step of Object.values(steps)) {
1577
+ if (step.package === pkg) {
1578
+ step.code = varName;
1579
+ delete step.package;
1580
+ }
1581
+ }
1582
+ }
1583
+ } else if (!buildOptions.packages[pkg]) {
1334
1584
  buildOptions.packages[pkg] = {};
1335
1585
  }
1336
1586
  }
@@ -1354,8 +1604,8 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1354
1604
  );
1355
1605
  for (const [pkgName, pkgPath] of packagePaths.entries()) {
1356
1606
  if (pkgName.startsWith("@walkeros/")) {
1357
- const pkgJsonPath = path9.join(pkgPath, "package.json");
1358
- const pkgJson = await fs8.readJSON(pkgJsonPath);
1607
+ const pkgJsonPath = path11.join(pkgPath, "package.json");
1608
+ const pkgJson = await fs10.readJSON(pkgJsonPath);
1359
1609
  if (!pkgJson.exports && pkgJson.module) {
1360
1610
  pkgJson.exports = {
1361
1611
  ".": {
@@ -1363,67 +1613,135 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1363
1613
  require: pkgJson.main
1364
1614
  }
1365
1615
  };
1366
- await fs8.writeJSON(pkgJsonPath, pkgJson, { spaces: 2 });
1616
+ await fs10.writeJSON(pkgJsonPath, pkgJson, { spaces: 2 });
1367
1617
  }
1368
1618
  }
1369
1619
  }
1370
- const packageJsonPath = path9.join(TEMP_DIR, "package.json");
1371
- await fs8.writeFile(
1620
+ const packageJsonPath = path11.join(TEMP_DIR, "package.json");
1621
+ await fs10.writeFile(
1372
1622
  packageJsonPath,
1373
1623
  JSON.stringify({ type: "module" }, null, 2)
1374
1624
  );
1375
1625
  logger.debug("Creating entry point");
1376
- const entryContent = await createEntryPoint(
1626
+ const { codeEntry, dataPayload, hasFlow } = await createEntryPoint(
1377
1627
  flowSettings,
1378
1628
  buildOptions,
1379
1629
  packagePaths
1380
1630
  );
1381
- const entryPath = path9.join(TEMP_DIR, "entry.js");
1382
- await fs8.writeFile(entryPath, entryContent);
1383
- logger.debug(
1384
- `Running esbuild (target: ${buildOptions.target || "es2018"}, format: ${buildOptions.format})`
1385
- );
1386
- const outputPath = path9.resolve(buildOptions.output);
1387
- await fs8.ensureDir(path9.dirname(outputPath));
1388
- const esbuildOptions = createEsbuildOptions(
1389
- buildOptions,
1390
- entryPath,
1391
- outputPath,
1392
- TEMP_DIR,
1393
- packagePaths,
1394
- logger
1395
- );
1396
- try {
1397
- await esbuild.build(esbuildOptions);
1398
- } catch (buildError) {
1399
- throw createBuildError(
1400
- buildError,
1401
- buildOptions.code || ""
1402
- );
1403
- } finally {
1404
- await esbuild.stop();
1405
- }
1406
- const outputStats = await fs8.stat(outputPath);
1407
- const sizeKB = (outputStats.size / 1024).toFixed(1);
1408
- const buildTime = ((Date.now() - bundleStartTime) / 1e3).toFixed(1);
1409
- logger.info(`Output: ${outputPath} (${sizeKB} KB, ${buildTime}s)`);
1631
+ const outputPath = path11.resolve(buildOptions.output);
1632
+ await fs10.ensureDir(path11.dirname(outputPath));
1633
+ let compiledCode = null;
1410
1634
  if (buildOptions.cache !== false) {
1411
- const configContent = generateCacheKeyContent(flowSettings, buildOptions);
1412
- const buildOutput = await fs8.readFile(outputPath, "utf-8");
1413
- await cacheBuild(configContent, buildOutput, CACHE_DIR);
1414
- logger.debug("Build cached for future use");
1635
+ compiledCode = await getCachedCode(codeEntry, CACHE_DIR);
1415
1636
  }
1416
- let stats;
1637
+ if (compiledCode) {
1638
+ logger.debug("Using cached compiled code (config-only change)");
1639
+ } else {
1640
+ logger.debug(
1641
+ `Running esbuild (target: ${buildOptions.target || "es2018"}, format: ${buildOptions.format})`
1642
+ );
1643
+ const entryPath = path11.join(TEMP_DIR, "entry.js");
1644
+ await fs10.writeFile(entryPath, codeEntry);
1645
+ const esbuildOptions = createEsbuildOptions(
1646
+ { ...buildOptions, minify: false },
1647
+ entryPath,
1648
+ outputPath,
1649
+ TEMP_DIR,
1650
+ packagePaths,
1651
+ logger
1652
+ );
1653
+ try {
1654
+ await esbuild.build(esbuildOptions);
1655
+ } catch (buildError) {
1656
+ throw createBuildError(
1657
+ buildError,
1658
+ buildOptions.code || ""
1659
+ );
1660
+ } finally {
1661
+ await esbuild.stop();
1662
+ }
1663
+ compiledCode = await fs10.readFile(outputPath, "utf-8");
1664
+ if (buildOptions.cache !== false) {
1665
+ await cacheCode(codeEntry, compiledCode, CACHE_DIR);
1666
+ }
1667
+ }
1668
+ const stage1Path = await ensureCodeOnDisk(
1669
+ codeEntry,
1670
+ compiledCode,
1671
+ CACHE_DIR
1672
+ );
1673
+ if (buildOptions.skipWrapper || !hasFlow) {
1674
+ const dataDeclaration = `const __configData = ${dataPayload};
1675
+ export { __configData };`;
1676
+ const banner = buildOptions.platform === "node" ? `import { createRequire } from 'module';const require = createRequire(import.meta.url);
1677
+ ` : "";
1678
+ const esmOutput = `${banner}${compiledCode}
1679
+ ${dataDeclaration}`;
1680
+ await fs10.writeFile(outputPath, esmOutput);
1681
+ } else {
1682
+ const stage2Entry = (buildOptions.platform || "node") === "browser" ? generateWebEntry(stage1Path, dataPayload, {
1683
+ windowCollector: buildOptions.windowCollector,
1684
+ windowElb: buildOptions.windowElb
1685
+ }) : generateServerEntry(stage1Path, dataPayload);
1686
+ const stage2EntryPath = path11.join(TEMP_DIR, "stage2.mjs");
1687
+ await fs10.writeFile(stage2EntryPath, stage2Entry);
1688
+ const stage2Options = {
1689
+ entryPoints: [stage2EntryPath],
1690
+ bundle: true,
1691
+ format: "esm",
1692
+ platform: buildOptions.platform,
1693
+ outfile: outputPath,
1694
+ treeShaking: true,
1695
+ logLevel: "error",
1696
+ minify: buildOptions.minify,
1697
+ ...buildOptions.minify && {
1698
+ minifyWhitespace: buildOptions.minifyOptions?.whitespace ?? true,
1699
+ minifyIdentifiers: buildOptions.minifyOptions?.identifiers ?? true,
1700
+ minifySyntax: buildOptions.minifyOptions?.syntax ?? true,
1701
+ legalComments: buildOptions.minifyOptions?.legalComments ?? "none",
1702
+ charset: "utf8"
1703
+ }
1704
+ };
1705
+ if (buildOptions.platform === "browser") {
1706
+ stage2Options.define = {
1707
+ "process.env.NODE_ENV": '"production"',
1708
+ global: "globalThis"
1709
+ };
1710
+ stage2Options.target = buildOptions.target || "es2018";
1711
+ } else {
1712
+ stage2Options.external = getNodeExternals();
1713
+ stage2Options.banner = {
1714
+ js: `import { createRequire } from 'module';const require = createRequire(import.meta.url);`
1715
+ };
1716
+ stage2Options.target = buildOptions.target || "node18";
1717
+ }
1718
+ try {
1719
+ await esbuild.build(stage2Options);
1720
+ } finally {
1721
+ await esbuild.stop();
1722
+ }
1723
+ }
1724
+ const outputStats = await fs10.stat(outputPath);
1725
+ const sizeKB = (outputStats.size / 1024).toFixed(1);
1726
+ const buildTime = ((Date.now() - bundleStartTime) / 1e3).toFixed(1);
1727
+ logger.info(`Output: ${outputPath} (${sizeKB} KB, ${buildTime}s)`);
1728
+ if (buildOptions.cache !== false) {
1729
+ const configContent = generateCacheKeyContent(flowSettings, buildOptions);
1730
+ const buildOutput = await fs10.readFile(outputPath, "utf-8");
1731
+ await cacheBuild(configContent, buildOutput, CACHE_DIR);
1732
+ logger.debug("Build cached for future use");
1733
+ }
1734
+ let stats;
1417
1735
  if (showStats) {
1418
1736
  stats = await collectBundleStats(
1419
1737
  outputPath,
1420
1738
  buildOptions.packages,
1421
1739
  bundleStartTime,
1422
- entryContent
1740
+ codeEntry
1423
1741
  );
1424
1742
  }
1425
1743
  if (buildOptions.include && buildOptions.include.length > 0) {
1426
- const outputDir = path9.dirname(outputPath);
1744
+ const outputDir = path11.dirname(outputPath);
1427
1745
  await copyIncludes(
1428
1746
  buildOptions.include,
1429
1747
  buildOptions.configDir || process.cwd(),
@@ -1436,13 +1754,13 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1436
1754
  throw error;
1437
1755
  } finally {
1438
1756
  if (!buildOptions.tempDir) {
1439
- fs8.remove(TEMP_DIR).catch(() => {
1757
+ fs10.remove(TEMP_DIR).catch(() => {
1440
1758
  });
1441
1759
  }
1442
1760
  }
1443
1761
  }
1444
1762
  async function collectBundleStats(outputPath, packages, startTime, entryContent) {
1445
- const stats = await fs8.stat(outputPath);
1763
+ const stats = await fs10.stat(outputPath);
1446
1764
  const totalSize = stats.size;
1447
1765
  const buildTime = Date.now() - startTime;
1448
1766
  const packageStats = Object.entries(packages).map(([name, pkg]) => {
@@ -1473,7 +1791,8 @@ function createEsbuildOptions(buildOptions, entryPath, outputPath, tempDir, pack
1473
1791
  const baseOptions = {
1474
1792
  entryPoints: [entryPath],
1475
1793
  bundle: true,
1476
- format: buildOptions.format,
1794
+ format: "esm",
1795
+ // Always ESM — platform wrapper handles final format
1477
1796
  platform: buildOptions.platform,
1478
1797
  outfile: outputPath,
1479
1798
  absWorkingDir: tempDir,
@@ -1506,11 +1825,6 @@ function createEsbuildOptions(buildOptions, entryPath, outputPath, tempDir, pack
1506
1825
  } else if (buildOptions.platform === "node") {
1507
1826
  const nodeExternals = getNodeExternals();
1508
1827
  baseOptions.external = buildOptions.external ? [...nodeExternals, ...buildOptions.external] : nodeExternals;
1509
- if (buildOptions.format === "esm") {
1510
- baseOptions.banner = {
1511
- js: `import { createRequire } from 'module';const require = createRequire(import.meta.url);`
1512
- };
1513
- }
1514
1828
  }
1515
1829
  if (buildOptions.target) {
1516
1830
  baseOptions.target = buildOptions.target;
@@ -1521,64 +1835,19 @@ function createEsbuildOptions(buildOptions, entryPath, outputPath, tempDir, pack
1521
1835
  }
1522
1836
  return baseOptions;
1523
1837
  }
1524
- function detectDestinationPackages(flowSettings) {
1525
- const destinationPackages = /* @__PURE__ */ new Set();
1526
- const destinations = flowSettings.destinations;
1527
- if (destinations) {
1528
- for (const [destKey, destConfig] of Object.entries(destinations)) {
1529
- if (typeof destConfig === "object" && destConfig !== null && "code" in destConfig && destConfig.code === true) {
1530
- continue;
1531
- }
1532
- if (typeof destConfig === "object" && destConfig !== null && "package" in destConfig && typeof destConfig.package === "string") {
1533
- destinationPackages.add(destConfig.package);
1534
- }
1535
- }
1536
- }
1537
- return destinationPackages;
1538
- }
1539
- function detectSourcePackages(flowSettings) {
1540
- const sourcePackages = /* @__PURE__ */ new Set();
1541
- const sources = flowSettings.sources;
1542
- if (sources) {
1543
- for (const [sourceKey, sourceConfig] of Object.entries(sources)) {
1544
- if (typeof sourceConfig === "object" && sourceConfig !== null && "code" in sourceConfig && sourceConfig.code === true) {
1545
- continue;
1546
- }
1547
- if (typeof sourceConfig === "object" && sourceConfig !== null && "package" in sourceConfig && typeof sourceConfig.package === "string") {
1548
- sourcePackages.add(sourceConfig.package);
1549
- }
1550
- }
1551
- }
1552
- return sourcePackages;
1553
- }
1554
- function detectTransformerPackages(flowSettings) {
1555
- const transformerPackages = /* @__PURE__ */ new Set();
1556
- const transformers = flowSettings.transformers;
1557
- if (transformers) {
1558
- for (const [transformerKey, transformerConfig] of Object.entries(
1559
- transformers
1560
- )) {
1561
- if (typeof transformerConfig === "object" && transformerConfig !== null && "code" in transformerConfig && transformerConfig.code === true) {
1562
- continue;
1563
- }
1564
- if (typeof transformerConfig === "object" && transformerConfig !== null && "package" in transformerConfig && typeof transformerConfig.package === "string") {
1565
- transformerPackages.add(transformerConfig.package);
1566
- }
1567
- }
1568
- }
1569
- return transformerPackages;
1570
- }
1571
- function detectStorePackages(flowSettings) {
1572
- const storePackages = /* @__PURE__ */ new Set();
1573
- const stores = flowSettings.stores;
1574
- if (stores) {
1575
- for (const [, storeConfig] of Object.entries(stores)) {
1576
- if (typeof storeConfig === "object" && storeConfig !== null && "package" in storeConfig && typeof storeConfig.package === "string") {
1577
- storePackages.add(storeConfig.package);
1838
+ function detectStepPackages(flowSettings, section) {
1839
+ const packages = /* @__PURE__ */ new Set();
1840
+ const steps = flowSettings[section];
1841
+ if (steps) {
1842
+ for (const [, stepConfig] of Object.entries(steps)) {
1843
+ if (typeof stepConfig !== "object" || stepConfig === null) continue;
1844
+ if ("code" in stepConfig && stepConfig.code === true) continue;
1845
+ if ("package" in stepConfig && typeof stepConfig.package === "string") {
1846
+ packages.add(stepConfig.package);
1578
1847
  }
1579
1848
  }
1580
1849
  }
1581
- return storePackages;
1850
+ return packages;
1582
1851
  }
1583
1852
  function getNodeExternals() {
1584
1853
  const externals = [];
@@ -1588,27 +1857,20 @@ function getNodeExternals() {
1588
1857
  }
1589
1858
  return externals;
1590
1859
  }
1591
- function collectStepPackages(flowSettings) {
1860
+ function collectAllStepPackages(flowSettings) {
1592
1861
  const allPackages = /* @__PURE__ */ new Set();
1593
- for (const pkg of detectSourcePackages(flowSettings)) {
1594
- allPackages.add(pkg);
1595
- }
1596
- for (const pkg of detectDestinationPackages(flowSettings)) {
1597
- allPackages.add(pkg);
1598
- }
1599
- for (const pkg of detectTransformerPackages(flowSettings)) {
1600
- allPackages.add(pkg);
1601
- }
1602
- for (const pkg of detectStorePackages(flowSettings)) {
1603
- allPackages.add(pkg);
1604
- }
1605
- const npmPackages = /* @__PURE__ */ new Set();
1606
- for (const pkg of allPackages) {
1607
- if (!pkg.startsWith(".") && !pkg.startsWith("/")) {
1608
- npmPackages.add(pkg);
1862
+ const sections = [
1863
+ "sources",
1864
+ "destinations",
1865
+ "transformers",
1866
+ "stores"
1867
+ ];
1868
+ for (const section of sections) {
1869
+ for (const pkg of detectStepPackages(flowSettings, section)) {
1870
+ allPackages.add(pkg);
1609
1871
  }
1610
1872
  }
1611
- return npmPackages;
1873
+ return allPackages;
1612
1874
  }
1613
1875
  function detectExplicitCodeImports(flowSettings) {
1614
1876
  const explicitCodeImports = /* @__PURE__ */ new Map();
@@ -1755,12 +2017,12 @@ function validateComponentNames(components, section) {
1755
2017
  }
1756
2018
  function validateStoreReferences(flowSettings, storeIds) {
1757
2019
  const refs = [];
1758
- function collectRefs(obj, path15) {
2020
+ function collectRefs(obj, path19) {
1759
2021
  if (typeof obj === "string" && obj.startsWith("$store:")) {
1760
- refs.push({ ref: obj.slice(7), location: path15 });
2022
+ refs.push({ ref: obj.slice(7), location: path19 });
1761
2023
  } else if (obj && typeof obj === "object") {
1762
2024
  for (const [key, val] of Object.entries(obj)) {
1763
- collectRefs(val, `${path15}.${key}`);
2025
+ collectRefs(val, `${path19}.${key}`);
1764
2026
  }
1765
2027
  }
1766
2028
  }
@@ -1785,10 +2047,10 @@ function validateStoreReferences(flowSettings, storeIds) {
1785
2047
  }
1786
2048
  }
1787
2049
  async function createEntryPoint(flowSettings, buildOptions, packagePaths) {
1788
- const destinationPackages = detectDestinationPackages(flowSettings);
1789
- const sourcePackages = detectSourcePackages(flowSettings);
1790
- const transformerPackages = detectTransformerPackages(flowSettings);
1791
- const storePackages = detectStorePackages(flowSettings);
2050
+ const sourcePackages = detectStepPackages(flowSettings, "sources");
2051
+ const destinationPackages = detectStepPackages(flowSettings, "destinations");
2052
+ const transformerPackages = detectStepPackages(flowSettings, "transformers");
2053
+ const storePackages = detectStepPackages(flowSettings, "stores");
1792
2054
  const explicitCodeImports = detectExplicitCodeImports(flowSettings);
1793
2055
  const storeIds = new Set(
1794
2056
  Object.keys(
@@ -1815,29 +2077,30 @@ async function createEntryPoint(flowSettings, buildOptions, packagePaths) {
1815
2077
  );
1816
2078
  const importsCode = importStatements.join("\n");
1817
2079
  const hasFlow = Object.values(flowSettings.sources || {}).some(
1818
- (s2) => s2.package || isInlineCode(s2.code)
2080
+ (s) => s.package || hasCodeReference(s.code)
1819
2081
  ) || Object.values(flowSettings.destinations || {}).some(
1820
- (d2) => d2.package || isInlineCode(d2.code)
2082
+ (d2) => d2.package || hasCodeReference(d2.code)
1821
2083
  );
1822
2084
  if (!hasFlow) {
1823
2085
  const userCode = buildOptions.code || "";
1824
- return importsCode ? `${importsCode}
2086
+ return {
2087
+ codeEntry: importsCode ? `${importsCode}
1825
2088
 
1826
- ${userCode}` : userCode;
2089
+ ${userCode}` : userCode,
2090
+ dataPayload: "{}",
2091
+ hasFlow: false
2092
+ };
1827
2093
  }
1828
- const { storesDeclaration, configObject } = buildConfigObject(
1829
- flowSettings,
1830
- explicitCodeImports
1831
- );
1832
- const wrappedCode = generatePlatformWrapper(
2094
+ const { storesDeclaration, codeConfigObject, dataPayload } = buildSplitConfigObject(flowSettings, explicitCodeImports);
2095
+ const wireConfigModule = generateSplitWireConfigModule(
1833
2096
  storesDeclaration,
1834
- configObject,
1835
- buildOptions.code || "",
1836
- buildOptions
2097
+ codeConfigObject,
2098
+ buildOptions.code || ""
1837
2099
  );
1838
- return importsCode ? `${importsCode}
2100
+ const codeEntry = importsCode ? `${importsCode}
1839
2101
 
1840
- ${wrappedCode}` : wrappedCode;
2102
+ ${wireConfigModule}` : wireConfigModule;
2103
+ return { codeEntry, dataPayload, hasFlow: true };
1841
2104
  }
1842
2105
  function createBuildError(buildError, code) {
1843
2106
  if (!buildError.errors || buildError.errors.length === 0) {
@@ -1862,12 +2125,55 @@ ${firstError.text}`
1862
2125
  ` + (location ? ` at ${location.file}:${location.line}:${location.column}` : "")
1863
2126
  );
1864
2127
  }
1865
- function buildConfigObject(flowSettings, explicitCodeImports) {
2128
+ function buildSplitConfigObject(flowSettings, explicitCodeImports) {
1866
2129
  const flowWithProps = flowSettings;
1867
2130
  const sources = flowWithProps.sources || {};
1868
2131
  const destinations = flowWithProps.destinations || {};
1869
2132
  const transformers = flowWithProps.transformers || {};
1870
2133
  const stores = flowWithProps.stores || {};
2134
+ const dataPayloadObj = {};
2135
+ function resolveCodeVar(step) {
2136
+ if (step.code && typeof step.code === "string" && !step.package) {
2137
+ return step.code;
2138
+ }
2139
+ if (step.code && typeof step.code === "string" && step.package && explicitCodeImports.has(step.package)) {
2140
+ return step.code;
2141
+ }
2142
+ return packageNameToVariable(step.package);
2143
+ }
2144
+ function getStepProps(step) {
2145
+ const props = {};
2146
+ for (const [key, value] of Object.entries(step)) {
2147
+ if (key === "code" || key === "package") continue;
2148
+ if (value !== void 0 && value !== null) {
2149
+ props[key] = value;
2150
+ }
2151
+ }
2152
+ return props;
2153
+ }
2154
+ function buildSplitStepEntry(section, stepId, step) {
2155
+ const codeVar = resolveCodeVar(
2156
+ step
2157
+ );
2158
+ const stepProps = getStepProps(step);
2159
+ const { codeProps, dataProps } = classifyStepProperties(stepProps);
2160
+ const codeEntries = [];
2161
+ codeEntries.push(`code: ${codeVar}`);
2162
+ for (const [key, value] of Object.entries(codeProps)) {
2163
+ if (key === "code") continue;
2164
+ codeEntries.push(`${key}: ${processConfigValue(value)}`);
2165
+ }
2166
+ for (const key of Object.keys(dataProps)) {
2167
+ codeEntries.push(`${key}: __data.${section}.${stepId}.${key}`);
2168
+ }
2169
+ if (Object.keys(dataProps).length > 0) {
2170
+ if (!dataPayloadObj[section]) dataPayloadObj[section] = {};
2171
+ dataPayloadObj[section][stepId] = dataProps;
2172
+ }
2173
+ return ` ${stepId}: {
2174
+ ${codeEntries.join(",\n ")}
2175
+ }`;
2176
+ }
1871
2177
  Object.entries(sources).forEach(([name, source]) => {
1872
2178
  if (source.code !== true) {
1873
2179
  validateReference("Source", name, source);
@@ -1884,104 +2190,89 @@ function buildConfigObject(flowSettings, explicitCodeImports) {
1884
2190
  }
1885
2191
  });
1886
2192
  const sourcesEntries = Object.entries(sources).filter(
1887
- ([, source]) => source.code !== true && (source.package || isInlineCode(source.code))
2193
+ ([, source]) => source.code !== true && (source.package || hasCodeReference(source.code))
1888
2194
  ).map(([key, source]) => {
1889
2195
  if (isInlineCode(source.code)) {
1890
2196
  return ` ${key}: ${generateInlineCode(source.code, source.config || {}, source.env, source.next, "next")}`;
1891
2197
  }
1892
- let codeVar;
1893
- if (source.code && typeof source.code === "string" && explicitCodeImports.has(source.package)) {
1894
- codeVar = source.code;
1895
- } else {
1896
- codeVar = packageNameToVariable(source.package);
1897
- }
1898
- const configStr = source.config ? processConfigValue(source.config) : "{}";
1899
- const envStr = source.env ? `,
1900
- env: ${processConfigValue(source.env)}` : "";
1901
- const nextStr = source.next ? `,
1902
- next: ${JSON.stringify(source.next)}` : "";
1903
- return ` ${key}: {
1904
- code: ${codeVar},
1905
- config: ${configStr}${envStr}${nextStr}
1906
- }`;
2198
+ return buildSplitStepEntry(
2199
+ "sources",
2200
+ key,
2201
+ source
2202
+ );
1907
2203
  });
1908
2204
  const destinationsEntries = Object.entries(destinations).filter(
1909
- ([, dest]) => dest.code !== true && (dest.package || isInlineCode(dest.code))
2205
+ ([, dest]) => dest.code !== true && (dest.package || hasCodeReference(dest.code))
1910
2206
  ).map(([key, dest]) => {
1911
2207
  if (isInlineCode(dest.code)) {
1912
2208
  return ` ${key}: ${generateInlineCode(dest.code, dest.config || {}, dest.env, dest.before, "before", true)}`;
1913
2209
  }
1914
- let codeVar;
1915
- if (dest.code && typeof dest.code === "string" && explicitCodeImports.has(dest.package)) {
1916
- codeVar = dest.code;
1917
- } else {
1918
- codeVar = packageNameToVariable(dest.package);
1919
- }
1920
- const configStr = dest.config ? processConfigValue(dest.config) : "{}";
1921
- const envStr = dest.env ? `,
1922
- env: ${processConfigValue(dest.env)}` : "";
1923
- const beforeStr = dest.before ? `,
1924
- before: ${JSON.stringify(dest.before)}` : "";
1925
- return ` ${key}: {
1926
- code: ${codeVar},
1927
- config: ${configStr}${envStr}${beforeStr}
1928
- }`;
2210
+ return buildSplitStepEntry(
2211
+ "destinations",
2212
+ key,
2213
+ dest
2214
+ );
1929
2215
  });
1930
2216
  const transformersEntries = Object.entries(transformers).filter(
1931
- ([, transformer]) => transformer.code !== true && (transformer.package || isInlineCode(transformer.code))
2217
+ ([, transformer]) => transformer.code !== true && (transformer.package || hasCodeReference(transformer.code))
1932
2218
  ).map(([key, transformer]) => {
1933
2219
  if (isInlineCode(transformer.code)) {
1934
2220
  return ` ${key}: ${generateInlineCode(transformer.code, transformer.config || {}, transformer.env, transformer.next, "next")}`;
1935
2221
  }
1936
- let codeVar;
1937
- if (transformer.code && typeof transformer.code === "string" && explicitCodeImports.has(transformer.package)) {
1938
- codeVar = transformer.code;
1939
- } else {
1940
- codeVar = packageNameToVariable(transformer.package);
1941
- }
1942
- const configStr = transformer.config ? processConfigValue(transformer.config) : "{}";
1943
- const envStr = transformer.env ? `,
1944
- env: ${processConfigValue(transformer.env)}` : "";
1945
- const nextStr = transformer.next ? `,
1946
- next: ${JSON.stringify(transformer.next)}` : "";
1947
- return ` ${key}: {
1948
- code: ${codeVar},
1949
- config: ${configStr}${envStr}${nextStr}
1950
- }`;
2222
+ return buildSplitStepEntry(
2223
+ "transformers",
2224
+ key,
2225
+ transformer
2226
+ );
1951
2227
  });
1952
2228
  Object.entries(stores).forEach(([name, store]) => {
1953
- if (store.package || isInlineCode(store.code)) {
2229
+ if (store.package || hasCodeReference(store.code)) {
1954
2230
  validateReference("Store", name, store);
1955
2231
  }
1956
2232
  });
1957
- const storesEntries = Object.entries(stores).filter(([, store]) => store.package || isInlineCode(store.code)).map(([key, store]) => {
2233
+ const storesEntries = Object.entries(stores).filter(([, store]) => store.package || hasCodeReference(store.code)).map(([key, store]) => {
1958
2234
  if (isInlineCode(store.code)) {
1959
2235
  return ` ${key}: ${generateInlineCode(store.code, store.config || {}, store.env)}`;
1960
2236
  }
1961
- let codeVar;
1962
- if (store.code && typeof store.code === "string" && explicitCodeImports.has(store.package)) {
1963
- codeVar = store.code;
1964
- } else {
1965
- codeVar = packageNameToVariable(store.package);
2237
+ const codeVar = resolveCodeVar(store);
2238
+ const storeProps = getStepProps(store);
2239
+ const { codeProps, dataProps } = classifyStepProperties(storeProps);
2240
+ const codeEntries = [];
2241
+ codeEntries.push(`code: ${codeVar}`);
2242
+ for (const [propKey, value] of Object.entries(codeProps)) {
2243
+ if (propKey === "code") continue;
2244
+ codeEntries.push(`${propKey}: ${processConfigValue(value)}`);
2245
+ }
2246
+ for (const propKey of Object.keys(dataProps)) {
2247
+ codeEntries.push(`${propKey}: __data.stores.${key}.${propKey}`);
2248
+ }
2249
+ if (Object.keys(dataProps).length > 0) {
2250
+ if (!dataPayloadObj["stores"]) dataPayloadObj["stores"] = {};
2251
+ dataPayloadObj["stores"][key] = dataProps;
1966
2252
  }
1967
- const configStr = store.config ? processConfigValue(store.config) : "{}";
1968
- const envStr = store.env ? `,
1969
- env: ${processConfigValue(store.env)}` : "";
1970
2253
  return ` ${key}: {
1971
- code: ${codeVar},
1972
- config: ${configStr}${envStr}
2254
+ ${codeEntries.join(",\n ")}
1973
2255
  }`;
1974
2256
  });
1975
2257
  const storesDeclaration = storesEntries.length > 0 ? `const stores = {
1976
2258
  ${storesEntries.join(",\n")}
1977
2259
  };` : "const stores = {};";
1978
- const collectorStr = flowWithProps.collector ? `,
1979
- ...${processConfigValue(flowWithProps.collector)}` : "";
2260
+ let collectorStr = "";
2261
+ if (flowWithProps.collector) {
2262
+ if (containsCodeMarkers(flowWithProps.collector)) {
2263
+ collectorStr = `,
2264
+ ...${processConfigValue(flowWithProps.collector)}`;
2265
+ } else {
2266
+ dataPayloadObj["collector"] = flowWithProps.collector;
2267
+ collectorStr = `,
2268
+ ...__data.collector`;
2269
+ }
2270
+ }
1980
2271
  const transformersStr = transformersEntries.length > 0 ? `,
1981
2272
  transformers: {
1982
2273
  ${transformersEntries.join(",\n")}
1983
2274
  }` : "";
1984
- const configObject = `{
2275
+ const codeConfigObject = `{
1985
2276
  sources: {
1986
2277
  ${sourcesEntries.join(",\n")}
1987
2278
  },
@@ -1990,7 +2281,69 @@ ${destinationsEntries.join(",\n")}
1990
2281
  }${transformersStr},
1991
2282
  stores${collectorStr}
1992
2283
  }`;
1993
- return { storesDeclaration, configObject };
2284
+ const dataPayload = JSON.stringify(dataPayloadObj, null, 2);
2285
+ return { storesDeclaration, codeConfigObject, dataPayload };
2286
+ }
2287
+ function generateSplitWireConfigModule(storesDeclaration, codeConfigObject, userCode) {
2288
+ const codeSection = userCode ? `
2289
+ ${userCode}
2290
+ ` : "";
2291
+ return `export function wireConfig(__data) {
2292
+ ${storesDeclaration}
2293
+
2294
+ const config = ${codeConfigObject};${codeSection}
2295
+
2296
+ return config;
2297
+ }
2298
+
2299
+ export { startFlow };`;
2300
+ }
2301
+ function generateServerEntry(stage1Path, dataPayload) {
2302
+ return `import { startFlow, wireConfig } from '${stage1Path}';
2303
+
2304
+ const __configData = ${dataPayload};
2305
+
2306
+ export default async function(context = {}) {
2307
+ const config = wireConfig(__configData);
2308
+
2309
+ if (context.logger) config.logger = context.logger;
2310
+
2311
+ if (context.sourceSettings && config.sources) {
2312
+ for (const src of Object.values(config.sources)) {
2313
+ if (src.config?.settings) {
2314
+ src.config.settings = { ...src.config.settings, ...context.sourceSettings };
2315
+ }
2316
+ }
2317
+ }
2318
+
2319
+ const result = await startFlow(config);
2320
+
2321
+ const httpSource = Object.values(result.collector.sources || {})
2322
+ .find(s => 'httpHandler' in s && typeof s.httpHandler === 'function');
2323
+
2324
+ return { ...result, httpHandler: httpSource ? httpSource.httpHandler : undefined };
2325
+ }`;
2326
+ }
2327
+ function generateWebEntry(stage1Path, dataPayload, options = {}) {
2328
+ const assignments = [];
2329
+ if (options.windowCollector) {
2330
+ assignments.push(
2331
+ ` if (typeof window !== 'undefined') window['${options.windowCollector}'] = collector;`
2332
+ );
2333
+ }
2334
+ if (options.windowElb) {
2335
+ assignments.push(
2336
+ ` if (typeof window !== 'undefined') window['${options.windowElb}'] = elb;`
2337
+ );
2338
+ }
2339
+ const assignmentCode = assignments.length > 0 ? "\n" + assignments.join("\n") : "";
2340
+ return `import { startFlow, wireConfig } from '${stage1Path}';
2341
+
2342
+ const __configData = ${dataPayload};
2343
+
2344
+ (async () => {
2345
+ const { collector, elb } = await startFlow(wireConfig(__configData));${assignmentCode}
2346
+ })();`;
1994
2347
  }
1995
2348
  function processConfigValue(value) {
1996
2349
  return serializeWithCode(value, 0);
@@ -2059,66 +2412,11 @@ ${spaces}}`;
2059
2412
  }
2060
2413
  return JSON.stringify(value);
2061
2414
  }
2062
- function generatePlatformWrapper(storesDeclaration, configObject, userCode, buildOptions) {
2063
- if (buildOptions.platform === "browser") {
2064
- const windowAssignments = [];
2065
- if (buildOptions.windowCollector) {
2066
- windowAssignments.push(
2067
- ` if (typeof window !== 'undefined') window['${buildOptions.windowCollector}'] = collector;`
2068
- );
2069
- }
2070
- if (buildOptions.windowElb) {
2071
- windowAssignments.push(
2072
- ` if (typeof window !== 'undefined') window['${buildOptions.windowElb}'] = elb;`
2073
- );
2074
- }
2075
- const assignments = windowAssignments.length > 0 ? "\n" + windowAssignments.join("\n") : "";
2076
- return `(async () => {
2077
- ${storesDeclaration}
2078
-
2079
- const config = ${configObject};
2080
-
2081
- ${userCode}
2082
-
2083
- const { collector, elb } = await startFlow(config);${assignments}
2084
- })();`;
2085
- } else {
2086
- const codeSection = userCode ? `
2087
- ${userCode}
2088
- ` : "";
2089
- return `export default async function(context = {}) {
2090
- ${storesDeclaration}
2091
-
2092
- const config = ${configObject};${codeSection}
2093
- // Apply context overrides (e.g., logger config from CLI)
2094
- if (context.logger) {
2095
- config.logger = { ...config.logger, ...context.logger };
2096
- }
2097
-
2098
- // When runner provides external server, strip port from sources
2099
- // so they don't self-listen (runner owns the port)
2100
- if (context.externalServer && config.sources) {
2101
- for (const src of Object.values(config.sources)) {
2102
- if (src.config && src.config.settings && 'port' in src.config.settings) {
2103
- delete src.config.settings.port;
2104
- }
2105
- }
2106
- }
2107
-
2108
- const result = await startFlow(config);
2109
-
2110
- // Discover httpHandler from initialized sources (duck-typing, no source-type coupling)
2111
- const httpSource = Object.values(result.collector.sources || {})
2112
- .find(s => 'httpHandler' in s && typeof s.httpHandler === 'function');
2113
-
2114
- return { ...result, httpHandler: httpSource ? httpSource.httpHandler : undefined };
2115
- }`;
2116
- }
2117
- }
2118
2415
  var VALID_JS_IDENTIFIER;
2119
2416
  var init_bundler = __esm({
2120
2417
  "src/commands/bundle/bundler.ts"() {
2121
2418
  "use strict";
2419
+ init_config_classifier();
2122
2420
  init_package_manager();
2123
2421
  init_tmp();
2124
2422
  init_build_cache();
@@ -2127,12 +2425,12 @@ var init_bundler = __esm({
2127
2425
  });
2128
2426
 
2129
2427
  // src/commands/bundle/upload.ts
2130
- import fs9 from "fs-extra";
2428
+ import fs11 from "fs-extra";
2131
2429
  function sanitizeUrl(url) {
2132
2430
  return url.split("?")[0];
2133
2431
  }
2134
2432
  async function uploadBundleToUrl(filePath, url, timeoutMs = 3e4) {
2135
- const bundleContent = await fs9.readFile(filePath);
2433
+ const bundleContent = await fs11.readFile(filePath);
2136
2434
  const doUpload = async (attempt) => {
2137
2435
  const response = await fetch(url, {
2138
2436
  method: "PUT",
@@ -2211,8 +2509,8 @@ var init_api_client = __esm({
2211
2509
  });
2212
2510
 
2213
2511
  // src/commands/bundle/dockerfile.ts
2214
- import path10 from "path";
2215
- import fs10 from "fs-extra";
2512
+ import path12 from "path";
2513
+ import fs12 from "fs-extra";
2216
2514
  function buildDockerfileContent(platform, includedFolders) {
2217
2515
  const bundleFile = platform === "web" ? "walker.js" : "bundle.mjs";
2218
2516
  const lines = [
@@ -2222,21 +2520,21 @@ function buildDockerfileContent(platform, includedFolders) {
2222
2520
  `COPY ${bundleFile} /app/flow/${bundleFile}`
2223
2521
  ];
2224
2522
  for (const folder of includedFolders) {
2225
- const name = path10.basename(folder);
2523
+ const name = path12.basename(folder);
2226
2524
  lines.push(`COPY ${name}/ /app/flow/${name}/`);
2227
2525
  }
2228
2526
  lines.push("", `ENV BUNDLE=/app/flow/${bundleFile}`, "", "EXPOSE 8080", "");
2229
2527
  return lines.join("\n");
2230
2528
  }
2231
2529
  async function generateDockerfile(outputDir, platform, logger, customFile, includedFolders) {
2232
- const destPath = path10.join(outputDir, "Dockerfile");
2233
- if (customFile && await fs10.pathExists(customFile)) {
2234
- await fs10.copy(customFile, destPath);
2530
+ const destPath = path12.join(outputDir, "Dockerfile");
2531
+ if (customFile && await fs12.pathExists(customFile)) {
2532
+ await fs12.copy(customFile, destPath);
2235
2533
  logger.info(`Dockerfile: ${destPath} (copied from ${customFile})`);
2236
2534
  return;
2237
2535
  }
2238
2536
  const dockerfile = buildDockerfileContent(platform, includedFolders || []);
2239
- await fs10.writeFile(destPath, dockerfile);
2537
+ await fs12.writeFile(destPath, dockerfile);
2240
2538
  logger.info(`Dockerfile: ${destPath}`);
2241
2539
  }
2242
2540
  var init_dockerfile = __esm({
@@ -2246,15 +2544,15 @@ var init_dockerfile = __esm({
2246
2544
  });
2247
2545
 
2248
2546
  // src/commands/bundle/index.ts
2249
- import path11 from "path";
2250
- import fs11 from "fs-extra";
2547
+ import path13 from "path";
2548
+ import fs13 from "fs-extra";
2251
2549
  import { getPlatform as getPlatform2 } from "@walkeros/core";
2252
2550
  function resolveOutputPath(output, buildOptions) {
2253
- const resolved = path11.resolve(output);
2254
- const ext = path11.extname(resolved);
2255
- if (output.endsWith("/") || output.endsWith(path11.sep) || !ext) {
2551
+ const resolved = path13.resolve(output);
2552
+ const ext = path13.extname(resolved);
2553
+ if (output.endsWith("/") || output.endsWith(path13.sep) || !ext) {
2256
2554
  const filename = buildOptions.platform === "browser" ? "walker.js" : "bundle.mjs";
2257
- return path11.join(resolved, filename);
2555
+ return path13.join(resolved, filename);
2258
2556
  }
2259
2557
  return resolved;
2260
2558
  }
@@ -2284,7 +2582,7 @@ async function bundleCommand(options) {
2284
2582
  } catch {
2285
2583
  throw new Error("Invalid JSON received on stdin");
2286
2584
  }
2287
- configPath = path11.resolve(process.cwd(), "stdin.config.json");
2585
+ configPath = path13.resolve(process.cwd(), "stdin.config.json");
2288
2586
  } else {
2289
2587
  const file = options.config || "bundle.config.json";
2290
2588
  configPath = resolveAsset(file, "config");
@@ -2338,19 +2636,19 @@ async function bundleCommand(options) {
2338
2636
  if (uploadUrl) {
2339
2637
  await uploadBundleToUrl(buildOptions.output, uploadUrl);
2340
2638
  logger.info(`Uploaded to: ${sanitizeUrl(uploadUrl)}`);
2341
- await fs11.remove(buildOptions.output);
2639
+ await fs13.remove(buildOptions.output);
2342
2640
  }
2343
2641
  if (!options.json && !options.all && options.stats && stats) {
2344
2642
  displayStats(stats, logger);
2345
2643
  }
2346
2644
  if (writingToStdout && !options.json) {
2347
- const bundleContent = await fs11.readFile(buildOptions.output);
2645
+ const bundleContent = await fs13.readFile(buildOptions.output);
2348
2646
  await writeResult(bundleContent, {});
2349
2647
  }
2350
2648
  if (options.dockerfile && options.output) {
2351
2649
  const platform = getPlatform2(flowSettings);
2352
2650
  if (platform) {
2353
- const outputDir = path11.dirname(buildOptions.output);
2651
+ const outputDir = path13.dirname(buildOptions.output);
2354
2652
  const customFile = typeof options.dockerfile === "string" ? options.dockerfile : void 0;
2355
2653
  await generateDockerfile(
2356
2654
  outputDir,
@@ -2369,7 +2667,7 @@ async function bundleCommand(options) {
2369
2667
  }
2370
2668
  }
2371
2669
  }
2372
- const duration = timer.end() / 1e3;
2670
+ const duration = timer.end();
2373
2671
  const successCount = results.filter((r2) => r2.success).length;
2374
2672
  const failureCount = results.filter((r2) => !r2.success).length;
2375
2673
  if (options.json) {
@@ -2406,7 +2704,7 @@ Build Summary: ${successCount}/${results.length} succeeded`
2406
2704
  }
2407
2705
  process.exit(0);
2408
2706
  } catch (error) {
2409
- const duration = timer.getElapsed() / 1e3;
2707
+ const duration = timer.getElapsed();
2410
2708
  const errorMessage = getErrorMessage(error);
2411
2709
  if (options.json) {
2412
2710
  const jsonError = createErrorOutput(errorMessage, duration);
@@ -2421,7 +2719,7 @@ Build Summary: ${successCount}/${results.length} succeeded`
2421
2719
  }
2422
2720
  async function bundle(configOrPath, options = {}) {
2423
2721
  let rawConfig;
2424
- let configPath = path11.resolve(process.cwd(), "walkeros.config.json");
2722
+ let configPath = path13.resolve(process.cwd(), "walkeros.config.json");
2425
2723
  if (typeof configOrPath === "string") {
2426
2724
  configPath = resolveAsset(configOrPath, "config");
2427
2725
  rawConfig = await loadJsonConfig(configPath);
@@ -2477,6 +2775,45 @@ var init_bundle = __esm({
2477
2775
  }
2478
2776
  });
2479
2777
 
2778
+ // src/commands/push/env-loader.ts
2779
+ var env_loader_exports = {};
2780
+ __export(env_loader_exports, {
2781
+ loadDestinationEnvs: () => loadDestinationEnvs
2782
+ });
2783
+ async function loadDestinationEnvs(destinations, packages, configDir) {
2784
+ const envs = {};
2785
+ const resolveDir = configDir || process.cwd();
2786
+ for (const [destKey, destConfig] of Object.entries(destinations)) {
2787
+ const typedConfig = destConfig;
2788
+ if (!typedConfig.package) {
2789
+ continue;
2790
+ }
2791
+ try {
2792
+ const packageName = typedConfig.package;
2793
+ const isDemoPackage = packageName.includes("-demo");
2794
+ const importPath = isDemoPackage ? resolvePackageImportPath(packageName, packages, resolveDir) : resolvePackageImportPath(packageName, packages, resolveDir, "/dev");
2795
+ const module = await import(importPath);
2796
+ const examplesModule = module.examples || module.default?.examples;
2797
+ const envModule = examplesModule?.env;
2798
+ if (envModule?.push) {
2799
+ envs[destKey] = {
2800
+ init: envModule.init,
2801
+ push: envModule.push,
2802
+ simulation: envModule.simulation || []
2803
+ };
2804
+ }
2805
+ } catch {
2806
+ }
2807
+ }
2808
+ return envs;
2809
+ }
2810
+ var init_env_loader = __esm({
2811
+ "src/commands/push/env-loader.ts"() {
2812
+ "use strict";
2813
+ init_package_path();
2814
+ }
2815
+ });
2816
+
2480
2817
  // src/runtime/cache.ts
2481
2818
  var cache_exports = {};
2482
2819
  __export(cache_exports, {
@@ -2531,15 +2868,15 @@ __export(utils_exports, {
2531
2868
  isPreBuiltConfig: () => isPreBuiltConfig,
2532
2869
  prepareBundleForRun: () => prepareBundleForRun
2533
2870
  });
2534
- import path13 from "path";
2535
- import fs14 from "fs-extra";
2871
+ import path17 from "path";
2872
+ import fs16 from "fs-extra";
2536
2873
  async function prepareBundleForRun(configPath, options) {
2537
2874
  const tempDir = getTmpPath(
2538
2875
  void 0,
2539
2876
  `run-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
2540
2877
  );
2541
- await fs14.ensureDir(tempDir);
2542
- const tempPath = path13.join(tempDir, "bundle.mjs");
2878
+ await fs16.ensureDir(tempDir);
2879
+ const tempPath = path17.join(tempDir, "bundle.mjs");
2543
2880
  await bundle(configPath, {
2544
2881
  cache: true,
2545
2882
  verbose: options.verbose,
@@ -2551,7 +2888,12 @@ async function prepareBundleForRun(configPath, options) {
2551
2888
  platform: "node"
2552
2889
  }
2553
2890
  });
2554
- return tempPath;
2891
+ return {
2892
+ bundlePath: tempPath,
2893
+ cleanup: async () => {
2894
+ await fs16.remove(tempDir);
2895
+ }
2896
+ };
2555
2897
  }
2556
2898
  function isPreBuiltConfig(configPath) {
2557
2899
  return configPath.endsWith(".mjs") || configPath.endsWith(".js") || configPath.endsWith(".cjs");
@@ -2567,1074 +2909,703 @@ var init_utils3 = __esm({
2567
2909
  // src/index.ts
2568
2910
  init_bundle();
2569
2911
 
2570
- // src/commands/simulate/simulator.ts
2571
- import fs12 from "fs-extra";
2912
+ // src/commands/push/index.ts
2913
+ import path16 from "path";
2914
+ import fs15 from "fs-extra";
2915
+ import {
2916
+ createIngest,
2917
+ getPlatform as getPlatform4,
2918
+ compileNext,
2919
+ resolveNext,
2920
+ isRouteArray,
2921
+ buildCacheContext
2922
+ } from "@walkeros/core";
2572
2923
 
2573
2924
  // ../collector/dist/index.mjs
2574
2925
  import { assign as o } from "@walkeros/core";
2575
2926
  import { assign as r, createLogger as i } from "@walkeros/core";
2576
- import { assign as a, clone as c, debounce as u, getId as f, getGrantedConsent as l, isDefined as g, isFunction as d, isObject as m, processEventMapping as p, tryCatchAsync as h, useHooks as w } from "@walkeros/core";
2577
- import { isArray as b } from "@walkeros/core";
2578
- import { tryCatch as y, tryCatchAsync as v } from "@walkeros/core";
2579
- import { getMappingValue as k, tryCatchAsync as O } from "@walkeros/core";
2580
- import { isObject as C, tryCatchAsync as j, useHooks as q } from "@walkeros/core";
2581
- import { assign as L, getId as Q, isFunction as V, isString as _ } from "@walkeros/core";
2582
- import { isObject as K } from "@walkeros/core";
2583
- import { getGrantedConsent as en, processEventMapping as tn, tryCatchAsync as on, useHooks as sn } from "@walkeros/core";
2584
- import { useHooks as an, tryCatchAsync as cn } from "@walkeros/core";
2585
- var e = { Action: "action", Actions: "actions", Config: "config", Consent: "consent", Context: "context", Custom: "custom", Destination: "destination", Elb: "elb", Globals: "globals", Hook: "hook", Init: "init", Link: "link", On: "on", Prefix: "data-elb", Ready: "ready", Run: "run", Session: "session", Shutdown: "shutdown", User: "user", Walker: "walker" };
2586
- var t = { Commands: e, Utils: { Storage: { Cookie: "cookie", Local: "local", Session: "session" } } };
2587
- function s(n, e2) {
2588
- let t2 = false;
2589
- const s2 = {};
2590
- return Object.entries(e2).forEach(([n2, e3]) => {
2591
- const o2 = !!e3;
2592
- s2[n2] = o2, t2 = t2 || o2;
2593
- }), n.consent = o(n.consent, s2), { update: s2, runQueue: t2 };
2594
- }
2595
- function D(n) {
2596
- const e2 = {};
2597
- for (const [t2, o2] of Object.entries(n)) o2.config?.next ? e2[t2] = { next: o2.config.next } : e2[t2] = {};
2598
- return e2;
2599
- }
2600
- function A(n, e2) {
2601
- const t2 = n.config || {}, o2 = n[e2];
2602
- return void 0 !== o2 ? { config: { ...t2, [e2]: o2 }, chainValue: o2 } : { config: t2, chainValue: void 0 };
2603
- }
2604
- function E(n, e2 = {}) {
2605
- if (!n) return [];
2606
- if (Array.isArray(n)) return n;
2607
- const t2 = [], o2 = /* @__PURE__ */ new Set();
2608
- let s2 = n;
2609
- for (; s2 && e2[s2] && !o2.has(s2); ) {
2610
- o2.add(s2), t2.push(s2);
2611
- const n2 = e2[s2].next;
2612
- if (Array.isArray(n2)) {
2613
- t2.push(...n2);
2927
+ import { buildCacheContext as a, clone as c, compileCache as u, checkCache as f, storeCache as l, compileNext as d, createIngest as g, debounce as p, getId as m, getGrantedConsent as h, isDefined as y, isFunction as v, isObject as b, isRouteArray as w, processEventMapping as k, resolveNext as C, tryCatchAsync as O, useHooks as j } from "@walkeros/core";
2928
+ import { isArray as A } from "@walkeros/core";
2929
+ import { tryCatch as x, tryCatchAsync as q } from "@walkeros/core";
2930
+ import { createIngest as S, getMappingValue as $, tryCatchAsync as D, compileNext as _, resolveNext as I, isRouteArray as P, compileCache as E, checkCache as M, storeCache as R, applyUpdate as H, buildCacheContext as T } from "@walkeros/core";
2931
+ import { createIngest as G, isObject as N, tryCatchAsync as B, useHooks as U, compileNext as F, resolveNext as W, isRouteArray as L, compileCache as V, checkCache as z, storeCache as J, buildCacheContext as K } from "@walkeros/core";
2932
+ import { assign as ve, getId as be, isFunction as we, isString as ke } from "@walkeros/core";
2933
+ import { isObject as Ce } from "@walkeros/core";
2934
+ import { createIngest as qe, getGrantedConsent as Se, processEventMapping as $e, tryCatchAsync as De, useHooks as _e } from "@walkeros/core";
2935
+ import { useHooks as Pe, tryCatchAsync as Ee } from "@walkeros/core";
2936
+ import { useHooks as Me } from "@walkeros/core";
2937
+ function Q(e, n) {
2938
+ return e.storeId && n.stores[e.storeId] ? n.stores[e.storeId] : n.stores.__cache;
2939
+ }
2940
+ function X(e) {
2941
+ const n = {};
2942
+ for (const [t, o2] of Object.entries(e)) {
2943
+ const e2 = o2.config?.next;
2944
+ e2 && !L(e2) ? n[t] = { next: e2 } : n[t] = {};
2945
+ }
2946
+ return n;
2947
+ }
2948
+ function Z(e, n = {}) {
2949
+ if (!e) return [];
2950
+ if (Array.isArray(e)) return e;
2951
+ const t = [], o2 = /* @__PURE__ */ new Set();
2952
+ let s = e;
2953
+ for (; s && n[s] && !o2.has(s); ) {
2954
+ o2.add(s), t.push(s);
2955
+ const e2 = n[s].next;
2956
+ if (Array.isArray(e2)) {
2957
+ t.push(...e2);
2614
2958
  break;
2615
2959
  }
2616
- s2 = n2;
2960
+ s = e2;
2617
2961
  }
2618
- return t2;
2962
+ return t;
2619
2963
  }
2620
- async function x(n, e2, t2) {
2621
- if (e2.init && !e2.config.init) {
2622
- const o2 = e2.type || "unknown", s2 = n.logger.scope(`transformer:${o2}`), r2 = { collector: n, logger: s2, id: t2, config: e2.config, env: $(e2.config.env) };
2623
- s2.debug("init");
2624
- const i2 = await q(e2.init, "TransformerInit", n.hooks)(r2);
2964
+ async function ee(e, n, t) {
2965
+ if (n.init && !n.config.init) {
2966
+ const o2 = n.type || "unknown", s = e.logger.scope(`transformer:${o2}`), r2 = { collector: e, logger: s, id: t, ingest: G(t), config: n.config, env: oe(n.config.env) };
2967
+ s.debug("init");
2968
+ const i2 = await U(n.init, "TransformerInit", e.hooks)(r2);
2625
2969
  if (false === i2) return false;
2626
- e2.config = { ...i2 || e2.config, env: i2?.env || e2.config.env, init: true }, s2.debug("init done");
2970
+ n.config = { ...i2 || n.config, env: i2?.env || n.config.env, init: true }, s.debug("init done");
2627
2971
  }
2628
2972
  return true;
2629
2973
  }
2630
- async function P(n, e2, t2, o2, s2, r2) {
2631
- const i2 = e2.type || "unknown", a2 = n.logger.scope(`transformer:${i2}`), c2 = { collector: n, logger: a2, id: t2, ingest: s2, config: e2.config, env: { ...$(e2.config.env), ...r2 ? { respond: r2 } : {} } };
2974
+ async function ne(e, n, t, o2, s, r2) {
2975
+ const i2 = n.type || "unknown", a2 = e.logger.scope(`transformer:${i2}`), c2 = { collector: e, logger: a2, id: t, ingest: s, config: n.config, env: { ...oe(n.config.env), ...r2 ? { respond: r2 } : {} } };
2632
2976
  a2.debug("push", { event: o2.name });
2633
- const u2 = await q(e2.push, "TransformerPush", n.hooks)(o2, c2);
2977
+ const u2 = await U(n.push, "TransformerPush", e.hooks)(o2, c2);
2634
2978
  return a2.debug("push done"), u2;
2635
2979
  }
2636
- async function S(n, e2, t2, o2, s2, r2) {
2637
- let i2 = o2, a2 = r2;
2638
- for (const o3 of t2) {
2639
- const t3 = e2[o3];
2640
- if (!t3) {
2641
- n.logger.warn(`Transformer not found: ${o3}`);
2980
+ async function te(e, n, t, o2, s, r2, i2) {
2981
+ i2 && s?._meta && (s._meta.chainPath = i2);
2982
+ let a2 = o2, c2 = r2;
2983
+ for (const o3 of t) {
2984
+ const r3 = n[o3];
2985
+ if (!r3) {
2986
+ e.logger.warn(`Transformer not found: ${o3}`);
2642
2987
  continue;
2643
2988
  }
2644
- if (!await j(x)(n, t3, o3)) return n.logger.error(`Transformer init failed: ${o3}`), null;
2645
- const r3 = await j(P, (e3) => (n.logger.scope(`transformer:${t3.type || "unknown"}`).error("Push failed", { error: e3 }), false))(n, t3, o3, i2, s2, a2);
2646
- if (false === r3) return null;
2647
- if (r3 && "object" == typeof r3) {
2648
- const { event: t4, respond: o4, next: c2 } = r3;
2649
- if (o4 && (a2 = o4), c2) {
2650
- const o5 = E(c2, D(e2));
2651
- return o5.length > 0 ? S(n, e2, o5, t4 || i2, s2, a2) : (n.logger.warn(`Branch target not found: ${JSON.stringify(c2)}`), null);
2652
- }
2653
- t4 && (i2 = t4);
2654
- }
2655
- }
2656
- return i2;
2657
- }
2658
- function $(n) {
2659
- return n && C(n) ? n : {};
2660
- }
2661
- async function R(n, e2, t2) {
2662
- const { code: o2, config: s2 = {}, env: r2 = {}, primary: i2, next: a2 } = t2;
2663
- let c2, u2;
2664
- const f2 = E(a2, D(n.transformers)), l2 = n.logger.scope("source").scope(e2), g2 = { push: (t3, o3 = {}) => n.push(t3, { ...o3, id: e2, ingest: c2, respond: u2, mapping: s2, preChain: f2 }), command: n.command, sources: n.sources, elb: n.sources.elb.push, logger: l2, ...r2 }, d2 = { collector: n, logger: l2, id: e2, config: s2, env: g2, setIngest: async (e3) => {
2665
- c2 = s2.ingest ? await k(e3, s2.ingest, { collector: n }) : void 0;
2666
- }, setRespond: (n2) => {
2667
- u2 = n2;
2668
- } }, m2 = await O(o2)(d2);
2669
- if (!m2) return;
2670
- const p2 = m2.type || "unknown", h2 = n.logger.scope(p2).scope(e2);
2671
- return g2.logger = h2, i2 && (m2.config = { ...m2.config, primary: i2 }), m2;
2672
- }
2673
- async function T(n, e2 = {}) {
2674
- const t2 = {};
2675
- for (const [o2, s2] of Object.entries(e2)) {
2676
- const { config: e3 = {} } = s2;
2677
- if (e3.require && e3.require.length > 0) {
2678
- n.pending.sources[o2] = s2;
2989
+ if (s && s._meta && s._meta.path.length > 256) return e.logger.error(`Max path length exceeded at ${o3}`), { event: null, respond: c2 };
2990
+ s && s._meta && (s._meta.hops++, s._meta.path.push(o3));
2991
+ if (!await B(ee)(e, r3, o3)) return e.logger.error(`Transformer init failed: ${o3}`), { event: null, respond: c2 };
2992
+ if (i2 && void 0 !== r3.config?.chainMocks?.[i2]) {
2993
+ const n2 = r3.config.chainMocks[i2];
2994
+ e.logger.scope(`transformer:${r3.type || "unknown"}`).debug("chainMock", { chain: i2 }), a2 = n2;
2679
2995
  continue;
2680
2996
  }
2681
- const r2 = await R(n, o2, s2);
2682
- r2 && (t2[o2] = r2);
2997
+ if (void 0 !== r3.config?.mock) {
2998
+ e.logger.scope(`transformer:${r3.type || "unknown"}`).debug("mock"), a2 = r3.config.mock;
2999
+ continue;
3000
+ }
3001
+ if (r3.config?.disabled) continue;
3002
+ const u2 = r3.config?.cache, f2 = u2 ? V(u2) : void 0, l2 = f2 ? Q(f2, e) : void 0;
3003
+ let d2;
3004
+ if (f2 && l2) {
3005
+ const e2 = K(s, a2), n2 = z(f2, l2, e2, `t:${o3}`);
3006
+ if ("HIT" === n2?.status && n2.value) {
3007
+ if (a2 = n2.value, f2.full) return { event: a2, respond: c2 };
3008
+ continue;
3009
+ }
3010
+ "MISS" === n2?.status && (d2 = { key: n2.key, ttl: n2.rule.ttl });
3011
+ }
3012
+ const g2 = r3.config.before;
3013
+ if (g2) {
3014
+ const t2 = Z("string" == typeof g2 || Array.isArray(g2) && !L(g2) ? g2 : W(F(g2), K(s, a2)) || void 0, X(n));
3015
+ if (t2.length > 0) {
3016
+ const o4 = await te(e, n, t2, a2, s, c2, i2);
3017
+ if (null === o4.event) return { event: null, respond: o4.respond ?? c2 };
3018
+ o4.respond && (c2 = o4.respond), a2 = Array.isArray(o4.event) ? o4.event[0] : o4.event;
3019
+ }
3020
+ }
3021
+ const p2 = await B(ne, (n2) => (e.logger.scope(`transformer:${r3.type || "unknown"}`).error("Push failed", { error: n2 }), false))(e, r3, o3, a2, s, c2);
3022
+ if (false === p2) return { event: null, respond: c2 };
3023
+ if (Array.isArray(p2)) {
3024
+ const r4 = t.slice(t.indexOf(o3) + 1), u3 = await Promise.all(p2.map(async (t2) => {
3025
+ const o4 = t2.event || a2, u4 = s ? { ...s, _meta: { ...s._meta, path: [...s._meta.path] } } : G("unknown");
3026
+ if (t2.next) {
3027
+ let s2 = t2.next;
3028
+ if (L(t2.next)) {
3029
+ const e2 = F(t2.next);
3030
+ s2 = W(e2, K(u4, o4));
3031
+ }
3032
+ if (s2) {
3033
+ const t3 = Z(s2, X(n));
3034
+ if (t3.length > 0) return te(e, n, t3, o4, u4, c2, i2);
3035
+ }
3036
+ return { event: o4, respond: c2 };
3037
+ }
3038
+ return r4.length > 0 ? te(e, n, r4, o4, u4, c2, i2) : { event: o4, respond: c2 };
3039
+ }));
3040
+ let f3 = c2;
3041
+ const l3 = [];
3042
+ for (const e2 of u3.flat()) if (null !== e2) if (e2 && "object" == typeof e2 && "event" in e2) {
3043
+ const n2 = e2;
3044
+ if (n2.respond && (f3 = n2.respond), null === n2.event) continue;
3045
+ Array.isArray(n2.event) ? l3.push(...n2.event) : l3.push(n2.event);
3046
+ } else l3.push(e2);
3047
+ return 0 === l3.length ? { event: null, respond: f3 } : 1 === l3.length ? { event: l3[0], respond: f3 } : { event: l3, respond: f3 };
3048
+ }
3049
+ if (p2 && "object" == typeof p2) {
3050
+ const { event: t2, respond: o4, next: r4 } = p2;
3051
+ if (o4 && (c2 = o4), r4) {
3052
+ let o5 = r4;
3053
+ if (L(r4)) {
3054
+ const e2 = F(r4);
3055
+ if (o5 = W(e2, K(s, a2)), !o5) {
3056
+ t2 && (a2 = t2);
3057
+ continue;
3058
+ }
3059
+ }
3060
+ const u3 = Z(o5, X(n));
3061
+ return u3.length > 0 ? te(e, n, u3, t2 || a2, s, c2, i2) : (e.logger.warn(`Branch target not found: ${JSON.stringify(r4)}`), { event: null, respond: c2 });
3062
+ }
3063
+ t2 && (a2 = t2);
3064
+ }
3065
+ if (d2 && l2 && J(l2, d2.key, a2, d2.ttl), (!p2 || "object" == typeof p2 && !p2.next) && r3.config.next && L(r3.config.next)) {
3066
+ const t2 = r3.config.next, o4 = F(t2), u3 = W(o4, K(s, a2));
3067
+ if (u3) {
3068
+ const t3 = Z(u3, X(n));
3069
+ if (t3.length > 0) return te(e, n, t3, a2, s, c2, i2);
3070
+ }
3071
+ return { event: a2, respond: c2 };
3072
+ }
2683
3073
  }
2684
- return t2;
3074
+ return { event: a2, respond: c2 };
2685
3075
  }
2686
- async function I(n, e2, t2) {
2687
- const o2 = n.on, s2 = o2[e2] || [], r2 = b(t2) ? t2 : [t2];
2688
- r2.forEach((n2) => {
2689
- s2.push(n2);
2690
- }), o2[e2] = s2, await G(n, e2, r2);
3076
+ function oe(e) {
3077
+ return e && N(e) ? e : {};
2691
3078
  }
2692
- function B(n, e2, t2, o2, s2) {
2693
- if (!e2.on) return;
2694
- const r2 = e2.type || "unknown", i2 = n.logger.scope(r2).scope("on").scope(o2), a2 = { collector: n, logger: i2, id: t2, config: e2.config, data: s2, env: W(e2.env, e2.config.env) };
2695
- y(e2.on)(o2, a2);
3079
+ function Te(e) {
3080
+ if (null === e || "object" != typeof e) return e;
3081
+ if (Array.isArray(e)) return e.map(Te);
3082
+ const n = {};
3083
+ for (const [t, o2] of Object.entries(e)) n[t] = "function" == typeof o2 ? o2 : Te(o2);
3084
+ return n;
2696
3085
  }
2697
- async function G(n, e2, o2, s2) {
2698
- let r2, i2 = o2 || [];
2699
- switch (o2 || (i2 = n.on[e2] || []), e2) {
2700
- case t.Commands.Consent:
2701
- r2 = s2 || n.consent;
2702
- break;
2703
- case t.Commands.Session:
2704
- r2 = n.session;
2705
- break;
2706
- case t.Commands.User:
2707
- r2 = s2 || n.user;
2708
- break;
2709
- case t.Commands.Custom:
2710
- r2 = s2 || n.custom;
2711
- break;
2712
- case t.Commands.Globals:
2713
- r2 = s2 || n.globals;
2714
- break;
2715
- case t.Commands.Config:
2716
- r2 = s2 || n.config;
2717
- break;
2718
- case t.Commands.Ready:
2719
- case t.Commands.Run:
2720
- default:
2721
- r2 = void 0;
2722
- }
2723
- let a2 = false;
2724
- for (const t2 of Object.values(n.sources)) if (t2.on) {
2725
- false === await v(t2.on)(e2, r2) && (a2 = true);
2726
- }
2727
- if (Object.entries(n.destinations).forEach(([t2, o3]) => {
2728
- if (o3.on) {
2729
- if (!o3.config.init) return o3.queueOn = o3.queueOn || [], void o3.queueOn.push({ type: e2, data: r2 });
2730
- B(n, o3, t2, e2, r2);
2731
- }
2732
- }), (Object.keys(n.pending.sources).length > 0 || Object.keys(n.pending.destinations).length > 0) && await (async function(n2, e3) {
2733
- for (const [t2, o3] of Object.entries(n2.pending.sources)) {
2734
- if (!n2.pending.sources[t2] || n2.sources[t2]) continue;
2735
- const s3 = o3.config?.require;
2736
- if (!s3) continue;
2737
- const r3 = s3.indexOf(e3);
2738
- if (-1 === r3) continue;
2739
- if (s3.splice(r3, 1), s3.length > 0) continue;
2740
- delete n2.pending.sources[t2];
2741
- const i3 = await R(n2, t2, o3);
2742
- i3 && (n2.sources[t2] = i3);
2743
- }
2744
- for (const [t2, o3] of Object.entries(n2.pending.destinations)) {
2745
- if (!n2.pending.destinations[t2] || n2.destinations[t2]) continue;
2746
- const s3 = o3.config?.require;
2747
- if (!s3) continue;
2748
- const r3 = s3.indexOf(e3);
2749
- if (-1 === r3) continue;
2750
- if (s3.splice(r3, 1), s3.length > 0) continue;
2751
- delete n2.pending.destinations[t2];
2752
- const i3 = M(o3);
2753
- false !== i3.config.queue && (i3.queuePush = [...n2.queue]), n2.destinations[t2] = i3;
2754
- }
2755
- })(n, e2), !i2.length) return !a2;
2756
- switch (e2) {
2757
- case t.Commands.Consent:
2758
- !(function(n2, e3, t2) {
2759
- const o3 = t2 || n2.consent;
2760
- e3.forEach((e4) => {
2761
- Object.keys(o3).filter((n3) => n3 in e4).forEach((t3) => {
2762
- y(e4[t3])(n2, o3);
2763
- });
2764
- });
2765
- })(n, i2, s2);
2766
- break;
2767
- case t.Commands.Ready:
2768
- case t.Commands.Run:
2769
- !(function(n2, e3) {
2770
- n2.allowed && e3.forEach((e4) => {
2771
- y(e4)(n2);
2772
- });
2773
- })(n, i2);
2774
- break;
2775
- case t.Commands.Session:
2776
- !(function(n2, e3) {
2777
- if (!n2.session) return;
2778
- e3.forEach((e4) => {
2779
- y(e4)(n2, n2.session);
2780
- });
2781
- })(n, i2);
2782
- break;
2783
- default:
2784
- i2.forEach((e3) => {
2785
- "function" == typeof e3 && y(e3)(n, r2);
2786
- });
2787
- }
2788
- return !a2;
2789
- }
2790
- async function H(n, e2, t2) {
2791
- const { code: o2, config: s2 = {}, env: r2 = {}, before: i2 } = e2;
2792
- if (!d(o2.push)) return J({ ok: false, failed: { invalid: { type: "invalid", error: "Destination code must have a push method" } } });
2793
- const a2 = t2 || s2 || { init: false }, c2 = i2 ? { ...a2, before: i2 } : a2, u2 = { ...o2, config: c2, env: W(o2.env, r2) };
2794
- let l2 = u2.config.id;
2795
- if (!l2) do {
2796
- l2 = f(4);
2797
- } while (n.destinations[l2]);
2798
- return n.destinations[l2] = u2, false !== u2.config.queue && (u2.queuePush = [...n.queue]), U(n, void 0, {}, { [l2]: u2 });
2799
- }
2800
- async function U(n, e2, t2 = {}, o2) {
2801
- const { allowed: s2, consent: r2, globals: i2, user: u2 } = n;
2802
- if (!s2) return J({ ok: false });
2803
- e2 && (n.queue.push(e2), n.status.in++), o2 || (o2 = n.destinations);
2804
- const f2 = await Promise.all(Object.entries(o2 || {}).map(async ([o3, s3]) => {
2805
- let f3 = (s3.queuePush || []).map((n2) => ({ ...n2, consent: r2 }));
2806
- if (s3.queuePush = [], e2) {
2807
- const n2 = c(e2);
2808
- f3.push(n2);
2809
- }
2810
- if (!f3.length && !s3.queueOn?.length) return { id: o3, destination: s3, skipped: true };
2811
- if (!f3.length && s3.queueOn?.length) {
2812
- const e3 = await h(z)(n, s3, o3);
2813
- return { id: o3, destination: s3, skipped: !e3 };
2814
- }
2815
- const g3 = [], d3 = f3.filter((n2) => {
2816
- const e3 = l(s3.config.consent, r2, n2.consent);
2817
- return !e3 || (n2.consent = e3, g3.push(n2), false);
2818
- });
2819
- if (s3.queuePush.push(...d3), !g3.length) return { id: o3, destination: s3, queue: f3 };
2820
- if (!await h(z)(n, s3, o3)) return { id: o3, destination: s3, queue: f3 };
2821
- let m3, p2;
2822
- s3.dlq || (s3.dlq = []);
2823
- const w2 = (function(n2, e3) {
2824
- const t3 = n2.config.before;
2825
- return t3 ? E(t3, D(e3)) : [];
2826
- })(s3, n.transformers);
2827
- let b2 = 0;
2828
- return await Promise.all(g3.map(async (e3) => {
2829
- e3.globals = a(i2, e3.globals), e3.user = a(u2, e3.user);
2830
- let r3 = e3;
2831
- if (w2.length > 0 && n.transformers && Object.keys(n.transformers).length > 0) {
2832
- const o4 = await S(n, n.transformers, w2, e3, t2.ingest, t2.respond);
2833
- if (null === o4) return e3;
2834
- r3 = o4;
2835
- }
2836
- const c2 = Date.now(), f4 = await h(F, (e4) => {
2837
- const t3 = s3.type || "unknown";
2838
- n.logger.scope(t3).error("Push failed", { error: e4, event: r3.name }), m3 = e4, s3.dlq.push([r3, e4]);
2839
- })(n, s3, o3, r3, t2.ingest, t2.respond);
2840
- return b2 += Date.now() - c2, void 0 !== f4 && (p2 = f4), e3;
2841
- })), { id: o3, destination: s3, error: m3, response: p2, totalDuration: b2 };
2842
- })), g2 = {}, d2 = {}, m2 = {};
2843
- for (const e3 of f2) {
2844
- if (e3.skipped) continue;
2845
- const t3 = { type: e3.destination.type || "unknown", data: e3.response };
2846
- n.status.destinations[e3.id] || (n.status.destinations[e3.id] = { count: 0, failed: 0, duration: 0 });
2847
- const o3 = n.status.destinations[e3.id], s3 = Date.now();
2848
- e3.error ? (t3.error = e3.error, m2[e3.id] = t3, o3.failed++, o3.lastAt = s3, o3.duration += e3.totalDuration || 0, n.status.failed++) : e3.queue && e3.queue.length ? d2[e3.id] = t3 : (g2[e3.id] = t3, o3.count++, o3.lastAt = s3, o3.duration += e3.totalDuration || 0, n.status.out++);
2849
- }
2850
- return J({ event: e2, ...Object.keys(g2).length && { done: g2 }, ...Object.keys(d2).length && { queued: d2 }, ...Object.keys(m2).length && { failed: m2 } });
2851
- }
2852
- async function z(n, e2, t2) {
2853
- if (e2.init && !e2.config.init) {
2854
- const o2 = e2.type || "unknown", s2 = n.logger.scope(o2), r2 = { collector: n, logger: s2, id: t2, config: e2.config, env: W(e2.env, e2.config.env) };
2855
- s2.debug("init");
2856
- const i2 = await w(e2.init, "DestinationInit", n.hooks)(r2);
2857
- if (false === i2) return i2;
2858
- if (e2.config = { ...i2 || e2.config, init: true }, e2.queueOn?.length) {
2859
- const o3 = e2.queueOn;
2860
- e2.queueOn = [];
2861
- for (const { type: s3, data: r3 } of o3) B(n, e2, t2, s3, r3);
2862
- }
2863
- s2.debug("init done");
2864
- }
2865
- return true;
2866
- }
2867
- async function F(n, e2, t2, o2, s2, r2) {
2868
- const { config: i2 } = e2, a2 = await p(o2, i2, n);
2869
- if (a2.ignore) return false;
2870
- const c2 = e2.type || "unknown", f2 = n.logger.scope(c2), l2 = { collector: n, logger: f2, id: t2, config: i2, data: a2.data, rule: a2.mapping, ingest: s2, env: { ...W(e2.env, i2.env), ...r2 ? { respond: r2 } : {} } }, d2 = a2.mapping, m2 = a2.mappingKey || "* *";
2871
- if (!d2?.batch || !e2.pushBatch) {
2872
- f2.debug("push", { event: a2.event.name });
2873
- const t3 = await w(e2.push, "DestinationPush", n.hooks)(a2.event, l2);
2874
- return f2.debug("push done"), t3;
2875
- }
2876
- {
2877
- if (e2.batches = e2.batches || {}, !e2.batches[m2]) {
2878
- const o4 = { key: m2, events: [], data: [] };
2879
- e2.batches[m2] = { batched: o4, batchFn: u(() => {
2880
- const o5 = e2.batches[m2].batched, a3 = { collector: n, logger: f2, id: t2, config: i2, data: void 0, rule: d2, ingest: s2, env: { ...W(e2.env, i2.env), ...r2 ? { respond: r2 } : {} } };
2881
- f2.debug("push batch", { events: o5.events.length }), w(e2.pushBatch, "DestinationPushBatch", n.hooks)(o5, a3), f2.debug("push batch done"), o5.events = [], o5.data = [];
2882
- }, d2.batch) };
2883
- }
2884
- const o3 = e2.batches[m2];
2885
- o3.batched.events.push(a2.event), g(a2.data) && o3.batched.data.push(a2.data), o3.batchFn();
2886
- }
2887
- return true;
2888
- }
2889
- function J(n) {
2890
- return { ok: !n?.failed, ...n };
2891
- }
2892
- function M(n) {
2893
- const { code: e2, config: t2 = {}, env: o2 = {} } = n, { config: s2 } = A(n, "before"), r2 = { ...e2.config, ...t2, ...s2 }, i2 = W(e2.env, o2);
2894
- return { ...e2, config: r2, env: i2 };
2895
- }
2896
- async function N(n, e2 = {}) {
2897
- const t2 = {};
2898
- for (const [o2, s2] of Object.entries(e2)) s2.config?.require?.length ? n.pending.destinations[o2] = s2 : t2[o2] = M(s2);
2899
- return t2;
2900
- }
2901
- function W(n, e2) {
2902
- return n || e2 ? e2 ? n && m(n) && m(e2) ? { ...n, ...e2 } : e2 : n : {};
2903
- }
2904
- async function X(n, e2, t2) {
2905
- const o2 = Object.entries(n).map(async ([n2, o3]) => {
2906
- const s2 = o3.destroy;
2907
- if (!s2) return;
2908
- const r2 = o3.type || "unknown", i2 = t2.scope(r2), a2 = { id: n2, config: o3.config, env: o3.env ?? {}, logger: i2 };
2909
- try {
2910
- await Promise.race([s2(a2), new Promise((t3, o4) => setTimeout(() => o4(new Error(`${e2} '${n2}' destroy timed out`)), 5e3))]);
2911
- } catch (t3) {
2912
- i2.error(`${e2} '${n2}' destroy failed: ${t3}`);
2913
- }
2914
- });
2915
- await Promise.allSettled(o2);
2916
- }
2917
- async function Y(n, e2, o2, r2) {
2918
- let i2, a2, c2 = false, u2 = false;
2919
- switch (e2) {
2920
- case t.Commands.Config:
2921
- K(o2) && (L(n.config, o2, { shallow: false }), a2 = o2, c2 = true);
2922
- break;
2923
- case t.Commands.Consent:
2924
- if (K(o2)) {
2925
- const { update: e3, runQueue: t2 } = s(n, o2);
2926
- a2 = e3, c2 = true, u2 = t2;
2927
- }
2928
- break;
2929
- case t.Commands.Custom:
2930
- K(o2) && (n.custom = L(n.custom, o2), a2 = o2, c2 = true);
2931
- break;
2932
- case t.Commands.Destination:
2933
- K(o2) && ("code" in o2 && K(o2.code) ? i2 = await H(n, o2, r2) : V(o2.push) && (i2 = await H(n, { code: o2 }, r2)));
2934
- break;
2935
- case t.Commands.Globals:
2936
- K(o2) && (n.globals = L(n.globals, o2), a2 = o2, c2 = true);
2937
- break;
2938
- case t.Commands.On:
2939
- _(o2) && await I(n, o2, r2);
2940
- break;
2941
- case t.Commands.Ready:
2942
- c2 = true;
2943
- break;
2944
- case t.Commands.Run:
2945
- i2 = await nn(n, o2), c2 = true;
2946
- break;
2947
- case t.Commands.Session:
2948
- c2 = true;
2949
- break;
2950
- case t.Commands.Shutdown:
2951
- await (async function(n2) {
2952
- const e3 = n2.logger;
2953
- await X(n2.sources, "source", e3), await X(n2.destinations, "destination", e3), await X(n2.transformers, "transformer", e3), await X(n2.stores, "store", e3);
2954
- })(n);
2955
- break;
2956
- case t.Commands.User:
2957
- K(o2) && (L(n.user, o2, { shallow: false }), a2 = o2, c2 = true);
2958
- }
2959
- return c2 && await G(n, e2, void 0, a2), u2 && (i2 = await U(n)), i2 || J({ ok: true });
2960
- }
2961
- function Z(n, e2) {
2962
- if (!e2.name) throw new Error("Event name is required");
2963
- const [t2, o2] = e2.name.split(" ");
2964
- if (!t2 || !o2) throw new Error("Event name is invalid");
2965
- ++n.count;
2966
- const { timestamp: s2 = Date.now(), group: r2 = n.group, count: i2 = n.count } = e2, { name: a2 = `${t2} ${o2}`, data: c2 = {}, context: u2 = {}, globals: f2 = n.globals, custom: l2 = {}, user: g2 = n.user, nested: d2 = [], consent: m2 = n.consent, id: p2 = `${s2}-${r2}-${i2}`, trigger: h2 = "", entity: w2 = t2, action: b2 = o2, timing: y2 = 0, version: v2 = { source: n.version, tagging: n.config.tagging || 0 }, source: k2 = { type: "collector", id: "", previous_id: "" } } = e2;
2967
- return { name: a2, data: c2, context: u2, globals: f2, custom: l2, user: g2, nested: d2, consent: m2, id: p2, trigger: h2, entity: w2, action: b2, timestamp: s2, timing: y2, group: r2, count: i2, version: v2, source: k2 };
2968
- }
2969
- async function nn(n, e2) {
2970
- n.allowed = true, n.count = 0, n.group = Q(), n.timing = Date.now(), e2 && (e2.consent && (n.consent = L(n.consent, e2.consent)), e2.user && (n.user = L(n.user, e2.user)), e2.globals && (n.globals = L(n.config.globalsStatic || {}, e2.globals)), e2.custom && (n.custom = L(n.custom, e2.custom))), Object.values(n.destinations).forEach((n2) => {
2971
- n2.queuePush = [];
2972
- }), n.queue = [], n.round++;
2973
- return await U(n);
2974
- }
2975
- function rn(n, e2) {
2976
- return sn(async (t2, o2 = {}) => await on(async () => {
2977
- const s2 = Date.now(), { id: r2, ingest: i2, respond: a2, mapping: c2, preChain: u2 } = o2;
2978
- let f2 = t2;
2979
- const l2 = i2 ? Object.freeze(i2) : void 0;
2980
- if (c2) {
2981
- const e3 = await tn(f2, c2, n);
2982
- if (e3.ignore) return J({ ok: true });
2983
- if (c2.consent) {
2984
- if (!en(c2.consent, n.consent, e3.event.consent)) return J({ ok: true });
2985
- }
2986
- f2 = e3.event;
2987
- }
2988
- if (u2?.length && n.transformers && Object.keys(n.transformers).length > 0) {
2989
- const e3 = await S(n, n.transformers, u2, f2, l2, a2);
2990
- if (null === e3) return J({ ok: true });
2991
- f2 = e3;
2992
- }
2993
- const g2 = e2(f2), d2 = Z(n, g2), m2 = await U(n, d2, { id: r2, ingest: l2, respond: a2 });
2994
- if (r2) {
2995
- n.status.sources[r2] || (n.status.sources[r2] = { count: 0, duration: 0 });
2996
- const e3 = n.status.sources[r2];
2997
- e3.count++, e3.lastAt = Date.now(), e3.duration += Date.now() - s2;
2998
- }
2999
- return m2;
3000
- }, () => J({ ok: false }))(), "Push", n.hooks);
3001
- }
3002
- async function un(n) {
3003
- const e2 = r({ globalsStatic: {}, sessionStatic: {}, tagging: 0, run: true }, n, { merge: false, extend: false }), t2 = { level: n.logger?.level, handler: n.logger?.handler }, o2 = i(t2), s2 = { ...e2.globalsStatic, ...n.globals }, a2 = { allowed: false, config: e2, consent: n.consent || {}, count: 0, custom: n.custom || {}, destinations: {}, transformers: {}, stores: {}, globals: s2, group: "", hooks: {}, logger: o2, on: {}, queue: [], round: 0, session: void 0, status: { startedAt: Date.now(), in: 0, out: 0, failed: 0, sources: {}, destinations: {} }, timing: Date.now(), user: n.user || {}, version: "3.0.2", sources: {}, pending: { sources: {}, destinations: {} }, push: void 0, command: void 0 };
3004
- a2.push = rn(a2, (n2) => ({ timing: Math.round((Date.now() - a2.timing) / 10) / 100, source: { type: "collector", id: "", previous_id: "" }, ...n2 })), a2.command = (function(n2, e3) {
3005
- return an(async (t3, o3, s3) => await cn(async () => await e3(n2, t3, o3, s3), () => J({ ok: false }))(), "Command", n2.hooks);
3006
- })(a2, Y);
3007
- const c2 = n.stores || {};
3008
- return a2.stores = await (async function(n2, e3 = {}) {
3009
- const t3 = {};
3010
- for (const [o3, s3] of Object.entries(e3)) {
3011
- const { code: e4, config: r2 = {}, env: i2 = {} } = s3, a3 = n2.logger.scope("store").scope(o3), c3 = { collector: n2, logger: a3, id: o3, config: r2, env: i2 }, u2 = await e4(c3);
3012
- t3[o3] = u2;
3013
- }
3014
- return t3;
3015
- })(a2, c2), (function(n2, e3, t3) {
3016
- const o3 = /* @__PURE__ */ new Map();
3017
- for (const [t4, s4] of Object.entries(n2)) e3[t4] && o3.set(s4, e3[t4]);
3018
- if (0 !== o3.size) {
3019
- for (const n3 of [t3.transformers, t3.destinations, t3.sources]) if (n3) for (const e4 of Object.values(n3)) s3(e4.env);
3020
- }
3021
- function s3(n3) {
3022
- if (n3) {
3023
- for (const [e4, t4] of Object.entries(n3)) if ("object" == typeof t4 && null !== t4) {
3024
- const s4 = o3.get(t4);
3025
- s4 && (n3[e4] = s4);
3026
- }
3027
- }
3028
- }
3029
- })(c2, a2.stores, n), a2.destinations = await N(a2, n.destinations || {}), a2.transformers = await (async function(n2, e3 = {}) {
3030
- const t3 = {};
3031
- for (const [o3, s3] of Object.entries(e3)) {
3032
- const { code: e4, env: r2 = {} } = s3, { config: i2 } = A(s3, "next"), a3 = Object.keys(r2).length > 0 ? { ...i2, env: r2 } : i2, c3 = n2.logger.scope("transformer").scope(o3), u2 = { collector: n2, logger: c3, id: o3, config: a3, env: r2 }, f2 = await e4(u2);
3033
- t3[o3] = f2;
3034
- }
3035
- return t3;
3036
- })(a2, n.transformers || {}), a2;
3037
- }
3038
- async function fn(n) {
3039
- n = n || {};
3040
- const e2 = await un(n), t2 = (o2 = e2, { type: "elb", config: {}, push: async (n2, e3, t3, s3, r3, i3) => {
3041
- if ("string" == typeof n2 && n2.startsWith("walker ")) {
3042
- const s4 = n2.replace("walker ", "");
3043
- return o2.command(s4, e3, t3);
3044
- }
3045
- let a3;
3046
- if ("string" == typeof n2) a3 = { name: n2 }, e3 && "object" == typeof e3 && !Array.isArray(e3) && (a3.data = e3);
3047
- else {
3048
- if (!n2 || "object" != typeof n2) return J({ ok: false });
3049
- a3 = n2, e3 && "object" == typeof e3 && !Array.isArray(e3) && (a3.data = { ...a3.data || {}, ...e3 });
3050
- }
3051
- return s3 && "object" == typeof s3 && (a3.context = s3), r3 && Array.isArray(r3) && (a3.nested = r3), i3 && "object" == typeof i3 && (a3.custom = i3), o2.push(a3);
3052
- } });
3053
- var o2;
3054
- e2.sources.elb = t2;
3055
- const s2 = await T(e2, n.sources || {});
3056
- Object.assign(e2.sources, s2);
3057
- const { consent: r2, user: i2, globals: a2, custom: c2 } = n;
3058
- r2 && await e2.command("consent", r2), i2 && await e2.command("user", i2), a2 && Object.assign(e2.globals, a2), c2 && Object.assign(e2.custom, c2), e2.config.run && await e2.command("run");
3059
- let u2 = t2.push;
3060
- const f2 = Object.values(e2.sources).filter((n2) => "elb" !== n2.type), l2 = f2.find((n2) => n2.config.primary);
3061
- return l2 ? u2 = l2.push : f2.length > 0 && (u2 = f2[0].push), { collector: e2, elb: u2 };
3062
- }
3063
- function ln(n) {
3064
- if (null === n || "object" != typeof n) return n;
3065
- if (Array.isArray(n)) return n.map(ln);
3066
- const e2 = {};
3067
- for (const [t2, o2] of Object.entries(n)) e2[t2] = "function" == typeof o2 ? o2 : ln(o2);
3068
- return e2;
3069
- }
3070
- function gn(n) {
3071
- const e2 = [], { simulation: t2, ...o2 } = n, s2 = ln(o2);
3072
- for (const n2 of t2) {
3073
- const t3 = n2.startsWith("call:") ? n2.slice(5) : n2, o3 = t3.split(".");
3074
- let r2 = s2;
3075
- for (let n3 = 0; n3 < o3.length - 1 && null != r2[o3[n3]]; n3++) r2 = r2[o3[n3]];
3086
+ function Ge(e) {
3087
+ const n = [], { simulation: t, ...o2 } = e, s = Te(o2);
3088
+ for (const e2 of t) {
3089
+ const t2 = e2.startsWith("call:") ? e2.slice(5) : e2, o3 = t2.split(".");
3090
+ let r2 = s;
3091
+ for (let e3 = 0; e3 < o3.length - 1 && null != r2[o3[e3]]; e3++) r2 = r2[o3[e3]];
3076
3092
  const i2 = o3[o3.length - 1];
3077
3093
  if (null == r2 || !(i2 in r2)) continue;
3078
3094
  const a2 = r2[i2];
3079
- "function" == typeof a2 && (r2[i2] = function(...n3) {
3080
- return e2.push({ fn: t3, args: n3, ts: Date.now() }), a2.apply(this, n3);
3095
+ "function" == typeof a2 && (r2[i2] = function(...e3) {
3096
+ return n.push({ fn: t2, args: e3, ts: Date.now() }), a2.apply(this, e3);
3081
3097
  });
3082
3098
  }
3083
- return { wrappedEnv: s2, calls: e2 };
3084
- }
3085
- async function dn(n) {
3086
- const e2 = Date.now();
3087
- try {
3088
- switch (n.step) {
3089
- case "transformer":
3090
- return await (async function(n2, e3) {
3091
- const { code: t2, config: o2 = {}, event: s2 } = n2, { collector: r2 } = await fn({ transformers: { sim: { code: t2, config: o2 } } }), i2 = r2.transformers?.sim;
3092
- if (!i2) throw new Error("Transformer failed to initialize");
3093
- const a2 = await i2.push(s2, { collector: r2, logger: r2.logger.scope("transformer").scope("sim"), id: "sim", config: i2.config, env: i2.config?.env || {} });
3094
- let c2;
3095
- c2 = false === a2 ? [] : null == a2 ? [s2] : [a2.event || s2];
3096
- return { step: "transformer", name: n2.name, events: c2, calls: [], duration: Date.now() - e3 };
3097
- })(n, e2);
3098
- case "source":
3099
- return await (async function(n2, e3) {
3100
- const { code: t2, config: o2 = {}, createTrigger: s2, input: r2, consent: i2 } = n2, { content: a2, trigger: c2 } = r2, u2 = { functional: true, marketing: true, analytics: true }, f2 = [], l2 = { consent: i2 || u2, sources: { sim: { code: t2, config: o2, next: "spy" } }, transformers: { spy: { code: () => ({ type: "spy", config: {}, push: (n3) => (f2.push(JSON.parse(JSON.stringify(n3))), { event: n3 }) }) } } }, { trigger: g2 } = await s2(l2);
3101
- void 0 !== a2 && await g2(c2?.type, c2?.options)(a2);
3102
- return { step: "source", name: n2.name, events: f2, calls: [], duration: Date.now() - e3 };
3103
- })(n, e2);
3104
- case "destination":
3105
- return await (async function(n2, e3) {
3106
- const { code: t2, config: o2 = {}, event: s2, consent: r2, env: i2, track: a2 } = n2, c2 = { functional: true, marketing: true, analytics: true };
3107
- let u2 = [], f2 = i2;
3108
- if (i2 && a2 && a2.length > 0) {
3109
- const n3 = gn({ ...i2, simulation: a2 });
3110
- f2 = n3.wrappedEnv, u2 = n3.calls;
3111
- }
3112
- const l2 = { ...o2 };
3113
- f2 && (l2.env = f2);
3114
- const { collector: g2 } = await fn({ consent: r2 || c2, destinations: { sim: { code: t2, config: l2 } } });
3115
- return await g2.push(s2), { step: "destination", name: n2.name, events: [], calls: u2, duration: Date.now() - e3 };
3116
- })(n, e2);
3117
- }
3118
- } catch (t2) {
3119
- return { step: n.step, name: n.name, events: [], calls: [], duration: Date.now() - e2, error: t2 instanceof Error ? t2 : new Error(String(t2)) };
3120
- }
3099
+ return { wrappedEnv: s, calls: n };
3121
3100
  }
3122
3101
 
3123
- // src/commands/simulate/simulator.ts
3102
+ // src/commands/push/index.ts
3124
3103
  init_cli_logger();
3125
3104
  init_core();
3126
- init_config();
3127
3105
  init_tmp();
3106
+ init_config();
3107
+ init_utils();
3108
+ init_bundler();
3128
3109
 
3129
- // src/commands/simulate/env-loader.ts
3130
- async function loadDestinationEnvs(destinations) {
3131
- const envs = {};
3132
- for (const [destKey, destConfig] of Object.entries(destinations)) {
3133
- const typedConfig = destConfig;
3134
- if (!typedConfig.package) {
3135
- continue;
3136
- }
3137
- try {
3138
- const packageName = typedConfig.package;
3139
- const isDemoPackage = packageName.includes("-demo");
3140
- const importPath = isDemoPackage ? packageName : `${packageName}/dev`;
3141
- const module = await import(importPath);
3142
- const examplesModule = module.examples || module.default?.examples;
3143
- const envModule = examplesModule?.env;
3144
- if (envModule?.push) {
3145
- envs[destKey] = {
3146
- init: envModule.init,
3147
- push: envModule.push,
3148
- simulation: envModule.simulation || []
3149
- };
3150
- }
3151
- } catch {
3152
- }
3153
- }
3154
- return envs;
3155
- }
3156
-
3157
- // src/commands/simulate/simulator.ts
3158
- function callsToUsage(destName, calls) {
3159
- if (!calls.length) return {};
3160
- return {
3161
- [destName]: calls.map((c2) => ({
3162
- type: "call",
3163
- path: c2.fn,
3164
- args: c2.args,
3165
- timestamp: c2.ts
3166
- }))
3167
- };
3168
- }
3169
- async function simulateCore(inputPath, event, options = {}) {
3170
- const logger = createCLILogger({
3171
- verbose: options.verbose || false,
3172
- silent: options.silent || false,
3173
- json: options.json || false
3174
- });
3175
- try {
3176
- logger.debug(`Simulating event: ${JSON.stringify(event)}`);
3177
- const result = await executeSimulation(event, inputPath, options.platform, {
3178
- flow: options.flow,
3179
- step: options.step,
3180
- verbose: options.verbose
3181
- });
3182
- return result;
3183
- } catch (error) {
3184
- const errorMessage = getErrorMessage(error);
3185
- return {
3186
- success: false,
3187
- error: errorMessage
3188
- };
3189
- }
3190
- }
3191
- function formatSimulationResult(result, options = {}) {
3192
- if (options.json) {
3193
- const output = {
3194
- result: result.elbResult,
3195
- usage: result.usage,
3196
- duration: result.duration
3197
- };
3198
- if (result.capturedEvents) {
3199
- output.capturedEvents = result.capturedEvents;
3200
- }
3201
- return JSON.stringify(output, null, 2);
3202
- }
3203
- const lines = [];
3204
- if (result.success) {
3205
- lines.push("Simulation completed");
3206
- } else {
3207
- lines.push(`Simulation failed: ${result.error}`);
3110
+ // src/commands/push/overrides.ts
3111
+ function buildOverrides(flags, flowConfig) {
3112
+ const simulateFlags = flags.simulate ?? [];
3113
+ const mockFlags = flags.mock ?? [];
3114
+ if (simulateFlags.length === 0 && mockFlags.length === 0) {
3115
+ return {};
3208
3116
  }
3209
- if (result.capturedEvents) {
3210
- lines.push(`Captured ${result.capturedEvents.length} event(s)`);
3211
- for (const evt of result.capturedEvents) {
3212
- lines.push(` - ${evt.name || "unknown"}`);
3213
- }
3117
+ const simulateNames = /* @__PURE__ */ new Set();
3118
+ const sourceSimulateNames = /* @__PURE__ */ new Set();
3119
+ const mockNames = /* @__PURE__ */ new Set();
3120
+ const overrides = {};
3121
+ for (const step of simulateFlags) {
3122
+ const { type, name } = parseStep(step);
3123
+ if (type === "destination") {
3124
+ simulateNames.add(name);
3125
+ if (!overrides.destinations) overrides.destinations = {};
3126
+ overrides.destinations[name] = { simulate: true };
3127
+ } else if (type === "transformer") {
3128
+ if (!overrides.transformers) overrides.transformers = {};
3129
+ overrides.transformers[name] = { simulate: true };
3130
+ } else {
3131
+ sourceSimulateNames.add(name);
3132
+ if (!overrides.sources) overrides.sources = {};
3133
+ overrides.sources[name] = { simulate: true };
3134
+ }
3135
+ }
3136
+ const hasSource = sourceSimulateNames.size > 0;
3137
+ const hasTransformer = overrides.transformers && Object.keys(overrides.transformers).length > 0;
3138
+ const hasDestination = simulateNames.size > 0;
3139
+ const simulatedTypes = [
3140
+ hasSource && "source",
3141
+ hasTransformer && "transformer",
3142
+ hasDestination && "destination"
3143
+ ].filter(Boolean);
3144
+ if (simulatedTypes.length > 1) {
3145
+ throw new Error(
3146
+ `Cannot simulate both ${simulatedTypes.join(" and ")} in the same invocation. Run separate commands for each step type.`
3147
+ );
3214
3148
  }
3215
- return lines.join("\n");
3216
- }
3217
- async function executeSimulation(event, inputPath, platformOverride, options = {}) {
3218
- const startTime = Date.now();
3219
- const tempDir = getTmpPath();
3220
- try {
3221
- await fs12.ensureDir(tempDir);
3222
- const detected = await detectInput(inputPath, platformOverride);
3223
- if (!isObject(event) || !("name" in event) || typeof event.name !== "string") {
3149
+ for (const step of mockFlags) {
3150
+ const eqIndex = step.indexOf("=");
3151
+ if (eqIndex === -1) {
3224
3152
  throw new Error(
3225
- 'Event must be an object with a "name" property of type string'
3153
+ `Invalid --mock format: "${step}". Expected destination.NAME=VALUE`
3226
3154
  );
3227
3155
  }
3228
- const typedEvent = event;
3229
- if (detected.type !== "config") {
3156
+ const stepPart = step.slice(0, eqIndex);
3157
+ const valuePart = step.slice(eqIndex + 1);
3158
+ const parsed = parseStep(stepPart);
3159
+ if (parsed.type === "source") {
3230
3160
  throw new Error(
3231
- `Input "${inputPath}" is not valid JSON config. simulate only accepts Flow.Config config files.`
3161
+ `--mock is not supported for sources. Use --simulate source.${parsed.name}`
3232
3162
  );
3233
3163
  }
3234
- return await executeConfigSimulation(
3235
- detected.content,
3236
- inputPath,
3237
- typedEvent,
3238
- tempDir,
3239
- startTime,
3240
- options.flow,
3241
- options.step
3242
- );
3243
- } catch (error) {
3244
- const duration = Date.now() - startTime;
3245
- return {
3246
- success: false,
3247
- error: getErrorMessage(error),
3248
- duration
3249
- };
3250
- } finally {
3251
- if (tempDir) {
3252
- await fs12.remove(tempDir).catch(() => {
3253
- });
3164
+ if (parsed.type === "transformer" && !parsed.chainType) {
3165
+ throw new Error(
3166
+ `Use --mock destination.NAME.before.${parsed.name}=VALUE for path-specific transformer mocks`
3167
+ );
3254
3168
  }
3255
- }
3256
- }
3257
- function parseStepTarget(stepTarget, flowConfig) {
3258
- if (stepTarget) {
3259
- const dotIndex = stepTarget.indexOf(".");
3260
- if (dotIndex > -1) {
3261
- const type = stepTarget.substring(0, dotIndex);
3262
- const name = stepTarget.substring(dotIndex + 1);
3263
- const section = flowConfig[type + "s"];
3264
- if (!section?.[name]) {
3265
- throw new Error(`Step "${stepTarget}" not found in flow config`);
3169
+ let parsedValue;
3170
+ try {
3171
+ parsedValue = JSON.parse(valuePart);
3172
+ } catch {
3173
+ parsedValue = valuePart;
3174
+ }
3175
+ if (parsed.chainType && parsed.transformerId) {
3176
+ const chainPath = `destination.${parsed.name}.${parsed.chainType}`;
3177
+ if (!overrides.transformerMocks) overrides.transformerMocks = {};
3178
+ if (!overrides.transformerMocks[chainPath])
3179
+ overrides.transformerMocks[chainPath] = {};
3180
+ overrides.transformerMocks[chainPath][parsed.transformerId] = parsedValue;
3181
+ } else {
3182
+ if (simulateNames.has(parsed.name)) {
3183
+ throw new Error(
3184
+ `Destination "${parsed.name}" cannot be in both --simulate and --mock`
3185
+ );
3186
+ }
3187
+ mockNames.add(parsed.name);
3188
+ if (!overrides.destinations) overrides.destinations = {};
3189
+ overrides.destinations[parsed.name] = { config: { mock: parsedValue } };
3190
+ }
3191
+ }
3192
+ const allDestinations = Object.keys(flowConfig.destinations ?? {});
3193
+ const targetedNames = /* @__PURE__ */ new Set([...simulateNames, ...mockNames]);
3194
+ if (targetedNames.size > 0) {
3195
+ if (!overrides.destinations) overrides.destinations = {};
3196
+ for (const destName of allDestinations) {
3197
+ if (!targetedNames.has(destName)) {
3198
+ overrides.destinations[destName] = { config: { disabled: true } };
3266
3199
  }
3267
- return { type, name, config: section[name] };
3268
3200
  }
3269
3201
  }
3270
- const destinations = flowConfig.destinations;
3271
- if (destinations) {
3272
- const [name, config] = Object.entries(destinations)[0];
3273
- return {
3274
- type: "destination",
3275
- name,
3276
- config
3277
- };
3278
- }
3279
- throw new Error("No destination found in flow config");
3202
+ return overrides;
3280
3203
  }
3281
- async function executeConfigSimulation(_content, configPath, typedEvent, tempDir, startTime, flowName, stepTarget) {
3282
- const { flowSettings } = await loadFlowConfig(configPath, {
3283
- flowName
3284
- });
3285
- const step = parseStepTarget(
3286
- stepTarget,
3287
- flowSettings
3288
- );
3289
- if (step.type === "destination") {
3290
- const packageName = step.config.package;
3291
- if (!packageName) {
3292
- throw new Error(`Destination "${step.name}" has no package field`);
3293
- }
3294
- const destModule = await import(packageName);
3295
- const code = destModule.default || Object.values(destModule)[0];
3296
- const destinations = flowSettings.destinations;
3297
- const envs = await loadDestinationEnvs(destinations || {});
3298
- const destEnv = envs[step.name];
3299
- const result = await dn({
3300
- step: "destination",
3301
- name: step.name,
3302
- code,
3303
- event: typedEvent,
3304
- config: step.config.config,
3305
- env: destEnv?.push,
3306
- track: destEnv?.simulation
3307
- });
3308
- const duration = Date.now() - startTime;
3309
- return {
3310
- success: !result.error,
3311
- error: result.error?.message,
3312
- usage: callsToUsage(step.name, result.calls),
3313
- duration,
3314
- logs: []
3315
- };
3316
- }
3317
- if (step.type === "transformer") {
3318
- const packageName = step.config.package;
3319
- if (!packageName) {
3320
- throw new Error(`Transformer "${step.name}" has no package field`);
3321
- }
3322
- const mod = await import(packageName);
3323
- const code = mod.default || Object.values(mod)[0];
3324
- const result = await dn({
3325
- step: "transformer",
3326
- name: step.name,
3327
- code,
3328
- event: typedEvent,
3329
- config: step.config.config
3330
- });
3331
- const duration = Date.now() - startTime;
3332
- return {
3333
- success: !result.error,
3334
- error: result.error?.message,
3335
- capturedEvents: result.events,
3336
- duration,
3337
- usage: {},
3338
- logs: []
3339
- };
3204
+ function parseStep(step) {
3205
+ const parts = step.split(".");
3206
+ if (parts.length < 2) {
3207
+ throw new Error(
3208
+ `Invalid step format: "${step}". Expected "source.NAME" or "destination.NAME"`
3209
+ );
3340
3210
  }
3341
- throw new Error(`Unknown step type: ${step.type}`);
3342
- }
3343
-
3344
- // src/commands/simulate/source-simulator.ts
3345
- init_core();
3346
- async function loadSourcePackage(packageName) {
3347
- const mainModule = await import(packageName);
3348
- const code = mainModule.default || Object.values(mainModule)[0];
3349
- if (!code || typeof code !== "function") {
3350
- throw new Error(`Package ${packageName} missing source init function`);
3211
+ const prefix = parts[0];
3212
+ if (prefix !== "source" && prefix !== "destination" && prefix !== "transformer") {
3213
+ throw new Error(
3214
+ `Unsupported step type: "${prefix}". Use "source", "destination", or "transformer"`
3215
+ );
3351
3216
  }
3352
- let createTrigger;
3353
- try {
3354
- const devModule = await import(`${packageName}/dev`);
3355
- const examples = devModule.examples || devModule.default?.examples;
3356
- if (examples?.createTrigger && typeof examples.createTrigger === "function") {
3357
- createTrigger = examples.createTrigger;
3358
- }
3359
- } catch {
3217
+ const name = parts[1];
3218
+ if (!name) {
3219
+ throw new Error(
3220
+ `Invalid step format: "${step}". Missing name after "${prefix}."`
3221
+ );
3360
3222
  }
3361
- return { code, createTrigger };
3362
- }
3363
- async function simulateSourceCLI(flowConfig, sourceInput, options) {
3364
- const startTime = Date.now();
3365
- try {
3366
- const sources = flowConfig.sources;
3367
- if (!sources) {
3368
- throw new Error("Flow config has no sources");
3369
- }
3370
- const sourceConfig = sources[options.sourceStep];
3371
- if (!sourceConfig) {
3372
- const available = Object.keys(sources).join(", ");
3223
+ if (parts.length >= 4) {
3224
+ const chainType = parts[2];
3225
+ if (chainType !== "before" && chainType !== "next") {
3373
3226
  throw new Error(
3374
- `Source "${options.sourceStep}" not found. Available: ${available}`
3227
+ `Invalid chain type: "${chainType}". Use "before" or "next"`
3375
3228
  );
3376
3229
  }
3377
- if (!sourceConfig.package) {
3378
- throw new Error(`Source "${options.sourceStep}" has no package field`);
3379
- }
3380
- const { code, createTrigger } = await loadSourcePackage(
3381
- sourceConfig.package
3382
- );
3383
- if (!createTrigger) {
3230
+ const transformerId = parts[3];
3231
+ if (!transformerId) {
3384
3232
  throw new Error(
3385
- `Source package ${sourceConfig.package} has no createTrigger export. Ensure the package exports createTrigger from its /dev subpath.`
3233
+ `Invalid step format: "${step}". Missing transformer name after "${chainType}."`
3386
3234
  );
3387
3235
  }
3388
- const result = await dn({
3389
- step: "source",
3390
- name: options.sourceStep,
3391
- code,
3392
- config: sourceConfig.config || {},
3393
- createTrigger,
3394
- input: sourceInput || { content: void 0 }
3395
- });
3396
- return {
3397
- success: !result.error,
3398
- error: result.error?.message,
3399
- capturedEvents: result.events,
3400
- duration: Date.now() - startTime
3401
- };
3402
- } catch (error) {
3403
- return {
3404
- success: false,
3405
- error: getErrorMessage(error),
3406
- duration: Date.now() - startTime
3407
- };
3408
- }
3409
- }
3410
-
3411
- // src/commands/simulate/index.ts
3412
- init_cli_logger();
3413
- init_core();
3414
- init_config();
3415
- init_validators();
3416
-
3417
- // src/commands/simulate/example-loader.ts
3418
- function findExample(config, exampleName, stepTarget) {
3419
- if (stepTarget) {
3420
- return findExampleInStep(config, exampleName, stepTarget);
3421
- }
3422
- return findExampleAcrossSteps(config, exampleName);
3423
- }
3424
- function findExampleInStep(config, exampleName, stepTarget) {
3425
- const dotIndex = stepTarget.indexOf(".");
3426
- if (dotIndex === -1) {
3427
- throw new Error(
3428
- `Invalid --step format: "${stepTarget}". Expected "type.name" (e.g. "destination.gtag")`
3429
- );
3430
- }
3431
- const type = stepTarget.substring(0, dotIndex);
3432
- const name = stepTarget.substring(dotIndex + 1);
3433
- const stepMap = getStepMap(config, type);
3434
- if (!stepMap) {
3435
- throw new Error(`No ${type}s found in flow config`);
3236
+ return { type: prefix, name, chainType, transformerId };
3436
3237
  }
3437
- const step = stepMap[name];
3438
- if (!step) {
3439
- const available = Object.keys(stepMap).join(", ");
3440
- throw new Error(`${type} "${name}" not found. Available: ${available}`);
3441
- }
3442
- const examples = step.examples;
3443
- if (!examples || !examples[exampleName]) {
3444
- const available = examples ? Object.keys(examples).join(", ") : "none";
3238
+ if (parts.length === 3) {
3445
3239
  throw new Error(
3446
- `Example "${exampleName}" not found in ${type} "${name}". Available: ${available}`
3240
+ `Invalid step format: "${step}". Specify a transformer: "${step}.TRANSFORMER_NAME"`
3447
3241
  );
3448
3242
  }
3449
- return {
3450
- stepType: type,
3451
- stepName: name,
3452
- exampleName,
3453
- example: examples[exampleName]
3454
- };
3243
+ return { type: prefix, name };
3455
3244
  }
3456
- function findExampleAcrossSteps(config, exampleName) {
3457
- const matches = [];
3458
- const stepTypes = ["source", "transformer", "destination"];
3459
- for (const type of stepTypes) {
3460
- const stepMap = getStepMap(config, type);
3461
- if (!stepMap) continue;
3462
- for (const [name, step] of Object.entries(stepMap)) {
3463
- const examples = step.examples;
3464
- if (examples && examples[exampleName]) {
3465
- matches.push({
3466
- stepType: type,
3467
- stepName: name,
3468
- exampleName,
3469
- example: examples[exampleName]
3470
- });
3245
+
3246
+ // src/commands/push/apply-overrides.ts
3247
+ function applyOverrides(config, overrides) {
3248
+ if (overrides.destinations) {
3249
+ const destinations = config.destinations;
3250
+ if (destinations) {
3251
+ for (const [id, override] of Object.entries(overrides.destinations)) {
3252
+ const dest = destinations[id];
3253
+ if (!dest) continue;
3254
+ if (!dest.config) dest.config = {};
3255
+ const destConfig = dest.config;
3256
+ if (override.config?.disabled) destConfig.disabled = true;
3257
+ if (override.config?.mock !== void 0)
3258
+ destConfig.mock = override.config.mock;
3259
+ if (override.env) {
3260
+ destConfig.env = override.env;
3261
+ }
3471
3262
  }
3472
3263
  }
3473
3264
  }
3474
- if (matches.length === 0) {
3475
- throw new Error(`Example "${exampleName}" not found in any step`);
3476
- }
3477
- if (matches.length > 1) {
3478
- const locations = matches.map((m2) => `${m2.stepType}.${m2.stepName}`).join(", ");
3479
- throw new Error(
3480
- `Example "${exampleName}" found in multiple steps: ${locations}. Use --step to disambiguate.`
3481
- );
3482
- }
3483
- return matches[0];
3484
- }
3485
- function getStepMap(config, type) {
3486
- switch (type) {
3487
- case "source":
3488
- return config.sources;
3489
- case "transformer":
3490
- return config.transformers;
3491
- case "destination":
3492
- return config.destinations;
3493
- default:
3494
- throw new Error(
3495
- `Invalid step type: "${type}". Must be "source", "transformer", or "destination"`
3496
- );
3265
+ if (overrides.transformerMocks) {
3266
+ const transformers = config.transformers;
3267
+ if (transformers) {
3268
+ for (const [chainPath, mocks] of Object.entries(
3269
+ overrides.transformerMocks
3270
+ )) {
3271
+ for (const [transformerId, mockValue] of Object.entries(mocks)) {
3272
+ const transformer = transformers[transformerId];
3273
+ if (!transformer) continue;
3274
+ if (!transformer.config) transformer.config = {};
3275
+ const tConfig = transformer.config;
3276
+ if (!tConfig.chainMocks) tConfig.chainMocks = {};
3277
+ tConfig.chainMocks[chainPath] = mockValue;
3278
+ }
3279
+ }
3280
+ }
3497
3281
  }
3498
3282
  }
3499
3283
 
3500
- // src/commands/simulate/compare.ts
3501
- function compareOutput(expected, actual) {
3502
- const expectedStr = JSON.stringify(expected, null, 2);
3503
- const actualStr = JSON.stringify(actual, null, 2);
3504
- if (expectedStr === actualStr) {
3505
- return { expected, actual, match: true };
3284
+ // src/commands/push/index.ts
3285
+ init_package_path();
3286
+
3287
+ // src/commands/push/flow-context.ts
3288
+ init_utils2();
3289
+ import path14 from "path";
3290
+ import { pathToFileURL } from "url";
3291
+ import { JSDOM, VirtualConsole } from "jsdom";
3292
+
3293
+ // src/commands/push/async-drain.ts
3294
+ function installTimerInterception(options = {}) {
3295
+ let nextId = 1;
3296
+ const pending = /* @__PURE__ */ new Map();
3297
+ const realSetTimeout = globalThis.setTimeout.bind(globalThis);
3298
+ const targets = [];
3299
+ function patchTarget(target) {
3300
+ targets.push({
3301
+ target,
3302
+ setTimeout: target.setTimeout,
3303
+ clearTimeout: target.clearTimeout,
3304
+ setInterval: target.setInterval,
3305
+ clearInterval: target.clearInterval
3306
+ });
3307
+ target.setTimeout = (callback, delay, ...args) => {
3308
+ if (typeof callback !== "function") return 0;
3309
+ const id = nextId++;
3310
+ pending.set(id, { id, callback, delay: delay ?? 0, type: "timeout", args, cleared: false });
3311
+ return id;
3312
+ };
3313
+ target.clearTimeout = (id) => {
3314
+ if (id == null) return;
3315
+ const numId = typeof id === "number" ? id : Number(id);
3316
+ const entry = pending.get(numId);
3317
+ if (entry) entry.cleared = true;
3318
+ };
3319
+ target.setInterval = (callback, delay, ...args) => {
3320
+ if (typeof callback !== "function") return 0;
3321
+ const id = nextId++;
3322
+ pending.set(id, { id, callback, delay: delay ?? 0, type: "interval", args, cleared: false });
3323
+ return id;
3324
+ };
3325
+ target.clearInterval = target.clearTimeout;
3326
+ }
3327
+ patchTarget(globalThis);
3328
+ if (options.domWindow && options.domWindow !== globalThis) {
3329
+ patchTarget(options.domWindow);
3330
+ }
3331
+ const drainMicrotasks = () => new Promise((resolve3) => realSetTimeout(resolve3, 0));
3332
+ async function flush(wallTimeout = 5e3) {
3333
+ const deadline = Date.now() + wallTimeout;
3334
+ const maxIterations = 100;
3335
+ let iterations = 0;
3336
+ while (iterations < maxIterations) {
3337
+ await drainMicrotasks();
3338
+ const ready = Array.from(pending.values()).filter((t) => !t.cleared).sort((a2, b2) => a2.delay - b2.delay);
3339
+ if (ready.length === 0) break;
3340
+ for (const timer of ready) {
3341
+ if (timer.cleared) continue;
3342
+ pending.delete(timer.id);
3343
+ if (timer.type === "interval") {
3344
+ timer.cleared = true;
3345
+ const newId = nextId++;
3346
+ pending.set(newId, { ...timer, id: newId, cleared: false });
3347
+ }
3348
+ try {
3349
+ timer.callback(...timer.args);
3350
+ } catch (err) {
3351
+ console.warn("[async-drain] Timer callback error:", err);
3352
+ }
3353
+ }
3354
+ await drainMicrotasks();
3355
+ if (Date.now() > deadline) {
3356
+ break;
3357
+ }
3358
+ iterations++;
3359
+ }
3360
+ pending.clear();
3506
3361
  }
3507
- return {
3508
- expected,
3509
- actual,
3510
- match: false,
3511
- diff: `Expected:
3512
- ${expectedStr}
3513
-
3514
- Actual:
3515
- ${actualStr}`
3516
- };
3362
+ function countPending() {
3363
+ return Array.from(pending.values()).filter((t) => !t.cleared).length;
3364
+ }
3365
+ function restore() {
3366
+ for (const saved of targets) {
3367
+ saved.target.setTimeout = saved.setTimeout;
3368
+ saved.target.clearTimeout = saved.clearTimeout;
3369
+ saved.target.setInterval = saved.setInterval;
3370
+ saved.target.clearInterval = saved.clearInterval;
3371
+ }
3372
+ pending.clear();
3373
+ }
3374
+ return { flush, countPending, restore };
3517
3375
  }
3518
3376
 
3519
- // src/commands/simulate/index.ts
3520
- async function simulateCommand(options) {
3521
- const logger = createCLILogger({ ...options, stderr: true });
3377
+ // src/commands/push/flow-context.ts
3378
+ async function withFlowContext(options, fn) {
3379
+ const { esmPath, platform, logger, snapshotCode, timeout, networkCalls, asyncDrain } = options;
3522
3380
  const startTime = Date.now();
3523
- try {
3524
- let config;
3525
- if (isStdinPiped() && !options.config) {
3526
- const stdinContent = await readStdin();
3527
- const fs15 = await import("fs-extra");
3528
- const path15 = await import("path");
3529
- const tmpPath = getTmpPath(void 0, "stdin-simulate.json");
3530
- await fs15.default.ensureDir(path15.default.dirname(tmpPath));
3531
- await fs15.default.writeFile(tmpPath, stdinContent, "utf-8");
3532
- config = tmpPath;
3533
- } else {
3534
- config = options.config || "bundle.config.json";
3535
- }
3536
- const result = await simulate(config, options.event, {
3537
- flow: options.flow,
3538
- json: options.json,
3539
- verbose: options.verbose,
3540
- silent: options.silent,
3541
- platform: options.platform,
3542
- step: options.step
3381
+ const g2 = global;
3382
+ let savedWindow, savedDocument, savedNavigator;
3383
+ let savedFetch;
3384
+ let dom;
3385
+ let timerControl;
3386
+ if (platform === "web") {
3387
+ const virtualConsole = new VirtualConsole();
3388
+ dom = new JSDOM("<!DOCTYPE html><html><body></body></html>", {
3389
+ url: "http://localhost",
3390
+ runScripts: "dangerously",
3391
+ resources: "usable",
3392
+ virtualConsole
3543
3393
  });
3544
- const resultWithDuration = {
3545
- ...result,
3546
- duration: (Date.now() - startTime) / 1e3
3547
- };
3548
- const formatted = formatSimulationResult(resultWithDuration, {
3549
- json: options.json
3394
+ savedWindow = g2.window;
3395
+ savedDocument = g2.document;
3396
+ savedNavigator = g2.navigator;
3397
+ g2.window = dom.window;
3398
+ g2.document = dom.window.document;
3399
+ Object.defineProperty(global, "navigator", {
3400
+ value: dom.window.navigator,
3401
+ configurable: true,
3402
+ writable: true
3550
3403
  });
3551
- await writeResult(formatted + "\n", { output: options.output });
3552
- process.exit(result.success ? 0 : 1);
3553
- } catch (error) {
3554
- const errorMessage = getErrorMessage(error);
3555
- if (options.json) {
3556
- const errorOutput = JSON.stringify(
3557
- {
3558
- success: false,
3559
- error: errorMessage,
3560
- duration: (Date.now() - startTime) / 1e3
3561
- },
3562
- null,
3563
- 2
3564
- );
3565
- await writeResult(errorOutput + "\n", { output: options.output });
3566
- } else {
3567
- logger.error(`Error: ${errorMessage}`);
3404
+ if (networkCalls) {
3405
+ savedFetch = global.fetch;
3406
+ applyNetworkPolyfills(dom, networkCalls);
3407
+ global.fetch = dom.window.fetch;
3568
3408
  }
3569
- process.exit(1);
3570
3409
  }
3571
- }
3572
- async function simulate(configOrPath, event, options = {}) {
3573
- if (typeof configOrPath !== "string") {
3574
- throw new Error(
3575
- "simulate() currently only supports config file paths. Please provide a path to a configuration file."
3576
- );
3410
+ if (asyncDrain) {
3411
+ timerControl = installTimerInterception({
3412
+ domWindow: platform === "web" && dom ? dom.window : void 0
3413
+ });
3577
3414
  }
3578
- let resolvedEvent = event;
3579
- if (typeof event === "string") {
3580
- resolvedEvent = await loadJsonFromSource(event, { name: "event" });
3581
- }
3582
- const isSourceSimulation = options.step?.startsWith("source.");
3583
- let result;
3584
- if (isSourceSimulation) {
3585
- const rawConfig = await loadJsonConfig(configOrPath);
3586
- const setup = validateFlowConfig(rawConfig);
3587
- const flowNames = Object.keys(setup.flows);
3588
- const flowName = options.flow || (flowNames.length === 1 ? flowNames[0] : void 0);
3589
- if (!flowName) {
3590
- throw new Error(
3591
- `Multiple flows found. Use --flow to specify which flow.
3592
- Available: ${flowNames.join(", ")}`
3593
- );
3415
+ try {
3416
+ if (snapshotCode) {
3417
+ if (platform === "web" && dom) {
3418
+ logger.debug("Evaluating snapshot in JSDOM");
3419
+ dom.window.eval(snapshotCode);
3420
+ } else {
3421
+ logger.debug("Evaluating snapshot in Node");
3422
+ const vm = await import("vm");
3423
+ vm.runInThisContext(snapshotCode);
3424
+ }
3594
3425
  }
3595
- const flowSettings = setup.flows[flowName];
3596
- if (!flowSettings) {
3426
+ const fileUrl = pathToFileURL(path14.resolve(esmPath)).href;
3427
+ const module = await import(`${fileUrl}?t=${Date.now()}`);
3428
+ const { wireConfig, startFlow } = module;
3429
+ if (typeof wireConfig !== "function" || typeof startFlow !== "function") {
3597
3430
  throw new Error(
3598
- `Flow "${flowName}" not found. Available: ${flowNames.join(", ")}`
3431
+ "Invalid ESM bundle: missing wireConfig or startFlow exports"
3599
3432
  );
3600
3433
  }
3601
- const sourceStep = options.step.substring("source.".length);
3602
- result = await simulateSourceCLI(
3603
- flowSettings,
3604
- resolvedEvent,
3605
- {
3606
- flow: options.flow,
3607
- sourceStep,
3608
- json: options.json,
3609
- verbose: options.verbose,
3610
- silent: options.silent
3434
+ const flowModule = {
3435
+ wireConfig,
3436
+ startFlow,
3437
+ __configData: module.__configData
3438
+ };
3439
+ if (timerControl) {
3440
+ const result = await fn(flowModule);
3441
+ await timerControl.flush(asyncDrain?.timeout ?? 5e3);
3442
+ return result;
3443
+ } else if (timeout) {
3444
+ const timeoutPromise = new Promise((_2, reject) => {
3445
+ setTimeout(
3446
+ () => reject(new Error(`Push timeout after ${timeout}ms`)),
3447
+ timeout
3448
+ );
3449
+ });
3450
+ return await Promise.race([fn(flowModule), timeoutPromise]);
3451
+ }
3452
+ return await fn(flowModule);
3453
+ } catch (error) {
3454
+ return {
3455
+ success: false,
3456
+ duration: Date.now() - startTime,
3457
+ error: getErrorMessage(error)
3458
+ };
3459
+ } finally {
3460
+ if (timerControl) timerControl.restore();
3461
+ if (savedFetch !== void 0) {
3462
+ cleanupNetworkPolyfills(savedFetch);
3463
+ }
3464
+ if (platform === "web") {
3465
+ if (savedWindow !== void 0) g2.window = savedWindow;
3466
+ else delete g2.window;
3467
+ if (savedDocument !== void 0) g2.document = savedDocument;
3468
+ else delete g2.document;
3469
+ if (savedNavigator !== void 0) {
3470
+ Object.defineProperty(global, "navigator", {
3471
+ value: savedNavigator,
3472
+ configurable: true,
3473
+ writable: true
3474
+ });
3475
+ } else {
3476
+ delete g2.navigator;
3611
3477
  }
3612
- );
3613
- } else {
3614
- result = await simulateCore(configOrPath, resolvedEvent, {
3615
- json: options.json ?? false,
3616
- verbose: options.verbose ?? false,
3617
- silent: options.silent ?? false,
3618
- flow: options.flow,
3619
- platform: options.platform,
3620
- step: options.step
3621
- });
3478
+ }
3622
3479
  }
3623
- return result;
3480
+ }
3481
+ function applyNetworkPolyfills(dom, networkCalls) {
3482
+ dom.window.fetch = (async (input, init) => {
3483
+ const url = typeof input === "string" ? input : input.toString();
3484
+ const method = init?.method || "GET";
3485
+ const body = init?.body !== void 0 && init?.body !== null ? String(init.body) : null;
3486
+ const headers = {};
3487
+ if (init?.headers) {
3488
+ if (init.headers instanceof Headers) {
3489
+ init.headers.forEach((v2, k2) => {
3490
+ headers[k2] = v2;
3491
+ });
3492
+ } else if (typeof init.headers === "object") {
3493
+ Object.entries(init.headers).forEach(
3494
+ ([k2, v2]) => {
3495
+ headers[k2] = v2;
3496
+ }
3497
+ );
3498
+ }
3499
+ }
3500
+ networkCalls.push({
3501
+ type: "fetch",
3502
+ url,
3503
+ method,
3504
+ body,
3505
+ headers,
3506
+ timestamp: Date.now()
3507
+ });
3508
+ return new Response("", { status: 200, statusText: "OK" });
3509
+ });
3510
+ dom.window.navigator.sendBeacon = (url, data) => {
3511
+ const body = data !== void 0 && data !== null ? String(data) : null;
3512
+ networkCalls.push({ type: "beacon", url, body, timestamp: Date.now() });
3513
+ return true;
3514
+ };
3515
+ }
3516
+ function cleanupNetworkPolyfills(savedFetch) {
3517
+ global.fetch = savedFetch;
3624
3518
  }
3625
3519
 
3626
- // src/commands/push/index.ts
3520
+ // src/commands/push/prepare.ts
3627
3521
  init_cli_logger();
3628
- init_core();
3629
3522
  init_tmp();
3630
3523
  init_config();
3631
3524
  init_bundler();
3632
- import path12 from "path";
3633
- import { JSDOM, VirtualConsole } from "jsdom";
3634
- import fs13 from "fs-extra";
3525
+ import path15 from "path";
3526
+ import fs14 from "fs-extra";
3635
3527
  import { getPlatform as getPlatform3 } from "@walkeros/core";
3636
- import { schemas as schemas2 } from "@walkeros/core/dev";
3637
- import { Level as Level2 } from "@walkeros/core";
3528
+ async function prepareFlow(options) {
3529
+ const logger = createCLILogger({
3530
+ silent: options.silent,
3531
+ verbose: options.verbose
3532
+ });
3533
+ logger.debug("Loading flow configuration");
3534
+ const { flowSettings, buildOptions } = await loadFlowConfig(
3535
+ options.configPath,
3536
+ {
3537
+ flowName: options.flow,
3538
+ logger
3539
+ }
3540
+ );
3541
+ const platform = getPlatform3(flowSettings);
3542
+ const configDir = buildOptions.configDir || process.cwd();
3543
+ const overrides = buildOverrides(
3544
+ { simulate: options.simulate, mock: options.mock },
3545
+ flowSettings
3546
+ );
3547
+ if (overrides.destinations) {
3548
+ const { loadDestinationEnvs: loadDestinationEnvs2 } = await Promise.resolve().then(() => (init_env_loader(), env_loader_exports));
3549
+ const envs = await loadDestinationEnvs2(
3550
+ flowSettings.destinations ?? {},
3551
+ flowSettings.packages,
3552
+ configDir
3553
+ );
3554
+ for (const [destId, env] of Object.entries(envs)) {
3555
+ if (overrides.destinations[destId] && env.push) {
3556
+ overrides.destinations[destId].env = env.push;
3557
+ if (env.simulation && env.simulation.length > 0) {
3558
+ overrides.destinations[destId].simulation = env.simulation;
3559
+ }
3560
+ }
3561
+ }
3562
+ }
3563
+ logger.debug("Bundling flow configuration");
3564
+ const tempDir = getTmpPath(
3565
+ void 0,
3566
+ `push-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
3567
+ );
3568
+ await fs14.ensureDir(tempDir);
3569
+ const bundlePath = path15.join(tempDir, "bundle.mjs");
3570
+ const pushBuildOptions = {
3571
+ ...buildOptions,
3572
+ output: bundlePath,
3573
+ format: "esm",
3574
+ platform: platform === "web" ? "browser" : "node",
3575
+ skipWrapper: true
3576
+ // CLI imports ESM directly -- no platform wrapper
3577
+ };
3578
+ await bundleCore(flowSettings, pushBuildOptions, logger, false);
3579
+ logger.debug(`Bundle created: ${bundlePath}`);
3580
+ return {
3581
+ bundlePath,
3582
+ platform,
3583
+ overrides,
3584
+ flowSettings,
3585
+ configDir,
3586
+ cleanup: async () => {
3587
+ await fs14.remove(tempDir).catch(() => {
3588
+ });
3589
+ }
3590
+ };
3591
+ }
3592
+
3593
+ // src/commands/push/index.ts
3594
+ import { schemas as schemas3 } from "@walkeros/core/dev";
3595
+ function resolveBeforeChain(before, transformers, ingest, event) {
3596
+ if (!before) return [];
3597
+ const next = before;
3598
+ if (isRouteArray(next)) {
3599
+ const compiled = compileNext(next);
3600
+ const resolved = resolveNext(compiled, buildCacheContext(ingest, event));
3601
+ if (!resolved) return [];
3602
+ return Z(resolved, X(transformers));
3603
+ }
3604
+ return Z(
3605
+ next,
3606
+ X(transformers)
3607
+ );
3608
+ }
3638
3609
  async function pushCore(inputPath, event, options = {}) {
3639
3610
  const logger = createCLILogger({
3640
3611
  silent: options.silent,
@@ -3643,53 +3614,45 @@ async function pushCore(inputPath, event, options = {}) {
3643
3614
  const startTime = Date.now();
3644
3615
  let tempDir;
3645
3616
  try {
3646
- const eventResult = schemas2.PartialEventSchema.safeParse(event);
3647
- if (!eventResult.success) {
3648
- const errors = eventResult.error.issues.map((issue) => `${String(issue.path.join("."))}: ${issue.message}`).join(", ");
3649
- throw new Error(`Invalid event: ${errors}`);
3650
- }
3651
- const parsedEvent = eventResult.data;
3652
- if (!parsedEvent.name) {
3653
- throw new Error('Invalid event: Missing required "name" property');
3654
- }
3655
- const validatedEvent = {
3656
- name: parsedEvent.name,
3657
- data: parsedEvent.data || {}
3658
- };
3659
- if (!validatedEvent.name.includes(" ")) {
3660
- logger.info(
3661
- `Warning: Event name "${validatedEvent.name}" should follow "ENTITY ACTION" format (e.g., "page view")`
3662
- );
3663
- }
3664
3617
  logger.debug("Detecting input type");
3665
3618
  const detected = await detectInput(
3666
3619
  inputPath,
3667
3620
  options.platform
3668
3621
  );
3669
3622
  let result;
3623
+ let snapshotCode;
3624
+ if (options.snapshot) {
3625
+ snapshotCode = await loadConfig(options.snapshot, {
3626
+ json: false
3627
+ });
3628
+ logger.debug(`Snapshot loaded (${snapshotCode.length} bytes)`);
3629
+ }
3670
3630
  if (detected.type === "config") {
3671
3631
  result = await executeConfigPush(
3672
3632
  {
3673
3633
  config: inputPath,
3674
3634
  flow: options.flow,
3675
- verbose: options.verbose
3635
+ verbose: options.verbose,
3636
+ mock: options.mock
3676
3637
  },
3677
- validatedEvent,
3638
+ event,
3678
3639
  logger,
3679
3640
  (dir) => {
3680
3641
  tempDir = dir;
3681
- }
3642
+ },
3643
+ snapshotCode
3682
3644
  );
3683
3645
  } else {
3684
3646
  result = await executeBundlePush(
3685
3647
  detected.content,
3686
3648
  detected.platform,
3687
- validatedEvent,
3649
+ event,
3688
3650
  logger,
3689
3651
  (dir) => {
3690
3652
  tempDir = dir;
3691
3653
  },
3692
- { logger: { level: options.verbose ? Level2.DEBUG : Level2.ERROR } }
3654
+ void 0,
3655
+ snapshotCode
3693
3656
  );
3694
3657
  }
3695
3658
  return result;
@@ -3701,7 +3664,7 @@ async function pushCore(inputPath, event, options = {}) {
3701
3664
  };
3702
3665
  } finally {
3703
3666
  if (tempDir) {
3704
- await fs13.remove(tempDir).catch(() => {
3667
+ await fs15.remove(tempDir).catch(() => {
3705
3668
  });
3706
3669
  }
3707
3670
  }
@@ -3712,21 +3675,63 @@ async function pushCommand(options) {
3712
3675
  try {
3713
3676
  let config;
3714
3677
  if (isStdinPiped() && !options.config) {
3715
- const stdinContent = await readStdin();
3716
- const tmpPath = getTmpPath(void 0, "stdin-push.json");
3717
- await fs13.ensureDir(path12.dirname(tmpPath));
3718
- await fs13.writeFile(tmpPath, stdinContent, "utf-8");
3719
- config = tmpPath;
3678
+ config = await readStdinToTempFile("push");
3720
3679
  } else {
3721
3680
  config = options.config || "bundle.config.json";
3722
3681
  }
3723
- const result = await push(config, options.event, {
3724
- flow: options.flow,
3725
- json: options.json,
3726
- verbose: options.verbose,
3727
- silent: options.silent,
3728
- platform: options.platform
3729
- });
3682
+ let resolvedEvent = options.event;
3683
+ if (typeof options.event === "string") {
3684
+ resolvedEvent = await loadJsonFromSource(options.event, {
3685
+ name: "event"
3686
+ });
3687
+ }
3688
+ const simulateFlag = options.simulate?.[0];
3689
+ let result;
3690
+ if (simulateFlag?.startsWith("source.")) {
3691
+ result = await simulateSource(config, resolvedEvent, {
3692
+ sourceId: simulateFlag.replace("source.", ""),
3693
+ flow: options.flow,
3694
+ silent: options.silent,
3695
+ verbose: options.verbose,
3696
+ snapshot: options.snapshot
3697
+ });
3698
+ } else if (simulateFlag?.startsWith("transformer.")) {
3699
+ result = await simulateTransformer(
3700
+ config,
3701
+ resolvedEvent,
3702
+ {
3703
+ transformerId: simulateFlag.replace("transformer.", ""),
3704
+ flow: options.flow,
3705
+ mock: options.mock,
3706
+ silent: options.silent,
3707
+ verbose: options.verbose,
3708
+ snapshot: options.snapshot
3709
+ }
3710
+ );
3711
+ } else if (simulateFlag?.startsWith("destination.")) {
3712
+ result = await simulateDestination(
3713
+ config,
3714
+ resolvedEvent,
3715
+ {
3716
+ destinationId: simulateFlag.replace("destination.", ""),
3717
+ flow: options.flow,
3718
+ mock: options.mock,
3719
+ silent: options.silent,
3720
+ verbose: options.verbose,
3721
+ snapshot: options.snapshot
3722
+ }
3723
+ );
3724
+ } else {
3725
+ result = await push(config, resolvedEvent, {
3726
+ flow: options.flow,
3727
+ json: options.json,
3728
+ verbose: options.verbose,
3729
+ silent: options.silent,
3730
+ platform: options.platform,
3731
+ mock: options.mock,
3732
+ snapshot: options.snapshot
3733
+ });
3734
+ }
3730
3735
  const duration = Date.now() - startTime;
3731
3736
  let output;
3732
3737
  if (options.json) {
@@ -3782,158 +3787,438 @@ async function push(configOrPath, event, options = {}) {
3782
3787
  "push() currently only supports config file paths. Config object support will be added in a future version. Please provide a path to a configuration file."
3783
3788
  );
3784
3789
  }
3785
- let resolvedEvent = event;
3786
- if (typeof event === "string") {
3787
- resolvedEvent = await loadJsonFromSource(event, { name: "event" });
3790
+ const parsed = schemas3.PartialEventSchema.safeParse(event);
3791
+ if (!parsed.success) {
3792
+ return {
3793
+ success: false,
3794
+ duration: 0,
3795
+ error: `Invalid event: ${parsed.error.issues.map((i2) => `${i2.path.join(".")}: ${i2.message}`).join(", ")}`
3796
+ };
3788
3797
  }
3789
- return await pushCore(configOrPath, resolvedEvent, {
3798
+ return pushCore(configOrPath, event, {
3790
3799
  json: options.json ?? false,
3791
3800
  verbose: options.verbose ?? false,
3792
3801
  silent: options.silent ?? false,
3793
3802
  flow: options.flow,
3794
- platform: options.platform
3803
+ platform: options.platform,
3804
+ mock: options.mock,
3805
+ snapshot: options.snapshot
3795
3806
  });
3796
3807
  }
3797
- async function executeConfigPush(options, validatedEvent, logger, setTempDir) {
3808
+ async function executeConfigPush(options, validatedEvent, logger, setTempDir, snapshotCode) {
3798
3809
  logger.debug("Loading flow configuration");
3799
3810
  const { flowSettings, buildOptions } = await loadFlowConfig(options.config, {
3800
3811
  flowName: options.flow,
3801
3812
  logger
3802
3813
  });
3803
- const platform = getPlatform3(flowSettings);
3814
+ const platform = getPlatform4(flowSettings);
3815
+ const overrides = buildOverrides(
3816
+ { mock: options.mock },
3817
+ flowSettings
3818
+ );
3819
+ if (overrides.destinations) {
3820
+ const { loadDestinationEnvs: loadDestinationEnvs2 } = await Promise.resolve().then(() => (init_env_loader(), env_loader_exports));
3821
+ const configDir = buildOptions.configDir || process.cwd();
3822
+ const envs = await loadDestinationEnvs2(
3823
+ flowSettings.destinations ?? {},
3824
+ flowSettings.packages,
3825
+ configDir
3826
+ );
3827
+ for (const [destId, env] of Object.entries(envs)) {
3828
+ if (overrides.destinations[destId] && env.push) {
3829
+ overrides.destinations[destId].env = env.push;
3830
+ if (env.simulation && env.simulation.length > 0) {
3831
+ overrides.destinations[destId].simulation = env.simulation;
3832
+ }
3833
+ }
3834
+ }
3835
+ }
3804
3836
  logger.debug("Bundling flow configuration");
3805
- const configDir = buildOptions.configDir || process.cwd();
3806
3837
  const tempDir = getTmpPath(
3807
3838
  void 0,
3808
3839
  `push-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
3809
3840
  );
3810
3841
  setTempDir(tempDir);
3811
- await fs13.ensureDir(tempDir);
3812
- const tempPath = path12.join(
3813
- tempDir,
3814
- `bundle.${platform === "web" ? "js" : "mjs"}`
3815
- );
3842
+ await fs15.ensureDir(tempDir);
3843
+ const tempPath = path16.join(tempDir, "bundle.mjs");
3816
3844
  const pushBuildOptions = {
3817
3845
  ...buildOptions,
3818
3846
  output: tempPath,
3819
- format: platform === "web" ? "iife" : "esm",
3847
+ format: "esm",
3820
3848
  platform: platform === "web" ? "browser" : "node",
3821
- ...platform === "web" && {
3822
- windowCollector: "collector",
3823
- windowElb: "elb"
3824
- }
3849
+ skipWrapper: true
3850
+ // CLI imports ESM directly — no platform wrapper
3825
3851
  };
3826
3852
  await bundleCore(flowSettings, pushBuildOptions, logger, false);
3827
3853
  logger.debug(`Bundle created: ${tempPath}`);
3828
- if (platform === "web") {
3829
- logger.debug("Executing in web environment (JSDOM)");
3830
- return executeWebPush(tempPath, validatedEvent, logger);
3831
- } else if (platform === "server") {
3832
- logger.debug("Executing in server environment (Node.js)");
3833
- return executeServerPush(tempPath, validatedEvent, logger, 6e4, {
3834
- logger: { level: options.verbose ? Level2.DEBUG : Level2.ERROR }
3835
- });
3836
- } else {
3837
- throw new Error(`Unsupported platform: ${platform}`);
3838
- }
3854
+ logger.debug(
3855
+ `Executing in ${platform} environment (${platform === "web" ? "JSDOM" : "Node.js"})`
3856
+ );
3857
+ return executeDestinationPush(
3858
+ tempPath,
3859
+ validatedEvent,
3860
+ logger,
3861
+ platform,
3862
+ overrides,
3863
+ snapshotCode,
3864
+ platform === "server" ? 6e4 : void 0
3865
+ );
3839
3866
  }
3840
- async function executeBundlePush(bundleContent, platform, validatedEvent, logger, setTempDir, context = {}) {
3867
+ async function executeBundlePush(bundleContent, platform, validatedEvent, logger, setTempDir, overrides = {}, snapshotCode) {
3841
3868
  const tempDir = getTmpPath(
3842
3869
  void 0,
3843
3870
  `push-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
3844
3871
  );
3845
3872
  setTempDir(tempDir);
3846
- await fs13.ensureDir(tempDir);
3847
- const tempPath = path12.join(
3848
- tempDir,
3849
- `bundle.${platform === "server" ? "mjs" : "js"}`
3850
- );
3851
- await fs13.writeFile(tempPath, bundleContent, "utf8");
3873
+ await fs15.ensureDir(tempDir);
3874
+ const tempPath = path16.join(tempDir, "bundle.mjs");
3875
+ await fs15.writeFile(tempPath, bundleContent, "utf8");
3852
3876
  logger.debug(`Bundle written to: ${tempPath}`);
3853
- if (platform === "web") {
3854
- logger.debug("Executing in web environment (JSDOM)");
3855
- return executeWebPush(tempPath, validatedEvent, logger);
3856
- } else {
3857
- logger.debug("Executing in server environment (Node.js)");
3858
- return executeServerPush(tempPath, validatedEvent, logger, 6e4, context);
3877
+ logger.debug(
3878
+ `Executing in ${platform} environment (${platform === "web" ? "JSDOM" : "Node.js"})`
3879
+ );
3880
+ return executeDestinationPush(
3881
+ tempPath,
3882
+ validatedEvent,
3883
+ logger,
3884
+ platform,
3885
+ overrides,
3886
+ snapshotCode,
3887
+ platform === "server" ? 6e4 : void 0
3888
+ );
3889
+ }
3890
+ async function executeDestinationPush(esmPath, event, logger, platform, overrides, snapshotCode, timeout) {
3891
+ const startTime = Date.now();
3892
+ const networkCalls = [];
3893
+ return withFlowContext(
3894
+ { esmPath, platform, logger, snapshotCode, timeout, networkCalls, asyncDrain: { timeout: 5e3 } },
3895
+ async (module) => {
3896
+ const config = module.wireConfig(module.__configData ?? void 0);
3897
+ applyOverrides(config, overrides || {});
3898
+ const result = await module.startFlow(config);
3899
+ if (!result?.collector?.push)
3900
+ throw new Error("Invalid bundle: collector missing push");
3901
+ const collector = result.collector;
3902
+ logger.info(`Pushing event: ${event.name}`);
3903
+ const elbResult = await collector.push(event);
3904
+ await collector.command("shutdown");
3905
+ return {
3906
+ success: true,
3907
+ elbResult,
3908
+ ...networkCalls.length > 0 ? { networkCalls } : {},
3909
+ duration: Date.now() - startTime
3910
+ };
3911
+ }
3912
+ );
3913
+ }
3914
+ async function simulateSource(configPath, input, options) {
3915
+ const startTime = Date.now();
3916
+ const prepared = await prepareFlow({
3917
+ configPath,
3918
+ flow: options.flow,
3919
+ simulate: ["source." + options.sourceId],
3920
+ silent: options.silent,
3921
+ verbose: options.verbose
3922
+ });
3923
+ try {
3924
+ const logger = createCLILogger({
3925
+ silent: options.silent,
3926
+ verbose: options.verbose
3927
+ });
3928
+ const sourceConfig = (prepared.flowSettings.sources ?? {})[options.sourceId];
3929
+ if (!sourceConfig?.package) {
3930
+ throw new Error(
3931
+ `Source "${options.sourceId}" has no package defined`
3932
+ );
3933
+ }
3934
+ const devPath = resolvePackageImportPath(
3935
+ sourceConfig.package,
3936
+ prepared.flowSettings.packages,
3937
+ prepared.configDir,
3938
+ "/dev"
3939
+ );
3940
+ const devModule = await import(devPath);
3941
+ const createTrigger = devModule.examples?.createTrigger || devModule.default?.examples?.createTrigger;
3942
+ if (!createTrigger) {
3943
+ throw new Error(
3944
+ `Source package "${sourceConfig.package}" has no createTrigger in /dev export`
3945
+ );
3946
+ }
3947
+ let snapshotCode;
3948
+ if (options.snapshot) {
3949
+ snapshotCode = await loadConfig(options.snapshot, {
3950
+ json: false
3951
+ });
3952
+ logger.debug(`Snapshot loaded (${snapshotCode.length} bytes)`);
3953
+ }
3954
+ const networkCalls = [];
3955
+ return await withFlowContext(
3956
+ {
3957
+ esmPath: prepared.bundlePath,
3958
+ platform: prepared.platform,
3959
+ logger,
3960
+ snapshotCode,
3961
+ networkCalls
3962
+ },
3963
+ async (module) => {
3964
+ const config = module.wireConfig(module.__configData ?? void 0);
3965
+ applyOverrides(config, prepared.overrides);
3966
+ const captured = [];
3967
+ config.hooks = {
3968
+ ...config.hooks || {},
3969
+ prePush: ({ fn }, event) => {
3970
+ captured.push({ event, timestamp: Date.now() });
3971
+ return { ok: true };
3972
+ }
3973
+ };
3974
+ const instance = await createTrigger(config, { sourceId: options.sourceId });
3975
+ const { trigger } = instance;
3976
+ logger.info("Simulating source");
3977
+ const inputRecord = input ?? {};
3978
+ const content = inputRecord.content ?? input;
3979
+ const triggerOpts = inputRecord.trigger;
3980
+ await trigger(triggerOpts?.type, triggerOpts?.options)(content);
3981
+ if (instance.flow?.collector?.command) {
3982
+ await instance.flow.collector.command("shutdown");
3983
+ }
3984
+ return {
3985
+ success: true,
3986
+ ...captured.length > 0 ? { captured } : {},
3987
+ ...networkCalls.length > 0 ? { networkCalls } : {},
3988
+ duration: Date.now() - startTime
3989
+ };
3990
+ }
3991
+ );
3992
+ } catch (error) {
3993
+ return {
3994
+ success: false,
3995
+ duration: Date.now() - startTime,
3996
+ error: getErrorMessage(error)
3997
+ };
3998
+ } finally {
3999
+ await prepared.cleanup();
3859
4000
  }
3860
4001
  }
3861
- async function executeWebPush(bundlePath, event, logger) {
4002
+ async function simulateTransformer(configPath, event, options) {
3862
4003
  const startTime = Date.now();
4004
+ const parsed = schemas3.PartialEventSchema.safeParse(event);
4005
+ if (!parsed.success) {
4006
+ return {
4007
+ success: false,
4008
+ duration: 0,
4009
+ error: parsed.error.message
4010
+ };
4011
+ }
4012
+ const prepared = await prepareFlow({
4013
+ configPath,
4014
+ flow: options.flow,
4015
+ simulate: ["transformer." + options.transformerId],
4016
+ mock: options.mock,
4017
+ silent: options.silent,
4018
+ verbose: options.verbose
4019
+ });
3863
4020
  try {
3864
- const virtualConsole = new VirtualConsole();
3865
- const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>", {
3866
- url: "http://localhost",
3867
- runScripts: "dangerously",
3868
- resources: "usable",
3869
- virtualConsole
4021
+ const logger = createCLILogger({
4022
+ silent: options.silent,
4023
+ verbose: options.verbose
3870
4024
  });
3871
- const { window } = dom;
3872
- logger.debug("Loading bundle...");
3873
- const bundleCode = await fs13.readFile(bundlePath, "utf8");
3874
- window.eval(bundleCode);
3875
- logger.debug("Waiting for collector...");
3876
- await waitForWindowProperty(
3877
- window,
3878
- "collector",
3879
- 5e3
4025
+ let snapshotCode;
4026
+ if (options.snapshot) {
4027
+ snapshotCode = await loadConfig(options.snapshot, {
4028
+ json: false
4029
+ });
4030
+ logger.debug(`Snapshot loaded (${snapshotCode.length} bytes)`);
4031
+ }
4032
+ const networkCalls = [];
4033
+ return await withFlowContext(
4034
+ {
4035
+ esmPath: prepared.bundlePath,
4036
+ platform: prepared.platform,
4037
+ logger,
4038
+ snapshotCode,
4039
+ networkCalls
4040
+ },
4041
+ async (module) => {
4042
+ const config = module.wireConfig(module.__configData ?? void 0);
4043
+ applyOverrides(config, prepared.overrides);
4044
+ if (config.sources) config.sources = {};
4045
+ if (config.destinations) config.destinations = {};
4046
+ const result = await module.startFlow(config);
4047
+ if (!result?.collector)
4048
+ throw new Error("Invalid bundle: collector not available");
4049
+ const collector = result.collector;
4050
+ const transformer = collector.transformers?.[options.transformerId];
4051
+ if (!transformer) {
4052
+ throw new Error(
4053
+ `Transformer "${options.transformerId}" not found in collector. Available: ${Object.keys(collector.transformers || {}).join(", ") || "none"}`
4054
+ );
4055
+ }
4056
+ const initialized = await ee(
4057
+ collector,
4058
+ transformer,
4059
+ options.transformerId
4060
+ );
4061
+ if (!initialized) {
4062
+ throw new Error(
4063
+ `Transformer "${options.transformerId}" failed to initialize`
4064
+ );
4065
+ }
4066
+ const inputEvent = event;
4067
+ const ingest = createIngest(options.transformerId);
4068
+ const captured = [];
4069
+ captured.push({ event: { ...inputEvent }, timestamp: Date.now() });
4070
+ logger.info(`Simulating transformer: ${options.transformerId}`);
4071
+ let processedEvent = inputEvent;
4072
+ const before = transformer.config.before;
4073
+ if (before && collector.transformers) {
4074
+ const beforeChainIds = resolveBeforeChain(
4075
+ before,
4076
+ collector.transformers,
4077
+ ingest,
4078
+ processedEvent
4079
+ );
4080
+ if (beforeChainIds.length > 0) {
4081
+ const beforeResult = await te(
4082
+ collector,
4083
+ collector.transformers,
4084
+ beforeChainIds,
4085
+ processedEvent,
4086
+ ingest,
4087
+ void 0,
4088
+ `transformer.${options.transformerId}.before`
4089
+ );
4090
+ if (beforeResult === null) {
4091
+ captured.push({ event: null, timestamp: Date.now() });
4092
+ await collector.command("shutdown");
4093
+ return {
4094
+ success: true,
4095
+ captured,
4096
+ duration: Date.now() - startTime
4097
+ };
4098
+ }
4099
+ processedEvent = Array.isArray(beforeResult) ? beforeResult[0] : beforeResult;
4100
+ }
4101
+ }
4102
+ const pushResult = await ne(
4103
+ collector,
4104
+ transformer,
4105
+ options.transformerId,
4106
+ processedEvent,
4107
+ ingest
4108
+ );
4109
+ if (pushResult === false) {
4110
+ captured.push({ event: null, timestamp: Date.now() });
4111
+ } else if (Array.isArray(pushResult)) {
4112
+ for (const r2 of pushResult) {
4113
+ captured.push({
4114
+ event: r2.event || processedEvent,
4115
+ timestamp: Date.now()
4116
+ });
4117
+ }
4118
+ } else if (pushResult && typeof pushResult === "object" && pushResult.event) {
4119
+ captured.push({ event: pushResult.event, timestamp: Date.now() });
4120
+ } else {
4121
+ captured.push({ event: processedEvent, timestamp: Date.now() });
4122
+ }
4123
+ await collector.command("shutdown");
4124
+ return {
4125
+ success: true,
4126
+ captured,
4127
+ ...networkCalls.length > 0 ? { networkCalls } : {},
4128
+ duration: Date.now() - startTime
4129
+ };
4130
+ }
3880
4131
  );
3881
- const windowObj = window;
3882
- const collector = windowObj.collector;
3883
- logger.info(`Pushing event: ${event.name}`);
3884
- const elbResult = await collector.push({
3885
- name: event.name,
3886
- data: event.data
3887
- });
3888
- return {
3889
- success: true,
3890
- elbResult,
3891
- duration: Date.now() - startTime
3892
- };
3893
4132
  } catch (error) {
3894
4133
  return {
3895
4134
  success: false,
3896
4135
  duration: Date.now() - startTime,
3897
4136
  error: getErrorMessage(error)
3898
4137
  };
4138
+ } finally {
4139
+ await prepared.cleanup();
3899
4140
  }
3900
4141
  }
3901
- async function executeServerPush(bundlePath, event, logger, timeout = 6e4, context = {}) {
4142
+ async function simulateDestination(configPath, event, options) {
3902
4143
  const startTime = Date.now();
3903
- let timer;
4144
+ const parsed = schemas3.PartialEventSchema.safeParse(event);
4145
+ if (!parsed.success) {
4146
+ return {
4147
+ success: false,
4148
+ duration: 0,
4149
+ error: parsed.error.message
4150
+ };
4151
+ }
4152
+ const prepared = await prepareFlow({
4153
+ configPath,
4154
+ flow: options.flow,
4155
+ simulate: ["destination." + options.destinationId],
4156
+ mock: options.mock,
4157
+ silent: options.silent,
4158
+ verbose: options.verbose
4159
+ });
3904
4160
  try {
3905
- const timeoutPromise = new Promise((_2, reject) => {
3906
- timer = setTimeout(
3907
- () => reject(new Error(`Server push timeout after ${timeout}ms`)),
3908
- timeout
3909
- );
4161
+ const logger = createCLILogger({
4162
+ silent: options.silent,
4163
+ verbose: options.verbose
3910
4164
  });
3911
- const executePromise = (async () => {
3912
- logger.debug("Importing bundle...");
3913
- const flowModule = await import(bundlePath);
3914
- if (!flowModule.default || typeof flowModule.default !== "function") {
3915
- throw new Error("Bundle does not export default factory function");
3916
- }
3917
- logger.debug("Calling factory function...");
3918
- const result = await flowModule.default(context);
3919
- if (!result || !result.collector || typeof result.collector.push !== "function") {
3920
- throw new Error(
3921
- "Factory function did not return valid result with collector"
3922
- );
3923
- }
3924
- const { collector } = result;
3925
- logger.info(`Pushing event: ${event.name}`);
3926
- const elbResult = await collector.push({
3927
- name: event.name,
3928
- data: event.data
4165
+ let snapshotCode;
4166
+ if (options.snapshot) {
4167
+ snapshotCode = await loadConfig(options.snapshot, {
4168
+ json: false
3929
4169
  });
3930
- return {
3931
- success: true,
3932
- elbResult,
3933
- duration: Date.now() - startTime
3934
- };
3935
- })();
3936
- return await Promise.race([executePromise, timeoutPromise]);
4170
+ }
4171
+ const networkCalls = [];
4172
+ return await withFlowContext(
4173
+ {
4174
+ esmPath: prepared.bundlePath,
4175
+ platform: prepared.platform,
4176
+ logger,
4177
+ snapshotCode,
4178
+ networkCalls
4179
+ },
4180
+ async (module) => {
4181
+ const config = module.wireConfig(module.__configData ?? void 0);
4182
+ applyOverrides(config, prepared.overrides);
4183
+ const destOverride = prepared.overrides.destinations?.[options.destinationId];
4184
+ let trackedCalls = [];
4185
+ if (destOverride?.simulation?.length) {
4186
+ const destinations = config.destinations;
4187
+ const destConfig = destinations[options.destinationId]?.config;
4188
+ if (destConfig?.env) {
4189
+ const combined = {
4190
+ ...destConfig.env,
4191
+ simulation: destOverride.simulation
4192
+ };
4193
+ const { wrappedEnv, calls } = Ge(combined);
4194
+ destConfig.env = wrappedEnv;
4195
+ trackedCalls = calls;
4196
+ }
4197
+ }
4198
+ if (config.sources) config.sources = {};
4199
+ const result = await module.startFlow(config);
4200
+ if (!result?.collector)
4201
+ throw new Error("Invalid bundle: collector not available");
4202
+ const collector = result.collector;
4203
+ if (!collector.destinations[options.destinationId] && !collector.pending.destinations[options.destinationId]) {
4204
+ throw new Error(
4205
+ `Destination "${options.destinationId}" not found in collector. Available: ${Object.keys(collector.destinations || {}).join(", ") || "none"}`
4206
+ );
4207
+ }
4208
+ logger.info(`Simulating destination: ${options.destinationId}`);
4209
+ const elbResult = await collector.push(event, {
4210
+ include: [options.destinationId]
4211
+ });
4212
+ await collector.command("shutdown");
4213
+ return {
4214
+ success: true,
4215
+ elbResult,
4216
+ ...trackedCalls.length > 0 ? { usage: { [options.destinationId]: trackedCalls } } : {},
4217
+ ...networkCalls.length > 0 ? { networkCalls } : {},
4218
+ duration: Date.now() - startTime
4219
+ };
4220
+ }
4221
+ );
3937
4222
  } catch (error) {
3938
4223
  return {
3939
4224
  success: false,
@@ -3941,35 +4226,17 @@ async function executeServerPush(bundlePath, event, logger, timeout = 6e4, conte
3941
4226
  error: getErrorMessage(error)
3942
4227
  };
3943
4228
  } finally {
3944
- clearTimeout(timer);
4229
+ await prepared.cleanup();
3945
4230
  }
3946
4231
  }
3947
- function waitForWindowProperty(window, prop, timeout = 5e3) {
3948
- return new Promise((resolve2, reject) => {
3949
- const start = Date.now();
3950
- const check = () => {
3951
- if (window[prop] !== void 0) {
3952
- resolve2();
3953
- } else if (Date.now() - start > timeout) {
3954
- reject(
3955
- new Error(
3956
- `Timeout waiting for window.${prop}. IIFE may have failed to execute.`
3957
- )
3958
- );
3959
- } else {
3960
- setImmediate(check);
3961
- }
3962
- };
3963
- check();
3964
- });
3965
- }
3966
4232
 
3967
4233
  // src/commands/run/index.ts
3968
4234
  init_cli_logger();
3969
4235
  init_core();
4236
+ init_tmp();
3970
4237
  init_config_file();
3971
4238
  init_auth();
3972
- import path14 from "path";
4239
+ import path18 from "path";
3973
4240
  import { writeFileSync as writeFileSync5 } from "fs";
3974
4241
  import { homedir as homedir2 } from "os";
3975
4242
  import { join as join4 } from "path";
@@ -4017,12 +4284,12 @@ async function resolveBundle(bundleEnv) {
4017
4284
  return { path: bundleEnv, source: "file" };
4018
4285
  }
4019
4286
  if (isUrl2(bundleEnv)) {
4020
- const path15 = await fetchBundle(bundleEnv, writePath);
4021
- return { path: path15, source: "url" };
4287
+ const path19 = await fetchBundle(bundleEnv, writePath);
4288
+ return { path: path19, source: "url" };
4022
4289
  }
4023
4290
  if (isStdinPiped()) {
4024
- const path15 = await readBundleFromStdin(writePath);
4025
- return { path: path15, source: "stdin" };
4291
+ const path19 = await readBundleFromStdin(writePath);
4292
+ return { path: path19, source: "stdin" };
4026
4293
  }
4027
4294
  return { path: bundleEnv, source: "file" };
4028
4295
  }
@@ -4186,12 +4453,14 @@ function validatePort(port) {
4186
4453
  init_utils3();
4187
4454
 
4188
4455
  // src/commands/run/pipeline.ts
4456
+ init_tmp();
4189
4457
  import { writeFileSync as writeFileSync4 } from "fs";
4458
+ import fs17 from "fs-extra";
4190
4459
 
4191
4460
  // src/runtime/health-server.ts
4192
4461
  import http from "http";
4193
4462
  function createHealthServer(port, logger) {
4194
- return new Promise((resolve2, reject) => {
4463
+ return new Promise((resolve3, reject) => {
4195
4464
  let flowHandler = null;
4196
4465
  const server = http.createServer((req, res) => {
4197
4466
  if (req.url === "/health" && req.method === "GET") {
@@ -4218,7 +4487,7 @@ function createHealthServer(port, logger) {
4218
4487
  server.headersTimeout = 1e4;
4219
4488
  server.listen(port, "0.0.0.0", () => {
4220
4489
  logger.info(`Health server listening on port ${port}`);
4221
- resolve2({
4490
+ resolve3({
4222
4491
  server,
4223
4492
  setFlowHandler(handler) {
4224
4493
  flowHandler = handler;
@@ -4233,28 +4502,45 @@ function createHealthServer(port, logger) {
4233
4502
  }
4234
4503
 
4235
4504
  // src/runtime/runner.ts
4236
- import { pathToFileURL } from "url";
4237
- import { resolve, dirname as dirname2 } from "path";
4238
- async function loadFlow(file, config, logger, loggerConfig, healthServer) {
4505
+ import { resolve as resolve2, dirname as dirname2 } from "path";
4506
+
4507
+ // src/runtime/load-bundle.ts
4508
+ import { resolve } from "path";
4509
+ import { pathToFileURL as pathToFileURL2 } from "url";
4510
+ async function loadBundle(file, context, logger) {
4239
4511
  const absolutePath = resolve(file);
4240
- const flowDir = dirname2(absolutePath);
4241
- process.chdir(flowDir);
4242
- const fileUrl = pathToFileURL(absolutePath).href;
4512
+ const fileUrl = pathToFileURL2(absolutePath).href;
4513
+ logger?.debug?.(`Importing bundle: ${absolutePath}`);
4243
4514
  const module = await import(`${fileUrl}?t=${Date.now()}`);
4244
4515
  if (!module.default || typeof module.default !== "function") {
4245
4516
  throw new Error(
4246
- `Invalid flow bundle: ${file} must export a default function`
4517
+ `Invalid bundle: ${file} must export a default factory function`
4247
4518
  );
4248
4519
  }
4520
+ logger?.debug?.("Calling factory function...");
4521
+ const result = await module.default(context ?? {});
4522
+ if (!result || !result.collector || typeof result.collector.push !== "function") {
4523
+ throw new Error(
4524
+ `Invalid bundle: factory must return { collector } with a push function`
4525
+ );
4526
+ }
4527
+ return {
4528
+ collector: result.collector,
4529
+ ...typeof result.httpHandler === "function" ? { httpHandler: result.httpHandler } : {}
4530
+ };
4531
+ }
4532
+
4533
+ // src/runtime/runner.ts
4534
+ async function loadFlow(file, config, logger, loggerConfig, healthServer) {
4535
+ const absolutePath = resolve2(file);
4536
+ const flowDir = dirname2(absolutePath);
4537
+ process.chdir(flowDir);
4249
4538
  const flowContext = {
4250
4539
  ...config,
4251
4540
  ...loggerConfig ? { logger: loggerConfig } : {},
4252
- ...healthServer ? { externalServer: true } : {}
4541
+ ...healthServer ? { sourceSettings: { port: void 0 } } : {}
4253
4542
  };
4254
- const result = await module.default(flowContext);
4255
- if (!result || !result.collector) {
4256
- throw new Error(`Invalid flow bundle: ${file} must return { collector }`);
4257
- }
4543
+ const result = await loadBundle(absolutePath, flowContext, logger);
4258
4544
  if (healthServer && typeof result.httpHandler === "function") {
4259
4545
  healthServer.setFlowHandler(result.httpHandler);
4260
4546
  }
@@ -4520,6 +4806,8 @@ async function runPipeline(options) {
4520
4806
  logger.info(`Port: ${port}`);
4521
4807
  let heartbeat = null;
4522
4808
  let poller = null;
4809
+ let currentBundleCleanup;
4810
+ let currentConfigPath;
4523
4811
  if (api) {
4524
4812
  heartbeat = createHeartbeat(
4525
4813
  {
@@ -4554,28 +4842,44 @@ async function runPipeline(options) {
4554
4842
  );
4555
4843
  return;
4556
4844
  }
4557
- const tmpConfigPath = `/tmp/walkeros-flow-${Date.now()}.json`;
4845
+ const tmpConfigPath = getTmpPath(
4846
+ void 0,
4847
+ `walkeros-flow-${Date.now()}.json`
4848
+ );
4558
4849
  writeFileSync4(
4559
4850
  tmpConfigPath,
4560
4851
  JSON.stringify(content, null, 2),
4561
4852
  "utf-8"
4562
4853
  );
4563
- const newBundle = await api.prepareBundleForRun(tmpConfigPath, {
4854
+ const newBundleResult = await api.prepareBundleForRun(tmpConfigPath, {
4564
4855
  verbose: false,
4565
4856
  silent: true,
4566
4857
  flowName: api.flowName
4567
4858
  });
4568
4859
  handle = await swapFlow(
4569
4860
  handle,
4570
- newBundle,
4861
+ newBundleResult.bundlePath,
4571
4862
  runtimeConfig,
4572
4863
  logger,
4573
4864
  loggerConfig,
4574
4865
  healthServer
4575
4866
  );
4576
- writeCache(api.cacheDir, newBundle, JSON.stringify(content), version);
4867
+ writeCache(
4868
+ api.cacheDir,
4869
+ newBundleResult.bundlePath,
4870
+ JSON.stringify(content),
4871
+ version
4872
+ );
4577
4873
  configVersion = version;
4578
4874
  if (heartbeat) heartbeat.updateConfigVersion(version);
4875
+ if (currentBundleCleanup)
4876
+ await currentBundleCleanup().catch(() => {
4877
+ });
4878
+ if (currentConfigPath)
4879
+ await fs17.remove(currentConfigPath).catch(() => {
4880
+ });
4881
+ currentBundleCleanup = newBundleResult.cleanup;
4882
+ currentConfigPath = tmpConfigPath;
4579
4883
  logger.info(`Hot-swapped to version ${version}`);
4580
4884
  }
4581
4885
  },
@@ -4597,6 +4901,10 @@ async function runPipeline(options) {
4597
4901
  await handle.collector.command("shutdown");
4598
4902
  }
4599
4903
  await healthServer.close();
4904
+ if (currentBundleCleanup) await currentBundleCleanup().catch(() => {
4905
+ });
4906
+ if (currentConfigPath) await fs17.remove(currentConfigPath).catch(() => {
4907
+ });
4600
4908
  logger.info("Shutdown complete");
4601
4909
  clearTimeout(forceTimer);
4602
4910
  process.exit(0);
@@ -4745,15 +5053,16 @@ async function resolveBundlePath(configInput, apiConfig, logger) {
4745
5053
  logger.info(`Bundle: ${resolved.path}`);
4746
5054
  }
4747
5055
  if (isPreBuiltConfig(resolved.path)) {
4748
- return path14.resolve(resolved.path);
5056
+ return path18.resolve(resolved.path);
4749
5057
  }
4750
5058
  const flowFile = validateFlowFile(resolved.path);
4751
5059
  logger.debug("Building flow bundle");
4752
- return lazyPrepareBundleForRun(flowFile, {
5060
+ const result = await lazyPrepareBundleForRun(flowFile, {
4753
5061
  verbose: false,
4754
5062
  silent: true,
4755
5063
  flowName: apiConfig?.flowName
4756
5064
  });
5065
+ return result.bundlePath;
4757
5066
  }
4758
5067
  if (apiConfig) {
4759
5068
  logger.info("Fetching config from API...");
@@ -4765,7 +5074,10 @@ async function resolveBundlePath(configInput, apiConfig, logger) {
4765
5074
  flowId: apiConfig.flowId
4766
5075
  });
4767
5076
  if (result.changed) {
4768
- const tmpConfigPath = `/tmp/walkeros-flow-${Date.now()}.json`;
5077
+ const tmpConfigPath = getTmpPath(
5078
+ void 0,
5079
+ `walkeros-flow-${Date.now()}.json`
5080
+ );
4769
5081
  writeFileSync5(
4770
5082
  tmpConfigPath,
4771
5083
  JSON.stringify(result.content, null, 2),
@@ -4773,7 +5085,7 @@ async function resolveBundlePath(configInput, apiConfig, logger) {
4773
5085
  );
4774
5086
  logger.info(`Config version: ${result.version}`);
4775
5087
  logger.info("Building flow...");
4776
- const bundlePath = await lazyPrepareBundleForRun(tmpConfigPath, {
5088
+ const bundleResult = await lazyPrepareBundleForRun(tmpConfigPath, {
4777
5089
  verbose: false,
4778
5090
  silent: true,
4779
5091
  flowName: apiConfig.flowName
@@ -4782,14 +5094,14 @@ async function resolveBundlePath(configInput, apiConfig, logger) {
4782
5094
  const { writeCache: writeCache2 } = await Promise.resolve().then(() => (init_cache(), cache_exports));
4783
5095
  writeCache2(
4784
5096
  apiConfig.cacheDir,
4785
- bundlePath,
5097
+ bundleResult.bundlePath,
4786
5098
  JSON.stringify(result.content),
4787
5099
  result.version
4788
5100
  );
4789
5101
  } catch {
4790
5102
  logger.debug("Cache write failed (non-critical)");
4791
5103
  }
4792
- return bundlePath;
5104
+ return bundleResult.bundlePath;
4793
5105
  }
4794
5106
  } catch (error) {
4795
5107
  logger.error(
@@ -4807,7 +5119,7 @@ async function resolveBundlePath(configInput, apiConfig, logger) {
4807
5119
  }
4808
5120
  const defaultFile = "server-collect.mjs";
4809
5121
  logger.debug(`No config specified, using default: ${defaultFile}`);
4810
- return path14.resolve(defaultFile);
5122
+ return path18.resolve(defaultFile);
4811
5123
  }
4812
5124
  async function run(options) {
4813
5125
  const startTime = Date.now();
@@ -4998,73 +5310,15 @@ function validateEntityActions(obj, prefix, errors) {
4998
5310
  }
4999
5311
 
5000
5312
  // src/commands/validate/validators/event.ts
5001
- import { schemas as schemas3 } from "@walkeros/core/dev";
5002
- var { PartialEventSchema } = schemas3;
5003
- function validateEvent(input) {
5004
- const errors = [];
5005
- const warnings = [];
5006
- const details = {};
5007
- const event = typeof input === "object" && input !== null ? input : {};
5008
- if (!("name" in event) || event.name === void 0) {
5009
- errors.push({
5010
- path: "name",
5011
- message: "Event must have a name field",
5012
- code: "MISSING_EVENT_NAME"
5013
- });
5014
- } else if (typeof event.name !== "string" || event.name.trim() === "") {
5015
- errors.push({
5016
- path: "name",
5017
- message: "Event name cannot be empty",
5018
- value: event.name,
5019
- code: "EMPTY_EVENT_NAME"
5020
- });
5021
- } else {
5022
- const name = event.name;
5023
- if (!name.includes(" ")) {
5024
- errors.push({
5025
- path: "name",
5026
- message: 'Event name must be "entity action" format with space (e.g., "page view")',
5027
- value: name,
5028
- code: "INVALID_EVENT_NAME"
5029
- });
5030
- details.entity = null;
5031
- details.action = null;
5032
- } else {
5033
- const parts = name.trim().split(/\s+/);
5034
- const action = parts.pop();
5035
- const entity = parts.join(" ");
5036
- details.entity = entity;
5037
- details.action = action;
5038
- }
5039
- }
5040
- const zodResult = PartialEventSchema.safeParse(input);
5041
- if (!zodResult.success) {
5042
- for (const issue of zodResult.error.issues) {
5043
- const path15 = issue.path.join(".");
5044
- if (path15 === "name") continue;
5045
- errors.push({
5046
- path: path15 || "root",
5047
- message: issue.message,
5048
- code: "SCHEMA_VALIDATION"
5049
- });
5050
- }
5051
- }
5052
- if (!event.consent) {
5053
- warnings.push({
5054
- path: "consent",
5055
- message: "No consent object provided",
5056
- suggestion: "Consider adding a consent object for GDPR/privacy compliance"
5057
- });
5058
- }
5059
- details.hasConsent = !!event.consent;
5060
- details.hasData = !!event.data;
5061
- details.hasContext = !!event.context;
5313
+ init_event_validation();
5314
+ function validateEvent2(input) {
5315
+ const result = validateEvent(input, "strict");
5062
5316
  return {
5063
- valid: errors.length === 0,
5317
+ valid: result.valid,
5064
5318
  type: "event",
5065
- errors,
5066
- warnings,
5067
- details
5319
+ errors: result.errors,
5320
+ warnings: result.warnings,
5321
+ details: result.details
5068
5322
  };
5069
5323
  }
5070
5324
 
@@ -5251,10 +5505,10 @@ function buildConnectionGraph(config) {
5251
5505
  function checkCompatibility(conn, errors, warnings) {
5252
5506
  const fromOuts = Object.entries(conn.from.examples).filter(([, ex]) => ex.out !== void 0 && ex.out !== false).map(([name, ex]) => ({ name, value: ex.out }));
5253
5507
  const toIns = Object.entries(conn.to.examples).filter(([, ex]) => ex.in !== void 0).map(([name, ex]) => ({ name, value: ex.in }));
5254
- const path15 = `${conn.from.type}.${conn.from.name} \u2192 ${conn.to.type}.${conn.to.name}`;
5508
+ const path19 = `${conn.from.type}.${conn.from.name} \u2192 ${conn.to.type}.${conn.to.name}`;
5255
5509
  if (fromOuts.length === 0 || toIns.length === 0) {
5256
5510
  warnings.push({
5257
- path: path15,
5511
+ path: path19,
5258
5512
  message: "Cannot check compatibility: missing out or in examples",
5259
5513
  suggestion: "Add out examples to the source step or in examples to the target step"
5260
5514
  });
@@ -5272,7 +5526,7 @@ function checkCompatibility(conn, errors, warnings) {
5272
5526
  }
5273
5527
  if (!hasMatch) {
5274
5528
  errors.push({
5275
- path: path15,
5529
+ path: path19,
5276
5530
  message: "No compatible out/in pair found between connected steps",
5277
5531
  code: "INCOMPATIBLE_EXAMPLES"
5278
5532
  });
@@ -5368,13 +5622,13 @@ function validateMapping(input) {
5368
5622
  import Ajv from "ajv";
5369
5623
  import { fetchPackageSchema } from "@walkeros/core";
5370
5624
  var SECTIONS = ["destinations", "sources", "transformers"];
5371
- function resolveEntry(path15, flowConfig) {
5625
+ function resolveEntry(path19, flowConfig) {
5372
5626
  const flows = flowConfig.flows;
5373
5627
  if (!flows || typeof flows !== "object") return "No flows found in config";
5374
5628
  const flowName = Object.keys(flows)[0];
5375
5629
  const flow = flows[flowName];
5376
5630
  if (!flow) return `Flow "${flowName}" is empty`;
5377
- const parts = path15.split(".");
5631
+ const parts = path19.split(".");
5378
5632
  if (parts.length === 2) {
5379
5633
  const [section, key] = parts;
5380
5634
  if (!SECTIONS.includes(section)) {
@@ -5411,15 +5665,15 @@ function resolveEntry(path15, flowConfig) {
5411
5665
  }
5412
5666
  return { section: matches[0].section, key, entry: matches[0].entry };
5413
5667
  }
5414
- return `Invalid path "${path15}". Use "section.key" or just "key"`;
5668
+ return `Invalid path "${path19}". Use "section.key" or just "key"`;
5415
5669
  }
5416
- async function validateEntry(path15, flowConfig) {
5417
- const resolved = resolveEntry(path15, flowConfig);
5670
+ async function validateEntry(path19, flowConfig) {
5671
+ const resolved = resolveEntry(path19, flowConfig);
5418
5672
  if (typeof resolved === "string") {
5419
5673
  return {
5420
5674
  valid: false,
5421
5675
  type: "entry",
5422
- errors: [{ path: path15, message: resolved, code: "ENTRY_VALIDATION" }],
5676
+ errors: [{ path: path19, message: resolved, code: "ENTRY_VALIDATION" }],
5423
5677
  warnings: [],
5424
5678
  details: {}
5425
5679
  };
@@ -5450,7 +5704,7 @@ async function validateEntry(path15, flowConfig) {
5450
5704
  type: "entry",
5451
5705
  errors: [
5452
5706
  {
5453
- path: path15,
5707
+ path: path19,
5454
5708
  message: error instanceof Error ? error.message : "Unknown error",
5455
5709
  code: "ENTRY_VALIDATION"
5456
5710
  }
@@ -5475,10 +5729,10 @@ async function validateEntry(path15, flowConfig) {
5475
5729
  const validate2 = ajv.compile(settingsSchema);
5476
5730
  const isValid = validate2(settings || {});
5477
5731
  if (!isValid) {
5478
- const errors = (validate2.errors || []).map((e2) => ({
5479
- path: e2.instancePath || "/",
5480
- message: e2.message || "Unknown error",
5481
- code: e2.keyword
5732
+ const errors = (validate2.errors || []).map((e) => ({
5733
+ path: e.instancePath || "/",
5734
+ message: e.message || "Unknown error",
5735
+ code: e.keyword
5482
5736
  }));
5483
5737
  return {
5484
5738
  valid: false,
@@ -5513,7 +5767,7 @@ async function validate(type, input, options = {}) {
5513
5767
  case "contract":
5514
5768
  return validateContract(resolved);
5515
5769
  case "event":
5516
- return validateEvent(resolved);
5770
+ return validateEvent2(resolved);
5517
5771
  case "flow":
5518
5772
  return validateFlow(resolved, { flow: options.flow });
5519
5773
  case "mapping":
@@ -5798,10 +6052,10 @@ async function deleteProject(options = {}) {
5798
6052
  throw new Error(error.error?.message || "Failed to delete project");
5799
6053
  return data ?? { success: true };
5800
6054
  }
5801
- async function handleResult(fn2, options) {
6055
+ async function handleResult(fn, options) {
5802
6056
  const logger = createCLILogger(options);
5803
6057
  try {
5804
- const result = await fn2();
6058
+ const result = await fn();
5805
6059
  await writeResult(JSON.stringify(result, null, 2), options);
5806
6060
  } catch (error) {
5807
6061
  logger.error(error instanceof Error ? error.message : String(error));
@@ -5957,10 +6211,10 @@ async function duplicateFlow(options) {
5957
6211
  if (error) throwApiError(error, "Failed to duplicate flow");
5958
6212
  return data;
5959
6213
  }
5960
- async function handleResult2(fn2, options) {
6214
+ async function handleResult2(fn, options) {
5961
6215
  const logger = createCLILogger(options);
5962
6216
  try {
5963
- const result = await fn2();
6217
+ const result = await fn();
5964
6218
  await writeResult(JSON.stringify(result, null, 2), options);
5965
6219
  } catch (error) {
5966
6220
  logger.error(error instanceof Error ? error.message : String(error));
@@ -6119,8 +6373,8 @@ async function deploy(options) {
6119
6373
  if (error) {
6120
6374
  try {
6121
6375
  throwApiError(error, "Failed to start deployment");
6122
- } catch (e2) {
6123
- if (e2 instanceof ApiError && e2.code === "AMBIGUOUS_CONFIG") {
6376
+ } catch (e) {
6377
+ if (e instanceof ApiError && e.code === "AMBIGUOUS_CONFIG") {
6124
6378
  const names = await getAvailableFlowNames({
6125
6379
  flowId: options.flowId,
6126
6380
  projectId
@@ -6131,7 +6385,7 @@ Available: ${names.join(", ")}`,
6131
6385
  { code: "AMBIGUOUS_CONFIG" }
6132
6386
  );
6133
6387
  }
6134
- throw e2;
6388
+ throw e;
6135
6389
  }
6136
6390
  }
6137
6391
  if (!options.wait) return data;
@@ -6268,7 +6522,7 @@ async function getDeploymentCommand(flowId, options) {
6268
6522
  // src/commands/deployments/index.ts
6269
6523
  init_auth();
6270
6524
  init_http();
6271
- import { getPlatform as getPlatform4 } from "@walkeros/core";
6525
+ import { getPlatform as getPlatform5 } from "@walkeros/core";
6272
6526
  init_cli_logger();
6273
6527
  init_output();
6274
6528
  init_loader();
@@ -6324,10 +6578,10 @@ async function deleteDeployment(options) {
6324
6578
  const data = await response.json().catch(() => null);
6325
6579
  return data ?? { success: true };
6326
6580
  }
6327
- async function handleResult3(fn2, options) {
6581
+ async function handleResult3(fn, options) {
6328
6582
  const logger = createCLILogger(options);
6329
6583
  try {
6330
- const result = await fn2();
6584
+ const result = await fn();
6331
6585
  await writeResult(JSON.stringify(result, null, 2), options);
6332
6586
  } catch (error) {
6333
6587
  logger.error(error instanceof Error ? error.message : String(error));
@@ -6401,7 +6655,7 @@ async function createDeployCommand(config, options) {
6401
6655
  const result2 = await loadFlowConfig(config, {
6402
6656
  flowName: options.flow
6403
6657
  });
6404
- type = getPlatform4(result2.flowSettings);
6658
+ type = getPlatform5(result2.flowSettings);
6405
6659
  }
6406
6660
  const deployment = await createDeployment({
6407
6661
  type,
@@ -6502,14 +6756,14 @@ async function feedbackCommand(text) {
6502
6756
  }
6503
6757
  }
6504
6758
  function promptUser(question) {
6505
- return new Promise((resolve2) => {
6759
+ return new Promise((resolve3) => {
6506
6760
  const rl = createInterface({
6507
6761
  input: process.stdin,
6508
6762
  output: process.stderr
6509
6763
  });
6510
6764
  rl.question(question, (answer) => {
6511
6765
  rl.close();
6512
- resolve2(answer);
6766
+ resolve3(answer);
6513
6767
  });
6514
6768
  });
6515
6769
  }
@@ -6522,6 +6776,108 @@ init_api_client();
6522
6776
  init_config_file();
6523
6777
  init_sse();
6524
6778
  init_utils();
6779
+
6780
+ // src/commands/simulate/example-loader.ts
6781
+ function findExample(config, exampleName, stepTarget) {
6782
+ if (stepTarget) {
6783
+ return findExampleInStep(config, exampleName, stepTarget);
6784
+ }
6785
+ return findExampleAcrossSteps(config, exampleName);
6786
+ }
6787
+ function findExampleInStep(config, exampleName, stepTarget) {
6788
+ const dotIndex = stepTarget.indexOf(".");
6789
+ if (dotIndex === -1) {
6790
+ throw new Error(
6791
+ `Invalid --step format: "${stepTarget}". Expected "type.name" (e.g. "destination.gtag")`
6792
+ );
6793
+ }
6794
+ const type = stepTarget.substring(0, dotIndex);
6795
+ const name = stepTarget.substring(dotIndex + 1);
6796
+ const stepMap = getStepMap(config, type);
6797
+ if (!stepMap) {
6798
+ throw new Error(`No ${type}s found in flow config`);
6799
+ }
6800
+ const step = stepMap[name];
6801
+ if (!step) {
6802
+ const available = Object.keys(stepMap).join(", ");
6803
+ throw new Error(`${type} "${name}" not found. Available: ${available}`);
6804
+ }
6805
+ const examples = step.examples;
6806
+ if (!examples || !examples[exampleName]) {
6807
+ const available = examples ? Object.keys(examples).join(", ") : "none";
6808
+ throw new Error(
6809
+ `Example "${exampleName}" not found in ${type} "${name}". Available: ${available}`
6810
+ );
6811
+ }
6812
+ return {
6813
+ stepType: type,
6814
+ stepName: name,
6815
+ exampleName,
6816
+ example: examples[exampleName]
6817
+ };
6818
+ }
6819
+ function findExampleAcrossSteps(config, exampleName) {
6820
+ const matches = [];
6821
+ const stepTypes = ["source", "transformer", "destination"];
6822
+ for (const type of stepTypes) {
6823
+ const stepMap = getStepMap(config, type);
6824
+ if (!stepMap) continue;
6825
+ for (const [name, step] of Object.entries(stepMap)) {
6826
+ const examples = step.examples;
6827
+ if (examples && examples[exampleName]) {
6828
+ matches.push({
6829
+ stepType: type,
6830
+ stepName: name,
6831
+ exampleName,
6832
+ example: examples[exampleName]
6833
+ });
6834
+ }
6835
+ }
6836
+ }
6837
+ if (matches.length === 0) {
6838
+ throw new Error(`Example "${exampleName}" not found in any step`);
6839
+ }
6840
+ if (matches.length > 1) {
6841
+ const locations = matches.map((m2) => `${m2.stepType}.${m2.stepName}`).join(", ");
6842
+ throw new Error(
6843
+ `Example "${exampleName}" found in multiple steps: ${locations}. Use --step to disambiguate.`
6844
+ );
6845
+ }
6846
+ return matches[0];
6847
+ }
6848
+ function getStepMap(config, type) {
6849
+ switch (type) {
6850
+ case "source":
6851
+ return config.sources;
6852
+ case "transformer":
6853
+ return config.transformers;
6854
+ case "destination":
6855
+ return config.destinations;
6856
+ default:
6857
+ throw new Error(
6858
+ `Invalid step type: "${type}". Must be "source", "transformer", or "destination"`
6859
+ );
6860
+ }
6861
+ }
6862
+
6863
+ // src/commands/simulate/compare.ts
6864
+ function compareOutput(expected, actual) {
6865
+ const expectedStr = JSON.stringify(expected, null, 2);
6866
+ const actualStr = JSON.stringify(actual, null, 2);
6867
+ if (expectedStr === actualStr) {
6868
+ return { expected, actual, match: true };
6869
+ }
6870
+ return {
6871
+ expected,
6872
+ actual,
6873
+ match: false,
6874
+ diff: `Expected:
6875
+ ${expectedStr}
6876
+
6877
+ Actual:
6878
+ ${actualStr}`
6879
+ };
6880
+ }
6525
6881
  export {
6526
6882
  ApiError,
6527
6883
  apiFetch,
@@ -6567,6 +6923,7 @@ export {
6567
6923
  listFlowsCommand,
6568
6924
  listProjects,
6569
6925
  listProjectsCommand,
6926
+ loadConfig,
6570
6927
  loadJsonConfig,
6571
6928
  loginCommand,
6572
6929
  logoutCommand,
@@ -6579,8 +6936,9 @@ export {
6579
6936
  requireProjectId,
6580
6937
  run,
6581
6938
  runCommand,
6582
- simulate,
6583
- simulateCommand,
6939
+ simulateDestination,
6940
+ simulateSource,
6941
+ simulateTransformer,
6584
6942
  throwApiError,
6585
6943
  updateFlow,
6586
6944
  updateFlowCommand,