toolcraft-openapi 0.0.123 → 0.0.124

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 (39) hide show
  1. package/dist/composition.json +6 -1
  2. package/node_modules/toolcraft-schema/LICENSE +21 -0
  3. package/node_modules/toolcraft-schema/README.md +89 -0
  4. package/node_modules/toolcraft-schema/dist/index.compile-check.d.ts +1 -0
  5. package/node_modules/toolcraft-schema/dist/index.compile-check.js +17 -0
  6. package/node_modules/toolcraft-schema/dist/index.d.ts +184 -0
  7. package/node_modules/toolcraft-schema/dist/index.js +295 -0
  8. package/node_modules/toolcraft-schema/dist/json-schema/compiler.d.ts +11 -0
  9. package/node_modules/toolcraft-schema/dist/json-schema/compiler.js +390 -0
  10. package/node_modules/toolcraft-schema/dist/json-schema/evaluate.d.ts +3 -0
  11. package/node_modules/toolcraft-schema/dist/json-schema/evaluate.js +442 -0
  12. package/node_modules/toolcraft-schema/dist/json-schema/index.d.ts +5 -0
  13. package/node_modules/toolcraft-schema/dist/json-schema/index.js +19 -0
  14. package/node_modules/toolcraft-schema/dist/json-schema/types.d.ts +33 -0
  15. package/node_modules/toolcraft-schema/dist/json-schema/types.js +1 -0
  16. package/node_modules/toolcraft-schema/dist/json-schema/utils.d.ts +19 -0
  17. package/node_modules/toolcraft-schema/dist/json-schema/utils.js +171 -0
  18. package/node_modules/toolcraft-schema/dist/json-schema-document.d.ts +14 -0
  19. package/node_modules/toolcraft-schema/dist/json-schema-document.js +17 -0
  20. package/node_modules/toolcraft-schema/dist/json.compile-check.d.ts +1 -0
  21. package/node_modules/toolcraft-schema/dist/json.compile-check.js +2 -0
  22. package/node_modules/toolcraft-schema/dist/json.d.ts +10 -0
  23. package/node_modules/toolcraft-schema/dist/json.js +5 -0
  24. package/node_modules/toolcraft-schema/dist/oneof.compile-check.d.ts +1 -0
  25. package/node_modules/toolcraft-schema/dist/oneof.compile-check.js +12 -0
  26. package/node_modules/toolcraft-schema/dist/oneof.d.ts +15 -0
  27. package/node_modules/toolcraft-schema/dist/oneof.js +18 -0
  28. package/node_modules/toolcraft-schema/dist/record.compile-check.d.ts +1 -0
  29. package/node_modules/toolcraft-schema/dist/record.compile-check.js +2 -0
  30. package/node_modules/toolcraft-schema/dist/record.d.ts +5 -0
  31. package/node_modules/toolcraft-schema/dist/record.js +6 -0
  32. package/node_modules/toolcraft-schema/dist/union.compile-check.d.ts +1 -0
  33. package/node_modules/toolcraft-schema/dist/union.compile-check.js +9 -0
  34. package/node_modules/toolcraft-schema/dist/union.d.ts +8 -0
  35. package/node_modules/toolcraft-schema/dist/union.js +45 -0
  36. package/node_modules/toolcraft-schema/dist/validate.d.ts +17 -0
  37. package/node_modules/toolcraft-schema/dist/validate.js +379 -0
  38. package/node_modules/toolcraft-schema/package.json +33 -0
  39. package/package.json +6 -4
@@ -38,7 +38,12 @@
38
38
  },
39
39
  {
40
40
  "name": "toolcraft-openapi",
41
- "version": "0.0.123",
41
+ "version": "0.0.124",
42
+ "license": "MIT"
43
+ },
44
+ {
45
+ "name": "toolcraft-schema",
46
+ "version": "0.0.124",
42
47
  "license": "MIT"
43
48
  },
