@bitfab/sdk 0.38.10 → 0.39.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/node.d.cts CHANGED
@@ -1 +1 @@
1
- export { ActiveSpanContext, AdaptContext, AdaptInputsFn, AllowedEnvVars, BITFAB_PROGRESS_PREFIX, BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, BitfabConfig, BitfabError, BitfabFunction, BitfabLangChainCallbackHandler, BitfabLangChainCallbackHandler as BitfabLangGraphCallbackHandler, BitfabLangGraphIntegration, BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, CaptureWhen, CapturedSpan, CodeChangeFile, CurrentSpan, CurrentTrace, DEFAULT_SERVICE_URL, DbBranchOptions, DbBranchReplayError, DbBranchTimings, DbSnapshotConfig, DbSnapshotProvider, DbSnapshotRef, DetachedTrace, HttpClient, LangGraphIntegrationOptions, MockOverride, MockOverrideCtx, MockOverrideInput, MockOverrideResolver, MockStrategy, MockValue, NO_MOCK_OVERRIDE, NodeMatcher, NodeMethodDecorator, NodeOptions, ProviderDefinition, ReplayBranch, ReplayError, ReplayItem, ReplayItemFinishProgress, ReplayItemStartProgress, ReplayOptions, ReplayOptionsFactory, ReplayProgress, ReplayProgressItem, ReplayRegistration, ReplayRegistry, ReplayRegistryContext, ReplayRegistryOptions, ReplayResult, SUPPORTED_PROVIDERS, SpanLookup, SpanMethodDecorator, SpanMethodDecoratorContext, SpanNodeMeta, SpanOccurrence, SpanOptions, SpanType, TokenUsage, TraceResponse, TracingProcessor, VercelCallParams, VercelGenerateResult, VercelStreamResult, WrapBAMLOptions, WrappedBamlFn, __version__, defineReplayRegistry, finalizers, flushTraces, getCurrentReplayBranch, getCurrentSpan, getCurrentTrace, reportReplayProgress, serializeReplayResult } from './index.cjs';
1
+ export { ActiveSpanContext, AdaptContext, AdaptInputsFn, AllowedEnvVars, BITFAB_PROGRESS_PREFIX, BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, BitfabConfig, BitfabError, BitfabFunction, BitfabLangChainCallbackHandler, BitfabLangChainCallbackHandler as BitfabLangGraphCallbackHandler, BitfabLangGraphIntegration, BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, CaptureWhen, CapturedSpan, CodeChangeFile, CurrentSpan, CurrentTrace, DEFAULT_SERVICE_URL, DbBranchOptions, DbBranchReplayError, DbBranchTimings, DbSnapshotConfig, DbSnapshotProvider, DbSnapshotRef, DetachedTrace, HttpClient, LangGraphIntegrationOptions, MockOverride, MockOverrideCtx, MockOverrideInput, MockOverrideResolver, MockStrategy, MockValue, NO_MOCK_OVERRIDE, NodeMatcher, NodeMethodDecorator, NodeOptions, ProviderDefinition, ReplayBranch, ReplayError, ReplayItem, ReplayItemFinishProgress, ReplayItemStartProgress, ReplayOptions, ReplayOptionsFactory, ReplayProgress, ReplayProgressItem, ReplayRegistration, ReplayRegistry, ReplayRegistryContext, ReplayRegistryOptions, ReplayResult, SUPPORTED_PROVIDERS, SeedCase, SeedResult, SpanLookup, SpanMethodDecorator, SpanMethodDecoratorContext, SpanNodeMeta, SpanOccurrence, SpanOptions, SpanType, TokenUsage, TraceIngestionType, TraceResponse, TracingProcessor, VercelCallParams, VercelGenerateResult, VercelStreamResult, WrapBAMLOptions, WrappedBamlFn, __version__, defineReplayRegistry, finalizers, flushTraces, getCurrentReplayBranch, getCurrentSpan, getCurrentTrace, reportReplayProgress, seedFromRegistry, serializeReplayResult } from './index.cjs';
package/dist/node.d.ts CHANGED
@@ -1 +1 @@
1
- export { ActiveSpanContext, AdaptContext, AdaptInputsFn, AllowedEnvVars, BITFAB_PROGRESS_PREFIX, BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, BitfabConfig, BitfabError, BitfabFunction, BitfabLangChainCallbackHandler, BitfabLangChainCallbackHandler as BitfabLangGraphCallbackHandler, BitfabLangGraphIntegration, BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, CaptureWhen, CapturedSpan, CodeChangeFile, CurrentSpan, CurrentTrace, DEFAULT_SERVICE_URL, DbBranchOptions, DbBranchReplayError, DbBranchTimings, DbSnapshotConfig, DbSnapshotProvider, DbSnapshotRef, DetachedTrace, HttpClient, LangGraphIntegrationOptions, MockOverride, MockOverrideCtx, MockOverrideInput, MockOverrideResolver, MockStrategy, MockValue, NO_MOCK_OVERRIDE, NodeMatcher, NodeMethodDecorator, NodeOptions, ProviderDefinition, ReplayBranch, ReplayError, ReplayItem, ReplayItemFinishProgress, ReplayItemStartProgress, ReplayOptions, ReplayOptionsFactory, ReplayProgress, ReplayProgressItem, ReplayRegistration, ReplayRegistry, ReplayRegistryContext, ReplayRegistryOptions, ReplayResult, SUPPORTED_PROVIDERS, SpanLookup, SpanMethodDecorator, SpanMethodDecoratorContext, SpanNodeMeta, SpanOccurrence, SpanOptions, SpanType, TokenUsage, TraceResponse, TracingProcessor, VercelCallParams, VercelGenerateResult, VercelStreamResult, WrapBAMLOptions, WrappedBamlFn, __version__, defineReplayRegistry, finalizers, flushTraces, getCurrentReplayBranch, getCurrentSpan, getCurrentTrace, reportReplayProgress, serializeReplayResult } from './index.js';
1
+ export { ActiveSpanContext, AdaptContext, AdaptInputsFn, AllowedEnvVars, BITFAB_PROGRESS_PREFIX, BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, BitfabConfig, BitfabError, BitfabFunction, BitfabLangChainCallbackHandler, BitfabLangChainCallbackHandler as BitfabLangGraphCallbackHandler, BitfabLangGraphIntegration, BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, CaptureWhen, CapturedSpan, CodeChangeFile, CurrentSpan, CurrentTrace, DEFAULT_SERVICE_URL, DbBranchOptions, DbBranchReplayError, DbBranchTimings, DbSnapshotConfig, DbSnapshotProvider, DbSnapshotRef, DetachedTrace, HttpClient, LangGraphIntegrationOptions, MockOverride, MockOverrideCtx, MockOverrideInput, MockOverrideResolver, MockStrategy, MockValue, NO_MOCK_OVERRIDE, NodeMatcher, NodeMethodDecorator, NodeOptions, ProviderDefinition, ReplayBranch, ReplayError, ReplayItem, ReplayItemFinishProgress, ReplayItemStartProgress, ReplayOptions, ReplayOptionsFactory, ReplayProgress, ReplayProgressItem, ReplayRegistration, ReplayRegistry, ReplayRegistryContext, ReplayRegistryOptions, ReplayResult, SUPPORTED_PROVIDERS, SeedCase, SeedResult, SpanLookup, SpanMethodDecorator, SpanMethodDecoratorContext, SpanNodeMeta, SpanOccurrence, SpanOptions, SpanType, TokenUsage, TraceIngestionType, TraceResponse, TracingProcessor, VercelCallParams, VercelGenerateResult, VercelStreamResult, WrapBAMLOptions, WrappedBamlFn, __version__, defineReplayRegistry, finalizers, flushTraces, getCurrentReplayBranch, getCurrentSpan, getCurrentTrace, reportReplayProgress, seedFromRegistry, serializeReplayResult } from './index.js';
package/dist/node.js CHANGED
@@ -12,22 +12,25 @@ import {
12
12
  finalizers,
13
13
  getCurrentReplayBranch,
14
14
  getCurrentSpan,
15
- getCurrentTrace
16
- } from "./chunk-UE4GGPM6.js";
15
+ getCurrentTrace,
16
+ seedFromRegistry
17
+ } from "./chunk-SHJ3ABUG.js";
17
18
  import "./chunk-ZUD7OFYB.js";
