@backendkit-labs/pipeline 0.2.0 → 0.3.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,163 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result) __defProp(target, key, result);
9
+ return result;
10
+ };
11
+
12
+ // src/core/pipeline.ts
13
+ var VALID_MODES = ["stop-on-first", "collect-all"];
14
+ function normalizeMode(mode) {
15
+ if (mode === "stop-on-first" || mode === "collect-all") {
16
+ return mode;
17
+ }
18
+ throw new TypeError(
19
+ `Invalid pipeline mode "${String(mode)}". Expected one of: ${VALID_MODES.map((m) => `"${m}"`).join(", ")}`
20
+ );
21
+ }
22
+ function isStepResult(value) {
23
+ return typeof value === "object" && value !== null && "ok" in value && typeof value.ok === "boolean";
24
+ }
25
+ function buildErrorResult(state, failedStep, cause) {
26
+ return {
27
+ ok: false,
28
+ error: {
29
+ mode: state.mode,
30
+ failedStep,
31
+ cause,
32
+ executedSteps: [...state.executedSteps],
33
+ durationMs: Date.now() - state.start,
34
+ failures: [...state.failures]
35
+ }
36
+ };
37
+ }
38
+ function buildSuccessResult(state) {
39
+ return {
40
+ ok: true,
41
+ value: state.ctx,
42
+ executedSteps: [...state.executedSteps],
43
+ durationMs: Date.now() - state.start
44
+ };
45
+ }
46
+ var Pipeline = class {
47
+ entries = [];
48
+ options;
49
+ constructor(options = {}) {
50
+ this.options = options;
51
+ }
52
+ pipe(step) {
53
+ this.entries.push({
54
+ instance: step,
55
+ name: step.stepName ?? step.constructor.name
56
+ });
57
+ return this;
58
+ }
59
+ pipeIf(condition, step) {
60
+ this.entries.push({
61
+ instance: step,
62
+ name: step.stepName ?? step.constructor.name,
63
+ condition
64
+ });
65
+ return this;
66
+ }
67
+ async run(initialCtx) {
68
+ const state = {
69
+ mode: normalizeMode(this.options.mode ?? "stop-on-first"),
70
+ start: Date.now(),
71
+ executedSteps: [],
72
+ failures: [],
73
+ ctx: initialCtx
74
+ };
75
+ const { onStep, onStepComplete, onError, onComplete } = this.options;
76
+ for (const entry of this.entries) {
77
+ const shouldContinue = await this.#executeEntry(entry, state, { onStep, onStepComplete, onError });
78
+ if (!shouldContinue) {
79
+ return buildErrorResult(state, state.failures[state.failures.length - 1].step, state.failures[state.failures.length - 1].cause);
80
+ }
81
+ }
82
+ if (state.failures.length > 0) {
83
+ return buildErrorResult(state, state.failures[0].step, state.failures[0].cause);
84
+ }
85
+ const durationMs = Date.now() - state.start;
86
+ try {
87
+ await onComplete?.(state.ctx, durationMs, {
88
+ executedSteps: [...state.executedSteps],
89
+ failures: [...state.failures]
90
+ });
91
+ } catch {
92
+ }
93
+ return buildSuccessResult(state);
94
+ }
95
+ async #executeEntry(entry, state, hooks) {
96
+ const { instance, name, condition } = entry;
97
+ const { onStep, onStepComplete, onError } = hooks;
98
+ if (condition) {
99
+ let shouldRun;
100
+ try {
101
+ shouldRun = condition(state.ctx);
102
+ } catch (cause) {
103
+ const error = cause instanceof Error ? cause : new Error(String(cause));
104
+ state.failures.push({ step: name, cause: error });
105
+ if (state.mode === "stop-on-first") {
106
+ return false;
107
+ }
108
+ state.executedSteps.push(name);
109
+ return true;
110
+ }
111
+ if (!shouldRun) return true;
112
+ }
113
+ try {
114
+ await onStep?.(name, state.ctx);
115
+ } catch {
116
+ }
117
+ const stepStart = Date.now();
118
+ const rawResult = await instance.handle(state.ctx);
119
+ const stepMs = Date.now() - stepStart;
120
+ if (!isStepResult(rawResult)) {
121
+ const error = new TypeError(
122
+ `Step "${name}" returned an invalid value. Expected a StepResult (object with "ok" boolean), got ${typeof rawResult}`
123
+ );
124
+ try {
125
+ await onError?.(name, error);
126
+ } catch {
127
+ }
128
+ state.failures.push({ step: name, cause: error });
129
+ if (state.mode === "stop-on-first") {
130
+ return false;
131
+ }
132
+ state.executedSteps.push(name);
133
+ return true;
134
+ }
135
+ const stepResult = rawResult;
136
+ if (!stepResult.ok) {
137
+ try {
138
+ await onError?.(name, stepResult.error);
139
+ } catch {
140
+ }
141
+ state.failures.push({ step: name, cause: stepResult.error });
142
+ if (state.mode === "stop-on-first") {
143
+ return false;
144
+ }
145
+ state.executedSteps.push(name);
146
+ return true;
147
+ }
148
+ state.ctx = stepResult.value;
149
+ state.executedSteps.push(name);
150
+ try {
151
+ await onStepComplete?.(name, state.ctx, stepMs);
152
+ } catch {
153
+ }
154
+ return true;
155
+ }
156
+ };
157
+ function pipeline(options) {
158
+ return new Pipeline(options);
159
+ }
160
+
161
+ export { Pipeline, __decorateClass, pipeline };
162
+ //# sourceMappingURL=chunk-GTIMFVE5.js.map
163
+ //# sourceMappingURL=chunk-GTIMFVE5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/pipeline.ts"],"names":[],"mappings":"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","file":"chunk-GTIMFVE5.js","sourcesContent":["import type {\n PipelineMode,\n PipelineOptions,\n PipelineRunResult,\n PipelineStep,\n PipelineStepFailure,\n StepResult,\n} from './types.js';\n\ninterface StepEntry<TContext, TError> {\n instance: PipelineStep<TContext, TError>;\n name: string;\n condition?: (ctx: TContext) => boolean;\n}\n\ninterface ExecutionState<TContext, TError> {\n readonly mode: PipelineMode;\n readonly start: number;\n readonly executedSteps: string[];\n readonly failures: PipelineStepFailure<TError>[];\n ctx: TContext;\n}\n\nconst VALID_MODES: readonly PipelineMode[] = ['stop-on-first', 'collect-all'];\n\nfunction normalizeMode(mode: unknown): PipelineMode {\n if (mode === 'stop-on-first' || mode === 'collect-all') {\n return mode;\n }\n throw new TypeError(\n `Invalid pipeline mode \"${String(mode)}\". Expected one of: ${VALID_MODES.map(m => `\"${m}\"`).join(', ')}`,\n );\n}\n\nfunction isStepResult<TContext, TError>(\n value: unknown,\n): value is StepResult<TContext, TError> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'ok' in value &&\n typeof (value as Record<string, unknown>).ok === 'boolean'\n );\n}\n\nfunction buildErrorResult<TContext, TError>(\n state: ExecutionState<TContext, TError>,\n failedStep: string,\n cause: TError,\n): PipelineRunResult<TContext, TError> {\n return {\n ok: false,\n error: {\n mode: state.mode,\n failedStep,\n cause,\n executedSteps: [...state.executedSteps],\n durationMs: Date.now() - state.start,\n failures: [...state.failures],\n },\n };\n}\n\nfunction buildSuccessResult<TContext, TError>(\n state: ExecutionState<TContext, TError>,\n): PipelineRunResult<TContext, TError> {\n return {\n ok: true,\n value: state.ctx,\n executedSteps: [...state.executedSteps],\n durationMs: Date.now() - state.start,\n };\n}\n\nexport class Pipeline<TContext, TError = unknown> {\n private readonly entries: StepEntry<TContext, TError>[] = [];\n private readonly options: PipelineOptions<TContext, TError>;\n\n constructor(options: PipelineOptions<TContext, TError> = {}) {\n this.options = options;\n }\n\n pipe(step: PipelineStep<TContext, TError>): this {\n this.entries.push({\n instance: step,\n name: step.stepName ?? step.constructor.name,\n });\n return this;\n }\n\n pipeIf(condition: (ctx: TContext) => boolean, step: PipelineStep<TContext, TError>): this {\n this.entries.push({\n instance: step,\n name: step.stepName ?? step.constructor.name,\n condition,\n });\n return this;\n }\n\n async run(initialCtx: TContext): Promise<PipelineRunResult<TContext, TError>> {\n const state: ExecutionState<TContext, TError> = {\n mode: normalizeMode(this.options.mode ?? 'stop-on-first'),\n start: Date.now(),\n executedSteps: [],\n failures: [],\n ctx: initialCtx,\n };\n\n const { onStep, onStepComplete, onError, onComplete } = this.options;\n\n for (const entry of this.entries) {\n const shouldContinue = await this.#executeEntry(entry, state, { onStep, onStepComplete, onError });\n if (!shouldContinue) {\n return buildErrorResult(state, state.failures[state.failures.length - 1].step, state.failures[state.failures.length - 1].cause);\n }\n }\n\n if (state.failures.length > 0) {\n return buildErrorResult(state, state.failures[0].step, state.failures[0].cause);\n }\n\n const durationMs = Date.now() - state.start;\n try {\n await onComplete?.(state.ctx, durationMs, {\n executedSteps: [...state.executedSteps],\n failures: [...state.failures],\n });\n } catch { /* noop */ }\n\n return buildSuccessResult(state);\n }\n\n async #executeEntry(\n entry: StepEntry<TContext, TError>,\n state: ExecutionState<TContext, TError>,\n hooks: {\n onStep?: (stepName: string, ctx: TContext) => void | Promise<void>;\n onStepComplete?: (stepName: string, ctx: TContext, durationMs: number) => void | Promise<void>;\n onError?: (stepName: string, error: TError) => void | Promise<void>;\n },\n ): Promise<boolean> {\n const { instance, name, condition } = entry;\n const { onStep, onStepComplete, onError } = hooks;\n\n // Evaluate condition — treat thrown exceptions as pipeline failures\n if (condition) {\n let shouldRun: boolean;\n try {\n shouldRun = condition(state.ctx);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n state.failures.push({ step: name, cause: error as unknown as TError });\n\n if (state.mode === 'stop-on-first') {\n return false; // signal caller to stop\n }\n\n state.executedSteps.push(name);\n return true; // continue to next entry\n }\n\n if (!shouldRun) return true; // skip step, continue\n }\n\n try { await onStep?.(name, state.ctx); } catch { /* noop */ }\n\n const stepStart = Date.now();\n const rawResult = await instance.handle(state.ctx);\n const stepMs = Date.now() - stepStart;\n\n // Validate step return value shape\n if (!isStepResult<TContext, TError>(rawResult)) {\n const error = new TypeError(\n `Step \"${name}\" returned an invalid value. Expected a StepResult (object with \"ok\" boolean), got ${typeof rawResult}`,\n );\n try { await onError?.(name, error as unknown as TError); } catch { /* noop */ }\n state.failures.push({ step: name, cause: error as unknown as TError });\n\n if (state.mode === 'stop-on-first') {\n return false;\n }\n\n state.executedSteps.push(name);\n return true;\n }\n\n const stepResult = rawResult;\n\n if (!stepResult.ok) {\n try { await onError?.(name, stepResult.error); } catch { /* noop */ }\n state.failures.push({ step: name, cause: stepResult.error });\n\n if (state.mode === 'stop-on-first') {\n return false;\n }\n\n state.executedSteps.push(name);\n return true;\n }\n\n state.ctx = stepResult.value;\n state.executedSteps.push(name);\n try { await onStepComplete?.(name, state.ctx, stepMs); } catch { /* noop */ }\n\n return true;\n }\n}\n\nexport function pipeline<TContext, TError = unknown>(\n options?: PipelineOptions<TContext, TError>,\n): Pipeline<TContext, TError> {\n return new Pipeline(options);\n}\n"]}
@@ -0,0 +1,167 @@
1
+ 'use strict';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __decorateClass = (decorators, target, key, kind) => {
6
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
7
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
8
+ if (decorator = decorators[i])
9
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
10
+ if (kind && result) __defProp(target, key, result);
11
+ return result;
12
+ };
13
+
14
+ // src/core/pipeline.ts
15
+ var VALID_MODES = ["stop-on-first", "collect-all"];
16
+ function normalizeMode(mode) {
17
+ if (mode === "stop-on-first" || mode === "collect-all") {
18
+ return mode;
19
+ }
20
+ throw new TypeError(
21
+ `Invalid pipeline mode "${String(mode)}". Expected one of: ${VALID_MODES.map((m) => `"${m}"`).join(", ")}`
22
+ );
23
+ }
24
+ function isStepResult(value) {
25
+ return typeof value === "object" && value !== null && "ok" in value && typeof value.ok === "boolean";
26
+ }
27
+ function buildErrorResult(state, failedStep, cause) {
28
+ return {
29
+ ok: false,
30
+ error: {
31
+ mode: state.mode,
32
+ failedStep,
33
+ cause,
34
+ executedSteps: [...state.executedSteps],
35
+ durationMs: Date.now() - state.start,
36
+ failures: [...state.failures]
37
+ }
38
+ };
39
+ }
40
+ function buildSuccessResult(state) {
41
+ return {
42
+ ok: true,
43
+ value: state.ctx,
44
+ executedSteps: [...state.executedSteps],
45
+ durationMs: Date.now() - state.start
46
+ };
47
+ }
48
+ var Pipeline = class {
49
+ entries = [];
50
+ options;
51
+ constructor(options = {}) {
52
+ this.options = options;
53
+ }
54
+ pipe(step) {
55
+ this.entries.push({
56
+ instance: step,
57
+ name: step.stepName ?? step.constructor.name
58
+ });
59
+ return this;
60
+ }
61
+ pipeIf(condition, step) {
62
+ this.entries.push({
63
+ instance: step,
64
+ name: step.stepName ?? step.constructor.name,
65
+ condition
66
+ });
67
+ return this;
68
+ }
69
+ async run(initialCtx) {
70
+ const state = {
71
+ mode: normalizeMode(this.options.mode ?? "stop-on-first"),
72
+ start: Date.now(),
73
+ executedSteps: [],
74
+ failures: [],
75
+ ctx: initialCtx
76
+ };
77
+ const { onStep, onStepComplete, onError, onComplete } = this.options;
78
+ for (const entry of this.entries) {
79
+ const shouldContinue = await this.#executeEntry(entry, state, { onStep, onStepComplete, onError });
80
+ if (!shouldContinue) {
81
+ return buildErrorResult(state, state.failures[state.failures.length - 1].step, state.failures[state.failures.length - 1].cause);
82
+ }
83
+ }
84
+ if (state.failures.length > 0) {
85
+ return buildErrorResult(state, state.failures[0].step, state.failures[0].cause);
86
+ }
87
+ const durationMs = Date.now() - state.start;
88
+ try {
89
+ await onComplete?.(state.ctx, durationMs, {
90
+ executedSteps: [...state.executedSteps],
91
+ failures: [...state.failures]
92
+ });
93
+ } catch {
94
+ }
95
+ return buildSuccessResult(state);
96
+ }
97
+ async #executeEntry(entry, state, hooks) {
98
+ const { instance, name, condition } = entry;
99
+ const { onStep, onStepComplete, onError } = hooks;
100
+ if (condition) {
101
+ let shouldRun;
102
+ try {
103
+ shouldRun = condition(state.ctx);
104
+ } catch (cause) {
105
+ const error = cause instanceof Error ? cause : new Error(String(cause));
106
+ state.failures.push({ step: name, cause: error });
107
+ if (state.mode === "stop-on-first") {
108
+ return false;
109
+ }
110
+ state.executedSteps.push(name);
111
+ return true;
112
+ }
113
+ if (!shouldRun) return true;
114
+ }
115
+ try {
116
+ await onStep?.(name, state.ctx);
117
+ } catch {
118
+ }
119
+ const stepStart = Date.now();
120
+ const rawResult = await instance.handle(state.ctx);
121
+ const stepMs = Date.now() - stepStart;
122
+ if (!isStepResult(rawResult)) {
123
+ const error = new TypeError(
124
+ `Step "${name}" returned an invalid value. Expected a StepResult (object with "ok" boolean), got ${typeof rawResult}`
125
+ );
126
+ try {
127
+ await onError?.(name, error);
128
+ } catch {
129
+ }
130
+ state.failures.push({ step: name, cause: error });
131
+ if (state.mode === "stop-on-first") {
132
+ return false;
133
+ }
134
+ state.executedSteps.push(name);
135
+ return true;
136
+ }
137
+ const stepResult = rawResult;
138
+ if (!stepResult.ok) {
139
+ try {
140
+ await onError?.(name, stepResult.error);
141
+ } catch {
142
+ }
143
+ state.failures.push({ step: name, cause: stepResult.error });
144
+ if (state.mode === "stop-on-first") {
145
+ return false;
146
+ }
147
+ state.executedSteps.push(name);
148
+ return true;
149
+ }
150
+ state.ctx = stepResult.value;
151
+ state.executedSteps.push(name);
152
+ try {
153
+ await onStepComplete?.(name, state.ctx, stepMs);
154
+ } catch {
155
+ }
156
+ return true;
157
+ }
158
+ };
159
+ function pipeline(options) {
160
+ return new Pipeline(options);
161
+ }
162
+
163
+ exports.Pipeline = Pipeline;
164
+ exports.__decorateClass = __decorateClass;
165
+ exports.pipeline = pipeline;
166
+ //# sourceMappingURL=chunk-N7VFWHTQ.cjs.map
167
+ //# sourceMappingURL=chunk-N7VFWHTQ.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/pipeline.ts"],"names":[],"mappings":"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","file":"chunk-N7VFWHTQ.cjs","sourcesContent":["import type {\n PipelineMode,\n PipelineOptions,\n PipelineRunResult,\n PipelineStep,\n PipelineStepFailure,\n StepResult,\n} from './types.js';\n\ninterface StepEntry<TContext, TError> {\n instance: PipelineStep<TContext, TError>;\n name: string;\n condition?: (ctx: TContext) => boolean;\n}\n\ninterface ExecutionState<TContext, TError> {\n readonly mode: PipelineMode;\n readonly start: number;\n readonly executedSteps: string[];\n readonly failures: PipelineStepFailure<TError>[];\n ctx: TContext;\n}\n\nconst VALID_MODES: readonly PipelineMode[] = ['stop-on-first', 'collect-all'];\n\nfunction normalizeMode(mode: unknown): PipelineMode {\n if (mode === 'stop-on-first' || mode === 'collect-all') {\n return mode;\n }\n throw new TypeError(\n `Invalid pipeline mode \"${String(mode)}\". Expected one of: ${VALID_MODES.map(m => `\"${m}\"`).join(', ')}`,\n );\n}\n\nfunction isStepResult<TContext, TError>(\n value: unknown,\n): value is StepResult<TContext, TError> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'ok' in value &&\n typeof (value as Record<string, unknown>).ok === 'boolean'\n );\n}\n\nfunction buildErrorResult<TContext, TError>(\n state: ExecutionState<TContext, TError>,\n failedStep: string,\n cause: TError,\n): PipelineRunResult<TContext, TError> {\n return {\n ok: false,\n error: {\n mode: state.mode,\n failedStep,\n cause,\n executedSteps: [...state.executedSteps],\n durationMs: Date.now() - state.start,\n failures: [...state.failures],\n },\n };\n}\n\nfunction buildSuccessResult<TContext, TError>(\n state: ExecutionState<TContext, TError>,\n): PipelineRunResult<TContext, TError> {\n return {\n ok: true,\n value: state.ctx,\n executedSteps: [...state.executedSteps],\n durationMs: Date.now() - state.start,\n };\n}\n\nexport class Pipeline<TContext, TError = unknown> {\n private readonly entries: StepEntry<TContext, TError>[] = [];\n private readonly options: PipelineOptions<TContext, TError>;\n\n constructor(options: PipelineOptions<TContext, TError> = {}) {\n this.options = options;\n }\n\n pipe(step: PipelineStep<TContext, TError>): this {\n this.entries.push({\n instance: step,\n name: step.stepName ?? step.constructor.name,\n });\n return this;\n }\n\n pipeIf(condition: (ctx: TContext) => boolean, step: PipelineStep<TContext, TError>): this {\n this.entries.push({\n instance: step,\n name: step.stepName ?? step.constructor.name,\n condition,\n });\n return this;\n }\n\n async run(initialCtx: TContext): Promise<PipelineRunResult<TContext, TError>> {\n const state: ExecutionState<TContext, TError> = {\n mode: normalizeMode(this.options.mode ?? 'stop-on-first'),\n start: Date.now(),\n executedSteps: [],\n failures: [],\n ctx: initialCtx,\n };\n\n const { onStep, onStepComplete, onError, onComplete } = this.options;\n\n for (const entry of this.entries) {\n const shouldContinue = await this.#executeEntry(entry, state, { onStep, onStepComplete, onError });\n if (!shouldContinue) {\n return buildErrorResult(state, state.failures[state.failures.length - 1].step, state.failures[state.failures.length - 1].cause);\n }\n }\n\n if (state.failures.length > 0) {\n return buildErrorResult(state, state.failures[0].step, state.failures[0].cause);\n }\n\n const durationMs = Date.now() - state.start;\n try {\n await onComplete?.(state.ctx, durationMs, {\n executedSteps: [...state.executedSteps],\n failures: [...state.failures],\n });\n } catch { /* noop */ }\n\n return buildSuccessResult(state);\n }\n\n async #executeEntry(\n entry: StepEntry<TContext, TError>,\n state: ExecutionState<TContext, TError>,\n hooks: {\n onStep?: (stepName: string, ctx: TContext) => void | Promise<void>;\n onStepComplete?: (stepName: string, ctx: TContext, durationMs: number) => void | Promise<void>;\n onError?: (stepName: string, error: TError) => void | Promise<void>;\n },\n ): Promise<boolean> {\n const { instance, name, condition } = entry;\n const { onStep, onStepComplete, onError } = hooks;\n\n // Evaluate condition — treat thrown exceptions as pipeline failures\n if (condition) {\n let shouldRun: boolean;\n try {\n shouldRun = condition(state.ctx);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n state.failures.push({ step: name, cause: error as unknown as TError });\n\n if (state.mode === 'stop-on-first') {\n return false; // signal caller to stop\n }\n\n state.executedSteps.push(name);\n return true; // continue to next entry\n }\n\n if (!shouldRun) return true; // skip step, continue\n }\n\n try { await onStep?.(name, state.ctx); } catch { /* noop */ }\n\n const stepStart = Date.now();\n const rawResult = await instance.handle(state.ctx);\n const stepMs = Date.now() - stepStart;\n\n // Validate step return value shape\n if (!isStepResult<TContext, TError>(rawResult)) {\n const error = new TypeError(\n `Step \"${name}\" returned an invalid value. Expected a StepResult (object with \"ok\" boolean), got ${typeof rawResult}`,\n );\n try { await onError?.(name, error as unknown as TError); } catch { /* noop */ }\n state.failures.push({ step: name, cause: error as unknown as TError });\n\n if (state.mode === 'stop-on-first') {\n return false;\n }\n\n state.executedSteps.push(name);\n return true;\n }\n\n const stepResult = rawResult;\n\n if (!stepResult.ok) {\n try { await onError?.(name, stepResult.error); } catch { /* noop */ }\n state.failures.push({ step: name, cause: stepResult.error });\n\n if (state.mode === 'stop-on-first') {\n return false;\n }\n\n state.executedSteps.push(name);\n return true;\n }\n\n state.ctx = stepResult.value;\n state.executedSteps.push(name);\n try { await onStepComplete?.(name, state.ctx, stepMs); } catch { /* noop */ }\n\n return true;\n }\n}\n\nexport function pipeline<TContext, TError = unknown>(\n options?: PipelineOptions<TContext, TError>,\n): Pipeline<TContext, TError> {\n return new Pipeline(options);\n}\n"]}
@@ -8,6 +8,17 @@ type StepResult<TContext, TError> = {
8
8
  interface PipelineStep<TContext, TError = unknown> {
9
9
  /** Optional human-readable name used in error reporting and hooks. Defaults to constructor.name. */
10
10
  readonly stepName?: string;
11
+ /**
12
+ * Execute this step against the given context.
13
+ *
14
+ * IMPORTANT — Immutability convention:
15
+ * Steps SHOULD return a NEW context object rather than mutating the existing one.
16
+ * Mutating `ctx` and returning `Ok(ctx)` is an anti-pattern that can cause subtle bugs
17
+ * in hooks and subsequent steps. When in doubt, spread: `Ok({ ...ctx, ...changes })`.
18
+ *
19
+ * @param ctx - The current pipeline context (do NOT mutate in place).
20
+ * @returns A StepResult — either `Ok(newCtx)` on success or `Err(error)` on failure.
21
+ */
11
22
  handle(ctx: TContext): Promise<StepResult<TContext, TError>>;
12
23
  }
13
24
  type PipelineMode = 'stop-on-first' | 'collect-all';
@@ -32,12 +43,16 @@ type PipelineRunResult<TContext, TError> = {
32
43
  readonly ok: false;
33
44
  readonly error: PipelineError<TError>;
34
45
  };
46
+ interface PipelineExecutionMetadata {
47
+ readonly executedSteps: string[];
48
+ readonly failures: PipelineStepFailure<unknown>[];
49
+ }
35
50
  interface PipelineOptions<TContext, TError> {
36
51
  mode?: PipelineMode;
37
52
  onStep?: (stepName: string, ctx: TContext) => void | Promise<void>;
38
53
  onStepComplete?: (stepName: string, ctx: TContext, durationMs: number) => void | Promise<void>;
39
54
  onError?: (stepName: string, error: TError) => void | Promise<void>;
40
- onComplete?: (ctx: TContext, durationMs: number) => void | Promise<void>;
55
+ onComplete?: (ctx: TContext, durationMs: number, metadata: PipelineExecutionMetadata) => void | Promise<void>;
41
56
  }
42
57
 
43
58
  declare class PipelineToken<TContext, TError = unknown> {
@@ -49,4 +64,4 @@ declare class PipelineToken<TContext, TError = unknown> {
49
64
  }
50
65
  declare function definePipeline<TContext, TError = unknown>(name: string): PipelineToken<TContext, TError>;
51
66
 
52
- export { type PipelineOptions as P, type StepResult as S, type PipelineStep as a, type PipelineRunResult as b, type PipelineError as c, type PipelineMode as d, type PipelineStepFailure as e, PipelineToken as f, definePipeline as g };
67
+ export { type PipelineOptions as P, type StepResult as S, type PipelineStep as a, type PipelineRunResult as b, type PipelineError as c, type PipelineExecutionMetadata as d, type PipelineMode as e, type PipelineStepFailure as f, PipelineToken as g, definePipeline as h };
@@ -8,6 +8,17 @@ type StepResult<TContext, TError> = {
8
8
  interface PipelineStep<TContext, TError = unknown> {
9
9
  /** Optional human-readable name used in error reporting and hooks. Defaults to constructor.name. */
10
10
  readonly stepName?: string;
11
+ /**
12
+ * Execute this step against the given context.
13
+ *
14
+ * IMPORTANT — Immutability convention:
15
+ * Steps SHOULD return a NEW context object rather than mutating the existing one.
16
+ * Mutating `ctx` and returning `Ok(ctx)` is an anti-pattern that can cause subtle bugs
17
+ * in hooks and subsequent steps. When in doubt, spread: `Ok({ ...ctx, ...changes })`.
18
+ *
19
+ * @param ctx - The current pipeline context (do NOT mutate in place).
20
+ * @returns A StepResult — either `Ok(newCtx)` on success or `Err(error)` on failure.
21
+ */
11
22
  handle(ctx: TContext): Promise<StepResult<TContext, TError>>;
12
23
  }
13
24
  type PipelineMode = 'stop-on-first' | 'collect-all';
@@ -32,12 +43,16 @@ type PipelineRunResult<TContext, TError> = {
32
43
  readonly ok: false;
33
44
  readonly error: PipelineError<TError>;
34
45
  };
46
+ interface PipelineExecutionMetadata {
47
+ readonly executedSteps: string[];
48
+ readonly failures: PipelineStepFailure<unknown>[];
49
+ }
35
50
  interface PipelineOptions<TContext, TError> {
36
51
  mode?: PipelineMode;
37
52
  onStep?: (stepName: string, ctx: TContext) => void | Promise<void>;
38
53
  onStepComplete?: (stepName: string, ctx: TContext, durationMs: number) => void | Promise<void>;
39
54
  onError?: (stepName: string, error: TError) => void | Promise<void>;
40
- onComplete?: (ctx: TContext, durationMs: number) => void | Promise<void>;
55
+ onComplete?: (ctx: TContext, durationMs: number, metadata: PipelineExecutionMetadata) => void | Promise<void>;
41
56
  }
42
57
 
43
58
  declare class PipelineToken<TContext, TError = unknown> {
@@ -49,4 +64,4 @@ declare class PipelineToken<TContext, TError = unknown> {
49
64
  }
50
65
  declare function definePipeline<TContext, TError = unknown>(name: string): PipelineToken<TContext, TError>;
51
66
 
52
- export { type PipelineOptions as P, type StepResult as S, type PipelineStep as a, type PipelineRunResult as b, type PipelineError as c, type PipelineMode as d, type PipelineStepFailure as e, PipelineToken as f, definePipeline as g };
67
+ export { type PipelineOptions as P, type StepResult as S, type PipelineStep as a, type PipelineRunResult as b, type PipelineError as c, type PipelineExecutionMetadata as d, type PipelineMode as e, type PipelineStepFailure as f, PipelineToken as g, definePipeline as h };
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkHVHE5ZDM_cjs = require('./chunk-HVHE5ZDM.cjs');
3
+ var chunkN7VFWHTQ_cjs = require('./chunk-N7VFWHTQ.cjs');
4
4
 
5
5
  // src/core/define-pipeline.ts
6
6
  var PipelineToken = class {
@@ -25,11 +25,11 @@ function Err(error) {
25
25
 
26
26
  Object.defineProperty(exports, "Pipeline", {
27
27
  enumerable: true,
28
- get: function () { return chunkHVHE5ZDM_cjs.Pipeline; }
28
+ get: function () { return chunkN7VFWHTQ_cjs.Pipeline; }
29
29
  });
30
30
  Object.defineProperty(exports, "pipeline", {
31
31
  enumerable: true,
32
- get: function () { return chunkHVHE5ZDM_cjs.pipeline; }
32
+ get: function () { return chunkN7VFWHTQ_cjs.pipeline; }
33
33
  });
34
34
  exports.Err = Err;
35
35
  exports.Ok = Ok;
package/dist/index.d.cts CHANGED
@@ -1,7 +1,8 @@
1
- import { P as PipelineOptions, a as PipelineStep, b as PipelineRunResult, S as StepResult } from './define-pipeline-BNKM59ML.cjs';
2
- export { c as PipelineError, d as PipelineMode, e as PipelineStepFailure, f as PipelineToken, g as definePipeline } from './define-pipeline-BNKM59ML.cjs';
1
+ import { P as PipelineOptions, a as PipelineStep, b as PipelineRunResult, S as StepResult } from './define-pipeline-CIUhML8D.cjs';
2
+ export { c as PipelineError, d as PipelineExecutionMetadata, e as PipelineMode, f as PipelineStepFailure, g as PipelineToken, h as definePipeline } from './define-pipeline-CIUhML8D.cjs';
3
3
 
4
4
  declare class Pipeline<TContext, TError = unknown> {
5
+ #private;
5
6
  private readonly entries;
6
7
  private readonly options;
7
8
  constructor(options?: PipelineOptions<TContext, TError>);
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { P as PipelineOptions, a as PipelineStep, b as PipelineRunResult, S as StepResult } from './define-pipeline-BNKM59ML.js';
2
- export { c as PipelineError, d as PipelineMode, e as PipelineStepFailure, f as PipelineToken, g as definePipeline } from './define-pipeline-BNKM59ML.js';
1
+ import { P as PipelineOptions, a as PipelineStep, b as PipelineRunResult, S as StepResult } from './define-pipeline-CIUhML8D.js';
2
+ export { c as PipelineError, d as PipelineExecutionMetadata, e as PipelineMode, f as PipelineStepFailure, g as PipelineToken, h as definePipeline } from './define-pipeline-CIUhML8D.js';
3
3
 
4
4
  declare class Pipeline<TContext, TError = unknown> {
5
+ #private;
5
6
  private readonly entries;
6
7
  private readonly options;
7
8
  constructor(options?: PipelineOptions<TContext, TError>);
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Pipeline, pipeline } from './chunk-RVSNXQ6G.js';
1
+ export { Pipeline, pipeline } from './chunk-GTIMFVE5.js';
2
2
 
3
3
  // src/core/define-pipeline.ts
4
4
  var PipelineToken = class {
@@ -1,20 +1,52 @@
1
1
  'use strict';
2
2
 
3
- var chunkHVHE5ZDM_cjs = require('../chunk-HVHE5ZDM.cjs');
3
+ var chunkN7VFWHTQ_cjs = require('../chunk-N7VFWHTQ.cjs');
4
4
  var common = require('@nestjs/common');
5
5
 
6
+ function validatePipelineDefinition(def, index) {
7
+ if (!def.token) {
8
+ throw new Error(
9
+ `Pipeline definition at index ${index} is missing "token". Use definePipeline() to create one.`
10
+ );
11
+ }
12
+ if (!Array.isArray(def.steps) || def.steps.length === 0) {
13
+ throw new Error(
14
+ `Pipeline "${def.token.description ?? index}" has no steps. Provide at least one step class.`
15
+ );
16
+ }
17
+ for (let i = 0; i < def.steps.length; i++) {
18
+ const step = def.steps[i];
19
+ if (typeof step !== "function") {
20
+ throw new Error(
21
+ `Step at index ${i} in pipeline "${def.token.description ?? index}" is not a class/constructor. Expected a Type<PipelineStep>, got ${typeof step}.`
22
+ );
23
+ }
24
+ }
25
+ }
26
+ function isPipelineStep(instance) {
27
+ return typeof instance === "object" && instance !== null && typeof instance.handle === "function";
28
+ }
6
29
  exports.PipelineModule = class PipelineModule {
7
30
  static forRoot(options) {
8
31
  const providers = [];
9
- for (const def of options.pipelines) {
32
+ for (let i = 0; i < options.pipelines.length; i++) {
33
+ const def = options.pipelines[i];
34
+ validatePipelineDefinition(def, i);
10
35
  for (const step of def.steps) {
11
36
  providers.push({ provide: step, useClass: step });
12
37
  }
13
38
  providers.push({
14
39
  provide: def.token.symbol,
15
40
  useFactory: (...stepInstances) => {
16
- const p = new chunkHVHE5ZDM_cjs.Pipeline(def.options ?? {});
17
- for (const instance of stepInstances) {
41
+ const p = new chunkN7VFWHTQ_cjs.Pipeline(def.options ?? {});
42
+ for (let j = 0; j < stepInstances.length; j++) {
43
+ const instance = stepInstances[j];
44
+ if (!isPipelineStep(instance)) {
45
+ const stepName = def.steps[j]?.name ?? String(j);
46
+ throw new Error(
47
+ `Step "${stepName}" in pipeline "${def.token.description}" does not implement PipelineStep. Expected a class with a "handle(ctx)" method.`
48
+ );
49
+ }
18
50
  p.pipe(instance);
19
51
  }
20
52
  return p;
@@ -30,7 +62,7 @@ exports.PipelineModule = class PipelineModule {
30
62
  };
31
63
  }
32
64
  };
33
- exports.PipelineModule = chunkHVHE5ZDM_cjs.__decorateClass([
65
+ exports.PipelineModule = chunkN7VFWHTQ_cjs.__decorateClass([
34
66
  common.Module({})
35
67
  ], exports.PipelineModule);
36
68
  var InjectPipeline = (token) => common.Inject(token.symbol);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/nestjs/pipeline.module.ts","../../src/nestjs/pipeline.decorator.ts"],"names":["PipelineModule","Pipeline","__decorateClass","Module","Inject"],"mappings":";;;;;AAMaA,yBAAN,oBAAA,CAAqB;AAAA,EAC1B,OAAO,QAAQ,OAAA,EAA+C;AAC5D,IAAA,MAAM,YAAwB,EAAC;AAE/B,IAAA,KAAA,MAAW,GAAA,IAAO,QAAQ,SAAA,EAAW;AACnC,MAAA,KAAA,MAAW,IAAA,IAAQ,IAAI,KAAA,EAAO;AAC5B,QAAA,SAAA,CAAU,KAAK,EAAE,OAAA,EAAS,IAAA,EAAM,QAAA,EAAU,MAAM,CAAA;AAAA,MAClD;AAEA,MAAA,SAAA,CAAU,IAAA,CAAK;AAAA,QACb,OAAA,EAAY,IAAI,KAAA,CAAM,MAAA;AAAA,QACtB,UAAA,EAAY,IAAI,aAAA,KAAoD;AAClE,UAAA,MAAM,IAAI,IAAIC,0BAAA,CAAS,GAAA,CAAI,OAAA,IAAW,EAAE,CAAA;AACxC,UAAA,KAAA,MAAW,YAAY,aAAA,EAAe;AACpC,YAAA,CAAA,CAAE,KAAK,QAAQ,CAAA;AAAA,UACjB;AACA,UAAA,OAAO,CAAA;AAAA,QACT,CAAA;AAAA,QACA,QAAQ,GAAA,CAAI;AAAA,OACb,CAAA;AAAA,IACH;AAEA,IAAA,OAAO;AAAA,MACL,MAAA,EAAWD,sBAAA;AAAA,MACX,SAAA;AAAA,MACA,OAAA,EAAW,SAAA;AAAA,MACX,MAAA,EAAW;AAAA,KACb;AAAA,EACF;AACF;AA7BaA,sBAAA,GAANE,iCAAA,CAAA;AAAA,EADNC,aAAA,CAAO,EAAE;AAAA,CAAA,EACGH,sBAAA,CAAA;ACHN,IAAM,cAAA,GAAiB,CAC5B,KAAA,KAC8BI,aAAA,CAAO,MAAM,MAAM","file":"index.cjs","sourcesContent":["import { DynamicModule, Module, Provider } from '@nestjs/common';\nimport { Pipeline } from '../core/pipeline.js';\nimport type { PipelineStep } from '../core/types.js';\nimport type { PipelineModuleOptions } from './pipeline.options.js';\n\n@Module({})\nexport class PipelineModule {\n static forRoot(options: PipelineModuleOptions): DynamicModule {\n const providers: Provider[] = [];\n\n for (const def of options.pipelines) {\n for (const step of def.steps) {\n providers.push({ provide: step, useClass: step });\n }\n\n providers.push({\n provide: def.token.symbol,\n useFactory: (...stepInstances: PipelineStep<unknown, unknown>[]) => {\n const p = new Pipeline(def.options ?? {});\n for (const instance of stepInstances) {\n p.pipe(instance);\n }\n return p;\n },\n inject: def.steps,\n });\n }\n\n return {\n module: PipelineModule,\n providers,\n exports: providers,\n global: true,\n };\n }\n}\n","import { Inject } from '@nestjs/common';\nimport type { PipelineToken } from '../core/define-pipeline.js';\n\nexport const InjectPipeline = <TContext, TError>(\n token: PipelineToken<TContext, TError>,\n): ReturnType<typeof Inject> => Inject(token.symbol);\n"]}
1
+ {"version":3,"sources":["../../src/nestjs/pipeline.module.ts","../../src/nestjs/pipeline.decorator.ts"],"names":["PipelineModule","Pipeline","__decorateClass","Module","Inject"],"mappings":"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","file":"index.cjs","sourcesContent":["import { DynamicModule, Module, Provider, Type } from '@nestjs/common';\nimport { Pipeline } from '../core/pipeline.js';\nimport type { PipelineStep } from '../core/types.js';\nimport type { PipelineDefinition, PipelineModuleOptions } from './pipeline.options.js';\n\nfunction validatePipelineDefinition(\n def: PipelineDefinition,\n index: number,\n): void {\n if (!def.token) {\n throw new Error(\n `Pipeline definition at index ${index} is missing \"token\". Use definePipeline() to create one.`,\n );\n }\n\n if (!Array.isArray(def.steps) || def.steps.length === 0) {\n throw new Error(\n `Pipeline \"${def.token.description ?? index}\" has no steps. Provide at least one step class.`,\n );\n }\n\n for (let i = 0; i < def.steps.length; i++) {\n const step = def.steps[i];\n if (typeof step !== 'function') {\n throw new Error(\n `Step at index ${i} in pipeline \"${def.token.description ?? index}\" is not a class/constructor. ` +\n `Expected a Type<PipelineStep>, got ${typeof step}.`,\n );\n }\n }\n}\n\nfunction isPipelineStep(instance: unknown): instance is PipelineStep<unknown, unknown> {\n return (\n typeof instance === 'object' &&\n instance !== null &&\n typeof (instance as PipelineStep<unknown, unknown>).handle === 'function'\n );\n}\n\n@Module({})\nexport class PipelineModule {\n static forRoot(options: PipelineModuleOptions): DynamicModule {\n const providers: Provider[] = [];\n\n for (let i = 0; i < options.pipelines.length; i++) {\n const def = options.pipelines[i];\n validatePipelineDefinition(def, i);\n\n for (const step of def.steps) {\n providers.push({ provide: step as Type, useClass: step as Type });\n }\n\n providers.push({\n provide: def.token.symbol,\n useFactory: (...stepInstances: unknown[]) => {\n const p = new Pipeline(def.options ?? {});\n\n for (let j = 0; j < stepInstances.length; j++) {\n const instance = stepInstances[j];\n\n if (!isPipelineStep(instance)) {\n const stepName = (def.steps[j] as Type)?.name ?? String(j);\n throw new Error(\n `Step \"${stepName}\" in pipeline \"${def.token.description}\" does not implement PipelineStep. ` +\n `Expected a class with a \"handle(ctx)\" method.`,\n );\n }\n\n p.pipe(instance);\n }\n\n return p;\n },\n inject: def.steps as Type[],\n });\n }\n\n return {\n module: PipelineModule,\n providers,\n exports: providers,\n global: true,\n };\n }\n}\n","import { Inject } from '@nestjs/common';\nimport type { PipelineToken } from '../core/define-pipeline.js';\n\nexport const InjectPipeline = <TContext, TError>(\n token: PipelineToken<TContext, TError>,\n): ReturnType<typeof Inject> => Inject(token.symbol);\n"]}
@@ -1,5 +1,5 @@
1
1
  import { Type, DynamicModule, Inject } from '@nestjs/common';
2
- import { f as PipelineToken, a as PipelineStep, P as PipelineOptions } from '../define-pipeline-BNKM59ML.cjs';
2
+ import { g as PipelineToken, a as PipelineStep, P as PipelineOptions } from '../define-pipeline-CIUhML8D.cjs';
3
3
 
4
4
  interface PipelineDefinition<TContext = unknown, TError = unknown> {
5
5
  token: PipelineToken<TContext, TError>;
@@ -1,5 +1,5 @@
1
1
  import { Type, DynamicModule, Inject } from '@nestjs/common';
2
- import { f as PipelineToken, a as PipelineStep, P as PipelineOptions } from '../define-pipeline-BNKM59ML.js';
2
+ import { g as PipelineToken, a as PipelineStep, P as PipelineOptions } from '../define-pipeline-CIUhML8D.js';
3
3
 
4
4
  interface PipelineDefinition<TContext = unknown, TError = unknown> {
5
5
  token: PipelineToken<TContext, TError>;
@@ -1,10 +1,35 @@
1
- import { __decorateClass, Pipeline } from '../chunk-RVSNXQ6G.js';
1
+ import { __decorateClass, Pipeline } from '../chunk-GTIMFVE5.js';
2
2
  import { Module, Inject } from '@nestjs/common';
3
3
 
4
+ function validatePipelineDefinition(def, index) {
5
+ if (!def.token) {
6
+ throw new Error(
7
+ `Pipeline definition at index ${index} is missing "token". Use definePipeline() to create one.`
8
+ );
9
+ }
10
+ if (!Array.isArray(def.steps) || def.steps.length === 0) {
11
+ throw new Error(
12
+ `Pipeline "${def.token.description ?? index}" has no steps. Provide at least one step class.`
13
+ );
14
+ }
15
+ for (let i = 0; i < def.steps.length; i++) {
16
+ const step = def.steps[i];
17
+ if (typeof step !== "function") {
18
+ throw new Error(
19
+ `Step at index ${i} in pipeline "${def.token.description ?? index}" is not a class/constructor. Expected a Type<PipelineStep>, got ${typeof step}.`
20
+ );
21
+ }
22
+ }
23
+ }
24
+ function isPipelineStep(instance) {
25
+ return typeof instance === "object" && instance !== null && typeof instance.handle === "function";
26
+ }
4
27
  var PipelineModule = class {
5
28
  static forRoot(options) {
6
29
  const providers = [];
7
- for (const def of options.pipelines) {
30
+ for (let i = 0; i < options.pipelines.length; i++) {
31
+ const def = options.pipelines[i];
32
+ validatePipelineDefinition(def, i);
8
33
  for (const step of def.steps) {
9
34
  providers.push({ provide: step, useClass: step });
10
35
  }
@@ -12,7 +37,14 @@ var PipelineModule = class {
12
37
  provide: def.token.symbol,
13
38
  useFactory: (...stepInstances) => {
14
39
  const p = new Pipeline(def.options ?? {});
15
- for (const instance of stepInstances) {
40
+ for (let j = 0; j < stepInstances.length; j++) {
41
+ const instance = stepInstances[j];
42
+ if (!isPipelineStep(instance)) {
43
+ const stepName = def.steps[j]?.name ?? String(j);
44
+ throw new Error(
45
+ `Step "${stepName}" in pipeline "${def.token.description}" does not implement PipelineStep. Expected a class with a "handle(ctx)" method.`
46
+ );
47
+ }
16
48
  p.pipe(instance);
17
49
  }
18
50
  return p;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/nestjs/pipeline.module.ts","../../src/nestjs/pipeline.decorator.ts"],"names":[],"mappings":";;;AAMO,IAAM,iBAAN,MAAqB;AAAA,EAC1B,OAAO,QAAQ,OAAA,EAA+C;AAC5D,IAAA,MAAM,YAAwB,EAAC;AAE/B,IAAA,KAAA,MAAW,GAAA,IAAO,QAAQ,SAAA,EAAW;AACnC,MAAA,KAAA,MAAW,IAAA,IAAQ,IAAI,KAAA,EAAO;AAC5B,QAAA,SAAA,CAAU,KAAK,EAAE,OAAA,EAAS,IAAA,EAAM,QAAA,EAAU,MAAM,CAAA;AAAA,MAClD;AAEA,MAAA,SAAA,CAAU,IAAA,CAAK;AAAA,QACb,OAAA,EAAY,IAAI,KAAA,CAAM,MAAA;AAAA,QACtB,UAAA,EAAY,IAAI,aAAA,KAAoD;AAClE,UAAA,MAAM,IAAI,IAAI,QAAA,CAAS,GAAA,CAAI,OAAA,IAAW,EAAE,CAAA;AACxC,UAAA,KAAA,MAAW,YAAY,aAAA,EAAe;AACpC,YAAA,CAAA,CAAE,KAAK,QAAQ,CAAA;AAAA,UACjB;AACA,UAAA,OAAO,CAAA;AAAA,QACT,CAAA;AAAA,QACA,QAAQ,GAAA,CAAI;AAAA,OACb,CAAA;AAAA,IACH;AAEA,IAAA,OAAO;AAAA,MACL,MAAA,EAAW,cAAA;AAAA,MACX,SAAA;AAAA,MACA,OAAA,EAAW,SAAA;AAAA,MACX,MAAA,EAAW;AAAA,KACb;AAAA,EACF;AACF;AA7Ba,cAAA,GAAN,eAAA,CAAA;AAAA,EADN,MAAA,CAAO,EAAE;AAAA,CAAA,EACG,cAAA,CAAA;ACHN,IAAM,cAAA,GAAiB,CAC5B,KAAA,KAC8B,MAAA,CAAO,MAAM,MAAM","file":"index.js","sourcesContent":["import { DynamicModule, Module, Provider } from '@nestjs/common';\nimport { Pipeline } from '../core/pipeline.js';\nimport type { PipelineStep } from '../core/types.js';\nimport type { PipelineModuleOptions } from './pipeline.options.js';\n\n@Module({})\nexport class PipelineModule {\n static forRoot(options: PipelineModuleOptions): DynamicModule {\n const providers: Provider[] = [];\n\n for (const def of options.pipelines) {\n for (const step of def.steps) {\n providers.push({ provide: step, useClass: step });\n }\n\n providers.push({\n provide: def.token.symbol,\n useFactory: (...stepInstances: PipelineStep<unknown, unknown>[]) => {\n const p = new Pipeline(def.options ?? {});\n for (const instance of stepInstances) {\n p.pipe(instance);\n }\n return p;\n },\n inject: def.steps,\n });\n }\n\n return {\n module: PipelineModule,\n providers,\n exports: providers,\n global: true,\n };\n }\n}\n","import { Inject } from '@nestjs/common';\nimport type { PipelineToken } from '../core/define-pipeline.js';\n\nexport const InjectPipeline = <TContext, TError>(\n token: PipelineToken<TContext, TError>,\n): ReturnType<typeof Inject> => Inject(token.symbol);\n"]}
1
+ {"version":3,"sources":["../../src/nestjs/pipeline.module.ts","../../src/nestjs/pipeline.decorator.ts"],"names":[],"mappings":";;;AAKA,SAAS,0BAAA,CACP,KACA,KAAA,EACM;AACN,EAAA,IAAI,CAAC,IAAI,KAAA,EAAO;AACd,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,gCAAgC,KAAK,CAAA,wDAAA;AAAA,KACvC;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,MAAM,OAAA,CAAQ,GAAA,CAAI,KAAK,CAAA,IAAK,GAAA,CAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG;AACvD,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,UAAA,EAAa,GAAA,CAAI,KAAA,CAAM,WAAA,IAAe,KAAK,CAAA,gDAAA;AAAA,KAC7C;AAAA,EACF;AAEA,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACzC,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,KAAA,CAAM,CAAC,CAAA;AACxB,IAAA,IAAI,OAAO,SAAS,UAAA,EAAY;AAC9B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,cAAA,EAAiB,CAAC,CAAA,cAAA,EAAiB,GAAA,CAAI,MAAM,WAAA,IAAe,KAAK,CAAA,iEAAA,EAC3B,OAAO,IAAI,CAAA,CAAA;AAAA,OACnD;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,eAAe,QAAA,EAA+D;AACrF,EAAA,OACE,OAAO,QAAA,KAAa,QAAA,IACpB,aAAa,IAAA,IACb,OAAQ,SAA4C,MAAA,KAAW,UAAA;AAEnE;AAGO,IAAM,iBAAN,MAAqB;AAAA,EAC1B,OAAO,QAAQ,OAAA,EAA+C;AAC5D,IAAA,MAAM,YAAwB,EAAC;AAE/B,IAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,OAAA,CAAQ,SAAA,CAAU,QAAQ,CAAA,EAAA,EAAK;AACjD,MAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,SAAA,CAAU,CAAC,CAAA;AAC/B,MAAA,0BAAA,CAA2B,KAAK,CAAC,CAAA;AAEjC,MAAA,KAAA,MAAW,IAAA,IAAQ,IAAI,KAAA,EAAO;AAC5B,QAAA,SAAA,CAAU,KAAK,EAAE,OAAA,EAAS,IAAA,EAAc,QAAA,EAAU,MAAc,CAAA;AAAA,MAClE;AAEA,MAAA,SAAA,CAAU,IAAA,CAAK;AAAA,QACb,OAAA,EAAY,IAAI,KAAA,CAAM,MAAA;AAAA,QACtB,UAAA,EAAY,IAAI,aAAA,KAA6B;AAC3C,UAAA,MAAM,IAAI,IAAI,QAAA,CAAS,GAAA,CAAI,OAAA,IAAW,EAAE,CAAA;AAExC,UAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,aAAA,CAAc,QAAQ,CAAA,EAAA,EAAK;AAC7C,YAAA,MAAM,QAAA,GAAW,cAAc,CAAC,CAAA;AAEhC,YAAA,IAAI,CAAC,cAAA,CAAe,QAAQ,CAAA,EAAG;AAC7B,cAAA,MAAM,WAAY,GAAA,CAAI,KAAA,CAAM,CAAC,CAAA,EAAY,IAAA,IAAQ,OAAO,CAAC,CAAA;AACzD,cAAA,MAAM,IAAI,KAAA;AAAA,gBACR,CAAA,MAAA,EAAS,QAAQ,CAAA,eAAA,EAAkB,GAAA,CAAI,MAAM,WAAW,CAAA,gFAAA;AAAA,eAE1D;AAAA,YACF;AAEA,YAAA,CAAA,CAAE,KAAK,QAAQ,CAAA;AAAA,UACjB;AAEA,UAAA,OAAO,CAAA;AAAA,QACT,CAAA;AAAA,QACA,QAAQ,GAAA,CAAI;AAAA,OACb,CAAA;AAAA,IACH;AAEA,IAAA,OAAO;AAAA,MACL,MAAA,EAAW,cAAA;AAAA,MACX,SAAA;AAAA,MACA,OAAA,EAAW,SAAA;AAAA,MACX,MAAA,EAAW;AAAA,KACb;AAAA,EACF;AACF;AA5Ca,cAAA,GAAN,eAAA,CAAA;AAAA,EADN,MAAA,CAAO,EAAE;AAAA,CAAA,EACG,cAAA,CAAA;ACtCN,IAAM,cAAA,GAAiB,CAC5B,KAAA,KAC8B,MAAA,CAAO,MAAM,MAAM","file":"index.js","sourcesContent":["import { DynamicModule, Module, Provider, Type } from '@nestjs/common';\nimport { Pipeline } from '../core/pipeline.js';\nimport type { PipelineStep } from '../core/types.js';\nimport type { PipelineDefinition, PipelineModuleOptions } from './pipeline.options.js';\n\nfunction validatePipelineDefinition(\n def: PipelineDefinition,\n index: number,\n): void {\n if (!def.token) {\n throw new Error(\n `Pipeline definition at index ${index} is missing \"token\". Use definePipeline() to create one.`,\n );\n }\n\n if (!Array.isArray(def.steps) || def.steps.length === 0) {\n throw new Error(\n `Pipeline \"${def.token.description ?? index}\" has no steps. Provide at least one step class.`,\n );\n }\n\n for (let i = 0; i < def.steps.length; i++) {\n const step = def.steps[i];\n if (typeof step !== 'function') {\n throw new Error(\n `Step at index ${i} in pipeline \"${def.token.description ?? index}\" is not a class/constructor. ` +\n `Expected a Type<PipelineStep>, got ${typeof step}.`,\n );\n }\n }\n}\n\nfunction isPipelineStep(instance: unknown): instance is PipelineStep<unknown, unknown> {\n return (\n typeof instance === 'object' &&\n instance !== null &&\n typeof (instance as PipelineStep<unknown, unknown>).handle === 'function'\n );\n}\n\n@Module({})\nexport class PipelineModule {\n static forRoot(options: PipelineModuleOptions): DynamicModule {\n const providers: Provider[] = [];\n\n for (let i = 0; i < options.pipelines.length; i++) {\n const def = options.pipelines[i];\n validatePipelineDefinition(def, i);\n\n for (const step of def.steps) {\n providers.push({ provide: step as Type, useClass: step as Type });\n }\n\n providers.push({\n provide: def.token.symbol,\n useFactory: (...stepInstances: unknown[]) => {\n const p = new Pipeline(def.options ?? {});\n\n for (let j = 0; j < stepInstances.length; j++) {\n const instance = stepInstances[j];\n\n if (!isPipelineStep(instance)) {\n const stepName = (def.steps[j] as Type)?.name ?? String(j);\n throw new Error(\n `Step \"${stepName}\" in pipeline \"${def.token.description}\" does not implement PipelineStep. ` +\n `Expected a class with a \"handle(ctx)\" method.`,\n );\n }\n\n p.pipe(instance);\n }\n\n return p;\n },\n inject: def.steps as Type[],\n });\n }\n\n return {\n module: PipelineModule,\n providers,\n exports: providers,\n global: true,\n };\n }\n}\n","import { Inject } from '@nestjs/common';\nimport type { PipelineToken } from '../core/define-pipeline.js';\n\nexport const InjectPipeline = <TContext, TError>(\n token: PipelineToken<TContext, TError>,\n): ReturnType<typeof Inject> => Inject(token.symbol);\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backendkit-labs/pipeline",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "license": "Apache-2.0",
5
5
  "author": {
6
6
  "name": "BackendKit Labs",
@@ -85,6 +85,7 @@
85
85
  "@eslint/js": "^9.39.4",
86
86
  "@nestjs/common": "^10.0.0",
87
87
  "@nestjs/core": "^10.0.0",
88
+ "@nestjs/testing": "^10.4.22",
88
89
  "@types/node": "^22.0.0",
89
90
  "eslint": "^9.0.0",
90
91
  "reflect-metadata": "^0.2.0",
@@ -94,4 +95,4 @@
94
95
  "typescript-eslint": "^8.59.3",
95
96
  "vitest": "^2.0.0"
96
97
  }
97
- }
98
+ }
@@ -1,109 +0,0 @@
1
- 'use strict';
2
-
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __decorateClass = (decorators, target, key, kind) => {
6
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
7
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
8
- if (decorator = decorators[i])
9
- result = (kind ? decorator(target, key, result) : decorator(result)) || result;
10
- if (kind && result) __defProp(target, key, result);
11
- return result;
12
- };
13
-
14
- // src/core/pipeline.ts
15
- var Pipeline = class {
16
- entries = [];
17
- options;
18
- constructor(options = {}) {
19
- this.options = options;
20
- }
21
- pipe(step) {
22
- this.entries.push({
23
- instance: step,
24
- name: step.stepName ?? step.constructor.name
25
- });
26
- return this;
27
- }
28
- pipeIf(condition, step) {
29
- this.entries.push({
30
- instance: step,
31
- name: step.stepName ?? step.constructor.name,
32
- condition
33
- });
34
- return this;
35
- }
36
- async run(initialCtx) {
37
- const start = Date.now();
38
- const { mode = "stop-on-first", onStep, onStepComplete, onError, onComplete } = this.options;
39
- const executedSteps = [];
40
- const failures = [];
41
- let ctx = initialCtx;
42
- for (const { instance, name, condition } of this.entries) {
43
- if (condition && !condition(ctx)) continue;
44
- try {
45
- await onStep?.(name, ctx);
46
- } catch {
47
- }
48
- const stepStart = Date.now();
49
- const stepResult = await instance.handle(ctx);
50
- const stepMs = Date.now() - stepStart;
51
- if (!stepResult.ok) {
52
- try {
53
- await onError?.(name, stepResult.error);
54
- } catch {
55
- }
56
- failures.push({ step: name, cause: stepResult.error });
57
- if (mode === "stop-on-first") {
58
- return {
59
- ok: false,
60
- error: {
61
- mode,
62
- failedStep: name,
63
- cause: stepResult.error,
64
- executedSteps: [...executedSteps],
65
- durationMs: Date.now() - start,
66
- failures
67
- }
68
- };
69
- }
70
- executedSteps.push(name);
71
- continue;
72
- }
73
- ctx = stepResult.value;
74
- executedSteps.push(name);
75
- try {
76
- await onStepComplete?.(name, ctx, stepMs);
77
- } catch {
78
- }
79
- }
80
- if (failures.length > 0) {
81
- return {
82
- ok: false,
83
- error: {
84
- mode,
85
- failedStep: failures[0].step,
86
- cause: failures[0].cause,
87
- executedSteps: [...executedSteps],
88
- durationMs: Date.now() - start,
89
- failures
90
- }
91
- };
92
- }
93
- const durationMs = Date.now() - start;
94
- try {
95
- await onComplete?.(ctx, durationMs);
96
- } catch {
97
- }
98
- return { ok: true, value: ctx, executedSteps: [...executedSteps], durationMs };
99
- }
100
- };
101
- function pipeline(options) {
102
- return new Pipeline(options);
103
- }
104
-
105
- exports.Pipeline = Pipeline;
106
- exports.__decorateClass = __decorateClass;
107
- exports.pipeline = pipeline;
108
- //# sourceMappingURL=chunk-HVHE5ZDM.cjs.map
109
- //# sourceMappingURL=chunk-HVHE5ZDM.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/pipeline.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAaO,IAAM,WAAN,MAA2C;AAAA,EAC/B,UAAyC,EAAC;AAAA,EAC1C,OAAA;AAAA,EAEjB,WAAA,CAAY,OAAA,GAA6C,EAAC,EAAG;AAC3D,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AAAA,EAEA,KAAK,IAAA,EAA4C;AAC/C,IAAA,IAAA,CAAK,QAAQ,IAAA,CAAK;AAAA,MAChB,QAAA,EAAU,IAAA;AAAA,MACV,IAAA,EAAU,IAAA,CAAK,QAAA,IAAY,IAAA,CAAK,WAAA,CAAY;AAAA,KAC7C,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAA,CAAO,WAAuC,IAAA,EAA4C;AACxF,IAAA,IAAA,CAAK,QAAQ,IAAA,CAAK;AAAA,MAChB,QAAA,EAAW,IAAA;AAAA,MACX,IAAA,EAAW,IAAA,CAAK,QAAA,IAAY,IAAA,CAAK,WAAA,CAAY,IAAA;AAAA,MAC7C;AAAA,KACD,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAM,IAAI,UAAA,EAAoE;AAC5E,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AACvB,IAAA,MAAM,EAAE,OAAO,eAAA,EAAiB,MAAA,EAAQ,gBAAgB,OAAA,EAAS,UAAA,KAAe,IAAA,CAAK,OAAA;AACrF,IAAA,MAAM,gBAA+C,EAAC;AACtD,IAAA,MAAM,WAA+C,EAAC;AACtD,IAAA,IAAM,GAAA,GAAM,UAAA;AAEZ,IAAA,KAAA,MAAW,EAAE,QAAA,EAAU,IAAA,EAAM,SAAA,EAAU,IAAK,KAAK,OAAA,EAAS;AACxD,MAAA,IAAI,SAAA,IAAa,CAAC,SAAA,CAAU,GAAG,CAAA,EAAG;AAElC,MAAA,IAAI;AAAE,QAAA,MAAM,MAAA,GAAS,MAAM,GAAG,CAAA;AAAA,MAAG,CAAA,CAAA,MAAQ;AAAA,MAAa;AACtD,MAAA,MAAM,SAAA,GAAa,KAAK,GAAA,EAAI;AAC5B,MAAA,MAAM,UAAA,GAAa,MAAM,QAAA,CAAS,MAAA,CAAO,GAAG,CAAA;AAC5C,MAAA,MAAM,MAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAEhC,MAAA,IAAI,CAAC,WAAW,EAAA,EAAI;AAClB,QAAA,IAAI;AAAE,UAAA,MAAM,OAAA,GAAU,IAAA,EAAM,UAAA,CAAW,KAAK,CAAA;AAAA,QAAG,CAAA,CAAA,MAAQ;AAAA,QAAa;AACpE,QAAA,QAAA,CAAS,KAAK,EAAE,IAAA,EAAM,MAAM,KAAA,EAAO,UAAA,CAAW,OAAO,CAAA;AAErD,QAAA,IAAI,SAAS,eAAA,EAAiB;AAC5B,UAAA,OAAO;AAAA,YACL,EAAA,EAAO,KAAA;AAAA,YACP,KAAA,EAAO;AAAA,cACL,IAAA;AAAA,cACA,UAAA,EAAe,IAAA;AAAA,cACf,OAAe,UAAA,CAAW,KAAA;AAAA,cAC1B,aAAA,EAAe,CAAC,GAAG,aAAa,CAAA;AAAA,cAChC,UAAA,EAAe,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAAA,cAC5B;AAAA;AACF,WACF;AAAA,QACF;AAGA,QAAA,aAAA,CAAc,KAAK,IAAI,CAAA;AACvB,QAAA;AAAA,MACF;AAEA,MAAA,GAAA,GAAM,UAAA,CAAW,KAAA;AACjB,MAAA,aAAA,CAAc,KAAK,IAAI,CAAA;AACvB,MAAA,IAAI;AAAE,QAAA,MAAM,cAAA,GAAiB,IAAA,EAAM,GAAA,EAAK,MAAM,CAAA;AAAA,MAAG,CAAA,CAAA,MAAQ;AAAA,MAAa;AAAA,IACxE;AAEA,IAAA,IAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AACvB,MAAA,OAAO;AAAA,QACL,EAAA,EAAO,KAAA;AAAA,QACP,KAAA,EAAO;AAAA,UACL,IAAA;AAAA,UACA,UAAA,EAAe,QAAA,CAAS,CAAC,CAAA,CAAE,IAAA;AAAA,UAC3B,KAAA,EAAe,QAAA,CAAS,CAAC,CAAA,CAAE,KAAA;AAAA,UAC3B,aAAA,EAAe,CAAC,GAAG,aAAa,CAAA;AAAA,UAChC,UAAA,EAAe,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAAA,UAC5B;AAAA;AACF,OACF;AAAA,IACF;AAEA,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAChC,IAAA,IAAI;AAAE,MAAA,MAAM,UAAA,GAAa,KAAK,UAAU,CAAA;AAAA,IAAG,CAAA,CAAA,MAAQ;AAAA,IAAa;AAChE,IAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,KAAA,EAAO,GAAA,EAAK,eAAe,CAAC,GAAG,aAAa,CAAA,EAAG,UAAA,EAAW;AAAA,EAC/E;AACF;AAEO,SAAS,SACd,OAAA,EAC4B;AAC5B,EAAA,OAAO,IAAI,SAAS,OAAO,CAAA;AAC7B","file":"chunk-HVHE5ZDM.cjs","sourcesContent":["import type {\n PipelineOptions,\n PipelineRunResult,\n PipelineStep,\n PipelineStepFailure,\n} from './types.js';\n\ninterface StepEntry<TContext, TError> {\n instance: PipelineStep<TContext, TError>;\n name: string;\n condition?: (ctx: TContext) => boolean;\n}\n\nexport class Pipeline<TContext, TError = unknown> {\n private readonly entries: StepEntry<TContext, TError>[] = [];\n private readonly options: PipelineOptions<TContext, TError>;\n\n constructor(options: PipelineOptions<TContext, TError> = {}) {\n this.options = options;\n }\n\n pipe(step: PipelineStep<TContext, TError>): this {\n this.entries.push({\n instance: step,\n name: step.stepName ?? step.constructor.name,\n });\n return this;\n }\n\n pipeIf(condition: (ctx: TContext) => boolean, step: PipelineStep<TContext, TError>): this {\n this.entries.push({\n instance: step,\n name: step.stepName ?? step.constructor.name,\n condition,\n });\n return this;\n }\n\n async run(initialCtx: TContext): Promise<PipelineRunResult<TContext, TError>> {\n const start = Date.now();\n const { mode = 'stop-on-first', onStep, onStepComplete, onError, onComplete } = this.options;\n const executedSteps: string[] = [];\n const failures: PipelineStepFailure<TError>[] = [];\n let ctx = initialCtx;\n\n for (const { instance, name, condition } of this.entries) {\n if (condition && !condition(ctx)) continue;\n\n try { await onStep?.(name, ctx); } catch { /* noop */ }\n const stepStart = Date.now();\n const stepResult = await instance.handle(ctx);\n const stepMs = Date.now() - stepStart;\n\n if (!stepResult.ok) {\n try { await onError?.(name, stepResult.error); } catch { /* noop */ }\n failures.push({ step: name, cause: stepResult.error });\n\n if (mode === 'stop-on-first') {\n return {\n ok: false,\n error: {\n mode,\n failedStep: name,\n cause: stepResult.error,\n executedSteps: [...executedSteps],\n durationMs: Date.now() - start,\n failures,\n },\n };\n }\n\n // collect-all: record the failed step and continue\n executedSteps.push(name);\n continue;\n }\n\n ctx = stepResult.value;\n executedSteps.push(name);\n try { await onStepComplete?.(name, ctx, stepMs); } catch { /* noop */ }\n }\n\n if (failures.length > 0) {\n return {\n ok: false,\n error: {\n mode,\n failedStep: failures[0].step,\n cause: failures[0].cause,\n executedSteps: [...executedSteps],\n durationMs: Date.now() - start,\n failures,\n },\n };\n }\n\n const durationMs = Date.now() - start;\n try { await onComplete?.(ctx, durationMs); } catch { /* noop */ }\n return { ok: true, value: ctx, executedSteps: [...executedSteps], durationMs };\n }\n}\n\nexport function pipeline<TContext, TError = unknown>(\n options?: PipelineOptions<TContext, TError>,\n): Pipeline<TContext, TError> {\n return new Pipeline(options);\n}\n"]}
@@ -1,105 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __decorateClass = (decorators, target, key, kind) => {
4
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
- if (decorator = decorators[i])
7
- result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
- if (kind && result) __defProp(target, key, result);
9
- return result;
10
- };
11
-
12
- // src/core/pipeline.ts
13
- var Pipeline = class {
14
- entries = [];
15
- options;
16
- constructor(options = {}) {
17
- this.options = options;
18
- }
19
- pipe(step) {
20
- this.entries.push({
21
- instance: step,
22
- name: step.stepName ?? step.constructor.name
23
- });
24
- return this;
25
- }
26
- pipeIf(condition, step) {
27
- this.entries.push({
28
- instance: step,
29
- name: step.stepName ?? step.constructor.name,
30
- condition
31
- });
32
- return this;
33
- }
34
- async run(initialCtx) {
35
- const start = Date.now();
36
- const { mode = "stop-on-first", onStep, onStepComplete, onError, onComplete } = this.options;
37
- const executedSteps = [];
38
- const failures = [];
39
- let ctx = initialCtx;
40
- for (const { instance, name, condition } of this.entries) {
41
- if (condition && !condition(ctx)) continue;
42
- try {
43
- await onStep?.(name, ctx);
44
- } catch {
45
- }
46
- const stepStart = Date.now();
47
- const stepResult = await instance.handle(ctx);
48
- const stepMs = Date.now() - stepStart;
49
- if (!stepResult.ok) {
50
- try {
51
- await onError?.(name, stepResult.error);
52
- } catch {
53
- }
54
- failures.push({ step: name, cause: stepResult.error });
55
- if (mode === "stop-on-first") {
56
- return {
57
- ok: false,
58
- error: {
59
- mode,
60
- failedStep: name,
61
- cause: stepResult.error,
62
- executedSteps: [...executedSteps],
63
- durationMs: Date.now() - start,
64
- failures
65
- }
66
- };
67
- }
68
- executedSteps.push(name);
69
- continue;
70
- }
71
- ctx = stepResult.value;
72
- executedSteps.push(name);
73
- try {
74
- await onStepComplete?.(name, ctx, stepMs);
75
- } catch {
76
- }
77
- }
78
- if (failures.length > 0) {
79
- return {
80
- ok: false,
81
- error: {
82
- mode,
83
- failedStep: failures[0].step,
84
- cause: failures[0].cause,
85
- executedSteps: [...executedSteps],
86
- durationMs: Date.now() - start,
87
- failures
88
- }
89
- };
90
- }
91
- const durationMs = Date.now() - start;
92
- try {
93
- await onComplete?.(ctx, durationMs);
94
- } catch {
95
- }
96
- return { ok: true, value: ctx, executedSteps: [...executedSteps], durationMs };
97
- }
98
- };
99
- function pipeline(options) {
100
- return new Pipeline(options);
101
- }
102
-
103
- export { Pipeline, __decorateClass, pipeline };
104
- //# sourceMappingURL=chunk-RVSNXQ6G.js.map
105
- //# sourceMappingURL=chunk-RVSNXQ6G.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/pipeline.ts"],"names":[],"mappings":";;;;;;;;;;;;AAaO,IAAM,WAAN,MAA2C;AAAA,EAC/B,UAAyC,EAAC;AAAA,EAC1C,OAAA;AAAA,EAEjB,WAAA,CAAY,OAAA,GAA6C,EAAC,EAAG;AAC3D,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AAAA,EAEA,KAAK,IAAA,EAA4C;AAC/C,IAAA,IAAA,CAAK,QAAQ,IAAA,CAAK;AAAA,MAChB,QAAA,EAAU,IAAA;AAAA,MACV,IAAA,EAAU,IAAA,CAAK,QAAA,IAAY,IAAA,CAAK,WAAA,CAAY;AAAA,KAC7C,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAA,CAAO,WAAuC,IAAA,EAA4C;AACxF,IAAA,IAAA,CAAK,QAAQ,IAAA,CAAK;AAAA,MAChB,QAAA,EAAW,IAAA;AAAA,MACX,IAAA,EAAW,IAAA,CAAK,QAAA,IAAY,IAAA,CAAK,WAAA,CAAY,IAAA;AAAA,MAC7C;AAAA,KACD,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAM,IAAI,UAAA,EAAoE;AAC5E,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AACvB,IAAA,MAAM,EAAE,OAAO,eAAA,EAAiB,MAAA,EAAQ,gBAAgB,OAAA,EAAS,UAAA,KAAe,IAAA,CAAK,OAAA;AACrF,IAAA,MAAM,gBAA+C,EAAC;AACtD,IAAA,MAAM,WAA+C,EAAC;AACtD,IAAA,IAAM,GAAA,GAAM,UAAA;AAEZ,IAAA,KAAA,MAAW,EAAE,QAAA,EAAU,IAAA,EAAM,SAAA,EAAU,IAAK,KAAK,OAAA,EAAS;AACxD,MAAA,IAAI,SAAA,IAAa,CAAC,SAAA,CAAU,GAAG,CAAA,EAAG;AAElC,MAAA,IAAI;AAAE,QAAA,MAAM,MAAA,GAAS,MAAM,GAAG,CAAA;AAAA,MAAG,CAAA,CAAA,MAAQ;AAAA,MAAa;AACtD,MAAA,MAAM,SAAA,GAAa,KAAK,GAAA,EAAI;AAC5B,MAAA,MAAM,UAAA,GAAa,MAAM,QAAA,CAAS,MAAA,CAAO,GAAG,CAAA;AAC5C,MAAA,MAAM,MAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAEhC,MAAA,IAAI,CAAC,WAAW,EAAA,EAAI;AAClB,QAAA,IAAI;AAAE,UAAA,MAAM,OAAA,GAAU,IAAA,EAAM,UAAA,CAAW,KAAK,CAAA;AAAA,QAAG,CAAA,CAAA,MAAQ;AAAA,QAAa;AACpE,QAAA,QAAA,CAAS,KAAK,EAAE,IAAA,EAAM,MAAM,KAAA,EAAO,UAAA,CAAW,OAAO,CAAA;AAErD,QAAA,IAAI,SAAS,eAAA,EAAiB;AAC5B,UAAA,OAAO;AAAA,YACL,EAAA,EAAO,KAAA;AAAA,YACP,KAAA,EAAO;AAAA,cACL,IAAA;AAAA,cACA,UAAA,EAAe,IAAA;AAAA,cACf,OAAe,UAAA,CAAW,KAAA;AAAA,cAC1B,aAAA,EAAe,CAAC,GAAG,aAAa,CAAA;AAAA,cAChC,UAAA,EAAe,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAAA,cAC5B;AAAA;AACF,WACF;AAAA,QACF;AAGA,QAAA,aAAA,CAAc,KAAK,IAAI,CAAA;AACvB,QAAA;AAAA,MACF;AAEA,MAAA,GAAA,GAAM,UAAA,CAAW,KAAA;AACjB,MAAA,aAAA,CAAc,KAAK,IAAI,CAAA;AACvB,MAAA,IAAI;AAAE,QAAA,MAAM,cAAA,GAAiB,IAAA,EAAM,GAAA,EAAK,MAAM,CAAA;AAAA,MAAG,CAAA,CAAA,MAAQ;AAAA,MAAa;AAAA,IACxE;AAEA,IAAA,IAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AACvB,MAAA,OAAO;AAAA,QACL,EAAA,EAAO,KAAA;AAAA,QACP,KAAA,EAAO;AAAA,UACL,IAAA;AAAA,UACA,UAAA,EAAe,QAAA,CAAS,CAAC,CAAA,CAAE,IAAA;AAAA,UAC3B,KAAA,EAAe,QAAA,CAAS,CAAC,CAAA,CAAE,KAAA;AAAA,UAC3B,aAAA,EAAe,CAAC,GAAG,aAAa,CAAA;AAAA,UAChC,UAAA,EAAe,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAAA,UAC5B;AAAA;AACF,OACF;AAAA,IACF;AAEA,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAChC,IAAA,IAAI;AAAE,MAAA,MAAM,UAAA,GAAa,KAAK,UAAU,CAAA;AAAA,IAAG,CAAA,CAAA,MAAQ;AAAA,IAAa;AAChE,IAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,KAAA,EAAO,GAAA,EAAK,eAAe,CAAC,GAAG,aAAa,CAAA,EAAG,UAAA,EAAW;AAAA,EAC/E;AACF;AAEO,SAAS,SACd,OAAA,EAC4B;AAC5B,EAAA,OAAO,IAAI,SAAS,OAAO,CAAA;AAC7B","file":"chunk-RVSNXQ6G.js","sourcesContent":["import type {\n PipelineOptions,\n PipelineRunResult,\n PipelineStep,\n PipelineStepFailure,\n} from './types.js';\n\ninterface StepEntry<TContext, TError> {\n instance: PipelineStep<TContext, TError>;\n name: string;\n condition?: (ctx: TContext) => boolean;\n}\n\nexport class Pipeline<TContext, TError = unknown> {\n private readonly entries: StepEntry<TContext, TError>[] = [];\n private readonly options: PipelineOptions<TContext, TError>;\n\n constructor(options: PipelineOptions<TContext, TError> = {}) {\n this.options = options;\n }\n\n pipe(step: PipelineStep<TContext, TError>): this {\n this.entries.push({\n instance: step,\n name: step.stepName ?? step.constructor.name,\n });\n return this;\n }\n\n pipeIf(condition: (ctx: TContext) => boolean, step: PipelineStep<TContext, TError>): this {\n this.entries.push({\n instance: step,\n name: step.stepName ?? step.constructor.name,\n condition,\n });\n return this;\n }\n\n async run(initialCtx: TContext): Promise<PipelineRunResult<TContext, TError>> {\n const start = Date.now();\n const { mode = 'stop-on-first', onStep, onStepComplete, onError, onComplete } = this.options;\n const executedSteps: string[] = [];\n const failures: PipelineStepFailure<TError>[] = [];\n let ctx = initialCtx;\n\n for (const { instance, name, condition } of this.entries) {\n if (condition && !condition(ctx)) continue;\n\n try { await onStep?.(name, ctx); } catch { /* noop */ }\n const stepStart = Date.now();\n const stepResult = await instance.handle(ctx);\n const stepMs = Date.now() - stepStart;\n\n if (!stepResult.ok) {\n try { await onError?.(name, stepResult.error); } catch { /* noop */ }\n failures.push({ step: name, cause: stepResult.error });\n\n if (mode === 'stop-on-first') {\n return {\n ok: false,\n error: {\n mode,\n failedStep: name,\n cause: stepResult.error,\n executedSteps: [...executedSteps],\n durationMs: Date.now() - start,\n failures,\n },\n };\n }\n\n // collect-all: record the failed step and continue\n executedSteps.push(name);\n continue;\n }\n\n ctx = stepResult.value;\n executedSteps.push(name);\n try { await onStepComplete?.(name, ctx, stepMs); } catch { /* noop */ }\n }\n\n if (failures.length > 0) {\n return {\n ok: false,\n error: {\n mode,\n failedStep: failures[0].step,\n cause: failures[0].cause,\n executedSteps: [...executedSteps],\n durationMs: Date.now() - start,\n failures,\n },\n };\n }\n\n const durationMs = Date.now() - start;\n try { await onComplete?.(ctx, durationMs); } catch { /* noop */ }\n return { ok: true, value: ctx, executedSteps: [...executedSteps], durationMs };\n }\n}\n\nexport function pipeline<TContext, TError = unknown>(\n options?: PipelineOptions<TContext, TError>,\n): Pipeline<TContext, TError> {\n return new Pipeline(options);\n}\n"]}