44
49
  {
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Poe Platform
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,89 @@
1
+ # toolcraft-schema
2
+
3
+ Zero-dependency schema builder for typed command inputs, runtime validation,
4
+ and JSON Schema generation.
5
+
6
+ ## Features
7
+
8
+ - Zero runtime dependencies
9
+ - Typed schema descriptors
10
+ - `Static<typeof schema>` type inference
11
+ - Runtime validation with `validateValue()`
12
+ - JSON Schema serialization via `toJsonSchema()`
13
+ - JSON Schema document serialization via `toJsonSchemaDocument()`
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ import { S, toJsonSchema, toJsonSchemaDocument, validateValue } from "toolcraft-schema";
19
+ import type { Static } from "toolcraft-schema";
20
+
21
+ const schema = S.Object({
22
+ name: S.String({ description: "User name" }),
23
+ retries: S.Optional(S.Number({ default: 3 })),
24
+ mode: S.Enum(["fast", "safe"] as const, { default: "safe" }),
25
+ tags: S.Array(S.String(), { default: [] })
26
+ });
27
+
28
+ type Input = Static<typeof schema>;
29
+ // {
30
+ // name: string;
31
+ // retries?: number;
32
+ // mode: "fast" | "safe";
33
+ // tags: string[];
34
+ // }
35
+
36
+ const jsonSchema = toJsonSchema(schema);
37
+ const document = toJsonSchemaDocument(schema, {
38
+ id: "https://example.test/schema.json",
39
+ title: "Example schema"
40
+ });
41
+ const validation = validateValue(schema, {
42
+ name: "Ada",
43
+ mode: "safe",
44
+ tags: []
45
+ });
46
+ ```
47
+
48
+ ## API
49
+
50
+ ### Builders
51
+
52
+ - `S.String({ description?, default?, short?, cliAliases? })`
53
+ - `S.Number({ description?, default?, short?, cliAliases? })`
54
+ - `S.Boolean({ description?, default?, short?, cliAliases? })`
55
+ - `S.Enum(values, { description?, default?, short?, cliAliases? })`
56
+ - `S.Array(itemSchema, { description?, default?, short?, cliAliases? })`
57
+ - `S.Record(valueSchema, { description?, default? })`
58
+ - `S.Union([schemaA, schemaB], { description?, default? })`
59
+ - `S.OneOf([schemaA, schemaB], { description?, default? })`
60
+ - `S.Object({ [key]: schema })`
61
+ - `S.Optional(schema)`
62
+
63
+ ### Type helpers
64
+
65
+ - `Static<typeof schema>` infers the runtime TypeScript shape for a schema descriptor.
66
+ - Object properties wrapped in `S.Optional(...)` become optional properties in `Static`.
67
+
68
+ ### JSON Schema generation
69
+
70
+ - `toJsonSchema(schema)` converts any schema descriptor to standard JSON Schema.
71
+ - `toJsonSchemaDocument(schema, options)` wraps `toJsonSchema(schema)` in a full JSON Schema document with `$schema`, optional `$id`, `title`, and `description`.
72
+ - Object properties not wrapped in `S.Optional(...)` are emitted in `required`.
73
+ - Defaults provided to schema builders are emitted as JSON Schema `default`.
74
+ - Nested `S.Object(...)` schemas produce nested JSON Schema objects.
75
+ - `S.Enum(...)` rejects empty or duplicate values at runtime for JavaScript callers.
76
+
77
+ ### Runtime validation
78
+
79
+ - `validateValue(schema, value)` returns `{ ok: true, value }` for valid input.
80
+ - Invalid input returns `{ ok: false, issues }` with path-aware diagnostics.
81
+ - Validation applies defaults from schema descriptors.
82
+
83
+ ## Environment Variables
84
+
85
+ This package exposes no environment variables.
86
+
87
+ ## Configuration
88
+
89
+ This package currently exposes no package-level configuration options.
@@ -0,0 +1,17 @@
1
+ import { S, toJsonSchemaDocument } from "./index.js";
2
+ const ignoredStringSchema = S.String({ description: "Name", default: "guest" });
3
+ const ignoredNumberSchema = S.Number({ description: "Count", default: 1 });
4
+ const ignoredBooleanSchema = S.Boolean({ description: "Enabled", default: false });
5
+ const ignoredEnumSchema = S.Enum(["admin", "user"], { default: "admin" });
6
+ const ignoredIntegerEnumSchema = S.Enum([1, 2], { jsonType: "integer" });
7
+ const ignoredArraySchema = S.Array(S.String(), { default: ["a"] });
8
+ const ignoredObjectSchema = S.Object({
9
+ name: S.String(),
10
+ retries: S.Optional(S.Number())
11
+ });
12
+ const ignoredOptionalSchema = S.Optional(S.Boolean());
13
+ const ignoredJsonSchemaDocument = toJsonSchemaDocument(ignoredObjectSchema, {
14
+ id: "https://example.test/schema.json",
15
+ title: "Example schema",
16
+ description: "Example schema document"
17
+ });
@@ -0,0 +1,184 @@
1
+ import { Json } from "./json.js";
2
+ import { OneOf } from "./oneof.js";
3
+ import { Record as RecordBuilder } from "./record.js";
4
+ import { Union } from "./union.js";
5
+ import { validate } from "./validate.js";
6
+ import type { JsonValue, JsonValueSchema } from "./json.js";
7
+ import type { JsonSchemaDocument, JsonSchemaDocumentOptions } from "./json-schema-document.js";
8
+ import type { OneOfSchema } from "./oneof.js";
9
+ import type { RecordSchema } from "./record.js";
10
+ import type { UnionSchema } from "./union.js";
11
+ import type { ValidationIssue, ValidationResult } from "./validate.js";
12
+ type JsonSchemaType = "string" | "number" | "integer" | "boolean" | "array" | "object";
13
+ type SchemaKind = "string" | "number" | "boolean" | "enum" | "array" | "object" | "optional" | "oneOf" | "union" | "record" | "json";
14
+ type EnumValue = string | number | boolean;
15
+ type JsonSchemaEnumValue = EnumValue | null;
16
+ type NumberJsonType = "number" | "integer";
17
+ type NonEmptyReadonlyArray<T> = readonly [T, ...T[]];
18
+ type ObjectShape = Record<string, AnySchema>;
19
+ type EmptyOptions = Record<never, never>;
20
+ type SchemaScope = "cli" | "mcp" | "sdk";
21
+ export type CliOutputMode = "rich" | "md" | "json";
22
+ export interface CliMissingParameterChoice<TValue> {
23
+ label: string;
24
+ value: TValue;
25
+ }
26
+ export interface CliMissingParameterContext {
27
+ commandPath: string;
28
+ params: Readonly<Record<string, unknown>>;
29
+ output: CliOutputMode;
30
+ stdinTTY: boolean;
31
+ stdoutTTY: boolean;
32
+ }
33
+ export interface CliMissingParameterResolution<TValue> {
34
+ choices: readonly CliMissingParameterChoice<TValue>[];
35
+ message?: string;
36
+ }
37
+ export interface CliSchemaOptions<TValue> {
38
+ resolveMissing?: (context: CliMissingParameterContext) => CliMissingParameterResolution<TValue> | undefined | Promise<CliMissingParameterResolution<TValue> | undefined>;
39
+ }
40
+ type StringMetadata = {
41
+ format?: string;
42
+ maxLength?: number;
43
+ minLength?: number;
44
+ pattern?: string;
45
+ secret?: boolean;
46
+ };
47
+ type NumberMetadata = {
48
+ maximum?: number;
49
+ minimum?: number;
50
+ secret?: boolean;
51
+ };
52
+ type ArrayMetadata = {
53
+ maxItems?: number;
54
+ minItems?: number;
55
+ };
56
+ type ObjectMetadata = {
57
+ additionalProperties?: boolean;
58
+ };
59
+ type OptionalKeys<TShape extends ObjectShape> = {
60
+ [TKey in keyof TShape]: TShape[TKey] extends OptionalSchema<any> ? TKey : never;
61
+ }[keyof TShape];
62
+ type RequiredKeys<TShape extends ObjectShape> = Exclude<keyof TShape, OptionalKeys<TShape>>;
63
+ type PropertyStatic<TSchema extends AnySchema> = TSchema extends OptionalSchema<infer TInner> ? Static<TInner> : Static<TSchema>;
64
+ type InferObject<TShape extends ObjectShape> = {
65
+ [TKey in RequiredKeys<TShape>]: PropertyStatic<TShape[TKey]>;
66
+ } & {
67
+ [TKey in OptionalKeys<TShape>]?: PropertyStatic<TShape[TKey]>;
68
+ };
69
+ type SchemaOptions<TDefault> = {
70
+ cli?: CliSchemaOptions<TDefault>;
71
+ description?: string;
72
+ cliDescription?: string;
73
+ cliAliases?: readonly string[];
74
+ default?: TDefault;
75
+ nullable?: boolean;
76
+ requiredScopes?: readonly SchemaScope[];
77
+ short?: string;
78
+ scope?: readonly SchemaScope[];
79
+ global?: boolean;
80
+ };
81
+ type WithNullable<TSchema extends AnySchema, TOptions extends {
82
+ nullable?: boolean;
83
+ }> = TOptions extends {
84
+ readonly nullable: true;
85
+ } ? TSchema & {
86
+ readonly nullable: true;
87
+ } : TSchema;
88
+ export interface SchemaBase<TKind extends SchemaKind, TStatic> {
89
+ readonly kind: TKind;
90
+ readonly cli?: CliSchemaOptions<TStatic>;
91
+ readonly description?: string;
92
+ readonly cliDescription?: string;
93
+ readonly cliAliases?: readonly string[];
94
+ readonly default?: TStatic;
95
+ readonly nullable?: boolean;
96
+ readonly requiredScopes?: readonly SchemaScope[];
97
+ readonly short?: string;
98
+ readonly scope?: readonly SchemaScope[];
99
+ readonly global?: boolean;
100
+ readonly __static?: TStatic;
101
+ }
102
+ export interface JsonSchema {
103
+ additionalProperties?: boolean | JsonSchema;
104
+ type?: JsonSchemaType;
105
+ description?: string;
106
+ default?: unknown;
107
+ enum?: ReadonlyArray<JsonSchemaEnumValue>;
108
+ format?: string;
109
+ items?: JsonSchema;
110
+ maxItems?: number;
111
+ maximum?: number;
112
+ maxLength?: number;
113
+ minItems?: number;
114
+ minimum?: number;
115
+ minLength?: number;
116
+ nullable?: boolean;
117
+ oneOf?: JsonSchema[];
118
+ pattern?: string;
119
+ properties?: Record<string, JsonSchema>;
120
+ required?: string[];
121
+ }
122
+ export interface StringSchema extends SchemaBase<"string", string>, StringMetadata {
123
+ }
124
+ export interface NumberSchema extends SchemaBase<"number", number>, NumberMetadata {
125
+ readonly jsonType?: NumberJsonType;
126
+ }
127
+ export type BooleanSchema = SchemaBase<"boolean", boolean>;
128
+ export interface EnumSchema<TValues extends NonEmptyReadonlyArray<EnumValue>> extends SchemaBase<"enum", TValues[number]> {
129
+ readonly values: TValues;
130
+ readonly jsonType?: "integer";
131
+ readonly labels?: Partial<Record<string, string>>;
132
+ readonly loadOptions?: (() => Array<{
133
+ label: string;
134
+ value: string;
135
+ }>) | (() => Promise<Array<{
136
+ label: string;
137
+ value: string;
138
+ }>>);
139
+ }
140
+ export interface ArraySchema<TItem extends AnySchema> extends SchemaBase<"array", Array<Static<TItem>>>, ArrayMetadata {
141
+ readonly item: TItem;
142
+ }
143
+ export interface ObjectSchema<TShape extends ObjectShape> extends SchemaBase<"object", InferObject<TShape>>, ObjectMetadata {
144
+ readonly shape: TShape;
145
+ }
146
+ export interface OptionalSchema<TInner extends AnySchema> extends SchemaBase<"optional", Static<TInner> | undefined> {
147
+ readonly inner: TInner;
148
+ }
149
+ export type AnySchema = StringSchema | NumberSchema | BooleanSchema | EnumSchema<NonEmptyReadonlyArray<EnumValue>> | ArraySchema<AnySchema> | ObjectSchema<ObjectShape> | OptionalSchema<AnySchema> | OneOfSchema<Record<string, ObjectSchema<any>>, string> | UnionSchema<readonly ObjectSchema<any>[]> | RecordSchema<AnySchema> | JsonValueSchema;
150
+ export type Static<TSchema extends AnySchema> = TSchema extends {
151
+ readonly nullable: true;
152
+ } ? TSchema extends SchemaBase<any, infer TStatic> ? TStatic | null : never : TSchema extends SchemaBase<any, infer TStatic> ? TStatic : never;
153
+ export declare const S: {
154
+ readonly String: <const TOptions extends SchemaOptions<string> & StringMetadata = EmptyOptions>(options?: TOptions) => WithNullable<StringSchema, TOptions>;
155
+ readonly Number: <const TOptions extends SchemaOptions<number> & NumberMetadata & {
156
+ jsonType?: NumberJsonType;
157
+ } = EmptyOptions>(options?: TOptions) => WithNullable<NumberSchema, TOptions>;
158
+ readonly Boolean: <const TOptions extends SchemaOptions<boolean> = EmptyOptions>(options?: TOptions) => WithNullable<BooleanSchema, TOptions>;
159
+ readonly Enum: <const TValues extends NonEmptyReadonlyArray<EnumValue>, const TOptions extends SchemaOptions<TValues[number]> & {
160
+ jsonType?: "integer";
161
+ labels?: Partial<Record<string, string>>;
162
+ loadOptions?: (() => Array<{
163
+ label: string;
164
+ value: string;
165
+ }>) | (() => Promise<Array<{
166
+ label: string;
167
+ value: string;
168
+ }>>);
169
+ } = EmptyOptions>(values: TValues, options?: TOptions) => WithNullable<EnumSchema<TValues>, TOptions>;
170
+ readonly Array: <TItem extends AnySchema, const TOptions extends SchemaOptions<Array<Static<TItem>>> & ArrayMetadata = EmptyOptions>(item: TItem, options?: TOptions) => WithNullable<ArraySchema<TItem>, TOptions>;
171
+ readonly Object: <const TShape extends ObjectShape, const TOptions extends SchemaOptions<InferObject<TShape>> & ObjectMetadata = EmptyOptions>(shape: TShape, options?: TOptions) => WithNullable<ObjectSchema<TShape>, TOptions>;
172
+ readonly Optional: <TInner extends AnySchema>(inner: TInner) => OptionalSchema<TInner>;
173
+ readonly OneOf: typeof OneOf;
174
+ readonly Union: typeof Union;
175
+ readonly Record: typeof RecordBuilder;
176
+ readonly Json: typeof Json;
177
+ };
178
+ export declare function toJsonSchema(schema: AnySchema): JsonSchema;
179
+ export declare function toJsonSchemaDocument(schema: AnySchema, options?: JsonSchemaDocumentOptions): JsonSchemaDocument;
180
+ export { Json, OneOf, RecordBuilder as Record, Union, validate };
181
+ export { compileJsonSchema, formatIssues } from "./json-schema/index.js";
182
+ export type { CompileJsonSchemaOptions, CompiledJsonSchema } from "./json-schema/index.js";
183
+ export type { JsonSchemaDocument, JsonSchemaDocumentOptions } from "./json-schema-document.js";
184
+ export type { JsonValue, JsonValueSchema, OneOfSchema, RecordSchema, UnionSchema, ValidationIssue, ValidationResult };
@@ -0,0 +1,295 @@
1
+ import { Json } from "./json.js";
2
+ import { createJsonSchemaDocument } from "./json-schema-document.js";
3
+ import { OneOf } from "./oneof.js";
4
+ import { Record as RecordBuilder } from "./record.js";
5
+ import { Union } from "./union.js";
6
+ import { validate } from "./validate.js";
7
+ function withMetadata(schema, jsonSchema) {
8
+ if (schema.description !== undefined) {
9
+ jsonSchema.description = schema.description;
10
+ }
11
+ if (schema.default !== undefined) {
12
+ jsonSchema.default = schema.default;
13
+ }
14
+ if (schema.nullable === true) {
15
+ jsonSchema.nullable = true;
16
+ }
17
+ return jsonSchema;
18
+ }
19
+ function withStringMetadata(schema, jsonSchema) {
20
+ if (schema.minLength !== undefined) {
21
+ jsonSchema.minLength = schema.minLength;
22
+ }
23
+ if (schema.maxLength !== undefined) {
24
+ jsonSchema.maxLength = schema.maxLength;
25
+ }
26
+ if (schema.pattern !== undefined) {
27
+ jsonSchema.pattern = schema.pattern;
28
+ }
29
+ if (schema.format !== undefined) {
30
+ jsonSchema.format = schema.format;
31
+ }
32
+ return withMetadata(schema, jsonSchema);
33
+ }
34
+ function withNumberMetadata(schema, jsonSchema) {
35
+ if (schema.minimum !== undefined) {
36
+ jsonSchema.minimum = schema.minimum;
37
+ }
38
+ if (schema.maximum !== undefined) {
39
+ jsonSchema.maximum = schema.maximum;
40
+ }
41
+ return withMetadata(schema, jsonSchema);
42
+ }
43
+ function withArrayMetadata(schema, jsonSchema) {
44
+ if (schema.minItems !== undefined) {
45
+ jsonSchema.minItems = schema.minItems;
46
+ }
47
+ if (schema.maxItems !== undefined) {
48
+ jsonSchema.maxItems = schema.maxItems;
49
+ }
50
+ return withMetadata(schema, jsonSchema);
51
+ }
52
+ function withObjectMetadata(schema, jsonSchema) {
53
+ jsonSchema.additionalProperties = schema.additionalProperties ?? false;
54
+ return withMetadata(schema, jsonSchema);
55
+ }
56
+ function getEnumJsonType(values) {
57
+ const [firstValue] = values;
58
+ if (firstValue === undefined) {
59
+ return undefined;
60
+ }
61
+ const firstType = typeof firstValue;
62
+ const isSinglePrimitiveType = values.every((value) => typeof value === firstType);
63
+ if (!isSinglePrimitiveType) {
64
+ return undefined;
65
+ }
66
+ if (firstType === "string" || firstType === "number" || firstType === "boolean") {
67
+ return firstType;
68
+ }
69
+ return undefined;
70
+ }
71
+ function isOptionalSchema(schema) {
72
+ return schema.kind === "optional";
73
+ }
74
+ function assertValidEnumValues(values) {
75
+ if (values.length === 0) {
76
+ throw new Error("Enum schema requires at least one value");
77
+ }
78
+ const uniqueValues = new Set(values);
79
+ if (uniqueValues.size !== values.length) {
80
+ throw new Error("Enum schema values must be unique");
81
+ }
82
+ if (values.some((value) => typeof value === "number" && !Number.isFinite(value))) {
83
+ throw new Error("Enum schema numeric values must be finite");
84
+ }
85
+ }
86
+ function assertNonNegativeInteger(value, name) {
87
+ if (value !== undefined && (!Number.isInteger(value) || value < 0)) {
88
+ throw new Error(`${name} must be a non-negative integer`);
89
+ }
90
+ }
91
+ function assertFiniteNumber(value, name) {
92
+ if (value !== undefined && !Number.isFinite(value)) {
93
+ throw new Error(`${name} must be finite`);
94
+ }
95
+ }
96
+ function assertMinMaxOrder(minimum, maximum, minimumName, maximumName) {
97
+ if (minimum !== undefined && maximum !== undefined && minimum > maximum) {
98
+ throw new Error(`${minimumName} must be less than or equal to ${maximumName}`);
99
+ }
100
+ }
101
+ function assertValidDefault(schema) {
102
+ if (schema.default === undefined) {
103
+ return;
104
+ }
105
+ const result = validate(schema, schema.default);
106
+ if (!result.ok) {
107
+ throw new Error(`default must satisfy schema: ${result.issues[0]?.message ?? "invalid default"}`);
108
+ }
109
+ }
110
+ function assertPattern(pattern) {
111
+ if (pattern === undefined) {
112
+ return;
113
+ }
114
+ try {
115
+ new RegExp(pattern);
116
+ }
117
+ catch {
118
+ throw new Error("pattern must be a valid regular expression");
119
+ }
120
+ }
121
+ function unwrapOptional(schema) {
122
+ if (isOptionalSchema(schema)) {
123
+ return unwrapOptional(schema.inner);
124
+ }
125
+ return schema;
126
+ }
127
+ function withInjectedDiscriminator(schema, discriminator, branchName) {
128
+ const branchJsonSchema = toJsonSchema(schema);
129
+ const properties = {
130
+ ...(branchJsonSchema.properties ?? {}),
131
+ [discriminator]: {
132
+ type: "string",
133
+ enum: [branchName]
134
+ }
135
+ };
136
+ const required = [...new Set([...(branchJsonSchema.required ?? []), discriminator])];
137
+ return {
138
+ ...branchJsonSchema,
139
+ type: "object",
140
+ properties,
141
+ required
142
+ };
143
+ }
144
+ export const S = {
145
+ String(options = {}) {
146
+ assertNonNegativeInteger(options.minLength, "minLength");
147
+ assertNonNegativeInteger(options.maxLength, "maxLength");
148
+ assertMinMaxOrder(options.minLength, options.maxLength, "minLength", "maxLength");
149
+ assertPattern(options.pattern);
150
+ const schema = {
151
+ kind: "string",
152
+ ...options
153
+ };
154
+ assertValidDefault(schema);
155
+ return schema;
156
+ },
157
+ Number(options = {}) {
158
+ assertFiniteNumber(options.minimum, "minimum");
159
+ assertFiniteNumber(options.maximum, "maximum");
160
+ assertMinMaxOrder(options.minimum, options.maximum, "minimum", "maximum");
161
+ assertFiniteNumber(options.default, "default");
162
+ if (options.jsonType === "integer" &&
163
+ options.default !== undefined &&
164
+ !Number.isInteger(options.default)) {
165
+ throw new Error("default must be an integer");
166
+ }
167
+ const schema = {
168
+ kind: "number",
169
+ ...options
170
+ };
171
+ assertValidDefault(schema);
172
+ return schema;
173
+ },
174
+ Boolean(options = {}) {
175
+ const schema = {
176
+ kind: "boolean",
177
+ ...options
178
+ };
179
+ assertValidDefault(schema);
180
+ return schema;
181
+ },
182
+ Enum(values, options = {}) {
183
+ assertValidEnumValues(values);
184
+ if (options.jsonType === "integer" &&
185
+ values.some((value) => typeof value !== "number" || !Number.isInteger(value))) {
186
+ throw new Error("Integer enum values must be integers");
187
+ }
188
+ const schema = {
189
+ kind: "enum",
190
+ values,
191
+ ...options
192
+ };
193
+ assertValidDefault(schema);
194
+ return schema;
195
+ },
196
+ Array(item, options = {}) {
197
+ assertNonNegativeInteger(options.minItems, "minItems");
198
+ assertNonNegativeInteger(options.maxItems, "maxItems");
199
+ assertMinMaxOrder(options.minItems, options.maxItems, "minItems", "maxItems");
200
+ const schema = {
201
+ kind: "array",
202
+ item,
203
+ ...options
204
+ };
205
+ assertValidDefault(schema);
206
+ return schema;
207
+ },
208
+ Object(shape, options = {}) {
209
+ const schema = {
210
+ kind: "object",
211
+ shape,
212
+ ...options
213
+ };
214
+ assertValidDefault(schema);
215
+ return schema;
216
+ },
217
+ Optional(inner) {
218
+ return {
219
+ kind: "optional",
220
+ inner
221
+ };
222
+ },
223
+ OneOf,
224
+ Union,
225
+ Record: RecordBuilder,
226
+ Json
227
+ };
228
+ export function toJsonSchema(schema) {
229
+ const unwrappedSchema = unwrapOptional(schema);
230
+ switch (unwrappedSchema.kind) {
231
+ case "string":
232
+ return withStringMetadata(unwrappedSchema, { type: "string" });
233
+ case "number":
234
+ return withNumberMetadata(unwrappedSchema, { type: unwrappedSchema.jsonType ?? "number" });
235
+ case "boolean":
236
+ return withMetadata(unwrappedSchema, { type: "boolean" });
237
+ case "enum": {
238
+ const jsonSchema = {
239
+ enum: unwrappedSchema.nullable === true
240
+ ? [...unwrappedSchema.values, null]
241
+ : [...unwrappedSchema.values]
242
+ };
243
+ const enumType = unwrappedSchema.jsonType ?? getEnumJsonType(unwrappedSchema.values);
244
+ if (enumType !== undefined) {
245
+ jsonSchema.type = enumType;
246
+ }
247
+ return withMetadata(unwrappedSchema, jsonSchema);
248
+ }
249
+ case "array":
250
+ return withArrayMetadata(unwrappedSchema, {
251
+ type: "array",
252
+ items: toJsonSchema(unwrappedSchema.item)
253
+ });
254
+ case "object": {
255
+ const properties = {};
256
+ const required = [];
257
+ for (const [key, propertySchema] of Object.entries(unwrappedSchema.shape)) {
258
+ Object.defineProperty(properties, key, {
259
+ enumerable: true,
260
+ configurable: true,
261
+ writable: true,
262
+ value: toJsonSchema(propertySchema)
263
+ });
264
+ if (!isOptionalSchema(propertySchema)) {
265
+ required.push(key);
266
+ }
267
+ }
268
+ return withObjectMetadata(unwrappedSchema, {
269
+ type: "object",
270
+ properties,
271
+ required
272
+ });
273
+ }
274
+ case "oneOf":
275
+ return withMetadata(unwrappedSchema, {
276
+ oneOf: Object.entries(unwrappedSchema.branches).map(([branchName, branchSchema]) => withInjectedDiscriminator(branchSchema, unwrappedSchema.discriminator, branchName))
277
+ });
278
+ case "union":
279
+ return withMetadata(unwrappedSchema, {
280
+ oneOf: unwrappedSchema.branches.map((branchSchema) => toJsonSchema(branchSchema))
281
+ });
282
+ case "record":
283
+ return withMetadata(unwrappedSchema, {
284
+ type: "object",
285
+ additionalProperties: toJsonSchema(unwrappedSchema.value)
286
+ });
287
+ case "json":
288
+ return withMetadata(unwrappedSchema, {});
289
+ }
290
+ }
291
+ export function toJsonSchemaDocument(schema, options = {}) {
292
+ return createJsonSchemaDocument(toJsonSchema(schema), options);
293
+ }
294
+ export { Json, OneOf, RecordBuilder as Record, Union, validate };
295
+ export { compileJsonSchema, formatIssues } from "./json-schema/index.js";
@@ -0,0 +1,11 @@
1
+ import type { CompileJsonSchemaOptions, SchemaNode } from "./types.js";
2
+ export interface CompiledGraph {
3
+ root: SchemaNode;
4
+ locations: Map<string, SchemaNode>;
5
+ resources: Map<string, SchemaNode>;
6
+ anchors: Map<string, SchemaNode>;
7
+ dynamicAnchors: Map<string, SchemaNode>;
8
+ resolve(node: SchemaNode, reference: string): SchemaNode;
9
+ dynamicAnchor(scope: SchemaNode, name: string): SchemaNode | undefined;
10
+ }
11
+ export declare function compileGraph(schema: unknown, options?: CompileJsonSchemaOptions): CompiledGraph;