@walkeros/cli 4.0.0-next-1777882869103 → 4.0.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
@@ -3401,7 +3401,51 @@ function installTimerInterception(options = {}) {
3401
3401
  }
3402
3402
  pending.clear();
3403
3403
  }
3404
- return { flush, countPending, restore };
3404
+ return { flush, countPending, restore, pending };
3405
+ }
3406
+
3407
+ // src/commands/push/async-drain-pump.ts
3408
+ var realSetImmediate = setImmediate;
3409
+ var DEFAULT_MAX_ITERATIONS = 1e3;
3410
+ var DEFAULT_MAX_WALL_MS = 3e4;
3411
+ var intervalRequeueCounter = -1;
3412
+ function startDrainPump(pending, options = {}) {
3413
+ const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;
3414
+ const maxWallMs = options.maxWallMs ?? DEFAULT_MAX_WALL_MS;
3415
+ const start = Date.now();
3416
+ let running = true;
3417
+ let iterations = 0;
3418
+ const tick = () => {
3419
+ if (!running) return;
3420
+ if (iterations >= maxIterations) return;
3421
+ if (Date.now() - start > maxWallMs) return;
3422
+ if (pending.size === 0) {
3423
+ realSetImmediate(tick);
3424
+ return;
3425
+ }
3426
+ iterations += 1;
3427
+ const snapshot = [...pending.values()].filter((t) => !t.cleared).sort((a, b) => a.delay - b.delay);
3428
+ for (const timer of snapshot) {
3429
+ pending.delete(timer.id);
3430
+ try {
3431
+ timer.callback(...timer.args);
3432
+ } catch (err) {
3433
+ console.warn(`[async-drain] timer ${timer.id} threw during pump:`, err);
3434
+ }
3435
+ if (timer.type === "interval" && !timer.cleared) {
3436
+ const requeued = {
3437
+ ...timer,
3438
+ id: intervalRequeueCounter--
3439
+ };
3440
+ pending.set(requeued.id, requeued);
3441
+ }
3442
+ }
3443
+ realSetImmediate(tick);
3444
+ };
3445
+ realSetImmediate(tick);
3446
+ return () => {
3447
+ running = false;
3448
+ };
3405
3449
  }
3406
3450
 
3407
3451
  // src/commands/push/flow-context.ts
@@ -3413,7 +3457,8 @@ async function withFlowContext(options, fn) {
3413
3457
  snapshotCode,
3414
3458
  timeout,
3415
3459
  networkCalls,
3416
- asyncDrain
3460
+ asyncDrain,
3461
+ drainPump
3417
3462
  } = options;
3418
3463
  const startTime = Date.now();
3419
3464
  const g = global;
@@ -3476,7 +3521,13 @@ async function withFlowContext(options, fn) {
3476
3521
  __devExports: module.__devExports
3477
3522
  };
