@walkeros/cli 3.1.1 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,7 @@ 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);
516
504
  }
517
505
  const trimmed = inputPath.trim();
518
506
  if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
@@ -524,12 +512,12 @@ var init_input_detector = __esm({
524
512
  "src/core/input-detector.ts"() {
525
513
  "use strict";
526
514
  init_utils();
527
- init_http();
528
- init_config_file();
529
515
  }
530
516
  });
531
517
 
532
518
  // src/core/stdin.ts
519
+ import fs5 from "fs-extra";
520
+ import path6 from "path";
533
521
  function isStdinPiped() {
534
522
  return !process.stdin.isTTY;
535
523
  }
@@ -544,9 +532,17 @@ async function readStdin() {
544
532
  }
545
533
  return content;
546
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
+ }
547
542
  var init_stdin = __esm({
548
543
  "src/core/stdin.ts"() {
549
544
  "use strict";
545
+ init_tmp();
550
546
  }
551
547
  });
552
548
 
@@ -603,6 +599,129 @@ var init_sse = __esm({
603
599
  }
604
600
  });
605
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
+
606
725
  // src/core/index.ts
607
726
  var init_core = __esm({
608
727
  "src/core/index.ts"() {
@@ -619,20 +738,19 @@ var init_core = __esm({
619
738
  init_auth();
620
739
  init_http();
621
740
  init_sse();
741
+ init_event_validation();
742
+ init_package_path();
622
743
  }
623
744
  });
624
745
 
625
746
  // src/config/validators.ts
626
- import { schemas } from "@walkeros/core/dev";
627
- function isObject(value) {
628
- return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
629
- }
747
+ import { schemas as schemas2 } from "@walkeros/core/dev";
630
748
  function validateFlowConfig(data) {
631
749
  const result = safeParseConfig(data);
632
750
  if (!result.success) {
633
751
  const errors = result.error.issues.map((issue) => {
634
- const path15 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
635
- return ` - ${path15}: ${issue.message}`;
752
+ const path19 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
753
+ return ` - ${path19}: ${issue.message}`;
636
754
  }).join("\n");
637
755
  throw new Error(`Invalid configuration:
638
756
  ${errors}`);
@@ -646,7 +764,7 @@ var safeParseConfig;
646
764
  var init_validators = __esm({
647
765
  "src/config/validators.ts"() {
648
766
  "use strict";
649
- ({ safeParseConfig } = schemas);
767
+ ({ safeParseConfig } = schemas2);
650
768
  }
651
769
  });
652
770
 
@@ -668,7 +786,7 @@ var init_build_defaults = __esm({
668
786
  minify: true,
669
787
  sourcemap: false,
670
788
  cache: true,
671
- windowCollector: "collector",
789
+ windowCollector: "walkerOS",
672
790
  windowElb: "elb"
673
791
  };
674
792
  SERVER_BUILD_DEFAULTS = {
@@ -687,8 +805,8 @@ var init_build_defaults = __esm({
687
805
  });
688
806
 
689
807
  // src/config/loader.ts
690
- import path6 from "path";
691
- import fs5 from "fs-extra";
808
+ import path8 from "path";
809
+ import fs7 from "fs-extra";
692
810
  import { getFlowSettings, getPlatform } from "@walkeros/core";
693
811
  function loadBundleConfig(rawConfig, options) {
694
812
  const config = validateFlowConfig(rawConfig);
@@ -707,11 +825,11 @@ function loadBundleConfig(rawConfig, options) {
707
825
  const buildDefaults = getBuildDefaults(platform);
708
826
  const packages = flowSettings.packages || {};
709
827
  const output = options.buildOverrides?.output || getDefaultOutput(platform);
710
- const configDir = isUrl(options.configPath) ? process.cwd() : path6.dirname(options.configPath);
828
+ const configDir = isUrl(options.configPath) ? process.cwd() : path8.dirname(options.configPath);
711
829
  let includes = config.include;
712
830
  if (!includes) {
713
- const defaultIncludePath = path6.resolve(configDir, DEFAULT_INCLUDE_FOLDER);
714
- if (fs5.pathExistsSync(defaultIncludePath)) {
831
+ const defaultIncludePath = path8.resolve(configDir, DEFAULT_INCLUDE_FOLDER);
832
+ if (fs7.pathExistsSync(defaultIncludePath)) {
715
833
  includes = [DEFAULT_INCLUDE_FOLDER];
716
834
  }
717
835
  }
@@ -796,6 +914,43 @@ var init_config = __esm({
796
914
  }
797
915
  });
798
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
+
799
954
  // src/core/cache-utils.ts
800
955
  import { getHashServer } from "@walkeros/server-core";
801
956
  import semver from "semver";
@@ -836,8 +991,8 @@ var init_cache_utils = __esm({
836
991
 
837
992
  // src/commands/bundle/package-manager.ts
838
993
  import pacote from "pacote";
839
- import path7 from "path";
840
- import fs6 from "fs-extra";
994
+ import path9 from "path";
995
+ import fs8 from "fs-extra";
841
996
  import semver2 from "semver";
842
997
  async function withTimeout(promise, ms, errorMessage) {
843
998
  let timer;
@@ -851,7 +1006,7 @@ async function withTimeout(promise, ms, errorMessage) {
851
1006
  }
852
1007
  }
853
1008
  function getPackageDirectory(baseDir, packageName) {
854
- return path7.join(baseDir, "node_modules", packageName);
1009
+ return path9.join(baseDir, "node_modules", packageName);
855
1010
  }
856
1011
  async function collectAllSpecs(packages, logger, configDir) {
857
1012
  const allSpecs = /* @__PURE__ */ new Map();
@@ -877,7 +1032,47 @@ async function collectAllSpecs(packages, logger, configDir) {
877
1032
  optional: item.optional,
878
1033
  localPath: item.localPath
879
1034
  });
880
- 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
+ }
881
1076
  let manifest;
882
1077
  try {
883
1078
  manifest = await withTimeout(
@@ -924,7 +1119,7 @@ async function collectAllSpecs(packages, logger, configDir) {
924
1119
  function resolveVersionConflicts(allSpecs, logger) {
925
1120
  const resolved = /* @__PURE__ */ new Map();
926
1121
  for (const [name, specs] of allSpecs) {
927
- const localSpec = specs.find((s2) => s2.localPath);
1122
+ const localSpec = specs.find((s) => s.localPath);
928
1123
  if (localSpec) {
929
1124
  resolved.set(name, {
930
1125
  name,
@@ -933,13 +1128,13 @@ function resolveVersionConflicts(allSpecs, logger) {
933
1128
  });
934
1129
  continue;
935
1130
  }
936
- const nonPeerSpecs = specs.filter((s2) => s2.source !== "peerDependency");
937
- 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");
938
1133
  let activeSpecs;
939
1134
  if (nonPeerSpecs.length > 0) {
940
1135
  activeSpecs = nonPeerSpecs;
941
1136
  } else {
942
- const requiredPeers = peerSpecs.filter((s2) => !s2.optional);
1137
+ const requiredPeers = peerSpecs.filter((s) => !s.optional);
943
1138
  if (requiredPeers.length === 0) {
944
1139
  logger.debug(`Skipping optional peer dependency: ${name}`);
945
1140
  continue;
@@ -949,19 +1144,19 @@ function resolveVersionConflicts(allSpecs, logger) {
949
1144
  activeSpecs.sort(
950
1145
  (a2, b2) => SOURCE_PRIORITY[a2.source] - SOURCE_PRIORITY[b2.source]
951
1146
  );
952
- const directSpecs = activeSpecs.filter((s2) => s2.source === "direct");
953
- 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);
954
1149
  let chosenVersion;
955
1150
  if (directExact) {
956
1151
  chosenVersion = directExact.spec;
957
1152
  } else if (directSpecs.length > 0) {
958
1153
  chosenVersion = directSpecs[0].spec;
959
1154
  } else {
960
- 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);
961
1156
  const uniqueExact = [...new Set(exactVersions)];
962
1157
  if (uniqueExact.length > 1) {
963
1158
  throw new Error(
964
- `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(", ")})`
965
1160
  );
966
1161
  } else if (uniqueExact.length === 1) {
967
1162
  chosenVersion = uniqueExact[0];
@@ -1008,7 +1203,7 @@ async function downloadPackages(packages, targetDir, logger, useCache = true, co
1008
1203
  logger.debug("Resolving dependencies");
1009
1204
  const allSpecs = await collectAllSpecs(packages, logger, configDir);
1010
1205
  const resolved = resolveVersionConflicts(allSpecs, logger);
1011
- await fs6.ensureDir(targetDir);
1206
+ await fs8.ensureDir(targetDir);
1012
1207
  const localPackageMap = /* @__PURE__ */ new Map();
1013
1208
  for (const pkg of packages) {
1014
1209
  if (pkg.path) localPackageMap.set(pkg.name, pkg.path);
@@ -1037,8 +1232,8 @@ async function downloadPackages(packages, targetDir, logger, useCache = true, co
1037
1232
  logger.debug(`Downloading ${packageSpec} (cached)`);
1038
1233
  }
1039
1234
  try {
1040
- await fs6.ensureDir(path7.dirname(packageDir));
1041
- await fs6.copy(cachedPath, packageDir);
1235
+ await fs8.ensureDir(path9.dirname(packageDir));
1236
+ await fs8.copy(cachedPath, packageDir);
1042
1237
  packagePaths.set(name, packageDir);
1043
1238
  continue;
1044
1239
  } catch {
@@ -1046,7 +1241,7 @@ async function downloadPackages(packages, targetDir, logger, useCache = true, co
1046
1241
  }
1047
1242
  }
1048
1243
  try {
1049
- await fs6.ensureDir(path7.dirname(packageDir));
1244
+ await fs8.ensureDir(path9.dirname(packageDir));
1050
1245
  const cacheDir = process.env.NPM_CACHE_DIR || getTmpPath(tmpDir, "cache", "npm");
1051
1246
  await withTimeout(
1052
1247
  pacote.extract(packageSpec, packageDir, {
@@ -1061,8 +1256,8 @@ async function downloadPackages(packages, targetDir, logger, useCache = true, co
1061
1256
  }
1062
1257
  if (useCache) {
1063
1258
  try {
1064
- await fs6.ensureDir(path7.dirname(cachedPath));
1065
- await fs6.copy(packageDir, cachedPath);
1259
+ await fs8.ensureDir(path9.dirname(cachedPath));
1260
+ await fs8.copy(packageDir, cachedPath);
1066
1261
  } catch {
1067
1262
  }
1068
1263
  }
@@ -1076,11 +1271,11 @@ async function downloadPackages(packages, targetDir, logger, useCache = true, co
1076
1271
  async function getCachedPackagePath(pkg, tmpDir) {
1077
1272
  const cacheDir = getTmpPath(tmpDir, "cache", "packages");
1078
1273
  const cacheKey = await getPackageCacheKey(pkg.name, pkg.version);
1079
- return path7.join(cacheDir, cacheKey);
1274
+ return path9.join(cacheDir, cacheKey);
1080
1275
  }
1081
1276
  async function isPackageCached(pkg, tmpDir) {
1082
1277
  const cachedPath = await getCachedPackagePath(pkg, tmpDir);
1083
- return fs6.pathExists(cachedPath);
1278
+ return fs8.pathExists(cachedPath);
1084
1279
  }
1085
1280
  function validateNoDuplicatePackages(packages) {
1086
1281
  const packageMap = /* @__PURE__ */ new Map();
@@ -1126,29 +1321,57 @@ var init_package_manager = __esm({
1126
1321
  });
1127
1322
 
1128
1323
  // src/core/build-cache.ts
1129
- import fs7 from "fs-extra";
1130
- import path8 from "path";
1324
+ import fs9 from "fs-extra";
1325
+ import path10 from "path";
1326
+ import { getHashServer as getHashServer2 } from "@walkeros/server-core";
1131
1327
  async function getBuildCachePath(configContent, tmpDir) {
1132
1328
  const cacheDir = getTmpPath(tmpDir, "cache", "builds");
1133
1329
  const cacheKey = await getFlowSettingsCacheKey(configContent);
1134
- return path8.join(cacheDir, `${cacheKey}.js`);
1330
+ return path10.join(cacheDir, `${cacheKey}.js`);
1135
1331
  }
1136
1332
  async function isBuildCached(configContent, tmpDir) {
1137
1333
  const cachePath = await getBuildCachePath(configContent, tmpDir);
1138
- return fs7.pathExists(cachePath);
1334
+ return fs9.pathExists(cachePath);
1139
1335
  }
1140
1336
  async function cacheBuild(configContent, buildOutput, tmpDir) {
1141
1337
  const cachePath = await getBuildCachePath(configContent, tmpDir);
1142
- await fs7.ensureDir(path8.dirname(cachePath));
1143
- await fs7.writeFile(cachePath, buildOutput, "utf-8");
1338
+ await fs9.ensureDir(path10.dirname(cachePath));
1339
+ await fs9.writeFile(cachePath, buildOutput, "utf-8");
1144
1340
  }
1145
1341
  async function getCachedBuild(configContent, tmpDir) {
1146
1342
  const cachePath = await getBuildCachePath(configContent, tmpDir);
1147
- if (await fs7.pathExists(cachePath)) {
1148
- 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");
1149
1362
  }
1150
1363
  return null;
1151
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
+ }
1152
1375
  var init_build_cache = __esm({
1153
1376
  "src/core/build-cache.ts"() {
1154
1377
  "use strict";
@@ -1158,19 +1381,23 @@ var init_build_cache = __esm({
1158
1381
  });
1159
1382
 
1160
1383
  // src/commands/bundle/bundler.ts
1161
- import crypto2 from "crypto";
1384
+ import crypto from "crypto";
1162
1385
  import esbuild from "esbuild";
1163
1386
  import { builtinModules } from "module";
1164
- import path9 from "path";
1165
- import fs8 from "fs-extra";
1387
+ import path11 from "path";
1388
+ import fs10 from "fs-extra";
1166
1389
  import { packageNameToVariable, ENV_MARKER_PREFIX } from "@walkeros/core";
1167
1390
  function isInlineCode(code) {
1168
1391
  return code !== null && typeof code === "object" && !Array.isArray(code) && "push" in code;
1169
1392
  }
1393
+ function hasCodeReference(code) {
1394
+ return isInlineCode(code) || typeof code === "string";
1395
+ }
1170
1396
  function validateReference(type, name, ref) {
1171
1397
  const hasPackage = !!ref.package;
1172
- const hasCode = isInlineCode(ref.code);
1173
- if (hasPackage && hasCode) {
1398
+ const hasInlineCode = isInlineCode(ref.code);
1399
+ const hasCode = hasCodeReference(ref.code);
1400
+ if (hasPackage && hasInlineCode) {
1174
1401
  throw new Error(
1175
1402
  `${type} "${name}": Cannot specify both package and code. Use one or the other.`
1176
1403
  );
@@ -1211,18 +1438,18 @@ function generateInlineCode(inline, config, env, chain, chainPropertyName, isDes
1211
1438
  }
1212
1439
  async function copyIncludes(includes, sourceDir, outputDir, logger) {
1213
1440
  for (const include of includes) {
1214
- const sourcePath = path9.resolve(sourceDir, include);
1215
- const folderName = path9.basename(include);
1216
- const destPath = path9.join(outputDir, folderName);
1217
- const resolvedOutput = path9.resolve(outputDir);
1218
- const resolvedSource = path9.resolve(sourcePath);
1219
- 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)) {
1220
1447
  throw new Error(
1221
1448
  `Circular include detected: "${include}" resolves to "${resolvedSource}" which overlaps with output directory "${resolvedOutput}"`
1222
1449
  );
1223
1450
  }
1224
- if (await fs8.pathExists(sourcePath)) {
1225
- await fs8.copy(sourcePath, destPath);
1451
+ if (await fs10.pathExists(sourcePath)) {
1452
+ await fs10.copy(sourcePath, destPath);
1226
1453
  logger.debug(`Copied ${include} to output`);
1227
1454
  } else {
1228
1455
  logger.warn(`Include folder not found: ${include}`);
@@ -1283,7 +1510,7 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1283
1510
  if (hasDeprecatedFeatures) {
1284
1511
  logger.warn("Skipping deprecated code: true entries from bundle.");
1285
1512
  }
1286
- const buildId = crypto2.randomUUID();
1513
+ const buildId = crypto.randomUUID();
1287
1514
  const TEMP_DIR = buildOptions.tempDir || getTmpPath(void 0, `walkeros-build-${buildId}`);
1288
1515
  const CACHE_DIR = buildOptions.tempDir || getTmpPath();
1289
1516
  if (buildOptions.cache !== false) {
@@ -1293,14 +1520,14 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1293
1520
  const cachedBuild = await getCachedBuild(configContent, CACHE_DIR);
1294
1521
  if (cachedBuild) {
1295
1522
  logger.debug("Using cached build");
1296
- const outputPath = path9.resolve(buildOptions.output);
1297
- await fs8.ensureDir(path9.dirname(outputPath));
1298
- await fs8.writeFile(outputPath, cachedBuild);
1299
- 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);
1300
1527
  const sizeKB = (stats.size / 1024).toFixed(1);
1301
1528
  logger.info(`Output: ${outputPath} (${sizeKB} KB, cached)`);
1302
1529
  if (showStats) {
1303
- const stats2 = await fs8.stat(outputPath);
1530
+ const stats2 = await fs10.stat(outputPath);
1304
1531
  const packageStats = Object.entries(buildOptions.packages).map(
1305
1532
  ([name, pkg]) => ({
1306
1533
  name: `${name}@${pkg.version || "latest"}`,
@@ -1323,7 +1550,7 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1323
1550
  }
1324
1551
  }
1325
1552
  try {
1326
- await fs8.ensureDir(TEMP_DIR);
1553
+ await fs10.ensureDir(TEMP_DIR);
1327
1554
  const hasSourcesOrDests = Object.keys(
1328
1555
  flowSettings.sources || {}
1329
1556
  ).length > 0 || Object.keys(
@@ -1332,9 +1559,28 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1332
1559
  if (hasSourcesOrDests && !buildOptions.packages["@walkeros/collector"]) {
1333
1560
  buildOptions.packages["@walkeros/collector"] = {};
1334
1561
  }
1335
- const stepPackages = collectStepPackages(flowSettings);
1562
+ const stepPackages = collectAllStepPackages(flowSettings);
1336
1563
  for (const pkg of stepPackages) {
1337
- 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]) {
1338
1584
  buildOptions.packages[pkg] = {};
1339
1585
  }
1340
1586
  }
@@ -1358,8 +1604,8 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1358
1604
  );
1359
1605
  for (const [pkgName, pkgPath] of packagePaths.entries()) {
1360
1606
  if (pkgName.startsWith("@walkeros/")) {
1361
- const pkgJsonPath = path9.join(pkgPath, "package.json");
1362
- const pkgJson = await fs8.readJSON(pkgJsonPath);
1607
+ const pkgJsonPath = path11.join(pkgPath, "package.json");
1608
+ const pkgJson = await fs10.readJSON(pkgJsonPath);
1363
1609
  if (!pkgJson.exports && pkgJson.module) {
1364
1610
  pkgJson.exports = {
1365
1611
  ".": {
@@ -1367,67 +1613,135 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1367
1613
  require: pkgJson.main
1368
1614
  }
1369
1615
  };
1370
- await fs8.writeJSON(pkgJsonPath, pkgJson, { spaces: 2 });
1616
+ await fs10.writeJSON(pkgJsonPath, pkgJson, { spaces: 2 });
1371
1617
  }
1372
1618
  }
1373
1619
  }
1374
- const packageJsonPath = path9.join(TEMP_DIR, "package.json");
1375
- await fs8.writeFile(
1620
+ const packageJsonPath = path11.join(TEMP_DIR, "package.json");
1621
+ await fs10.writeFile(
1376
1622
  packageJsonPath,
1377
1623
  JSON.stringify({ type: "module" }, null, 2)
1378
1624
  );
1379
1625
  logger.debug("Creating entry point");
1380
- const entryContent = await createEntryPoint(
1626
+ const { codeEntry, dataPayload, hasFlow } = await createEntryPoint(
1381
1627
  flowSettings,
1382
1628
  buildOptions,
1383
1629
  packagePaths
1384
1630
  );
1385
- const entryPath = path9.join(TEMP_DIR, "entry.js");
1386
- await fs8.writeFile(entryPath, entryContent);
1387
- logger.debug(
1388
- `Running esbuild (target: ${buildOptions.target || "es2018"}, format: ${buildOptions.format})`
1389
- );
1390
- const outputPath = path9.resolve(buildOptions.output);
1391
- await fs8.ensureDir(path9.dirname(outputPath));
1392
- const esbuildOptions = createEsbuildOptions(
1393
- buildOptions,
1394
- entryPath,
1395
- outputPath,
1396
- TEMP_DIR,
1397
- packagePaths,
1398
- logger
1399
- );
1400
- try {
1401
- await esbuild.build(esbuildOptions);
1402
- } catch (buildError) {
1403
- throw createBuildError(
1404
- buildError,
1405
- buildOptions.code || ""
1406
- );
1407
- } finally {
1408
- await esbuild.stop();
1409
- }
1410
- const outputStats = await fs8.stat(outputPath);
1411
- const sizeKB = (outputStats.size / 1024).toFixed(1);
1412
- const buildTime = ((Date.now() - bundleStartTime) / 1e3).toFixed(1);
1413
- 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;
1414
1634
  if (buildOptions.cache !== false) {
1415
- const configContent = generateCacheKeyContent(flowSettings, buildOptions);
1416
- const buildOutput = await fs8.readFile(outputPath, "utf-8");
1417
- await cacheBuild(configContent, buildOutput, CACHE_DIR);
1418
- logger.debug("Build cached for future use");
1635
+ compiledCode = await getCachedCode(codeEntry, CACHE_DIR);
1419
1636
  }
1420
- let stats;
1421
- if (showStats) {
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;
1735
+ if (showStats) {
1422
1736
  stats = await collectBundleStats(
1423
1737
  outputPath,
1424
1738
  buildOptions.packages,
1425
1739
  bundleStartTime,
1426
- entryContent
1740
+ codeEntry
1427
1741
  );
1428
1742
  }
1429
1743
  if (buildOptions.include && buildOptions.include.length > 0) {
1430
- const outputDir = path9.dirname(outputPath);
1744
+ const outputDir = path11.dirname(outputPath);
1431
1745
  await copyIncludes(
1432
1746
  buildOptions.include,
1433
1747
  buildOptions.configDir || process.cwd(),
@@ -1440,13 +1754,13 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1440
1754
  throw error;
1441
1755
  } finally {
1442
1756
  if (!buildOptions.tempDir) {
1443
- fs8.remove(TEMP_DIR).catch(() => {
1757
+ fs10.remove(TEMP_DIR).catch(() => {
1444
1758
  });
1445
1759
  }
1446
1760
  }
1447
1761
  }
1448
1762
  async function collectBundleStats(outputPath, packages, startTime, entryContent) {
1449
- const stats = await fs8.stat(outputPath);
1763
+ const stats = await fs10.stat(outputPath);
1450
1764
  const totalSize = stats.size;
1451
1765
  const buildTime = Date.now() - startTime;
1452
1766
  const packageStats = Object.entries(packages).map(([name, pkg]) => {
@@ -1477,7 +1791,8 @@ function createEsbuildOptions(buildOptions, entryPath, outputPath, tempDir, pack
1477
1791
  const baseOptions = {
1478
1792
  entryPoints: [entryPath],
1479
1793
  bundle: true,
1480
- format: buildOptions.format,
1794
+ format: "esm",
1795
+ // Always ESM — platform wrapper handles final format
1481
1796
  platform: buildOptions.platform,
1482
1797
  outfile: outputPath,
1483
1798
  absWorkingDir: tempDir,
@@ -1510,11 +1825,6 @@ function createEsbuildOptions(buildOptions, entryPath, outputPath, tempDir, pack
1510
1825
  } else if (buildOptions.platform === "node") {
1511
1826
  const nodeExternals = getNodeExternals();
1512
1827
  baseOptions.external = buildOptions.external ? [...nodeExternals, ...buildOptions.external] : nodeExternals;
1513
- if (buildOptions.format === "esm") {
1514
- baseOptions.banner = {
1515
- js: `import { createRequire } from 'module';const require = createRequire(import.meta.url);`
1516
- };
1517
- }
1518
1828
  }
1519
1829
  if (buildOptions.target) {
1520
1830
  baseOptions.target = buildOptions.target;
@@ -1525,64 +1835,19 @@ function createEsbuildOptions(buildOptions, entryPath, outputPath, tempDir, pack
1525
1835
  }
1526
1836
  return baseOptions;
1527
1837
  }
1528
- function detectDestinationPackages(flowSettings) {
1529
- const destinationPackages = /* @__PURE__ */ new Set();
1530
- const destinations = flowSettings.destinations;
1531
- if (destinations) {
1532
- for (const [destKey, destConfig] of Object.entries(destinations)) {
1533
- if (typeof destConfig === "object" && destConfig !== null && "code" in destConfig && destConfig.code === true) {
1534
- continue;
1535
- }
1536
- if (typeof destConfig === "object" && destConfig !== null && "package" in destConfig && typeof destConfig.package === "string") {
1537
- destinationPackages.add(destConfig.package);
1538
- }
1539
- }
1540
- }
1541
- return destinationPackages;
1542
- }
1543
- function detectSourcePackages(flowSettings) {
1544
- const sourcePackages = /* @__PURE__ */ new Set();
1545
- const sources = flowSettings.sources;
1546
- if (sources) {
1547
- for (const [sourceKey, sourceConfig] of Object.entries(sources)) {
1548
- if (typeof sourceConfig === "object" && sourceConfig !== null && "code" in sourceConfig && sourceConfig.code === true) {
1549
- continue;
1550
- }
1551
- if (typeof sourceConfig === "object" && sourceConfig !== null && "package" in sourceConfig && typeof sourceConfig.package === "string") {
1552
- sourcePackages.add(sourceConfig.package);
1553
- }
1554
- }
1555
- }
1556
- return sourcePackages;
1557
- }
1558
- function detectTransformerPackages(flowSettings) {
1559
- const transformerPackages = /* @__PURE__ */ new Set();
1560
- const transformers = flowSettings.transformers;
1561
- if (transformers) {
1562
- for (const [transformerKey, transformerConfig] of Object.entries(
1563
- transformers
1564
- )) {
1565
- if (typeof transformerConfig === "object" && transformerConfig !== null && "code" in transformerConfig && transformerConfig.code === true) {
1566
- continue;
1567
- }
1568
- if (typeof transformerConfig === "object" && transformerConfig !== null && "package" in transformerConfig && typeof transformerConfig.package === "string") {
1569
- transformerPackages.add(transformerConfig.package);
1570
- }
1571
- }
1572
- }
1573
- return transformerPackages;
1574
- }
1575
- function detectStorePackages(flowSettings) {
1576
- const storePackages = /* @__PURE__ */ new Set();
1577
- const stores = flowSettings.stores;
1578
- if (stores) {
1579
- for (const [, storeConfig] of Object.entries(stores)) {
1580
- if (typeof storeConfig === "object" && storeConfig !== null && "package" in storeConfig && typeof storeConfig.package === "string") {
1581
- 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);
1582
1847
  }
1583
1848
  }
1584
1849
  }
1585
- return storePackages;
1850
+ return packages;
1586
1851
  }
1587
1852
  function getNodeExternals() {
1588
1853
  const externals = [];
@@ -1592,27 +1857,20 @@ function getNodeExternals() {
1592
1857
  }
1593
1858
  return externals;
1594
1859
  }
1595
- function collectStepPackages(flowSettings) {
1860
+ function collectAllStepPackages(flowSettings) {
1596
1861
  const allPackages = /* @__PURE__ */ new Set();
1597
- for (const pkg of detectSourcePackages(flowSettings)) {
1598
- allPackages.add(pkg);
1599
- }
1600
- for (const pkg of detectDestinationPackages(flowSettings)) {
1601
- allPackages.add(pkg);
1602
- }
1603
- for (const pkg of detectTransformerPackages(flowSettings)) {
1604
- allPackages.add(pkg);
1605
- }
1606
- for (const pkg of detectStorePackages(flowSettings)) {
1607
- allPackages.add(pkg);
1608
- }
1609
- const npmPackages = /* @__PURE__ */ new Set();
1610
- for (const pkg of allPackages) {
1611
- if (!pkg.startsWith(".") && !pkg.startsWith("/")) {
1612
- 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);
1613
1871
  }
1614
1872
  }
1615
- return npmPackages;
1873
+ return allPackages;
1616
1874
  }
1617
1875
  function detectExplicitCodeImports(flowSettings) {
1618
1876
  const explicitCodeImports = /* @__PURE__ */ new Map();
@@ -1759,12 +2017,12 @@ function validateComponentNames(components, section) {
1759
2017
  }
1760
2018
  function validateStoreReferences(flowSettings, storeIds) {
1761
2019
  const refs = [];
1762
- function collectRefs(obj, path15) {
2020
+ function collectRefs(obj, path19) {
1763
2021
  if (typeof obj === "string" && obj.startsWith("$store:")) {
1764
- refs.push({ ref: obj.slice(7), location: path15 });
2022
+ refs.push({ ref: obj.slice(7), location: path19 });
1765
2023
  } else if (obj && typeof obj === "object") {
1766
2024
  for (const [key, val] of Object.entries(obj)) {
1767
- collectRefs(val, `${path15}.${key}`);
2025
+ collectRefs(val, `${path19}.${key}`);
1768
2026
  }
1769
2027
  }
1770
2028
  }
@@ -1789,10 +2047,10 @@ function validateStoreReferences(flowSettings, storeIds) {
1789
2047
  }
1790
2048
  }
1791
2049
  async function createEntryPoint(flowSettings, buildOptions, packagePaths) {
1792
- const destinationPackages = detectDestinationPackages(flowSettings);
1793
- const sourcePackages = detectSourcePackages(flowSettings);
1794
- const transformerPackages = detectTransformerPackages(flowSettings);
1795
- 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");
1796
2054
  const explicitCodeImports = detectExplicitCodeImports(flowSettings);
1797
2055
  const storeIds = new Set(
1798
2056
  Object.keys(
@@ -1819,29 +2077,30 @@ async function createEntryPoint(flowSettings, buildOptions, packagePaths) {
1819
2077
  );
1820
2078
  const importsCode = importStatements.join("\n");
1821
2079
  const hasFlow = Object.values(flowSettings.sources || {}).some(
1822
- (s2) => s2.package || isInlineCode(s2.code)
2080
+ (s) => s.package || hasCodeReference(s.code)
1823
2081
  ) || Object.values(flowSettings.destinations || {}).some(
1824
- (d2) => d2.package || isInlineCode(d2.code)
2082
+ (d2) => d2.package || hasCodeReference(d2.code)
1825
2083
  );
1826
2084
  if (!hasFlow) {
1827
2085
  const userCode = buildOptions.code || "";
1828
- return importsCode ? `${importsCode}
2086
+ return {
2087
+ codeEntry: importsCode ? `${importsCode}
1829
2088
 
1830
- ${userCode}` : userCode;
2089
+ ${userCode}` : userCode,
2090
+ dataPayload: "{}",
2091
+ hasFlow: false
2092
+ };
1831
2093
  }
1832
- const { storesDeclaration, configObject } = buildConfigObject(
1833
- flowSettings,
1834
- explicitCodeImports
1835
- );
1836
- const wrappedCode = generatePlatformWrapper(
2094
+ const { storesDeclaration, codeConfigObject, dataPayload } = buildSplitConfigObject(flowSettings, explicitCodeImports);
2095
+ const wireConfigModule = generateSplitWireConfigModule(
1837
2096
  storesDeclaration,
1838
- configObject,
1839
- buildOptions.code || "",
1840
- buildOptions
2097
+ codeConfigObject,
2098
+ buildOptions.code || ""
1841
2099
  );
1842
- return importsCode ? `${importsCode}
2100
+ const codeEntry = importsCode ? `${importsCode}
1843
2101
 
1844
- ${wrappedCode}` : wrappedCode;
2102
+ ${wireConfigModule}` : wireConfigModule;
2103
+ return { codeEntry, dataPayload, hasFlow: true };
1845
2104
  }
1846
2105
  function createBuildError(buildError, code) {
1847
2106
  if (!buildError.errors || buildError.errors.length === 0) {
@@ -1866,12 +2125,55 @@ ${firstError.text}`
1866
2125
  ` + (location ? ` at ${location.file}:${location.line}:${location.column}` : "")
1867
2126
  );
1868
2127
  }
1869
- function buildConfigObject(flowSettings, explicitCodeImports) {
2128
+ function buildSplitConfigObject(flowSettings, explicitCodeImports) {
1870
2129
  const flowWithProps = flowSettings;
1871
2130
  const sources = flowWithProps.sources || {};
1872
2131
  const destinations = flowWithProps.destinations || {};
1873
2132
  const transformers = flowWithProps.transformers || {};
1874
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
+ }
1875
2177
  Object.entries(sources).forEach(([name, source]) => {
1876
2178
  if (source.code !== true) {
1877
2179
  validateReference("Source", name, source);
@@ -1888,104 +2190,89 @@ function buildConfigObject(flowSettings, explicitCodeImports) {
1888
2190
  }
1889
2191
  });
1890
2192
  const sourcesEntries = Object.entries(sources).filter(
1891
- ([, source]) => source.code !== true && (source.package || isInlineCode(source.code))
2193
+ ([, source]) => source.code !== true && (source.package || hasCodeReference(source.code))
1892
2194
  ).map(([key, source]) => {
1893
2195
  if (isInlineCode(source.code)) {
1894
2196
  return ` ${key}: ${generateInlineCode(source.code, source.config || {}, source.env, source.next, "next")}`;
1895
2197
  }
1896
- let codeVar;
1897
- if (source.code && typeof source.code === "string" && explicitCodeImports.has(source.package)) {
1898
- codeVar = source.code;
1899
- } else {
1900
- codeVar = packageNameToVariable(source.package);
1901
- }
1902
- const configStr = source.config ? processConfigValue(source.config) : "{}";
1903
- const envStr = source.env ? `,
1904
- env: ${processConfigValue(source.env)}` : "";
1905
- const nextStr = source.next ? `,
1906
- next: ${JSON.stringify(source.next)}` : "";
1907
- return ` ${key}: {
1908
- code: ${codeVar},
1909
- config: ${configStr}${envStr}${nextStr}
1910
- }`;
2198
+ return buildSplitStepEntry(
2199
+ "sources",
2200
+ key,
2201
+ source
2202
+ );
1911
2203
  });
1912
2204
  const destinationsEntries = Object.entries(destinations).filter(
1913
- ([, dest]) => dest.code !== true && (dest.package || isInlineCode(dest.code))
2205
+ ([, dest]) => dest.code !== true && (dest.package || hasCodeReference(dest.code))
1914
2206
  ).map(([key, dest]) => {
1915
2207
  if (isInlineCode(dest.code)) {
1916
2208
  return ` ${key}: ${generateInlineCode(dest.code, dest.config || {}, dest.env, dest.before, "before", true)}`;
1917
2209
  }
1918
- let codeVar;
1919
- if (dest.code && typeof dest.code === "string" && explicitCodeImports.has(dest.package)) {
1920
- codeVar = dest.code;
1921
- } else {
1922
- codeVar = packageNameToVariable(dest.package);
1923
- }
1924
- const configStr = dest.config ? processConfigValue(dest.config) : "{}";
1925
- const envStr = dest.env ? `,
1926
- env: ${processConfigValue(dest.env)}` : "";
1927
- const beforeStr = dest.before ? `,
1928
- before: ${JSON.stringify(dest.before)}` : "";
1929
- return ` ${key}: {
1930
- code: ${codeVar},
1931
- config: ${configStr}${envStr}${beforeStr}
1932
- }`;
2210
+ return buildSplitStepEntry(
2211
+ "destinations",
2212
+ key,
2213
+ dest
2214
+ );
1933
2215
  });
1934
2216
  const transformersEntries = Object.entries(transformers).filter(
1935
- ([, transformer]) => transformer.code !== true && (transformer.package || isInlineCode(transformer.code))
2217
+ ([, transformer]) => transformer.code !== true && (transformer.package || hasCodeReference(transformer.code))
1936
2218
  ).map(([key, transformer]) => {
1937
2219
  if (isInlineCode(transformer.code)) {
1938
2220
  return ` ${key}: ${generateInlineCode(transformer.code, transformer.config || {}, transformer.env, transformer.next, "next")}`;
1939
2221
  }
1940
- let codeVar;
1941
- if (transformer.code && typeof transformer.code === "string" && explicitCodeImports.has(transformer.package)) {
1942
- codeVar = transformer.code;
1943
- } else {
1944
- codeVar = packageNameToVariable(transformer.package);
1945
- }
1946
- const configStr = transformer.config ? processConfigValue(transformer.config) : "{}";
1947
- const envStr = transformer.env ? `,
1948
- env: ${processConfigValue(transformer.env)}` : "";
1949
- const nextStr = transformer.next ? `,
1950
- next: ${JSON.stringify(transformer.next)}` : "";
1951
- return ` ${key}: {
1952
- code: ${codeVar},
1953
- config: ${configStr}${envStr}${nextStr}
1954
- }`;
2222
+ return buildSplitStepEntry(
2223
+ "transformers",
2224
+ key,
2225
+ transformer
2226
+ );
1955
2227
  });
1956
2228
  Object.entries(stores).forEach(([name, store]) => {
1957
- if (store.package || isInlineCode(store.code)) {
2229
+ if (store.package || hasCodeReference(store.code)) {
1958
2230
  validateReference("Store", name, store);
1959
2231
  }
1960
2232
  });
1961
- 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]) => {
1962
2234
  if (isInlineCode(store.code)) {
1963
2235
  return ` ${key}: ${generateInlineCode(store.code, store.config || {}, store.env)}`;
1964
2236
  }
1965
- let codeVar;
1966
- if (store.code && typeof store.code === "string" && explicitCodeImports.has(store.package)) {
1967
- codeVar = store.code;
1968
- } else {
1969
- 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;
1970
2252
  }
1971
- const configStr = store.config ? processConfigValue(store.config) : "{}";
1972
- const envStr = store.env ? `,
1973
- env: ${processConfigValue(store.env)}` : "";
1974
2253
  return ` ${key}: {
1975
- code: ${codeVar},
1976
- config: ${configStr}${envStr}
2254
+ ${codeEntries.join(",\n ")}
1977
2255
  }`;
1978
2256
  });
1979
2257
  const storesDeclaration = storesEntries.length > 0 ? `const stores = {
1980
2258
  ${storesEntries.join(",\n")}
1981
2259
  };` : "const stores = {};";
1982
- const collectorStr = flowWithProps.collector ? `,
1983
- ...${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
+ }
1984
2271
  const transformersStr = transformersEntries.length > 0 ? `,
1985
2272
  transformers: {
1986
2273
  ${transformersEntries.join(",\n")}
1987
2274
  }` : "";
1988
- const configObject = `{
2275
+ const codeConfigObject = `{
1989
2276
  sources: {
1990
2277
  ${sourcesEntries.join(",\n")}
1991
2278
  },
@@ -1994,7 +2281,69 @@ ${destinationsEntries.join(",\n")}
1994
2281
  }${transformersStr},
1995
2282
  stores${collectorStr}
1996
2283
  }`;
1997
- 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
+ })();`;
1998
2347
  }
1999
2348
  function processConfigValue(value) {
2000
2349
  return serializeWithCode(value, 0);
@@ -2063,66 +2412,11 @@ ${spaces}}`;
2063
2412
  }
2064
2413
  return JSON.stringify(value);
2065
2414
  }
2066
- function generatePlatformWrapper(storesDeclaration, configObject, userCode, buildOptions) {
2067
- if (buildOptions.platform === "browser") {
2068
- const windowAssignments = [];
2069
- if (buildOptions.windowCollector) {
2070
- windowAssignments.push(
2071
- ` if (typeof window !== 'undefined') window['${buildOptions.windowCollector}'] = collector;`
2072
- );
2073
- }
2074
- if (buildOptions.windowElb) {
2075
- windowAssignments.push(
2076
- ` if (typeof window !== 'undefined') window['${buildOptions.windowElb}'] = elb;`
2077
- );
2078
- }
2079
- const assignments = windowAssignments.length > 0 ? "\n" + windowAssignments.join("\n") : "";
2080
- return `(async () => {
2081
- ${storesDeclaration}
2082
-
2083
- const config = ${configObject};
2084
-
2085
- ${userCode}
2086
-
2087
- const { collector, elb } = await startFlow(config);${assignments}
2088
- })();`;
2089
- } else {
2090
- const codeSection = userCode ? `
2091
- ${userCode}
2092
- ` : "";
2093
- return `export default async function(context = {}) {
2094
- ${storesDeclaration}
2095
-
2096
- const config = ${configObject};${codeSection}
2097
- // Apply context overrides (e.g., logger config from CLI)
2098
- if (context.logger) {
2099
- config.logger = { ...config.logger, ...context.logger };
2100
- }
2101
-
2102
- // When runner provides external server, strip port from sources
2103
- // so they don't self-listen (runner owns the port)
2104
- if (context.externalServer && config.sources) {
2105
- for (const src of Object.values(config.sources)) {
2106
- if (src.config && src.config.settings && 'port' in src.config.settings) {
2107
- delete src.config.settings.port;
2108
- }
2109
- }
2110
- }
2111
-
2112
- const result = await startFlow(config);
2113
-
2114
- // Discover httpHandler from initialized sources (duck-typing, no source-type coupling)
2115
- const httpSource = Object.values(result.collector.sources || {})
2116
- .find(s => 'httpHandler' in s && typeof s.httpHandler === 'function');
2117
-
2118
- return { ...result, httpHandler: httpSource ? httpSource.httpHandler : undefined };
2119
- }`;
2120
- }
2121
- }
2122
2415
  var VALID_JS_IDENTIFIER;
2123
2416
  var init_bundler = __esm({
2124
2417
  "src/commands/bundle/bundler.ts"() {
2125
2418
  "use strict";
2419
+ init_config_classifier();
2126
2420
  init_package_manager();
2127
2421
  init_tmp();
2128
2422
  init_build_cache();
@@ -2131,12 +2425,12 @@ var init_bundler = __esm({
2131
2425
  });
2132
2426
 
2133
2427
  // src/commands/bundle/upload.ts
2134
- import fs9 from "fs-extra";
2428
+ import fs11 from "fs-extra";
2135
2429
  function sanitizeUrl(url) {
2136
2430
  return url.split("?")[0];
2137
2431
  }
2138
2432
  async function uploadBundleToUrl(filePath, url, timeoutMs = 3e4) {
2139
- const bundleContent = await fs9.readFile(filePath);
2433
+ const bundleContent = await fs11.readFile(filePath);
2140
2434
  const doUpload = async (attempt) => {
2141
2435
  const response = await fetch(url, {
2142
2436
  method: "PUT",
@@ -2215,8 +2509,8 @@ var init_api_client = __esm({
2215
2509
  });
2216
2510
 
2217
2511
  // src/commands/bundle/dockerfile.ts
2218
- import path10 from "path";
2219
- import fs10 from "fs-extra";
2512
+ import path12 from "path";
2513
+ import fs12 from "fs-extra";
2220
2514
  function buildDockerfileContent(platform, includedFolders) {
2221
2515
  const bundleFile = platform === "web" ? "walker.js" : "bundle.mjs";
2222
2516
  const lines = [
@@ -2226,21 +2520,21 @@ function buildDockerfileContent(platform, includedFolders) {
2226
2520
  `COPY ${bundleFile} /app/flow/${bundleFile}`
2227
2521
  ];
2228
2522
  for (const folder of includedFolders) {
2229
- const name = path10.basename(folder);
2523
+ const name = path12.basename(folder);
2230
2524
  lines.push(`COPY ${name}/ /app/flow/${name}/`);
2231
2525
  }
2232
2526
  lines.push("", `ENV BUNDLE=/app/flow/${bundleFile}`, "", "EXPOSE 8080", "");
2233
2527
  return lines.join("\n");
2234
2528
  }
2235
2529
  async function generateDockerfile(outputDir, platform, logger, customFile, includedFolders) {
2236
- const destPath = path10.join(outputDir, "Dockerfile");
2237
- if (customFile && await fs10.pathExists(customFile)) {
2238
- 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);
2239
2533
  logger.info(`Dockerfile: ${destPath} (copied from ${customFile})`);
2240
2534
  return;
2241
2535
  }
2242
2536
  const dockerfile = buildDockerfileContent(platform, includedFolders || []);
2243
- await fs10.writeFile(destPath, dockerfile);
2537
+ await fs12.writeFile(destPath, dockerfile);
2244
2538
  logger.info(`Dockerfile: ${destPath}`);
2245
2539
  }
2246
2540
  var init_dockerfile = __esm({
@@ -2250,15 +2544,15 @@ var init_dockerfile = __esm({
2250
2544
  });
2251
2545
 
2252
2546
  // src/commands/bundle/index.ts
2253
- import path11 from "path";
2254
- import fs11 from "fs-extra";
2547
+ import path13 from "path";
2548
+ import fs13 from "fs-extra";
2255
2549
  import { getPlatform as getPlatform2 } from "@walkeros/core";
2256
2550
  function resolveOutputPath(output, buildOptions) {
2257
- const resolved = path11.resolve(output);
2258
- const ext = path11.extname(resolved);
2259
- 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) {
2260
2554
  const filename = buildOptions.platform === "browser" ? "walker.js" : "bundle.mjs";
2261
- return path11.join(resolved, filename);
2555
+ return path13.join(resolved, filename);
2262
2556
  }
2263
2557
  return resolved;
2264
2558
  }
@@ -2288,7 +2582,7 @@ async function bundleCommand(options) {
2288
2582
  } catch {
2289
2583
  throw new Error("Invalid JSON received on stdin");
2290
2584
  }
2291
- configPath = path11.resolve(process.cwd(), "stdin.config.json");
2585
+ configPath = path13.resolve(process.cwd(), "stdin.config.json");
2292
2586
  } else {
2293
2587
  const file = options.config || "bundle.config.json";
2294
2588
  configPath = resolveAsset(file, "config");
@@ -2342,19 +2636,19 @@ async function bundleCommand(options) {
2342
2636
  if (uploadUrl) {
2343
2637
  await uploadBundleToUrl(buildOptions.output, uploadUrl);
2344
2638
  logger.info(`Uploaded to: ${sanitizeUrl(uploadUrl)}`);
2345
- await fs11.remove(buildOptions.output);
2639
+ await fs13.remove(buildOptions.output);
2346
2640
  }
2347
2641
  if (!options.json && !options.all && options.stats && stats) {
2348
2642
  displayStats(stats, logger);
2349
2643
  }
2350
2644
  if (writingToStdout && !options.json) {
2351
- const bundleContent = await fs11.readFile(buildOptions.output);
2645
+ const bundleContent = await fs13.readFile(buildOptions.output);
2352
2646
  await writeResult(bundleContent, {});
2353
2647
  }
2354
2648
  if (options.dockerfile && options.output) {
2355
2649
  const platform = getPlatform2(flowSettings);
2356
2650
  if (platform) {
2357
- const outputDir = path11.dirname(buildOptions.output);
2651
+ const outputDir = path13.dirname(buildOptions.output);
2358
2652
  const customFile = typeof options.dockerfile === "string" ? options.dockerfile : void 0;
2359
2653
  await generateDockerfile(
2360
2654
  outputDir,
@@ -2373,7 +2667,7 @@ async function bundleCommand(options) {
2373
2667
  }
2374
2668
  }
2375
2669
  }
2376
- const duration = timer.end() / 1e3;
2670
+ const duration = timer.end();
2377
2671
  const successCount = results.filter((r2) => r2.success).length;
2378
2672
  const failureCount = results.filter((r2) => !r2.success).length;
2379
2673
  if (options.json) {
@@ -2410,7 +2704,7 @@ Build Summary: ${successCount}/${results.length} succeeded`
2410
2704
  }
2411
2705
  process.exit(0);
2412
2706
  } catch (error) {
2413
- const duration = timer.getElapsed() / 1e3;
2707
+ const duration = timer.getElapsed();
2414
2708
  const errorMessage = getErrorMessage(error);
2415
2709
  if (options.json) {
2416
2710
  const jsonError = createErrorOutput(errorMessage, duration);
@@ -2425,7 +2719,7 @@ Build Summary: ${successCount}/${results.length} succeeded`
2425
2719
  }
2426
2720
  async function bundle(configOrPath, options = {}) {
2427
2721
  let rawConfig;
2428
- let configPath = path11.resolve(process.cwd(), "walkeros.config.json");
2722
+ let configPath = path13.resolve(process.cwd(), "walkeros.config.json");
2429
2723
  if (typeof configOrPath === "string") {
2430
2724
  configPath = resolveAsset(configOrPath, "config");
2431
2725
  rawConfig = await loadJsonConfig(configPath);
@@ -2481,6 +2775,45 @@ var init_bundle = __esm({
2481
2775
  }
2482
2776
  });
2483
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
+
2484
2817
  // src/runtime/cache.ts
2485
2818
  var cache_exports = {};
2486
2819
  __export(cache_exports, {
@@ -2535,15 +2868,15 @@ __export(utils_exports, {
2535
2868
  isPreBuiltConfig: () => isPreBuiltConfig,
2536
2869
  prepareBundleForRun: () => prepareBundleForRun
2537
2870
  });
2538
- import path13 from "path";
2539
- import fs14 from "fs-extra";
2871
+ import path17 from "path";
2872
+ import fs16 from "fs-extra";
2540
2873
  async function prepareBundleForRun(configPath, options) {
2541
2874
  const tempDir = getTmpPath(
2542
2875
  void 0,
2543
2876
  `run-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
2544
2877
  );
2545
- await fs14.ensureDir(tempDir);
2546
- const tempPath = path13.join(tempDir, "bundle.mjs");
2878
+ await fs16.ensureDir(tempDir);
2879
+ const tempPath = path17.join(tempDir, "bundle.mjs");
2547
2880
  await bundle(configPath, {
2548
2881
  cache: true,
2549
2882
  verbose: options.verbose,
@@ -2555,7 +2888,12 @@ async function prepareBundleForRun(configPath, options) {
2555
2888
  platform: "node"
2556
2889
  }
2557
2890
  });
2558
- return tempPath;
2891
+ return {
2892
+ bundlePath: tempPath,
2893
+ cleanup: async () => {
2894
+ await fs16.remove(tempDir);
2895
+ }
2896
+ };
2559
2897
  }
2560
2898
  function isPreBuiltConfig(configPath) {
2561
2899
  return configPath.endsWith(".mjs") || configPath.endsWith(".js") || configPath.endsWith(".cjs");
@@ -2571,1062 +2909,703 @@ var init_utils3 = __esm({
2571
2909
  // src/index.ts
2572
2910
  init_bundle();
2573
2911
 
2574
- // src/commands/simulate/simulator.ts
2575
- 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";
2576
2923
 
2577
2924
  // ../collector/dist/index.mjs
2578
2925
  import { assign as o } from "@walkeros/core";
2579
2926
  import { assign as r, createLogger as i } from "@walkeros/core";
2580
- 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";
2581
- import { isArray as b } from "@walkeros/core";
2582
- import { tryCatch as y, tryCatchAsync as v } from "@walkeros/core";
2583
- import { getMappingValue as k, tryCatchAsync as O } from "@walkeros/core";
2584
- import { isObject as C, tryCatchAsync as j, useHooks as q } from "@walkeros/core";
2585
- import { assign as L, getId as Q, isFunction as V, isString as _ } from "@walkeros/core";
2586
- import { isObject as K } from "@walkeros/core";
2587
- import { getGrantedConsent as en, processEventMapping as tn, tryCatchAsync as on, useHooks as sn } from "@walkeros/core";
2588
- import { useHooks as an, tryCatchAsync as cn } from "@walkeros/core";
2589
- 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" };
2590
- var t = { Commands: e, Utils: { Storage: { Cookie: "cookie", Local: "local", Session: "session" } } };
2591
- function s(n, e2) {
2592
- let t2 = false;
2593
- const s2 = {};
2594
- return Object.entries(e2).forEach(([n2, e3]) => {
2595
- const o2 = !!e3;
2596
- s2[n2] = o2, t2 = t2 || o2;
2597
- }), n.consent = o(n.consent, s2), { update: s2, runQueue: t2 };
2598
- }
2599
- function D(n) {
2600
- const e2 = {};
2601
- for (const [t2, o2] of Object.entries(n)) o2.config?.next ? e2[t2] = { next: o2.config.next } : e2[t2] = {};
2602
- return e2;
2603
- }
2604
- function A(n, e2) {
2605
- const t2 = n.config || {}, o2 = n[e2];
2606
- return void 0 !== o2 ? { config: { ...t2, [e2]: o2 }, chainValue: o2 } : { config: t2, chainValue: void 0 };
2607
- }
2608
- function E(n, e2 = {}) {
2609
- if (!n) return [];
2610
- if (Array.isArray(n)) return n;
2611
- const t2 = [], o2 = /* @__PURE__ */ new Set();
2612
- let s2 = n;
2613
- for (; s2 && e2[s2] && !o2.has(s2); ) {
2614
- o2.add(s2), t2.push(s2);
2615
- const n2 = e2[s2].next;
2616
- if (Array.isArray(n2)) {
2617
- 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);
2618
2958
  break;
2619
2959
  }
2620
- s2 = n2;
2960
+ s = e2;
2621
2961
  }
2622
- return t2;
2962
+ return t;
2623
2963
  }
2624
- async function x(n, e2, t2) {
2625
- if (e2.init && !e2.config.init) {
2626
- 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) };
2627
- s2.debug("init");
2628
- 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);
2629
2969
  if (false === i2) return false;
2630
- 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");
2631
2971
  }
2632
2972
  return true;
2633
2973
  }
2634
- async function P(n, e2, t2, o2, s2, r2) {
2635
- 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 } : {} } };
2636
2976
  a2.debug("push", { event: o2.name });
2637
- const u2 = await q(e2.push, "TransformerPush", n.hooks)(o2, c2);
2977
+ const u2 = await U(n.push, "TransformerPush", e.hooks)(o2, c2);
2638
2978
  return a2.debug("push done"), u2;
2639
2979
  }
2640
- async function S(n, e2, t2, o2, s2, r2) {
2641
- let i2 = o2, a2 = r2;
2642
- for (const o3 of t2) {
2643
- const t3 = e2[o3];
2644
- if (!t3) {
2645
- 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}`);
2646
2987
  continue;
2647
2988
  }
2648
- if (!await j(x)(n, t3, o3)) return n.logger.error(`Transformer init failed: ${o3}`), null;
2649
- 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);
2650
- if (false === r3) return null;
2651
- if (r3 && "object" == typeof r3) {
2652
- const { event: t4, respond: o4, next: c2 } = r3;
2653
- if (o4 && (a2 = o4), c2) {
2654
- const o5 = E(c2, D(e2));
2655
- return o5.length > 0 ? S(n, e2, o5, t4 || i2, s2, a2) : (n.logger.warn(`Branch target not found: ${JSON.stringify(c2)}`), null);
2656
- }
2657
- t4 && (i2 = t4);
2658
- }
2659
- }
2660
- return i2;
2661
- }
2662
- function $(n) {
2663
- return n && C(n) ? n : {};
2664
- }
2665
- async function R(n, e2, t2) {
2666
- const { code: o2, config: s2 = {}, env: r2 = {}, primary: i2, next: a2 } = t2;
2667
- let c2, u2;
2668
- 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) => {
2669
- c2 = s2.ingest ? await k(e3, s2.ingest, { collector: n }) : void 0;
2670
- }, setRespond: (n2) => {
2671
- u2 = n2;
2672
- } }, m2 = await O(o2)(d2);
2673
- if (!m2) return;
2674
- const p2 = m2.type || "unknown", h2 = n.logger.scope(p2).scope(e2);
2675
- return g2.logger = h2, i2 && (m2.config = { ...m2.config, primary: i2 }), m2;
2676
- }
2677
- async function T(n, e2 = {}) {
2678
- const t2 = {};
2679
- for (const [o2, s2] of Object.entries(e2)) {
2680
- const { config: e3 = {} } = s2;
2681
- if (e3.require && e3.require.length > 0) {
2682
- 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;
2995
+ continue;
2996
+ }
2997
+ if (void 0 !== r3.config?.mock) {
2998
+ e.logger.scope(`transformer:${r3.type || "unknown"}`).debug("mock"), a2 = r3.config.mock;
2683
2999
  continue;
2684
3000
  }
2685
- const r2 = await R(n, o2, s2);
2686
- r2 && (t2[o2] = r2);
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
+ }
2687
3073
  }
2688
- return t2;
3074
+ return { event: a2, respond: c2 };
2689
3075
  }
2690
- async function I(n, e2, t2) {
2691
- const o2 = n.on, s2 = o2[e2] || [], r2 = b(t2) ? t2 : [t2];
2692
- r2.forEach((n2) => {
2693
- s2.push(n2);
2694
- }), o2[e2] = s2, await G(n, e2, r2);
3076
+ function oe(e) {
3077
+ return e && N(e) ? e : {};
2695
3078
  }
2696
- function B(n, e2, t2, o2, s2) {
2697
- if (!e2.on) return;
2698
- 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) };
2699
- 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;
2700
3085
  }
2701
- async function G(n, e2, o2, s2) {
2702
- let r2, i2 = o2 || [];
2703
- switch (o2 || (i2 = n.on[e2] || []), e2) {
2704
- case t.Commands.Consent:
2705
- r2 = s2 || n.consent;
2706
- break;
2707
- case t.Commands.Session:
2708
- r2 = n.session;
2709
- break;
2710
- case t.Commands.User:
2711
- r2 = s2 || n.user;
2712
- break;
2713
- case t.Commands.Custom:
2714
- r2 = s2 || n.custom;
2715
- break;
2716
- case t.Commands.Globals:
2717
- r2 = s2 || n.globals;
2718
- break;
2719
- case t.Commands.Config:
2720
- r2 = s2 || n.config;
2721
- break;
2722
- case t.Commands.Ready:
2723
- case t.Commands.Run:
2724
- default:
2725
- r2 = void 0;
2726
- }
2727
- let a2 = false;
2728
- for (const t2 of Object.values(n.sources)) if (t2.on) {
2729
- false === await v(t2.on)(e2, r2) && (a2 = true);
2730
- }
2731
- if (Object.entries(n.destinations).forEach(([t2, o3]) => {
2732
- if (o3.on) {
2733
- if (!o3.config.init) return o3.queueOn = o3.queueOn || [], void o3.queueOn.push({ type: e2, data: r2 });
2734
- B(n, o3, t2, e2, r2);
2735
- }
2736
- }), (Object.keys(n.pending.sources).length > 0 || Object.keys(n.pending.destinations).length > 0) && await (async function(n2, e3) {
2737
- for (const [t2, o3] of Object.entries(n2.pending.sources)) {
2738
- if (!n2.pending.sources[t2] || n2.sources[t2]) continue;
2739
- const s3 = o3.config?.require;
2740
- if (!s3) continue;
2741
- const r3 = s3.indexOf(e3);
2742
- if (-1 === r3) continue;
2743
- if (s3.splice(r3, 1), s3.length > 0) continue;
2744
- delete n2.pending.sources[t2];
2745
- const i3 = await R(n2, t2, o3);
2746
- i3 && (n2.sources[t2] = i3);
2747
- }
2748
- for (const [t2, o3] of Object.entries(n2.pending.destinations)) {
2749
- if (!n2.pending.destinations[t2] || n2.destinations[t2]) continue;
2750
- const s3 = o3.config?.require;
2751
- if (!s3) continue;
2752
- const r3 = s3.indexOf(e3);
2753
- if (-1 === r3) continue;
2754
- if (s3.splice(r3, 1), s3.length > 0) continue;
2755
- delete n2.pending.destinations[t2];
2756
- const i3 = M(o3);
2757
- false !== i3.config.queue && (i3.queuePush = [...n2.queue]), n2.destinations[t2] = i3;
2758
- }
2759
- })(n, e2), !i2.length) return !a2;
2760
- switch (e2) {
2761
- case t.Commands.Consent:
2762
- !(function(n2, e3, t2) {
2763
- const o3 = t2 || n2.consent;
2764
- e3.forEach((e4) => {
2765
- Object.keys(o3).filter((n3) => n3 in e4).forEach((t3) => {
2766
- y(e4[t3])(n2, o3);
2767
- });
2768
- });
2769
- })(n, i2, s2);
2770
- break;
2771
- case t.Commands.Ready:
2772
- case t.Commands.Run:
2773
- !(function(n2, e3) {
2774
- n2.allowed && e3.forEach((e4) => {
2775
- y(e4)(n2);
2776
- });
2777
- })(n, i2);
2778
- break;
2779
- case t.Commands.Session:
2780
- !(function(n2, e3) {
2781
- if (!n2.session) return;
2782
- e3.forEach((e4) => {
2783
- y(e4)(n2, n2.session);
2784
- });
2785
- })(n, i2);
2786
- break;
2787
- default:
2788
- i2.forEach((e3) => {
2789
- "function" == typeof e3 && y(e3)(n, r2);
2790
- });
2791
- }
2792
- return !a2;
2793
- }
2794
- async function H(n, e2, t2) {
2795
- const { code: o2, config: s2 = {}, env: r2 = {}, before: i2 } = e2;
2796
- if (!d(o2.push)) return J({ ok: false, failed: { invalid: { type: "invalid", error: "Destination code must have a push method" } } });
2797
- const a2 = t2 || s2 || { init: false }, c2 = i2 ? { ...a2, before: i2 } : a2, u2 = { ...o2, config: c2, env: W(o2.env, r2) };
2798
- let l2 = u2.config.id;
2799
- if (!l2) do {
2800
- l2 = f(4);
2801
- } while (n.destinations[l2]);
2802
- return n.destinations[l2] = u2, false !== u2.config.queue && (u2.queuePush = [...n.queue]), U(n, void 0, {}, { [l2]: u2 });
2803
- }
2804
- async function U(n, e2, t2 = {}, o2) {
2805
- const { allowed: s2, consent: r2, globals: i2, user: u2 } = n;
2806
- if (!s2) return J({ ok: false });
2807
- e2 && (n.queue.push(e2), n.status.in++), o2 || (o2 = n.destinations);
2808
- const f2 = await Promise.all(Object.entries(o2 || {}).map(async ([o3, s3]) => {
2809
- let f3 = (s3.queuePush || []).map((n2) => ({ ...n2, consent: r2 }));
2810
- if (s3.queuePush = [], e2) {
2811
- const n2 = c(e2);
2812
- f3.push(n2);
2813
- }
2814
- if (!f3.length && !s3.queueOn?.length) return { id: o3, destination: s3, skipped: true };
2815
- if (!f3.length && s3.queueOn?.length) {
2816
- const e3 = await h(z)(n, s3, o3);
2817
- return { id: o3, destination: s3, skipped: !e3 };
2818
- }
2819
- const g3 = [], d3 = f3.filter((n2) => {
2820
- const e3 = l(s3.config.consent, r2, n2.consent);
2821
- return !e3 || (n2.consent = e3, g3.push(n2), false);
2822
- });
2823
- if (s3.queuePush.push(...d3), !g3.length) return { id: o3, destination: s3, queue: f3 };
2824
- if (!await h(z)(n, s3, o3)) return { id: o3, destination: s3, queue: f3 };
2825
- let m3, p2;
2826
- s3.dlq || (s3.dlq = []);
2827
- const w2 = (function(n2, e3) {
2828
- const t3 = n2.config.before;
2829
- return t3 ? E(t3, D(e3)) : [];
2830
- })(s3, n.transformers);
2831
- let b2 = 0;
2832
- return await Promise.all(g3.map(async (e3) => {
2833
- e3.globals = a(i2, e3.globals), e3.user = a(u2, e3.user);
2834
- let r3 = e3;
2835
- if (w2.length > 0 && n.transformers && Object.keys(n.transformers).length > 0) {
2836
- const o4 = await S(n, n.transformers, w2, e3, t2.ingest, t2.respond);
2837
- if (null === o4) return e3;
2838
- r3 = o4;
2839
- }
2840
- const c2 = Date.now(), f4 = await h(F, (e4) => {
2841
- const t3 = s3.type || "unknown";
2842
- n.logger.scope(t3).error("Push failed", { error: e4, event: r3.name }), m3 = e4, s3.dlq.push([r3, e4]);
2843
- })(n, s3, o3, r3, t2.ingest, t2.respond);
2844
- return b2 += Date.now() - c2, void 0 !== f4 && (p2 = f4), e3;
2845
- })), { id: o3, destination: s3, error: m3, response: p2, totalDuration: b2 };
2846
- })), g2 = {}, d2 = {}, m2 = {};
2847
- for (const e3 of f2) {
2848
- if (e3.skipped) continue;
2849
- const t3 = { type: e3.destination.type || "unknown", data: e3.response };
2850
- n.status.destinations[e3.id] || (n.status.destinations[e3.id] = { count: 0, failed: 0, duration: 0 });
2851
- const o3 = n.status.destinations[e3.id], s3 = Date.now();
2852
- 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++);
2853
- }
2854
- return J({ event: e2, ...Object.keys(g2).length && { done: g2 }, ...Object.keys(d2).length && { queued: d2 }, ...Object.keys(m2).length && { failed: m2 } });
2855
- }
2856
- async function z(n, e2, t2) {
2857
- if (e2.init && !e2.config.init) {
2858
- 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) };
2859
- s2.debug("init");
2860
- const i2 = await w(e2.init, "DestinationInit", n.hooks)(r2);
2861
- if (false === i2) return i2;
2862
- if (e2.config = { ...i2 || e2.config, init: true }, e2.queueOn?.length) {
2863
- const o3 = e2.queueOn;
2864
- e2.queueOn = [];
2865
- for (const { type: s3, data: r3 } of o3) B(n, e2, t2, s3, r3);
2866
- }
2867
- s2.debug("init done");
2868
- }
2869
- return true;
2870
- }
2871
- async function F(n, e2, t2, o2, s2, r2) {
2872
- const { config: i2 } = e2, a2 = await p(o2, i2, n);
2873
- if (a2.ignore) return false;
2874
- 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 || "* *";
2875
- if (!d2?.batch || !e2.pushBatch) {
2876
- f2.debug("push", { event: a2.event.name });
2877
- const t3 = await w(e2.push, "DestinationPush", n.hooks)(a2.event, l2);
2878
- return f2.debug("push done"), t3;
2879
- }
2880
- {
2881
- if (e2.batches = e2.batches || {}, !e2.batches[m2]) {
2882
- const o4 = { key: m2, events: [], data: [] };
2883
- e2.batches[m2] = { batched: o4, batchFn: u(() => {
2884
- 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 } : {} } };
2885
- 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 = [];
2886
- }, d2.batch) };
2887
- }
2888
- const o3 = e2.batches[m2];
2889
- o3.batched.events.push(a2.event), g(a2.data) && o3.batched.data.push(a2.data), o3.batchFn();
2890
- }
2891
- return true;
2892
- }
2893
- function J(n) {
2894
- return { ok: !n?.failed, ...n };
2895
- }
2896
- function M(n) {
2897
- const { code: e2, config: t2 = {}, env: o2 = {} } = n, { config: s2 } = A(n, "before"), r2 = { ...e2.config, ...t2, ...s2 }, i2 = W(e2.env, o2);
2898
- return { ...e2, config: r2, env: i2 };
2899
- }
2900
- async function N(n, e2 = {}) {
2901
- const t2 = {};
2902
- for (const [o2, s2] of Object.entries(e2)) s2.config?.require?.length ? n.pending.destinations[o2] = s2 : t2[o2] = M(s2);
2903
- return t2;
2904
- }
2905
- function W(n, e2) {
2906
- return n || e2 ? e2 ? n && m(n) && m(e2) ? { ...n, ...e2 } : e2 : n : {};
2907
- }
2908
- async function X(n, e2, t2) {
2909
- const o2 = Object.entries(n).map(async ([n2, o3]) => {
2910
- const s2 = o3.destroy;
2911
- if (!s2) return;
2912
- const r2 = o3.type || "unknown", i2 = t2.scope(r2), a2 = { id: n2, config: o3.config, env: o3.env ?? {}, logger: i2 };
2913
- try {
2914
- await Promise.race([s2(a2), new Promise((t3, o4) => setTimeout(() => o4(new Error(`${e2} '${n2}' destroy timed out`)), 5e3))]);
2915
- } catch (t3) {
2916
- i2.error(`${e2} '${n2}' destroy failed: ${t3}`);
2917
- }
2918
- });
2919
- await Promise.allSettled(o2);
2920
- }
2921
- async function Y(n, e2, o2, r2) {
2922
- let i2, a2, c2 = false, u2 = false;
2923
- switch (e2) {
2924
- case t.Commands.Config:
2925
- K(o2) && (L(n.config, o2, { shallow: false }), a2 = o2, c2 = true);
2926
- break;
2927
- case t.Commands.Consent:
2928
- if (K(o2)) {
2929
- const { update: e3, runQueue: t2 } = s(n, o2);
2930
- a2 = e3, c2 = true, u2 = t2;
2931
- }
2932
- break;
2933
- case t.Commands.Custom:
2934
- K(o2) && (n.custom = L(n.custom, o2), a2 = o2, c2 = true);
2935
- break;
2936
- case t.Commands.Destination:
2937
- K(o2) && ("code" in o2 && K(o2.code) ? i2 = await H(n, o2, r2) : V(o2.push) && (i2 = await H(n, { code: o2 }, r2)));
2938
- break;
2939
- case t.Commands.Globals:
2940
- K(o2) && (n.globals = L(n.globals, o2), a2 = o2, c2 = true);
2941
- break;
2942
- case t.Commands.On:
2943
- _(o2) && await I(n, o2, r2);
2944
- break;
2945
- case t.Commands.Ready:
2946
- c2 = true;
2947
- break;
2948
- case t.Commands.Run:
2949
- i2 = await nn(n, o2), c2 = true;
2950
- break;
2951
- case t.Commands.Session:
2952
- c2 = true;
2953
- break;
2954
- case t.Commands.Shutdown:
2955
- await (async function(n2) {
2956
- const e3 = n2.logger;
2957
- await X(n2.sources, "source", e3), await X(n2.destinations, "destination", e3), await X(n2.transformers, "transformer", e3), await X(n2.stores, "store", e3);
2958
- })(n);
2959
- break;
2960
- case t.Commands.User:
2961
- K(o2) && (L(n.user, o2, { shallow: false }), a2 = o2, c2 = true);
2962
- }
2963
- return c2 && await G(n, e2, void 0, a2), u2 && (i2 = await U(n)), i2 || J({ ok: true });
2964
- }
2965
- function Z(n, e2) {
2966
- if (!e2.name) throw new Error("Event name is required");
2967
- const [t2, o2] = e2.name.split(" ");
2968
- if (!t2 || !o2) throw new Error("Event name is invalid");
2969
- ++n.count;
2970
- 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;
2971
- 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 };
2972
- }
2973
- async function nn(n, e2) {
2974
- 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) => {
2975
- n2.queuePush = [];
2976
- }), n.queue = [], n.round++;
2977
- return await U(n);
2978
- }
2979
- function rn(n, e2) {
2980
- return sn(async (t2, o2 = {}) => await on(async () => {
2981
- const s2 = Date.now(), { id: r2, ingest: i2, respond: a2, mapping: c2, preChain: u2 } = o2;
2982
- let f2 = t2;
2983
- const l2 = i2 ? Object.freeze(i2) : void 0;
2984
- if (c2) {
2985
- const e3 = await tn(f2, c2, n);
2986
- if (e3.ignore) return J({ ok: true });
2987
- if (c2.consent) {
2988
- if (!en(c2.consent, n.consent, e3.event.consent)) return J({ ok: true });
2989
- }
2990
- f2 = e3.event;
2991
- }
2992
- if (u2?.length && n.transformers && Object.keys(n.transformers).length > 0) {
2993
- const e3 = await S(n, n.transformers, u2, f2, l2, a2);
2994
- if (null === e3) return J({ ok: true });
2995
- f2 = e3;
2996
- }
2997
- const g2 = e2(f2), d2 = Z(n, g2), m2 = await U(n, d2, { id: r2, ingest: l2, respond: a2 });
2998
- if (r2) {
2999
- n.status.sources[r2] || (n.status.sources[r2] = { count: 0, duration: 0 });
3000
- const e3 = n.status.sources[r2];
3001
- e3.count++, e3.lastAt = Date.now(), e3.duration += Date.now() - s2;
3002
- }
3003
- return m2;
3004
- }, () => J({ ok: false }))(), "Push", n.hooks);
3005
- }
3006
- async function un(n) {
3007
- 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.1.0", sources: {}, pending: { sources: {}, destinations: {} }, push: void 0, command: void 0 };
3008
- 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) {
3009
- return an(async (t3, o3, s3) => await cn(async () => await e3(n2, t3, o3, s3), () => J({ ok: false }))(), "Command", n2.hooks);
3010
- })(a2, Y);
3011
- const c2 = n.stores || {};
3012
- return a2.stores = await (async function(n2, e3 = {}) {
3013
- const t3 = {};
3014
- for (const [o3, s3] of Object.entries(e3)) {
3015
- 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);
3016
- t3[o3] = u2;
3017
- }
3018
- return t3;
3019
- })(a2, c2), (function(n2, e3, t3) {
3020
- const o3 = /* @__PURE__ */ new Map();
3021
- for (const [t4, s4] of Object.entries(n2)) e3[t4] && o3.set(s4, e3[t4]);
3022
- if (0 !== o3.size) {
3023
- for (const n3 of [t3.transformers, t3.destinations, t3.sources]) if (n3) for (const e4 of Object.values(n3)) s3(e4.env);
3024
- }
3025
- function s3(n3) {
3026
- if (n3) {
3027
- for (const [e4, t4] of Object.entries(n3)) if ("object" == typeof t4 && null !== t4) {
3028
- const s4 = o3.get(t4);
3029
- s4 && (n3[e4] = s4);
3030
- }
3031
- }
3032
- }
3033
- })(c2, a2.stores, n), a2.destinations = await N(a2, n.destinations || {}), a2.transformers = await (async function(n2, e3 = {}) {
3034
- const t3 = {};
3035
- for (const [o3, s3] of Object.entries(e3)) {
3036
- 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);
3037
- t3[o3] = f2;
3038
- }
3039
- return t3;
3040
- })(a2, n.transformers || {}), a2;
3041
- }
3042
- async function fn(n) {
3043
- n = n || {};
3044
- const e2 = await un(n), t2 = (o2 = e2, { type: "elb", config: {}, push: async (n2, e3, t3, s3, r3, i3) => {
3045
- if ("string" == typeof n2 && n2.startsWith("walker ")) {
3046
- const s4 = n2.replace("walker ", "");
3047
- return o2.command(s4, e3, t3);
3048
- }
3049
- let a3;
3050
- if ("string" == typeof n2) a3 = { name: n2 }, e3 && "object" == typeof e3 && !Array.isArray(e3) && (a3.data = e3);
3051
- else {
3052
- if (!n2 || "object" != typeof n2) return J({ ok: false });
3053
- a3 = n2, e3 && "object" == typeof e3 && !Array.isArray(e3) && (a3.data = { ...a3.data || {}, ...e3 });
3054
- }
3055
- 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);
3056
- } });
3057
- var o2;
3058
- e2.sources.elb = t2;
3059
- const s2 = await T(e2, n.sources || {});
3060
- Object.assign(e2.sources, s2);
3061
- const { consent: r2, user: i2, globals: a2, custom: c2 } = n;
3062
- 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");
3063
- let u2 = t2.push;
3064
- const f2 = Object.values(e2.sources).filter((n2) => "elb" !== n2.type), l2 = f2.find((n2) => n2.config.primary);
3065
- return l2 ? u2 = l2.push : f2.length > 0 && (u2 = f2[0].push), { collector: e2, elb: u2 };
3066
- }
3067
- function ln(n) {
3068
- if (null === n || "object" != typeof n) return n;
3069
- if (Array.isArray(n)) return n.map(ln);
3070
- const e2 = {};
3071
- for (const [t2, o2] of Object.entries(n)) e2[t2] = "function" == typeof o2 ? o2 : ln(o2);
3072
- return e2;
3073
- }
3074
- function gn(n) {
3075
- const e2 = [], { simulation: t2, ...o2 } = n, s2 = ln(o2);
3076
- for (const n2 of t2) {
3077
- const t3 = n2.startsWith("call:") ? n2.slice(5) : n2, o3 = t3.split(".");
3078
- let r2 = s2;
3079
- 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]];
3080
3092
  const i2 = o3[o3.length - 1];
3081
3093
  if (null == r2 || !(i2 in r2)) continue;
3082
3094
  const a2 = r2[i2];
3083
- "function" == typeof a2 && (r2[i2] = function(...n3) {
3084
- 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);
3085
3097
  });
3086
3098
  }
3087
- return { wrappedEnv: s2, calls: e2 };
3088
- }
3089
- async function dn(n) {
3090
- const e2 = Date.now();
3091
- try {
3092
- switch (n.step) {
3093
- case "transformer":
3094
- return await (async function(n2, e3) {
3095
- const { code: t2, config: o2 = {}, event: s2 } = n2, { collector: r2 } = await fn({ transformers: { sim: { code: t2, config: o2 } } }), i2 = r2.transformers?.sim;
3096
- if (!i2) throw new Error("Transformer failed to initialize");
3097
- const a2 = await i2.push(s2, { collector: r2, logger: r2.logger.scope("transformer").scope("sim"), id: "sim", config: i2.config, env: i2.config?.env || {} });
3098
- let c2;
3099
- c2 = false === a2 ? [] : null == a2 ? [s2] : [a2.event || s2];
3100
- return { step: "transformer", name: n2.name, events: c2, calls: [], duration: Date.now() - e3 };
3101
- })(n, e2);
3102
- case "source":
3103
- return await (async function(n2, e3) {
3104
- 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);
3105
- void 0 !== a2 && await g2(c2?.type, c2?.options)(a2);
3106
- return { step: "source", name: n2.name, events: f2, calls: [], duration: Date.now() - e3 };
3107
- })(n, e2);
3108
- case "destination":
3109
- return await (async function(n2, e3) {
3110
- const { code: t2, config: o2 = {}, event: s2, consent: r2, env: i2, track: a2 } = n2, c2 = { functional: true, marketing: true, analytics: true };
3111
- let u2 = [], f2 = i2;
3112
- if (i2 && a2 && a2.length > 0) {
3113
- const n3 = gn({ ...i2, simulation: a2 });
3114
- f2 = n3.wrappedEnv, u2 = n3.calls;
3115
- }
3116
- const l2 = { ...o2 };
3117
- f2 && (l2.env = f2);
3118
- const { collector: g2 } = await fn({ consent: r2 || c2, destinations: { sim: { code: t2, config: l2 } } });
3119
- return await g2.push(s2), { step: "destination", name: n2.name, events: [], calls: u2, duration: Date.now() - e3 };
3120
- })(n, e2);
3121
- }
3122
- } catch (t2) {
3123
- return { step: n.step, name: n.name, events: [], calls: [], duration: Date.now() - e2, error: t2 instanceof Error ? t2 : new Error(String(t2)) };
3124
- }
3099
+ return { wrappedEnv: s, calls: n };
3125
3100
  }
3126
3101
 
3127
- // src/commands/simulate/simulator.ts
3102
+ // src/commands/push/index.ts
3128
3103
  init_cli_logger();
3129
3104
  init_core();
3130
- init_config();
3131
3105
  init_tmp();
3106
+ init_config();
3107
+ init_utils();
3108
+ init_bundler();
3132
3109
 
3133
- // src/commands/simulate/env-loader.ts
3134
- async function loadDestinationEnvs(destinations) {
3135
- const envs = {};
3136
- for (const [destKey, destConfig] of Object.entries(destinations)) {
3137
- const typedConfig = destConfig;
3138
- if (!typedConfig.package) {
3139
- continue;
3140
- }
3141
- try {
3142
- const packageName = typedConfig.package;
3143
- const isDemoPackage = packageName.includes("-demo");
3144
- const importPath = isDemoPackage ? packageName : `${packageName}/dev`;
3145
- const module = await import(importPath);
3146
- const examplesModule = module.examples || module.default?.examples;
3147
- const envModule = examplesModule?.env;
3148
- if (envModule?.push) {
3149
- envs[destKey] = {
3150
- init: envModule.init,
3151
- push: envModule.push,
3152
- simulation: envModule.simulation || []
3153
- };
3154
- }
3155
- } catch {
3156
- }
3157
- }
3158
- return envs;
3159
- }
3160
-
3161
- // src/commands/simulate/simulator.ts
3162
- function callsToUsage(destName, calls) {
3163
- if (!calls.length) return {};
3164
- return {
3165
- [destName]: calls.map((c2) => ({
3166
- type: "call",
3167
- path: c2.fn,
3168
- args: c2.args,
3169
- timestamp: c2.ts
3170
- }))
3171
- };
3172
- }
3173
- async function simulateCore(inputPath, event, options = {}) {
3174
- const logger = createCLILogger({
3175
- verbose: options.verbose || false,
3176
- silent: options.silent || false,
3177
- json: options.json || false
3178
- });
3179
- try {
3180
- logger.debug(`Simulating event: ${JSON.stringify(event)}`);
3181
- const result = await executeSimulation(event, inputPath, options.platform, {
3182
- flow: options.flow,
3183
- step: options.step,
3184
- verbose: options.verbose
3185
- });
3186
- return result;
3187
- } catch (error) {
3188
- const errorMessage = getErrorMessage(error);
3189
- return {
3190
- success: false,
3191
- error: errorMessage
3192
- };
3193
- }
3194
- }
3195
- function formatSimulationResult(result, options = {}) {
3196
- if (options.json) {
3197
- const output = {
3198
- result: result.elbResult,
3199
- usage: result.usage,
3200
- duration: result.duration
3201
- };
3202
- if (result.capturedEvents) {
3203
- output.capturedEvents = result.capturedEvents;
3204
- }
3205
- return JSON.stringify(output, null, 2);
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 {};
3206
3116
  }
3207
- const lines = [];
3208
- if (result.success) {
3209
- lines.push("Simulation completed");
3210
- } else {
3211
- lines.push(`Simulation failed: ${result.error}`);
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
+ );
3212
3148
  }
3213
- if (result.capturedEvents) {
3214
- lines.push(`Captured ${result.capturedEvents.length} event(s)`);
3215
- for (const evt of result.capturedEvents) {
3216
- lines.push(` - ${evt.name || "unknown"}`);
3149
+ for (const step of mockFlags) {
3150
+ const eqIndex = step.indexOf("=");
3151
+ if (eqIndex === -1) {
3152
+ throw new Error(
3153
+ `Invalid --mock format: "${step}". Expected destination.NAME=VALUE`
3154
+ );
3217
3155
  }
3218
- }
3219
- return lines.join("\n");
3220
- }
3221
- async function executeSimulation(event, inputPath, platformOverride, options = {}) {
3222
- const startTime = Date.now();
3223
- const tempDir = getTmpPath();
3224
- try {
3225
- await fs12.ensureDir(tempDir);
3226
- const { flowSettings } = await loadFlowConfig(inputPath, {
3227
- flowName: options.flow
3228
- });
3229
- if (!isObject(event) || !("name" in event) || typeof event.name !== "string") {
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
- 'Event must be an object with a "name" property of type string'
3161
+ `--mock is not supported for sources. Use --simulate source.${parsed.name}`
3232
3162
  );
3233
3163
  }
3234
- const typedEvent = event;
3235
- return await executeConfigSimulation(
3236
- flowSettings,
3237
- typedEvent,
3238
- tempDir,
3239
- startTime,
3240
- options.step
3241
- );
3242
- } catch (error) {
3243
- const duration = Date.now() - startTime;
3244
- return {
3245
- success: false,
3246
- error: getErrorMessage(error),
3247
- duration
3248
- };
3249
- } finally {
3250
- if (tempDir) {
3251
- await fs12.remove(tempDir).catch(() => {
3252
- });
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
+ );
3253
3168
  }
3254
- }
3255
- }
3256
- function parseStepTarget(stepTarget, flowSettings) {
3257
- if (stepTarget) {
3258
- const dotIndex = stepTarget.indexOf(".");
3259
- if (dotIndex > -1) {
3260
- const type = stepTarget.substring(0, dotIndex);
3261
- const name = stepTarget.substring(dotIndex + 1);
3262
- const section = type === "destination" ? flowSettings.destinations : flowSettings.transformers;
3263
- if (!section?.[name]) {
3264
- 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 } };
3265
3199
  }
3266
- return { type, name, config: section[name] };
3267
3200
  }
3268
3201
  }
3269
- if (flowSettings.destinations) {
3270
- const [name, config] = Object.entries(flowSettings.destinations)[0];
3271
- return {
3272
- type: "destination",
3273
- name,
3274
- config
3275
- };
3276
- }
3277
- throw new Error("No destination found in flow config");
3278
- }
3279
- async function executeConfigSimulation(flowSettings, typedEvent, tempDir, startTime, stepTarget) {
3280
- const step = parseStepTarget(stepTarget, flowSettings);
3281
- if (step.type === "destination") {
3282
- const packageName = step.config.package;
3283
- if (!packageName) {
3284
- throw new Error(`Destination "${step.name}" has no package field`);
3285
- }
3286
- const destModule = await import(packageName);
3287
- const code = destModule.default || Object.values(destModule)[0];
3288
- const destinations = flowSettings.destinations;
3289
- const envs = await loadDestinationEnvs(destinations || {});
3290
- const destEnv = envs[step.name];
3291
- const result = await dn({
3292
- step: "destination",
3293
- name: step.name,
3294
- code,
3295
- event: typedEvent,
3296
- config: step.config.config,
3297
- env: destEnv?.push,
3298
- track: destEnv?.simulation
3299
- });
3300
- const duration = Date.now() - startTime;
3301
- return {
3302
- success: !result.error,
3303
- error: result.error?.message,
3304
- usage: callsToUsage(step.name, result.calls),
3305
- duration,
3306
- logs: []
3307
- };
3308
- }
3309
- if (step.type === "transformer") {
3310
- const packageName = step.config.package;
3311
- if (!packageName) {
3312
- throw new Error(`Transformer "${step.name}" has no package field`);
3313
- }
3314
- const mod = await import(packageName);
3315
- const code = mod.default || Object.values(mod)[0];
3316
- const result = await dn({
3317
- step: "transformer",
3318
- name: step.name,
3319
- code,
3320
- event: typedEvent,
3321
- config: step.config.config
3322
- });
3323
- const duration = Date.now() - startTime;
3324
- return {
3325
- success: !result.error,
3326
- error: result.error?.message,
3327
- capturedEvents: result.events,
3328
- duration,
3329
- usage: {},
3330
- logs: []
3331
- };
3332
- }
3333
- throw new Error(`Unknown step type: ${step.type}`);
3202
+ return overrides;
3334
3203
  }
3335
-
3336
- // src/commands/simulate/source-simulator.ts
3337
- init_core();
3338
- async function loadSourcePackage(packageName) {
3339
- const mainModule = await import(packageName);
3340
- const code = mainModule.default || Object.values(mainModule)[0];
3341
- if (!code || typeof code !== "function") {
3342
- throw new Error(`Package ${packageName} missing source init function`);
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
+ );
3343
3210
  }
3344
- let createTrigger;
3345
- try {
3346
- const devModule = await import(`${packageName}/dev`);
3347
- const examples = devModule.examples || devModule.default?.examples;
3348
- if (examples?.createTrigger && typeof examples.createTrigger === "function") {
3349
- createTrigger = examples.createTrigger;
3350
- }
3351
- } catch {
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
+ );
3352
3216
  }
3353
- return { code, createTrigger };
3354
- }
3355
- async function simulateSourceCLI(flowConfig, sourceInput, options) {
3356
- const startTime = Date.now();
3357
- try {
3358
- const sources = flowConfig.sources;
3359
- if (!sources) {
3360
- throw new Error("Flow config has no sources");
3361
- }
3362
- const sourceConfig = sources[options.sourceStep];
3363
- if (!sourceConfig) {
3364
- const available = Object.keys(sources).join(", ");
3217
+ const name = parts[1];
3218
+ if (!name) {
3219
+ throw new Error(
3220
+ `Invalid step format: "${step}". Missing name after "${prefix}."`
3221
+ );
3222
+ }
3223
+ if (parts.length >= 4) {
3224
+ const chainType = parts[2];
3225
+ if (chainType !== "before" && chainType !== "next") {
3365
3226
  throw new Error(
3366
- `Source "${options.sourceStep}" not found. Available: ${available}`
3227
+ `Invalid chain type: "${chainType}". Use "before" or "next"`
3367
3228
  );
3368
3229
  }
3369
- if (!sourceConfig.package) {
3370
- throw new Error(`Source "${options.sourceStep}" has no package field`);
3371
- }
3372
- const { code, createTrigger } = await loadSourcePackage(
3373
- sourceConfig.package
3374
- );
3375
- if (!createTrigger) {
3230
+ const transformerId = parts[3];
3231
+ if (!transformerId) {
3376
3232
  throw new Error(
3377
- `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}."`
3378
3234
  );
3379
3235
  }
3380
- const result = await dn({
3381
- step: "source",
3382
- name: options.sourceStep,
3383
- code,
3384
- config: sourceConfig.config || {},
3385
- createTrigger,
3386
- input: sourceInput || { content: void 0 }
3387
- });
3388
- return {
3389
- success: !result.error,
3390
- error: result.error?.message,
3391
- capturedEvents: result.events,
3392
- duration: Date.now() - startTime
3393
- };
3394
- } catch (error) {
3395
- return {
3396
- success: false,
3397
- error: getErrorMessage(error),
3398
- duration: Date.now() - startTime
3399
- };
3236
+ return { type: prefix, name, chainType, transformerId };
3400
3237
  }
3401
- }
3402
-
3403
- // src/commands/simulate/index.ts
3404
- init_cli_logger();
3405
- init_core();
3406
- init_config();
3407
- init_validators();
3408
-
3409
- // src/commands/simulate/example-loader.ts
3410
- function findExample(config, exampleName, stepTarget) {
3411
- if (stepTarget) {
3412
- return findExampleInStep(config, exampleName, stepTarget);
3413
- }
3414
- return findExampleAcrossSteps(config, exampleName);
3415
- }
3416
- function findExampleInStep(config, exampleName, stepTarget) {
3417
- const dotIndex = stepTarget.indexOf(".");
3418
- if (dotIndex === -1) {
3419
- throw new Error(
3420
- `Invalid --step format: "${stepTarget}". Expected "type.name" (e.g. "destination.gtag")`
3421
- );
3422
- }
3423
- const type = stepTarget.substring(0, dotIndex);
3424
- const name = stepTarget.substring(dotIndex + 1);
3425
- const stepMap = getStepMap(config, type);
3426
- if (!stepMap) {
3427
- throw new Error(`No ${type}s found in flow config`);
3428
- }
3429
- const step = stepMap[name];
3430
- if (!step) {
3431
- const available = Object.keys(stepMap).join(", ");
3432
- throw new Error(`${type} "${name}" not found. Available: ${available}`);
3433
- }
3434
- const examples = step.examples;
3435
- if (!examples || !examples[exampleName]) {
3436
- const available = examples ? Object.keys(examples).join(", ") : "none";
3238
+ if (parts.length === 3) {
3437
3239
  throw new Error(
3438
- `Example "${exampleName}" not found in ${type} "${name}". Available: ${available}`
3240
+ `Invalid step format: "${step}". Specify a transformer: "${step}.TRANSFORMER_NAME"`
3439
3241
  );
3440
3242
  }
3441
- return {
3442
- stepType: type,
3443
- stepName: name,
3444
- exampleName,
3445
- example: examples[exampleName]
3446
- };
3243
+ return { type: prefix, name };
3447
3244
  }
3448
- function findExampleAcrossSteps(config, exampleName) {
3449
- const matches = [];
3450
- const stepTypes = ["source", "transformer", "destination"];
3451
- for (const type of stepTypes) {
3452
- const stepMap = getStepMap(config, type);
3453
- if (!stepMap) continue;
3454
- for (const [name, step] of Object.entries(stepMap)) {
3455
- const examples = step.examples;
3456
- if (examples && examples[exampleName]) {
3457
- matches.push({
3458
- stepType: type,
3459
- stepName: name,
3460
- exampleName,
3461
- example: examples[exampleName]
3462
- });
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
+ }
3463
3262
  }
3464
3263
  }
3465
3264
  }
3466
- if (matches.length === 0) {
3467
- throw new Error(`Example "${exampleName}" not found in any step`);
3468
- }
3469
- if (matches.length > 1) {
3470
- const locations = matches.map((m2) => `${m2.stepType}.${m2.stepName}`).join(", ");
3471
- throw new Error(
3472
- `Example "${exampleName}" found in multiple steps: ${locations}. Use --step to disambiguate.`
3473
- );
3474
- }
3475
- return matches[0];
3476
- }
3477
- function getStepMap(config, type) {
3478
- switch (type) {
3479
- case "source":
3480
- return config.sources;
3481
- case "transformer":
3482
- return config.transformers;
3483
- case "destination":
3484
- return config.destinations;
3485
- default:
3486
- throw new Error(
3487
- `Invalid step type: "${type}". Must be "source", "transformer", or "destination"`
3488
- );
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
+ }
3489
3281
  }
3490
3282
  }
3491
3283
 
3492
- // src/commands/simulate/compare.ts
3493
- function compareOutput(expected, actual) {
3494
- const expectedStr = JSON.stringify(expected, null, 2);
3495
- const actualStr = JSON.stringify(actual, null, 2);
3496
- if (expectedStr === actualStr) {
3497
- 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();
3361
+ }
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();
3498
3373
  }
3499
- return {
3500
- expected,
3501
- actual,
3502
- match: false,
3503
- diff: `Expected:
3504
- ${expectedStr}
3505
-
3506
- Actual:
3507
- ${actualStr}`
3508
- };
3374
+ return { flush, countPending, restore };
3509
3375
  }
3510
3376
 
3511
- // src/commands/simulate/index.ts
3512
- async function simulateCommand(options) {
3513
- 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;
3514
3380
  const startTime = Date.now();
3515
- try {
3516
- let config;
3517
- if (isStdinPiped() && !options.config) {
3518
- const stdinContent = await readStdin();
3519
- const fs15 = await import("fs-extra");
3520
- const path15 = await import("path");
3521
- const tmpPath = getTmpPath(void 0, "stdin-simulate.json");
3522
- await fs15.default.ensureDir(path15.default.dirname(tmpPath));
3523
- await fs15.default.writeFile(tmpPath, stdinContent, "utf-8");
3524
- config = tmpPath;
3525
- } else {
3526
- config = options.config || "bundle.config.json";
3527
- }
3528
- const result = await simulate(config, options.event, {
3529
- flow: options.flow,
3530
- json: options.json,
3531
- verbose: options.verbose,
3532
- silent: options.silent,
3533
- platform: options.platform,
3534
- 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
3535
3393
  });
3536
- const resultWithDuration = {
3537
- ...result,
3538
- duration: (Date.now() - startTime) / 1e3
3539
- };
3540
- const formatted = formatSimulationResult(resultWithDuration, {
3541
- 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
3542
3403
  });
3543
- await writeResult(formatted + "\n", { output: options.output });
3544
- process.exit(result.success ? 0 : 1);
3545
- } catch (error) {
3546
- const errorMessage = getErrorMessage(error);
3547
- if (options.json) {
3548
- const errorOutput = JSON.stringify(
3549
- {
3550
- success: false,
3551
- error: errorMessage,
3552
- duration: (Date.now() - startTime) / 1e3
3553
- },
3554
- null,
3555
- 2
3556
- );
3557
- await writeResult(errorOutput + "\n", { output: options.output });
3558
- } else {
3559
- logger.error(`Error: ${errorMessage}`);
3404
+ if (networkCalls) {
3405
+ savedFetch = global.fetch;
3406
+ applyNetworkPolyfills(dom, networkCalls);
3407
+ global.fetch = dom.window.fetch;
3560
3408
  }
3561
- process.exit(1);
3562
3409
  }
3563
- }
3564
- async function simulate(configOrPath, event, options = {}) {
3565
- if (typeof configOrPath !== "string") {
3566
- throw new Error(
3567
- "simulate() currently only supports config file paths. Please provide a path to a configuration file."
3568
- );
3410
+ if (asyncDrain) {
3411
+ timerControl = installTimerInterception({
3412
+ domWindow: platform === "web" && dom ? dom.window : void 0
3413
+ });
3569
3414
  }
3570
- let resolvedEvent = event;
3571
- if (typeof event === "string") {
3572
- resolvedEvent = await loadJsonFromSource(event, { name: "event" });
3573
- }
3574
- const isSourceSimulation = options.step?.startsWith("source.");
3575
- let result;
3576
- if (isSourceSimulation) {
3577
- const rawConfig = await loadJsonConfig(configOrPath);
3578
- const setup = validateFlowConfig(rawConfig);
3579
- const flowNames = Object.keys(setup.flows);
3580
- const flowName = options.flow || (flowNames.length === 1 ? flowNames[0] : void 0);
3581
- if (!flowName) {
3582
- throw new Error(
3583
- `Multiple flows found. Use --flow to specify which flow.
3584
- Available: ${flowNames.join(", ")}`
3585
- );
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
+ }
3586
3425
  }
3587
- const flowSettings = setup.flows[flowName];
3588
- 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") {
3589
3430
  throw new Error(
3590
- `Flow "${flowName}" not found. Available: ${flowNames.join(", ")}`
3431
+ "Invalid ESM bundle: missing wireConfig or startFlow exports"
3591
3432
  );
3592
3433
  }
3593
- const sourceStep = options.step.substring("source.".length);
3594
- result = await simulateSourceCLI(
3595
- flowSettings,
3596
- resolvedEvent,
3597
- {
3598
- flow: options.flow,
3599
- sourceStep,
3600
- json: options.json,
3601
- verbose: options.verbose,
3602
- 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;
3603
3477
  }
3604
- );
3605
- } else {
3606
- result = await simulateCore(configOrPath, resolvedEvent, {
3607
- json: options.json ?? false,
3608
- verbose: options.verbose ?? false,
3609
- silent: options.silent ?? false,
3610
- flow: options.flow,
3611
- platform: options.platform,
3612
- step: options.step
3613
- });
3478
+ }
3614
3479
  }
3615
- 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;
3616
3518
  }
3617
3519
 
3618
- // src/commands/push/index.ts
3520
+ // src/commands/push/prepare.ts
3619
3521
  init_cli_logger();
3620
- init_core();
3621
3522
  init_tmp();
3622
3523
  init_config();
3623
3524
  init_bundler();
3624
- import path12 from "path";
3625
- import { JSDOM, VirtualConsole } from "jsdom";
3626
- import fs13 from "fs-extra";
3525
+ import path15 from "path";
3526
+ import fs14 from "fs-extra";
3627
3527
  import { getPlatform as getPlatform3 } from "@walkeros/core";
3628
- import { schemas as schemas2 } from "@walkeros/core/dev";
3629
- 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
+ }
3630
3609
  async function pushCore(inputPath, event, options = {}) {
3631
3610
  const logger = createCLILogger({
3632
3611
  silent: options.silent,
@@ -3635,53 +3614,45 @@ async function pushCore(inputPath, event, options = {}) {
3635
3614
  const startTime = Date.now();
3636
3615
  let tempDir;
3637
3616
  try {
3638
- const eventResult = schemas2.PartialEventSchema.safeParse(event);
3639
- if (!eventResult.success) {
3640
- const errors = eventResult.error.issues.map((issue) => `${String(issue.path.join("."))}: ${issue.message}`).join(", ");
3641
- throw new Error(`Invalid event: ${errors}`);
3642
- }
3643
- const parsedEvent = eventResult.data;
3644
- if (!parsedEvent.name) {
3645
- throw new Error('Invalid event: Missing required "name" property');
3646
- }
3647
- const validatedEvent = {
3648
- name: parsedEvent.name,
3649
- data: parsedEvent.data || {}
3650
- };
3651
- if (!validatedEvent.name.includes(" ")) {
3652
- logger.info(
3653
- `Warning: Event name "${validatedEvent.name}" should follow "ENTITY ACTION" format (e.g., "page view")`
3654
- );
3655
- }
3656
3617
  logger.debug("Detecting input type");
3657
3618
  const detected = await detectInput(
3658
3619
  inputPath,
3659
3620
  options.platform
3660
3621
  );
3661
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
+ }
3662
3630
  if (detected.type === "config") {
3663
3631
  result = await executeConfigPush(
3664
3632
  {
3665
3633
  config: inputPath,
3666
3634
  flow: options.flow,
3667
- verbose: options.verbose
3635
+ verbose: options.verbose,
3636
+ mock: options.mock
3668
3637
  },
3669
- validatedEvent,
3638
+ event,
3670
3639
  logger,
3671
3640
  (dir) => {
3672
3641
  tempDir = dir;
3673
- }
3642
+ },
3643
+ snapshotCode
3674
3644
  );
3675
3645
  } else {
3676
3646
  result = await executeBundlePush(
3677
3647
  detected.content,
3678
3648
  detected.platform,
3679
- validatedEvent,
3649
+ event,
3680
3650
  logger,
3681
3651
  (dir) => {
3682
3652
  tempDir = dir;
3683
3653
  },
3684
- { logger: { level: options.verbose ? Level2.DEBUG : Level2.ERROR } }
3654
+ void 0,
3655
+ snapshotCode
3685
3656
  );
3686
3657
  }
3687
3658
  return result;
@@ -3693,7 +3664,7 @@ async function pushCore(inputPath, event, options = {}) {
3693
3664
  };
3694
3665
  } finally {
3695
3666
  if (tempDir) {
3696
- await fs13.remove(tempDir).catch(() => {
3667
+ await fs15.remove(tempDir).catch(() => {
3697
3668
  });
3698
3669
  }
3699
3670
  }
@@ -3704,33 +3675,67 @@ async function pushCommand(options) {
3704
3675
  try {
3705
3676
  let config;
3706
3677
  if (isStdinPiped() && !options.config) {
3707
- const stdinContent = await readStdin();
3708
- const tmpPath = getTmpPath(void 0, "stdin-push.json");
3709
- await fs13.ensureDir(path12.dirname(tmpPath));
3710
- await fs13.writeFile(tmpPath, stdinContent, "utf-8");
3711
- config = tmpPath;
3678
+ config = await readStdinToTempFile("push");
3712
3679
  } else {
3713
3680
  config = options.config || "bundle.config.json";
3714
3681
  }
3715
- const result = await push(config, options.event, {
3716
- flow: options.flow,
3717
- json: options.json,
3718
- verbose: options.verbose,
3719
- silent: options.silent,
3720
- platform: options.platform
3721
- });
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
+ }
3722
3735
  const duration = Date.now() - startTime;
3723
3736
  let output;
3724
3737
  if (options.json) {
3725
- output = JSON.stringify(
3726
- {
3727
- success: result.success,
3728
- event: result.elbResult,
3729
- duration
3730
- },
3731
- null,
3732
- 2
3733
- );
3738
+ output = JSON.stringify({ ...result, duration }, null, 2);
3734
3739
  } else {
3735
3740
  const lines = [];
3736
3741
  if (result.success) {
@@ -3774,158 +3779,438 @@ async function push(configOrPath, event, options = {}) {
3774
3779
  "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."
3775
3780
  );
3776
3781
  }
3777
- let resolvedEvent = event;
3778
- if (typeof event === "string") {
3779
- resolvedEvent = await loadJsonFromSource(event, { name: "event" });
3782
+ const parsed = schemas3.PartialEventSchema.safeParse(event);
3783
+ if (!parsed.success) {
3784
+ return {
3785
+ success: false,
3786
+ duration: 0,
3787
+ error: `Invalid event: ${parsed.error.issues.map((i2) => `${i2.path.join(".")}: ${i2.message}`).join(", ")}`
3788
+ };
3780
3789
  }
3781
- return await pushCore(configOrPath, resolvedEvent, {
3790
+ return pushCore(configOrPath, event, {
3782
3791
  json: options.json ?? false,
3783
3792
  verbose: options.verbose ?? false,
3784
3793
  silent: options.silent ?? false,
3785
3794
  flow: options.flow,
3786
- platform: options.platform
3795
+ platform: options.platform,
3796
+ mock: options.mock,
3797
+ snapshot: options.snapshot
3787
3798
  });
3788
3799
  }
3789
- async function executeConfigPush(options, validatedEvent, logger, setTempDir) {
3800
+ async function executeConfigPush(options, validatedEvent, logger, setTempDir, snapshotCode) {
3790
3801
  logger.debug("Loading flow configuration");
3791
3802
  const { flowSettings, buildOptions } = await loadFlowConfig(options.config, {
3792
3803
  flowName: options.flow,
3793
3804
  logger
3794
3805
  });
3795
- const platform = getPlatform3(flowSettings);
3806
+ const platform = getPlatform4(flowSettings);
3807
+ const overrides = buildOverrides(
3808
+ { mock: options.mock },
3809
+ flowSettings
3810
+ );
3811
+ if (overrides.destinations) {
3812
+ const { loadDestinationEnvs: loadDestinationEnvs2 } = await Promise.resolve().then(() => (init_env_loader(), env_loader_exports));
3813
+ const configDir = buildOptions.configDir || process.cwd();
3814
+ const envs = await loadDestinationEnvs2(
3815
+ flowSettings.destinations ?? {},
3816
+ flowSettings.packages,
3817
+ configDir
3818
+ );
3819
+ for (const [destId, env] of Object.entries(envs)) {
3820
+ if (overrides.destinations[destId] && env.push) {
3821
+ overrides.destinations[destId].env = env.push;
3822
+ if (env.simulation && env.simulation.length > 0) {
3823
+ overrides.destinations[destId].simulation = env.simulation;
3824
+ }
3825
+ }
3826
+ }
3827
+ }
3796
3828
  logger.debug("Bundling flow configuration");
3797
- const configDir = buildOptions.configDir || process.cwd();
3798
3829
  const tempDir = getTmpPath(
3799
3830
  void 0,
3800
3831
  `push-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
3801
3832
  );
3802
3833
  setTempDir(tempDir);
3803
- await fs13.ensureDir(tempDir);
3804
- const tempPath = path12.join(
3805
- tempDir,
3806
- `bundle.${platform === "web" ? "js" : "mjs"}`
3807
- );
3834
+ await fs15.ensureDir(tempDir);
3835
+ const tempPath = path16.join(tempDir, "bundle.mjs");
3808
3836
  const pushBuildOptions = {
3809
3837
  ...buildOptions,
3810
3838
  output: tempPath,
3811
- format: platform === "web" ? "iife" : "esm",
3839
+ format: "esm",
3812
3840
  platform: platform === "web" ? "browser" : "node",
3813
- ...platform === "web" && {
3814
- windowCollector: "collector",
3815
- windowElb: "elb"
3816
- }
3841
+ skipWrapper: true
3842
+ // CLI imports ESM directly — no platform wrapper
3817
3843
  };
3818
3844
  await bundleCore(flowSettings, pushBuildOptions, logger, false);
3819
3845
  logger.debug(`Bundle created: ${tempPath}`);
3820
- if (platform === "web") {
3821
- logger.debug("Executing in web environment (JSDOM)");
3822
- return executeWebPush(tempPath, validatedEvent, logger);
3823
- } else if (platform === "server") {
3824
- logger.debug("Executing in server environment (Node.js)");
3825
- return executeServerPush(tempPath, validatedEvent, logger, 6e4, {
3826
- logger: { level: options.verbose ? Level2.DEBUG : Level2.ERROR }
3827
- });
3828
- } else {
3829
- throw new Error(`Unsupported platform: ${platform}`);
3830
- }
3846
+ logger.debug(
3847
+ `Executing in ${platform} environment (${platform === "web" ? "JSDOM" : "Node.js"})`
3848
+ );
3849
+ return executeDestinationPush(
3850
+ tempPath,
3851
+ validatedEvent,
3852
+ logger,
3853
+ platform,
3854
+ overrides,
3855
+ snapshotCode,
3856
+ platform === "server" ? 6e4 : void 0
3857
+ );
3831
3858
  }
3832
- async function executeBundlePush(bundleContent, platform, validatedEvent, logger, setTempDir, context = {}) {
3859
+ async function executeBundlePush(bundleContent, platform, validatedEvent, logger, setTempDir, overrides = {}, snapshotCode) {
3833
3860
  const tempDir = getTmpPath(
3834
3861
  void 0,
3835
3862
  `push-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
3836
3863
  );
3837
3864
  setTempDir(tempDir);
3838
- await fs13.ensureDir(tempDir);
3839
- const tempPath = path12.join(
3840
- tempDir,
3841
- `bundle.${platform === "server" ? "mjs" : "js"}`
3842
- );
3843
- await fs13.writeFile(tempPath, bundleContent, "utf8");
3865
+ await fs15.ensureDir(tempDir);
3866
+ const tempPath = path16.join(tempDir, "bundle.mjs");
3867
+ await fs15.writeFile(tempPath, bundleContent, "utf8");
3844
3868
  logger.debug(`Bundle written to: ${tempPath}`);
3845
- if (platform === "web") {
3846
- logger.debug("Executing in web environment (JSDOM)");
3847
- return executeWebPush(tempPath, validatedEvent, logger);
3848
- } else {
3849
- logger.debug("Executing in server environment (Node.js)");
3850
- return executeServerPush(tempPath, validatedEvent, logger, 6e4, context);
3869
+ logger.debug(
3870
+ `Executing in ${platform} environment (${platform === "web" ? "JSDOM" : "Node.js"})`
3871
+ );
3872
+ return executeDestinationPush(
3873
+ tempPath,
3874
+ validatedEvent,
3875
+ logger,
3876
+ platform,
3877
+ overrides,
3878
+ snapshotCode,
3879
+ platform === "server" ? 6e4 : void 0
3880
+ );
3881
+ }
3882
+ async function executeDestinationPush(esmPath, event, logger, platform, overrides, snapshotCode, timeout) {
3883
+ const startTime = Date.now();
3884
+ const networkCalls = [];
3885
+ return withFlowContext(
3886
+ { esmPath, platform, logger, snapshotCode, timeout, networkCalls, asyncDrain: { timeout: 5e3 } },
3887
+ async (module) => {
3888
+ const config = module.wireConfig(module.__configData ?? void 0);
3889
+ applyOverrides(config, overrides || {});
3890
+ const result = await module.startFlow(config);
3891
+ if (!result?.collector?.push)
3892
+ throw new Error("Invalid bundle: collector missing push");
3893
+ const collector = result.collector;
3894
+ logger.info(`Pushing event: ${event.name}`);
3895
+ const elbResult = await collector.push(event);
3896
+ await collector.command("shutdown");
3897
+ return {
3898
+ success: true,
3899
+ elbResult,
3900
+ ...networkCalls.length > 0 ? { networkCalls } : {},
3901
+ duration: Date.now() - startTime
3902
+ };
3903
+ }
3904
+ );
3905
+ }
3906
+ async function simulateSource(configPath, input, options) {
3907
+ const startTime = Date.now();
3908
+ const prepared = await prepareFlow({
3909
+ configPath,
3910
+ flow: options.flow,
3911
+ simulate: ["source." + options.sourceId],
3912
+ silent: options.silent,
3913
+ verbose: options.verbose
3914
+ });
3915
+ try {
3916
+ const logger = createCLILogger({
3917
+ silent: options.silent,
3918
+ verbose: options.verbose
3919
+ });
3920
+ const sourceConfig = (prepared.flowSettings.sources ?? {})[options.sourceId];
3921
+ if (!sourceConfig?.package) {
3922
+ throw new Error(
3923
+ `Source "${options.sourceId}" has no package defined`
3924
+ );
3925
+ }
3926
+ const devPath = resolvePackageImportPath(
3927
+ sourceConfig.package,
3928
+ prepared.flowSettings.packages,
3929
+ prepared.configDir,
3930
+ "/dev"
3931
+ );
3932
+ const devModule = await import(devPath);
3933
+ const createTrigger = devModule.examples?.createTrigger || devModule.default?.examples?.createTrigger;
3934
+ if (!createTrigger) {
3935
+ throw new Error(
3936
+ `Source package "${sourceConfig.package}" has no createTrigger in /dev export`
3937
+ );
3938
+ }
3939
+ let snapshotCode;
3940
+ if (options.snapshot) {
3941
+ snapshotCode = await loadConfig(options.snapshot, {
3942
+ json: false
3943
+ });
3944
+ logger.debug(`Snapshot loaded (${snapshotCode.length} bytes)`);
3945
+ }
3946
+ const networkCalls = [];
3947
+ return await withFlowContext(
3948
+ {
3949
+ esmPath: prepared.bundlePath,
3950
+ platform: prepared.platform,
3951
+ logger,
3952
+ snapshotCode,
3953
+ networkCalls
3954
+ },
3955
+ async (module) => {
3956
+ const config = module.wireConfig(module.__configData ?? void 0);
3957
+ applyOverrides(config, prepared.overrides);
3958
+ const captured = [];
3959
+ config.hooks = {
3960
+ ...config.hooks || {},
3961
+ prePush: ({ fn }, event) => {
3962
+ captured.push({ event, timestamp: Date.now() });
3963
+ return { ok: true };
3964
+ }
3965
+ };
3966
+ const instance = await createTrigger(config, { sourceId: options.sourceId });
3967
+ const { trigger } = instance;
3968
+ logger.info("Simulating source");
3969
+ const inputRecord = input ?? {};
3970
+ const content = inputRecord.content ?? input;
3971
+ const triggerOpts = inputRecord.trigger;
3972
+ await trigger(triggerOpts?.type, triggerOpts?.options)(content);
3973
+ if (instance.flow?.collector?.command) {
3974
+ await instance.flow.collector.command("shutdown");
3975
+ }
3976
+ return {
3977
+ success: true,
3978
+ ...captured.length > 0 ? { captured } : {},
3979
+ ...networkCalls.length > 0 ? { networkCalls } : {},
3980
+ duration: Date.now() - startTime
3981
+ };
3982
+ }
3983
+ );
3984
+ } catch (error) {
3985
+ return {
3986
+ success: false,
3987
+ duration: Date.now() - startTime,
3988
+ error: getErrorMessage(error)
3989
+ };
3990
+ } finally {
3991
+ await prepared.cleanup();
3851
3992
  }
3852
3993
  }
3853
- async function executeWebPush(bundlePath, event, logger) {
3994
+ async function simulateTransformer(configPath, event, options) {
3854
3995
  const startTime = Date.now();
3996
+ const parsed = schemas3.PartialEventSchema.safeParse(event);
3997
+ if (!parsed.success) {
3998
+ return {
3999
+ success: false,
4000
+ duration: 0,
4001
+ error: parsed.error.message
4002
+ };
4003
+ }
4004
+ const prepared = await prepareFlow({
4005
+ configPath,
4006
+ flow: options.flow,
4007
+ simulate: ["transformer." + options.transformerId],
4008
+ mock: options.mock,
4009
+ silent: options.silent,
4010
+ verbose: options.verbose
4011
+ });
3855
4012
  try {
3856
- const virtualConsole = new VirtualConsole();
3857
- const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>", {
3858
- url: "http://localhost",
3859
- runScripts: "dangerously",
3860
- resources: "usable",
3861
- virtualConsole
4013
+ const logger = createCLILogger({
4014
+ silent: options.silent,
4015
+ verbose: options.verbose
3862
4016
  });
3863
- const { window } = dom;
3864
- logger.debug("Loading bundle...");
3865
- const bundleCode = await fs13.readFile(bundlePath, "utf8");
3866
- window.eval(bundleCode);
3867
- logger.debug("Waiting for collector...");
3868
- await waitForWindowProperty(
3869
- window,
3870
- "collector",
3871
- 5e3
4017
+ let snapshotCode;
4018
+ if (options.snapshot) {
4019
+ snapshotCode = await loadConfig(options.snapshot, {
4020
+ json: false
4021
+ });
4022
+ logger.debug(`Snapshot loaded (${snapshotCode.length} bytes)`);
4023
+ }
4024
+ const networkCalls = [];
4025
+ return await withFlowContext(
4026
+ {
4027
+ esmPath: prepared.bundlePath,
4028
+ platform: prepared.platform,
4029
+ logger,
4030
+ snapshotCode,
4031
+ networkCalls
4032
+ },
4033
+ async (module) => {
4034
+ const config = module.wireConfig(module.__configData ?? void 0);
4035
+ applyOverrides(config, prepared.overrides);
4036
+ if (config.sources) config.sources = {};
4037
+ if (config.destinations) config.destinations = {};
4038
+ const result = await module.startFlow(config);
4039
+ if (!result?.collector)
4040
+ throw new Error("Invalid bundle: collector not available");
4041
+ const collector = result.collector;
4042
+ const transformer = collector.transformers?.[options.transformerId];
4043
+ if (!transformer) {
4044
+ throw new Error(
4045
+ `Transformer "${options.transformerId}" not found in collector. Available: ${Object.keys(collector.transformers || {}).join(", ") || "none"}`
4046
+ );
4047
+ }
4048
+ const initialized = await ee(
4049
+ collector,
4050
+ transformer,
4051
+ options.transformerId
4052
+ );
4053
+ if (!initialized) {
4054
+ throw new Error(
4055
+ `Transformer "${options.transformerId}" failed to initialize`
4056
+ );
4057
+ }
4058
+ const inputEvent = event;
4059
+ const ingest = createIngest(options.transformerId);
4060
+ const captured = [];
4061
+ captured.push({ event: { ...inputEvent }, timestamp: Date.now() });
4062
+ logger.info(`Simulating transformer: ${options.transformerId}`);
4063
+ let processedEvent = inputEvent;
4064
+ const before = transformer.config.before;
4065
+ if (before && collector.transformers) {
4066
+ const beforeChainIds = resolveBeforeChain(
4067
+ before,
4068
+ collector.transformers,
4069
+ ingest,
4070
+ processedEvent
4071
+ );
4072
+ if (beforeChainIds.length > 0) {
4073
+ const beforeResult = await te(
4074
+ collector,
4075
+ collector.transformers,
4076
+ beforeChainIds,
4077
+ processedEvent,
4078
+ ingest,
4079
+ void 0,
4080
+ `transformer.${options.transformerId}.before`
4081
+ );
4082
+ if (beforeResult === null) {
4083
+ captured.push({ event: null, timestamp: Date.now() });
4084
+ await collector.command("shutdown");
4085
+ return {
4086
+ success: true,
4087
+ captured,
4088
+ duration: Date.now() - startTime
4089
+ };
4090
+ }
4091
+ processedEvent = Array.isArray(beforeResult) ? beforeResult[0] : beforeResult;
4092
+ }
4093
+ }
4094
+ const pushResult = await ne(
4095
+ collector,
4096
+ transformer,
4097
+ options.transformerId,
4098
+ processedEvent,
4099
+ ingest
4100
+ );
4101
+ if (pushResult === false) {
4102
+ captured.push({ event: null, timestamp: Date.now() });
4103
+ } else if (Array.isArray(pushResult)) {
4104
+ for (const r2 of pushResult) {
4105
+ captured.push({
4106
+ event: r2.event || processedEvent,
4107
+ timestamp: Date.now()
4108
+ });
4109
+ }
4110
+ } else if (pushResult && typeof pushResult === "object" && pushResult.event) {
4111
+ captured.push({ event: pushResult.event, timestamp: Date.now() });
4112
+ } else {
4113
+ captured.push({ event: processedEvent, timestamp: Date.now() });
4114
+ }
4115
+ await collector.command("shutdown");
4116
+ return {
4117
+ success: true,
4118
+ captured,
4119
+ ...networkCalls.length > 0 ? { networkCalls } : {},
4120
+ duration: Date.now() - startTime
4121
+ };
4122
+ }
3872
4123
  );
3873
- const windowObj = window;
3874
- const collector = windowObj.collector;
3875
- logger.info(`Pushing event: ${event.name}`);
3876
- const elbResult = await collector.push({
3877
- name: event.name,
3878
- data: event.data
3879
- });
3880
- return {
3881
- success: true,
3882
- elbResult,
3883
- duration: Date.now() - startTime
3884
- };
3885
4124
  } catch (error) {
3886
4125
  return {
3887
4126
  success: false,
3888
4127
  duration: Date.now() - startTime,
3889
4128
  error: getErrorMessage(error)
3890
4129
  };
4130
+ } finally {
4131
+ await prepared.cleanup();
3891
4132
  }
3892
4133
  }
3893
- async function executeServerPush(bundlePath, event, logger, timeout = 6e4, context = {}) {
4134
+ async function simulateDestination(configPath, event, options) {
3894
4135
  const startTime = Date.now();
3895
- let timer;
4136
+ const parsed = schemas3.PartialEventSchema.safeParse(event);
4137
+ if (!parsed.success) {
4138
+ return {
4139
+ success: false,
4140
+ duration: 0,
4141
+ error: parsed.error.message
4142
+ };
4143
+ }
4144
+ const prepared = await prepareFlow({
4145
+ configPath,
4146
+ flow: options.flow,
4147
+ simulate: ["destination." + options.destinationId],
4148
+ mock: options.mock,
4149
+ silent: options.silent,
4150
+ verbose: options.verbose
4151
+ });
3896
4152
  try {
3897
- const timeoutPromise = new Promise((_2, reject) => {
3898
- timer = setTimeout(
3899
- () => reject(new Error(`Server push timeout after ${timeout}ms`)),
3900
- timeout
3901
- );
4153
+ const logger = createCLILogger({
4154
+ silent: options.silent,
4155
+ verbose: options.verbose
3902
4156
  });
3903
- const executePromise = (async () => {
3904
- logger.debug("Importing bundle...");
3905
- const flowModule = await import(bundlePath);
3906
- if (!flowModule.default || typeof flowModule.default !== "function") {
3907
- throw new Error("Bundle does not export default factory function");
3908
- }
3909
- logger.debug("Calling factory function...");
3910
- const result = await flowModule.default(context);
3911
- if (!result || !result.collector || typeof result.collector.push !== "function") {
3912
- throw new Error(
3913
- "Factory function did not return valid result with collector"
3914
- );
3915
- }
3916
- const { collector } = result;
3917
- logger.info(`Pushing event: ${event.name}`);
3918
- const elbResult = await collector.push({
3919
- name: event.name,
3920
- data: event.data
4157
+ let snapshotCode;
4158
+ if (options.snapshot) {
4159
+ snapshotCode = await loadConfig(options.snapshot, {
4160
+ json: false
3921
4161
  });
3922
- return {
3923
- success: true,
3924
- elbResult,
3925
- duration: Date.now() - startTime
3926
- };
3927
- })();
3928
- return await Promise.race([executePromise, timeoutPromise]);
4162
+ }
4163
+ const networkCalls = [];
4164
+ return await withFlowContext(
4165
+ {
4166
+ esmPath: prepared.bundlePath,
4167
+ platform: prepared.platform,
4168
+ logger,
4169
+ snapshotCode,
4170
+ networkCalls
4171
+ },
4172
+ async (module) => {
4173
+ const config = module.wireConfig(module.__configData ?? void 0);
4174
+ applyOverrides(config, prepared.overrides);
4175
+ const destOverride = prepared.overrides.destinations?.[options.destinationId];
4176
+ let trackedCalls = [];
4177
+ if (destOverride?.simulation?.length) {
4178
+ const destinations = config.destinations;
4179
+ const destConfig = destinations[options.destinationId]?.config;
4180
+ if (destConfig?.env) {
4181
+ const combined = {
4182
+ ...destConfig.env,
4183
+ simulation: destOverride.simulation
4184
+ };
4185
+ const { wrappedEnv, calls } = Ge(combined);
4186
+ destConfig.env = wrappedEnv;
4187
+ trackedCalls = calls;
4188
+ }
4189
+ }
4190
+ if (config.sources) config.sources = {};
4191
+ const result = await module.startFlow(config);
4192
+ if (!result?.collector)
4193
+ throw new Error("Invalid bundle: collector not available");
4194
+ const collector = result.collector;
4195
+ if (!collector.destinations[options.destinationId] && !collector.pending.destinations[options.destinationId]) {
4196
+ throw new Error(
4197
+ `Destination "${options.destinationId}" not found in collector. Available: ${Object.keys(collector.destinations || {}).join(", ") || "none"}`
4198
+ );
4199
+ }
4200
+ logger.info(`Simulating destination: ${options.destinationId}`);
4201
+ const elbResult = await collector.push(event, {
4202
+ include: [options.destinationId]
4203
+ });
4204
+ await collector.command("shutdown");
4205
+ return {
4206
+ success: true,
4207
+ elbResult,
4208
+ ...trackedCalls.length > 0 ? { usage: { [options.destinationId]: trackedCalls } } : {},
4209
+ ...networkCalls.length > 0 ? { networkCalls } : {},
4210
+ duration: Date.now() - startTime
4211
+ };
4212
+ }
4213
+ );
3929
4214
  } catch (error) {
3930
4215
  return {
3931
4216
  success: false,
@@ -3933,35 +4218,17 @@ async function executeServerPush(bundlePath, event, logger, timeout = 6e4, conte
3933
4218
  error: getErrorMessage(error)
3934
4219
  };
3935
4220
  } finally {
3936
- clearTimeout(timer);
4221
+ await prepared.cleanup();
3937
4222
  }
3938
4223
  }
3939
- function waitForWindowProperty(window, prop, timeout = 5e3) {
3940
- return new Promise((resolve2, reject) => {
3941
- const start = Date.now();
3942
- const check = () => {
3943
- if (window[prop] !== void 0) {
3944
- resolve2();
3945
- } else if (Date.now() - start > timeout) {
3946
- reject(
3947
- new Error(
3948
- `Timeout waiting for window.${prop}. IIFE may have failed to execute.`
3949
- )
3950
- );
3951
- } else {
3952
- setImmediate(check);
3953
- }
3954
- };
3955
- check();
3956
- });
3957
- }
3958
4224
 
3959
4225
  // src/commands/run/index.ts
3960
4226
  init_cli_logger();
3961
4227
  init_core();
4228
+ init_tmp();
3962
4229
  init_config_file();
3963
4230
  init_auth();
3964
- import path14 from "path";
4231
+ import path18 from "path";
3965
4232
  import { writeFileSync as writeFileSync5 } from "fs";
3966
4233
  import { homedir as homedir2 } from "os";
3967
4234
  import { join as join4 } from "path";
@@ -4009,12 +4276,12 @@ async function resolveBundle(bundleEnv) {
4009
4276
  return { path: bundleEnv, source: "file" };
4010
4277
  }
4011
4278
  if (isUrl2(bundleEnv)) {
4012
- const path15 = await fetchBundle(bundleEnv, writePath);
4013
- return { path: path15, source: "url" };
4279
+ const path19 = await fetchBundle(bundleEnv, writePath);
4280
+ return { path: path19, source: "url" };
4014
4281
  }
4015
4282
  if (isStdinPiped()) {
4016
- const path15 = await readBundleFromStdin(writePath);
4017
- return { path: path15, source: "stdin" };
4283
+ const path19 = await readBundleFromStdin(writePath);
4284
+ return { path: path19, source: "stdin" };
4018
4285
  }
4019
4286
  return { path: bundleEnv, source: "file" };
4020
4287
  }
@@ -4178,12 +4445,14 @@ function validatePort(port) {
4178
4445
  init_utils3();
4179
4446
 
4180
4447
  // src/commands/run/pipeline.ts
4448
+ init_tmp();
4181
4449
  import { writeFileSync as writeFileSync4 } from "fs";
4450
+ import fs17 from "fs-extra";
4182
4451
 
4183
4452
  // src/runtime/health-server.ts
4184
4453
  import http from "http";
4185
4454
  function createHealthServer(port, logger) {
4186
- return new Promise((resolve2, reject) => {
4455
+ return new Promise((resolve3, reject) => {
4187
4456
  let flowHandler = null;
4188
4457
  const server = http.createServer((req, res) => {
4189
4458
  if (req.url === "/health" && req.method === "GET") {
@@ -4210,7 +4479,7 @@ function createHealthServer(port, logger) {
4210
4479
  server.headersTimeout = 1e4;
4211
4480
  server.listen(port, "0.0.0.0", () => {
4212
4481
  logger.info(`Health server listening on port ${port}`);
4213
- resolve2({
4482
+ resolve3({
4214
4483
  server,
4215
4484
  setFlowHandler(handler) {
4216
4485
  flowHandler = handler;
@@ -4225,28 +4494,45 @@ function createHealthServer(port, logger) {
4225
4494
  }
4226
4495
 
4227
4496
  // src/runtime/runner.ts
4228
- import { pathToFileURL } from "url";
4229
- import { resolve, dirname as dirname2 } from "path";
4230
- async function loadFlow(file, config, logger, loggerConfig, healthServer) {
4497
+ import { resolve as resolve2, dirname as dirname2 } from "path";
4498
+
4499
+ // src/runtime/load-bundle.ts
4500
+ import { resolve } from "path";
4501
+ import { pathToFileURL as pathToFileURL2 } from "url";
4502
+ async function loadBundle(file, context, logger) {
4231
4503
  const absolutePath = resolve(file);
4232
- const flowDir = dirname2(absolutePath);
4233
- process.chdir(flowDir);
4234
- const fileUrl = pathToFileURL(absolutePath).href;
4504
+ const fileUrl = pathToFileURL2(absolutePath).href;
4505
+ logger?.debug?.(`Importing bundle: ${absolutePath}`);
4235
4506
  const module = await import(`${fileUrl}?t=${Date.now()}`);
4236
4507
  if (!module.default || typeof module.default !== "function") {
4237
4508
  throw new Error(
4238
- `Invalid flow bundle: ${file} must export a default function`
4509
+ `Invalid bundle: ${file} must export a default factory function`
4239
4510
  );
4240
4511
  }
4512
+ logger?.debug?.("Calling factory function...");
4513
+ const result = await module.default(context ?? {});
4514
+ if (!result || !result.collector || typeof result.collector.push !== "function") {
4515
+ throw new Error(
4516
+ `Invalid bundle: factory must return { collector } with a push function`
4517
+ );
4518
+ }
4519
+ return {
4520
+ collector: result.collector,
4521
+ ...typeof result.httpHandler === "function" ? { httpHandler: result.httpHandler } : {}
4522
+ };
4523
+ }
4524
+
4525
+ // src/runtime/runner.ts
4526
+ async function loadFlow(file, config, logger, loggerConfig, healthServer) {
4527
+ const absolutePath = resolve2(file);
4528
+ const flowDir = dirname2(absolutePath);
4529
+ process.chdir(flowDir);
4241
4530
  const flowContext = {
4242
4531
  ...config,
4243
4532
  ...loggerConfig ? { logger: loggerConfig } : {},
4244
- ...healthServer ? { externalServer: true } : {}
4533
+ ...healthServer ? { sourceSettings: { port: void 0 } } : {}
4245
4534
  };
4246
- const result = await module.default(flowContext);
4247
- if (!result || !result.collector) {
4248
- throw new Error(`Invalid flow bundle: ${file} must return { collector }`);
4249
- }
4535
+ const result = await loadBundle(absolutePath, flowContext, logger);
4250
4536
  if (healthServer && typeof result.httpHandler === "function") {
4251
4537
  healthServer.setFlowHandler(result.httpHandler);
4252
4538
  }
@@ -4512,6 +4798,8 @@ async function runPipeline(options) {
4512
4798
  logger.info(`Port: ${port}`);
4513
4799
  let heartbeat = null;
4514
4800
  let poller = null;
4801
+ let currentBundleCleanup;
4802
+ let currentConfigPath;
4515
4803
  if (api) {
4516
4804
  heartbeat = createHeartbeat(
4517
4805
  {
@@ -4546,28 +4834,44 @@ async function runPipeline(options) {
4546
4834
  );
4547
4835
  return;
4548
4836
  }
4549
- const tmpConfigPath = `/tmp/walkeros-flow-${Date.now()}.json`;
4837
+ const tmpConfigPath = getTmpPath(
4838
+ void 0,
4839
+ `walkeros-flow-${Date.now()}.json`
4840
+ );
4550
4841
  writeFileSync4(
4551
4842
  tmpConfigPath,
4552
4843
  JSON.stringify(content, null, 2),
4553
4844
  "utf-8"
4554
4845
  );
4555
- const newBundle = await api.prepareBundleForRun(tmpConfigPath, {
4846
+ const newBundleResult = await api.prepareBundleForRun(tmpConfigPath, {
4556
4847
  verbose: false,
4557
4848
  silent: true,
4558
4849
  flowName: api.flowName
4559
4850
  });
4560
4851
  handle = await swapFlow(
4561
4852
  handle,
4562
- newBundle,
4853
+ newBundleResult.bundlePath,
4563
4854
  runtimeConfig,
4564
4855
  logger,
4565
4856
  loggerConfig,
4566
4857
  healthServer
4567
4858
  );
4568
- writeCache(api.cacheDir, newBundle, JSON.stringify(content), version);
4859
+ writeCache(
4860
+ api.cacheDir,
4861
+ newBundleResult.bundlePath,
4862
+ JSON.stringify(content),
4863
+ version
4864
+ );
4569
4865
  configVersion = version;
4570
4866
  if (heartbeat) heartbeat.updateConfigVersion(version);
4867
+ if (currentBundleCleanup)
4868
+ await currentBundleCleanup().catch(() => {
4869
+ });
4870
+ if (currentConfigPath)
4871
+ await fs17.remove(currentConfigPath).catch(() => {
4872
+ });
4873
+ currentBundleCleanup = newBundleResult.cleanup;
4874
+ currentConfigPath = tmpConfigPath;
4571
4875
  logger.info(`Hot-swapped to version ${version}`);
4572
4876
  }
4573
4877
  },
@@ -4589,6 +4893,10 @@ async function runPipeline(options) {
4589
4893
  await handle.collector.command("shutdown");
4590
4894
  }
4591
4895
  await healthServer.close();
4896
+ if (currentBundleCleanup) await currentBundleCleanup().catch(() => {
4897
+ });
4898
+ if (currentConfigPath) await fs17.remove(currentConfigPath).catch(() => {
4899
+ });
4592
4900
  logger.info("Shutdown complete");
4593
4901
  clearTimeout(forceTimer);
4594
4902
  process.exit(0);
@@ -4737,15 +5045,16 @@ async function resolveBundlePath(configInput, apiConfig, logger) {
4737
5045
  logger.info(`Bundle: ${resolved.path}`);
4738
5046
  }
4739
5047
  if (isPreBuiltConfig(resolved.path)) {
4740
- return path14.resolve(resolved.path);
5048
+ return path18.resolve(resolved.path);
4741
5049
  }
4742
5050
  const flowFile = validateFlowFile(resolved.path);
4743
5051
  logger.debug("Building flow bundle");
4744
- return lazyPrepareBundleForRun(flowFile, {
5052
+ const result = await lazyPrepareBundleForRun(flowFile, {
4745
5053
  verbose: false,
4746
5054
  silent: true,
4747
5055
  flowName: apiConfig?.flowName
4748
5056
  });
5057
+ return result.bundlePath;
4749
5058
  }
4750
5059
  if (apiConfig) {
4751
5060
  logger.info("Fetching config from API...");
@@ -4757,7 +5066,10 @@ async function resolveBundlePath(configInput, apiConfig, logger) {
4757
5066
  flowId: apiConfig.flowId
4758
5067
  });
4759
5068
  if (result.changed) {
4760
- const tmpConfigPath = `/tmp/walkeros-flow-${Date.now()}.json`;
5069
+ const tmpConfigPath = getTmpPath(
5070
+ void 0,
5071
+ `walkeros-flow-${Date.now()}.json`
5072
+ );
4761
5073
  writeFileSync5(
4762
5074
  tmpConfigPath,
4763
5075
  JSON.stringify(result.content, null, 2),
@@ -4765,7 +5077,7 @@ async function resolveBundlePath(configInput, apiConfig, logger) {
4765
5077
  );
4766
5078
  logger.info(`Config version: ${result.version}`);
4767
5079
  logger.info("Building flow...");
4768
- const bundlePath = await lazyPrepareBundleForRun(tmpConfigPath, {
5080
+ const bundleResult = await lazyPrepareBundleForRun(tmpConfigPath, {
4769
5081
  verbose: false,
4770
5082
  silent: true,
4771
5083
  flowName: apiConfig.flowName
@@ -4774,14 +5086,14 @@ async function resolveBundlePath(configInput, apiConfig, logger) {
4774
5086
  const { writeCache: writeCache2 } = await Promise.resolve().then(() => (init_cache(), cache_exports));
4775
5087
  writeCache2(
4776
5088
  apiConfig.cacheDir,
4777
- bundlePath,
5089
+ bundleResult.bundlePath,
4778
5090
  JSON.stringify(result.content),
4779
5091
  result.version
4780
5092
  );
4781
5093
  } catch {
4782
5094
  logger.debug("Cache write failed (non-critical)");
4783
5095
  }
4784
- return bundlePath;
5096
+ return bundleResult.bundlePath;
4785
5097
  }
4786
5098
  } catch (error) {
4787
5099
  logger.error(
@@ -4799,7 +5111,7 @@ async function resolveBundlePath(configInput, apiConfig, logger) {
4799
5111
  }
4800
5112
  const defaultFile = "server-collect.mjs";
4801
5113
  logger.debug(`No config specified, using default: ${defaultFile}`);
4802
- return path14.resolve(defaultFile);
5114
+ return path18.resolve(defaultFile);
4803
5115
  }
4804
5116
  async function run(options) {
4805
5117
  const startTime = Date.now();
@@ -4990,73 +5302,15 @@ function validateEntityActions(obj, prefix, errors) {
4990
5302
  }
4991
5303
 
4992
5304
  // src/commands/validate/validators/event.ts
4993
- import { schemas as schemas3 } from "@walkeros/core/dev";
4994
- var { PartialEventSchema } = schemas3;
4995
- function validateEvent(input) {
4996
- const errors = [];
4997
- const warnings = [];
4998
- const details = {};
4999
- const event = typeof input === "object" && input !== null ? input : {};
5000
- if (!("name" in event) || event.name === void 0) {
5001
- errors.push({
5002
- path: "name",
5003
- message: "Event must have a name field",
5004
- code: "MISSING_EVENT_NAME"
5005
- });
5006
- } else if (typeof event.name !== "string" || event.name.trim() === "") {
5007
- errors.push({
5008
- path: "name",
5009
- message: "Event name cannot be empty",
5010
- value: event.name,
5011
- code: "EMPTY_EVENT_NAME"
5012
- });
5013
- } else {
5014
- const name = event.name;
5015
- if (!name.includes(" ")) {
5016
- errors.push({
5017
- path: "name",
5018
- message: 'Event name must be "entity action" format with space (e.g., "page view")',
5019
- value: name,
5020
- code: "INVALID_EVENT_NAME"
5021
- });
5022
- details.entity = null;
5023
- details.action = null;
5024
- } else {
5025
- const parts = name.trim().split(/\s+/);
5026
- const action = parts.pop();
5027
- const entity = parts.join(" ");
5028
- details.entity = entity;
5029
- details.action = action;
5030
- }
5031
- }
5032
- const zodResult = PartialEventSchema.safeParse(input);
5033
- if (!zodResult.success) {
5034
- for (const issue of zodResult.error.issues) {
5035
- const path15 = issue.path.join(".");
5036
- if (path15 === "name") continue;
5037
- errors.push({
5038
- path: path15 || "root",
5039
- message: issue.message,
5040
- code: "SCHEMA_VALIDATION"
5041
- });
5042
- }
5043
- }
5044
- if (!event.consent) {
5045
- warnings.push({
5046
- path: "consent",
5047
- message: "No consent object provided",
5048
- suggestion: "Consider adding a consent object for GDPR/privacy compliance"
5049
- });
5050
- }
5051
- details.hasConsent = !!event.consent;
5052
- details.hasData = !!event.data;
5053
- details.hasContext = !!event.context;
5305
+ init_event_validation();
5306
+ function validateEvent2(input) {
5307
+ const result = validateEvent(input, "strict");
5054
5308
  return {
5055
- valid: errors.length === 0,
5309
+ valid: result.valid,
5056
5310
  type: "event",
5057
- errors,
5058
- warnings,
5059
- details
5311
+ errors: result.errors,
5312
+ warnings: result.warnings,
5313
+ details: result.details
5060
5314
  };
5061
5315
  }
5062
5316
 
@@ -5243,10 +5497,10 @@ function buildConnectionGraph(config) {
5243
5497
  function checkCompatibility(conn, errors, warnings) {
5244
5498
  const fromOuts = Object.entries(conn.from.examples).filter(([, ex]) => ex.out !== void 0 && ex.out !== false).map(([name, ex]) => ({ name, value: ex.out }));
5245
5499
  const toIns = Object.entries(conn.to.examples).filter(([, ex]) => ex.in !== void 0).map(([name, ex]) => ({ name, value: ex.in }));
5246
- const path15 = `${conn.from.type}.${conn.from.name} \u2192 ${conn.to.type}.${conn.to.name}`;
5500
+ const path19 = `${conn.from.type}.${conn.from.name} \u2192 ${conn.to.type}.${conn.to.name}`;
5247
5501
  if (fromOuts.length === 0 || toIns.length === 0) {
5248
5502
  warnings.push({
5249
- path: path15,
5503
+ path: path19,
5250
5504
  message: "Cannot check compatibility: missing out or in examples",
5251
5505
  suggestion: "Add out examples to the source step or in examples to the target step"
5252
5506
  });
@@ -5264,7 +5518,7 @@ function checkCompatibility(conn, errors, warnings) {
5264
5518
  }
5265
5519
  if (!hasMatch) {
5266
5520
  errors.push({
5267
- path: path15,
5521
+ path: path19,
5268
5522
  message: "No compatible out/in pair found between connected steps",
5269
5523
  code: "INCOMPATIBLE_EXAMPLES"
5270
5524
  });
@@ -5360,13 +5614,13 @@ function validateMapping(input) {
5360
5614
  import Ajv from "ajv";
5361
5615
  import { fetchPackageSchema } from "@walkeros/core";
5362
5616
  var SECTIONS = ["destinations", "sources", "transformers"];
5363
- function resolveEntry(path15, flowConfig) {
5617
+ function resolveEntry(path19, flowConfig) {
5364
5618
  const flows = flowConfig.flows;
5365
5619
  if (!flows || typeof flows !== "object") return "No flows found in config";
5366
5620
  const flowName = Object.keys(flows)[0];
5367
5621
  const flow = flows[flowName];
5368
5622
  if (!flow) return `Flow "${flowName}" is empty`;
5369
- const parts = path15.split(".");
5623
+ const parts = path19.split(".");
5370
5624
  if (parts.length === 2) {
5371
5625
  const [section, key] = parts;
5372
5626
  if (!SECTIONS.includes(section)) {
@@ -5403,15 +5657,15 @@ function resolveEntry(path15, flowConfig) {
5403
5657
  }
5404
5658
  return { section: matches[0].section, key, entry: matches[0].entry };
5405
5659
  }
5406
- return `Invalid path "${path15}". Use "section.key" or just "key"`;
5660
+ return `Invalid path "${path19}". Use "section.key" or just "key"`;
5407
5661
  }
5408
- async function validateEntry(path15, flowConfig) {
5409
- const resolved = resolveEntry(path15, flowConfig);
5662
+ async function validateEntry(path19, flowConfig) {
5663
+ const resolved = resolveEntry(path19, flowConfig);
5410
5664
  if (typeof resolved === "string") {
5411
5665
  return {
5412
5666
  valid: false,
5413
5667
  type: "entry",
5414
- errors: [{ path: path15, message: resolved, code: "ENTRY_VALIDATION" }],
5668
+ errors: [{ path: path19, message: resolved, code: "ENTRY_VALIDATION" }],
5415
5669
  warnings: [],
5416
5670
  details: {}
5417
5671
  };
@@ -5442,7 +5696,7 @@ async function validateEntry(path15, flowConfig) {
5442
5696
  type: "entry",
5443
5697
  errors: [
5444
5698
  {
5445
- path: path15,
5699
+ path: path19,
5446
5700
  message: error instanceof Error ? error.message : "Unknown error",
5447
5701
  code: "ENTRY_VALIDATION"
5448
5702
  }
@@ -5467,10 +5721,10 @@ async function validateEntry(path15, flowConfig) {
5467
5721
  const validate2 = ajv.compile(settingsSchema);
5468
5722
  const isValid = validate2(settings || {});
5469
5723
  if (!isValid) {
5470
- const errors = (validate2.errors || []).map((e2) => ({
5471
- path: e2.instancePath || "/",
5472
- message: e2.message || "Unknown error",
5473
- code: e2.keyword
5724
+ const errors = (validate2.errors || []).map((e) => ({
5725
+ path: e.instancePath || "/",
5726
+ message: e.message || "Unknown error",
5727
+ code: e.keyword
5474
5728
  }));
5475
5729
  return {
5476
5730
  valid: false,
@@ -5505,7 +5759,7 @@ async function validate(type, input, options = {}) {
5505
5759
  case "contract":
5506
5760
  return validateContract(resolved);
5507
5761
  case "event":
5508
- return validateEvent(resolved);
5762
+ return validateEvent2(resolved);
5509
5763
  case "flow":
5510
5764
  return validateFlow(resolved, { flow: options.flow });
5511
5765
  case "mapping":
@@ -5790,10 +6044,10 @@ async function deleteProject(options = {}) {
5790
6044
  throw new Error(error.error?.message || "Failed to delete project");
5791
6045
  return data ?? { success: true };
5792
6046
  }
5793
- async function handleResult(fn2, options) {
6047
+ async function handleResult(fn, options) {
5794
6048
  const logger = createCLILogger(options);
5795
6049
  try {
5796
- const result = await fn2();
6050
+ const result = await fn();
5797
6051
  await writeResult(JSON.stringify(result, null, 2), options);
5798
6052
  } catch (error) {
5799
6053
  logger.error(error instanceof Error ? error.message : String(error));
@@ -5949,10 +6203,10 @@ async function duplicateFlow(options) {
5949
6203
  if (error) throwApiError(error, "Failed to duplicate flow");
5950
6204
  return data;
5951
6205
  }
5952
- async function handleResult2(fn2, options) {
6206
+ async function handleResult2(fn, options) {
5953
6207
  const logger = createCLILogger(options);
5954
6208
  try {
5955
- const result = await fn2();
6209
+ const result = await fn();
5956
6210
  await writeResult(JSON.stringify(result, null, 2), options);
5957
6211
  } catch (error) {
5958
6212
  logger.error(error instanceof Error ? error.message : String(error));
@@ -6111,8 +6365,8 @@ async function deploy(options) {
6111
6365
  if (error) {
6112
6366
  try {
6113
6367
  throwApiError(error, "Failed to start deployment");
6114
- } catch (e2) {
6115
- if (e2 instanceof ApiError && e2.code === "AMBIGUOUS_CONFIG") {
6368
+ } catch (e) {
6369
+ if (e instanceof ApiError && e.code === "AMBIGUOUS_CONFIG") {
6116
6370
  const names = await getAvailableFlowNames({
6117
6371
  flowId: options.flowId,
6118
6372
  projectId
@@ -6123,7 +6377,7 @@ Available: ${names.join(", ")}`,
6123
6377
  { code: "AMBIGUOUS_CONFIG" }
6124
6378
  );
6125
6379
  }
6126
- throw e2;
6380
+ throw e;
6127
6381
  }
6128
6382
  }
6129
6383
  if (!options.wait) return data;
@@ -6260,7 +6514,7 @@ async function getDeploymentCommand(flowId, options) {
6260
6514
  // src/commands/deployments/index.ts
6261
6515
  init_auth();
6262
6516
  init_http();
6263
- import { getPlatform as getPlatform4 } from "@walkeros/core";
6517
+ import { getPlatform as getPlatform5 } from "@walkeros/core";
6264
6518
  init_cli_logger();
6265
6519
  init_output();
6266
6520
  init_loader();
@@ -6316,10 +6570,10 @@ async function deleteDeployment(options) {
6316
6570
  const data = await response.json().catch(() => null);
6317
6571
  return data ?? { success: true };
6318
6572
  }
6319
- async function handleResult3(fn2, options) {
6573
+ async function handleResult3(fn, options) {
6320
6574
  const logger = createCLILogger(options);
6321
6575
  try {
6322
- const result = await fn2();
6576
+ const result = await fn();
6323
6577
  await writeResult(JSON.stringify(result, null, 2), options);
6324
6578
  } catch (error) {
6325
6579
  logger.error(error instanceof Error ? error.message : String(error));
@@ -6393,7 +6647,7 @@ async function createDeployCommand(config, options) {
6393
6647
  const result2 = await loadFlowConfig(config, {
6394
6648
  flowName: options.flow
6395
6649
  });
6396
- type = getPlatform4(result2.flowSettings);
6650
+ type = getPlatform5(result2.flowSettings);
6397
6651
  }
6398
6652
  const deployment = await createDeployment({
6399
6653
  type,
@@ -6494,14 +6748,14 @@ async function feedbackCommand(text) {
6494
6748
  }
6495
6749
  }
6496
6750
  function promptUser(question) {
6497
- return new Promise((resolve2) => {
6751
+ return new Promise((resolve3) => {
6498
6752
  const rl = createInterface({
6499
6753
  input: process.stdin,
6500
6754
  output: process.stderr
6501
6755
  });
6502
6756
  rl.question(question, (answer) => {
6503
6757
  rl.close();
6504
- resolve2(answer);
6758
+ resolve3(answer);
6505
6759
  });
6506
6760
  });
6507
6761
  }
@@ -6514,6 +6768,108 @@ init_api_client();
6514
6768
  init_config_file();
6515
6769
  init_sse();
6516
6770
  init_utils();
6771
+
6772
+ // src/commands/simulate/example-loader.ts
6773
+ function findExample(config, exampleName, stepTarget) {
6774
+ if (stepTarget) {
6775
+ return findExampleInStep(config, exampleName, stepTarget);
6776
+ }
6777
+ return findExampleAcrossSteps(config, exampleName);
6778
+ }
6779
+ function findExampleInStep(config, exampleName, stepTarget) {
6780
+ const dotIndex = stepTarget.indexOf(".");
6781
+ if (dotIndex === -1) {
6782
+ throw new Error(
6783
+ `Invalid --step format: "${stepTarget}". Expected "type.name" (e.g. "destination.gtag")`
6784
+ );
6785
+ }
6786
+ const type = stepTarget.substring(0, dotIndex);
6787
+ const name = stepTarget.substring(dotIndex + 1);
6788
+ const stepMap = getStepMap(config, type);
6789
+ if (!stepMap) {
6790
+ throw new Error(`No ${type}s found in flow config`);
6791
+ }
6792
+ const step = stepMap[name];
6793
+ if (!step) {
6794
+ const available = Object.keys(stepMap).join(", ");
6795
+ throw new Error(`${type} "${name}" not found. Available: ${available}`);
6796
+ }
6797
+ const examples = step.examples;
6798
+ if (!examples || !examples[exampleName]) {
6799
+ const available = examples ? Object.keys(examples).join(", ") : "none";
6800
+ throw new Error(
6801
+ `Example "${exampleName}" not found in ${type} "${name}". Available: ${available}`
6802
+ );
6803
+ }
6804
+ return {
6805
+ stepType: type,
6806
+ stepName: name,
6807
+ exampleName,
6808
+ example: examples[exampleName]
6809
+ };
6810
+ }
6811
+ function findExampleAcrossSteps(config, exampleName) {
6812
+ const matches = [];
6813
+ const stepTypes = ["source", "transformer", "destination"];
6814
+ for (const type of stepTypes) {
6815
+ const stepMap = getStepMap(config, type);
6816
+ if (!stepMap) continue;
6817
+ for (const [name, step] of Object.entries(stepMap)) {
6818
+ const examples = step.examples;
6819
+ if (examples && examples[exampleName]) {
6820
+ matches.push({
6821
+ stepType: type,
6822
+ stepName: name,
6823
+ exampleName,
6824
+ example: examples[exampleName]
6825
+ });
6826
+ }
6827
+ }
6828
+ }
6829
+ if (matches.length === 0) {
6830
+ throw new Error(`Example "${exampleName}" not found in any step`);
6831
+ }
6832
+ if (matches.length > 1) {
6833
+ const locations = matches.map((m2) => `${m2.stepType}.${m2.stepName}`).join(", ");
6834
+ throw new Error(
6835
+ `Example "${exampleName}" found in multiple steps: ${locations}. Use --step to disambiguate.`
6836
+ );
6837
+ }
6838
+ return matches[0];
6839
+ }
6840
+ function getStepMap(config, type) {
6841
+ switch (type) {
6842
+ case "source":
6843
+ return config.sources;
6844
+ case "transformer":
6845
+ return config.transformers;
6846
+ case "destination":
6847
+ return config.destinations;
6848
+ default:
6849
+ throw new Error(
6850
+ `Invalid step type: "${type}". Must be "source", "transformer", or "destination"`
6851
+ );
6852
+ }
6853
+ }
6854
+
6855
+ // src/commands/simulate/compare.ts
6856
+ function compareOutput(expected, actual) {
6857
+ const expectedStr = JSON.stringify(expected, null, 2);
6858
+ const actualStr = JSON.stringify(actual, null, 2);
6859
+ if (expectedStr === actualStr) {
6860
+ return { expected, actual, match: true };
6861
+ }
6862
+ return {
6863
+ expected,
6864
+ actual,
6865
+ match: false,
6866
+ diff: `Expected:
6867
+ ${expectedStr}
6868
+
6869
+ Actual:
6870
+ ${actualStr}`
6871
+ };
6872
+ }
6517
6873
  export {
6518
6874
  ApiError,
6519
6875
  apiFetch,
@@ -6559,6 +6915,7 @@ export {
6559
6915
  listFlowsCommand,
6560
6916
  listProjects,
6561
6917
  listProjectsCommand,
6918
+ loadConfig,
6562
6919
  loadJsonConfig,
6563
6920
  loginCommand,
6564
6921
  logoutCommand,
@@ -6571,8 +6928,9 @@ export {
6571
6928
  requireProjectId,
6572
6929
  run,
6573
6930
  runCommand,
6574
- simulate,
6575
- simulateCommand,
6931
+ simulateDestination,
6932
+ simulateSource,
6933
+ simulateTransformer,
6576
6934
  throwApiError,
6577
6935
  updateFlow,
6578
6936
  updateFlowCommand,