@stackra/pipeline 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.
package/dist/index.js ADDED
@@ -0,0 +1,491 @@
1
+ 'use strict';
2
+
3
+ var container = require('@stackra/container');
4
+ var contracts = require('@stackra/contracts');
5
+
6
+ var __defProp = Object.defineProperty;
7
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
+
9
+ // src/errors/pipeline.error.ts
10
+ var PipelineError = class _PipelineError extends Error {
11
+ static {
12
+ __name(this, "PipelineError");
13
+ }
14
+ /**
15
+ * Machine-readable error code for programmatic handling.
16
+ */
17
+ code;
18
+ /**
19
+ * The original error that caused this pipeline error, if any.
20
+ */
21
+ cause;
22
+ /**
23
+ * @param message - Human-readable description of what went wrong
24
+ * @param code - Machine-readable error code
25
+ * @param cause - The original error that triggered this pipeline error
26
+ */
27
+ constructor(message, code = "PIPELINE_ERROR", cause) {
28
+ super(message);
29
+ this.name = "PipelineError";
30
+ this.code = code;
31
+ this.cause = cause;
32
+ Object.setPrototypeOf(this, _PipelineError.prototype);
33
+ }
34
+ };
35
+
36
+ // src/services/pipeline.service.ts
37
+ function _ts_decorate(decorators, target, key, desc) {
38
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
39
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
40
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
41
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
42
+ }
43
+ __name(_ts_decorate, "_ts_decorate");
44
+ function _ts_metadata(k, v) {
45
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
46
+ }
47
+ __name(_ts_metadata, "_ts_metadata");
48
+ function _ts_param(paramIndex, decorator) {
49
+ return function(target, key) {
50
+ decorator(target, key, paramIndex);
51
+ };
52
+ }
53
+ __name(_ts_param, "_ts_param");
54
+ var DEFAULT_METHOD = "handle";
55
+ exports.Pipeline = class Pipeline {
56
+ static {
57
+ __name(this, "Pipeline");
58
+ }
59
+ // ══════════════════════════════════════════════════════════════════════════════
60
+ // Private State
61
+ // ══════════════════════════════════════════════════════════════════════════════
62
+ /** The value flowing through the pipeline. */
63
+ passable;
64
+ /** The ordered list of pipes to execute. */
65
+ pipes = [];
66
+ /** The method name to call on pipe objects. */
67
+ method = DEFAULT_METHOD;
68
+ /** Optional callback invoked after pipeline execution. */
69
+ finallyCallback;
70
+ /** Optional DI container for resolving string pipes. */
71
+ container;
72
+ // ══════════════════════════════════════════════════════════════════════════════
73
+ // Constructor
74
+ // ══════════════════════════════════════════════════════════════════════════════
75
+ /**
76
+ * @param container - Optional application context for resolving string-based pipes.
77
+ * When provided, string pipes are resolved via `container.get(pipeName)`.
78
+ * When omitted, string pipes will throw a PipelineError.
79
+ */
80
+ constructor(container) {
81
+ this.container = container ?? void 0;
82
+ }
83
+ // ══════════════════════════════════════════════════════════════════════════════
84
+ // Fluent API
85
+ // ══════════════════════════════════════════════════════════════════════════════
86
+ /**
87
+ * Set the passable object being sent through the pipeline.
88
+ *
89
+ * @param passable - The value to send through the pipes
90
+ * @returns This pipeline instance for chaining
91
+ */
92
+ send(passable) {
93
+ this.passable = passable;
94
+ return this;
95
+ }
96
+ /**
97
+ * Set the array of pipes (replaces any existing pipes).
98
+ *
99
+ * @param pipes - Array of pipe definitions to process the passable through
100
+ * @returns This pipeline instance for chaining
101
+ */
102
+ through(pipes) {
103
+ this.pipes = [
104
+ ...pipes
105
+ ];
106
+ return this;
107
+ }
108
+ /**
109
+ * Append additional pipes to the pipeline.
110
+ *
111
+ * Unlike `through()` which replaces all pipes, `pipe()` adds to the
112
+ * existing chain. Useful for conditionally extending a pipeline.
113
+ *
114
+ * @param pipes - One or more pipe definitions to append
115
+ * @returns This pipeline instance for chaining
116
+ */
117
+ pipe(...pipes) {
118
+ this.pipes.push(...pipes);
119
+ return this;
120
+ }
121
+ /**
122
+ * Change the handler method name called on pipe objects.
123
+ *
124
+ * By default, the pipeline calls `handle(passable, next)` on pipe objects.
125
+ * Use `via()` to call a different method (e.g., `process`, `transform`).
126
+ *
127
+ * @param method - The method name to invoke on pipe objects
128
+ * @returns This pipeline instance for chaining
129
+ */
130
+ via(method) {
131
+ this.method = method;
132
+ return this;
133
+ }
134
+ /**
135
+ * Register a finally callback invoked after pipeline execution.
136
+ *
137
+ * The callback receives the passable after all pipes and the destination
138
+ * have processed it. Useful for cleanup, logging, or telemetry.
139
+ *
140
+ * @param callback - Function to call with the passable after execution
141
+ * @returns This pipeline instance for chaining
142
+ */
143
+ finally(callback) {
144
+ this.finallyCallback = callback;
145
+ return this;
146
+ }
147
+ /**
148
+ * Execute the pipeline with a final destination closure.
149
+ *
150
+ * The destination is the innermost function — it receives the passable
151
+ * after all pipes have processed it. The pipeline is built using
152
+ * `reduceRight` (equivalent to Laravel's `array_reduce(array_reverse(...))`).
153
+ *
154
+ * @typeParam R - The return type of the destination
155
+ * @param destination - Final handler that produces the pipeline's return value
156
+ * @returns The result of the destination function
157
+ * @throws {PipelineError} When a pipe cannot be resolved or executed
158
+ */
159
+ then(destination) {
160
+ const pipeline = this.pipes.reduceRight((next, pipe) => {
161
+ return (passable) => {
162
+ return this.carry(pipe, passable, next);
163
+ };
164
+ }, this.prepareDestination(destination));
165
+ const result = pipeline(this.passable);
166
+ if (this.finallyCallback) {
167
+ this.finallyCallback(this.passable);
168
+ }
169
+ return result;
170
+ }
171
+ /**
172
+ * Execute the pipeline and return the passable as-is.
173
+ *
174
+ * Equivalent to `then((passable) => passable)`. Useful when the pipeline
175
+ * transforms the passable in place and no final destination is needed.
176
+ *
177
+ * @returns The passable after all pipes have processed it
178
+ */
179
+ thenReturn() {
180
+ return this.then((passable) => passable);
181
+ }
182
+ // ══════════════════════════════════════════════════════════════════════════════
183
+ // Private Helpers
184
+ // ══════════════════════════════════════════════════════════════════════════════
185
+ /**
186
+ * Wrap the destination in a closure matching the pipeline's signature.
187
+ *
188
+ * @param destination - The final handler
189
+ * @returns A closure compatible with the reduce chain
190
+ */
191
+ prepareDestination(destination) {
192
+ return (passable) => {
193
+ return destination(passable);
194
+ };
195
+ }
196
+ /**
197
+ * Invoke a single pipe with the passable and next callback.
198
+ *
199
+ * Resolves the pipe type and delegates to the appropriate handler:
200
+ * - Function → called directly
201
+ * - String → resolved from container, method called
202
+ * - Array (tuple) → first element resolved, rest are params
203
+ * - Object → method called directly
204
+ *
205
+ * @param pipe - The pipe to invoke
206
+ * @param passable - The current passable value
207
+ * @param next - The next function in the chain
208
+ * @returns The result of invoking the pipe
209
+ * @throws {PipelineError} When the pipe cannot be resolved or invoked
210
+ */
211
+ carry(pipe, passable, next) {
212
+ try {
213
+ if (Array.isArray(pipe)) {
214
+ return this.handleTuplePipe(pipe, passable, next);
215
+ }
216
+ if (typeof pipe === "function") {
217
+ return pipe(passable, next);
218
+ }
219
+ if (typeof pipe === "string") {
220
+ return this.handleStringPipe(pipe, passable, next, []);
221
+ }
222
+ if (typeof pipe === "object" && pipe !== null) {
223
+ return this.handleObjectPipe(pipe, passable, next, []);
224
+ }
225
+ throw new PipelineError(`Invalid pipe type: ${typeof pipe}. Expected function, string, object, or tuple.`, "INVALID_PIPE_TYPE");
226
+ } catch (error) {
227
+ if (error instanceof PipelineError) {
228
+ throw error;
229
+ }
230
+ const err = error;
231
+ throw new PipelineError(`Pipe execution failed: ${err.message}`, "PIPE_EXECUTION_FAILED", err);
232
+ }
233
+ }
234
+ /**
235
+ * Handle a tuple pipe by extracting the pipe and its parameters.
236
+ *
237
+ * @param tuple - The [pipe, ...params] tuple
238
+ * @param passable - The current passable
239
+ * @param next - The next function
240
+ * @returns The result of invoking the pipe
241
+ */
242
+ handleTuplePipe(tuple, passable, next) {
243
+ const [pipeEntry, ...params] = tuple;
244
+ if (typeof pipeEntry === "function") {
245
+ return pipeEntry(passable, next);
246
+ }
247
+ if (typeof pipeEntry === "string") {
248
+ return this.handleStringPipe(pipeEntry, passable, next, params);
249
+ }
250
+ if (typeof pipeEntry === "object" && pipeEntry !== null) {
251
+ return this.handleObjectPipe(pipeEntry, passable, next, params);
252
+ }
253
+ throw new PipelineError(`Invalid pipe entry in tuple: ${typeof pipeEntry}.`, "INVALID_PIPE_ENTRY");
254
+ }
255
+ /**
256
+ * Resolve a string pipe from the container and invoke its handler method.
257
+ *
258
+ * @param name - The service name/token to resolve
259
+ * @param passable - The current passable
260
+ * @param next - The next function
261
+ * @param params - Additional parameters to pass to the handler
262
+ * @returns The result of invoking the resolved pipe
263
+ * @throws {PipelineError} When no container is available or resolution fails
264
+ */
265
+ handleStringPipe(name, passable, next, params) {
266
+ if (!this.container) {
267
+ throw new PipelineError(`Cannot resolve pipe "${name}": no DI container available. Provide a container to the Pipeline constructor or use object/function pipes.`, "NO_CONTAINER");
268
+ }
269
+ let pipeInstance;
270
+ try {
271
+ pipeInstance = this.container.get(name);
272
+ } catch (error) {
273
+ const err = error;
274
+ throw new PipelineError(`Failed to resolve pipe "${name}" from the container: ${err.message}`, "PIPE_RESOLUTION_FAILED", err);
275
+ }
276
+ if (!pipeInstance || typeof pipeInstance !== "object") {
277
+ throw new PipelineError(`Resolved pipe "${name}" is not an object. Got: ${typeof pipeInstance}`, "INVALID_RESOLVED_PIPE");
278
+ }
279
+ return this.handleObjectPipe(pipeInstance, passable, next, params);
280
+ }
281
+ /**
282
+ * Invoke a handler method on a pipe object instance.
283
+ *
284
+ * @param pipe - The pipe object instance
285
+ * @param passable - The current passable
286
+ * @param next - The next function
287
+ * @param params - Additional parameters to pass after passable and next
288
+ * @returns The result of invoking the method
289
+ * @throws {PipelineError} When the method does not exist on the pipe
290
+ */
291
+ handleObjectPipe(pipe, passable, next, params) {
292
+ const method = this.method;
293
+ const handler = pipe[method];
294
+ if (typeof handler !== "function") {
295
+ const pipeName = pipe.constructor?.name ?? "anonymous";
296
+ throw new PipelineError(`Pipe "${pipeName}" does not have a "${method}" method.`, "METHOD_NOT_FOUND");
297
+ }
298
+ return handler.call(pipe, passable, next, ...params);
299
+ }
300
+ };
301
+ exports.Pipeline = _ts_decorate([
302
+ container.Injectable(),
303
+ _ts_param(0, container.Optional()),
304
+ _ts_param(0, container.Inject(contracts.APPLICATION)),
305
+ _ts_metadata("design:type", Function),
306
+ _ts_metadata("design:paramtypes", [
307
+ typeof IApplication === "undefined" ? Object : IApplication
308
+ ])
309
+ ], exports.Pipeline);
310
+ function _ts_decorate2(decorators, target, key, desc) {
311
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
312
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
313
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
314
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
315
+ }
316
+ __name(_ts_decorate2, "_ts_decorate");
317
+ function _ts_metadata2(k, v) {
318
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
319
+ }
320
+ __name(_ts_metadata2, "_ts_metadata");
321
+ function _ts_param2(paramIndex, decorator) {
322
+ return function(target, key) {
323
+ decorator(target, key, paramIndex);
324
+ };
325
+ }
326
+ __name(_ts_param2, "_ts_param");
327
+ exports.PipelineHub = class PipelineHub {
328
+ static {
329
+ __name(this, "PipelineHub");
330
+ }
331
+ // ══════════════════════════════════════════════════════════════════════════════
332
+ // Private State
333
+ // ══════════════════════════════════════════════════════════════════════════════
334
+ /** Registry of named pipeline definitions. */
335
+ pipelines = /* @__PURE__ */ new Map();
336
+ /** Default pipeline definition (fallback when no name specified). */
337
+ defaultPipeline;
338
+ /** Optional DI container for creating Pipeline instances. */
339
+ container;
340
+ // ══════════════════════════════════════════════════════════════════════════════
341
+ // Constructor
342
+ // ══════════════════════════════════════════════════════════════════════════════
343
+ /**
344
+ * @param container - Optional application context passed to created Pipeline instances
345
+ */
346
+ constructor(container) {
347
+ this.container = container ?? void 0;
348
+ }
349
+ // ══════════════════════════════════════════════════════════════════════════════
350
+ // Public API
351
+ // ══════════════════════════════════════════════════════════════════════════════
352
+ /**
353
+ * Register the default pipeline definition.
354
+ *
355
+ * The default pipeline is used when `pipe()` is called without a name.
356
+ *
357
+ * @param callback - Pipeline definition that configures and executes a pipeline
358
+ */
359
+ defaults(callback) {
360
+ this.defaultPipeline = callback;
361
+ }
362
+ /**
363
+ * Register a named pipeline definition.
364
+ *
365
+ * @param name - Unique name for this pipeline preset
366
+ * @param callback - Pipeline definition that configures and executes a pipeline
367
+ * @throws {PipelineError} When the name is empty
368
+ */
369
+ pipeline(name, callback) {
370
+ if (!name) {
371
+ throw new PipelineError("Pipeline name cannot be empty.", "INVALID_PIPELINE_NAME");
372
+ }
373
+ this.pipelines.set(name, callback);
374
+ }
375
+ /**
376
+ * Execute a named (or default) pipeline with the given passable.
377
+ *
378
+ * Creates a fresh Pipeline instance, passes it along with the passable
379
+ * to the registered definition, and returns the result.
380
+ *
381
+ * @typeParam T - The type of the passable value
382
+ * @param passable - The value to send through the pipeline
383
+ * @param pipelineName - Optional name of the pipeline preset to execute.
384
+ * Falls back to the default pipeline if omitted.
385
+ * @returns The result of the pipeline execution
386
+ * @throws {PipelineError} When the named pipeline or default is not registered
387
+ */
388
+ pipe(passable, pipelineName) {
389
+ const definition = this.resolveDefinition(pipelineName);
390
+ const pipeline = new exports.Pipeline(this.container);
391
+ return definition(pipeline, passable);
392
+ }
393
+ /**
394
+ * Check if a named pipeline definition exists in the registry.
395
+ *
396
+ * @param name - The pipeline name to check
397
+ * @returns True if the pipeline is registered
398
+ */
399
+ has(name) {
400
+ return this.pipelines.has(name);
401
+ }
402
+ // ══════════════════════════════════════════════════════════════════════════════
403
+ // Private Helpers
404
+ // ══════════════════════════════════════════════════════════════════════════════
405
+ /**
406
+ * Resolve a pipeline definition by name or fall back to the default.
407
+ *
408
+ * @param name - Optional pipeline name
409
+ * @returns The resolved pipeline definition
410
+ * @throws {PipelineError} When the definition cannot be found
411
+ */
412
+ resolveDefinition(name) {
413
+ if (name) {
414
+ const definition = this.pipelines.get(name);
415
+ if (!definition) {
416
+ throw new PipelineError(`Pipeline "${name}" is not registered in the hub.`, "PIPELINE_NOT_FOUND");
417
+ }
418
+ return definition;
419
+ }
420
+ if (!this.defaultPipeline) {
421
+ throw new PipelineError("No default pipeline is registered. Call hub.defaults() or provide a pipeline name.", "NO_DEFAULT_PIPELINE");
422
+ }
423
+ return this.defaultPipeline;
424
+ }
425
+ };
426
+ exports.PipelineHub = _ts_decorate2([
427
+ container.Injectable(),
428
+ _ts_param2(0, container.Optional()),
429
+ _ts_param2(0, container.Inject(contracts.APPLICATION)),
430
+ _ts_metadata2("design:type", Function),
431
+ _ts_metadata2("design:paramtypes", [
432
+ typeof IApplication === "undefined" ? Object : IApplication
433
+ ])
434
+ ], exports.PipelineHub);
435
+
436
+ // src/constants/pipeline.constant.ts
437
+ var PIPELINE_FACTORY = /* @__PURE__ */ Symbol.for("PIPELINE_FACTORY");
438
+
439
+ // src/pipeline.module.ts
440
+ function _ts_decorate3(decorators, target, key, desc) {
441
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
442
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
443
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
444
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
445
+ }
446
+ __name(_ts_decorate3, "_ts_decorate");
447
+ exports.PipelineModule = class _PipelineModule {
448
+ static {
449
+ __name(this, "PipelineModule");
450
+ }
451
+ /**
452
+ * Register the pipeline module globally.
453
+ *
454
+ * @returns Dynamic module definition with PipelineHub and factory
455
+ */
456
+ static forRoot() {
457
+ return {
458
+ module: _PipelineModule,
459
+ global: true,
460
+ providers: [
461
+ // PipelineHub singleton
462
+ exports.PipelineHub,
463
+ // Pipeline factory — creates fresh instances on demand
464
+ {
465
+ provide: PIPELINE_FACTORY,
466
+ useFactory: /* @__PURE__ */ __name((app) => {
467
+ return () => new exports.Pipeline(app);
468
+ }, "useFactory"),
469
+ inject: [
470
+ {
471
+ token: contracts.APPLICATION,
472
+ optional: true
473
+ }
474
+ ]
475
+ }
476
+ ],
477
+ exports: [
478
+ exports.PipelineHub,
479
+ PIPELINE_FACTORY
480
+ ]
481
+ };
482
+ }
483
+ };
484
+ exports.PipelineModule = _ts_decorate3([
485
+ container.Module({})
486
+ ], exports.PipelineModule);
487
+
488
+ exports.PIPELINE_FACTORY = PIPELINE_FACTORY;
489
+ exports.PipelineError = PipelineError;
490
+ //# sourceMappingURL=index.js.map
491
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors/pipeline.error.ts","../src/services/pipeline.service.ts","../src/services/pipeline-hub.service.ts","../src/constants/pipeline.constant.ts","../src/pipeline.module.ts"],"names":["PipelineError","Error","code","cause","message","name","Object","setPrototypeOf","prototype","DEFAULT_METHOD","Pipeline","passable","pipes","method","finallyCallback","container","undefined","send","through","pipe","push","via","finally","callback","then","destination","pipeline","reduceRight","next","carry","prepareDestination","result","thenReturn","Array","isArray","handleTuplePipe","handleStringPipe","handleObjectPipe","error","err","tuple","pipeEntry","params","pipeInstance","get","handler","pipeName","call","PipelineHub","pipelines","Map","defaultPipeline","defaults","set","pipelineName","definition","resolveDefinition","has","PIPELINE_FACTORY","Symbol","for","PipelineModule","forRoot","module","global","providers","provide","useFactory","app","inject","token","APPLICATION","optional","exports"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @file pipeline.error.ts\n * @module @stackra/pipeline/errors\n * @description Pipeline-specific error class for all pipeline operation failures.\n * Thrown when a pipe cannot be resolved, the pipeline is misconfigured,\n * or a pipe throws during execution.\n */\n\n// ════════════════════════════════════════════════════════════════════════════════\n// Error Class\n// ════════════════════════════════════════════════════════════════════════════════\n\n/**\n * Error thrown when a pipeline operation fails.\n *\n * Covers scenarios such as:\n * - Pipe resolution failure (string pipe not found in container)\n * - Invalid pipe type provided\n * - Pipeline method not found on a pipe object\n * - Pipe execution failure (wrapped original error)\n *\n * @example\n * ```typescript\n * throw new PipelineError(\n * 'Pipe \"ValidateInput\" could not be resolved from the container.',\n * 'PIPE_RESOLUTION_FAILED',\n * );\n * ```\n */\nexport class PipelineError extends Error {\n /**\n * Machine-readable error code for programmatic handling.\n */\n public readonly code: string;\n\n /**\n * The original error that caused this pipeline error, if any.\n */\n public override readonly cause?: Error;\n\n /**\n * @param message - Human-readable description of what went wrong\n * @param code - Machine-readable error code\n * @param cause - The original error that triggered this pipeline error\n */\n public constructor(message: string, code: string = 'PIPELINE_ERROR', cause?: Error) {\n super(message);\n this.name = 'PipelineError';\n this.code = code;\n this.cause = cause;\n Object.setPrototypeOf(this, PipelineError.prototype);\n }\n}\n","/**\n * @file pipeline.service.ts\n * @module @stackra/pipeline/services\n * @description Laravel-style middleware Pipeline with fluent API and DI container integration.\n * Implements the classic array_reduce(reverse(pipes), carry(), destination) pattern\n * for composing middleware chains with type safety.\n */\n\nimport { Injectable, Optional, Inject } from '@stackra/container';\nimport { APPLICATION } from '@stackra/contracts';\nimport type { IApplication } from '@stackra/contracts';\n\nimport type { PipeType, PipeTuple } from '../interfaces';\nimport { PipelineError } from '../errors';\n\n// ════════════════════════════════════════════════════════════════════════════════\n// Constants\n// ════════════════════════════════════════════════════════════════════════════════\n\n/** Default method name called on pipe objects. */\nconst DEFAULT_METHOD = 'handle';\n\n// ════════════════════════════════════════════════════════════════════════════════\n// Pipeline Class\n// ════════════════════════════════════════════════════════════════════════════════\n\n/**\n * Laravel-style middleware pipeline with fluent API.\n *\n * Composes a chain of pipes (middleware) that process a passable value\n * sequentially. Each pipe can inspect, modify, or short-circuit the passable\n * before passing it to the next pipe in the chain.\n *\n * The pipeline supports four pipe types:\n * - **Closure** — `(passable, next) => result`\n * - **String** — resolved from the DI container by name\n * - **Object** — instance with a handler method (default: `handle`)\n * - **Tuple** — `[pipe, ...params]` for parameterized pipes\n *\n * Core algorithm (mirrors Laravel):\n * ```\n * pipeline = array_reduce(reverse(pipes), carry(), prepareDestination(destination))\n * return pipeline(passable)\n * ```\n *\n * @typeParam TPassable - The type of value flowing through the pipeline\n * @typeParam TReturn - The type returned by the pipeline's `then()` method\n *\n * @example\n * ```typescript\n * const result = new Pipeline<Request, Response>(container)\n * .send(request)\n * .through([AuthMiddleware, LoggingMiddleware])\n * .then((req) => handleRequest(req));\n * ```\n */\n@Injectable()\nexport class Pipeline<TPassable = unknown, TReturn = TPassable> {\n // ══════════════════════════════════════════════════════════════════════════════\n // Private State\n // ══════════════════════════════════════════════════════════════════════════════\n\n /** The value flowing through the pipeline. */\n private passable!: TPassable;\n\n /** The ordered list of pipes to execute. */\n private pipes: PipeType[] = [];\n\n /** The method name to call on pipe objects. */\n private method: string = DEFAULT_METHOD;\n\n /** Optional callback invoked after pipeline execution. */\n private finallyCallback?: (passable: TPassable) => void;\n\n /** Optional DI container for resolving string pipes. */\n private readonly container?: IApplication;\n\n // ══════════════════════════════════════════════════════════════════════════════\n // Constructor\n // ══════════════════════════════════════════════════════════════════════════════\n\n /**\n * @param container - Optional application context for resolving string-based pipes.\n * When provided, string pipes are resolved via `container.get(pipeName)`.\n * When omitted, string pipes will throw a PipelineError.\n */\n public constructor(@Optional() @Inject(APPLICATION) container?: IApplication) {\n this.container = container ?? undefined;\n }\n\n // ══════════════════════════════════════════════════════════════════════════════\n // Fluent API\n // ══════════════════════════════════════════════════════════════════════════════\n\n /**\n * Set the passable object being sent through the pipeline.\n *\n * @param passable - The value to send through the pipes\n * @returns This pipeline instance for chaining\n */\n public send(passable: TPassable): this {\n this.passable = passable;\n return this;\n }\n\n /**\n * Set the array of pipes (replaces any existing pipes).\n *\n * @param pipes - Array of pipe definitions to process the passable through\n * @returns This pipeline instance for chaining\n */\n public through(pipes: PipeType[]): this {\n this.pipes = [...pipes];\n return this;\n }\n\n /**\n * Append additional pipes to the pipeline.\n *\n * Unlike `through()` which replaces all pipes, `pipe()` adds to the\n * existing chain. Useful for conditionally extending a pipeline.\n *\n * @param pipes - One or more pipe definitions to append\n * @returns This pipeline instance for chaining\n */\n public pipe(...pipes: PipeType[]): this {\n this.pipes.push(...pipes);\n return this;\n }\n\n /**\n * Change the handler method name called on pipe objects.\n *\n * By default, the pipeline calls `handle(passable, next)` on pipe objects.\n * Use `via()` to call a different method (e.g., `process`, `transform`).\n *\n * @param method - The method name to invoke on pipe objects\n * @returns This pipeline instance for chaining\n */\n public via(method: string): this {\n this.method = method;\n return this;\n }\n\n /**\n * Register a finally callback invoked after pipeline execution.\n *\n * The callback receives the passable after all pipes and the destination\n * have processed it. Useful for cleanup, logging, or telemetry.\n *\n * @param callback - Function to call with the passable after execution\n * @returns This pipeline instance for chaining\n */\n public finally(callback: (passable: TPassable) => void): this {\n this.finallyCallback = callback;\n return this;\n }\n\n /**\n * Execute the pipeline with a final destination closure.\n *\n * The destination is the innermost function — it receives the passable\n * after all pipes have processed it. The pipeline is built using\n * `reduceRight` (equivalent to Laravel's `array_reduce(array_reverse(...))`).\n *\n * @typeParam R - The return type of the destination\n * @param destination - Final handler that produces the pipeline's return value\n * @returns The result of the destination function\n * @throws {PipelineError} When a pipe cannot be resolved or executed\n */\n public then<R = TReturn>(destination: (passable: TPassable) => R): R {\n const pipeline = this.pipes.reduceRight<(passable: TPassable) => R>((next, pipe) => {\n return (passable: TPassable): R => {\n return this.carry(pipe, passable, next as (passable: TPassable) => unknown) as R;\n };\n }, this.prepareDestination(destination));\n\n const result = pipeline(this.passable);\n\n if (this.finallyCallback) {\n this.finallyCallback(this.passable);\n }\n\n return result;\n }\n\n /**\n * Execute the pipeline and return the passable as-is.\n *\n * Equivalent to `then((passable) => passable)`. Useful when the pipeline\n * transforms the passable in place and no final destination is needed.\n *\n * @returns The passable after all pipes have processed it\n */\n public thenReturn(): TPassable {\n return this.then((passable) => passable) as unknown as TPassable;\n }\n\n // ══════════════════════════════════════════════════════════════════════════════\n // Private Helpers\n // ══════════════════════════════════════════════════════════════════════════════\n\n /**\n * Wrap the destination in a closure matching the pipeline's signature.\n *\n * @param destination - The final handler\n * @returns A closure compatible with the reduce chain\n */\n private prepareDestination<R>(\n destination: (passable: TPassable) => R\n ): (passable: TPassable) => R {\n return (passable: TPassable): R => {\n return destination(passable);\n };\n }\n\n /**\n * Invoke a single pipe with the passable and next callback.\n *\n * Resolves the pipe type and delegates to the appropriate handler:\n * - Function → called directly\n * - String → resolved from container, method called\n * - Array (tuple) → first element resolved, rest are params\n * - Object → method called directly\n *\n * @param pipe - The pipe to invoke\n * @param passable - The current passable value\n * @param next - The next function in the chain\n * @returns The result of invoking the pipe\n * @throws {PipelineError} When the pipe cannot be resolved or invoked\n */\n private carry(\n pipe: PipeType,\n passable: TPassable,\n next: (passable: TPassable) => unknown\n ): unknown {\n try {\n // Tuple pipe: [pipe, ...params]\n if (Array.isArray(pipe)) {\n return this.handleTuplePipe(pipe as PipeTuple, passable, next);\n }\n\n // Function pipe\n if (typeof pipe === 'function') {\n return (pipe as (passable: TPassable, next: (passable: TPassable) => unknown) => unknown)(\n passable,\n next\n );\n }\n\n // String pipe (container resolution)\n if (typeof pipe === 'string') {\n return this.handleStringPipe(pipe, passable, next, []);\n }\n\n // Object pipe (instance with handler method)\n if (typeof pipe === 'object' && pipe !== null) {\n return this.handleObjectPipe(pipe, passable, next, []);\n }\n\n throw new PipelineError(\n `Invalid pipe type: ${typeof pipe}. Expected function, string, object, or tuple.`,\n 'INVALID_PIPE_TYPE'\n );\n } catch (error: unknown) {\n if (error instanceof PipelineError) {\n throw error;\n }\n const err = error as Error;\n throw new PipelineError(\n `Pipe execution failed: ${err.message}`,\n 'PIPE_EXECUTION_FAILED',\n err\n );\n }\n }\n\n /**\n * Handle a tuple pipe by extracting the pipe and its parameters.\n *\n * @param tuple - The [pipe, ...params] tuple\n * @param passable - The current passable\n * @param next - The next function\n * @returns The result of invoking the pipe\n */\n private handleTuplePipe(\n tuple: PipeTuple,\n passable: TPassable,\n next: (passable: TPassable) => unknown\n ): unknown {\n const [pipeEntry, ...params] = tuple;\n\n if (typeof pipeEntry === 'function') {\n return (\n pipeEntry as (passable: TPassable, next: (passable: TPassable) => unknown) => unknown\n )(passable, next);\n }\n\n if (typeof pipeEntry === 'string') {\n return this.handleStringPipe(pipeEntry, passable, next, params);\n }\n\n if (typeof pipeEntry === 'object' && pipeEntry !== null) {\n return this.handleObjectPipe(pipeEntry, passable, next, params);\n }\n\n throw new PipelineError(\n `Invalid pipe entry in tuple: ${typeof pipeEntry}.`,\n 'INVALID_PIPE_ENTRY'\n );\n }\n\n /**\n * Resolve a string pipe from the container and invoke its handler method.\n *\n * @param name - The service name/token to resolve\n * @param passable - The current passable\n * @param next - The next function\n * @param params - Additional parameters to pass to the handler\n * @returns The result of invoking the resolved pipe\n * @throws {PipelineError} When no container is available or resolution fails\n */\n private handleStringPipe(\n name: string,\n passable: TPassable,\n next: (passable: TPassable) => unknown,\n params: unknown[]\n ): unknown {\n if (!this.container) {\n throw new PipelineError(\n `Cannot resolve pipe \"${name}\": no DI container available. ` +\n 'Provide a container to the Pipeline constructor or use object/function pipes.',\n 'NO_CONTAINER'\n );\n }\n\n let pipeInstance: unknown;\n try {\n pipeInstance = this.container.get(name);\n } catch (error: unknown) {\n const err = error as Error;\n throw new PipelineError(\n `Failed to resolve pipe \"${name}\" from the container: ${err.message}`,\n 'PIPE_RESOLUTION_FAILED',\n err\n );\n }\n\n if (!pipeInstance || typeof pipeInstance !== 'object') {\n throw new PipelineError(\n `Resolved pipe \"${name}\" is not an object. Got: ${typeof pipeInstance}`,\n 'INVALID_RESOLVED_PIPE'\n );\n }\n\n return this.handleObjectPipe(pipeInstance, passable, next, params);\n }\n\n /**\n * Invoke a handler method on a pipe object instance.\n *\n * @param pipe - The pipe object instance\n * @param passable - The current passable\n * @param next - The next function\n * @param params - Additional parameters to pass after passable and next\n * @returns The result of invoking the method\n * @throws {PipelineError} When the method does not exist on the pipe\n */\n private handleObjectPipe(\n pipe: object,\n passable: TPassable,\n next: (passable: TPassable) => unknown,\n params: unknown[]\n ): unknown {\n const method = this.method;\n const handler = (pipe as Record<string, unknown>)[method];\n\n if (typeof handler !== 'function') {\n const pipeName = pipe.constructor?.name ?? 'anonymous';\n throw new PipelineError(\n `Pipe \"${pipeName}\" does not have a \"${method}\" method.`,\n 'METHOD_NOT_FOUND'\n );\n }\n\n return (handler as (...args: unknown[]) => unknown).call(pipe, passable, next, ...params);\n }\n}\n","/**\n * @file pipeline-hub.service.ts\n * @module @stackra/pipeline/services\n * @description Named pipeline presets registry (Hub).\n * Allows registering named pipeline definitions and executing them by name.\n * Equivalent to Laravel's Pipeline Hub concept for reusable pipeline configurations.\n */\n\nimport { Injectable, Optional, Inject } from '@stackra/container';\nimport { APPLICATION } from '@stackra/contracts';\nimport type { IApplication } from '@stackra/contracts';\n\nimport type { PipelineDefinition } from '../interfaces';\nimport { Pipeline } from './pipeline.service';\nimport { PipelineError } from '../errors';\n\n// ════════════════════════════════════════════════════════════════════════════════\n// Hub Class\n// ════════════════════════════════════════════════════════════════════════════════\n\n/**\n * Named pipeline presets registry.\n *\n * The Hub stores named pipeline definitions that can be executed by name.\n * This enables reusable pipeline configurations that multiple consumers\n * can invoke without knowing the pipe chain details.\n *\n * A `defaults` pipeline is used as the fallback when no specific pipeline\n * name is provided to `pipe()`.\n *\n * @example\n * ```typescript\n * const hub = new PipelineHub(container);\n *\n * hub.pipeline('order-validation', (pipeline, passable) =>\n * pipeline\n * .send(passable)\n * .through([ValidateStock, ValidatePayment, ValidateAddress])\n * .thenReturn()\n * );\n *\n * const result = hub.pipe(orderData, 'order-validation');\n * ```\n */\n@Injectable()\nexport class PipelineHub {\n // ══════════════════════════════════════════════════════════════════════════════\n // Private State\n // ══════════════════════════════════════════════════════════════════════════════\n\n /** Registry of named pipeline definitions. */\n private readonly pipelines: Map<string, PipelineDefinition> = new Map();\n\n /** Default pipeline definition (fallback when no name specified). */\n private defaultPipeline?: PipelineDefinition;\n\n /** Optional DI container for creating Pipeline instances. */\n private readonly container?: IApplication;\n\n // ══════════════════════════════════════════════════════════════════════════════\n // Constructor\n // ══════════════════════════════════════════════════════════════════════════════\n\n /**\n * @param container - Optional application context passed to created Pipeline instances\n */\n public constructor(@Optional() @Inject(APPLICATION) container?: IApplication) {\n this.container = container ?? undefined;\n }\n\n // ══════════════════════════════════════════════════════════════════════════════\n // Public API\n // ══════════════════════════════════════════════════════════════════════════════\n\n /**\n * Register the default pipeline definition.\n *\n * The default pipeline is used when `pipe()` is called without a name.\n *\n * @param callback - Pipeline definition that configures and executes a pipeline\n */\n public defaults(callback: PipelineDefinition): void {\n this.defaultPipeline = callback;\n }\n\n /**\n * Register a named pipeline definition.\n *\n * @param name - Unique name for this pipeline preset\n * @param callback - Pipeline definition that configures and executes a pipeline\n * @throws {PipelineError} When the name is empty\n */\n public pipeline(name: string, callback: PipelineDefinition): void {\n if (!name) {\n throw new PipelineError('Pipeline name cannot be empty.', 'INVALID_PIPELINE_NAME');\n }\n this.pipelines.set(name, callback);\n }\n\n /**\n * Execute a named (or default) pipeline with the given passable.\n *\n * Creates a fresh Pipeline instance, passes it along with the passable\n * to the registered definition, and returns the result.\n *\n * @typeParam T - The type of the passable value\n * @param passable - The value to send through the pipeline\n * @param pipelineName - Optional name of the pipeline preset to execute.\n * Falls back to the default pipeline if omitted.\n * @returns The result of the pipeline execution\n * @throws {PipelineError} When the named pipeline or default is not registered\n */\n public pipe<T>(passable: T, pipelineName?: string): unknown {\n const definition = this.resolveDefinition(pipelineName);\n const pipeline = new Pipeline(this.container);\n\n return definition(pipeline, passable);\n }\n\n /**\n * Check if a named pipeline definition exists in the registry.\n *\n * @param name - The pipeline name to check\n * @returns True if the pipeline is registered\n */\n public has(name: string): boolean {\n return this.pipelines.has(name);\n }\n\n // ══════════════════════════════════════════════════════════════════════════════\n // Private Helpers\n // ══════════════════════════════════════════════════════════════════════════════\n\n /**\n * Resolve a pipeline definition by name or fall back to the default.\n *\n * @param name - Optional pipeline name\n * @returns The resolved pipeline definition\n * @throws {PipelineError} When the definition cannot be found\n */\n private resolveDefinition(name?: string): PipelineDefinition {\n if (name) {\n const definition = this.pipelines.get(name);\n if (!definition) {\n throw new PipelineError(\n `Pipeline \"${name}\" is not registered in the hub.`,\n 'PIPELINE_NOT_FOUND'\n );\n }\n return definition;\n }\n\n if (!this.defaultPipeline) {\n throw new PipelineError(\n 'No default pipeline is registered. Call hub.defaults() or provide a pipeline name.',\n 'NO_DEFAULT_PIPELINE'\n );\n }\n\n return this.defaultPipeline;\n }\n}\n","/**\n * @file pipeline.constant.ts\n * @module @stackra/pipeline/constants\n * @description DI tokens and constants for the pipeline module.\n */\n\n// ════════════════════════════════════════════════════════════════════════════════\n// DI Tokens\n// ════════════════════════════════════════════════════════════════════════════════\n\n/**\n * DI token for the pipeline factory function.\n *\n * Inject this token to obtain a factory that creates fresh Pipeline instances.\n * Each call to the factory produces a new Pipeline with no residual state.\n *\n * @example\n * ```typescript\n * @Inject(PIPELINE_FACTORY) private readonly createPipeline: PipelineFactory\n * ```\n */\nexport const PIPELINE_FACTORY = Symbol.for('PIPELINE_FACTORY');\n","/**\n * @file pipeline.module.ts\n * @module @stackra/pipeline\n * @description DI module for the pipeline system.\n * Registers the PipelineHub as a global singleton and provides a\n * PIPELINE_FACTORY token for creating fresh Pipeline instances on demand.\n *\n * The Pipeline class itself is NOT a singleton — each usage creates a new\n * instance via the factory. This ensures no state leaks between usages.\n */\n\nimport { Module } from '@stackra/container';\nimport type { DynamicModule } from '@stackra/contracts';\nimport { APPLICATION } from '@stackra/contracts';\nimport type { IApplication } from '@stackra/contracts';\n\nimport { PipelineHub } from './services';\nimport { Pipeline } from './services';\nimport { PIPELINE_FACTORY } from './constants';\n\n// ════════════════════════════════════════════════════════════════════════════════\n// Module\n// ════════════════════════════════════════════════════════════════════════════════\n\n/**\n * Pipeline DI module.\n *\n * Provides:\n * - `PipelineHub` — global singleton for named pipeline presets\n * - `PIPELINE_FACTORY` — factory function for creating fresh Pipeline instances\n *\n * The Pipeline is intentionally NOT registered as a singleton. Each call to\n * the factory produces a new instance with clean state. This mirrors Laravel's\n * pattern where `app(Pipeline::class)` always returns a fresh pipeline.\n *\n * @example\n * ```typescript\n * import { PipelineModule } from '@stackra/pipeline';\n *\n * @Module({\n * imports: [PipelineModule.forRoot()],\n * })\n * export class AppModule {}\n * ```\n *\n * @example\n * ```typescript\n * // Injecting the factory in a service\n * @Injectable()\n * class OrderService {\n * constructor(\n * @Inject(PIPELINE_FACTORY) private readonly createPipeline: PipelineFactory,\n * ) {}\n *\n * processOrder(order: Order): Order {\n * return this.createPipeline<Order>()\n * .send(order)\n * .through([ValidateOrder, CalculateTax, ApplyDiscount])\n * .thenReturn();\n * }\n * }\n * ```\n */\n@Module({})\nexport class PipelineModule {\n /**\n * Register the pipeline module globally.\n *\n * @returns Dynamic module definition with PipelineHub and factory\n */\n public static forRoot(): DynamicModule {\n return {\n module: PipelineModule,\n global: true,\n providers: [\n // PipelineHub singleton\n PipelineHub,\n\n // Pipeline factory — creates fresh instances on demand\n {\n provide: PIPELINE_FACTORY,\n useFactory: (app?: IApplication) => {\n return <T>(): Pipeline<T> => new Pipeline<T>(app);\n },\n inject: [{ token: APPLICATION, optional: true }],\n },\n ],\n exports: [PipelineHub, PIPELINE_FACTORY],\n };\n }\n}\n"]}