@walkeros/cli 4.2.0 → 4.2.1-next-1781538735002
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/dist/cli.js +822 -381
- package/dist/index.d.ts +879 -114
- package/dist/index.js +844 -399
- package/dist/index.js.map +1 -1
- package/openapi/spec.json +2543 -1442
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -25,6 +25,10 @@ function createCLILogger(options = {}) {
|
|
|
25
25
|
handler: (level, message, _context, scope) => {
|
|
26
26
|
const scopePath = scope.length > 0 ? `[${scope.join(":")}] ` : "";
|
|
27
27
|
const fullMessage = `${scopePath}${message}`;
|
|
28
|
+
try {
|
|
29
|
+
options.onLine?.(level, fullMessage);
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
28
32
|
if (level === Level.ERROR) {
|
|
29
33
|
if (!json) console.error(chalk.red(fullMessage));
|
|
30
34
|
return;
|
|
@@ -2755,6 +2759,9 @@ ${firstError.text}`
|
|
|
2755
2759
|
` + (location ? ` at ${location.file}:${location.line}:${location.column}` : "")
|
|
2756
2760
|
);
|
|
2757
2761
|
}
|
|
2762
|
+
function buildDataPayload(flowSettings) {
|
|
2763
|
+
return buildSplitConfigObject(flowSettings, detectNamedImports(flowSettings)).dataPayloadObj;
|
|
2764
|
+
}
|
|
2758
2765
|
function buildSplitConfigObject(flowSettings, namedImports) {
|
|
2759
2766
|
const sources = flowSettings.sources || {};
|
|
2760
2767
|
const destinations = flowSettings.destinations || {};
|
|
@@ -2777,7 +2784,7 @@ function buildSplitConfigObject(flowSettings, namedImports) {
|
|
|
2777
2784
|
}
|
|
2778
2785
|
return props;
|
|
2779
2786
|
}
|
|
2780
|
-
function buildSplitStepEntry(section,
|
|
2787
|
+
function buildSplitStepEntry(section, stepId2, step) {
|
|
2781
2788
|
const codeVar = resolveCodeVar(step);
|
|
2782
2789
|
const stepProps = getStepProps(step);
|
|
2783
2790
|
const { codeProps, dataProps } = classifyStepProperties(stepProps);
|
|
@@ -2788,13 +2795,13 @@ function buildSplitConfigObject(flowSettings, namedImports) {
|
|
|
2788
2795
|
codeEntries.push(`${key}: ${processConfigValue(value)}`);
|
|
2789
2796
|
}
|
|
2790
2797
|
for (const key of Object.keys(dataProps)) {
|
|
2791
|
-
codeEntries.push(`${key}: __data.${section}.${
|
|
2798
|
+
codeEntries.push(`${key}: __data.${section}.${stepId2}.${key}`);
|
|
2792
2799
|
}
|
|
2793
2800
|
if (Object.keys(dataProps).length > 0) {
|
|
2794
2801
|
if (!dataPayloadObj[section]) dataPayloadObj[section] = {};
|
|
2795
|
-
dataPayloadObj[section][
|
|
2802
|
+
dataPayloadObj[section][stepId2] = dataProps;
|
|
2796
2803
|
}
|
|
2797
|
-
return ` ${
|
|
2804
|
+
return ` ${stepId2}: {
|
|
2798
2805
|
${codeEntries.join(",\n ")}
|
|
2799
2806
|
}`;
|
|
2800
2807
|
}
|
|
@@ -2904,7 +2911,7 @@ ${destinationsEntries.join(",\n")}
|
|
|
2904
2911
|
stores${collectorStr}
|
|
2905
2912
|
}`;
|
|
2906
2913
|
const dataPayload = JSON.stringify(dataPayloadObj, null, 2);
|
|
2907
|
-
return { storesDeclaration, codeConfigObject, dataPayload };
|
|
2914
|
+
return { storesDeclaration, codeConfigObject, dataPayloadObj, dataPayload };
|
|
2908
2915
|
}
|
|
2909
2916
|
function generateSplitWireConfigModule(storesDeclaration, codeConfigObject, userCode) {
|
|
2910
2917
|
const codeSection = userCode ? `
|
|
@@ -3848,7 +3855,8 @@ import {
|
|
|
3848
3855
|
createIngest,
|
|
3849
3856
|
getPlatform as getPlatform3,
|
|
3850
3857
|
getNextSteps,
|
|
3851
|
-
buildCacheContext
|
|
3858
|
+
buildCacheContext,
|
|
3859
|
+
stepId
|
|
3852
3860
|
} from "@walkeros/core";
|
|
3853
3861
|
import {
|
|
3854
3862
|
enrichEvent,
|
|
@@ -4052,13 +4060,13 @@ function installTimerInterception(options = {}) {
|
|
|
4052
4060
|
setInterval: Reflect.get(target, "setInterval"),
|
|
4053
4061
|
clearInterval: Reflect.get(target, "clearInterval")
|
|
4054
4062
|
});
|
|
4055
|
-
const trackedSetTimeout = (callback,
|
|
4063
|
+
const trackedSetTimeout = (callback, delay2, ...args) => {
|
|
4056
4064
|
if (typeof callback !== "function") return 0;
|
|
4057
4065
|
const id = nextId++;
|
|
4058
4066
|
pending.set(id, {
|
|
4059
4067
|
id,
|
|
4060
4068
|
callback,
|
|
4061
|
-
delay:
|
|
4069
|
+
delay: delay2 ?? 0,
|
|
4062
4070
|
type: "timeout",
|
|
4063
4071
|
args,
|
|
4064
4072
|
cleared: false
|
|
@@ -4071,13 +4079,13 @@ function installTimerInterception(options = {}) {
|
|
|
4071
4079
|
const entry = pending.get(numId);
|
|
4072
4080
|
if (entry) entry.cleared = true;
|
|
4073
4081
|
};
|
|
4074
|
-
const trackedSetInterval = (callback,
|
|
4082
|
+
const trackedSetInterval = (callback, delay2, ...args) => {
|
|
4075
4083
|
if (typeof callback !== "function") return 0;
|
|
4076
4084
|
const id = nextId++;
|
|
4077
4085
|
pending.set(id, {
|
|
4078
4086
|
id,
|
|
4079
4087
|
callback,
|
|
4080
|
-
delay:
|
|
4088
|
+
delay: delay2 ?? 0,
|
|
4081
4089
|
type: "interval",
|
|
4082
4090
|
args,
|
|
4083
4091
|
cleared: false
|
|
@@ -4354,7 +4362,7 @@ function toError(error) {
|
|
|
4354
4362
|
return error instanceof Error ? error : new Error(getErrorMessage(error));
|
|
4355
4363
|
}
|
|
4356
4364
|
function buildSimulationResult(args) {
|
|
4357
|
-
const { step, name, startTime, captured, usage, error } = args;
|
|
4365
|
+
const { step, name, startTime, captured, usage, mappingKey, error } = args;
|
|
4358
4366
|
const events = (captured ?? []).filter(hasEvent).map((entry) => entry.event);
|
|
4359
4367
|
const calls = usage ? Object.values(usage).flat().map((call) => ({ fn: call.fn, args: call.args, ts: call.ts })) : [];
|
|
4360
4368
|
return {
|
|
@@ -4363,6 +4371,7 @@ function buildSimulationResult(args) {
|
|
|
4363
4371
|
events,
|
|
4364
4372
|
calls,
|
|
4365
4373
|
duration: Date.now() - startTime,
|
|
4374
|
+
...mappingKey !== void 0 ? { mappingKey } : {},
|
|
4366
4375
|
...error !== void 0 ? { error: toError(error) } : {}
|
|
4367
4376
|
};
|
|
4368
4377
|
}
|
|
@@ -4956,7 +4965,9 @@ async function simulateSource(configOrPath, input, options) {
|
|
|
4956
4965
|
`Source package "${sourceConfig.package}" has no createTrigger in /dev export`
|
|
4957
4966
|
);
|
|
4958
4967
|
}
|
|
4959
|
-
const flowConfig = module.wireConfig(
|
|
4968
|
+
const flowConfig = module.wireConfig(
|
|
4969
|
+
options.data ?? module.__configData ?? void 0
|
|
4970
|
+
);
|
|
4960
4971
|
applyOverrides(flowConfig, prepared.overrides);
|
|
4961
4972
|
const captured = [];
|
|
4962
4973
|
flowConfig.hooks = {
|
|
@@ -5061,7 +5072,9 @@ async function simulateTransformer(configOrPath, event, options) {
|
|
|
5061
5072
|
networkCalls
|
|
5062
5073
|
},
|
|
5063
5074
|
async (module) => {
|
|
5064
|
-
const flowConfig = module.wireConfig(
|
|
5075
|
+
const flowConfig = module.wireConfig(
|
|
5076
|
+
options.data ?? module.__configData ?? void 0
|
|
5077
|
+
);
|
|
5065
5078
|
applyOverrides(flowConfig, prepared.overrides);
|
|
5066
5079
|
if (flowConfig.sources) flowConfig.sources = {};
|
|
5067
5080
|
if (flowConfig.destinations) flowConfig.destinations = {};
|
|
@@ -5227,7 +5240,9 @@ async function simulateCollector(configOrPath, event, options) {
|
|
|
5227
5240
|
networkCalls
|
|
5228
5241
|
},
|
|
5229
5242
|
async (module) => {
|
|
5230
|
-
const flowConfig = module.wireConfig(
|
|
5243
|
+
const flowConfig = module.wireConfig(
|
|
5244
|
+
options.data ?? module.__configData ?? void 0
|
|
5245
|
+
);
|
|
5231
5246
|
applyOverrides(flowConfig, prepared.overrides);
|
|
5232
5247
|
if (flowConfig.sources) flowConfig.sources = {};
|
|
5233
5248
|
if (flowConfig.destinations) flowConfig.destinations = {};
|
|
@@ -5330,7 +5345,9 @@ async function simulateDestination(configOrPath, event, options) {
|
|
|
5330
5345
|
networkCalls
|
|
5331
5346
|
},
|
|
5332
5347
|
async (module) => {
|
|
5333
|
-
const flowConfig = module.wireConfig(
|
|
5348
|
+
const flowConfig = module.wireConfig(
|
|
5349
|
+
options.data ?? module.__configData ?? void 0
|
|
5350
|
+
);
|
|
5334
5351
|
applyOverrides(flowConfig, prepared.overrides);
|
|
5335
5352
|
const destPkg = (prepared.flowSettings.destinations ?? {})[options.destinationId];
|
|
5336
5353
|
let trackedCalls = [];
|
|
@@ -5366,6 +5383,16 @@ async function simulateDestination(configOrPath, event, options) {
|
|
|
5366
5383
|
);
|
|
5367
5384
|
}
|
|
5368
5385
|
logger.info(`Simulating destination: ${options.destinationId}`);
|
|
5386
|
+
let mappingKey;
|
|
5387
|
+
const targetStepId = stepId("destination", options.destinationId);
|
|
5388
|
+
const captureMappingKey = (state) => {
|
|
5389
|
+
if (state.stepId === targetStepId && state.mappingKey) {
|
|
5390
|
+
mappingKey = state.mappingKey;
|
|
5391
|
+
}
|
|
5392
|
+
};
|
|
5393
|
+
if (collector.observers instanceof Set) {
|
|
5394
|
+
collector.observers.add(captureMappingKey);
|
|
5395
|
+
}
|
|
5369
5396
|
await collector.push(event, {
|
|
5370
5397
|
include: [options.destinationId]
|
|
5371
5398
|
});
|
|
@@ -5374,7 +5401,8 @@ async function simulateDestination(configOrPath, event, options) {
|
|
|
5374
5401
|
step: "destination",
|
|
5375
5402
|
name: options.destinationId,
|
|
5376
5403
|
startTime,
|
|
5377
|
-
usage: trackedCalls.length ? { [options.destinationId]: trackedCalls } : void 0
|
|
5404
|
+
usage: trackedCalls.length ? { [options.destinationId]: trackedCalls } : void 0,
|
|
5405
|
+
mappingKey
|
|
5378
5406
|
});
|
|
5379
5407
|
},
|
|
5380
5408
|
(error) => buildSimulationResult({
|
|
@@ -5399,13 +5427,88 @@ async function simulateDestination(configOrPath, event, options) {
|
|
|
5399
5427
|
// src/commands/run/index.ts
|
|
5400
5428
|
init_cli_logger();
|
|
5401
5429
|
init_core();
|
|
5402
|
-
init_tmp();
|
|
5403
|
-
init_config_file();
|
|
5404
|
-
init_auth();
|
|
5405
5430
|
import path18 from "path";
|
|
5406
5431
|
import { writeFileSync as writeFileSync5 } from "fs";
|
|
5407
5432
|
import { homedir as homedir2 } from "os";
|
|
5408
5433
|
import { join as join7 } from "path";
|
|
5434
|
+
import { Level as Level2 } from "@walkeros/core";
|
|
5435
|
+
|
|
5436
|
+
// src/runtime/runner.ts
|
|
5437
|
+
import { resolve as resolve3, dirname as dirname3 } from "path";
|
|
5438
|
+
|
|
5439
|
+
// src/runtime/load-bundle.ts
|
|
5440
|
+
import { resolve as resolve2 } from "path";
|
|
5441
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
5442
|
+
async function loadBundle(file, context2, logger) {
|
|
5443
|
+
const absolutePath = resolve2(file);
|
|
5444
|
+
const fileUrl = pathToFileURL2(absolutePath).href;
|
|
5445
|
+
logger?.debug?.(`Importing bundle: ${absolutePath}`);
|
|
5446
|
+
const module = await import(`${fileUrl}?t=${Date.now()}`);
|
|
5447
|
+
if (!module.default || typeof module.default !== "function") {
|
|
5448
|
+
throw new Error(
|
|
5449
|
+
`Invalid bundle: ${file} must export a default factory function`
|
|
5450
|
+
);
|
|
5451
|
+
}
|
|
5452
|
+
logger?.debug?.("Calling factory function...");
|
|
5453
|
+
const result = await module.default(context2 ?? {});
|
|
5454
|
+
if (!result || !result.collector || typeof result.collector.push !== "function") {
|
|
5455
|
+
throw new Error(
|
|
5456
|
+
`Invalid bundle: factory must return { collector } with a push function`
|
|
5457
|
+
);
|
|
5458
|
+
}
|
|
5459
|
+
return {
|
|
5460
|
+
collector: result.collector,
|
|
5461
|
+
...typeof result.httpHandler === "function" ? { httpHandler: result.httpHandler } : {}
|
|
5462
|
+
};
|
|
5463
|
+
}
|
|
5464
|
+
|
|
5465
|
+
// src/runtime/runner.ts
|
|
5466
|
+
async function loadFlow(file, config, logger, loggerConfig, healthServer, observers) {
|
|
5467
|
+
const absolutePath = resolve3(file);
|
|
5468
|
+
const flowDir = dirname3(absolutePath);
|
|
5469
|
+
process.chdir(flowDir);
|
|
5470
|
+
const flowContext = {
|
|
5471
|
+
...config,
|
|
5472
|
+
...loggerConfig ? { logger: loggerConfig } : {},
|
|
5473
|
+
...healthServer ? { sourceSettings: { port: void 0 } } : {},
|
|
5474
|
+
...observers ? { observers } : {}
|
|
5475
|
+
};
|
|
5476
|
+
const result = await loadBundle(absolutePath, flowContext, logger);
|
|
5477
|
+
if (healthServer && typeof result.httpHandler === "function") {
|
|
5478
|
+
healthServer.setFlowHandler(result.httpHandler);
|
|
5479
|
+
}
|
|
5480
|
+
return {
|
|
5481
|
+
collector: {
|
|
5482
|
+
command: result.collector.command,
|
|
5483
|
+
status: result.collector.status
|
|
5484
|
+
},
|
|
5485
|
+
file,
|
|
5486
|
+
httpHandler: result.httpHandler
|
|
5487
|
+
};
|
|
5488
|
+
}
|
|
5489
|
+
async function swapFlow(currentHandle, newFile, config, logger, loggerConfig, healthServer, observers) {
|
|
5490
|
+
logger.info("Shutting down current flow for hot-swap...");
|
|
5491
|
+
if (healthServer) {
|
|
5492
|
+
healthServer.setFlowHandler(null);
|
|
5493
|
+
}
|
|
5494
|
+
try {
|
|
5495
|
+
if (currentHandle.collector.command) {
|
|
5496
|
+
await currentHandle.collector.command("shutdown");
|
|
5497
|
+
}
|
|
5498
|
+
} catch (error) {
|
|
5499
|
+
logger.debug(`Shutdown warning: ${error}`);
|
|
5500
|
+
}
|
|
5501
|
+
const newHandle = await loadFlow(
|
|
5502
|
+
newFile,
|
|
5503
|
+
config,
|
|
5504
|
+
logger,
|
|
5505
|
+
loggerConfig,
|
|
5506
|
+
healthServer,
|
|
5507
|
+
observers
|
|
5508
|
+
);
|
|
5509
|
+
logger.info("Flow swapped successfully");
|
|
5510
|
+
return newHandle;
|
|
5511
|
+
}
|
|
5409
5512
|
|
|
5410
5513
|
// src/runtime/resolve-bundle.ts
|
|
5411
5514
|
init_stdin();
|
|
@@ -5416,11 +5519,127 @@ import {
|
|
|
5416
5519
|
rmSync,
|
|
5417
5520
|
writeFileSync as writeFileSync2
|
|
5418
5521
|
} from "fs";
|
|
5419
|
-
import { dirname as
|
|
5522
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
5420
5523
|
import { Readable } from "stream";
|
|
5421
5524
|
import { x as tarExtract } from "tar";
|
|
5525
|
+
|
|
5526
|
+
// src/runtime/fetch-retry.ts
|
|
5527
|
+
var DEFAULT_PER_ATTEMPT_TIMEOUT_MS = 3e4;
|
|
5528
|
+
var DEFAULT_MAX_TOTAL_MS = 6e4;
|
|
5529
|
+
var DEFAULT_ATTEMPTS = 3;
|
|
5530
|
+
var MIN_ATTEMPT_BUDGET_MS = 1e3;
|
|
5531
|
+
var BASE_BACKOFF_MS = [2e3, 5e3];
|
|
5532
|
+
var JITTER = 0.2;
|
|
5533
|
+
var RETRYABLE_NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
5534
|
+
"ECONNRESET",
|
|
5535
|
+
"ECONNREFUSED",
|
|
5536
|
+
"ETIMEDOUT",
|
|
5537
|
+
"EAI_AGAIN",
|
|
5538
|
+
"ENOTFOUND"
|
|
5539
|
+
]);
|
|
5540
|
+
function readErrorCode(value) {
|
|
5541
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
5542
|
+
const code = Reflect.get(value, "code");
|
|
5543
|
+
return typeof code === "string" ? code : void 0;
|
|
5544
|
+
}
|
|
5545
|
+
function readErrorName(value) {
|
|
5546
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
5547
|
+
const name = Reflect.get(value, "name");
|
|
5548
|
+
return typeof name === "string" ? name : void 0;
|
|
5549
|
+
}
|
|
5550
|
+
function isTransientThrow(error) {
|
|
5551
|
+
const name = readErrorName(error);
|
|
5552
|
+
if (name === "TimeoutError" || name === "AbortError") return true;
|
|
5553
|
+
const directCode = readErrorCode(error);
|
|
5554
|
+
if (directCode && RETRYABLE_NETWORK_CODES.has(directCode)) return true;
|
|
5555
|
+
if (typeof error === "object" && error !== null) {
|
|
5556
|
+
const causeCode = readErrorCode(Reflect.get(error, "cause"));
|
|
5557
|
+
if (causeCode && RETRYABLE_NETWORK_CODES.has(causeCode)) return true;
|
|
5558
|
+
}
|
|
5559
|
+
return error instanceof TypeError;
|
|
5560
|
+
}
|
|
5561
|
+
function isTransientStatus(status) {
|
|
5562
|
+
return status >= 500 || status === 429;
|
|
5563
|
+
}
|
|
5564
|
+
function readErrorMessage(value) {
|
|
5565
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
5566
|
+
const message = Reflect.get(value, "message");
|
|
5567
|
+
return typeof message === "string" ? message : void 0;
|
|
5568
|
+
}
|
|
5569
|
+
function describeReason(reason) {
|
|
5570
|
+
if (reason.kind === "status") return `HTTP ${reason.status}`;
|
|
5571
|
+
const name = readErrorName(reason.error);
|
|
5572
|
+
const message = reason.error instanceof Error ? reason.error.message : String(reason.error);
|
|
5573
|
+
let detail = name ? `${name}: ${message}` : message;
|
|
5574
|
+
if (typeof reason.error === "object" && reason.error !== null) {
|
|
5575
|
+
const cause = Reflect.get(reason.error, "cause");
|
|
5576
|
+
const causeCode = readErrorCode(cause);
|
|
5577
|
+
const causeMessage = readErrorMessage(cause);
|
|
5578
|
+
const causeDetail = causeCode ?? causeMessage;
|
|
5579
|
+
if (causeDetail) detail = `${detail} (${causeDetail})`;
|
|
5580
|
+
}
|
|
5581
|
+
return detail;
|
|
5582
|
+
}
|
|
5583
|
+
function backoffForAttempt(index) {
|
|
5584
|
+
const base = BASE_BACKOFF_MS[Math.min(index, BASE_BACKOFF_MS.length - 1)] ?? 0;
|
|
5585
|
+
const spread = base * JITTER;
|
|
5586
|
+
return base + (Math.random() * 2 - 1) * spread;
|
|
5587
|
+
}
|
|
5588
|
+
function delay(ms) {
|
|
5589
|
+
return new Promise((resolve5) => {
|
|
5590
|
+
setTimeout(resolve5, ms);
|
|
5591
|
+
});
|
|
5592
|
+
}
|
|
5593
|
+
async function fetchWithRetry(url, options = {}) {
|
|
5594
|
+
const attempts = options.attempts ?? DEFAULT_ATTEMPTS;
|
|
5595
|
+
const perAttemptTimeoutMs = options.perAttemptTimeoutMs ?? DEFAULT_PER_ATTEMPT_TIMEOUT_MS;
|
|
5596
|
+
const maxTotalMs = options.maxTotalMs ?? DEFAULT_MAX_TOTAL_MS;
|
|
5597
|
+
const init = options.init;
|
|
5598
|
+
const start = Date.now();
|
|
5599
|
+
let lastReason;
|
|
5600
|
+
let made = 0;
|
|
5601
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
5602
|
+
const remaining = maxTotalMs - (Date.now() - start);
|
|
5603
|
+
if (remaining <= MIN_ATTEMPT_BUDGET_MS) break;
|
|
5604
|
+
const attemptTimeoutMs = Math.min(perAttemptTimeoutMs, remaining);
|
|
5605
|
+
made = attempt + 1;
|
|
5606
|
+
let reason;
|
|
5607
|
+
try {
|
|
5608
|
+
const controller = new AbortController();
|
|
5609
|
+
const timeoutId = setTimeout(() => controller.abort(), attemptTimeoutMs);
|
|
5610
|
+
let response;
|
|
5611
|
+
try {
|
|
5612
|
+
response = await fetch(url, {
|
|
5613
|
+
...init,
|
|
5614
|
+
signal: controller.signal
|
|
5615
|
+
});
|
|
5616
|
+
} finally {
|
|
5617
|
+
clearTimeout(timeoutId);
|
|
5618
|
+
}
|
|
5619
|
+
if (!isTransientStatus(response.status)) return response;
|
|
5620
|
+
await response.body?.cancel();
|
|
5621
|
+
reason = { kind: "status", status: response.status };
|
|
5622
|
+
} catch (error) {
|
|
5623
|
+
if (!isTransientThrow(error)) throw error;
|
|
5624
|
+
reason = { kind: "throw", error };
|
|
5625
|
+
}
|
|
5626
|
+
lastReason = reason;
|
|
5627
|
+
const isLastAttempt = attempt === attempts - 1;
|
|
5628
|
+
const budgetSpent = Date.now() - start >= maxTotalMs;
|
|
5629
|
+
if (isLastAttempt || budgetSpent) break;
|
|
5630
|
+
const sleepMs = Math.min(
|
|
5631
|
+
backoffForAttempt(attempt),
|
|
5632
|
+
maxTotalMs - (Date.now() - start)
|
|
5633
|
+
);
|
|
5634
|
+
if (sleepMs <= 0) break;
|
|
5635
|
+
await delay(sleepMs);
|
|
5636
|
+
}
|
|
5637
|
+
const cause = lastReason ? describeReason(lastReason) : "no attempts made";
|
|
5638
|
+
throw new Error(`Fetch failed after ${made} attempts: ${cause}`);
|
|
5639
|
+
}
|
|
5640
|
+
|
|
5641
|
+
// src/runtime/resolve-bundle.ts
|
|
5422
5642
|
var ARCHIVE_ENTRY = "flow.mjs";
|
|
5423
|
-
var FETCH_TIMEOUT_MS = 3e4;
|
|
5424
5643
|
function getDefaultWritePath() {
|
|
5425
5644
|
if (existsSync2("/app/flow")) return "/app/flow/flow.mjs";
|
|
5426
5645
|
return "/tmp/walkeros-flow.mjs";
|
|
@@ -5438,16 +5657,14 @@ function isArchive(value, contentType) {
|
|
|
5438
5657
|
return false;
|
|
5439
5658
|
}
|
|
5440
5659
|
function writeBundleToDisk(writePath, content) {
|
|
5441
|
-
const dir =
|
|
5660
|
+
const dir = dirname4(writePath);
|
|
5442
5661
|
if (!existsSync2(dir)) {
|
|
5443
5662
|
mkdirSync2(dir, { recursive: true });
|
|
5444
5663
|
}
|
|
5445
5664
|
writeFileSync2(writePath, content, "utf-8");
|
|
5446
5665
|
}
|
|
5447
5666
|
async function fetchOk(url) {
|
|
5448
|
-
const response = await
|
|
5449
|
-
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
5450
|
-
});
|
|
5667
|
+
const response = await fetchWithRetry(url);
|
|
5451
5668
|
if (!response.ok) {
|
|
5452
5669
|
throw new Error(
|
|
5453
5670
|
`Failed to fetch bundle from ${url}: ${response.status} ${response.statusText}`
|
|
@@ -5499,7 +5716,7 @@ async function readBundleFromStdin(writePath) {
|
|
|
5499
5716
|
}
|
|
5500
5717
|
async function resolveBundle(bundleEnv) {
|
|
5501
5718
|
const writePath = getDefaultWritePath();
|
|
5502
|
-
const archiveDestDir =
|
|
5719
|
+
const archiveDestDir = dirname4(writePath);
|
|
5503
5720
|
if (!isUrl2(bundleEnv) && existsSync2(bundleEnv)) {
|
|
5504
5721
|
if (isArchive(bundleEnv)) {
|
|
5505
5722
|
const path20 = await extractToDir(
|
|
@@ -5568,10 +5785,7 @@ async function fetchConfig(options) {
|
|
|
5568
5785
|
options.token,
|
|
5569
5786
|
options.lastEtag ? { "If-None-Match": options.lastEtag } : void 0
|
|
5570
5787
|
);
|
|
5571
|
-
const response = await
|
|
5572
|
-
headers,
|
|
5573
|
-
signal: AbortSignal.timeout(3e4)
|
|
5574
|
-
});
|
|
5788
|
+
const response = await fetchWithRetry(url, { init: { headers } });
|
|
5575
5789
|
if (response.status === 304) {
|
|
5576
5790
|
return { changed: false };
|
|
5577
5791
|
}
|
|
@@ -5592,301 +5806,151 @@ async function fetchConfig(options) {
|
|
|
5592
5806
|
};
|
|
5593
5807
|
}
|
|
5594
5808
|
|
|
5595
|
-
// src/
|
|
5809
|
+
// src/runtime/index.ts
|
|
5596
5810
|
init_cache();
|
|
5597
5811
|
|
|
5598
|
-
// src/
|
|
5599
|
-
|
|
5600
|
-
import { existsSync as existsSync4 } from "fs";
|
|
5601
|
-
|
|
5602
|
-
// src/schemas/primitives.ts
|
|
5603
|
-
import { z } from "@walkeros/core/dev";
|
|
5604
|
-
var PortSchema = z.number().int("Port must be an integer").min(1, "Port must be at least 1").max(65535, "Port must be at most 65535").describe("HTTP server port number");
|
|
5605
|
-
var FilePathSchema = z.string().min(1, "File path cannot be empty").describe("Path to configuration file");
|
|
5606
|
-
|
|
5607
|
-
// src/schemas/run.ts
|
|
5608
|
-
import { z as z2 } from "@walkeros/core/dev";
|
|
5609
|
-
var RunOptionsSchema = z2.object({
|
|
5610
|
-
flow: FilePathSchema,
|
|
5611
|
-
port: PortSchema.default(8080),
|
|
5612
|
-
flowName: z2.string().optional().describe("Specific flow name to run")
|
|
5613
|
-
});
|
|
5614
|
-
|
|
5615
|
-
// src/schemas/validate.ts
|
|
5616
|
-
import { z as z3 } from "@walkeros/core/dev";
|
|
5617
|
-
var ValidationTypeSchema = z3.enum(["contract", "event", "flow", "mapping"]).describe('Validation type: "event", "flow", "mapping", or "contract"');
|
|
5618
|
-
var ValidateOptionsSchema = z3.object({
|
|
5619
|
-
flow: z3.string().optional().describe("Flow name for multi-flow configs"),
|
|
5620
|
-
path: z3.string().optional().describe(
|
|
5621
|
-
'Entry path for package schema validation (e.g., "destinations.snowplow", "sources.browser")'
|
|
5622
|
-
)
|
|
5623
|
-
});
|
|
5624
|
-
var ValidateInputShape = {
|
|
5625
|
-
type: ValidationTypeSchema,
|
|
5626
|
-
input: z3.string().min(1).describe("JSON string, file path, or URL to validate"),
|
|
5627
|
-
flow: z3.string().optional().describe("Flow name for multi-flow configs"),
|
|
5628
|
-
path: z3.string().optional().describe(
|
|
5629
|
-
'Entry path for package schema validation (e.g., "destinations.snowplow"). When provided, validates the entry against its package JSON Schema instead of using --type.'
|
|
5630
|
-
)
|
|
5631
|
-
};
|
|
5632
|
-
var ValidateInputSchema = z3.object(ValidateInputShape);
|
|
5633
|
-
|
|
5634
|
-
// src/schemas/bundle.ts
|
|
5635
|
-
import { z as z4 } from "@walkeros/core/dev";
|
|
5636
|
-
var BundleOptionsSchema = z4.object({
|
|
5637
|
-
silent: z4.boolean().optional().describe("Suppress all output"),
|
|
5638
|
-
verbose: z4.boolean().optional().describe("Enable verbose logging"),
|
|
5639
|
-
stats: z4.boolean().optional().default(true).describe("Return bundle statistics"),
|
|
5640
|
-
cache: z4.boolean().optional().default(true).describe("Enable package caching"),
|
|
5641
|
-
flowName: z4.string().optional().describe("Flow name for multi-flow configs")
|
|
5642
|
-
});
|
|
5643
|
-
var BundleInputShape = {
|
|
5644
|
-
configPath: FilePathSchema.describe(
|
|
5645
|
-
"Path to flow configuration file (JSON or JavaScript), URL, or inline JSON string"
|
|
5646
|
-
),
|
|
5647
|
-
flow: z4.string().optional().describe("Flow name for multi-flow configs"),
|
|
5648
|
-
stats: z4.boolean().optional().default(true).describe("Return bundle statistics"),
|
|
5649
|
-
output: z4.string().optional().describe("Output file path (defaults to config-defined)")
|
|
5650
|
-
};
|
|
5651
|
-
var BundleInputSchema = z4.object(BundleInputShape);
|
|
5812
|
+
// src/runtime/heartbeat.ts
|
|
5813
|
+
import { randomBytes } from "crypto";
|
|
5652
5814
|
|
|
5653
|
-
// src/
|
|
5654
|
-
import {
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
)
|
|
5673
|
-
}
|
|
5674
|
-
var
|
|
5815
|
+
// src/version.ts
|
|
5816
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
5817
|
+
import { fileURLToPath } from "url";
|
|
5818
|
+
import { dirname as dirname5, join as join6 } from "path";
|
|
5819
|
+
var versionFilename = fileURLToPath(import.meta.url);
|
|
5820
|
+
var versionDirname = dirname5(versionFilename);
|
|
5821
|
+
function findPackageJson() {
|
|
5822
|
+
const paths = [
|
|
5823
|
+
join6(versionDirname, "../package.json"),
|
|
5824
|
+
// dist/ or src/
|
|
5825
|
+
join6(versionDirname, "../../package.json")
|
|
5826
|
+
// src/core/ (not used, but safe)
|
|
5827
|
+
];
|
|
5828
|
+
for (const p of paths) {
|
|
5829
|
+
try {
|
|
5830
|
+
return readFileSync3(p, "utf-8");
|
|
5831
|
+
} catch {
|
|
5832
|
+
}
|
|
5833
|
+
}
|
|
5834
|
+
return JSON.stringify({ version: "0.0.0" });
|
|
5835
|
+
}
|
|
5836
|
+
var VERSION = JSON.parse(findPackageJson()).version;
|
|
5675
5837
|
|
|
5676
|
-
// src/
|
|
5677
|
-
|
|
5678
|
-
var PushOptionsSchema = z6.object({
|
|
5679
|
-
silent: z6.boolean().optional().describe("Suppress all output"),
|
|
5680
|
-
verbose: z6.boolean().optional().describe("Enable verbose logging"),
|
|
5681
|
-
json: z6.boolean().optional().describe("Format output as JSON")
|
|
5682
|
-
});
|
|
5683
|
-
var PushInputShape = {
|
|
5684
|
-
configPath: FilePathSchema.describe("Path to flow configuration file"),
|
|
5685
|
-
event: z6.string().min(1).describe("Event as JSON string, file path, or URL"),
|
|
5686
|
-
flow: z6.string().optional().describe("Flow name for multi-flow configs"),
|
|
5687
|
-
platform: PlatformSchema.optional().describe("Override platform detection")
|
|
5688
|
-
};
|
|
5689
|
-
var PushInputSchema = z6.object(PushInputShape);
|
|
5838
|
+
// src/runtime/heartbeat.ts
|
|
5839
|
+
init_http();
|
|
5690
5840
|
|
|
5691
|
-
// src/
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5841
|
+
// src/runtime/redact.ts
|
|
5842
|
+
var MAX_LENGTH = 256;
|
|
5843
|
+
var MIN_TOKEN_LEN = 20;
|
|
5844
|
+
var ENTROPY_THRESHOLD = 4;
|
|
5845
|
+
var FORCE_MASK_PREFIXES = [
|
|
5846
|
+
"sk-",
|
|
5847
|
+
"sk_",
|
|
5848
|
+
"pk_",
|
|
5849
|
+
"ghp_",
|
|
5850
|
+
"gho_",
|
|
5851
|
+
"xoxb-",
|
|
5852
|
+
"xoxp-",
|
|
5853
|
+
"AKIA"
|
|
5854
|
+
];
|
|
5855
|
+
var RE_URL_CREDS = /(:\/\/[^/:@\s]+:)[^@\s]+(@)/g;
|
|
5856
|
+
var RE_JSON_PRIVATE_KEY = /("private_key"\s*:\s*)"[^"]*"/g;
|
|
5857
|
+
var RE_PEM_BEGIN = /^\s*-----BEGIN [A-Z ]*PRIVATE KEY-----/i;
|
|
5858
|
+
var RE_PEM_END = /-----END[A-Z -]*-----/i;
|
|
5859
|
+
var TOKEN_VALUE_CLASS = "[A-Za-z0-9+/._-]";
|
|
5860
|
+
var RE_KV_SECRET = new RegExp(
|
|
5861
|
+
`(^|\\s)([A-Za-z_][A-Za-z0-9_.]{0,63}\\s*[=:]\\s*)(${TOKEN_VALUE_CLASS}{12,}={0,2})`,
|
|
5862
|
+
"g"
|
|
5863
|
+
);
|
|
5864
|
+
var RE_TOKEN_RUN = new RegExp(`${TOKEN_VALUE_CLASS}{${MIN_TOKEN_LEN},}`, "g");
|
|
5865
|
+
function escapeRegex(s) {
|
|
5866
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5867
|
+
}
|
|
5868
|
+
var PREFIX_ALTERNATION = [...FORCE_MASK_PREFIXES].sort((a, b) => b.length - a.length).map(escapeRegex).join("|");
|
|
5869
|
+
var RE_PREFIXED_TOKEN = new RegExp(
|
|
5870
|
+
`(?:${PREFIX_ALTERNATION})${TOKEN_VALUE_CLASS}*`,
|
|
5871
|
+
"g"
|
|
5872
|
+
);
|
|
5873
|
+
function shannonEntropy(s) {
|
|
5874
|
+
if (s.length === 0) return 0;
|
|
5875
|
+
const counts = /* @__PURE__ */ new Map();
|
|
5876
|
+
for (const ch of s) counts.set(ch, (counts.get(ch) ?? 0) + 1);
|
|
5877
|
+
let entropy = 0;
|
|
5878
|
+
for (const count of counts.values()) {
|
|
5879
|
+
const p = count / s.length;
|
|
5880
|
+
entropy -= p * Math.log2(p);
|
|
5881
|
+
}
|
|
5882
|
+
return entropy;
|
|
5883
|
+
}
|
|
5884
|
+
var RE_ALL_HEX = /^[0-9a-fA-F]+$/;
|
|
5885
|
+
var RE_ALL_DIGIT = /^[0-9]+$/;
|
|
5886
|
+
var RE_BASE64_SPECIAL = /[+/=]/;
|
|
5887
|
+
var RE_HAS_DIGIT = /[0-9]/;
|
|
5888
|
+
var RE_HAS_LETTER = /[A-Za-z]/;
|
|
5889
|
+
function shouldMaskToken(run2) {
|
|
5890
|
+
if (RE_ALL_HEX.test(run2)) return true;
|
|
5891
|
+
if (RE_ALL_DIGIT.test(run2)) return true;
|
|
5892
|
+
if (RE_BASE64_SPECIAL.test(run2)) return true;
|
|
5893
|
+
if (RE_HAS_DIGIT.test(run2) && RE_HAS_LETTER.test(run2)) return true;
|
|
5894
|
+
if (shannonEntropy(run2) >= ENTROPY_THRESHOLD) return true;
|
|
5895
|
+
return false;
|
|
5702
5896
|
}
|
|
5703
|
-
function
|
|
5704
|
-
const
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5897
|
+
function removePemBlocks(lines) {
|
|
5898
|
+
const out = [];
|
|
5899
|
+
let inBlock = false;
|
|
5900
|
+
for (const line of lines) {
|
|
5901
|
+
if (!inBlock) {
|
|
5902
|
+
if (RE_PEM_BEGIN.test(line)) {
|
|
5903
|
+
inBlock = true;
|
|
5904
|
+
continue;
|
|
5905
|
+
}
|
|
5906
|
+
out.push(line);
|
|
5907
|
+
} else {
|
|
5908
|
+
if (RE_PEM_END.test(line)) {
|
|
5909
|
+
inBlock = false;
|
|
5910
|
+
}
|
|
5911
|
+
}
|
|
5711
5912
|
}
|
|
5913
|
+
return out;
|
|
5712
5914
|
}
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
import {
|
|
5722
|
-
createBatchedPoster,
|
|
5723
|
-
createTelemetryObserver,
|
|
5724
|
-
getTraceUntil,
|
|
5725
|
-
resolveTelemetryOptions
|
|
5726
|
-
} from "@walkeros/core";
|
|
5727
|
-
|
|
5728
|
-
// src/runtime/health-server.ts
|
|
5729
|
-
import http from "http";
|
|
5730
|
-
function createHealthServer(port, logger) {
|
|
5731
|
-
return new Promise((resolve5, reject) => {
|
|
5732
|
-
let flowHandler = null;
|
|
5733
|
-
let ready = false;
|
|
5734
|
-
let failureReason = null;
|
|
5735
|
-
const server = http.createServer((req, res) => {
|
|
5736
|
-
if (req.url === "/health" && req.method === "GET") {
|
|
5737
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
5738
|
-
res.end(JSON.stringify({ status: "ok" }));
|
|
5739
|
-
return;
|
|
5740
|
-
}
|
|
5741
|
-
if (req.url === "/ready" && req.method === "GET") {
|
|
5742
|
-
const code = ready ? 200 : 503;
|
|
5743
|
-
const status = ready ? "ready" : failureReason ? "failed" : "not_ready";
|
|
5744
|
-
res.writeHead(code, { "Content-Type": "application/json" });
|
|
5745
|
-
res.end(
|
|
5746
|
-
JSON.stringify(
|
|
5747
|
-
failureReason && !ready ? { status, reason: failureReason } : { status }
|
|
5748
|
-
)
|
|
5749
|
-
);
|
|
5750
|
-
return;
|
|
5751
|
-
}
|
|
5752
|
-
if (flowHandler) {
|
|
5753
|
-
flowHandler(req, res);
|
|
5754
|
-
return;
|
|
5755
|
-
}
|
|
5756
|
-
res.writeHead(503, { "Content-Type": "application/json" });
|
|
5757
|
-
res.end(JSON.stringify({ error: "No flow loaded" }));
|
|
5758
|
-
});
|
|
5759
|
-
server.keepAliveTimeout = 5e3;
|
|
5760
|
-
server.headersTimeout = 1e4;
|
|
5761
|
-
server.listen(port, "0.0.0.0", () => {
|
|
5762
|
-
logger.info(`Health server listening on port ${port}`);
|
|
5763
|
-
resolve5({
|
|
5764
|
-
server,
|
|
5765
|
-
setFlowHandler(handler) {
|
|
5766
|
-
flowHandler = handler;
|
|
5767
|
-
},
|
|
5768
|
-
setReady(value) {
|
|
5769
|
-
ready = value;
|
|
5770
|
-
if (value) failureReason = null;
|
|
5771
|
-
},
|
|
5772
|
-
setFailed(reason) {
|
|
5773
|
-
ready = false;
|
|
5774
|
-
failureReason = reason;
|
|
5775
|
-
},
|
|
5776
|
-
close: () => new Promise((res, rej) => {
|
|
5777
|
-
server.close((err) => err ? rej(err) : res());
|
|
5778
|
-
})
|
|
5779
|
-
});
|
|
5780
|
-
});
|
|
5781
|
-
server.on("error", reject);
|
|
5782
|
-
});
|
|
5783
|
-
}
|
|
5784
|
-
|
|
5785
|
-
// src/runtime/runner.ts
|
|
5786
|
-
import { resolve as resolve3, dirname as dirname4 } from "path";
|
|
5787
|
-
|
|
5788
|
-
// src/runtime/load-bundle.ts
|
|
5789
|
-
import { resolve as resolve2 } from "path";
|
|
5790
|
-
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
5791
|
-
async function loadBundle(file, context2, logger) {
|
|
5792
|
-
const absolutePath = resolve2(file);
|
|
5793
|
-
const fileUrl = pathToFileURL2(absolutePath).href;
|
|
5794
|
-
logger?.debug?.(`Importing bundle: ${absolutePath}`);
|
|
5795
|
-
const module = await import(`${fileUrl}?t=${Date.now()}`);
|
|
5796
|
-
if (!module.default || typeof module.default !== "function") {
|
|
5797
|
-
throw new Error(
|
|
5798
|
-
`Invalid bundle: ${file} must export a default factory function`
|
|
5799
|
-
);
|
|
5800
|
-
}
|
|
5801
|
-
logger?.debug?.("Calling factory function...");
|
|
5802
|
-
const result = await module.default(context2 ?? {});
|
|
5803
|
-
if (!result || !result.collector || typeof result.collector.push !== "function") {
|
|
5804
|
-
throw new Error(
|
|
5805
|
-
`Invalid bundle: factory must return { collector } with a push function`
|
|
5806
|
-
);
|
|
5807
|
-
}
|
|
5808
|
-
return {
|
|
5809
|
-
collector: result.collector,
|
|
5810
|
-
...typeof result.httpHandler === "function" ? { httpHandler: result.httpHandler } : {}
|
|
5811
|
-
};
|
|
5812
|
-
}
|
|
5813
|
-
|
|
5814
|
-
// src/runtime/runner.ts
|
|
5815
|
-
async function loadFlow(file, config, logger, loggerConfig, healthServer, observers) {
|
|
5816
|
-
const absolutePath = resolve3(file);
|
|
5817
|
-
const flowDir = dirname4(absolutePath);
|
|
5818
|
-
process.chdir(flowDir);
|
|
5819
|
-
const flowContext = {
|
|
5820
|
-
...config,
|
|
5821
|
-
...loggerConfig ? { logger: loggerConfig } : {},
|
|
5822
|
-
...healthServer ? { sourceSettings: { port: void 0 } } : {},
|
|
5823
|
-
...observers ? { observers } : {}
|
|
5824
|
-
};
|
|
5825
|
-
const result = await loadBundle(absolutePath, flowContext, logger);
|
|
5826
|
-
if (healthServer && typeof result.httpHandler === "function") {
|
|
5827
|
-
healthServer.setFlowHandler(result.httpHandler);
|
|
5828
|
-
}
|
|
5829
|
-
return {
|
|
5830
|
-
collector: {
|
|
5831
|
-
command: result.collector.command,
|
|
5832
|
-
status: result.collector.status
|
|
5833
|
-
},
|
|
5834
|
-
file,
|
|
5835
|
-
httpHandler: result.httpHandler
|
|
5836
|
-
};
|
|
5837
|
-
}
|
|
5838
|
-
async function swapFlow(currentHandle, newFile, config, logger, loggerConfig, healthServer, observers) {
|
|
5839
|
-
logger.info("Shutting down current flow for hot-swap...");
|
|
5840
|
-
if (healthServer) {
|
|
5841
|
-
healthServer.setFlowHandler(null);
|
|
5842
|
-
}
|
|
5843
|
-
try {
|
|
5844
|
-
if (currentHandle.collector.command) {
|
|
5845
|
-
await currentHandle.collector.command("shutdown");
|
|
5846
|
-
}
|
|
5847
|
-
} catch (error) {
|
|
5848
|
-
logger.debug(`Shutdown warning: ${error}`);
|
|
5849
|
-
}
|
|
5850
|
-
const newHandle = await loadFlow(
|
|
5851
|
-
newFile,
|
|
5852
|
-
config,
|
|
5853
|
-
logger,
|
|
5854
|
-
loggerConfig,
|
|
5855
|
-
healthServer,
|
|
5856
|
-
observers
|
|
5915
|
+
function maskLine(line) {
|
|
5916
|
+
let s = line;
|
|
5917
|
+
s = s.replace(RE_URL_CREDS, "$1***$2");
|
|
5918
|
+
s = s.replace(RE_KV_SECRET, "$1$2***");
|
|
5919
|
+
s = s.replace(RE_PREFIXED_TOKEN, "***");
|
|
5920
|
+
s = s.replace(
|
|
5921
|
+
RE_TOKEN_RUN,
|
|
5922
|
+
(match) => shouldMaskToken(match) ? "***" : match
|
|
5857
5923
|
);
|
|
5858
|
-
|
|
5859
|
-
|
|
5924
|
+
return s;
|
|
5925
|
+
}
|
|
5926
|
+
function redactLine(line) {
|
|
5927
|
+
const withoutJsonKey = line.replace(RE_JSON_PRIVATE_KEY, '$1"***"');
|
|
5928
|
+
const rawLines = withoutJsonKey.split("\n");
|
|
5929
|
+
const cleanLines = removePemBlocks(rawLines);
|
|
5930
|
+
const maskedLines = cleanLines.map(maskLine);
|
|
5931
|
+
const joined = maskedLines.join("\n");
|
|
5932
|
+
if (joined.length > MAX_LENGTH) {
|
|
5933
|
+
return joined.slice(0, MAX_LENGTH - 1) + "\u2026";
|
|
5934
|
+
}
|
|
5935
|
+
return joined;
|
|
5936
|
+
}
|
|
5937
|
+
function redactErrors(errors) {
|
|
5938
|
+
return errors.map((e) => ({
|
|
5939
|
+
message: redactLine(e.message),
|
|
5940
|
+
count: e.count,
|
|
5941
|
+
firstSeen: new Date(e.firstSeen).toISOString(),
|
|
5942
|
+
lastSeen: new Date(e.lastSeen).toISOString()
|
|
5943
|
+
}));
|
|
5860
5944
|
}
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
import { fileURLToPath } from "url";
|
|
5868
|
-
import { dirname as dirname5, join as join6 } from "path";
|
|
5869
|
-
var versionFilename = fileURLToPath(import.meta.url);
|
|
5870
|
-
var versionDirname = dirname5(versionFilename);
|
|
5871
|
-
function findPackageJson() {
|
|
5872
|
-
const paths = [
|
|
5873
|
-
join6(versionDirname, "../package.json"),
|
|
5874
|
-
// dist/ or src/
|
|
5875
|
-
join6(versionDirname, "../../package.json")
|
|
5876
|
-
// src/core/ (not used, but safe)
|
|
5877
|
-
];
|
|
5878
|
-
for (const p of paths) {
|
|
5879
|
-
try {
|
|
5880
|
-
return readFileSync3(p, "utf-8");
|
|
5881
|
-
} catch {
|
|
5882
|
-
}
|
|
5883
|
-
}
|
|
5884
|
-
return JSON.stringify({ version: "0.0.0" });
|
|
5945
|
+
function redactLogs(entries) {
|
|
5946
|
+
return entries.map((e) => ({
|
|
5947
|
+
time: new Date(e.time).toISOString(),
|
|
5948
|
+
level: e.level,
|
|
5949
|
+
message: redactLine(e.message)
|
|
5950
|
+
}));
|
|
5885
5951
|
}
|
|
5886
|
-
var VERSION = JSON.parse(findPackageJson()).version;
|
|
5887
5952
|
|
|
5888
5953
|
// src/runtime/heartbeat.ts
|
|
5889
|
-
init_http();
|
|
5890
5954
|
function computeCounterDelta(current, last) {
|
|
5891
5955
|
const destinations = {};
|
|
5892
5956
|
for (const [name, dest] of Object.entries(current.destinations)) {
|
|
@@ -5947,6 +6011,8 @@ function createHeartbeat(config, logger) {
|
|
|
5947
6011
|
};
|
|
5948
6012
|
counters = computeCounterDelta(current, lastReported);
|
|
5949
6013
|
}
|
|
6014
|
+
const errors = config.getErrors ? redactErrors(config.getErrors()) : [];
|
|
6015
|
+
const logs = config.getLogs ? redactLogs(config.getLogs().slice(-50)) : [];
|
|
5950
6016
|
const response = await fetch(
|
|
5951
6017
|
`${config.appUrl}/api/projects/${config.projectId}/runners/heartbeat`,
|
|
5952
6018
|
{
|
|
@@ -5963,7 +6029,9 @@ function createHeartbeat(config, logger) {
|
|
|
5963
6029
|
configVersion,
|
|
5964
6030
|
cliVersion: VERSION,
|
|
5965
6031
|
uptime: Math.floor((Date.now() - startTime) / 1e3),
|
|
5966
|
-
...counters && { counters }
|
|
6032
|
+
...counters && { counters },
|
|
6033
|
+
...errors.length && { recentErrors: errors },
|
|
6034
|
+
...logs.length && { recentLogs: logs }
|
|
5967
6035
|
}),
|
|
5968
6036
|
signal: AbortSignal.timeout(1e4)
|
|
5969
6037
|
}
|
|
@@ -6038,6 +6106,275 @@ function createPoller(config, logger) {
|
|
|
6038
6106
|
return { start, stop, pollOnce };
|
|
6039
6107
|
}
|
|
6040
6108
|
|
|
6109
|
+
// src/runtime/health-server.ts
|
|
6110
|
+
import http from "http";
|
|
6111
|
+
function createHealthServer(port, logger) {
|
|
6112
|
+
return new Promise((resolve5, reject) => {
|
|
6113
|
+
let flowHandler = null;
|
|
6114
|
+
let ready = false;
|
|
6115
|
+
let failureReason = null;
|
|
6116
|
+
const server = http.createServer((req, res) => {
|
|
6117
|
+
if (req.url === "/health" && req.method === "GET") {
|
|
6118
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6119
|
+
res.end(JSON.stringify({ status: "ok" }));
|
|
6120
|
+
return;
|
|
6121
|
+
}
|
|
6122
|
+
if (req.url === "/ready" && req.method === "GET") {
|
|
6123
|
+
const code = ready ? 200 : 503;
|
|
6124
|
+
const status = ready ? "ready" : failureReason ? "failed" : "not_ready";
|
|
6125
|
+
res.writeHead(code, { "Content-Type": "application/json" });
|
|
6126
|
+
res.end(
|
|
6127
|
+
JSON.stringify(
|
|
6128
|
+
failureReason && !ready ? { status, reason: failureReason } : { status }
|
|
6129
|
+
)
|
|
6130
|
+
);
|
|
6131
|
+
return;
|
|
6132
|
+
}
|
|
6133
|
+
if (flowHandler) {
|
|
6134
|
+
flowHandler(req, res);
|
|
6135
|
+
return;
|
|
6136
|
+
}
|
|
6137
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
6138
|
+
res.end(JSON.stringify({ error: "No flow loaded" }));
|
|
6139
|
+
});
|
|
6140
|
+
server.keepAliveTimeout = 5e3;
|
|
6141
|
+
server.headersTimeout = 1e4;
|
|
6142
|
+
server.listen(port, "0.0.0.0", () => {
|
|
6143
|
+
logger.info(`Health server listening on port ${port}`);
|
|
6144
|
+
resolve5({
|
|
6145
|
+
server,
|
|
6146
|
+
setFlowHandler(handler) {
|
|
6147
|
+
flowHandler = handler;
|
|
6148
|
+
},
|
|
6149
|
+
setReady(value) {
|
|
6150
|
+
ready = value;
|
|
6151
|
+
if (value) failureReason = null;
|
|
6152
|
+
},
|
|
6153
|
+
setFailed(reason) {
|
|
6154
|
+
ready = false;
|
|
6155
|
+
failureReason = reason;
|
|
6156
|
+
},
|
|
6157
|
+
close: () => new Promise((res, rej) => {
|
|
6158
|
+
server.close((err) => err ? rej(err) : res());
|
|
6159
|
+
})
|
|
6160
|
+
});
|
|
6161
|
+
});
|
|
6162
|
+
server.on("error", reject);
|
|
6163
|
+
});
|
|
6164
|
+
}
|
|
6165
|
+
|
|
6166
|
+
// src/runtime/secrets-fetcher.ts
|
|
6167
|
+
init_http();
|
|
6168
|
+
var SecretsHttpError = class extends Error {
|
|
6169
|
+
constructor(status, statusText) {
|
|
6170
|
+
super(`Failed to fetch secrets: ${status} ${statusText}`);
|
|
6171
|
+
this.status = status;
|
|
6172
|
+
this.name = "SecretsHttpError";
|
|
6173
|
+
}
|
|
6174
|
+
status;
|
|
6175
|
+
};
|
|
6176
|
+
async function fetchSecrets(options) {
|
|
6177
|
+
const { appUrl, token, projectId, flowId } = options;
|
|
6178
|
+
const url = `${appUrl}/api/projects/${encodeURIComponent(projectId)}/flows/${encodeURIComponent(flowId)}/secrets/values`;
|
|
6179
|
+
const res = await fetchWithRetry(url, {
|
|
6180
|
+
maxTotalMs: 2e4,
|
|
6181
|
+
init: {
|
|
6182
|
+
headers: mergeAuthHeaders(token, { "Content-Type": "application/json" })
|
|
6183
|
+
}
|
|
6184
|
+
});
|
|
6185
|
+
await throwIfRunnerAuthFailure(res);
|
|
6186
|
+
if (!res.ok) {
|
|
6187
|
+
throw new SecretsHttpError(res.status, res.statusText);
|
|
6188
|
+
}
|
|
6189
|
+
const json = await res.json();
|
|
6190
|
+
return json.values;
|
|
6191
|
+
}
|
|
6192
|
+
|
|
6193
|
+
// src/runtime/log-ring.ts
|
|
6194
|
+
var LogRing = class {
|
|
6195
|
+
constructor(max) {
|
|
6196
|
+
this.max = max;
|
|
6197
|
+
}
|
|
6198
|
+
max;
|
|
6199
|
+
entries = [];
|
|
6200
|
+
add(entry) {
|
|
6201
|
+
this.entries.push(entry);
|
|
6202
|
+
if (this.entries.length > this.max) this.entries.shift();
|
|
6203
|
+
}
|
|
6204
|
+
snapshot(limit = this.max) {
|
|
6205
|
+
return this.entries.slice(Math.max(0, this.entries.length - limit));
|
|
6206
|
+
}
|
|
6207
|
+
};
|
|
6208
|
+
var ErrorRing = class {
|
|
6209
|
+
constructor(maxUnique, now = () => Date.now()) {
|
|
6210
|
+
this.maxUnique = maxUnique;
|
|
6211
|
+
this.now = now;
|
|
6212
|
+
}
|
|
6213
|
+
maxUnique;
|
|
6214
|
+
now;
|
|
6215
|
+
map = /* @__PURE__ */ new Map();
|
|
6216
|
+
add(message) {
|
|
6217
|
+
const ts = this.now();
|
|
6218
|
+
const existing = this.map.get(message);
|
|
6219
|
+
if (existing) {
|
|
6220
|
+
existing.count += 1;
|
|
6221
|
+
existing.lastSeen = ts;
|
|
6222
|
+
return;
|
|
6223
|
+
}
|
|
6224
|
+
if (this.map.size >= this.maxUnique) {
|
|
6225
|
+
let oldestKey;
|
|
6226
|
+
let oldest = Infinity;
|
|
6227
|
+
for (const [k, v] of this.map) {
|
|
6228
|
+
if (v.lastSeen < oldest) {
|
|
6229
|
+
oldest = v.lastSeen;
|
|
6230
|
+
oldestKey = k;
|
|
6231
|
+
}
|
|
6232
|
+
}
|
|
6233
|
+
if (oldestKey !== void 0) this.map.delete(oldestKey);
|
|
6234
|
+
}
|
|
6235
|
+
this.map.set(message, { message, count: 1, firstSeen: ts, lastSeen: ts });
|
|
6236
|
+
}
|
|
6237
|
+
snapshot() {
|
|
6238
|
+
return [...this.map.values()].sort((a, b) => b.lastSeen - a.lastSeen);
|
|
6239
|
+
}
|
|
6240
|
+
};
|
|
6241
|
+
|
|
6242
|
+
// src/commands/run/index.ts
|
|
6243
|
+
init_tmp();
|
|
6244
|
+
init_config_file();
|
|
6245
|
+
init_auth();
|
|
6246
|
+
init_cache();
|
|
6247
|
+
|
|
6248
|
+
// src/commands/run/validators.ts
|
|
6249
|
+
init_asset_resolver();
|
|
6250
|
+
import { existsSync as existsSync4 } from "fs";
|
|
6251
|
+
|
|
6252
|
+
// src/schemas/primitives.ts
|
|
6253
|
+
import { z } from "@walkeros/core/dev";
|
|
6254
|
+
var PortSchema = z.number().int("Port must be an integer").min(1, "Port must be at least 1").max(65535, "Port must be at most 65535").describe("HTTP server port number");
|
|
6255
|
+
var FilePathSchema = z.string().min(1, "File path cannot be empty").describe("Path to configuration file");
|
|
6256
|
+
|
|
6257
|
+
// src/schemas/run.ts
|
|
6258
|
+
import { z as z2 } from "@walkeros/core/dev";
|
|
6259
|
+
var RunOptionsSchema = z2.object({
|
|
6260
|
+
flow: FilePathSchema,
|
|
6261
|
+
port: PortSchema.default(8080),
|
|
6262
|
+
flowName: z2.string().optional().describe("Specific flow name to run")
|
|
6263
|
+
});
|
|
6264
|
+
|
|
6265
|
+
// src/schemas/validate.ts
|
|
6266
|
+
import { z as z3 } from "@walkeros/core/dev";
|
|
6267
|
+
var ValidationTypeSchema = z3.enum(["contract", "event", "flow", "mapping"]).describe('Validation type: "event", "flow", "mapping", or "contract"');
|
|
6268
|
+
var ValidateOptionsSchema = z3.object({
|
|
6269
|
+
flow: z3.string().optional().describe("Flow name for multi-flow configs"),
|
|
6270
|
+
path: z3.string().optional().describe(
|
|
6271
|
+
'Entry path for package schema validation (e.g., "destinations.snowplow", "sources.browser")'
|
|
6272
|
+
)
|
|
6273
|
+
});
|
|
6274
|
+
var ValidateInputShape = {
|
|
6275
|
+
type: ValidationTypeSchema,
|
|
6276
|
+
input: z3.string().min(1).describe("JSON string, file path, or URL to validate"),
|
|
6277
|
+
flow: z3.string().optional().describe("Flow name for multi-flow configs"),
|
|
6278
|
+
path: z3.string().optional().describe(
|
|
6279
|
+
'Entry path for package schema validation (e.g., "destinations.snowplow"). When provided, validates the entry against its package JSON Schema instead of using --type.'
|
|
6280
|
+
)
|
|
6281
|
+
};
|
|
6282
|
+
var ValidateInputSchema = z3.object(ValidateInputShape);
|
|
6283
|
+
|
|
6284
|
+
// src/schemas/bundle.ts
|
|
6285
|
+
import { z as z4 } from "@walkeros/core/dev";
|
|
6286
|
+
var BundleOptionsSchema = z4.object({
|
|
6287
|
+
silent: z4.boolean().optional().describe("Suppress all output"),
|
|
6288
|
+
verbose: z4.boolean().optional().describe("Enable verbose logging"),
|
|
6289
|
+
stats: z4.boolean().optional().default(true).describe("Return bundle statistics"),
|
|
6290
|
+
cache: z4.boolean().optional().default(true).describe("Enable package caching"),
|
|
6291
|
+
flowName: z4.string().optional().describe("Flow name for multi-flow configs")
|
|
6292
|
+
});
|
|
6293
|
+
var BundleInputShape = {
|
|
6294
|
+
configPath: FilePathSchema.describe(
|
|
6295
|
+
"Path to flow configuration file (JSON or JavaScript), URL, or inline JSON string"
|
|
6296
|
+
),
|
|
6297
|
+
flow: z4.string().optional().describe("Flow name for multi-flow configs"),
|
|
6298
|
+
stats: z4.boolean().optional().default(true).describe("Return bundle statistics"),
|
|
6299
|
+
output: z4.string().optional().describe("Output file path (defaults to config-defined)")
|
|
6300
|
+
};
|
|
6301
|
+
var BundleInputSchema = z4.object(BundleInputShape);
|
|
6302
|
+
|
|
6303
|
+
// src/schemas/simulate.ts
|
|
6304
|
+
import { z as z5 } from "@walkeros/core/dev";
|
|
6305
|
+
var PlatformSchema = z5.enum(["web", "server"]).describe("Platform type for event processing");
|
|
6306
|
+
var SimulateOptionsSchema = z5.object({
|
|
6307
|
+
silent: z5.boolean().optional().describe("Suppress all output"),
|
|
6308
|
+
verbose: z5.boolean().optional().describe("Enable verbose logging"),
|
|
6309
|
+
json: z5.boolean().optional().describe("Format output as JSON")
|
|
6310
|
+
});
|
|
6311
|
+
var SimulateInputShape = {
|
|
6312
|
+
configPath: FilePathSchema.describe(
|
|
6313
|
+
"Path to flow configuration file, URL, or inline JSON string"
|
|
6314
|
+
),
|
|
6315
|
+
event: z5.string().min(1).optional().describe(
|
|
6316
|
+
"Event as JSON string, file path, or URL. For sources: { content, trigger?, env? }."
|
|
6317
|
+
),
|
|
6318
|
+
flow: z5.string().optional().describe("Flow name for multi-flow configs"),
|
|
6319
|
+
platform: PlatformSchema.optional().describe("Override platform detection"),
|
|
6320
|
+
step: z5.string().optional().describe(
|
|
6321
|
+
'Step target in type.name format (e.g. "source.browser", "destination.gtag")'
|
|
6322
|
+
)
|
|
6323
|
+
};
|
|
6324
|
+
var SimulateInputSchema = z5.object(SimulateInputShape);
|
|
6325
|
+
|
|
6326
|
+
// src/schemas/push.ts
|
|
6327
|
+
import { z as z6 } from "@walkeros/core/dev";
|
|
6328
|
+
var PushOptionsSchema = z6.object({
|
|
6329
|
+
silent: z6.boolean().optional().describe("Suppress all output"),
|
|
6330
|
+
verbose: z6.boolean().optional().describe("Enable verbose logging"),
|
|
6331
|
+
json: z6.boolean().optional().describe("Format output as JSON")
|
|
6332
|
+
});
|
|
6333
|
+
var PushInputShape = {
|
|
6334
|
+
configPath: FilePathSchema.describe("Path to flow configuration file"),
|
|
6335
|
+
event: z6.string().min(1).describe("Event as JSON string, file path, or URL"),
|
|
6336
|
+
flow: z6.string().optional().describe("Flow name for multi-flow configs"),
|
|
6337
|
+
platform: PlatformSchema.optional().describe("Override platform detection")
|
|
6338
|
+
};
|
|
6339
|
+
var PushInputSchema = z6.object(PushInputShape);
|
|
6340
|
+
|
|
6341
|
+
// src/commands/run/validators.ts
|
|
6342
|
+
function validateFlowFile(filePath) {
|
|
6343
|
+
const absolutePath = resolveAsset(filePath, "bundle");
|
|
6344
|
+
if (!existsSync4(absolutePath)) {
|
|
6345
|
+
throw new Error(
|
|
6346
|
+
`Flow file not found: ${filePath}
|
|
6347
|
+
Resolved path: ${absolutePath}
|
|
6348
|
+
Make sure the file exists and the path is correct`
|
|
6349
|
+
);
|
|
6350
|
+
}
|
|
6351
|
+
return absolutePath;
|
|
6352
|
+
}
|
|
6353
|
+
function validatePort(port) {
|
|
6354
|
+
const result = PortSchema.safeParse(port);
|
|
6355
|
+
if (!result.success) {
|
|
6356
|
+
throw new Error(
|
|
6357
|
+
`Invalid port: ${port}
|
|
6358
|
+
Port must be an integer between 1 and 65535
|
|
6359
|
+
Example: --port 8080`
|
|
6360
|
+
);
|
|
6361
|
+
}
|
|
6362
|
+
}
|
|
6363
|
+
|
|
6364
|
+
// src/commands/run/index.ts
|
|
6365
|
+
init_utils3();
|
|
6366
|
+
|
|
6367
|
+
// src/commands/run/pipeline.ts
|
|
6368
|
+
init_tmp();
|
|
6369
|
+
import { writeFileSync as writeFileSync4 } from "fs";
|
|
6370
|
+
import fs17 from "fs-extra";
|
|
6371
|
+
import {
|
|
6372
|
+
createBatchedPoster,
|
|
6373
|
+
createTelemetryObserver,
|
|
6374
|
+
getTraceUntil,
|
|
6375
|
+
resolveTelemetryOptions
|
|
6376
|
+
} from "@walkeros/core";
|
|
6377
|
+
|
|
6041
6378
|
// src/runtime/trace-poller.ts
|
|
6042
6379
|
init_http();
|
|
6043
6380
|
import { setTraceUntil } from "@walkeros/core";
|
|
@@ -6100,42 +6437,26 @@ function createTracePoller(config, logger) {
|
|
|
6100
6437
|
}
|
|
6101
6438
|
var defaultFetch = (url, init) => fetch(url, init);
|
|
6102
6439
|
|
|
6103
|
-
// src/runtime/secrets-fetcher.ts
|
|
6104
|
-
init_http();
|
|
6105
|
-
var SecretsHttpError = class extends Error {
|
|
6106
|
-
constructor(status, statusText) {
|
|
6107
|
-
super(`Failed to fetch secrets: ${status} ${statusText}`);
|
|
6108
|
-
this.status = status;
|
|
6109
|
-
this.name = "SecretsHttpError";
|
|
6110
|
-
}
|
|
6111
|
-
status;
|
|
6112
|
-
};
|
|
6113
|
-
async function fetchSecrets(options) {
|
|
6114
|
-
const { appUrl, token, projectId, flowId } = options;
|
|
6115
|
-
const url = `${appUrl}/api/projects/${encodeURIComponent(projectId)}/flows/${encodeURIComponent(flowId)}/secrets/values`;
|
|
6116
|
-
const res = await fetch(url, {
|
|
6117
|
-
headers: mergeAuthHeaders(token, { "Content-Type": "application/json" })
|
|
6118
|
-
});
|
|
6119
|
-
await throwIfRunnerAuthFailure(res);
|
|
6120
|
-
if (!res.ok) {
|
|
6121
|
-
throw new SecretsHttpError(res.status, res.statusText);
|
|
6122
|
-
}
|
|
6123
|
-
const json = await res.json();
|
|
6124
|
-
return json.values;
|
|
6125
|
-
}
|
|
6126
|
-
|
|
6127
6440
|
// src/commands/run/pipeline.ts
|
|
6128
6441
|
init_cache();
|
|
6129
6442
|
async function runPipeline(options) {
|
|
6130
6443
|
const { bundlePath, port, logger, loggerConfig, api } = options;
|
|
6131
6444
|
let configVersion;
|
|
6445
|
+
const configFrozen = readConfigFrozen();
|
|
6132
6446
|
if (api) {
|
|
6133
6447
|
await injectSecrets(api, logger);
|
|
6134
6448
|
}
|
|
6135
6449
|
logger.info(`walkeros/flow v${VERSION}`);
|
|
6136
6450
|
logger.info(`Instance: ${getInstanceId()}`);
|
|
6451
|
+
if (configFrozen) {
|
|
6452
|
+
logger.info("Config frozen: hot-swap and heartbeat disabled");
|
|
6453
|
+
}
|
|
6137
6454
|
const healthServer = await createHealthServer(port, logger);
|
|
6138
|
-
const
|
|
6455
|
+
const observeLevel = readObserveLevel(logger);
|
|
6456
|
+
const telemetryObservers = buildTelemetryObservers(
|
|
6457
|
+
api?.flowId ?? "flow",
|
|
6458
|
+
observeLevel
|
|
6459
|
+
);
|
|
6139
6460
|
const runtimeConfig = { port };
|
|
6140
6461
|
let handle;
|
|
6141
6462
|
try {
|
|
@@ -6164,20 +6485,24 @@ async function runPipeline(options) {
|
|
|
6164
6485
|
const ingestToken = process.env.WALKEROS_INGEST_TOKEN;
|
|
6165
6486
|
const deploymentId = process.env.WALKEROS_DEPLOYMENT_ID;
|
|
6166
6487
|
if (observerBase && ingestToken && deploymentId) {
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6488
|
+
if (observeLevel === "trace") {
|
|
6489
|
+
logger.info("Trace poller: skipped (observe level is trace)");
|
|
6490
|
+
} else {
|
|
6491
|
+
tracePoller = createTracePoller(
|
|
6492
|
+
{
|
|
6493
|
+
url: `${observerBase}/trace/${deploymentId}`,
|
|
6494
|
+
token: ingestToken,
|
|
6495
|
+
intervalMs: 15e3
|
|
6496
|
+
},
|
|
6497
|
+
logger
|
|
6498
|
+
);
|
|
6499
|
+
tracePoller.start();
|
|
6500
|
+
logger.info("Trace poller: active (every 15s)");
|
|
6501
|
+
}
|
|
6177
6502
|
}
|
|
6178
6503
|
let currentBundleCleanup;
|
|
6179
6504
|
let currentConfigPath;
|
|
6180
|
-
if (api) {
|
|
6505
|
+
if (api && !configFrozen) {
|
|
6181
6506
|
heartbeat = createHeartbeat(
|
|
6182
6507
|
{
|
|
6183
6508
|
appUrl: api.appUrl,
|
|
@@ -6187,7 +6512,9 @@ async function runPipeline(options) {
|
|
|
6187
6512
|
deploymentId: api.deploymentId,
|
|
6188
6513
|
configVersion,
|
|
6189
6514
|
intervalMs: api.heartbeatIntervalMs,
|
|
6190
|
-
getCounters: () => handle.collector.status
|
|
6515
|
+
getCounters: () => handle.collector.status,
|
|
6516
|
+
getErrors: () => options.errorRing?.snapshot() ?? [],
|
|
6517
|
+
getLogs: () => options.logRing?.snapshot() ?? []
|
|
6191
6518
|
},
|
|
6192
6519
|
logger
|
|
6193
6520
|
);
|
|
@@ -6294,17 +6621,39 @@ async function runPipeline(options) {
|
|
|
6294
6621
|
await new Promise(() => {
|
|
6295
6622
|
});
|
|
6296
6623
|
}
|
|
6297
|
-
|
|
6624
|
+
var OBSERVE_LEVELS = [
|
|
6625
|
+
"off",
|
|
6626
|
+
"standard",
|
|
6627
|
+
"trace"
|
|
6628
|
+
];
|
|
6629
|
+
function readConfigFrozen() {
|
|
6630
|
+
const raw = process.env.WALKEROS_CONFIG_FROZEN;
|
|
6631
|
+
return raw === "1" || raw === "true";
|
|
6632
|
+
}
|
|
6633
|
+
function readObserveLevel(logger) {
|
|
6634
|
+
const raw = process.env.WALKEROS_OBSERVE_LEVEL;
|
|
6635
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
6636
|
+
const level = OBSERVE_LEVELS.find((candidate) => candidate === raw);
|
|
6637
|
+
if (!level) {
|
|
6638
|
+
logger.warn(
|
|
6639
|
+
`Ignoring invalid WALKEROS_OBSERVE_LEVEL "${raw}" (expected off, standard, or trace)`
|
|
6640
|
+
);
|
|
6641
|
+
return void 0;
|
|
6642
|
+
}
|
|
6643
|
+
return level;
|
|
6644
|
+
}
|
|
6645
|
+
function buildTelemetryObservers(flowId, observeLevel) {
|
|
6298
6646
|
const base = process.env.WALKEROS_OBSERVER_URL;
|
|
6299
6647
|
const token = process.env.WALKEROS_INGEST_TOKEN;
|
|
6300
6648
|
const deploymentId = process.env.WALKEROS_DEPLOYMENT_ID;
|
|
6301
6649
|
if (!base || !token || !deploymentId) return void 0;
|
|
6302
6650
|
const url = `${base}/ingest/${deploymentId}`;
|
|
6303
6651
|
const emit = createBatchedPoster({ url, token });
|
|
6652
|
+
const observe = observeLevel !== void 0 ? { level: observeLevel } : void 0;
|
|
6304
6653
|
return [
|
|
6305
6654
|
createTelemetryObserver(
|
|
6306
6655
|
emit,
|
|
6307
|
-
() => resolveTelemetryOptions({ flowId, traceUntil: getTraceUntil() })
|
|
6656
|
+
() => resolveTelemetryOptions({ flowId, observe, traceUntil: getTraceUntil() })
|
|
6308
6657
|
)
|
|
6309
6658
|
];
|
|
6310
6659
|
}
|
|
@@ -6347,7 +6696,21 @@ async function lazyPrepareBundleForRun(configPath, options) {
|
|
|
6347
6696
|
async function runCommand(options) {
|
|
6348
6697
|
const timer = createTimer();
|
|
6349
6698
|
timer.start();
|
|
6350
|
-
const
|
|
6699
|
+
const errorRing = new ErrorRing(20);
|
|
6700
|
+
const logRing = new LogRing(100);
|
|
6701
|
+
const LEVEL_NAME = {
|
|
6702
|
+
[Level2.ERROR]: "error",
|
|
6703
|
+
[Level2.WARN]: "warn",
|
|
6704
|
+
[Level2.INFO]: "info",
|
|
6705
|
+
[Level2.DEBUG]: "debug"
|
|
6706
|
+
};
|
|
6707
|
+
const logger = createCLILogger({
|
|
6708
|
+
...options,
|
|
6709
|
+
onLine: (level, message) => {
|
|
6710
|
+
if (level === Level2.ERROR) errorRing.add(message);
|
|
6711
|
+
logRing.add({ time: Date.now(), level: LEVEL_NAME[level], message });
|
|
6712
|
+
}
|
|
6713
|
+
});
|
|
6351
6714
|
try {
|
|
6352
6715
|
if (options.envFile) {
|
|
6353
6716
|
const { loadEnvFile: loadEnvFile2 } = await Promise.resolve().then(() => (init_env_file(), env_file_exports));
|
|
@@ -6417,7 +6780,9 @@ async function runCommand(options) {
|
|
|
6417
6780
|
port,
|
|
6418
6781
|
logger: logger.scope("runner"),
|
|
6419
6782
|
loggerConfig: options.verbose ? { level: 0 } : void 0,
|
|
6420
|
-
api: apiConfig
|
|
6783
|
+
api: apiConfig,
|
|
6784
|
+
errorRing,
|
|
6785
|
+
logRing
|
|
6421
6786
|
});
|
|
6422
6787
|
} catch (error) {
|
|
6423
6788
|
const duration = timer.getElapsed() / 1e3;
|
|
@@ -7235,7 +7600,7 @@ function validateMapping(input) {
|
|
|
7235
7600
|
// src/commands/validate/validators/entry.ts
|
|
7236
7601
|
import Ajv from "ajv";
|
|
7237
7602
|
import { fetchPackageSchema } from "@walkeros/core";
|
|
7238
|
-
var CLIENT_HEADER = "walkeros-cli/4.2.
|
|
7603
|
+
var CLIENT_HEADER = "walkeros-cli/4.2.1-next-1781538735002";
|
|
7239
7604
|
var SECTIONS = ["destinations", "sources", "transformers"];
|
|
7240
7605
|
function resolveEntry(path20, flowConfig) {
|
|
7241
7606
|
const flows = flowConfig.flows;
|
|
@@ -7800,8 +8165,8 @@ import createClient from "openapi-fetch";
|
|
|
7800
8165
|
init_config_file();
|
|
7801
8166
|
import { createHash } from "crypto";
|
|
7802
8167
|
import semver4 from "semver";
|
|
7803
|
-
var bakedContractVersion = true ? "
|
|
7804
|
-
var bakedContractHash = true ? "
|
|
8168
|
+
var bakedContractVersion = true ? "2.1.0" : PLACEHOLDER;
|
|
8169
|
+
var bakedContractHash = true ? "e5c8e836e1bd59ac2c56d40a998943f301e7e4516528c66d87dc51d6525c8b57" : "";
|
|
7805
8170
|
function isRecord2(value) {
|
|
7806
8171
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7807
8172
|
}
|
|
@@ -7932,9 +8297,13 @@ function createApiClient() {
|
|
|
7932
8297
|
}
|
|
7933
8298
|
|
|
7934
8299
|
// src/core/api-error.ts
|
|
8300
|
+
var EXIT_RETRYABLE = 75;
|
|
7935
8301
|
var ApiError = class extends Error {
|
|
7936
8302
|
code;
|
|
7937
8303
|
details;
|
|
8304
|
+
status;
|
|
8305
|
+
retryable;
|
|
8306
|
+
retryAfterSeconds;
|
|
7938
8307
|
// Populated only for CLIENT_OUTDATED responses (HTTP 426).
|
|
7939
8308
|
minVersion;
|
|
7940
8309
|
clientVersion;
|
|
@@ -7946,6 +8315,9 @@ var ApiError = class extends Error {
|
|
|
7946
8315
|
this.name = "ApiError";
|
|
7947
8316
|
this.code = options?.code;
|
|
7948
8317
|
this.details = options?.details;
|
|
8318
|
+
this.status = options?.status;
|
|
8319
|
+
this.retryable = options?.retryable;
|
|
8320
|
+
this.retryAfterSeconds = options?.retryAfterSeconds;
|
|
7949
8321
|
this.minVersion = options?.minVersion;
|
|
7950
8322
|
this.clientVersion = options?.clientVersion;
|
|
7951
8323
|
this.client = options?.client;
|
|
@@ -7953,25 +8325,62 @@ var ApiError = class extends Error {
|
|
|
7953
8325
|
this.docs = options?.docs;
|
|
7954
8326
|
}
|
|
7955
8327
|
};
|
|
7956
|
-
function
|
|
7957
|
-
if (error
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
8328
|
+
function extractApiErrorOptions(error, fallbackMessage) {
|
|
8329
|
+
if (!error || typeof error !== "object" || !("error" in error) || typeof error.error !== "object") {
|
|
8330
|
+
return null;
|
|
8331
|
+
}
|
|
8332
|
+
const inner = error.error;
|
|
8333
|
+
const message = inner.message || fallbackMessage;
|
|
8334
|
+
const code = inner.code;
|
|
8335
|
+
const details = inner.details?.errors;
|
|
8336
|
+
const options = { code, details };
|
|
8337
|
+
if (code === "CLIENT_OUTDATED") {
|
|
8338
|
+
options.minVersion = inner.minVersion;
|
|
8339
|
+
options.clientVersion = inner.clientVersion;
|
|
8340
|
+
options.client = inner.client;
|
|
8341
|
+
options.upgrade = inner.upgrade;
|
|
8342
|
+
options.docs = inner.docs;
|
|
7971
8343
|
}
|
|
8344
|
+
return { message, options };
|
|
8345
|
+
}
|
|
8346
|
+
function throwApiError(error, fallbackMessage) {
|
|
8347
|
+
const extracted = extractApiErrorOptions(error, fallbackMessage);
|
|
8348
|
+
if (extracted) throw new ApiError(extracted.message, extracted.options);
|
|
7972
8349
|
throw new ApiError(fallbackMessage);
|
|
7973
8350
|
}
|
|
8351
|
+
function parseRetryAfter(value) {
|
|
8352
|
+
if (!value) return void 0;
|
|
8353
|
+
const trimmed = value.trim();
|
|
8354
|
+
if (/^\d+$/.test(trimmed)) return parseInt(trimmed, 10);
|
|
8355
|
+
const when = Date.parse(trimmed);
|
|
8356
|
+
if (Number.isNaN(when)) return void 0;
|
|
8357
|
+
return Math.max(0, Math.round((when - Date.now()) / 1e3));
|
|
8358
|
+
}
|
|
8359
|
+
function throwApiResponseError(response, body, fallbackMessage) {
|
|
8360
|
+
const status = response.status;
|
|
8361
|
+
const retryAfterSeconds = parseRetryAfter(
|
|
8362
|
+
response.headers.get("retry-after")
|
|
8363
|
+
);
|
|
8364
|
+
const retryable = status === 429 || status === 503 && retryAfterSeconds !== void 0;
|
|
8365
|
+
const extracted = extractApiErrorOptions(body, fallbackMessage);
|
|
8366
|
+
const message = extracted?.message ?? fallbackMessage;
|
|
8367
|
+
const options = {
|
|
8368
|
+
...extracted?.options ?? {},
|
|
8369
|
+
status,
|
|
8370
|
+
retryable,
|
|
8371
|
+
retryAfterSeconds
|
|
8372
|
+
};
|
|
8373
|
+
throw new ApiError(message, options);
|
|
8374
|
+
}
|
|
8375
|
+
function machineReadableErrorLine(err) {
|
|
8376
|
+
const code = err instanceof ApiError ? err.code ?? "UNKNOWN" : "UNKNOWN";
|
|
8377
|
+
const retryable = err instanceof ApiError ? err.retryable === true : false;
|
|
8378
|
+
const retryAfter = err instanceof ApiError && err.retryAfterSeconds !== void 0 ? ` retryAfter=${err.retryAfterSeconds}` : "";
|
|
8379
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8380
|
+
return `error: code=${code} retryable=${retryable}${retryAfter} message=${message}`;
|
|
8381
|
+
}
|
|
7974
8382
|
function handleCliError(err) {
|
|
8383
|
+
console.error(machineReadableErrorLine(err));
|
|
7975
8384
|
if (err instanceof ApiError && err.code === "CLIENT_OUTDATED") {
|
|
7976
8385
|
console.error(`
|
|
7977
8386
|
${err.message}
|
|
@@ -7981,8 +8390,12 @@ ${err.message}
|
|
|
7981
8390
|
`);
|
|
7982
8391
|
process.exit(2);
|
|
7983
8392
|
}
|
|
7984
|
-
|
|
7985
|
-
|
|
8393
|
+
if (err instanceof ApiError && err.retryable) {
|
|
8394
|
+
const hint = err.retryAfterSeconds !== void 0 ? ` Retry after ${err.retryAfterSeconds}s.` : " This is temporary, retry shortly.";
|
|
8395
|
+
console.error(`${err.message}${hint}`);
|
|
8396
|
+
process.exit(EXIT_RETRYABLE);
|
|
8397
|
+
}
|
|
8398
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
7986
8399
|
process.exit(1);
|
|
7987
8400
|
}
|
|
7988
8401
|
|
|
@@ -8278,6 +8691,7 @@ async function readFlowStdin() {
|
|
|
8278
8691
|
}
|
|
8279
8692
|
|
|
8280
8693
|
// src/commands/deploy/index.ts
|
|
8694
|
+
import { randomUUID } from "crypto";
|
|
8281
8695
|
init_auth();
|
|
8282
8696
|
init_http();
|
|
8283
8697
|
init_sse();
|
|
@@ -8308,8 +8722,9 @@ async function getAvailableFlowNames(options) {
|
|
|
8308
8722
|
const settings = flow.settings;
|
|
8309
8723
|
return settings?.map((c) => c.name) ?? [];
|
|
8310
8724
|
}
|
|
8725
|
+
var DEFAULT_DEPLOY_WAIT_MS = 12 * 60 * 1e3;
|
|
8311
8726
|
async function streamDeploymentStatus(projectId, deploymentId, options) {
|
|
8312
|
-
const timeoutMs = options.timeout ??
|
|
8727
|
+
const timeoutMs = options.timeout ?? DEFAULT_DEPLOY_WAIT_MS;
|
|
8313
8728
|
const response = await apiFetch(
|
|
8314
8729
|
`/api/projects/${projectId}/deployments/${deploymentId}/stream`,
|
|
8315
8730
|
{
|
|
@@ -8368,7 +8783,10 @@ async function deploy(options) {
|
|
|
8368
8783
|
}
|
|
8369
8784
|
const { data, error } = await client.POST(
|
|
8370
8785
|
"/api/projects/{projectId}/flows/{flowId}/deploy",
|
|
8371
|
-
{
|
|
8786
|
+
{
|
|
8787
|
+
params: { path: { projectId, flowId: options.flowId } },
|
|
8788
|
+
headers: { "Idempotency-Key": randomUUID() }
|
|
8789
|
+
}
|
|
8372
8790
|
);
|
|
8373
8791
|
if (error) {
|
|
8374
8792
|
try {
|
|
@@ -8398,13 +8816,38 @@ Available: ${names.join(", ")}`,
|
|
|
8398
8816
|
}
|
|
8399
8817
|
async function deploySettings(options) {
|
|
8400
8818
|
const { flowId, projectId, settingsId } = options;
|
|
8401
|
-
const
|
|
8819
|
+
const triggerDeploy = () => apiFetch(
|
|
8402
8820
|
`/api/projects/${projectId}/flows/${flowId}/settings/${settingsId}/deploy`,
|
|
8403
|
-
{ method: "POST" }
|
|
8821
|
+
{ method: "POST", headers: { "Idempotency-Key": randomUUID() } }
|
|
8404
8822
|
);
|
|
8823
|
+
let response = await triggerDeploy();
|
|
8824
|
+
if (!response.ok && options.wait) {
|
|
8825
|
+
let retryBody = {};
|
|
8826
|
+
try {
|
|
8827
|
+
retryBody = await response.clone().json();
|
|
8828
|
+
} catch {
|
|
8829
|
+
retryBody = {};
|
|
8830
|
+
}
|
|
8831
|
+
try {
|
|
8832
|
+
throwApiResponseError(
|
|
8833
|
+
response,
|
|
8834
|
+
retryBody,
|
|
8835
|
+
`Deploy failed (${response.status})`
|
|
8836
|
+
);
|
|
8837
|
+
} catch (e) {
|
|
8838
|
+
if (e instanceof ApiError && e.retryable) {
|
|
8839
|
+
const waitSeconds = Math.min(e.retryAfterSeconds ?? 5, 60);
|
|
8840
|
+
options.onStatus?.("rate_limited", `retrying in ${waitSeconds}s`);
|
|
8841
|
+
await new Promise((resolve5) => setTimeout(resolve5, waitSeconds * 1e3));
|
|
8842
|
+
response = await triggerDeploy();
|
|
8843
|
+
} else {
|
|
8844
|
+
throw e;
|
|
8845
|
+
}
|
|
8846
|
+
}
|
|
8847
|
+
}
|
|
8405
8848
|
if (!response.ok) {
|
|
8406
8849
|
const body = await response.json().catch(() => ({}));
|
|
8407
|
-
|
|
8850
|
+
throwApiResponseError(response, body, `Deploy failed (${response.status})`);
|
|
8408
8851
|
}
|
|
8409
8852
|
const data = await response.json();
|
|
8410
8853
|
if (!options.wait) return data;
|
|
@@ -8428,7 +8871,7 @@ async function getDeployment(options) {
|
|
|
8428
8871
|
);
|
|
8429
8872
|
if (!response.ok) {
|
|
8430
8873
|
const body = await response.json().catch(() => ({}));
|
|
8431
|
-
|
|
8874
|
+
throwApiResponseError(response, body, "Failed to get deployment");
|
|
8432
8875
|
}
|
|
8433
8876
|
return response.json();
|
|
8434
8877
|
}
|
|
@@ -8441,16 +8884,20 @@ async function getDeployment(options) {
|
|
|
8441
8884
|
return data;
|
|
8442
8885
|
}
|
|
8443
8886
|
var statusLabels = {
|
|
8444
|
-
|
|
8445
|
-
"
|
|
8446
|
-
"bundling:publishing": "Publishing to web...",
|
|
8447
|
-
deploying: "Deploying container...",
|
|
8887
|
+
"deploying:building": "Building bundle...",
|
|
8888
|
+
"deploying:publishing": "Publishing to web...",
|
|
8448
8889
|
"deploying:provisioning": "Provisioning container...",
|
|
8449
8890
|
"deploying:starting": "Starting container...",
|
|
8891
|
+
deploying: "Deploying...",
|
|
8450
8892
|
active: "Container is live",
|
|
8451
8893
|
published: "Published",
|
|
8894
|
+
stopped: "Stopped",
|
|
8452
8895
|
failed: "Deployment failed"
|
|
8453
8896
|
};
|
|
8897
|
+
function renderStatusLabel(status, substatus) {
|
|
8898
|
+
const key = substatus ? `${status}:${substatus}` : status;
|
|
8899
|
+
return statusLabels[key] || statusLabels[status] || `Status: ${status}`;
|
|
8900
|
+
}
|
|
8454
8901
|
async function deployCommand(flowId, options) {
|
|
8455
8902
|
const log = createCLILogger(options);
|
|
8456
8903
|
const timeoutMs = options.timeout ? parseInt(options.timeout, 10) * 1e3 : void 0;
|
|
@@ -8462,10 +8909,7 @@ async function deployCommand(flowId, options) {
|
|
|
8462
8909
|
wait: options.wait !== false,
|
|
8463
8910
|
timeout: timeoutMs,
|
|
8464
8911
|
onStatus: options.json ? void 0 : (status, substatus) => {
|
|
8465
|
-
|
|
8466
|
-
log.info(
|
|
8467
|
-
statusLabels[key] || statusLabels[status] || `Status: ${status}`
|
|
8468
|
-
);
|
|
8912
|
+
log.info(renderStatusLabel(status, substatus));
|
|
8469
8913
|
}
|
|
8470
8914
|
});
|
|
8471
8915
|
if (options.json) {
|
|
@@ -8480,7 +8924,7 @@ async function deployCommand(flowId, options) {
|
|
|
8480
8924
|
} else if (r.status === "failed") {
|
|
8481
8925
|
log.error(`Failed: ${r.errorMessage || "Unknown error"}`);
|
|
8482
8926
|
process.exit(1);
|
|
8483
|
-
} else if (r.status === "
|
|
8927
|
+
} else if (r.status === "deploying") {
|
|
8484
8928
|
log.info(`Deployment started: ${r.deploymentId} (${r.type})`);
|
|
8485
8929
|
} else {
|
|
8486
8930
|
log.info(`Status: ${r.status}`);
|
|
@@ -8708,20 +9152,16 @@ async function createDeployCommand(config, options) {
|
|
|
8708
9152
|
log.info(`Deployment created: ${result.id}`);
|
|
8709
9153
|
log.info(` Slug: ${result.slug}`);
|
|
8710
9154
|
log.info(` Type: ${result.type}`);
|
|
8711
|
-
if (result.deployToken) {
|
|
8712
|
-
log.info(` Token: ${result.deployToken}`);
|
|
8713
|
-
log.warn(" Save this token \u2014 it will not be shown again.");
|
|
8714
|
-
}
|
|
8715
9155
|
log.info("");
|
|
8716
9156
|
log.info("Run locally:");
|
|
8717
9157
|
log.info(
|
|
8718
9158
|
` walkeros run ${isRemoteFlow ? "flow.json" : config} --deploy ${result.id}`
|
|
8719
9159
|
);
|
|
8720
9160
|
log.info("");
|
|
8721
|
-
log.info("
|
|
8722
|
-
log.info(
|
|
8723
|
-
|
|
8724
|
-
);
|
|
9161
|
+
log.info("Create a deploy token for this flow in the app");
|
|
9162
|
+
log.info("(Settings, Self-hosted deploy token) and set it as");
|
|
9163
|
+
log.info("WALKEROS_DEPLOY_TOKEN, then run with Docker:");
|
|
9164
|
+
log.info(" docker run -e WALKEROS_DEPLOY_TOKEN \\");
|
|
8725
9165
|
log.info(" -e WALKEROS_APP_URL=https://app.walkeros.io \\");
|
|
8726
9166
|
log.info(" walkeros/flow:latest");
|
|
8727
9167
|
} catch (err) {
|
|
@@ -8807,14 +9247,14 @@ init_config_file();
|
|
|
8807
9247
|
|
|
8808
9248
|
// src/telemetry/install-id.ts
|
|
8809
9249
|
init_config_file();
|
|
8810
|
-
import { randomUUID } from "crypto";
|
|
9250
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
8811
9251
|
function getInstallationId() {
|
|
8812
9252
|
return readConfig()?.installationId;
|
|
8813
9253
|
}
|
|
8814
9254
|
function createInstallationId() {
|
|
8815
9255
|
const existing = readConfig()?.installationId;
|
|
8816
9256
|
if (existing) return existing;
|
|
8817
|
-
const id =
|
|
9257
|
+
const id = randomUUID2();
|
|
8818
9258
|
writeTelemetryOnlyConfig({ installationId: id });
|
|
8819
9259
|
return id;
|
|
8820
9260
|
}
|
|
@@ -8875,6 +9315,8 @@ function telemetryDisableCommand() {
|
|
|
8875
9315
|
|
|
8876
9316
|
// src/index.ts
|
|
8877
9317
|
init_bundle();
|
|
9318
|
+
init_bundler();
|
|
9319
|
+
init_config_classifier();
|
|
8878
9320
|
|
|
8879
9321
|
// src/commands/bundle/validate-structure.ts
|
|
8880
9322
|
init_structural_validators();
|
|
@@ -9560,12 +10002,15 @@ export {
|
|
|
9560
10002
|
apiFetch,
|
|
9561
10003
|
bakedContractHash,
|
|
9562
10004
|
bakedContractVersion,
|
|
10005
|
+
buildDataPayload,
|
|
9563
10006
|
bundle,
|
|
9564
10007
|
bundleCommand,
|
|
9565
10008
|
canonicalContractHash,
|
|
10009
|
+
classifyStepProperties,
|
|
9566
10010
|
clientContextHeaders,
|
|
9567
10011
|
compareContract,
|
|
9568
10012
|
compareOutput,
|
|
10013
|
+
containsCodeMarkers,
|
|
9569
10014
|
createApiClient,
|
|
9570
10015
|
createDeployCommand,
|
|
9571
10016
|
createDeployment,
|