@outfitter/contracts 0.4.1 → 0.5.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.
Files changed (100) hide show
  1. package/README.md +18 -15
  2. package/dist/actions.d.ts +8 -3
  3. package/dist/actions.js +25 -6
  4. package/dist/assert/index.d.ts +7 -3
  5. package/dist/assert/index.js +59 -6
  6. package/dist/capabilities.js +57 -8
  7. package/dist/context.d.ts +8 -3
  8. package/dist/context.js +1 -1
  9. package/dist/envelope.d.ts +6 -2
  10. package/dist/envelope.js +49 -7
  11. package/dist/errors.d.ts +6 -2
  12. package/dist/errors.js +7 -1
  13. package/dist/from-fetch.d.ts +7 -0
  14. package/dist/from-fetch.js +110 -0
  15. package/dist/handler.d.ts +7 -2
  16. package/dist/hints.d.ts +2 -0
  17. package/dist/index.d.ts +25 -14
  18. package/dist/index.js +17 -153
  19. package/dist/internal/error-base.d.ts +2 -0
  20. package/dist/internal/error-base.js +31 -0
  21. package/dist/internal/error-operational.d.ts +3 -0
  22. package/dist/internal/error-operational.js +125 -0
  23. package/dist/internal/error-serialization.d.ts +7 -0
  24. package/dist/{shared/@outfitter/contracts-3wj7xghe.js → internal/error-serialization.js} +28 -67
  25. package/dist/internal/error-taxonomy.d.ts +2 -0
  26. package/dist/internal/error-taxonomy.js +21 -0
  27. package/dist/internal/error-validation.d.ts +3 -0
  28. package/dist/internal/error-validation.js +121 -0
  29. package/dist/internal/safe-json.d.ts +7 -0
  30. package/dist/internal/safe-json.js +66 -0
  31. package/dist/internal/schema-converters.d.ts +26 -0
  32. package/dist/internal/schema-converters.js +12 -0
  33. package/dist/internal/schema-primitives.d.ts +10 -0
  34. package/dist/internal/schema-primitives.js +9 -0
  35. package/dist/internal/schema-types.d.ts +2 -0
  36. package/dist/internal/schema-types.js +9 -0
  37. package/dist/logging.js +11 -3
  38. package/dist/recovery.d.ts +6 -2
  39. package/dist/recovery.js +49 -6
  40. package/dist/resilience.d.ts +6 -2
  41. package/dist/resilience.js +80 -4
  42. package/dist/result/index.js +1 -16
  43. package/dist/result/utilities.js +29 -7
  44. package/dist/schema.d.ts +2 -1
  45. package/dist/schema.js +165 -2
  46. package/dist/serialization.d.ts +8 -2
  47. package/dist/serialization.js +1 -3
  48. package/dist/shared/@outfitter/{contracts-k71jqd1m.d.ts → contracts-10p5q75w.d.ts} +1 -1
  49. package/dist/shared/@outfitter/contracts-1zzcpfyg.d.ts +40 -0
  50. package/dist/shared/@outfitter/contracts-3f5k5tg5.d.ts +28 -0
  51. package/dist/shared/@outfitter/contracts-3qmyq81n.d.ts +78 -0
  52. package/dist/shared/@outfitter/contracts-3re9d4bp.js +114 -0
  53. package/dist/shared/@outfitter/contracts-735ecmbq.d.ts +107 -0
  54. package/dist/shared/@outfitter/contracts-7a0xmwbg.d.ts +11 -0
  55. package/dist/shared/@outfitter/contracts-8cmkh2db.d.ts +31 -0
  56. package/dist/shared/@outfitter/{contracts-agmt8915.js → contracts-c3qfce25.js} +3 -0
  57. package/dist/shared/@outfitter/{contracts-1waabxbk.d.ts → contracts-drwd9ywk.d.ts} +4 -1
  58. package/dist/shared/@outfitter/{contracts-0snpmkdt.js → contracts-hgh47193.js} +10 -4
  59. package/dist/shared/@outfitter/contracts-hrepwwne.js +62 -0
  60. package/dist/shared/@outfitter/contracts-jtn6b927.js +18 -0
  61. package/dist/shared/@outfitter/contracts-jtt6dnmg.js +2 -0
  62. package/dist/shared/@outfitter/contracts-jyhqr766.js +25 -0
  63. package/dist/shared/@outfitter/contracts-mehpmvwp.d.ts +164 -0
  64. package/dist/shared/@outfitter/contracts-msxdg52h.d.ts +125 -0
  65. package/dist/shared/@outfitter/{contracts-95cc3y06.d.ts → contracts-mt027fqj.d.ts} +2 -1
  66. package/dist/shared/@outfitter/contracts-njb2art4.d.ts +174 -0
  67. package/dist/shared/@outfitter/contracts-p77yjs4g.d.ts +46 -0
  68. package/dist/shared/@outfitter/contracts-qpbv29bg.d.ts +59 -0
  69. package/dist/shared/@outfitter/contracts-sawwfgb5.js +111 -0
  70. package/dist/shared/@outfitter/{contracts-e4m948m7.d.ts → contracts-t4txv24h.d.ts} +2 -1
  71. package/dist/shared/@outfitter/contracts-vbgt9rfn.d.ts +74 -0
  72. package/dist/shared/@outfitter/{contracts-56pcsavx.d.ts → contracts-vhajx4gg.d.ts} +8 -2
  73. package/dist/shared/@outfitter/contracts-vhr2ep6b.js +3 -0
  74. package/dist/shared/@outfitter/contracts-w7nvcwrp.d.ts +44 -0
  75. package/dist/shared/@outfitter/contracts-x0ppyt7e.d.ts +76 -0
  76. package/dist/shared/@outfitter/{contracts-0akf2sm6.d.ts → contracts-zma4mscd.d.ts} +16 -1
  77. package/dist/shared/@outfitter/contracts-zsgxsa91.d.ts +84 -0
  78. package/dist/stream.d.ts +2 -0
  79. package/dist/stream.js +1 -0
  80. package/dist/validation.d.ts +7 -3
  81. package/dist/validation.js +6 -2
  82. package/dist/wrap-error.d.ts +7 -0
  83. package/dist/wrap-error.js +71 -0
  84. package/package.json +44 -20
  85. package/dist/shared/@outfitter/contracts-31penhwa.d.ts +0 -81
  86. package/dist/shared/@outfitter/contracts-3gswmhb1.d.ts +0 -446
  87. package/dist/shared/@outfitter/contracts-4zaj7ejb.js +0 -52
  88. package/dist/shared/@outfitter/contracts-85nd53s9.js +0 -53
  89. package/dist/shared/@outfitter/contracts-9wtm5nsw.d.ts +0 -42
  90. package/dist/shared/@outfitter/contracts-cp5c6dws.js +0 -32
  91. package/dist/shared/@outfitter/contracts-d0tq2adf.js +0 -60
  92. package/dist/shared/@outfitter/contracts-mmg0npfk.d.ts +0 -30
  93. package/dist/shared/@outfitter/contracts-phjhz5q3.js +0 -293
  94. package/dist/shared/@outfitter/contracts-q0v44kef.js +0 -28
  95. package/dist/shared/@outfitter/contracts-r21yet6j.js +0 -80
  96. package/dist/shared/@outfitter/contracts-sm6vak1a.js +0 -14
  97. package/dist/shared/@outfitter/contracts-t79engf9.d.ts +0 -60
  98. package/dist/shared/@outfitter/contracts-wfht4q2b.js +0 -341
  99. package/dist/shared/@outfitter/contracts-zx72gyh1.js +0 -32
  100. /package/dist/{shared/@outfitter/contracts-37gpc56f.js → hints.js} +0 -0
