@cueapi/cuechain 0.0.1 → 0.0.2
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/README.md +27 -0
- package/dist/index.cjs +16 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +16 -4
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -208,6 +208,33 @@ const desc = myPipeline.describe()
|
|
|
208
208
|
|
|
209
209
|
Schemas are serialized as JSON Schema via `zod-to-json-schema`.
|
|
210
210
|
|
|
211
|
+
## Handling Failures Safely
|
|
212
|
+
|
|
213
|
+
When a step fails, the `Failure` object contains the raw `input`, `output`, and `error.message` from the step. This is useful for debugging but may contain sensitive data if your step processes PII, API keys, or other confidential information.
|
|
214
|
+
|
|
215
|
+
**Before exposing failures to end users, logs, or monitoring systems, sanitize the failure object:**
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
const result = await myPipeline.run(input)
|
|
219
|
+
|
|
220
|
+
if (!result.ok) {
|
|
221
|
+
// Internal logging — full context
|
|
222
|
+
logger.debug('Pipeline failure', result.failure)
|
|
223
|
+
|
|
224
|
+
// User-facing — redact raw data
|
|
225
|
+
const safeError = {
|
|
226
|
+
step: result.failure.step,
|
|
227
|
+
reason: result.failure.reason,
|
|
228
|
+
type: result.failure.type,
|
|
229
|
+
attempts: result.failure.attempts,
|
|
230
|
+
// Omit: input, output (may contain sensitive data)
|
|
231
|
+
}
|
|
232
|
+
return { error: safeError }
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
A pipeline-level `onFailure` redaction hook is planned for v0.2.
|
|
237
|
+
|
|
211
238
|
## Cuechain + CueAPI
|
|
212
239
|
|
|
213
240
|
Cuechain verifies contracts between steps. [CueAPI](https://cueapi.ai) verifies outcomes against reality. Use one, use both.
|
package/dist/index.cjs
CHANGED
|
@@ -4,13 +4,20 @@ var zodToJsonSchema = require('zod-to-json-schema');
|
|
|
4
4
|
|
|
5
5
|
// src/step.ts
|
|
6
6
|
function defineStep(config) {
|
|
7
|
+
const maxAttempts = config.retry?.maxAttempts ?? 1;
|
|
8
|
+
if (maxAttempts < 1 || maxAttempts > 20) {
|
|
9
|
+
throw new RangeError(`maxAttempts must be between 1 and 20 (got ${maxAttempts})`);
|
|
10
|
+
}
|
|
11
|
+
if (!Number.isInteger(maxAttempts)) {
|
|
12
|
+
throw new RangeError(`maxAttempts must be an integer (got ${maxAttempts})`);
|
|
13
|
+
}
|
|
7
14
|
return {
|
|
8
15
|
name: config.name,
|
|
9
16
|
inputSchema: config.input,
|
|
10
17
|
outputSchema: config.output,
|
|
11
18
|
gates: config.gates ?? [],
|
|
12
19
|
retry: {
|
|
13
|
-
maxAttempts
|
|
20
|
+
maxAttempts,
|
|
14
21
|
on: config.retry?.on ?? ["schema", "gate"]
|
|
15
22
|
},
|
|
16
23
|
run: config.run
|
|
@@ -26,9 +33,14 @@ function formatZodError(error) {
|
|
|
26
33
|
}
|
|
27
34
|
function runGates(gates, output) {
|
|
28
35
|
for (const gate of gates) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
try {
|
|
37
|
+
const result = gate(output);
|
|
38
|
+
if (!result.ok) {
|
|
39
|
+
return { reason: result.reason, context: result.context };
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
43
|
+
return { reason: `Gate threw: ${reason}`, thrown: true };
|
|
32
44
|
}
|
|
33
45
|
}
|
|
34
46
|
return null;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/step.ts","../src/runner.ts","../src/types.ts","../src/pipeline.ts"],"names":["zodToJsonSchema"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { z } from 'zod'\nimport type { Step, StepConfig } from './types.js'\n\n/**\n * Define a pipeline step with typed input/output schemas, optional quality gates,\n * and retry configuration.\n *\n * @example\n * ```ts\n * const extractTitle = defineStep({\n * name: 'extract-title',\n * input: z.object({ draft: z.string() }),\n * output: z.object({ title: z.string().max(80) }),\n * gates: [\n * (out) => out.title.includes(':')\n * ? { ok: false, reason: 'title must not contain colons' }\n * : { ok: true }\n * ],\n * retry: { maxAttempts: 3, on: ['schema', 'gate'] },\n * run: async (input, failureContext) => {\n * return { title: 'extracted title' };\n * }\n * });\n * ```\n */\nexport function defineStep<TInput, TOutput>(\n config: StepConfig<TInput, TOutput>,\n): Step<TInput, TOutput> {\n return {\n name: config.name,\n inputSchema: config.input as z.ZodType<TInput>,\n outputSchema: config.output as z.ZodType<TOutput>,\n gates: config.gates ?? [],\n retry: {\n maxAttempts: config.retry?.maxAttempts ?? 1,\n on: config.retry?.on ?? ['schema', 'gate'],\n },\n run: config.run,\n }\n}\n","import type { ZodError } from 'zod'\nimport type { FailureContext, Failure, Gate, Result, Step } from './types.js'\n\n/**\n * Format a ZodError into a human-readable reason string.\n */\nfunction formatZodError(error: ZodError): string {\n return error.issues\n .map((issue) => {\n const path = issue.path.length > 0 ? `${issue.path.join('.')}: ` : ''\n return `${path}${issue.message}`\n })\n .join('; ')\n}\n\n/**\n * Run quality gates against a step's output.\n * Returns the first failure, or null if all gates pass.\n */\nfunction runGates(gates: Gate<unknown>[], output: unknown): { reason: string; context?: unknown } | null {\n for (const gate of gates) {\n const result = gate(output)\n if (!result.ok) {\n return { reason: result.reason, context: result.context }\n }\n }\n return null\n}\n\n/**\n * Execute a single step with retry logic.\n * Returns a Result with the validated output or a structured failure.\n */\nexport async function executeStep(\n step: Step<unknown, unknown>,\n input: unknown,\n): Promise<Result<unknown>> {\n // Validate input schema\n const inputResult = step.inputSchema.safeParse(input)\n if (!inputResult.success) {\n return {\n ok: false,\n failure: {\n step: step.name,\n reason: `Input validation failed: ${formatZodError(inputResult.error)}`,\n type: 'schema_input',\n attempts: 0,\n input,\n },\n }\n }\n\n const validatedInput = inputResult.data\n const maxAttempts = step.retry.maxAttempts\n const retryOn = new Set(step.retry.on)\n\n let failureContext: FailureContext | undefined\n let lastFailure: Failure | undefined\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n // Run the step function\n const rawOutput = await step.run(validatedInput, failureContext)\n\n // Validate output schema\n const outputResult = step.outputSchema.safeParse(rawOutput)\n if (!outputResult.success) {\n const reason = `Output validation failed: ${formatZodError(outputResult.error)}`\n lastFailure = {\n step: step.name,\n reason,\n type: 'schema_output',\n attempts: attempt,\n input: validatedInput,\n output: rawOutput,\n }\n\n if (attempt < maxAttempts && retryOn.has('schema')) {\n failureContext = { reason, attempt, type: 'schema' }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n\n const validatedOutput = outputResult.data\n\n // Run quality gates\n const gateFailure = runGates(step.gates as Gate<unknown>[], validatedOutput)\n if (gateFailure) {\n lastFailure = {\n step: step.name,\n reason: `Gate failed: ${gateFailure.reason}`,\n type: 'gate',\n attempts: attempt,\n input: validatedInput,\n output: validatedOutput,\n }\n\n if (attempt < maxAttempts && retryOn.has('gate')) {\n failureContext = {\n reason: gateFailure.reason,\n attempt,\n type: 'gate',\n }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n\n // Success\n return { ok: true, value: validatedOutput }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n lastFailure = {\n step: step.name,\n reason: `Exception: ${reason}`,\n type: 'exception',\n attempts: attempt,\n input: validatedInput,\n }\n\n if (attempt < maxAttempts && retryOn.has('exception')) {\n failureContext = { reason, attempt, type: 'exception' }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n }\n\n // Should not reach here, but safety net\n return {\n ok: false,\n failure: lastFailure ?? {\n step: step.name,\n reason: 'Unknown failure',\n type: 'exception',\n attempts: maxAttempts,\n input: validatedInput,\n },\n }\n}\n","import type { z } from 'zod'\n\n/**\n * Result of a quality gate check.\n * Gates are pure synchronous functions — no LLM calls, no async.\n */\nexport type GateResult =\n | { ok: true }\n | { ok: false; reason: string; context?: unknown }\n\n/**\n * A quality gate function. Receives a step's validated output\n * and returns a pass/fail verdict.\n */\nexport type Gate<T> = (output: T) => GateResult\n\n/**\n * Context passed to a step's `run` function on retry attempts.\n * Contains the reason the previous attempt failed, the attempt number,\n * and the failure type so the step can self-correct.\n */\nexport interface FailureContext {\n reason: string\n attempt: number\n type: 'schema' | 'gate' | 'exception'\n}\n\n/**\n * What kinds of failures should trigger a retry.\n */\nexport type RetryOn = 'schema' | 'gate' | 'exception'\n\n/**\n * Retry configuration for a step.\n */\nexport interface RetryConfig {\n /** Maximum number of attempts (including the first). Default: 1 (no retry). */\n maxAttempts?: number\n /** Which failure types trigger a retry. Default: ['schema', 'gate'] */\n on?: RetryOn[]\n}\n\n/**\n * Configuration for defining a step.\n * Generic parameters are inferred value types, not Zod schema types.\n */\nexport interface StepConfig<TInput, TOutput> {\n name: string\n input: z.ZodType<TInput, z.ZodTypeDef, unknown>\n output: z.ZodType<TOutput, z.ZodTypeDef, unknown>\n gates?: Gate<TOutput>[]\n retry?: RetryConfig\n run: (input: TInput, failureContext?: FailureContext) => Promise<TOutput>\n}\n\n/**\n * A defined step — the unit of work in a pipeline.\n * Generic parameters are value types (what the step consumes/produces),\n * not Zod schema types. This allows compile-time type checking of\n * step-to-step handoffs without Zod subclass compatibility issues.\n */\nexport interface Step<TInput = unknown, TOutput = unknown> {\n readonly name: string\n readonly inputSchema: z.ZodType<TInput>\n readonly outputSchema: z.ZodType<TOutput>\n readonly gates: Gate<TOutput>[]\n readonly retry: Required<RetryConfig>\n readonly run: (input: TInput, failureContext?: FailureContext) => Promise<TOutput>\n}\n\n/**\n * Structured failure returned when a pipeline halts.\n */\nexport interface Failure {\n step: string\n reason: string\n type: 'schema_input' | 'schema_output' | 'gate' | 'exception'\n attempts: number\n input: unknown\n output?: unknown\n}\n\n/**\n * Pipeline result type. Success or structured failure.\n */\nexport type Result<T> =\n | { ok: true; value: T }\n | { ok: false; failure: Failure }\n\n/**\n * Metadata returned by pipeline.describe().\n */\nexport interface PipelineDescription {\n name: string\n steps: StepDescription[]\n}\n\nexport interface StepDescription {\n name: string\n inputSchema: Record<string, unknown>\n outputSchema: Record<string, unknown>\n gates: number\n retry: Required<RetryConfig>\n}\n\n/**\n * Error thrown by .runOrThrow() on pipeline failure.\n */\nexport class PipelineError extends Error {\n constructor(public readonly failure: Failure) {\n super(`Pipeline failed at step \"${failure.step}\": ${failure.reason}`)\n this.name = 'PipelineError'\n }\n}\n","import { zodToJsonSchema } from 'zod-to-json-schema'\nimport { executeStep } from './runner.js'\nimport type { Failure, PipelineDescription, Result, Step } from './types.js'\nimport { PipelineError } from './types.js'\n\n/**\n * A compiled pipeline that can be run, described, or run-or-throw.\n */\nexport interface Pipeline<TInput, TOutput> {\n /** Pipeline name */\n readonly name: string\n\n /**\n * Add a step to the pipeline. Returns a new pipeline with the step appended.\n * TypeScript enforces that the step's input type matches the previous step's output type.\n */\n step<TNext>(step: Step<TOutput, TNext>): Pipeline<TInput, TNext>\n\n /**\n * Run the pipeline. Returns a Result — either { ok: true, value } or { ok: false, failure }.\n * No exceptions escape.\n */\n run(input: TInput): Promise<Result<TOutput>>\n\n /**\n * Run the pipeline and throw a PipelineError on failure.\n * For developers who prefer thrown exceptions over Result types.\n */\n runOrThrow(input: TInput): Promise<TOutput>\n\n /**\n * Return structured metadata about the pipeline:\n * step names, JSON Schema for inputs/outputs, gate count, retry config.\n */\n describe(): PipelineDescription\n}\n\n/**\n * Initial pipeline builder returned by pipeline().\n * The first .step() call sets both input and output types.\n */\nexport interface PipelineBuilder {\n readonly name: string\n step<TInput, TOutput>(step: Step<TInput, TOutput>): Pipeline<TInput, TOutput>\n}\n\n/**\n * Internal pipeline implementation.\n */\nclass PipelineImpl<TInput, TOutput> implements Pipeline<TInput, TOutput> {\n constructor(\n readonly name: string,\n // Use Step<unknown, unknown> internally; type safety is at the API boundary\n private readonly steps: Step<unknown, unknown>[],\n ) {}\n\n step<TNext>(newStep: Step<TOutput, TNext>): Pipeline<TInput, TNext> {\n return new PipelineImpl<TInput, TNext>(this.name, [\n ...this.steps,\n newStep as Step<unknown, unknown>,\n ])\n }\n\n async run(input: TInput): Promise<Result<TOutput>> {\n let current: unknown = input\n\n for (const step of this.steps) {\n const result = await executeStep(step, current)\n if (!result.ok) {\n return result as { ok: false; failure: Failure }\n }\n current = result.value\n }\n\n return { ok: true, value: current as TOutput }\n }\n\n async runOrThrow(input: TInput): Promise<TOutput> {\n const result = await this.run(input)\n if (!result.ok) {\n throw new PipelineError(result.failure)\n }\n return result.value\n }\n\n describe(): PipelineDescription {\n return {\n name: this.name,\n steps: this.steps.map((step) => ({\n name: step.name,\n inputSchema: zodToJsonSchema(step.inputSchema) as Record<string, unknown>,\n outputSchema: zodToJsonSchema(step.outputSchema) as Record<string, unknown>,\n gates: step.gates.length,\n retry: step.retry,\n })),\n }\n }\n}\n\n/**\n * Create a new pipeline with the given name.\n * Call .step() to add the first step — this sets the pipeline's input type.\n *\n * @example\n * ```ts\n * const myPipeline = pipeline('my-pipeline')\n * .step(loadData)\n * .step(transformData)\n * .step(validateResult);\n *\n * const result = await myPipeline.run({ source: 'input.json' });\n * ```\n */\nexport function pipeline(name: string): PipelineBuilder {\n return {\n name,\n step<TInput, TOutput>(firstStep: Step<TInput, TOutput>): Pipeline<TInput, TOutput> {\n return new PipelineImpl<TInput, TOutput>(name, [firstStep as Step<unknown, unknown>])\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/step.ts","../src/runner.ts","../src/types.ts","../src/pipeline.ts"],"names":["zodToJsonSchema"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { z } from 'zod'\nimport type { Step, StepConfig } from './types.js'\n\n/**\n * Define a pipeline step with typed input/output schemas, optional quality gates,\n * and retry configuration.\n *\n * @example\n * ```ts\n * const extractTitle = defineStep({\n * name: 'extract-title',\n * input: z.object({ draft: z.string() }),\n * output: z.object({ title: z.string().max(80) }),\n * gates: [\n * (out) => out.title.includes(':')\n * ? { ok: false, reason: 'title must not contain colons' }\n * : { ok: true }\n * ],\n * retry: { maxAttempts: 3, on: ['schema', 'gate'] },\n * run: async (input, failureContext) => {\n * return { title: 'extracted title' };\n * }\n * });\n * ```\n */\nexport function defineStep<TInput, TOutput>(\n config: StepConfig<TInput, TOutput>,\n): Step<TInput, TOutput> {\n const maxAttempts = config.retry?.maxAttempts ?? 1\n\n if (maxAttempts < 1 || maxAttempts > 20) {\n throw new RangeError(`maxAttempts must be between 1 and 20 (got ${maxAttempts})`)\n }\n\n if (!Number.isInteger(maxAttempts)) {\n throw new RangeError(`maxAttempts must be an integer (got ${maxAttempts})`)\n }\n\n return {\n name: config.name,\n inputSchema: config.input as z.ZodType<TInput>,\n outputSchema: config.output as z.ZodType<TOutput>,\n gates: config.gates ?? [],\n retry: {\n maxAttempts,\n on: config.retry?.on ?? ['schema', 'gate'],\n },\n run: config.run,\n }\n}\n","import type { ZodError } from 'zod'\nimport type { Failure, FailureContext, Gate, Result, Step } from './types.js'\n\n/**\n * Format a ZodError into a human-readable reason string.\n */\nfunction formatZodError(error: ZodError): string {\n return error.issues\n .map((issue) => {\n const path = issue.path.length > 0 ? `${issue.path.join('.')}: ` : ''\n return `${path}${issue.message}`\n })\n .join('; ')\n}\n\n/**\n * Run quality gates against a step's output.\n * Returns the first failure, or null if all gates pass.\n */\nfunction runGates(\n gates: Gate<unknown>[],\n output: unknown,\n): { reason: string; context?: unknown; thrown?: boolean } | null {\n for (const gate of gates) {\n try {\n const result = gate(output)\n if (!result.ok) {\n return { reason: result.reason, context: result.context }\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n return { reason: `Gate threw: ${reason}`, thrown: true }\n }\n }\n return null\n}\n\n/**\n * Execute a single step with retry logic.\n * Returns a Result with the validated output or a structured failure.\n */\nexport async function executeStep(\n step: Step<unknown, unknown>,\n input: unknown,\n): Promise<Result<unknown>> {\n // Validate input schema\n const inputResult = step.inputSchema.safeParse(input)\n if (!inputResult.success) {\n return {\n ok: false,\n failure: {\n step: step.name,\n reason: `Input validation failed: ${formatZodError(inputResult.error)}`,\n type: 'schema_input',\n attempts: 0,\n input,\n },\n }\n }\n\n const validatedInput = inputResult.data\n const maxAttempts = step.retry.maxAttempts\n const retryOn = new Set(step.retry.on)\n\n let failureContext: FailureContext | undefined\n let lastFailure: Failure | undefined\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n // Run the step function\n const rawOutput = await step.run(validatedInput, failureContext)\n\n // Validate output schema\n const outputResult = step.outputSchema.safeParse(rawOutput)\n if (!outputResult.success) {\n const reason = `Output validation failed: ${formatZodError(outputResult.error)}`\n lastFailure = {\n step: step.name,\n reason,\n type: 'schema_output',\n attempts: attempt,\n input: validatedInput,\n output: rawOutput,\n }\n\n if (attempt < maxAttempts && retryOn.has('schema')) {\n failureContext = { reason, attempt, type: 'schema' }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n\n const validatedOutput = outputResult.data\n\n // Run quality gates\n const gateFailure = runGates(step.gates as Gate<unknown>[], validatedOutput)\n if (gateFailure) {\n lastFailure = {\n step: step.name,\n reason: `Gate failed: ${gateFailure.reason}`,\n type: 'gate',\n attempts: attempt,\n input: validatedInput,\n output: validatedOutput,\n }\n\n if (attempt < maxAttempts && retryOn.has('gate')) {\n failureContext = {\n reason: gateFailure.reason,\n attempt,\n type: 'gate',\n }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n\n // Success\n return { ok: true, value: validatedOutput }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n lastFailure = {\n step: step.name,\n reason: `Exception: ${reason}`,\n type: 'exception',\n attempts: attempt,\n input: validatedInput,\n }\n\n if (attempt < maxAttempts && retryOn.has('exception')) {\n failureContext = { reason, attempt, type: 'exception' }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n }\n\n // Should not reach here, but safety net\n return {\n ok: false,\n failure: lastFailure ?? {\n step: step.name,\n reason: 'Unknown failure',\n type: 'exception',\n attempts: maxAttempts,\n input: validatedInput,\n },\n }\n}\n","import type { z } from 'zod'\n\n/**\n * Result of a quality gate check.\n * Gates are pure synchronous functions — no LLM calls, no async.\n */\nexport type GateResult = { ok: true } | { ok: false; reason: string; context?: unknown }\n\n/**\n * A quality gate function. Receives a step's validated output\n * and returns a pass/fail verdict.\n */\nexport type Gate<T> = (output: T) => GateResult\n\n/**\n * Context passed to a step's `run` function on retry attempts.\n * Contains the reason the previous attempt failed, the attempt number,\n * and the failure type so the step can self-correct.\n */\nexport interface FailureContext {\n reason: string\n attempt: number\n type: 'schema' | 'gate' | 'exception'\n}\n\n/**\n * What kinds of failures should trigger a retry.\n */\nexport type RetryOn = 'schema' | 'gate' | 'exception'\n\n/**\n * Retry configuration for a step.\n */\nexport interface RetryConfig {\n /** Maximum number of attempts (including the first). Default: 1 (no retry). */\n maxAttempts?: number\n /** Which failure types trigger a retry. Default: ['schema', 'gate'] */\n on?: RetryOn[]\n}\n\n/**\n * Configuration for defining a step.\n * Generic parameters are inferred value types, not Zod schema types.\n */\nexport interface StepConfig<TInput, TOutput> {\n name: string\n input: z.ZodType<TInput, z.ZodTypeDef, unknown>\n output: z.ZodType<TOutput, z.ZodTypeDef, unknown>\n gates?: Gate<TOutput>[]\n retry?: RetryConfig\n run: (input: TInput, failureContext?: FailureContext) => Promise<TOutput>\n}\n\n/**\n * A defined step — the unit of work in a pipeline.\n * Generic parameters are value types (what the step consumes/produces),\n * not Zod schema types. This allows compile-time type checking of\n * step-to-step handoffs without Zod subclass compatibility issues.\n */\nexport interface Step<TInput = unknown, TOutput = unknown> {\n readonly name: string\n readonly inputSchema: z.ZodType<TInput>\n readonly outputSchema: z.ZodType<TOutput>\n readonly gates: Gate<TOutput>[]\n readonly retry: Required<RetryConfig>\n readonly run: (input: TInput, failureContext?: FailureContext) => Promise<TOutput>\n}\n\n/**\n * Structured failure returned when a pipeline halts.\n */\nexport interface Failure {\n step: string\n reason: string\n type: 'schema_input' | 'schema_output' | 'gate' | 'exception'\n attempts: number\n input: unknown\n output?: unknown\n}\n\n/**\n * Pipeline result type. Success or structured failure.\n */\nexport type Result<T> = { ok: true; value: T } | { ok: false; failure: Failure }\n\n/**\n * Metadata returned by pipeline.describe().\n */\nexport interface PipelineDescription {\n name: string\n steps: StepDescription[]\n}\n\nexport interface StepDescription {\n name: string\n inputSchema: Record<string, unknown>\n outputSchema: Record<string, unknown>\n gates: number\n retry: Required<RetryConfig>\n}\n\n/**\n * Error thrown by .runOrThrow() on pipeline failure.\n */\nexport class PipelineError extends Error {\n constructor(public readonly failure: Failure) {\n super(`Pipeline failed at step \"${failure.step}\": ${failure.reason}`)\n this.name = 'PipelineError'\n }\n}\n","import { zodToJsonSchema } from 'zod-to-json-schema'\nimport { executeStep } from './runner.js'\nimport type { Failure, PipelineDescription, Result, Step } from './types.js'\nimport { PipelineError } from './types.js'\n\n/**\n * A compiled pipeline that can be run, described, or run-or-throw.\n */\nexport interface Pipeline<TInput, TOutput> {\n /** Pipeline name */\n readonly name: string\n\n /**\n * Add a step to the pipeline. Returns a new pipeline with the step appended.\n * TypeScript enforces that the step's input type matches the previous step's output type.\n */\n step<TNext>(step: Step<TOutput, TNext>): Pipeline<TInput, TNext>\n\n /**\n * Run the pipeline. Returns a Result — either { ok: true, value } or { ok: false, failure }.\n * No exceptions escape.\n */\n run(input: TInput): Promise<Result<TOutput>>\n\n /**\n * Run the pipeline and throw a PipelineError on failure.\n * For developers who prefer thrown exceptions over Result types.\n */\n runOrThrow(input: TInput): Promise<TOutput>\n\n /**\n * Return structured metadata about the pipeline:\n * step names, JSON Schema for inputs/outputs, gate count, retry config.\n */\n describe(): PipelineDescription\n}\n\n/**\n * Initial pipeline builder returned by pipeline().\n * The first .step() call sets both input and output types.\n */\nexport interface PipelineBuilder {\n readonly name: string\n step<TInput, TOutput>(step: Step<TInput, TOutput>): Pipeline<TInput, TOutput>\n}\n\n/**\n * Internal pipeline implementation.\n */\nclass PipelineImpl<TInput, TOutput> implements Pipeline<TInput, TOutput> {\n constructor(\n readonly name: string,\n // Use Step<unknown, unknown> internally; type safety is at the API boundary\n private readonly steps: Step<unknown, unknown>[],\n ) {}\n\n step<TNext>(newStep: Step<TOutput, TNext>): Pipeline<TInput, TNext> {\n return new PipelineImpl<TInput, TNext>(this.name, [\n ...this.steps,\n newStep as Step<unknown, unknown>,\n ])\n }\n\n async run(input: TInput): Promise<Result<TOutput>> {\n let current: unknown = input\n\n for (const step of this.steps) {\n const result = await executeStep(step, current)\n if (!result.ok) {\n return result as { ok: false; failure: Failure }\n }\n current = result.value\n }\n\n return { ok: true, value: current as TOutput }\n }\n\n async runOrThrow(input: TInput): Promise<TOutput> {\n const result = await this.run(input)\n if (!result.ok) {\n throw new PipelineError(result.failure)\n }\n return result.value\n }\n\n describe(): PipelineDescription {\n return {\n name: this.name,\n steps: this.steps.map((step) => ({\n name: step.name,\n inputSchema: zodToJsonSchema(step.inputSchema) as Record<string, unknown>,\n outputSchema: zodToJsonSchema(step.outputSchema) as Record<string, unknown>,\n gates: step.gates.length,\n retry: step.retry,\n })),\n }\n }\n}\n\n/**\n * Create a new pipeline with the given name.\n * Call .step() to add the first step — this sets the pipeline's input type.\n *\n * @example\n * ```ts\n * const myPipeline = pipeline('my-pipeline')\n * .step(loadData)\n * .step(transformData)\n * .step(validateResult);\n *\n * const result = await myPipeline.run({ source: 'input.json' });\n * ```\n */\nexport function pipeline(name: string): PipelineBuilder {\n return {\n name,\n step<TInput, TOutput>(firstStep: Step<TInput, TOutput>): Pipeline<TInput, TOutput> {\n return new PipelineImpl<TInput, TOutput>(name, [firstStep as Step<unknown, unknown>])\n },\n }\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,20 @@ import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
|
2
2
|
|
|
3
3
|
// src/step.ts
|
|
4
4
|
function defineStep(config) {
|
|
5
|
+
const maxAttempts = config.retry?.maxAttempts ?? 1;
|
|
6
|
+
if (maxAttempts < 1 || maxAttempts > 20) {
|
|
7
|
+
throw new RangeError(`maxAttempts must be between 1 and 20 (got ${maxAttempts})`);
|
|
8
|
+
}
|
|
9
|
+
if (!Number.isInteger(maxAttempts)) {
|
|
10
|
+
throw new RangeError(`maxAttempts must be an integer (got ${maxAttempts})`);
|
|
11
|
+
}
|
|
5
12
|
return {
|
|
6
13
|
name: config.name,
|
|
7
14
|
inputSchema: config.input,
|
|
8
15
|
outputSchema: config.output,
|
|
9
16
|
gates: config.gates ?? [],
|
|
10
17
|
retry: {
|
|
11
|
-
maxAttempts
|
|
18
|
+
maxAttempts,
|
|
12
19
|
on: config.retry?.on ?? ["schema", "gate"]
|
|
13
20
|
},
|
|
14
21
|
run: config.run
|
|
@@ -24,9 +31,14 @@ function formatZodError(error) {
|
|
|
24
31
|
}
|
|
25
32
|
function runGates(gates, output) {
|
|
26
33
|
for (const gate of gates) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
try {
|
|
35
|
+
const result = gate(output);
|
|
36
|
+
if (!result.ok) {
|
|
37
|
+
return { reason: result.reason, context: result.context };
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
41
|
+
return { reason: `Gate threw: ${reason}`, thrown: true };
|
|
30
42
|
}
|
|
31
43
|
}
|
|
32
44
|
return null;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/step.ts","../src/runner.ts","../src/types.ts","../src/pipeline.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { z } from 'zod'\nimport type { Step, StepConfig } from './types.js'\n\n/**\n * Define a pipeline step with typed input/output schemas, optional quality gates,\n * and retry configuration.\n *\n * @example\n * ```ts\n * const extractTitle = defineStep({\n * name: 'extract-title',\n * input: z.object({ draft: z.string() }),\n * output: z.object({ title: z.string().max(80) }),\n * gates: [\n * (out) => out.title.includes(':')\n * ? { ok: false, reason: 'title must not contain colons' }\n * : { ok: true }\n * ],\n * retry: { maxAttempts: 3, on: ['schema', 'gate'] },\n * run: async (input, failureContext) => {\n * return { title: 'extracted title' };\n * }\n * });\n * ```\n */\nexport function defineStep<TInput, TOutput>(\n config: StepConfig<TInput, TOutput>,\n): Step<TInput, TOutput> {\n return {\n name: config.name,\n inputSchema: config.input as z.ZodType<TInput>,\n outputSchema: config.output as z.ZodType<TOutput>,\n gates: config.gates ?? [],\n retry: {\n maxAttempts: config.retry?.maxAttempts ?? 1,\n on: config.retry?.on ?? ['schema', 'gate'],\n },\n run: config.run,\n }\n}\n","import type { ZodError } from 'zod'\nimport type { FailureContext, Failure, Gate, Result, Step } from './types.js'\n\n/**\n * Format a ZodError into a human-readable reason string.\n */\nfunction formatZodError(error: ZodError): string {\n return error.issues\n .map((issue) => {\n const path = issue.path.length > 0 ? `${issue.path.join('.')}: ` : ''\n return `${path}${issue.message}`\n })\n .join('; ')\n}\n\n/**\n * Run quality gates against a step's output.\n * Returns the first failure, or null if all gates pass.\n */\nfunction runGates(gates: Gate<unknown>[], output: unknown): { reason: string; context?: unknown } | null {\n for (const gate of gates) {\n const result = gate(output)\n if (!result.ok) {\n return { reason: result.reason, context: result.context }\n }\n }\n return null\n}\n\n/**\n * Execute a single step with retry logic.\n * Returns a Result with the validated output or a structured failure.\n */\nexport async function executeStep(\n step: Step<unknown, unknown>,\n input: unknown,\n): Promise<Result<unknown>> {\n // Validate input schema\n const inputResult = step.inputSchema.safeParse(input)\n if (!inputResult.success) {\n return {\n ok: false,\n failure: {\n step: step.name,\n reason: `Input validation failed: ${formatZodError(inputResult.error)}`,\n type: 'schema_input',\n attempts: 0,\n input,\n },\n }\n }\n\n const validatedInput = inputResult.data\n const maxAttempts = step.retry.maxAttempts\n const retryOn = new Set(step.retry.on)\n\n let failureContext: FailureContext | undefined\n let lastFailure: Failure | undefined\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n // Run the step function\n const rawOutput = await step.run(validatedInput, failureContext)\n\n // Validate output schema\n const outputResult = step.outputSchema.safeParse(rawOutput)\n if (!outputResult.success) {\n const reason = `Output validation failed: ${formatZodError(outputResult.error)}`\n lastFailure = {\n step: step.name,\n reason,\n type: 'schema_output',\n attempts: attempt,\n input: validatedInput,\n output: rawOutput,\n }\n\n if (attempt < maxAttempts && retryOn.has('schema')) {\n failureContext = { reason, attempt, type: 'schema' }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n\n const validatedOutput = outputResult.data\n\n // Run quality gates\n const gateFailure = runGates(step.gates as Gate<unknown>[], validatedOutput)\n if (gateFailure) {\n lastFailure = {\n step: step.name,\n reason: `Gate failed: ${gateFailure.reason}`,\n type: 'gate',\n attempts: attempt,\n input: validatedInput,\n output: validatedOutput,\n }\n\n if (attempt < maxAttempts && retryOn.has('gate')) {\n failureContext = {\n reason: gateFailure.reason,\n attempt,\n type: 'gate',\n }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n\n // Success\n return { ok: true, value: validatedOutput }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n lastFailure = {\n step: step.name,\n reason: `Exception: ${reason}`,\n type: 'exception',\n attempts: attempt,\n input: validatedInput,\n }\n\n if (attempt < maxAttempts && retryOn.has('exception')) {\n failureContext = { reason, attempt, type: 'exception' }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n }\n\n // Should not reach here, but safety net\n return {\n ok: false,\n failure: lastFailure ?? {\n step: step.name,\n reason: 'Unknown failure',\n type: 'exception',\n attempts: maxAttempts,\n input: validatedInput,\n },\n }\n}\n","import type { z } from 'zod'\n\n/**\n * Result of a quality gate check.\n * Gates are pure synchronous functions — no LLM calls, no async.\n */\nexport type GateResult =\n | { ok: true }\n | { ok: false; reason: string; context?: unknown }\n\n/**\n * A quality gate function. Receives a step's validated output\n * and returns a pass/fail verdict.\n */\nexport type Gate<T> = (output: T) => GateResult\n\n/**\n * Context passed to a step's `run` function on retry attempts.\n * Contains the reason the previous attempt failed, the attempt number,\n * and the failure type so the step can self-correct.\n */\nexport interface FailureContext {\n reason: string\n attempt: number\n type: 'schema' | 'gate' | 'exception'\n}\n\n/**\n * What kinds of failures should trigger a retry.\n */\nexport type RetryOn = 'schema' | 'gate' | 'exception'\n\n/**\n * Retry configuration for a step.\n */\nexport interface RetryConfig {\n /** Maximum number of attempts (including the first). Default: 1 (no retry). */\n maxAttempts?: number\n /** Which failure types trigger a retry. Default: ['schema', 'gate'] */\n on?: RetryOn[]\n}\n\n/**\n * Configuration for defining a step.\n * Generic parameters are inferred value types, not Zod schema types.\n */\nexport interface StepConfig<TInput, TOutput> {\n name: string\n input: z.ZodType<TInput, z.ZodTypeDef, unknown>\n output: z.ZodType<TOutput, z.ZodTypeDef, unknown>\n gates?: Gate<TOutput>[]\n retry?: RetryConfig\n run: (input: TInput, failureContext?: FailureContext) => Promise<TOutput>\n}\n\n/**\n * A defined step — the unit of work in a pipeline.\n * Generic parameters are value types (what the step consumes/produces),\n * not Zod schema types. This allows compile-time type checking of\n * step-to-step handoffs without Zod subclass compatibility issues.\n */\nexport interface Step<TInput = unknown, TOutput = unknown> {\n readonly name: string\n readonly inputSchema: z.ZodType<TInput>\n readonly outputSchema: z.ZodType<TOutput>\n readonly gates: Gate<TOutput>[]\n readonly retry: Required<RetryConfig>\n readonly run: (input: TInput, failureContext?: FailureContext) => Promise<TOutput>\n}\n\n/**\n * Structured failure returned when a pipeline halts.\n */\nexport interface Failure {\n step: string\n reason: string\n type: 'schema_input' | 'schema_output' | 'gate' | 'exception'\n attempts: number\n input: unknown\n output?: unknown\n}\n\n/**\n * Pipeline result type. Success or structured failure.\n */\nexport type Result<T> =\n | { ok: true; value: T }\n | { ok: false; failure: Failure }\n\n/**\n * Metadata returned by pipeline.describe().\n */\nexport interface PipelineDescription {\n name: string\n steps: StepDescription[]\n}\n\nexport interface StepDescription {\n name: string\n inputSchema: Record<string, unknown>\n outputSchema: Record<string, unknown>\n gates: number\n retry: Required<RetryConfig>\n}\n\n/**\n * Error thrown by .runOrThrow() on pipeline failure.\n */\nexport class PipelineError extends Error {\n constructor(public readonly failure: Failure) {\n super(`Pipeline failed at step \"${failure.step}\": ${failure.reason}`)\n this.name = 'PipelineError'\n }\n}\n","import { zodToJsonSchema } from 'zod-to-json-schema'\nimport { executeStep } from './runner.js'\nimport type { Failure, PipelineDescription, Result, Step } from './types.js'\nimport { PipelineError } from './types.js'\n\n/**\n * A compiled pipeline that can be run, described, or run-or-throw.\n */\nexport interface Pipeline<TInput, TOutput> {\n /** Pipeline name */\n readonly name: string\n\n /**\n * Add a step to the pipeline. Returns a new pipeline with the step appended.\n * TypeScript enforces that the step's input type matches the previous step's output type.\n */\n step<TNext>(step: Step<TOutput, TNext>): Pipeline<TInput, TNext>\n\n /**\n * Run the pipeline. Returns a Result — either { ok: true, value } or { ok: false, failure }.\n * No exceptions escape.\n */\n run(input: TInput): Promise<Result<TOutput>>\n\n /**\n * Run the pipeline and throw a PipelineError on failure.\n * For developers who prefer thrown exceptions over Result types.\n */\n runOrThrow(input: TInput): Promise<TOutput>\n\n /**\n * Return structured metadata about the pipeline:\n * step names, JSON Schema for inputs/outputs, gate count, retry config.\n */\n describe(): PipelineDescription\n}\n\n/**\n * Initial pipeline builder returned by pipeline().\n * The first .step() call sets both input and output types.\n */\nexport interface PipelineBuilder {\n readonly name: string\n step<TInput, TOutput>(step: Step<TInput, TOutput>): Pipeline<TInput, TOutput>\n}\n\n/**\n * Internal pipeline implementation.\n */\nclass PipelineImpl<TInput, TOutput> implements Pipeline<TInput, TOutput> {\n constructor(\n readonly name: string,\n // Use Step<unknown, unknown> internally; type safety is at the API boundary\n private readonly steps: Step<unknown, unknown>[],\n ) {}\n\n step<TNext>(newStep: Step<TOutput, TNext>): Pipeline<TInput, TNext> {\n return new PipelineImpl<TInput, TNext>(this.name, [\n ...this.steps,\n newStep as Step<unknown, unknown>,\n ])\n }\n\n async run(input: TInput): Promise<Result<TOutput>> {\n let current: unknown = input\n\n for (const step of this.steps) {\n const result = await executeStep(step, current)\n if (!result.ok) {\n return result as { ok: false; failure: Failure }\n }\n current = result.value\n }\n\n return { ok: true, value: current as TOutput }\n }\n\n async runOrThrow(input: TInput): Promise<TOutput> {\n const result = await this.run(input)\n if (!result.ok) {\n throw new PipelineError(result.failure)\n }\n return result.value\n }\n\n describe(): PipelineDescription {\n return {\n name: this.name,\n steps: this.steps.map((step) => ({\n name: step.name,\n inputSchema: zodToJsonSchema(step.inputSchema) as Record<string, unknown>,\n outputSchema: zodToJsonSchema(step.outputSchema) as Record<string, unknown>,\n gates: step.gates.length,\n retry: step.retry,\n })),\n }\n }\n}\n\n/**\n * Create a new pipeline with the given name.\n * Call .step() to add the first step — this sets the pipeline's input type.\n *\n * @example\n * ```ts\n * const myPipeline = pipeline('my-pipeline')\n * .step(loadData)\n * .step(transformData)\n * .step(validateResult);\n *\n * const result = await myPipeline.run({ source: 'input.json' });\n * ```\n */\nexport function pipeline(name: string): PipelineBuilder {\n return {\n name,\n step<TInput, TOutput>(firstStep: Step<TInput, TOutput>): Pipeline<TInput, TOutput> {\n return new PipelineImpl<TInput, TOutput>(name, [firstStep as Step<unknown, unknown>])\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/step.ts","../src/runner.ts","../src/types.ts","../src/pipeline.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { z } from 'zod'\nimport type { Step, StepConfig } from './types.js'\n\n/**\n * Define a pipeline step with typed input/output schemas, optional quality gates,\n * and retry configuration.\n *\n * @example\n * ```ts\n * const extractTitle = defineStep({\n * name: 'extract-title',\n * input: z.object({ draft: z.string() }),\n * output: z.object({ title: z.string().max(80) }),\n * gates: [\n * (out) => out.title.includes(':')\n * ? { ok: false, reason: 'title must not contain colons' }\n * : { ok: true }\n * ],\n * retry: { maxAttempts: 3, on: ['schema', 'gate'] },\n * run: async (input, failureContext) => {\n * return { title: 'extracted title' };\n * }\n * });\n * ```\n */\nexport function defineStep<TInput, TOutput>(\n config: StepConfig<TInput, TOutput>,\n): Step<TInput, TOutput> {\n const maxAttempts = config.retry?.maxAttempts ?? 1\n\n if (maxAttempts < 1 || maxAttempts > 20) {\n throw new RangeError(`maxAttempts must be between 1 and 20 (got ${maxAttempts})`)\n }\n\n if (!Number.isInteger(maxAttempts)) {\n throw new RangeError(`maxAttempts must be an integer (got ${maxAttempts})`)\n }\n\n return {\n name: config.name,\n inputSchema: config.input as z.ZodType<TInput>,\n outputSchema: config.output as z.ZodType<TOutput>,\n gates: config.gates ?? [],\n retry: {\n maxAttempts,\n on: config.retry?.on ?? ['schema', 'gate'],\n },\n run: config.run,\n }\n}\n","import type { ZodError } from 'zod'\nimport type { Failure, FailureContext, Gate, Result, Step } from './types.js'\n\n/**\n * Format a ZodError into a human-readable reason string.\n */\nfunction formatZodError(error: ZodError): string {\n return error.issues\n .map((issue) => {\n const path = issue.path.length > 0 ? `${issue.path.join('.')}: ` : ''\n return `${path}${issue.message}`\n })\n .join('; ')\n}\n\n/**\n * Run quality gates against a step's output.\n * Returns the first failure, or null if all gates pass.\n */\nfunction runGates(\n gates: Gate<unknown>[],\n output: unknown,\n): { reason: string; context?: unknown; thrown?: boolean } | null {\n for (const gate of gates) {\n try {\n const result = gate(output)\n if (!result.ok) {\n return { reason: result.reason, context: result.context }\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n return { reason: `Gate threw: ${reason}`, thrown: true }\n }\n }\n return null\n}\n\n/**\n * Execute a single step with retry logic.\n * Returns a Result with the validated output or a structured failure.\n */\nexport async function executeStep(\n step: Step<unknown, unknown>,\n input: unknown,\n): Promise<Result<unknown>> {\n // Validate input schema\n const inputResult = step.inputSchema.safeParse(input)\n if (!inputResult.success) {\n return {\n ok: false,\n failure: {\n step: step.name,\n reason: `Input validation failed: ${formatZodError(inputResult.error)}`,\n type: 'schema_input',\n attempts: 0,\n input,\n },\n }\n }\n\n const validatedInput = inputResult.data\n const maxAttempts = step.retry.maxAttempts\n const retryOn = new Set(step.retry.on)\n\n let failureContext: FailureContext | undefined\n let lastFailure: Failure | undefined\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n // Run the step function\n const rawOutput = await step.run(validatedInput, failureContext)\n\n // Validate output schema\n const outputResult = step.outputSchema.safeParse(rawOutput)\n if (!outputResult.success) {\n const reason = `Output validation failed: ${formatZodError(outputResult.error)}`\n lastFailure = {\n step: step.name,\n reason,\n type: 'schema_output',\n attempts: attempt,\n input: validatedInput,\n output: rawOutput,\n }\n\n if (attempt < maxAttempts && retryOn.has('schema')) {\n failureContext = { reason, attempt, type: 'schema' }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n\n const validatedOutput = outputResult.data\n\n // Run quality gates\n const gateFailure = runGates(step.gates as Gate<unknown>[], validatedOutput)\n if (gateFailure) {\n lastFailure = {\n step: step.name,\n reason: `Gate failed: ${gateFailure.reason}`,\n type: 'gate',\n attempts: attempt,\n input: validatedInput,\n output: validatedOutput,\n }\n\n if (attempt < maxAttempts && retryOn.has('gate')) {\n failureContext = {\n reason: gateFailure.reason,\n attempt,\n type: 'gate',\n }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n\n // Success\n return { ok: true, value: validatedOutput }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n lastFailure = {\n step: step.name,\n reason: `Exception: ${reason}`,\n type: 'exception',\n attempts: attempt,\n input: validatedInput,\n }\n\n if (attempt < maxAttempts && retryOn.has('exception')) {\n failureContext = { reason, attempt, type: 'exception' }\n continue\n }\n return { ok: false, failure: lastFailure }\n }\n }\n\n // Should not reach here, but safety net\n return {\n ok: false,\n failure: lastFailure ?? {\n step: step.name,\n reason: 'Unknown failure',\n type: 'exception',\n attempts: maxAttempts,\n input: validatedInput,\n },\n }\n}\n","import type { z } from 'zod'\n\n/**\n * Result of a quality gate check.\n * Gates are pure synchronous functions — no LLM calls, no async.\n */\nexport type GateResult = { ok: true } | { ok: false; reason: string; context?: unknown }\n\n/**\n * A quality gate function. Receives a step's validated output\n * and returns a pass/fail verdict.\n */\nexport type Gate<T> = (output: T) => GateResult\n\n/**\n * Context passed to a step's `run` function on retry attempts.\n * Contains the reason the previous attempt failed, the attempt number,\n * and the failure type so the step can self-correct.\n */\nexport interface FailureContext {\n reason: string\n attempt: number\n type: 'schema' | 'gate' | 'exception'\n}\n\n/**\n * What kinds of failures should trigger a retry.\n */\nexport type RetryOn = 'schema' | 'gate' | 'exception'\n\n/**\n * Retry configuration for a step.\n */\nexport interface RetryConfig {\n /** Maximum number of attempts (including the first). Default: 1 (no retry). */\n maxAttempts?: number\n /** Which failure types trigger a retry. Default: ['schema', 'gate'] */\n on?: RetryOn[]\n}\n\n/**\n * Configuration for defining a step.\n * Generic parameters are inferred value types, not Zod schema types.\n */\nexport interface StepConfig<TInput, TOutput> {\n name: string\n input: z.ZodType<TInput, z.ZodTypeDef, unknown>\n output: z.ZodType<TOutput, z.ZodTypeDef, unknown>\n gates?: Gate<TOutput>[]\n retry?: RetryConfig\n run: (input: TInput, failureContext?: FailureContext) => Promise<TOutput>\n}\n\n/**\n * A defined step — the unit of work in a pipeline.\n * Generic parameters are value types (what the step consumes/produces),\n * not Zod schema types. This allows compile-time type checking of\n * step-to-step handoffs without Zod subclass compatibility issues.\n */\nexport interface Step<TInput = unknown, TOutput = unknown> {\n readonly name: string\n readonly inputSchema: z.ZodType<TInput>\n readonly outputSchema: z.ZodType<TOutput>\n readonly gates: Gate<TOutput>[]\n readonly retry: Required<RetryConfig>\n readonly run: (input: TInput, failureContext?: FailureContext) => Promise<TOutput>\n}\n\n/**\n * Structured failure returned when a pipeline halts.\n */\nexport interface Failure {\n step: string\n reason: string\n type: 'schema_input' | 'schema_output' | 'gate' | 'exception'\n attempts: number\n input: unknown\n output?: unknown\n}\n\n/**\n * Pipeline result type. Success or structured failure.\n */\nexport type Result<T> = { ok: true; value: T } | { ok: false; failure: Failure }\n\n/**\n * Metadata returned by pipeline.describe().\n */\nexport interface PipelineDescription {\n name: string\n steps: StepDescription[]\n}\n\nexport interface StepDescription {\n name: string\n inputSchema: Record<string, unknown>\n outputSchema: Record<string, unknown>\n gates: number\n retry: Required<RetryConfig>\n}\n\n/**\n * Error thrown by .runOrThrow() on pipeline failure.\n */\nexport class PipelineError extends Error {\n constructor(public readonly failure: Failure) {\n super(`Pipeline failed at step \"${failure.step}\": ${failure.reason}`)\n this.name = 'PipelineError'\n }\n}\n","import { zodToJsonSchema } from 'zod-to-json-schema'\nimport { executeStep } from './runner.js'\nimport type { Failure, PipelineDescription, Result, Step } from './types.js'\nimport { PipelineError } from './types.js'\n\n/**\n * A compiled pipeline that can be run, described, or run-or-throw.\n */\nexport interface Pipeline<TInput, TOutput> {\n /** Pipeline name */\n readonly name: string\n\n /**\n * Add a step to the pipeline. Returns a new pipeline with the step appended.\n * TypeScript enforces that the step's input type matches the previous step's output type.\n */\n step<TNext>(step: Step<TOutput, TNext>): Pipeline<TInput, TNext>\n\n /**\n * Run the pipeline. Returns a Result — either { ok: true, value } or { ok: false, failure }.\n * No exceptions escape.\n */\n run(input: TInput): Promise<Result<TOutput>>\n\n /**\n * Run the pipeline and throw a PipelineError on failure.\n * For developers who prefer thrown exceptions over Result types.\n */\n runOrThrow(input: TInput): Promise<TOutput>\n\n /**\n * Return structured metadata about the pipeline:\n * step names, JSON Schema for inputs/outputs, gate count, retry config.\n */\n describe(): PipelineDescription\n}\n\n/**\n * Initial pipeline builder returned by pipeline().\n * The first .step() call sets both input and output types.\n */\nexport interface PipelineBuilder {\n readonly name: string\n step<TInput, TOutput>(step: Step<TInput, TOutput>): Pipeline<TInput, TOutput>\n}\n\n/**\n * Internal pipeline implementation.\n */\nclass PipelineImpl<TInput, TOutput> implements Pipeline<TInput, TOutput> {\n constructor(\n readonly name: string,\n // Use Step<unknown, unknown> internally; type safety is at the API boundary\n private readonly steps: Step<unknown, unknown>[],\n ) {}\n\n step<TNext>(newStep: Step<TOutput, TNext>): Pipeline<TInput, TNext> {\n return new PipelineImpl<TInput, TNext>(this.name, [\n ...this.steps,\n newStep as Step<unknown, unknown>,\n ])\n }\n\n async run(input: TInput): Promise<Result<TOutput>> {\n let current: unknown = input\n\n for (const step of this.steps) {\n const result = await executeStep(step, current)\n if (!result.ok) {\n return result as { ok: false; failure: Failure }\n }\n current = result.value\n }\n\n return { ok: true, value: current as TOutput }\n }\n\n async runOrThrow(input: TInput): Promise<TOutput> {\n const result = await this.run(input)\n if (!result.ok) {\n throw new PipelineError(result.failure)\n }\n return result.value\n }\n\n describe(): PipelineDescription {\n return {\n name: this.name,\n steps: this.steps.map((step) => ({\n name: step.name,\n inputSchema: zodToJsonSchema(step.inputSchema) as Record<string, unknown>,\n outputSchema: zodToJsonSchema(step.outputSchema) as Record<string, unknown>,\n gates: step.gates.length,\n retry: step.retry,\n })),\n }\n }\n}\n\n/**\n * Create a new pipeline with the given name.\n * Call .step() to add the first step — this sets the pipeline's input type.\n *\n * @example\n * ```ts\n * const myPipeline = pipeline('my-pipeline')\n * .step(loadData)\n * .step(transformData)\n * .step(validateResult);\n *\n * const result = await myPipeline.run({ source: 'input.json' });\n * ```\n */\nexport function pipeline(name: string): PipelineBuilder {\n return {\n name,\n step<TInput, TOutput>(firstStep: Step<TInput, TOutput>): Pipeline<TInput, TOutput> {\n return new PipelineImpl<TInput, TOutput>(name, [firstStep as Step<unknown, unknown>])\n },\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cueapi/cuechain",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Contract-verification primitive for TypeScript AI agent pipelines",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -52,6 +52,9 @@
|
|
|
52
52
|
"bugs": {
|
|
53
53
|
"url": "https://github.com/cueapi/cuechain/issues"
|
|
54
54
|
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
},
|
|
55
58
|
"dependencies": {
|
|
56
59
|
"zod-to-json-schema": "^3.24.0"
|
|
57
60
|
},
|
|
@@ -74,4 +77,4 @@
|
|
|
74
77
|
"esbuild"
|
|
75
78
|
]
|
|
76
79
|
}
|
|
77
|
-
}
|
|
80
|
+
}
|