@usejourney/core 0.1.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.
@@ -0,0 +1,696 @@
1
+ import { z, ZodType } from 'zod';
2
+ export { infer as ZodInfer, ZodType, ZodTypeAny, z } from 'zod';
3
+
4
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
5
+ declare const EndpointResponseBrand: unique symbol;
6
+ interface EndpointRef<TResponse> {
7
+ readonly method: HttpMethod;
8
+ readonly path: string;
9
+ readonly operationId?: string;
10
+ readonly [EndpointResponseBrand]?: TResponse;
11
+ }
12
+ interface EndpointDescriptor {
13
+ readonly method: HttpMethod;
14
+ readonly path: string;
15
+ readonly baseUrl?: string;
16
+ }
17
+ type Endpoint<TResponse = unknown> = EndpointRef<TResponse> | EndpointDescriptor;
18
+ type ResponseOf<E> = E extends EndpointRef<infer R> ? R : unknown;
19
+ declare function isEndpointRef(e: Endpoint): e is EndpointRef<unknown>;
20
+
21
+ declare const JourneyConfigSchema: z.ZodObject<{
22
+ name: z.ZodOptional<z.ZodString>;
23
+ spec: z.ZodDefault<z.ZodString>;
24
+ generatedDir: z.ZodDefault<z.ZodString>;
25
+ journeysDir: z.ZodDefault<z.ZodString>;
26
+ environmentsDir: z.ZodDefault<z.ZodString>;
27
+ defaultEnvironment: z.ZodOptional<z.ZodString>;
28
+ baseUrl: z.ZodOptional<z.ZodString>;
29
+ runHistoryKeepCount: z.ZodDefault<z.ZodNumber>;
30
+ tlsRejectUnauthorized: z.ZodDefault<z.ZodBoolean>;
31
+ }, "strict", z.ZodTypeAny, {
32
+ spec: string;
33
+ generatedDir: string;
34
+ journeysDir: string;
35
+ environmentsDir: string;
36
+ runHistoryKeepCount: number;
37
+ tlsRejectUnauthorized: boolean;
38
+ name?: string | undefined;
39
+ defaultEnvironment?: string | undefined;
40
+ baseUrl?: string | undefined;
41
+ }, {
42
+ name?: string | undefined;
43
+ spec?: string | undefined;
44
+ generatedDir?: string | undefined;
45
+ journeysDir?: string | undefined;
46
+ environmentsDir?: string | undefined;
47
+ defaultEnvironment?: string | undefined;
48
+ baseUrl?: string | undefined;
49
+ runHistoryKeepCount?: number | undefined;
50
+ tlsRejectUnauthorized?: boolean | undefined;
51
+ }>;
52
+ type JourneyConfig = z.infer<typeof JourneyConfigSchema>;
53
+ interface LoadedConfig {
54
+ readonly config: JourneyConfig;
55
+ readonly projectDir: string;
56
+ readonly configPath: string;
57
+ }
58
+ /**
59
+ * Effective base URL for runtime requests. `config.baseUrl` wins when set;
60
+ * otherwise the active environment's `BASE_URL` is used. Returns `undefined`
61
+ * if neither source supplies one — descriptor endpoints can still carry their
62
+ * own per-step baseUrl in that case.
63
+ */
64
+ declare function resolveBaseUrl(config: JourneyConfig): string | undefined;
65
+ declare function resolveConfigPaths(loaded: LoadedConfig): {
66
+ specPath: string;
67
+ generatedDir: string;
68
+ journeysDir: string;
69
+ environmentsDir: string;
70
+ };
71
+ declare function loadConfig(projectDir: string): Promise<LoadedConfig>;
72
+
73
+ type EnvValues = Record<string, string>;
74
+ declare function setActiveEnvironment(name: string, values: EnvValues): void;
75
+ declare function clearActiveEnvironment(): void;
76
+ declare function env(key: string): string;
77
+ declare function tryEnv(key: string): string | undefined;
78
+ declare function loadEnvironment(environmentsDir: string, name: string): Promise<EnvValues>;
79
+ declare function listEnvironments(environmentsDir: string): Promise<string[]>;
80
+
81
+ declare class AssertionError extends Error {
82
+ constructor(message: string);
83
+ }
84
+ interface Expectation<T> {
85
+ toBe(expected: T): void;
86
+ toEqual(expected: unknown): void;
87
+ toBeDefined(): void;
88
+ toContain(expected: unknown): void;
89
+ toMatch(expected: RegExp | string): void;
90
+ toBeGreaterThan(n: number): void;
91
+ toBeGreaterThanOrEqual(n: number): void;
92
+ toBeLessThan(n: number): void;
93
+ toBeLessThanOrEqual(n: number): void;
94
+ toHaveLength(n: number): void;
95
+ }
96
+ declare function expect<T>(value: T): Expectation<T>;
97
+
98
+ /**
99
+ * Output cache for sub-journey invocations. When an `invokeJourney(...)` call
100
+ * supplies a `cacheKey`, the resolved key + child journey name identify a
101
+ * cache slot; a hit short-circuits the child run and replays the stored
102
+ * output. See the M7 RFC (#86) for the lifetime model.
103
+ */
104
+ /** Cache lifetime selected via `--cache`. */
105
+ type CacheMode = "off" | "run" | "process" | "disk";
106
+ interface CacheEntry {
107
+ value: unknown;
108
+ /** Epoch ms after which the entry is stale; absent → no expiry. */
109
+ expiresAt?: number;
110
+ }
111
+ /**
112
+ * Storage backend for sub-journey outputs. `get`/`set` may be sync (memory) or
113
+ * async (disk); callers `await` the result either way.
114
+ */
115
+ interface SubJourneyCache {
116
+ get(key: string): CacheEntry | undefined | Promise<CacheEntry | undefined>;
117
+ set(key: string, value: unknown, ttlMs?: number): void | Promise<void>;
118
+ }
119
+ /** Composite cache key — `${childJourneyName}:${resolvedKey}`. */
120
+ declare function subJourneyCacheKey(journeyName: string, resolvedKey: string): string;
121
+ /**
122
+ * In-memory cache. Lives as long as the holding process keeps the instance —
123
+ * the CLI reuses one instance for `--cache=process` (persists across runs of
124
+ * `journey serve`) and a fresh one per run for `--cache=run`.
125
+ */
126
+ declare class MemorySubJourneyCache implements SubJourneyCache {
127
+ private readonly store;
128
+ get(key: string): CacheEntry | undefined;
129
+ set(key: string, value: unknown, ttlMs?: number): void;
130
+ clear(): void;
131
+ }
132
+ /**
133
+ * Disk-backed cache: one JSON file per key under `dir`, named by the SHA-256
134
+ * of the composite key. Survives process restarts (`--cache=disk`).
135
+ *
136
+ * Values must be JSON-serializable — a sub-journey `output(...)` is normally a
137
+ * plain object, so this holds in practice. Non-JSON values (Date, undefined,
138
+ * functions) are silently lost in the round-trip.
139
+ */
140
+ declare class DiskSubJourneyCache implements SubJourneyCache {
141
+ private readonly dir;
142
+ constructor(dir: string);
143
+ private pathFor;
144
+ get(key: string): Promise<CacheEntry | undefined>;
145
+ set(key: string, value: unknown, ttlMs?: number): Promise<void>;
146
+ }
147
+ /**
148
+ * Builds the cache backend for a `CacheMode`. `off` → no cache. `run` and
149
+ * `process` → a fresh `MemorySubJourneyCache` (the caller controls reuse, and
150
+ * so the lifetime). `disk` → a `DiskSubJourneyCache` under `diskDir`.
151
+ */
152
+ declare function createSubJourneyCache(mode: CacheMode, opts: {
153
+ diskDir: string;
154
+ }): SubJourneyCache | undefined;
155
+
156
+ /**
157
+ * Pluggable request/response logger. The runtime calls into a logger attached
158
+ * to the HttpContext (if any); CLI/GUI surfaces wire one up when --debug or
159
+ * DEBUG=journey is set.
160
+ */
161
+ interface RequestLog {
162
+ method: string;
163
+ url: string;
164
+ headers: Record<string, string>;
165
+ body?: unknown;
166
+ }
167
+ interface ResponseLog {
168
+ status: number;
169
+ headers: Record<string, string>;
170
+ body: unknown;
171
+ durationMs: number;
172
+ }
173
+ /**
174
+ * Fired at the top of a run before any step executes. The runtime allocates a
175
+ * runId if the caller didn't pass one in, so subscribers can correlate later
176
+ * events back to the run.
177
+ */
178
+ interface RunStartEvent {
179
+ runId: string;
180
+ journeyNames: string[];
181
+ }
182
+ /** Fired once per run after every journey has either completed or halted. */
183
+ interface RunEndEvent {
184
+ runId: string;
185
+ ok: boolean;
186
+ durationMs: number;
187
+ results: ReadonlyArray<{
188
+ name: string;
189
+ ok: boolean;
190
+ }>;
191
+ }
192
+ /**
193
+ * Fired once per journey, right after `runJourney` has finished collecting the
194
+ * step list (a single execution of `def.body()`) and before any `onStepStart`.
195
+ * The full ordered step list is known at this point — subscribers can use it to
196
+ * pre-render a timeline without waiting for each `step:start` to arrive.
197
+ *
198
+ * Particularly useful for journeys whose bodies inject steps via helpers (e.g.
199
+ * `registerAuthStep()` calling `step()` from inside the body): a static parse
200
+ * of the source can't see those, but `onPlanned` does.
201
+ *
202
+ * `stepIdxOffset` is the absolute index of the first step in this journey
203
+ * within the surrounding `runAllRegistered` call; consumers can use it to map
204
+ * positions in `steps` back to the monotonic `stepIdx` values that subsequent
205
+ * `step:start` / `step:end` events will carry.
206
+ */
207
+ /**
208
+ * One entry in a planned pipeline. `kind` is `"step"` for an HTTP step
209
+ * (carries method/path from the endpoint) and `"sub"` for an
210
+ * `invokeJourney(...)` node (carries the child journey's display name;
211
+ * method/path are unset).
212
+ *
213
+ * A `"sub"` node carries `children` — the best-effort plan of the child
214
+ * pipeline, discovered by evaluating the reusable journey body at plan time
215
+ * (recursively, so nested sub-journeys are included). `incomplete` is set
216
+ * when that discovery could not run — e.g. the body threw, or the recursion
217
+ * cap was hit — in which case `children` is absent and subscribers fall back
218
+ * to the live `onGroupStart` / step events once the sub-journey executes.
219
+ *
220
+ * Plan-time discovery is best-effort: conditional `step()` calls and a
221
+ * sub-journey that turns out to be a cache hit can make the planned tree
222
+ * differ from what actually runs. The live group/step events are always
223
+ * authoritative.
224
+ */
225
+ interface PlannedNode {
226
+ kind?: "step" | "sub";
227
+ name: string;
228
+ method?: string;
229
+ path?: string;
230
+ /** Sub-journey only — best-effort plan of the child pipeline. */
231
+ children?: PlannedNode[];
232
+ /** Sub-journey only — true when the child pipeline could not be discovered. */
233
+ incomplete?: boolean;
234
+ }
235
+ interface RunPlannedEvent {
236
+ runId: string;
237
+ journeyIdx: number;
238
+ journeyName: string;
239
+ stepIdxOffset: number;
240
+ /**
241
+ * Resolved pipeline entries, in order. A `"sub"` entry nests its child
242
+ * pipeline under `children` (see `PlannedNode`) so subscribers can
243
+ * pre-render the full tree — including nested sub-journeys — without
244
+ * waiting for the run to enter each group.
245
+ */
246
+ steps: ReadonlyArray<PlannedNode>;
247
+ }
248
+ /**
249
+ * Fired at the moment a sub-journey node (an `invokeJourney(handle, ...)`
250
+ * call) begins executing — before any of the child's steps run. The `stepIdx`
251
+ * is the slot consumed by the sub-journey itself in the parent run's
252
+ * monotonic counter; child step events that follow carry `firstChildStepIdx`
253
+ * and beyond.
254
+ */
255
+ interface GroupStartEvent {
256
+ runId: string;
257
+ journeyIdx: number;
258
+ /** Display name for the timeline (override on the call site, else handle.name). */
259
+ name: string;
260
+ /** Child journey's authored name (i.e. handle.name). Distinct from `name` only when overridden. */
261
+ childJourneyName: string;
262
+ /** Slot consumed by the sub-journey node itself. */
263
+ stepIdx: number;
264
+ /** First child stepIdx that will fire inside this group (== `stepIdx + 1`). */
265
+ firstChildStepIdx: number;
266
+ /** "miss" / "hit". Until #90 lands the cache store, this is always "miss". */
267
+ cacheStatus: "miss" | "hit";
268
+ /** Resolved cache key if the caller supplied a `cacheKey` opt. Omitted otherwise. */
269
+ resolvedKey?: string;
270
+ }
271
+ /** Fired once the sub-journey node finishes (success, failure, or cache hit short-circuit). */
272
+ interface GroupEndEvent {
273
+ runId: string;
274
+ journeyIdx: number;
275
+ name: string;
276
+ childJourneyName: string;
277
+ stepIdx: number;
278
+ /** Last child stepIdx that fired inside this group. Equal to `stepIdx` when the child had no steps (or hit cache). */
279
+ lastChildStepIdx: number;
280
+ ok: boolean;
281
+ durationMs: number;
282
+ error?: string;
283
+ }
284
+ /**
285
+ * Fired when a step starts executing, before any lazy-resolved headers/query/
286
+ * body are materialized and before `onRequest`. `stepIdx` is monotonic across
287
+ * the whole run, not just within a journey — this way consumers don't need to
288
+ * track journey boundaries to correlate events.
289
+ */
290
+ interface StepStartEvent {
291
+ runId: string;
292
+ journeyIdx: number;
293
+ journeyName: string;
294
+ stepIdx: number;
295
+ name: string;
296
+ }
297
+ /** Fired when a step finishes, after `onResponse`/`onError`. */
298
+ interface StepEndEvent {
299
+ runId: string;
300
+ journeyIdx: number;
301
+ stepIdx: number;
302
+ ok: boolean;
303
+ durationMs: number;
304
+ error?: string;
305
+ }
306
+ /**
307
+ * Fired for `console.log` / `console.warn` / `console.error` calls made from
308
+ * inside step hooks (e.g. `after(res) { console.log(res.body.id); }`). The
309
+ * subscriber attaches its own `stepIdx`; core carries only the raw level + text.
310
+ */
311
+ interface LogEvent {
312
+ level: "info" | "warn" | "error";
313
+ text: string;
314
+ }
315
+ interface JourneyLogger {
316
+ onRunStart?(event: RunStartEvent): void;
317
+ onRunEnd?(event: RunEndEvent): void;
318
+ onPlanned?(event: RunPlannedEvent): void;
319
+ onStepStart?(event: StepStartEvent): void;
320
+ onStepEnd?(event: StepEndEvent): void;
321
+ onRequest?(req: RequestLog): void;
322
+ onResponse?(req: RequestLog, res: ResponseLog): void;
323
+ onError?(req: RequestLog, error: unknown, durationMs: number): void;
324
+ /** Fired when a sub-journey node begins executing, before any child step events. */
325
+ onGroupStart?(event: GroupStartEvent): void;
326
+ /** Fired when a sub-journey node finishes; carries the child step range and the group outcome. */
327
+ onGroupEnd?(event: GroupEndEvent): void;
328
+ /**
329
+ * Fired for user-code `console.*` calls captured during a run. The runner
330
+ * installs a shim that forwards each call here and to the original console
331
+ * so terminal output stays intact.
332
+ */
333
+ onLog?(event: LogEvent): void;
334
+ info?(message: string): void;
335
+ }
336
+ /** Header names that get redacted before logging by default. */
337
+ declare const SECRET_HEADERS: ReadonlyArray<string>;
338
+ /**
339
+ * Walks `err.cause` up to `depth` links and joins each message with ` ← `.
340
+ * Surfaces the real reason behind `fetch failed` (Node's fetch wraps undici
341
+ * errors, e.g. `ECONNREFUSED`, `ENOTFOUND`, OpenSSL cert errors). Includes
342
+ * the error `code` in parentheses when present.
343
+ */
344
+ declare function describeError(err: unknown, depth?: number): string;
345
+ declare function maskHeaders(headers: Record<string, string>, masks?: ReadonlyArray<string>): Record<string, string>;
346
+ interface ConsoleLoggerOptions {
347
+ /** Where to write each line. Defaults to console.error so stdout stays clean. */
348
+ write?: (line: string) => void;
349
+ /** Mask secret-looking headers (default true). */
350
+ mask?: boolean;
351
+ /** Truncate logged response/request bodies past this many chars (default 1024). */
352
+ maxBodyChars?: number;
353
+ }
354
+ declare function createConsoleLogger(opts?: ConsoleLoggerOptions): JourneyLogger;
355
+ /**
356
+ * Returns a console logger when DEBUG env var includes "journey" (or "*"),
357
+ * otherwise undefined. Useful for CLI bins that want to opt in via env var.
358
+ */
359
+ declare function loggerFromEnv(env?: NodeJS.ProcessEnv): JourneyLogger | undefined;
360
+
361
+ interface HttpResponse<T = unknown> {
362
+ status: number;
363
+ headers: Record<string, string>;
364
+ body: T;
365
+ }
366
+ interface HttpContext {
367
+ /** Fallback base URL when the endpoint doesn't carry its own. */
368
+ baseUrl?: string;
369
+ /** Global default headers applied before per-step headers. */
370
+ defaultHeaders?: Record<string, string>;
371
+ /** Injectable fetch — default is global fetch. Tests override this. */
372
+ fetchImpl?: typeof fetch;
373
+ /** Optional logger called before/after each request. */
374
+ logger?: JourneyLogger;
375
+ /**
376
+ * Optional undici `Dispatcher` (typed as `unknown` so core stays
377
+ * dependency-free). When set, it is forwarded to `fetch` as `init.dispatcher`
378
+ * so callers can disable TLS verification, route through a proxy, or pin a
379
+ * client cert. The CLI's `--insecure` flag uses this.
380
+ */
381
+ dispatcher?: unknown;
382
+ /**
383
+ * Optional run-scoped AbortSignal. When set, every `fetch` issued through
384
+ * `execute` (and through the instrumented `@usejourney/core` `fetch` helper)
385
+ * receives it, and `runJourney` stops iterating steps as soon as it fires.
386
+ * Used by the dev server's `POST /api/runs/:id/abort` route to cancel an
387
+ * in-flight run.
388
+ */
389
+ signal?: AbortSignal;
390
+ /**
391
+ * Optional sub-journey output cache. When set, an `invokeJourney(...)` call
392
+ * that supplies a `cacheKey` (and isn't `cache: "off"`) replays a stored
393
+ * output instead of re-running the child. Absent → caching disabled
394
+ * (`--cache=off`). Wired by the CLI from the `--cache` flag.
395
+ */
396
+ subJourneyCache?: SubJourneyCache;
397
+ /** Default TTL (ms) for sub-journey cache writes; per-call `cacheTtlMs` overrides. */
398
+ subJourneyCacheTtlMs?: number;
399
+ }
400
+ interface RequestSpec {
401
+ method: HttpMethod;
402
+ url: string;
403
+ headers: Record<string, string>;
404
+ body?: unknown;
405
+ timeoutMs?: number;
406
+ }
407
+ interface BuildRequestOptions {
408
+ endpoint: Endpoint;
409
+ params?: Record<string, string | number>;
410
+ query?: Record<string, string | number | boolean | undefined>;
411
+ headers?: Record<string, string>;
412
+ body?: unknown;
413
+ timeoutMs?: number;
414
+ }
415
+ declare function resolveUrl(endpoint: Endpoint, ctx: HttpContext, params: Record<string, string | number> | undefined, query: Record<string, string | number | boolean | undefined> | undefined): string;
416
+ declare function buildRequest(opts: BuildRequestOptions, ctx: HttpContext): RequestSpec;
417
+ declare function execute(req: RequestSpec, ctx: HttpContext): Promise<HttpResponse>;
418
+
419
+ /** Caller-supplied run metadata. runId is forwarded to every lifecycle event. */
420
+ interface RunMeta {
421
+ runId?: string;
422
+ /**
423
+ * Absolute (monotonic across journey boundaries) stepIdx to stop after. The
424
+ * run still emits `run:end` cleanly; journeys/steps past this index are not
425
+ * collected or executed. Use for "run only up to this step" in the GUI.
426
+ */
427
+ upToStepIdx?: number;
428
+ }
429
+ type Lazy<T> = T | (() => T | Promise<T>);
430
+ interface StepOptions<E extends Endpoint> {
431
+ endpoint: E;
432
+ params?: Lazy<Record<string, string | number>>;
433
+ query?: Lazy<Record<string, string | number | boolean | undefined>>;
434
+ headers?: Lazy<Record<string, string>>;
435
+ body?: Lazy<unknown>;
436
+ timeoutMs?: number;
437
+ assert?: (res: HttpResponse<ResponseOf<E>>) => void | Promise<void>;
438
+ after?: (res: HttpResponse<ResponseOf<E>>) => void | Promise<void>;
439
+ }
440
+ interface StepDef {
441
+ name: string;
442
+ options: StepOptions<Endpoint>;
443
+ }
444
+ /**
445
+ * Recorded call to a reusable journey, captured in the parent's pipeline at
446
+ * registration time. The `handle` carries the child def and its input/output
447
+ * schemas; everything else is per-call configuration. See `invokeJourney`.
448
+ */
449
+ interface SubJourneyCallDef {
450
+ handle: JourneyHandle<unknown, unknown>;
451
+ /** Display label for the timeline; defaults to `handle.name`. */
452
+ name?: string;
453
+ /** Lazy or eager input. Validated against `handle.inputs` (if set) before child body runs. */
454
+ inputs?: Lazy<unknown>;
455
+ /** Cache opts — plumbed through `onGroupStart`. Store lookup lands in #90. */
456
+ cacheKey?: string | ((input: unknown) => string);
457
+ cacheTtlMs?: number;
458
+ cache?: "off" | "inherit";
459
+ assert?: (out: unknown) => void | Promise<void>;
460
+ after?: (out: unknown) => void | Promise<void>;
461
+ }
462
+ /**
463
+ * One ordered entry in the resolved pipeline of a journey. Both kinds consume
464
+ * exactly one `stepIdx` slot in the run's monotonic counter; child steps
465
+ * inside a `sub` node consume their own slots in the same counter.
466
+ */
467
+ type PipelineNode = {
468
+ kind: "step";
469
+ def: StepDef;
470
+ } | {
471
+ kind: "sub";
472
+ def: SubJourneyCallDef;
473
+ };
474
+ /**
475
+ * Typed reference to a reusable journey. Returned by `journey(name, { reusable: true, ... }, body)`.
476
+ * Pass it to `invokeJourney(handle, ...)`; the phantom `I`/`O` parameters drive
477
+ * type inference at the call site.
478
+ */
479
+ interface JourneyHandle<I = unknown, O = unknown> {
480
+ readonly name: string;
481
+ readonly inputs?: ZodType<I>;
482
+ readonly outputs?: ZodType<O>;
483
+ /** Internal: the def the runtime invokes. Treat as opaque. */
484
+ readonly __def: JourneyDef;
485
+ /** Phantom — never assigned at runtime, only present in the type. */
486
+ readonly __input?: I;
487
+ readonly __output?: O;
488
+ }
489
+ /**
490
+ * k6 `export const options` shape for an exported journey. Strict named fields
491
+ * cover the common load profiles (vus + duration, iterations, stages); the
492
+ * index signature passes everything else through so adding a k6 knob (e.g.
493
+ * thresholds, scenarios, ext) does not require a core release.
494
+ */
495
+ interface K6JourneyOptions {
496
+ vus?: number;
497
+ duration?: string;
498
+ iterations?: number;
499
+ stages?: Array<{
500
+ duration: string;
501
+ target: number;
502
+ }>;
503
+ [extra: string]: unknown;
504
+ }
505
+ /**
506
+ * Per-call configuration accepted by the 3-arg `journey()` overloads. The
507
+ * field set is split by mode:
508
+ *
509
+ * - Entry journeys (default — registered for `runAllRegistered`): `tags`, `k6`.
510
+ * - Reusable journeys (`reusable: true`, no auto-run, returns a handle):
511
+ * `inputs`, `outputs`.
512
+ *
513
+ * The two sets are disjoint in the public overloads. Mixing is an `as any`
514
+ * escape from the type-level split; the footgun guard in `runAllRegistered`
515
+ * catches it at run start.
516
+ */
517
+ interface JourneyOptions {
518
+ /** Entry-only — drives `journey export k6 --tag` filtering. */
519
+ tags?: string[];
520
+ /** Entry-only — baked into the emitted k6 script's `export const options`. */
521
+ k6?: K6JourneyOptions;
522
+ /** Switches to reusable mode when true. */
523
+ reusable?: boolean;
524
+ /** Reusable-only — child input schema. Validated before the child body runs. */
525
+ inputs?: ZodType;
526
+ /** Reusable-only — child output schema. Validated when the child completes. */
527
+ outputs?: ZodType;
528
+ }
529
+ interface JourneyDef {
530
+ name: string;
531
+ /** Bodies for entries take no argument; reusable bodies take the validated input. */
532
+ body: (input?: unknown) => void | Promise<void>;
533
+ options?: JourneyOptions;
534
+ }
535
+ interface StepResult {
536
+ name: string;
537
+ ok: boolean;
538
+ request?: {
539
+ method: string;
540
+ url: string;
541
+ };
542
+ response?: HttpResponse;
543
+ error?: string;
544
+ durationMs: number;
545
+ /** Set on sub-journey nodes; the flat list of child step results in execution order. */
546
+ children?: StepResult[];
547
+ /** Set on sub-journey nodes — distinguishes a group entry from a regular step. */
548
+ kind?: "step" | "sub";
549
+ /** Set on sub-journey nodes — whether the output cache was hit or missed. */
550
+ cacheStatus?: "hit" | "miss";
551
+ }
552
+ interface JourneyResult {
553
+ name: string;
554
+ ok: boolean;
555
+ steps: StepResult[];
556
+ durationMs: number;
557
+ }
558
+ /**
559
+ * Returns the HttpContext bound to the currently-executing journey, or
560
+ * undefined when called outside a run. Consumed by the instrumented `fetch`
561
+ * helper so that ad-hoc HTTP calls made from inside step hooks land on the
562
+ * same logger as the steps themselves.
563
+ */
564
+ declare function getCurrentCtx(): HttpContext | undefined;
565
+ /** Type for the journey body when no `inputs` schema is set. */
566
+ type EntryBody = () => void | Promise<void>;
567
+ /** Type for a reusable journey body — receives the validated input. */
568
+ type ReusableBody<I> = (input: I) => void | Promise<void>;
569
+ /** Strict reusable options — discriminated by literal `reusable: true`. */
570
+ interface ReusableJourneyOptions<I, O> {
571
+ reusable: true;
572
+ inputs?: ZodType<I>;
573
+ outputs?: ZodType<O>;
574
+ }
575
+ /** Strict entry options — `reusable` must be absent or false. */
576
+ interface EntryJourneyOptions {
577
+ reusable?: false;
578
+ tags?: string[];
579
+ k6?: K6JourneyOptions;
580
+ }
581
+ declare function journey<I, O>(name: string, options: ReusableJourneyOptions<I, O>, body: ReusableBody<I>): JourneyHandle<I, O>;
582
+ declare function journey(name: string, options: EntryJourneyOptions, body: EntryBody): void;
583
+ declare function journey(name: string, body: EntryBody): void;
584
+ declare function step<E extends Endpoint>(name: string, options: StepOptions<E>): void;
585
+ /** Options accepted at an `invokeJourney(handle, opts)` call site. */
586
+ interface InvokeJourneyOptions<I, O> {
587
+ inputs?: I | (() => I | Promise<I>);
588
+ /** Override the timeline display label; defaults to `handle.name`. */
589
+ name?: string;
590
+ cacheKey?: string | ((input: I) => string);
591
+ cacheTtlMs?: number;
592
+ cache?: "off" | "inherit";
593
+ assert?: (out: O) => void | Promise<void>;
594
+ after?: (out: O) => void | Promise<void>;
595
+ }
596
+ /**
597
+ * Pipeline-level primitive: registers a sub-journey call as a node in the
598
+ * surrounding journey's pipeline. Like `step()`, this is called during the
599
+ * registration phase (the journey body), not at execution time — the child
600
+ * runs inline when the runtime reaches the node.
601
+ */
602
+ declare function invokeJourney<I, O>(handle: JourneyHandle<I, O>, opts?: InvokeJourneyOptions<I, O>): void;
603
+ /**
604
+ * Terminal helper for reusable journeys: records the value returned to the
605
+ * parent's `invokeJourney({ after })`. Called from inside any step's `after`
606
+ * hook within the child body. Multiple calls: last wins (with a warn log).
607
+ * Called outside a reusable journey context: warn and no-op.
608
+ */
609
+ declare function output(value: unknown): void;
610
+ declare function getRegisteredJourneys(): ReadonlyArray<JourneyDef>;
611
+ declare function clearRegistry(): void;
612
+ /** Walks a journey body and returns every pipeline node (step + sub) in order. */
613
+ declare function collectPipeline(def: JourneyDef): Promise<ReadonlyArray<PipelineNode>>;
614
+ /**
615
+ * Collects the child pipeline of a sub-journey call, resolving the call's
616
+ * `inputs` and passing them to the reusable body so `input.*` references in
617
+ * the child resolve. Lets an exporter (#92) walk a journey into a nested
618
+ * tree without running it. Best-effort: input resolution failures fall back
619
+ * to `undefined`, mirroring `discoverChildNodes`.
620
+ */
621
+ declare function collectSubPipeline(call: SubJourneyCallDef): Promise<ReadonlyArray<PipelineNode>>;
622
+ /**
623
+ * Resolves a journey's plan tree without executing it — the same nested
624
+ * `PlannedNode[]` that `onPlanned` broadcasts at run start. Evaluates the
625
+ * journey body (and, best-effort, each sub-journey body) to discover steps;
626
+ * performs no HTTP. Lets a surface pre-render a timeline before the run.
627
+ *
628
+ * `env()` references in the body are resolved against the active environment,
629
+ * so callers should `setActiveEnvironment(...)` first, exactly as they would
630
+ * before a run.
631
+ */
632
+ declare function planJourney(def: JourneyDef): Promise<PlannedNode[]>;
633
+ /**
634
+ * Runs a single entry journey. Emits `onStepStart` / `onStepEnd` (and
635
+ * `onGroupStart` / `onGroupEnd` for any sub-journey nodes) through
636
+ * `ctx.logger`; run-level lifecycle events (`onRunStart` / `onRunEnd`) are
637
+ * the caller's responsibility — use `runAllRegistered` to get the full set.
638
+ *
639
+ * Pass `opts.journeyIdx` (and `opts.runId`) so step events carry the same
640
+ * correlation identifiers as surrounding `runAllRegistered` invocations would
641
+ * use; otherwise a fresh runId is minted and journeyIdx defaults to 0.
642
+ */
643
+ declare function runJourney(def: JourneyDef, ctx: HttpContext, opts?: {
644
+ runId?: string;
645
+ journeyIdx?: number;
646
+ stepIdxOffset?: number;
647
+ upToStepIdx?: number;
648
+ }): Promise<JourneyResult>;
649
+ /**
650
+ * Runs every currently-registered entry journey, clearing the registry first.
651
+ * Emits `onRunStart` / `onRunEnd` bookends around the set, with each journey's
652
+ * steps in between sharing the same runId. stepIdx is monotonic across the
653
+ * whole run so a subscriber can key network/log streams by stepIdx without
654
+ * caring about journey boundaries.
655
+ */
656
+ declare function runAllRegistered(ctx: HttpContext, opts?: RunMeta): Promise<JourneyResult[]>;
657
+
658
+ interface RunRecord {
659
+ id: string;
660
+ timestamp: string;
661
+ results: JourneyResult[];
662
+ }
663
+ interface RunSummary {
664
+ id: string;
665
+ timestamp: string;
666
+ journeyNames: string[];
667
+ ok: boolean;
668
+ /** Sum of durationMs across all journeys in the run. */
669
+ durationMs: number;
670
+ /** Total step count across all journeys. */
671
+ stepCount: number;
672
+ }
673
+ declare function writeRun(cacheDir: string, results: JourneyResult[]): Promise<RunRecord>;
674
+ declare function listRuns(cacheDir: string): Promise<RunSummary[]>;
675
+ declare function readRun(cacheDir: string, id: string): Promise<RunRecord | undefined>;
676
+ declare function pruneRuns(cacheDir: string, keep: number): Promise<number>;
677
+
678
+ type FetchInput = Parameters<typeof globalThis.fetch>[0];
679
+ type FetchInit = Parameters<typeof globalThis.fetch>[1];
680
+ type FetchResponse = Awaited<ReturnType<typeof globalThis.fetch>>;
681
+ /**
682
+ * Drop-in replacement for `globalThis.fetch` that routes through the
683
+ * currently-executing journey's logger when called from inside a step hook.
684
+ *
685
+ * Outside a run context (no active `runJourney`) it delegates to the platform
686
+ * `fetch` with no behaviour change — the same helper module is therefore
687
+ * safe to import from ad-hoc scripts or top-level setup code.
688
+ *
689
+ * Intended use: auth-helper steps that mint a token from a separate service
690
+ * inside an `after` hook. Raw `globalThis.fetch` works but is invisible to
691
+ * the Debug Console and run history; this wrapper makes those calls
692
+ * observable without forcing helpers to thread the HttpContext through.
693
+ */
694
+ declare function fetch$1(input: FetchInput, init?: FetchInit): Promise<FetchResponse>;
695
+
696
+ export { AssertionError, type BuildRequestOptions, type CacheEntry, type CacheMode, type ConsoleLoggerOptions, DiskSubJourneyCache, type Endpoint, type EndpointDescriptor, type EndpointRef, type EntryJourneyOptions, type EnvValues, type Expectation, type GroupEndEvent, type GroupStartEvent, type HttpContext, type HttpMethod, type HttpResponse, type InvokeJourneyOptions, type JourneyConfig, JourneyConfigSchema, type JourneyDef, type JourneyHandle, type JourneyLogger, type JourneyOptions, type JourneyResult, type K6JourneyOptions, type LoadedConfig, type LogEvent, MemorySubJourneyCache, type PipelineNode, type PlannedNode, type RequestLog, type RequestSpec, type ResponseLog, type ResponseOf, type ReusableJourneyOptions, type RunEndEvent, type RunMeta, type RunPlannedEvent, type RunRecord, type RunStartEvent, type RunSummary, SECRET_HEADERS, type StepDef, type StepEndEvent, type StepOptions, type StepResult, type StepStartEvent, type SubJourneyCache, type SubJourneyCallDef, buildRequest, clearActiveEnvironment, clearRegistry, collectPipeline, collectSubPipeline, createConsoleLogger, createSubJourneyCache, describeError, env, execute, expect, fetch$1 as fetch, getCurrentCtx, getRegisteredJourneys, invokeJourney, isEndpointRef, journey, listEnvironments, listRuns, loadConfig, loadEnvironment, loggerFromEnv, maskHeaders, output, planJourney, pruneRuns, readRun, resolveBaseUrl, resolveConfigPaths, resolveUrl, runAllRegistered, runJourney, setActiveEnvironment, step, subJourneyCacheKey, tryEnv, writeRun };