@@ -0,0 +1,78 @@
1
+ import { AssertionError } from "./contracts-mehpmvwp.js";
2
+ import { Result } from "better-result";
3
+ /**
4
+ * Array type guaranteed to have at least one element.
5
+ */
6
+ type NonEmptyArray<T> = [T, ...T[]];
7
+ /**
8
+ * Type guard for NonEmptyArray.
9
+ */
10
+ declare const isNonEmptyArray: <T>(arr: readonly T[]) => arr is NonEmptyArray<T>;
11
+ /**
12
+ * Assert a value is defined (not null or undefined).
13
+ * Returns Result instead of throwing.
14
+ */
15
+ declare const assertDefined: <T>(value: T | null | undefined, message?: string) => Result<T, InstanceType<typeof AssertionError>>;
16
+ /**
17
+ * Assert array has at least one element.
18
+ * Returns NonEmptyArray on success.
19
+ */
20
+ declare const assertNonEmpty: <T>(arr: readonly T[], message?: string) => Result<NonEmptyArray<T>, InstanceType<typeof AssertionError>>;
21
+ /**
22
+ * Assert value matches a predicate.
23
+ * Supports type guard predicates for narrowing.
24
+ */
25
+ declare function assertMatches<
26
+ T,
27
+ U extends T
28
+ >(value: T, predicate: (v: T) => v is U, message?: string): Result<U, InstanceType<typeof AssertionError>>;
29
+ declare function assertMatches<T>(value: T, predicate: (v: T) => boolean, message?: string): Result<T, InstanceType<typeof AssertionError>>;
30
+ /**
31
+ * Assert a Result is Ok and return the narrowed value.
32
+ *
33
+ * Throws a descriptive error if the Result is Err, making it ideal for
34
+ * test assertions with Bun's test runner.
35
+ *
36
+ * @param result - The Result to assert
37
+ * @param message - Optional context message prepended to the error
38
+ * @returns The unwrapped Ok value with type narrowing to `T`
39
+ * @throws Error with descriptive message if Result is Err
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * import { expectOk } from "@outfitter/contracts";
44
+ *
45
+ * const result = await fetchUser(id);
46
+ * const user = expectOk(result); // throws if Err, returns User if Ok
47
+ * expect(user.name).toBe("Alice");
48
+ * ```
49
+ */
50
+ declare const expectOk: <
51
+ T,
52
+ E
53
+ >(result: Result<T, E>, message?: string) => T;
54
+ /**
55
+ * Assert a Result is Err and return the narrowed error.
56
+ *
57
+ * Throws a descriptive error if the Result is Ok, making it ideal for
58
+ * test assertions with Bun's test runner.
59
+ *
60
+ * @param result - The Result to assert
61
+ * @param message - Optional context message prepended to the error
62
+ * @returns The unwrapped Err value with type narrowing to `E`
63
+ * @throws Error with descriptive message if Result is Ok
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * import { expectErr } from "@outfitter/contracts";
68
+ *
69
+ * const result = validateInput(invalidData);
70
+ * const error = expectErr(result); // throws if Ok, returns error if Err
71
+ * expect(error.category).toBe("validation");
72
+ * ```
73
+ */
74
+ declare const expectErr: <
75
+ T,
76
+ E
77
+ >(result: Result<T, E>, message?: string) => E;
78
+ export { NonEmptyArray, isNonEmptyArray, assertDefined, assertNonEmpty, assertMatches, expectOk, expectErr };
@@ -0,0 +1,114 @@
1
+ // @bun
2
+ // packages/contracts/src/internal/schema-primitives.ts
3
+ function convertString(def) {
4
+ const schema = { type: "string" };
5
+ if (def.checks) {
6
+ for (const check of def.checks) {
7
+ const normalizedCheck = check?._zod?.def ?? check?.def ?? check;
8
+ if (normalizedCheck?.kind) {
9
+ switch (normalizedCheck.kind) {
10
+ case "min":
11
+ schema.minLength = normalizedCheck.value;
12
+ break;
13
+ case "max":
14
+ schema.maxLength = normalizedCheck.value;
15
+ break;
16
+ case "length":
17
+ schema.minLength = normalizedCheck.value;
18
+ schema.maxLength = normalizedCheck.value;
19
+ break;
20
+ case "email":
21
+ schema.pattern = "^[^@]+@[^@]+\\.[^@]+$";
22
+ break;
23
+ case "url":
24
+ schema.pattern = "^https?://";
25
+ break;
26
+ case "uuid":
27
+ schema.pattern = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$";
28
+ break;
29
+ case "regex":
30
+ schema.pattern = normalizedCheck.regex?.source ?? normalizedCheck.pattern?.source ?? (typeof normalizedCheck.pattern === "string" ? normalizedCheck.pattern : undefined);
31
+ break;
32
+ default:
33
+ break;
34
+ }
35
+ continue;
36
+ }
37
+ if (!normalizedCheck?.check) {
38
+ continue;
39
+ }
40
+ switch (normalizedCheck.check) {
41
+ case "min_length":
42
+ schema.minLength = normalizedCheck.minimum;
43
+ break;
44
+ case "max_length":
45
+ schema.maxLength = normalizedCheck.maximum;
46
+ break;
47
+ case "string_format":
48
+ if (normalizedCheck.pattern) {
49
+ schema.pattern = typeof normalizedCheck.pattern === "string" ? normalizedCheck.pattern : normalizedCheck.pattern.source;
50
+ }
51
+ if (normalizedCheck.format && normalizedCheck.format !== "regex") {
52
+ schema.format = normalizedCheck.format;
53
+ }
54
+ break;
55
+ default:
56
+ break;
57
+ }
58
+ }
59
+ }
60
+ return schema;
61
+ }
62
+ function convertNumber(def) {
63
+ const schema = { type: "number" };
64
+ if (def.checks) {
65
+ for (const check of def.checks) {
66
+ const normalizedCheck = check?._zod?.def ?? check?.def ?? check;
67
+ if (normalizedCheck?.kind) {
68
+ switch (normalizedCheck.kind) {
69
+ case "min":
70
+ schema.minimum = normalizedCheck.value;
71
+ break;
72
+ case "max":
73
+ schema.maximum = normalizedCheck.value;
74
+ break;
75
+ case "int":
76
+ schema.type = "integer";
77
+ break;
78
+ default:
79
+ break;
80
+ }
81
+ continue;
82
+ }
83
+ if (!normalizedCheck?.check) {
84
+ continue;
85
+ }
86
+ switch (normalizedCheck.check) {
87
+ case "greater_than":
88
+ if (normalizedCheck.inclusive) {
89
+ schema.minimum = normalizedCheck.value;
90
+ } else {
91
+ schema.exclusiveMinimum = normalizedCheck.value;
92
+ }
93
+ break;
94
+ case "less_than":
95
+ if (normalizedCheck.inclusive) {
96
+ schema.maximum = normalizedCheck.value;
97
+ } else {
98
+ schema.exclusiveMaximum = normalizedCheck.value;
99
+ }
100
+ break;
101
+ case "number_format":
102
+ if (normalizedCheck.format === "int" || normalizedCheck.format === "safeint") {
103
+ schema.type = "integer";
104
+ }
105
+ break;
106
+ default:
107
+ break;
108
+ }
109
+ }
110
+ }
111
+ return schema;
112
+ }
113
+
114
+ export { convertString, convertNumber };
@@ -0,0 +1,107 @@
1
+ import { AuthErrorBase, CancelledErrorBase, InternalErrorBase, NetworkErrorBase, PermissionErrorBase, RateLimitErrorBase, TimeoutErrorBase } from "./contracts-qpbv29bg.js";
2
+ /**
3
+ * Authorization denied.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * new PermissionError({ message: "Cannot delete read-only resource" });
8
+ * ```
9
+ */
10
+ declare class PermissionError extends PermissionErrorBase {
11
+ readonly category: "permission";
12
+ /** Create a PermissionError with optional context. */
13
+ static create(message: string, context?: Record<string, unknown>): PermissionError;
14
+ exitCode(): number;
15
+ statusCode(): number;
16
+ }
17
+ /**
18
+ * Operation timed out.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * new TimeoutError({ message: "Database query timed out after 5000ms", operation: "Database query", timeoutMs: 5000 });
23
+ * ```
24
+ */
25
+ declare class TimeoutError extends TimeoutErrorBase {
26
+ readonly category: "timeout";
27
+ /** Create a TimeoutError with auto-generated message. */
28
+ static create(operation: string, timeoutMs: number): TimeoutError;
29
+ exitCode(): number;
30
+ statusCode(): number;
31
+ }
32
+ /**
33
+ * Rate limit exceeded.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * new RateLimitError({ message: "Rate limit exceeded, retry after 60s", retryAfterSeconds: 60 });
38
+ * ```
39
+ */
40
+ declare class RateLimitError extends RateLimitErrorBase {
41
+ readonly category: "rate_limit";
42
+ /** Create a RateLimitError with optional retry hint. */
43
+ static create(message: string, retryAfterSeconds?: number): RateLimitError;
44
+ exitCode(): number;
45
+ statusCode(): number;
46
+ }
47
+ /**
48
+ * Network/transport failure.
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * new NetworkError({ message: "Connection refused to api.example.com" });
53
+ * ```
54
+ */
55
+ declare class NetworkError extends NetworkErrorBase {
56
+ readonly category: "network";
57
+ /** Create a NetworkError with optional context. */
58
+ static create(message: string, context?: Record<string, unknown>): NetworkError;
59
+ exitCode(): number;
60
+ statusCode(): number;
61
+ }
62
+ /**
63
+ * Unexpected internal error.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * new InternalError({ message: "Unexpected state in processor" });
68
+ * ```
69
+ */
70
+ declare class InternalError extends InternalErrorBase {
71
+ readonly category: "internal";
72
+ /** Create an InternalError with optional context. */
73
+ static create(message: string, context?: Record<string, unknown>): InternalError;
74
+ exitCode(): number;
75
+ statusCode(): number;
76
+ }
77
+ /**
78
+ * Authentication failed (missing or invalid credentials).
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * new AuthError({ message: "Invalid API key", reason: "invalid" });
83
+ * ```
84
+ */
85
+ declare class AuthError extends AuthErrorBase {
86
+ readonly category: "auth";
87
+ /** Create an AuthError with optional reason. */
88
+ static create(message: string, reason?: "missing" | "invalid" | "expired"): AuthError;
89
+ exitCode(): number;
90
+ statusCode(): number;
91
+ }
92
+ /**
93
+ * Operation cancelled by user or signal.
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * new CancelledError({ message: "Operation cancelled by user" });
98
+ * ```
99
+ */
100
+ declare class CancelledError extends CancelledErrorBase {
101
+ readonly category: "cancelled";
102
+ /** Create a CancelledError. */
103
+ static create(message: string): CancelledError;
104
+ exitCode(): number;
105
+ statusCode(): number;
106
+ }
107
+ export { PermissionError, TimeoutError, RateLimitError, NetworkError, InternalError, AuthError, CancelledError };
@@ -0,0 +1,11 @@
1
+ import { AuthError, CancelledError, InternalError, NetworkError, PermissionError, RateLimitError, TimeoutError } from "./contracts-735ecmbq.js";
2
+ import { AlreadyExistsError, AmbiguousError, AssertionError, ConflictError, NotFoundError, ValidationError } from "./contracts-mehpmvwp.js";
3
+ /**
4
+ * Canonical union type of all concrete error class instances.
5
+ */
6
+ type OutfitterError = InstanceType<typeof ValidationError> | InstanceType<typeof AmbiguousError> | InstanceType<typeof AssertionError> | InstanceType<typeof NotFoundError> | InstanceType<typeof AlreadyExistsError> | InstanceType<typeof ConflictError> | InstanceType<typeof PermissionError> | InstanceType<typeof TimeoutError> | InstanceType<typeof RateLimitError> | InstanceType<typeof NetworkError> | InstanceType<typeof InternalError> | InstanceType<typeof AuthError> | InstanceType<typeof CancelledError>;
7
+ /**
8
+ * @deprecated Use `OutfitterError` instead. This alias will be removed in v1.0.
9
+ */
10
+ type AnyKitError = OutfitterError;
11
+ export { OutfitterError, AnyKitError };
@@ -0,0 +1,31 @@
1
+ import { JsonSchema } from "./contracts-w7nvcwrp.js";
2
+ import { z } from "zod";
3
+ /**
4
+ * Convert a Zod schema to JSON Schema format.
5
+ *
6
+ * This is a simplified converter that handles common Zod types.
7
+ * For complex schemas, consider using a full zod-to-json-schema library.
8
+ *
9
+ * @param schema - Zod schema to convert
10
+ * @returns JSON Schema representation
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const zodSchema = z.object({
15
+ * name: z.string(),
16
+ * age: z.number().optional(),
17
+ * });
18
+ *
19
+ * const jsonSchema = zodToJsonSchema(zodSchema);
20
+ * // {
21
+ * // type: "object",
22
+ * // properties: {
23
+ * // name: { type: "string" },
24
+ * // age: { type: "number" },
25
+ * // },
26
+ * // required: ["name"],
27
+ * // }
28
+ * ```
29
+ */
30
+ declare function zodToJsonSchema(schema: z.ZodType<unknown>): JsonSchema;
31
+ export { zodToJsonSchema };
@@ -22,6 +22,9 @@ function createContext(options) {
22
22
  if (options.signal !== undefined) {
23
23
  ctx.signal = options.signal;
24
24
  }
25
+ if (options.progress !== undefined) {
26
+ ctx.progress = options.progress;
27
+ }
25
28
  if (options.workspaceRoot !== undefined) {
26
29
  ctx.workspaceRoot = options.workspaceRoot;
27
30
  }
@@ -1,4 +1,5 @@
1
- import { HandlerContext, ResolvedConfig } from "./contracts-0akf2sm6.js";
1
+ import { HandlerContext, ResolvedConfig } from "./contracts-zma4mscd.js";
2
+ import { ProgressCallback } from "./contracts-msxdg52h.js";
2
3
  import { Logger } from "./contracts-rwzqy9rn.js";
3
4
  /**
4
5
  * Options for creating a handler context.
@@ -12,6 +13,8 @@ interface CreateContextOptions {
12
13
  env?: Record<string, string | undefined>;
13
14
  /** Logger instance (uses no-op logger if not provided) */
14
15
  logger?: Logger;
16
+ /** Streaming progress callback (undefined means no streaming) */
17
+ progress?: ProgressCallback;
15
18
  /** Explicit request ID (generates UUIDv7 if not provided) */
16
19
  requestId?: string;
17
20
  /** Abort signal for cancellation */
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  ValidationError
4
- } from "./contracts-phjhz5q3.js";
4
+ } from "./contracts-vhr2ep6b.js";
5
5
 
