@agentuity/schema 0.0.69

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 (115) hide show
  1. package/AGENTS.md +86 -0
  2. package/README.md +323 -0
  3. package/dist/base.d.ts +111 -0
  4. package/dist/base.d.ts.map +1 -0
  5. package/dist/base.js +93 -0
  6. package/dist/base.js.map +1 -0
  7. package/dist/coerce/boolean.d.ts +37 -0
  8. package/dist/coerce/boolean.d.ts.map +1 -0
  9. package/dist/coerce/boolean.js +49 -0
  10. package/dist/coerce/boolean.js.map +1 -0
  11. package/dist/coerce/date.d.ts +36 -0
  12. package/dist/coerce/date.d.ts.map +1 -0
  13. package/dist/coerce/date.js +60 -0
  14. package/dist/coerce/date.js.map +1 -0
  15. package/dist/coerce/number.d.ts +36 -0
  16. package/dist/coerce/number.d.ts.map +1 -0
  17. package/dist/coerce/number.js +59 -0
  18. package/dist/coerce/number.js.map +1 -0
  19. package/dist/coerce/string.d.ts +35 -0
  20. package/dist/coerce/string.d.ts.map +1 -0
  21. package/dist/coerce/string.js +47 -0
  22. package/dist/coerce/string.js.map +1 -0
  23. package/dist/complex/array.d.ts +56 -0
  24. package/dist/complex/array.d.ts.map +1 -0
  25. package/dist/complex/array.js +96 -0
  26. package/dist/complex/array.js.map +1 -0
  27. package/dist/complex/object.d.ts +76 -0
  28. package/dist/complex/object.d.ts.map +1 -0
  29. package/dist/complex/object.js +104 -0
  30. package/dist/complex/object.js.map +1 -0
  31. package/dist/complex/record.d.ts +53 -0
  32. package/dist/complex/record.d.ts.map +1 -0
  33. package/dist/complex/record.js +109 -0
  34. package/dist/complex/record.js.map +1 -0
  35. package/dist/index.d.ts +151 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +128 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/json-schema.d.ts +60 -0
  40. package/dist/json-schema.d.ts.map +1 -0
  41. package/dist/json-schema.js +280 -0
  42. package/dist/json-schema.js.map +1 -0
  43. package/dist/primitives/any.d.ts +44 -0
  44. package/dist/primitives/any.d.ts.map +1 -0
  45. package/dist/primitives/any.js +57 -0
  46. package/dist/primitives/any.js.map +1 -0
  47. package/dist/primitives/boolean.d.ts +39 -0
  48. package/dist/primitives/boolean.d.ts.map +1 -0
  49. package/dist/primitives/boolean.js +53 -0
  50. package/dist/primitives/boolean.js.map +1 -0
  51. package/dist/primitives/null.d.ts +26 -0
  52. package/dist/primitives/null.d.ts.map +1 -0
  53. package/dist/primitives/null.js +40 -0
  54. package/dist/primitives/null.js.map +1 -0
  55. package/dist/primitives/number.d.ts +87 -0
  56. package/dist/primitives/number.d.ts.map +1 -0
  57. package/dist/primitives/number.js +129 -0
  58. package/dist/primitives/number.js.map +1 -0
  59. package/dist/primitives/string.d.ts +64 -0
  60. package/dist/primitives/string.d.ts.map +1 -0
  61. package/dist/primitives/string.js +102 -0
  62. package/dist/primitives/string.js.map +1 -0
  63. package/dist/primitives/undefined.d.ts +26 -0
  64. package/dist/primitives/undefined.d.ts.map +1 -0
  65. package/dist/primitives/undefined.js +40 -0
  66. package/dist/primitives/undefined.js.map +1 -0
  67. package/dist/primitives/unknown.d.ts +47 -0
  68. package/dist/primitives/unknown.d.ts.map +1 -0
  69. package/dist/primitives/unknown.js +56 -0
  70. package/dist/primitives/unknown.js.map +1 -0
  71. package/dist/utils/literal.d.ts +47 -0
  72. package/dist/utils/literal.d.ts.map +1 -0
  73. package/dist/utils/literal.js +64 -0
  74. package/dist/utils/literal.js.map +1 -0
  75. package/dist/utils/nullable.d.ts +50 -0
  76. package/dist/utils/nullable.d.ts.map +1 -0
  77. package/dist/utils/nullable.js +69 -0
  78. package/dist/utils/nullable.js.map +1 -0
  79. package/dist/utils/optional.d.ts +50 -0
  80. package/dist/utils/optional.d.ts.map +1 -0
  81. package/dist/utils/optional.js +69 -0
  82. package/dist/utils/optional.js.map +1 -0
  83. package/dist/utils/union.d.ts +60 -0
  84. package/dist/utils/union.d.ts.map +1 -0
  85. package/dist/utils/union.js +87 -0
  86. package/dist/utils/union.js.map +1 -0
  87. package/package.json +39 -0
  88. package/src/__tests__/coerce.test.ts +88 -0
  89. package/src/__tests__/complex.test.ts +124 -0
  90. package/src/__tests__/errors.test.ts +129 -0
  91. package/src/__tests__/json-schema.test.ts +138 -0
  92. package/src/__tests__/primitives.test.ts +184 -0
  93. package/src/__tests__/type-inference.test.ts +68 -0
  94. package/src/__tests__/utils.test.ts +100 -0
  95. package/src/base.ts +185 -0
  96. package/src/coerce/boolean.ts +56 -0
  97. package/src/coerce/date.ts +68 -0
  98. package/src/coerce/number.ts +67 -0
  99. package/src/coerce/string.ts +54 -0
  100. package/src/complex/array.ts +108 -0
  101. package/src/complex/object.ts +141 -0
  102. package/src/complex/record.ts +129 -0
  103. package/src/index.ts +177 -0
  104. package/src/json-schema.ts +331 -0
  105. package/src/primitives/any.ts +64 -0
  106. package/src/primitives/boolean.ts +60 -0
  107. package/src/primitives/null.ts +47 -0
  108. package/src/primitives/number.ts +141 -0
  109. package/src/primitives/string.ts +113 -0
  110. package/src/primitives/undefined.ts +47 -0
  111. package/src/primitives/unknown.ts +63 -0
  112. package/src/utils/literal.ts +71 -0
  113. package/src/utils/nullable.ts +80 -0
  114. package/src/utils/optional.ts +80 -0
  115. package/src/utils/union.ts +103 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nullable.d.ts","sourceRoot":"","sources":["../../src/utils/nullable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAG7C;;;;;;;;;;;;;GAaG;AAEH,qBAAa,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CACrD,YAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAEnD,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,WAAW;;;0BAGD,OAAO;eAMM;YAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;YAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;SAAE;MACjF;IAGF,OAAO,CAAC,YAAY,CAAyC;gBAEjD,MAAM,EAAE,CAAC;IAIrB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKnC,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAO5E,QAAQ;IAIR,KAAK,0EAA2B;IAChC,SAAS,wGAA+B;CACxC;AAED;;;;;;;;;;;;GAYG;AAEH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAEjF"}