18
19
  import {
19
20
  BITFAB_PROGRESS_PREFIX,
20
- BitfabError,
21
- DEFAULT_SERVICE_URL,
22
21
  DbBranchReplayError,
23
- HttpClient,
24
22
  NO_MOCK_OVERRIDE,
25
23
  ReplayError,
26
- __version__,
27
- flushTraces,
28
24
  reportReplayProgress,
29
25
  serializeReplayResult
30
- } from "./chunk-BNOVHUQB.js";
26
+ } from "./chunk-FW7PZ3EP.js";
27
+ import {
28
+ BitfabError,
29
+ DEFAULT_SERVICE_URL,
30
+ HttpClient,
31
+ __version__,
32
+ flushTraces
33
+ } from "./chunk-2JQSYJJR.js";
31
34
  import {
32
35
  assertAsyncStorageRegistered,
33
36
  registerAsyncLocalStorageClass
@@ -67,6 +70,7 @@ export {
67
70
  getCurrentSpan,
68
71
  getCurrentTrace,
69
72
  reportReplayProgress,
73
+ seedFromRegistry,
70
74
  serializeReplayResult
71
75
  };
72
76
  //# sourceMappingURL=node.js.map
package/dist/node.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/asyncStorageNode.ts","../src/node.ts"],"sourcesContent":["/**\n * Synchronous AsyncLocalStorage registration for Node.js.\n *\n * This module is a side-effect-only import: it registers the Node.js\n * AsyncLocalStorage class into the shared registry so span nesting\n * works immediately - no async gap, no microtask delay.\n *\n * It is imported by `node.ts` (the Node.js-specific entry point) as\n * the FIRST import, before `index.ts` or `client.ts` are evaluated.\n *\n * This file must ONLY be imported in Node.js environments (not browsers).\n * It uses a static `import` of `node:async_hooks`, which will fail in\n * browser bundlers. The `node.ts` entry point is conditionally selected\n * via package.json `exports` conditions, so browsers never see this file.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\"\nimport type { AsyncLocalStorageLike } from \"./asyncStorage.js\"\nimport { registerAsyncLocalStorageClass } from \"./asyncStorage.js\"\n\nregisterAsyncLocalStorageClass(\n AsyncLocalStorage as unknown as new () => AsyncLocalStorageLike<unknown>,\n)\n","/**\n * Node.js-specific entry point for the Bitfab SDK.\n *\n * Selected automatically via package.json `exports` conditions when the\n * consumer's runtime or bundler supports the \"node\" condition (Node.js,\n * most server-side bundlers).\n *\n * This entry point differs from the default (`index.ts`) in one way:\n * it synchronously registers Node.js's `AsyncLocalStorage` before any\n * other SDK code evaluates. This eliminates the async initialization\n * gap that the default entry point has (where the first span might\n * execute before the dynamic import of `node:async_hooks` resolves).\n *\n * The default entry point (`index.ts`) is used for browsers and other\n * environments where `node:async_hooks` is unavailable. There, span\n * nesting degrades gracefully to a shared stack (correct for sequential\n * async, but not for concurrent Promise.all patterns).\n */\n\n// ⚠️ IMPORT ORDER MATTERS\n// asyncStorageNode MUST be imported before index.js.\n// It registers the AsyncLocalStorage class synchronously during module\n// evaluation. index.js (via client.ts) reads from that registration at\n// span creation time. If this import is moved after index.js or removed,\n// span nesting silently degrades to the browser fallback (flat spans).\nimport \"./asyncStorageNode.js\"\n\nexport * from \"./index.js\"\n\n// Verify registration succeeded. This turns a silent degradation into a\n// loud error if someone reorders the imports above or if asyncStorageNode.ts\n// fails to register for any reason. Only runs in the Node.js entry point\n// where we know node:async_hooks must be available.\nimport { assertAsyncStorageRegistered } from \"./asyncStorage.js\"\n\nassertAsyncStorageRegistered()\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,SAAS,yBAAyB;AAIlC;AAAA,EACE;AACF;;;ACaA,6BAA6B;","names":[]}
1
+ {"version":3,"sources":["../src/asyncStorageNode.ts","../src/node.ts"],"sourcesContent":["/**\n * Synchronous AsyncLocalStorage registration for Node.js.\n *\n * This module is a side-effect-only import: it registers the Node.js\n * AsyncLocalStorage class into the shared registry so span nesting\n * works immediately - no async gap, no microtask delay.\n *\n * It is imported by `node.ts` (the Node.js-specific entry point) as\n * the FIRST import, before `index.ts` or `client.ts` are evaluated.\n *\n * This file must ONLY be imported in Node.js environments (not browsers).\n * It uses a static `import` of `node:async_hooks`, which will fail in\n * browser bundlers. The `node.ts` entry point is conditionally selected\n * via package.json `exports` conditions, so browsers never see this file.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\"\nimport type { AsyncLocalStorageLike } from \"./asyncStorage.js\"\nimport { registerAsyncLocalStorageClass } from \"./asyncStorage.js\"\n\nregisterAsyncLocalStorageClass(\n AsyncLocalStorage as unknown as new () => AsyncLocalStorageLike<unknown>,\n)\n","/**\n * Node.js-specific entry point for the Bitfab SDK.\n *\n * Selected automatically via package.json `exports` conditions when the\n * consumer's runtime or bundler supports the \"node\" condition (Node.js,\n * most server-side bundlers).\n *\n * This entry point differs from the default (`index.ts`) in one way:\n * it synchronously registers Node.js's `AsyncLocalStorage` before any\n * other SDK code evaluates. This eliminates the async initialization\n * gap that the default entry point has (where the first span might\n * execute before the dynamic import of `node:async_hooks` resolves).\n *\n * The default entry point (`index.ts`) is used for browsers and other\n * environments where `node:async_hooks` is unavailable. There, span\n * nesting degrades gracefully to a shared stack (correct for sequential\n * async, but not for concurrent Promise.all patterns).\n */\n\n// ⚠️ IMPORT ORDER MATTERS\n// asyncStorageNode MUST be imported before index.js.\n// It registers the AsyncLocalStorage class synchronously during module\n// evaluation. index.js (via client.ts) reads from that registration at\n// span creation time. If this import is moved after index.js or removed,\n// span nesting silently degrades to the browser fallback (flat spans).\nimport \"./asyncStorageNode.js\"\n\nexport * from \"./index.js\"\n\n// Verify registration succeeded. This turns a silent degradation into a\n// loud error if someone reorders the imports above or if asyncStorageNode.ts\n// fails to register for any reason. Only runs in the Node.js entry point\n// where we know node:async_hooks must be available.\nimport { assertAsyncStorageRegistered } from \"./asyncStorage.js\"\n\nassertAsyncStorageRegistered()\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,SAAS,yBAAyB;AAIlC;AAAA,EACE;AACF;;;ACaA,6BAA6B;","names":[]}
@@ -7,7 +7,8 @@ import {
7
7
  serializeReplayResult,
8
8
  sleepForReplayPersistence,
9
9
  waitForReplayPersistence
10
- } from "./chunk-BNOVHUQB.js";
10
+ } from "./chunk-FW7PZ3EP.js";
11
+ import "./chunk-2JQSYJJR.js";
11
12
  import "./chunk-H6LZRFMN.js";
12
13
  export {
13
14
  BITFAB_PROGRESS_PREFIX,
@@ -19,4 +20,4 @@ export {
19
20
  sleepForReplayPersistence,
20
21
  waitForReplayPersistence
21
22
  };
22
- //# sourceMappingURL=replay-SMUBBMNK.js.map
23
+ //# sourceMappingURL=replay-MZYX4IFV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/replayCli.js CHANGED
@@ -1,150 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ BitfabError
4
+ } from "./chunk-RH4K4RSS.js";
2
5
 
3
6
  // src/replayCli.ts
4
7
  import { tsImport } from "tsx/esm/api";
5
8
 
6
- // src/errors.ts
7
- var BitfabError = class extends Error {
8
- constructor(message, url, status, retryAfterMs) {
9
- super(message);
10
- this.url = url;
11
- this.status = status;
12
- this.retryAfterMs = retryAfterMs;
13
- this.name = "BitfabError";
14
- }
15
- };
16
-
17
9
  // src/codeChange.ts
18
10
  var NUL = String.fromCharCode(0);
19
11
 
20
- // src/compress.ts
21
- var gzipNode;
22
- var _nodeGzipReady = (typeof process !== "undefined" && process.versions?.node ? (
23
- // The join trick hides "node:zlib" from static analysis so bundlers that
24
- // ban Node.js built-ins don't fail at build time. webpackIgnore tells
25
- // webpack/turbopack to emit a native import() so Node.js can resolve the
26
- // module at runtime. Same pattern as `asyncStorage.ts`.
27
- import(
28
- /* webpackIgnore: true */
29
- ["node", "zlib"].join(":")
30
- ).then(({ gzip }) => {
31
- gzipNode = (data) => new Promise((resolve, reject) => {
32
- gzip(data, (error, result) => {
33
- if (error) {
34
- reject(error);
35
- } else {
36
- resolve(result);
37
- }
38
- });
39
- });
40
- }).catch(() => {
41
- })
42
- ) : Promise.resolve()).then(() => {
43
- });
44
-
45
- // src/asyncStorage.ts
46
- var AsyncLocalStorageClass = null;
47
- var initDone = false;
48
- function registerAsyncLocalStorageClass(cls) {
49
- if (!AsyncLocalStorageClass) {
50
- AsyncLocalStorageClass = cls;
51
- }
52
- initDone = true;
53
- }
54
- var asyncStorageReady = (typeof process !== "undefined" && process.versions?.node ? (
55
- // The join trick hides "node:async_hooks" from static analysis so
56
- // bundlers that ban Node.js built-ins don't fail at build time.
57
- // webpackIgnore tells webpack/turbopack to emit a native import()
58
- // so Node.js can resolve the module at runtime.
59
- import(
60
- /* webpackIgnore: true */
61
- ["node", "async_hooks"].join(":")
62
- ).then(
63
- (mod) => {
64
- registerAsyncLocalStorageClass(mod.AsyncLocalStorage);
65
- }
66
- ).catch(() => {
67
- })
68
- ) : Promise.resolve()).then(() => {
69
- initDone = true;
70
- });
71
- function createAsyncLocalStorage() {
72
- return AsyncLocalStorageClass ? new AsyncLocalStorageClass() : null;
73
- }
74
-
75
- // src/replayContext.ts
76
- var replayContextStorage = null;
77
- var REPLAY_CONTEXT_STORAGE_SYMBOL = /* @__PURE__ */ Symbol.for("bitfab.replayContextStorage");
78
- var replayContextReady = asyncStorageReady.then(() => {
79
- const shared = globalThis;
80
- const existing = shared[REPLAY_CONTEXT_STORAGE_SYMBOL];
81
- if (existing) {
82
- replayContextStorage = existing;
83
- return;
84
- }
85
- const created = createAsyncLocalStorage();
86
- if (created) {
87
- shared[REPLAY_CONTEXT_STORAGE_SYMBOL] = created;
88
- replayContextStorage = created;
89
- }
90
- });
91
-
92
- // src/payloadBudget.ts
93
- var textEncoder = typeof TextEncoder !== "undefined" ? new TextEncoder() : null;
94
-
95
- // src/otel.ts
96
- import { SpanStatusCode } from "@opentelemetry/api";
97
- import {
98
- ExportResultCode
99
- } from "@opentelemetry/core";
100
- import { resourceFromAttributes } from "@opentelemetry/resources";
101
- import {
102
- AlwaysOnSampler,
103
- BasicTracerProvider,
104
- BatchSpanProcessor
105
- } from "@opentelemetry/sdk-trace-base";
106
- var DEFAULT_LIFECYCLE_TIMEOUT_MS = 3e4;
107
- var liveTransports = /* @__PURE__ */ new Set();
108
- async function forEachLiveTransport(timeoutMs, run) {
109
- const deadline = Date.now() + Math.max(timeoutMs, 0);
110
- let succeeded = true;
111
- for (const transport of [...liveTransports]) {
112
- succeeded = await run(transport, Math.max(0, deadline - Date.now())) && succeeded;
113
- }
114
- return succeeded;
115
- }
116
- function shutdownOtelTransports(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS) {
117
- return forEachLiveTransport(
118
- timeoutMs,
119
- (transport, remaining) => transport.shutdown(remaining)
120
- );
121
- }
122
-
123
- // src/transport.ts
124
- function shutdownTraceTransports(timeoutMs) {
125
- return shutdownOtelTransports(timeoutMs);
126
- }
127
-
128
- // src/http.ts
129
- var EXIT_FLUSH_TIMEOUT_MS = 5e3;
130
- var pendingTracePromises = /* @__PURE__ */ new Set();
131
- if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
132
- let isFlushing = false;
133
- process.on("beforeExit", () => {
134
- if (isFlushing) {
135
- return;
136
- }
137
- isFlushing = true;
138
- void Promise.allSettled([
139
- ...Array.from(pendingTracePromises).map((p) => p.catch(() => {
140
- })),
141
- shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false)
142
- ]).then(() => {
143
- isFlushing = false;
144
- });
145
- });
146
- }
147
-
148
12
  // src/serialize.ts