6
6
  // packages/contracts/src/validation.ts
7
7
  import { Result } from "better-result";
@@ -23,8 +23,8 @@ function createValidator(schema) {
23
23
  return validateInput(schema, input);
24
24
  };
25
25
  }
26
- function validateInput(schema, input) {
27
- const parseResult = schema.safeParse(input);
26
+ function parseWithSchema(schema, data) {
27
+ const parseResult = schema.safeParse(data);
28
28
  if (parseResult.success) {
29
29
  return Result.ok(parseResult.data);
30
30
  }
@@ -36,5 +36,11 @@ function validateInput(schema, input) {
36
36
  }
37
37
  return Result.err(new ValidationError(errorProps));
38
38
  }
39
+ function validateInput(schema, input) {
40
+ return parseWithSchema(schema, input);
41
+ }
42
+ function parseInput(schema, data) {
43
+ return parseWithSchema(schema, data);
44
+ }
39
45
 
40
- export { createValidator, validateInput };
46
+ export { formatZodIssues, createValidator, validateInput, parseInput };
@@ -0,0 +1,62 @@
1
+ // @bun
2
+ import {
3
+ getDef
4
+ } from "./contracts-jyhqr766.js";
5
+
6
+ // packages/contracts/src/internal/schema-converters.ts
7
+ function convertArray(def, convertZodType) {
8
+ const element = def.element ?? def.type;
9
+ const schema = {
10
+ type: "array",
11
+ items: element ? convertZodType(element) : {}
12
+ };
13
+ return schema;
14
+ }
15
+ function isFieldOptional(fieldDef) {
16
+ if (!(fieldDef?.typeName || fieldDef?.type)) {
17
+ return false;
18
+ }
19
+ const typeName = fieldDef.typeName ?? fieldDef.type;
20
+ if (typeName === "ZodOptional" || typeName === "ZodDefault" || typeName === "optional" || typeName === "default") {
21
+ return true;
22
+ }
23
+ if (typeName === "ZodEffects") {
24
+ return isFieldOptional(getDef(fieldDef.schema));
25
+ }
26
+ if (typeName === "ZodPipeline" || typeName === "pipe") {
27
+ const inputOptional = isFieldOptional(getDef(fieldDef.in));
28
+ const outputDef = getDef(fieldDef.out);
29
+ const outputType = outputDef?.typeName ?? outputDef?.type;
30
+ if (outputType === "transform") {
31
+ return inputOptional;
32
+ }
33
+ const outputOptional = isFieldOptional(outputDef);
34
+ return inputOptional && outputOptional;
35
+ }
36
+ if (typeName === "ZodNullable" || typeName === "nullable") {
37
+ return isFieldOptional(getDef(fieldDef.innerType));
38
+ }
39
+ return false;
40
+ }
41
+ function convertObject(def, convertZodType) {
42
+ const properties = {};
43
+ const required = [];
44
+ const shape = typeof def.shape === "function" ? def.shape() : def.shape;
45
+ for (const [key, value] of Object.entries(shape ?? {})) {
46
+ properties[key] = convertZodType(value);
47
+ const fieldDef = getDef(value);
48
+ if (!isFieldOptional(fieldDef)) {
49
+ required.push(key);
50
+ }
51
+ }
52
+ const schema = {
53
+ type: "object",
54
+ properties
55
+ };
56
+ if (required.length > 0) {
57
+ schema.required = required;
58
+ }
59
+ return schema;
60
+ }
61
+
62
+ export { convertArray, isFieldOptional, convertObject };
@@ -0,0 +1,18 @@
1
+ // @bun
2
+ // packages/contracts/src/internal/error-base.ts
3
+ import { TaggedError } from "better-result";
4
+ var ValidationErrorBase = TaggedError("ValidationError")();
5
+ var AmbiguousErrorBase = TaggedError("AmbiguousError")();
6
+ var AssertionErrorBase = TaggedError("AssertionError")();
7
+ var NotFoundErrorBase = TaggedError("NotFoundError")();
8
+ var AlreadyExistsErrorBase = TaggedError("AlreadyExistsError")();
9
+ var ConflictErrorBase = TaggedError("ConflictError")();
10
+ var PermissionErrorBase = TaggedError("PermissionError")();
11
+ var TimeoutErrorBase = TaggedError("TimeoutError")();
12
+ var RateLimitErrorBase = TaggedError("RateLimitError")();
13
+ var NetworkErrorBase = TaggedError("NetworkError")();
14
+ var InternalErrorBase = TaggedError("InternalError")();
15
+ var AuthErrorBase = TaggedError("AuthError")();
16
+ var CancelledErrorBase = TaggedError("CancelledError")();
17
+
18
+ export { ValidationErrorBase, AmbiguousErrorBase, AssertionErrorBase, NotFoundErrorBase, AlreadyExistsErrorBase, ConflictErrorBase, PermissionErrorBase, TimeoutErrorBase, RateLimitErrorBase, NetworkErrorBase, InternalErrorBase, AuthErrorBase, CancelledErrorBase };
@@ -0,0 +1,2 @@
1
+ export { deserializeError, serializeError } from "../../internal/error-serialization.js";
2
+ export { safeParse, safeStringify } from "../../internal/safe-json.js";
@@ -0,0 +1,25 @@
1
+ // @bun
2
+ // packages/contracts/src/internal/schema-types.ts
3
+ function getDef(schemaOrDef) {
4
+ if (!schemaOrDef) {
5
+ return;
6
+ }
7
+ if (schemaOrDef._def) {
8
+ return schemaOrDef._def;
9
+ }
10
+ if (schemaOrDef.def) {
11
+ return schemaOrDef.def;
12
+ }
13
+ return schemaOrDef;
14
+ }
15
+ function getDescription(schema, def) {
16
+ if (typeof schema?.description === "string") {
17
+ return schema.description;
18
+ }
19
+ if (typeof def?.description === "string") {
20
+ return def.description;
21
+ }
22
+ return;
23
+ }
24
+
25
+ export { getDef, getDescription };
@@ -0,0 +1,164 @@
1
+ import { AlreadyExistsErrorBase, AmbiguousErrorBase, AssertionErrorBase, ConflictErrorBase, NotFoundErrorBase, ValidationErrorBase } from "./contracts-qpbv29bg.js";
2
+ /**
3
+ * Input validation failed.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * new ValidationError({ message: "Email format invalid", field: "email" });
8
+ * new ValidationError({
9
+ * message: "Value out of range",
10
+ * field: "age",
11
+ * context: { min: 0, max: 150, received: -1 },
12
+ * });
13
+ * ```
14
+ */
15
+ declare class ValidationError extends ValidationErrorBase {
16
+ readonly category: "validation";
17
+ /** Create a ValidationError with auto-generated message from field name. */
18
+ static create(field: string, reason: string, context?: Record<string, unknown>): ValidationError;
19
+ /**
20
+ * Create a freeform ValidationError without a specific field.
21
+ *
22
+ * Use when the validation failure applies to the input as a whole
23
+ * rather than a single field (e.g., "Invalid pipeline configuration").
24
+ *
25
+ * @param message - Human-readable validation error message
26
+ * @param context - Optional structured context for debugging
27
+ */
28
+ static fromMessage(message: string, context?: Record<string, unknown>): ValidationError;
29
+ exitCode(): number;
30
+ statusCode(): number;
31
+ }
32
+ /**
33
+ * Multiple matches found — user must disambiguate.
34
+ *
35
+ * Used in search/resolution systems where partial input matches
36
+ * multiple candidates. Carries the candidate list so transport
37
+ * layers can prompt disambiguation.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * new AmbiguousError({
42
+ * message: "Multiple headings match 'Intro'",
43
+ * candidates: ["Introduction", "Intro to APIs"],
44
+ * });
45
+ * AmbiguousError.create("heading", ["Introduction", "Intro to APIs"]);
46
+ * ```
47
+ */
48
+ declare class AmbiguousError extends AmbiguousErrorBase {
49
+ readonly category: "validation";
50
+ /** Create an AmbiguousError with auto-generated message. */
51
+ static create(what: string, candidates: string[], context?: Record<string, unknown>): AmbiguousError;
52
+ exitCode(): number;
53
+ statusCode(): number;
54
+ }
55
+ /**
56
+ * Assertion failed (invariant violation).
57
+ *
58
+ * Used by assertion utilities that return Result types instead of throwing.
59
+ * AssertionError indicates a programming bug — an invariant that should
60
+ * never be violated was broken. These are internal errors, not user input
61
+ * validation failures.
62
+ *
63
+ * **Category rationale**: Uses `internal` (not `validation`) because:
64
+ * - Assertions check **invariants** (programmer assumptions), not user input
65
+ * - A failed assertion means "this should be impossible if the code is correct"
66
+ * - User-facing validation uses {@link ValidationError} with helpful field info
67
+ * - HTTP 500 is correct: this is a server bug, not a client mistake
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * // In domain logic after validation has passed
72
+ * const result = assertDefined(cachedValue, "Cache should always have value after init");
73
+ * if (result.isErr()) {
74
+ * return result; // Propagate as internal error
75
+ * }
76
+ * ```
77
+ *
78
+ * @see ValidationError - For user input validation failures (HTTP 400)
79
+ */
80
+ declare class AssertionError extends AssertionErrorBase {
81
+ readonly category: "internal";
82
+ exitCode(): number;
83
+ statusCode(): number;
84
+ }
85
+ /**
86
+ * Requested resource not found.
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * new NotFoundError({ message: "note not found: abc123", resourceType: "note", resourceId: "abc123" });
91
+ * new NotFoundError({
92
+ * message: "Heading not found",
93
+ * resourceType: "heading",
94
+ * resourceId: "h:Intro",
95
+ * context: { availableHeadings: ["Introduction", "Getting Started"] },
96
+ * });
97
+ * ```
98
+ */
99
+ declare class NotFoundError extends NotFoundErrorBase {
100
+ readonly category: "not_found";
101
+ /** Create a NotFoundError with auto-generated message. */
102
+ static create(resourceType: string, resourceId: string, context?: Record<string, unknown>): NotFoundError;
103
+ exitCode(): number;
104
+ statusCode(): number;
105
+ }
106
+ /**
107
+ * Resource already exists — the inverse of {@link NotFoundError}.
108
+ *
109
+ * Use when a create/write operation fails because the target resource
110
+ * is already present. Carries `resourceType` and `resourceId` to identify
111
+ * what already exists, mirroring {@link NotFoundError}'s structure.
112
+ *
113
+ * Maps to HTTP 409 (Conflict) and exit code 3.
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * new AlreadyExistsError({
118
+ * message: "File already exists: notes/meeting.md",
119
+ * resourceType: "file",
120
+ * resourceId: "notes/meeting.md",
121
+ * });
122
+ * AlreadyExistsError.create("file", "notes/meeting.md");
123
+ * ```
124
+ *
125
+ * @see ConflictError - For general state conflicts (version mismatch, concurrent modification)
126
+ * @see NotFoundError - The inverse: resource does not exist
127
+ */
128
+ declare class AlreadyExistsError extends AlreadyExistsErrorBase {
129
+ readonly category: "conflict";
130
+ /** Create an AlreadyExistsError with auto-generated message. */
131
+ static create(resourceType: string, resourceId: string, context?: Record<string, unknown>): AlreadyExistsError;
132
+ exitCode(): number;
133
+ statusCode(): number;
134
+ }
135
+ /**
136
+ * State conflict (version mismatch, concurrent modification).
137
+ *
138
+ * Use for general conflicts that don't fit {@link AlreadyExistsError}:
139
+ * optimistic locking failures, concurrent writes, ETag mismatches,
140
+ * or any case where the operation can't proceed due to state divergence.
141
+ *
142
+ * Maps to HTTP 409 (Conflict) and exit code 3.
143
+ *
144
+ * **Choosing the right conflict error:**
145
+ * - Resource already exists? Use {@link AlreadyExistsError}
146
+ * - Version/ETag mismatch? Use {@link ConflictError}
147
+ * - Concurrent modification detected? Use {@link ConflictError}
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * new ConflictError({ message: "Resource was modified by another process" });
152
+ * ConflictError.create("ETag mismatch: expected abc, got def");
153
+ * ```
154
+ *
155
+ * @see AlreadyExistsError - For "resource already exists" specifically
156
+ */
157
+ declare class ConflictError extends ConflictErrorBase {
158
+ readonly category: "conflict";
159
+ /** Create a ConflictError with optional context. */
160
+ static create(message: string, context?: Record<string, unknown>): ConflictError;
161
+ exitCode(): number;
162
+ statusCode(): number;
163
+ }
164
+ export { ValidationError, AmbiguousError, AssertionError, NotFoundError, AlreadyExistsError, ConflictError };