@@ -0,0 +1,69 @@
1
+ import { success, createParseMethods } from '../base';
2
+ /**
3
+ * Schema for nullable values (T | null).
4
+ * Accepts null or the wrapped schema's type.
5
+ *
6
+ * @template T - The wrapped schema type
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const schema = s.nullable(s.string());
11
+ * schema.parse('hello'); // 'hello'
12
+ * schema.parse(null); // null
13
+ * schema.parse(123); // throws ValidationError
14
+ * ```
15
+ */
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ export class NullableSchema {
18
+ schema;
19
+ description;
20
+ '~standard' = {
21
+ version: 1,
22
+ vendor: 'agentuity',
23
+ validate: (value) => {
24
+ if (value === null) {
25
+ return success(null);
26
+ }
27
+ return this.schema['~standard'].validate(value);
28
+ },
29
+ types: undefined,
30
+ };
31
+ // Type-safe parse methods for this instance
32
+ parseMethods = createParseMethods();
33
+ constructor(schema) {
34
+ this.schema = schema;
35
+ }
36
+ describe(description) {
37
+ this.description = description;
38
+ return this;
39
+ }
40
+ optional() {
41
+ // Import here to avoid circular dependency
42
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
43
+ const { optional } = require('./optional.js');
44
+ return optional(this);
45
+ }
46
+ nullable() {
47
+ return this; // Already nullable
48
+ }
49
+ parse = this.parseMethods.parse;
50
+ safeParse = this.parseMethods.safeParse;
51
+ }
52
+ /**
53
+ * Make a schema nullable (T | null).
54
+ *
55
+ * @param schema - The schema to make nullable
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const userSchema = s.object({
60
+ * name: s.string(),
61
+ * bio: s.nullable(s.string())
62
+ * });
63
+ * ```
64
+ */
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
+ export function nullable(schema) {
67
+ return new NullableSchema(schema);
68
+ }
69
+ //# sourceMappingURL=nullable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nullable.js","sourceRoot":"","sources":["../../src/utils/nullable.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEtD;;;;;;;;;;;;;GAaG;AACH,8DAA8D;AAC9D,MAAM,OAAO,cAAc;IAGjB,MAAM,CAAI;IACnB,WAAW,CAAU;IAEZ,WAAW,GAAG;QACtB,OAAO,EAAE,CAAU;QACnB,MAAM,EAAE,WAAW;QACnB,QAAQ,EAAE,CAAC,KAAc,EAAE,EAAE;YAC5B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACpB,OAAO,OAAO,CAAC,IAAuB,CAAC,CAAC;YACzC,CAAC;YACD,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC;QACD,KAAK,EAAE,SAA2E;KAClF,CAAC;IAEF,4CAA4C;IACpC,YAAY,GAAG,kBAAkB,EAAmB,CAAC;IAE7D,YAAY,MAAS;QACpB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAED,QAAQ,CAAC,WAAmB;QAC3B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,OAAO,IAAI,CAAC;IACb,CAAC;IAED,QAAQ;QACP,2CAA2C;QAC3C,iEAAiE;QACjE,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;QAC9C,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,QAAQ;QACP,OAAO,IAAI,CAAC,CAAC,mBAAmB;IACjC,CAAC;IAED,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;IAChC,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC;CACxC;AAED;;;;;;;;;;;;GAYG;AACH,8DAA8D;AAC9D,MAAM,UAAU,QAAQ,CAA6B,MAAS;IAC7D,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,50 @@
1
+ import type { Schema, Infer } from '../base';
2
+ /**
3
+ * Schema for optional values (T | undefined).
4
+ * Accepts undefined or the wrapped schema's type.
5
+ *
6
+ * @template T - The wrapped schema type
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const schema = s.optional(s.string());
11
+ * schema.parse('hello'); // 'hello'
12
+ * schema.parse(undefined); // undefined
13
+ * schema.parse(123); // throws ValidationError
14
+ * ```
15
+ */
16
+ export declare class OptionalSchema<T extends Schema<any, any>> implements Schema<Infer<T> | undefined, Infer<T> | undefined> {
17
+ readonly schema: T;
18
+ description?: string;
19
+ readonly '~standard': {
20
+ version: 1;
21
+ vendor: string;
22
+ validate: (value: unknown) => import("@agentuity/core").StandardSchemaV1.Result<any> | Promise<import("@agentuity/core").StandardSchemaV1.Result<any>>;
23
+ types: {
24
+ input: Infer<T> | undefined;
25
+ output: Infer<T> | undefined;
26
+ };
27
+ };
28
+ private parseMethods;
29
+ constructor(schema: T);
30
+ describe(description: string): this;
31
+ optional(): this;
32
+ nullable(): Schema<Infer<T> | undefined | null, Infer<T> | undefined | null>;
33
+ parse: (this: Schema<any, Infer<T> | undefined>, value: unknown) => Infer<T> | undefined;
34
+ safeParse: (this: Schema<any, Infer<T> | undefined>, value: unknown) => import("..").SafeParseResult<Infer<T> | undefined>;
35
+ }
36
+ /**
37
+ * Make a schema optional (T | undefined).
38
+ *
39
+ * @param schema - The schema to make optional
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const userSchema = s.object({
44
+ * name: s.string(),
45
+ * nickname: s.optional(s.string())
46
+ * });
47
+ * ```
48
+ */
49
+ export declare function optional<T extends Schema<any, any>>(schema: T): OptionalSchema<T>;
50
+ //# sourceMappingURL=optional.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optional.d.ts","sourceRoot":"","sources":["../../src/utils/optional.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAG7C;;;;;;;;;;;;;GAaG;AAEH,qBAAa,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CACrD,YAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAE7D,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,WAAW;;;0BAGD,OAAO;eAMM;YAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;YAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;SAAE;MAC3F;IAGF,OAAO,CAAC,YAAY,CAA8C;gBAEtD,MAAM,EAAE,CAAC;IAIrB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKnC,QAAQ;IAIR,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC;IAO5E,KAAK,oFAA2B;IAChC,SAAS,kHAA+B;CACxC;AAED;;;;;;;;;;;;GAYG;AAEH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAEjF"}
@@ -0,0 +1,69 @@
1
+ import { success, createParseMethods } from '../base';
2
+ /**
3
+ * Schema for optional values (T | undefined).
4
+ * Accepts undefined or the wrapped schema's type.
5
+ *
6
+ * @template T - The wrapped schema type
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const schema = s.optional(s.string());
11
+ * schema.parse('hello'); // 'hello'
12
+ * schema.parse(undefined); // undefined
13
+ * schema.parse(123); // throws ValidationError
14
+ * ```
15
+ */
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ export class OptionalSchema {
18
+ schema;
19
+ description;
20
+ '~standard' = {
21
+ version: 1,
22
+ vendor: 'agentuity',
23
+ validate: (value) => {
24
+ if (value === undefined) {
25
+ return success(undefined);
26
+ }
27
+ return this.schema['~standard'].validate(value);
28
+ },
29
+ types: undefined,
30
+ };
31
+ // Type-safe parse methods for this instance
32
+ parseMethods = createParseMethods();
33
+ constructor(schema) {
34
+ this.schema = schema;
35
+ }
36
+ describe(description) {
37
+ this.description = description;
38
+ return this;
39
+ }
40
+ optional() {
41
+ return this; // Already optional
42
+ }
43
+ nullable() {
44
+ // Import here to avoid circular dependency
45
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
46
+ const { nullable } = require('./nullable.js');
47
+ return nullable(this);
48
+ }
49
+ parse = this.parseMethods.parse;
50
+ safeParse = this.parseMethods.safeParse;
51
+ }
52
+ /**
53
+ * Make a schema optional (T | undefined).
54
+ *
55
+ * @param schema - The schema to make optional
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const userSchema = s.object({
60
+ * name: s.string(),
61
+ * nickname: s.optional(s.string())
62
+ * });
63
+ * ```
64
+ */
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
+ export function optional(schema) {
67
+ return new OptionalSchema(schema);
68
+ }
69
+ //# sourceMappingURL=optional.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optional.js","sourceRoot":"","sources":["../../src/utils/optional.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEtD;;;;;;;;;;;;;GAaG;AACH,8DAA8D;AAC9D,MAAM,OAAO,cAAc;IAGjB,MAAM,CAAI;IACnB,WAAW,CAAU;IAEZ,WAAW,GAAG;QACtB,OAAO,EAAE,CAAU;QACnB,MAAM,EAAE,WAAW;QACnB,QAAQ,EAAE,CAAC,KAAc,EAAE,EAAE;YAC5B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACzB,OAAO,OAAO,CAAC,SAAiC,CAAC,CAAC;YACnD,CAAC;YACD,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC;QACD,KAAK,EAAE,SAAqF;KAC5F,CAAC;IAEF,4CAA4C;IACpC,YAAY,GAAG,kBAAkB,EAAwB,CAAC;IAElE,YAAY,MAAS;QACpB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAED,QAAQ,CAAC,WAAmB;QAC3B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,OAAO,IAAI,CAAC;IACb,CAAC;IAED,QAAQ;QACP,OAAO,IAAI,CAAC,CAAC,mBAAmB;IACjC,CAAC;IAED,QAAQ;QACP,2CAA2C;QAC3C,iEAAiE;QACjE,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;QAC9C,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;IAChC,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC;CACxC;AAED;;;;;;;;;;;;GAYG;AACH,8DAA8D;AAC9D,MAAM,UAAU,QAAQ,CAA6B,MAAS;IAC7D,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,60 @@
1
+ import type { Schema, Infer } from '../base';
2
+ type InferUnion<T extends Schema<any, any>[]> = Infer<T[number]>;
3
+ /**
4
+ * Schema for union types (one of multiple possible schemas).
5
+ * Validates against each schema until one succeeds.
6
+ *
7
+ * @template T - Array of schema types
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const idSchema = s.union(s.string(), s.number());
12
+ * idSchema.parse('abc123'); // 'abc123'
13
+ * idSchema.parse(123); // 123
14
+ *
15
+ * const roleSchema = s.union(
16
+ * s.literal('admin'),
17
+ * s.literal('user'),
18
+ * s.literal('guest')
19
+ * );
20
+ * ```
21
+ */
22
+ export declare class UnionSchema<T extends Schema<any, any>[]> implements Schema<InferUnion<T>, InferUnion<T>> {
23
+ private schemas;
24
+ description?: string;
25
+ private parseMethods;
26
+ constructor(schemas: T);
27
+ readonly '~standard': {
28
+ version: 1;
29
+ vendor: string;
30
+ validate: (value: unknown) => any;
31
+ types: {
32
+ input: InferUnion<T>;
33
+ output: InferUnion<T>;
34
+ };
35
+ };
36
+ describe(description: string): this;
37
+ optional(): import("..").OptionalSchema<this>;
38
+ nullable(): import("..").NullableSchema<this>;
39
+ parse: (this: Schema<any, InferUnion<T>>, value: unknown) => InferUnion<T>;
40
+ safeParse: (this: Schema<any, InferUnion<T>>, value: unknown) => import("..").SafeParseResult<InferUnion<T>>;
41
+ }
42
+ /**
43
+ * Create a union schema (one of multiple possible types).
44
+ *
45
+ * @param schemas - Variable number of schemas to union
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const idSchema = s.union(s.string(), s.number());
50
+ *
51
+ * const roleSchema = s.union(
52
+ * s.literal('admin'),
53
+ * s.literal('user'),
54
+ * s.literal('guest')
55
+ * );
56
+ * ```
57
+ */
58
+ export declare function union<T extends Schema<any, any>[]>(...schemas: T): UnionSchema<T>;
59
+ export {};
60
+ //# sourceMappingURL=union.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"union.d.ts","sourceRoot":"","sources":["../../src/utils/union.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAM7C,KAAK,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAEjE;;;;;;;;;;;;;;;;;;GAkBG;AAEH,qBAAa,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CACpD,YAAW,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAKnC,OAAO,CAAC,OAAO;IAH3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,YAAY,CAAuC;gBAEvC,OAAO,EAAE,CAAC;IAE9B,QAAQ,CAAC,WAAW;;;0BAGD,OAAO;eAwBM;YAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;YAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;SAAE;MAC7E;IAEF,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKnC,QAAQ;IAIR,QAAQ;IAIR,KAAK,sEAA2B;IAChC,SAAS,oGAA+B;CACxC;AAED;;;;;;;;;;;;;;;GAeG;AAEH,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAEjF"}
@@ -0,0 +1,87 @@
1
+ import { createIssue, failure, createParseMethods } from '../base';
2
+ import { optional } from '../utils/optional';
3
+ import { nullable } from '../utils/nullable';
4
+ /**
5
+ * Schema for union types (one of multiple possible schemas).
6
+ * Validates against each schema until one succeeds.
7
+ *
8
+ * @template T - Array of schema types
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const idSchema = s.union(s.string(), s.number());
13
+ * idSchema.parse('abc123'); // 'abc123'
14
+ * idSchema.parse(123); // 123
15
+ *
16
+ * const roleSchema = s.union(
17
+ * s.literal('admin'),
18
+ * s.literal('user'),
19
+ * s.literal('guest')
20
+ * );
21
+ * ```
22
+ */
23
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
+ export class UnionSchema {
25
+ schemas;
26
+ description;
27
+ parseMethods = createParseMethods();
28
+ constructor(schemas) {
29
+ this.schemas = schemas;
30
+ }
31
+ '~standard' = {
32
+ version: 1,
33
+ vendor: 'agentuity',
34
+ validate: (value) => {
35
+ const allIssues = [];
36
+ for (const schema of this.schemas) {
37
+ const result = schema['~standard'].validate(value);
38
+ // Only support synchronous validation for now
39
+ if (result instanceof Promise) {
40
+ throw new Error('Async validation not supported');
41
+ }
42
+ if (!result.issues) {
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ return result;
45
+ }
46
+ allIssues.push(...result.issues);
47
+ }
48
+ return failure([
49
+ createIssue(`Value did not match any of the union types (${allIssues.length} validation errors)`),
50
+ ]);
51
+ },
52
+ types: undefined,
53
+ };
54
+ describe(description) {
55
+ this.description = description;
56
+ return this;
57
+ }
58
+ optional() {
59
+ return optional(this);
60
+ }
61
+ nullable() {
62
+ return nullable(this);
63
+ }
64
+ parse = this.parseMethods.parse;
65
+ safeParse = this.parseMethods.safeParse;
66
+ }
67
+ /**
68
+ * Create a union schema (one of multiple possible types).
69
+ *
70
+ * @param schemas - Variable number of schemas to union
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * const idSchema = s.union(s.string(), s.number());
75
+ *
76
+ * const roleSchema = s.union(
77
+ * s.literal('admin'),
78
+ * s.literal('user'),
79
+ * s.literal('guest')
80
+ * );
81
+ * ```
82
+ */
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ export function union(...schemas) {
85
+ return new UnionSchema(schemas);
86
+ }
87
+ //# sourceMappingURL=union.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"union.js","sourceRoot":"","sources":["../../src/utils/union.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAK7C;;;;;;;;;;;;;;;;;;GAkBG;AACH,8DAA8D;AAC9D,MAAM,OAAO,WAAW;IAMH;IAHpB,WAAW,CAAU;IACb,YAAY,GAAG,kBAAkB,EAAiB,CAAC;IAE3D,YAAoB,OAAU;QAAV,YAAO,GAAP,OAAO,CAAG;IAAG,CAAC;IAEzB,WAAW,GAAG;QACtB,OAAO,EAAE,CAAU;QACnB,MAAM,EAAE,WAAW;QACnB,QAAQ,EAAE,CAAC,KAAc,EAAE,EAAE;YAC5B,MAAM,SAAS,GAAqC,EAAE,CAAC;YAEvD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACnC,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAEnD,8CAA8C;gBAC9C,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;oBAC/B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;gBACnD,CAAC;gBAED,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;oBACpB,8DAA8D;oBAC9D,OAAO,MAAa,CAAC;gBACtB,CAAC;gBACD,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YAClC,CAAC;YAED,OAAO,OAAO,CAAC;gBACd,WAAW,CACV,+CAA+C,SAAS,CAAC,MAAM,qBAAqB,CACpF;aACD,CAAC,CAAC;QACJ,CAAC;QACD,KAAK,EAAE,SAAuE;KAC9E,CAAC;IAEF,QAAQ,CAAC,WAAmB;QAC3B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,OAAO,IAAI,CAAC;IACb,CAAC;IAED,QAAQ;QACP,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,QAAQ;QACP,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;IAChC,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC;CACxC;AAED;;;;;;;;;;;;;;;GAeG;AACH,8DAA8D;AAC9D,MAAM,UAAU,KAAK,CAA+B,GAAG,OAAU;IAChE,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@agentuity/schema",
3
+ "version": "0.0.69",
4
+ "license": "Apache-2.0",
5
+ "author": "Agentuity employees and contributors",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "AGENTS.md",
11
+ "README.md",
12
+ "src",
13
+ "dist"
14
+ ],
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ }
20
+ },
21
+ "scripts": {
22
+ "clean": "rm -rf dist",
23
+ "build": "bunx tsc --build --force",
24
+ "typecheck": "bunx tsc --noEmit",
25
+ "test": "bun test",
26
+ "prepublishOnly": "bun run clean && bun run build"
27
+ },
28
+ "dependencies": {
29
+ "@agentuity/core": "0.0.69"
30
+ },
31
+ "devDependencies": {
32
+ "@types/bun": "latest",
33
+ "bun-types": "latest",
34
+ "typescript": "^5.9.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }
@@ -0,0 +1,88 @@
1
+ import { describe, test, expect } from 'bun:test';
2
+ import { s, ValidationError } from '../index.js';
3
+
4
+ describe('Coercion Schemas', () => {
5
+ describe('coerce.string', () => {
6
+ const schema = s.coerce.string();
7
+
8
+ test('should coerce numbers to strings', () => {
9
+ expect(schema.parse(123)).toBe('123');
10
+ });
11
+
12
+ test('should coerce booleans to strings', () => {
13
+ expect(schema.parse(true)).toBe('true');
14
+ expect(schema.parse(false)).toBe('false');
15
+ });
16
+
17
+ test('should keep strings as-is', () => {
18
+ expect(schema.parse('hello')).toBe('hello');
19
+ });
20
+ });
21
+
22
+ describe('coerce.number', () => {
23
+ const schema = s.coerce.number();
24
+
25
+ test('should coerce string numbers', () => {
26
+ expect(schema.parse('123')).toBe(123);
27
+ expect(schema.parse('45.67')).toBe(45.67);
28
+ });
29
+
30
+ test('should coerce booleans', () => {
31
+ expect(schema.parse(true)).toBe(1);
32
+ expect(schema.parse(false)).toBe(0);
33
+ });
34
+
35
+ test('should keep numbers as-is', () => {
36
+ expect(schema.parse(42)).toBe(42);
37
+ });
38
+
39
+ test('should reject invalid coercions', () => {
40
+ expect(() => schema.parse('not-a-number')).toThrow(ValidationError);
41
+ expect(() => schema.parse(NaN)).toThrow(ValidationError);
42
+ });
43
+ });
44
+
45
+ describe('coerce.boolean', () => {
46
+ const schema = s.coerce.boolean();
47
+
48
+ test('should coerce truthy values', () => {
49
+ expect(schema.parse(1)).toBe(true);
50
+ expect(schema.parse('hello')).toBe(true);
51
+ expect(schema.parse([])).toBe(true);
52
+ });
53
+
54
+ test('should coerce falsy values', () => {
55
+ expect(schema.parse(0)).toBe(false);
56
+ expect(schema.parse('')).toBe(false);
57
+ expect(schema.parse(null)).toBe(false);
58
+ expect(schema.parse(undefined)).toBe(false);
59
+ });
60
+ });
61
+
62
+ describe('coerce.date', () => {
63
+ const schema = s.coerce.date();
64
+
65
+ test('should coerce ISO strings', () => {
66
+ const result = schema.parse('2025-01-01');
67
+ expect(result).toBeInstanceOf(Date);
68
+ expect(result.getFullYear()).toBe(2025);
69
+ });
70
+
71
+ test('should coerce timestamps', () => {
72
+ const timestamp = Date.now();
73
+ const result = schema.parse(timestamp);
74
+ expect(result).toBeInstanceOf(Date);
75
+ expect(result.getTime()).toBe(timestamp);
76
+ });
77
+
78
+ test('should keep dates as-is', () => {
79
+ const date = new Date('2025-01-01');
80
+ const result = schema.parse(date);
81
+ expect(result).toBe(date);
82
+ });
83
+
84
+ test('should reject invalid dates', () => {
85
+ expect(() => schema.parse('invalid-date')).toThrow(ValidationError);
86
+ });
87
+ });
88
+ });
@@ -0,0 +1,124 @@
1
+ import { describe, test, expect } from 'bun:test';
2
+ import { s, ValidationError } from '../index.js';
3
+
4
+ describe('Complex Schemas', () => {
5
+ describe('object', () => {
6
+ const schema = s.object({
7
+ name: s.string(),
8
+ age: s.number(),
9
+ active: s.boolean(),
10
+ });
11
+
12
+ test('should validate objects', () => {
13
+ const result = schema.parse({
14
+ name: 'John',
15
+ age: 30,
16
+ active: true,
17
+ });
18
+ expect(result.name).toBe('John');
19
+ expect(result.age).toBe(30);
20
+ expect(result.active).toBe(true);
21
+ });
22
+
23
+ test('should reject invalid objects', () => {
24
+ expect(() =>
25
+ schema.parse({
26
+ name: 'John',
27
+ age: 'thirty',
28
+ active: true,
29
+ })
30
+ ).toThrow(ValidationError);
31
+ });
32
+
33
+ test('should report field path in errors', () => {
34
+ try {
35
+ schema.parse({
36
+ name: 'John',
37
+ age: 'invalid',
38
+ active: true,
39
+ });
40
+ throw new Error('Expected parse to throw');
41
+ } catch (error) {
42
+ expect(error).toBeInstanceOf(ValidationError);
43
+ if (error instanceof ValidationError) {
44
+ expect(error.issues[0].path).toEqual(['age']);
45
+ } else {
46
+ throw error;
47
+ }
48
+ }
49
+ });
50
+
51
+ test('should work with nested objects', () => {
52
+ const nested = s.object({
53
+ user: s.object({
54
+ name: s.string(),
55
+ email: s.string(),
56
+ }),
57
+ });
58
+
59
+ const result = nested.parse({
60
+ user: {
61
+ name: 'Alice',
62
+ email: 'alice@example.com',
63
+ },
64
+ });
65
+ expect(result.user.name).toBe('Alice');
66
+ });
67
+ });
68
+
69
+ describe('array', () => {
70
+ const schema = s.array(s.string());
71
+
72
+ test('should validate arrays', () => {
73
+ const result = schema.parse(['a', 'b', 'c']);
74
+ expect(result).toEqual(['a', 'b', 'c']);
75
+ });
76
+
77
+ test('should reject non-arrays', () => {
78
+ expect(() => schema.parse('not-an-array')).toThrow(ValidationError);
79
+ });
80
+
81
+ test('should validate array items', () => {
82
+ expect(() => schema.parse(['a', 123, 'c'])).toThrow(ValidationError);
83
+ });
84
+
85
+ test('should report array index in errors', () => {
86
+ try {
87
+ schema.parse(['a', 123, 'c']);
88
+ throw new Error('Expected parse to throw');
89
+ } catch (error) {
90
+ expect(error).toBeInstanceOf(ValidationError);
91
+ if (error instanceof ValidationError) {
92
+ expect(error.issues[0].path).toContain(1);
93
+ } else {
94
+ throw error;
95
+ }
96
+ }
97
+ });
98
+
99
+ test('should work with array of objects', () => {
100
+ const objArray = s.array(
101
+ s.object({
102
+ id: s.number(),
103
+ name: s.string(),
104
+ })
105
+ );
106
+
107
+ const result = objArray.parse([
108
+ { id: 1, name: 'First' },
109
+ { id: 2, name: 'Second' },
110
+ ]);
111
+ expect(result.length).toBe(2);
112
+ expect(result[0].id).toBe(1);
113
+ });
114
+ });
115
+ });
116
+ describe('record', () => {
117
+ const schema = s.record(s.string(), s.number());
118
+ test('should validate records', () => {
119
+ expect(schema.parse({ a: 1, b: 2 })).toEqual({ a: 1, b: 2 });
120
+ });
121
+ test('should reject non-objects', () => {
122
+ expect(() => schema.parse([])).toThrow(ValidationError);
123
+ });
124
+ });