149
13
  import superjson from "superjson";
150
14
 
@@ -222,7 +86,8 @@ var VALUE_FLAGS = /* @__PURE__ */ new Set([
222
86
  var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
223
87
  "--db-branch",
224
88
  "--no-db-branch",
225
- "--no-code-change"
89
+ "--no-code-change",
90
+ "--dry-run"
226
91
  ]);
227
92
  var HELP_FLAGS = /* @__PURE__ */ new Set(["--help", "-h"]);
228
93
  function usage(registry) {
@@ -239,12 +104,81 @@ Options:
239
104
  --grader-ids id1,id2
240
105
  --mock none|all|marked
241
106
  --db-branch, --no-db-branch
107
+ --dry-run (resolve inputs, run nothing)
242
108
  --params PATH
243
109
  --param name=value (repeatable)
244
110
  -h, --help`;
245
111
  }
246
112
  var ReplayCliHelp = class extends Error {
247
113
  };
114
+ function parseSeedCases(raw, path) {
115
+ const trimmed = raw.trim();
116
+ if (trimmed.length === 0) {
117
+ throw new BitfabError(`Seed file '${path}' is empty.`);
118
+ }
119
+ const values = trimmed.startsWith("[") ? JSON.parse(trimmed) : trimmed.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => JSON.parse(line));
120
+ return values.map((value, index) => {
121
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
122
+ throw new BitfabError(
123
+ `Seed case ${index} in '${path}' must be an object with an "input" array.`
124
+ );
125
+ }
126
+ const { input } = value;
127
+ if (!Array.isArray(input)) {
128
+ throw new BitfabError(
129
+ `Seed case ${index} in '${path}' is missing an "input" array. Wrap a single argument as [arg].`
130
+ );
131
+ }
132
+ return value;
133
+ });
134
+ }
135
+ async function seedFromRegistry(registry, pipeline, cases) {
136
+ const registration = registry[pipeline];
137
+ if (registration === void 0) {
138
+ throw new BitfabError(
139
+ `Unknown pipeline '${pipeline}'. Registered: ${Object.keys(registry).join(", ")}`
140
+ );
141
+ }
142
+ const traceFunctionKey = resolveTraceFunctionKey(registration);
143
+ const traceIds = cases.map(
144
+ (seedCase) => registration.client.seedTrace(traceFunctionKey, {
145
+ input: seedCase.input,
146
+ expected: seedCase.expected,
147
+ fn: registration.fn,
148
+ metadata: seedCase.metadata,
149
+ sessionId: seedCase.sessionId
150
+ })
151
+ );
152
+ const { flushTraces: flushTraces2 } = await import("./http-GJZJKNZZ.js");
153
+ await flushTraces2(3e4);
154
+ return { pipeline, traceFunctionKey, traceIds };
155
+ }
156
+ var SEED_USAGE = 'Usage: bitfab-replay --registry <path> <pipeline> --seed <cases.jsonl>\n\nEach case is a JSON object with an "input" array, plus optional "expected", "metadata", and "sessionId". A JSON array of those objects works too.';
157
+ async function runSeedCli(registry, argv, io = {}) {
158
+ const stdout = io.stdout ?? console.log;
159
+ const stderr = io.stderr ?? console.error;
160
+ if (argv.some((value) => HELP_FLAGS.has(value))) {
161
+ throw new ReplayCliHelp(SEED_USAGE);
162
+ }
163
+ const pipeline = argv[0];
164
+ if (pipeline === void 0 || registry[pipeline] === void 0) {
165
+ throw new BitfabError(SEED_USAGE);
166
+ }
167
+ const seedIndex = argv.indexOf("--seed");
168
+ const casesPath = argv[seedIndex + 1];
169
+ if (casesPath === void 0 || casesPath.startsWith("--")) {
170
+ throw new BitfabError("--seed requires a path to a cases file.");
171
+ }
172
+ const readFile = io.readFile ?? defaultReadFile;
173
+ const cases = parseSeedCases(await readFile(casesPath), casesPath);
174
+ stderr(`[seed] Seeding ${cases.length} case(s) into "${pipeline}"...`);
175
+ const result = await seedFromRegistry(registry, pipeline, cases);
176
+ stderr(
177
+ `[seed] Wrote ${result.traceIds.length} trace(s) for "${result.traceFunctionKey}". Replay them with --trace-ids ${result.traceIds.slice(0, 3).join(",")}${result.traceIds.length > 3 ? ",..." : ""}`
178
+ );
179
+ stdout(JSON.stringify(result, null, 2));
180
+ return result;
181
+ }
248
182
  function requirePositiveInteger(flag, raw) {
249
183
  const value = Number(raw);
250
184
  if (!Number.isInteger(value) || value < 1) {
@@ -293,6 +227,9 @@ function parseReplayCliArgs(registry, argv) {
293
227
  case "--no-code-change":
294
228
  parsed.noCodeChange = true;
295
229
  break;
230
+ case "--dry-run":
231
+ parsed.dryRun = true;
232
+ break;
296
233
  }
297
234
  continue;
298
235
  }
@@ -434,11 +371,22 @@ function valuesEqual(left, right) {
434
371
  function renderSummary(pipeline, result, stderr) {
435
372
  let same = 0;
436
373
  let changed = 0;
374
+ let matched = 0;
375
+ let missed = 0;
437
376
  let errors = 0;
438
377
  for (const item of result.items) {
439
378
  if (item.error !== null) {
440
379
  errors += 1;
441
- } else if (valuesEqual(item.result, item.originalOutput)) {
380
+ continue;
381
+ }
382
+ const equal = valuesEqual(item.result, item.originalOutput);
383
+ if (item.ingestionType === "seeded") {
384
+ if (equal) {
385
+ matched += 1;
386
+ } else {
387
+ missed += 1;
388
+ }
389
+ } else if (equal) {
442
390
  same += 1;
443
391
  } else {
444
392
  changed += 1;
@@ -447,14 +395,39 @@ function renderSummary(pipeline, result, stderr) {
447
395
  stderr("\n\u2500\u2500\u2500 Summary \u2500\u2500\u2500");
448
396
  stderr(` Pipeline: ${pipeline}`);
449
397
  stderr(` Replayed: ${result.items.length}`);
450
- stderr(` Same: ${same}`);
451
- stderr(` Changed: ${changed}`);
398
+ if (same > 0 || changed > 0 || matched + missed === 0) {
399
+ stderr(` Same: ${same}`);
400
+ stderr(` Changed: ${changed}`);
401
+ }
402
+ if (matched > 0 || missed > 0) {
403
+ stderr(` Matched expected: ${matched}`);
404
+ stderr(` Missed expected: ${missed}`);
405
+ }
452
406
  if (errors > 0) {
453
407
  stderr(` Errors: ${errors}`);
454
408
  }
455
409
  stderr(`
456
410
  ${result.testRunUrl}`);
457
411
  }
412
+ function renderDryRun(pipeline, result, stderr) {
413
+ stderr("\n\u2500\u2500\u2500 Dry run \u2500\u2500\u2500");
414
+ stderr(` Pipeline: ${pipeline}`);
415
+ stderr(` Resolved: ${result.items.length} (nothing was executed)`);
416
+ for (const item of result.items) {
417
+ stderr(`
418
+ ${item.originalTraceId}`);
419
+ stderr(` args: ${safeJson(item.input)}`);
420
+ }
421
+ stderr(`
422
+ ${result.testRunUrl}`);
423
+ }
424
+ function safeJson(value) {
425
+ try {
426
+ return JSON.stringify(value) ?? String(value);
427
+ } catch {
428
+ return String(value);
429
+ }
430
+ }
458
431
  function reportReplayError(error, stderr) {
459
432
  for (const item of error.items) {
460
433
  stderr(
@@ -493,6 +466,7 @@ async function runReplayCli(registry, argv = typeof process === "undefined" ? []
493
466
  dbBranch: args.dbBranch && registrationOptions.dbBranch !== void 0 && registrationOptions.dbBranch !== false ? registrationOptions.dbBranch : args.dbBranch
494
467
  },
495
468
  ...args.noCodeChange === true ? { codeChangeDescription: null, codeChangeFiles: null } : {},
469
+ ...args.dryRun === true ? { dryRun: true } : {},
496
470
  ...codeChange === void 0 ? {} : {
497
471
  codeChangeDescription: codeChange.description,
498
472
  codeChangeFiles: codeChange.files
@@ -517,7 +491,7 @@ async function runReplayCli(registry, argv = typeof process === "undefined" ? []
517
491
  options.limit = registrationOptions.limit ?? 10;
518
492
  }
519
493
  stderr(
520
- `[replay] Replaying ${options.traceIds?.length ?? options.limit ?? "dataset"} traces from "${traceFunctionKey}"...`
494
+ `[replay] ${args.dryRun === true ? "Resolving inputs for" : "Replaying"} ${options.traceIds?.length ?? options.limit ?? "dataset"} traces from "${traceFunctionKey}"...`
521
495
  );
522
496
  try {
523
497
  const result = await registration.client.replay(
@@ -525,8 +499,17 @@ async function runReplayCli(registry, argv = typeof process === "undefined" ? []
525
499
  registration.fn,
526
500
  options
527
501
  );
528
- renderSummary(args.pipeline, result, stderr);
502
+ if (args.dryRun === true) {
503
+ renderDryRun(args.pipeline, result, stderr);
504
+ } else {
505
+ renderSummary(args.pipeline, result, stderr);
506
+ }
529
507
  stdout(serializeReplayResult(result));
508
+ if (result.items.length === 0) {
509
+ throw new BitfabError(
510
+ `No traces matched "${traceFunctionKey}", so nothing was replayed. Seed cases with --seed, or capture a trace first.`
511
+ );
512
+ }
530
513
  return result;
531
514
  } catch (error) {
532
515
  if (error instanceof ReplayError) {
@@ -537,7 +520,7 @@ async function runReplayCli(registry, argv = typeof process === "undefined" ? []
537
520
  }
538
521
 
539
522
  // src/replayCli.ts
540
- var USAGE = "Usage: bitfab-replay --registry <path> <pipeline> [options]\nRun bitfab-replay --registry <path> --help to list replay options.";
523
+ var USAGE = "Usage: bitfab-replay --registry <path> <pipeline> [options]\n bitfab-replay --registry <path> <pipeline> --seed <cases.jsonl>\nRun bitfab-replay --registry <path> --help to list replay options.";
541
524
  async function parseCommandArgs(argv) {
542
525
  if (argv.some((value) => value === "--help" || value === "-h") && !argv.includes("--registry")) {
543
526
  throw new ReplayCliHelp(USAGE);
@@ -577,6 +560,9 @@ function resolveRegistry(moduleValue, path) {
577
560
  async function runReplayCommand(argv, io = {}, importer = importRegistry) {
578
561
  const { registryPath, replayArgs } = await parseCommandArgs(argv);
579
562
  const registry = resolveRegistry(await importer(registryPath), registryPath);
563
+ if (replayArgs.includes("--seed")) {
564
+ return runSeedCli(registry, replayArgs, io);
565
+ }
580
566
  return runReplayCli(registry, replayArgs, io);
581
567
  }
582
568
  async function runReplayMain(argv, io = {}) {