3478
3523
  if (timerControl) {
3479
- const result = await fn(flowModule);
3524
+ const stopPump = drainPump ? startDrainPump(timerControl.pending) : null;
3525
+ let result;
3526
+ try {
3527
+ result = await fn(flowModule);
3528
+ } finally {
3529
+ if (stopPump) stopPump();
3530
+ }
3480
3531
  await timerControl.flush(asyncDrain?.timeout ?? 5e3);
3481
3532
  return result;
3482
3533
  } else if (timeout) {
@@ -3620,6 +3671,145 @@ async function prepareFlow(input) {
3620
3671
 
3621
3672
  // src/commands/push/index.ts
3622
3673
  import { schemas as schemas3 } from "@walkeros/core/dev";
3674
+
3675
+ // src/commands/push/plan-simulate.ts
3676
+ function planSimulate(flags) {
3677
+ if (flags.length === 0) return { kind: "none", ids: [] };
3678
+ const parsed = flags.map((flag) => {
3679
+ const step = parseStep(flag);
3680
+ if (step.chainType) {
3681
+ throw new Error(
3682
+ `--simulate "${flag}": chain syntax (${step.type}.${step.name}.${step.chainType}.\u2026) is not supported for --simulate. Use --mock for path-specific overrides.`
3683
+ );
3684
+ }
3685
+ return step;
3686
+ });
3687
+ const types = new Set(parsed.map((p) => p.type));
3688
+ if (types.size > 1) {
3689
+ const sorted = [...types].sort();
3690
+ throw new Error(
3691
+ `Cannot --simulate ${sorted.join(" and ")} in the same invocation. Run separate commands for each step type.`
3692
+ );
3693
+ }
3694
+ const [type] = types;
3695
+ const ids = [...new Set(parsed.map((p) => p.name))];
3696
+ if ((type === "source" || type === "transformer") && ids.length > 1) {
3697
+ throw new Error(
3698
+ `--simulate ${type}.* expects a single target; got ${ids.length}. Run one --simulate ${type}.NAME per invocation.`
3699
+ );
3700
+ }
3701
+ return { kind: type, ids };
3702
+ }
3703
+
3704
+ // src/commands/push/dispatch-simulate.ts
3705
+ function dispatchSimulate(flags) {
3706
+ const plan = planSimulate(flags);
3707
+ return { route: plan.kind, ids: plan.ids };
3708
+ }
3709
+
3710
+ // src/commands/push/run.ts
3711
+ init_core();
3712
+ init_config();
3713
+ async function runPushCommand(options) {
3714
+ const startTime = Date.now();
3715
+ try {
3716
+ const plan = dispatchSimulate(options.simulate ?? []);
3717
+ let config;
3718
+ if (isStdinPiped() && !options.config) {
3719
+ config = await readStdinToTempFile("push");
3720
+ } else {
3721
+ config = options.config || "bundle.config.json";
3722
+ }
3723
+ let resolvedEvent = options.event;
3724
+ if (typeof options.event === "string") {
3725
+ resolvedEvent = await loadJsonFromSource(options.event, {
3726
+ name: "event"
3727
+ });
3728
+ }
3729
+ let result;
3730
+ switch (plan.route) {
3731
+ case "none":
3732
+ result = await push(config, resolvedEvent, {
3733
+ flow: options.flow,
3734
+ json: options.json,
3735
+ verbose: options.verbose,
3736
+ silent: options.silent,
3737
+ platform: options.platform,
3738
+ mock: options.mock,
3739
+ snapshot: options.snapshot
3740
+ });
3741
+ break;
3742
+ case "source":
3743
+ result = await simulateSource(config, resolvedEvent, {
3744
+ sourceId: plan.ids[0],
3745
+ flow: options.flow,
3746
+ silent: options.silent,
3747
+ verbose: options.verbose,
3748
+ snapshot: options.snapshot
3749
+ });
3750
+ break;
3751
+ case "transformer":
3752
+ result = await simulateTransformer(
3753
+ config,
3754
+ resolvedEvent,
3755
+ {
3756
+ transformerId: plan.ids[0],
3757
+ flow: options.flow,
3758
+ mock: options.mock,
3759
+ silent: options.silent,
3760
+ verbose: options.verbose,
3761
+ snapshot: options.snapshot
3762
+ }
3763
+ );
3764
+ break;
3765
+ case "destination":
3766
+ result = await runDestinationSimulationLoop(
3767
+ config,
3768
+ resolvedEvent,
3769
+ plan.ids,
3770
+ options
3771
+ );
3772
+ break;
3773
+ }
3774
+ return result;
3775
+ } catch (error) {
3776
+ return {
3777
+ success: false,
3778
+ duration: Date.now() - startTime,
3779
+ error: getErrorMessage(error)
3780
+ };
3781
+ }
3782
+ }
3783
+ async function runDestinationSimulationLoop(config, event, destinationIds, options) {
3784
+ const startTime = Date.now();
3785
+ const perDestination = {};
3786
+ for (const destinationId of destinationIds) {
3787
+ const r = await simulateDestination(config, event, {
3788
+ destinationId,
3789
+ flow: options.flow,
3790
+ mock: options.mock,
3791
+ silent: options.silent,
3792
+ verbose: options.verbose,
3793
+ snapshot: options.snapshot
3794
+ });
3795
+ perDestination[destinationId] = r;
3796
+ if (!r.success) {
3797
+ return {
3798
+ success: false,
3799
+ duration: Date.now() - startTime,
3800
+ error: `simulate destination.${destinationId}: ${r.error ?? "unknown error"}`,
3801
+ perDestination
3802
+ };
3803
+ }
3804
+ }
3805
+ return {
3806
+ success: true,
3807
+ duration: Date.now() - startTime,
3808
+ perDestination
3809
+ };
3810
+ }
3811
+
3812
+ // src/commands/push/index.ts
3623
3813
  function resolveBeforeChain(before, transformers, ingest, event) {
3624
3814
  if (!before) return [];
3625
3815
  const next = before;
@@ -3698,108 +3888,32 @@ async function pushCore(inputPath, event, options = {}) {
3698
3888
  }
3699
3889
  }
3700
3890
  async function pushCommand(options) {
3701
- const logger = createCLILogger({ ...options, stderr: true });
3702
- const startTime = Date.now();
3703
- try {
3704
- let config;
3705
- if (isStdinPiped() && !options.config) {
3706
- config = await readStdinToTempFile("push");
3707
- } else {
3708
- config = options.config || "bundle.config.json";
3709
- }
3710
- let resolvedEvent = options.event;
3711
- if (typeof options.event === "string") {
3712
- resolvedEvent = await loadJsonFromSource(options.event, {
3713
- name: "event"
3714
- });
3715
- }
3716
- const simulateFlag = options.simulate?.[0];
3717
- let result;
3718
- if (simulateFlag?.startsWith("source.")) {
3719
- result = await simulateSource(config, resolvedEvent, {
3720
- sourceId: simulateFlag.replace("source.", ""),
3721
- flow: options.flow,
3722
- silent: options.silent,
3723
- verbose: options.verbose,
3724
- snapshot: options.snapshot
3725
- });
3726
- } else if (simulateFlag?.startsWith("transformer.")) {
3727
- result = await simulateTransformer(
3728
- config,
3729
- resolvedEvent,
3730
- {
3731
- transformerId: simulateFlag.replace("transformer.", ""),
3732
- flow: options.flow,
3733
- mock: options.mock,
3734
- silent: options.silent,
3735
- verbose: options.verbose,
3736
- snapshot: options.snapshot
3737
- }
3738
- );
3739
- } else if (simulateFlag?.startsWith("destination.")) {
3740
- result = await simulateDestination(
3741
- config,
3742
- resolvedEvent,
3743
- {
3744
- destinationId: simulateFlag.replace("destination.", ""),
3745
- flow: options.flow,
3746
- mock: options.mock,
3747
- silent: options.silent,
3748
- verbose: options.verbose,
3749
- snapshot: options.snapshot
3750
- }
3751
- );
3752
- } else {
3753
- result = await push(config, resolvedEvent, {
3754
- flow: options.flow,
3755
- json: options.json,
3756
- verbose: options.verbose,
3757
- silent: options.silent,
3758
- platform: options.platform,
3759
- mock: options.mock,
3760
- snapshot: options.snapshot
3761
- });
3762
- }
3763
- const duration = Date.now() - startTime;
3764
- let output;
3765
- if (options.json) {
3766
- output = JSON.stringify({ ...result, duration }, null, 2);
3767
- } else {
3768
- const lines = [];
3769
- if (result.success) {
3770
- lines.push("Event pushed successfully");
3771
- if (result.elbResult && typeof result.elbResult === "object") {
3772
- const pushResult = result.elbResult;
3773
- if ("id" in pushResult && pushResult.id)
3774
- lines.push(` Event ID: ${pushResult.id}`);
3775
- if ("entity" in pushResult && pushResult.entity)
3776
- lines.push(` Entity: ${pushResult.entity}`);
3777
- if ("action" in pushResult && pushResult.action)
3778
- lines.push(` Action: ${pushResult.action}`);
3779
- }
3780
- lines.push(` Duration: ${duration}ms`);
3781
- } else {
3782
- lines.push(`Error: ${result.error}`);
3891
+ const result = await runPushCommand(options);
3892
+ const duration = result.duration;
3893
+ let output;
3894
+ if (options.json) {
3895
+ output = JSON.stringify({ ...result, duration }, null, 2);
3896
+ } else {
3897
+ const lines = [];
3898
+ if (result.success) {
3899
+ lines.push("Event pushed successfully");
3900
+ if (result.elbResult && typeof result.elbResult === "object") {
3901
+ const pushResult = result.elbResult;
3902
+ if ("id" in pushResult && pushResult.id)
3903
+ lines.push(` Event ID: ${pushResult.id}`);
3904
+ if ("entity" in pushResult && pushResult.entity)
3905
+ lines.push(` Entity: ${pushResult.entity}`);
3906
+ if ("action" in pushResult && pushResult.action)
3907
+ lines.push(` Action: ${pushResult.action}`);
3783
3908
  }
3784
- output = lines.join("\n");
3785
- }
3786
- await writeResult(output + "\n", { output: options.output });
3787
- process.exit(result.success ? 0 : 1);
3788
- } catch (error) {
3789
- const duration = Date.now() - startTime;
3790
- const errorMessage = getErrorMessage(error);
3791
- if (options.json) {
3792
- const errorOutput = JSON.stringify(
3793
- { success: false, error: errorMessage, duration },
3794
- null,
3795
- 2
3796
- );
3797
- await writeResult(errorOutput + "\n", { output: options.output });
3909
+ lines.push(` Duration: ${duration}ms`);
3798
3910
  } else {
3799
- logger.error(`Error: ${errorMessage}`);
3911
+ lines.push(`Error: ${result.error}`);
3800
3912
  }
3801
- process.exit(1);
3913
+ output = lines.join("\n");
3802
3914
  }
3915
+ await writeResult(output + "\n", { output: options.output });
3916
+ process.exit(result.success ? 0 : 1);
3803
3917
  }
3804
3918
  async function push(configOrPath, event, options = {}) {
3805
3919
  if (typeof configOrPath !== "string") {
@@ -3898,7 +4012,12 @@ async function executeDestinationPush(esmPath, event, logger, platform, override
3898
4012
  snapshotCode,
3899
4013
  timeout,
3900
4014
  networkCalls,
3901
- asyncDrain: { timeout: 5e3 }
4015
+ asyncDrain: { timeout: 5e3 },
4016
+ // Real push (non-simulate) needs the pump so destinations whose init
4017
+ // awaits a captured setTimeout (e.g., amplitude engagement plugin)
4018
+ // don't deadlock. Simulate routes use their own withFlowContext call
4019
+ // sites without `drainPump`, preserving snapshot ordering.
4020
+ drainPump: true
3902
4021
  },
3903
4022
  async (module) => {
3904
4023
  const config = module.wireConfig(module.__configData ?? void 0);
@@ -5752,7 +5871,7 @@ function validateMapping(input) {
5752
5871
  // src/commands/validate/validators/entry.ts
5753
5872
  import Ajv from "ajv";
5754
5873
  import { fetchPackageSchema } from "@walkeros/core";
5755
- var CLIENT_HEADER = "walkeros-cli/4.0.0-next-1777882869103";
5874
+ var CLIENT_HEADER = "walkeros-cli/4.0.0";
5756
5875
  var SECTIONS = ["destinations", "sources", "transformers"];
5757
5876
  function resolveEntry(path19, flowConfig) {
5758
5877
  const flows = flowConfig.flows;
@@ -7525,9 +7644,6 @@ __export(telemetry_exports, {
7525
7644
  });
7526
7645
 
7527
7646
  // src/telemetry/emitter.ts
7528
- import { readFileSync as readFileSync4 } from "fs";
7529
- import { join as join6, dirname as dirname5 } from "path";
7530
- import { fileURLToPath as fileURLToPath2 } from "url";
7531
7647
  import { startFlow } from "@walkeros/collector";
7532
7648
  import { destinationAPI } from "@walkeros/server-destination-api";
7533
7649
 
@@ -7591,6 +7707,7 @@ function maybePrintFirstRunNotice() {
7591
7707
  }
7592
7708
 
7593
7709
  // src/telemetry/emitter.ts
7710
+ init_config_file();
7594
7711
  var SEND_TIMEOUT_MS = 1e3;
7595
7712
  async function createEmitter(opts) {
7596
7713
  if (!isTelemetryEnabled()) {
@@ -7608,7 +7725,7 @@ async function createEmitter(opts) {
7608
7725
  }
7609
7726
  const device = maybeDevice;
7610
7727
  const debug = isDebugMode();
7611
- const endpoint = process.env.TELEMETRY_ENDPOINT || loadFlowJsonEndpoint();
7728
+ const endpoint = resolveTelemetryEndpoint();
7612
7729
  if (!endpoint && !debug) {
7613
7730
  return {
7614
7731
  async send() {
@@ -7683,15 +7800,10 @@ async function createEmitter(opts) {
7683
7800
  }
7684
7801
  };
7685
7802
  }
7686
- function loadFlowJsonEndpoint() {
7687
- try {
7688
- const here = dirname5(fileURLToPath2(import.meta.url));
7689
- const raw = JSON.parse(readFileSync4(join6(here, "flow.json"), "utf-8"));
7690
- const url = raw.flows?.default?.destinations?.api?.config?.url;
7691
- if (url && !url.startsWith("$")) return url;
7692
- } catch {
7693
- }
7694
- return void 0;
7803
+ function resolveTelemetryEndpoint() {
7804
+ const appUrl = resolveAppUrl();
7805
+ if (!appUrl) return void 0;
7806
+ return `${appUrl.replace(/\/$/, "")}/api/telemetry`;
7695
7807
  }
7696
7808
  async function withTimeout2(p, ms) {
7697
7809
  return Promise.race([