toolcraft-schema 0.0.38 → 0.0.40

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 CHANGED
@@ -10,18 +10,19 @@ Zero-dependency schema builder for typed command inputs and JSON Schema generati
10
10
  - Typed schema descriptors
11
11
  - `Static<typeof schema>` type inference
12
12
  - JSON Schema serialization via `toJsonSchema()`
13
+ - JSON Schema document serialization via `toJsonSchemaDocument()`
13
14
 
14
15
  ## Usage
15
16
 
16
17
  ```ts
17
- import { S, toJsonSchema } from "toolcraft-schema";
18
+ import { S, toJsonSchema, toJsonSchemaDocument } from "toolcraft-schema";
18
19
  import type { Static } from "toolcraft-schema";
19
20
 
20
21
  const schema = S.Object({
21
22
  name: S.String({ description: "User name" }),
22
23
  retries: S.Optional(S.Number({ default: 3 })),
23
24
  mode: S.Enum(["fast", "safe"] as const, { default: "safe" }),
24
- tags: S.Array(S.String(), { default: [] }),
25
+ tags: S.Array(S.String(), { default: [] })
25
26
  });
26
27
 
27
28
  type Input = Static<typeof schema>;
@@ -33,17 +34,21 @@ type Input = Static<typeof schema>;
33
34
  // }
34
35
 
35
36
  const jsonSchema = toJsonSchema(schema);
37
+ const document = toJsonSchemaDocument(schema, {
38
+ id: "https://example.test/schema.json",
39
+ title: "Example schema"
40
+ });
36
41
  ```
37
42
 
38
43
  ## API
39
44
 
40
45
  ### Builders
41
46
 
42
- - `S.String({ description?, default? })`
43
- - `S.Number({ description?, default? })`
44
- - `S.Boolean({ description?, default? })`
45
- - `S.Enum(values, { description?, default? })`
46
- - `S.Array(itemSchema, { description?, default? })`
47
+ - `S.String({ description?, default?, short?, cliAliases? })`
48
+ - `S.Number({ description?, default?, short?, cliAliases? })`
49
+ - `S.Boolean({ description?, default?, short?, cliAliases? })`
50
+ - `S.Enum(values, { description?, default?, short?, cliAliases? })`
51
+ - `S.Array(itemSchema, { description?, default?, short?, cliAliases? })`
47
52
  - `S.Object({ [key]: schema })`
48
53
  - `S.Optional(schema)`
49
54
 
@@ -55,6 +60,7 @@ const jsonSchema = toJsonSchema(schema);
55
60
  ### JSON Schema generation
56
61
 
57
62
  - `toJsonSchema(schema)` converts any schema descriptor to standard JSON Schema.
63
+ - `toJsonSchemaDocument(schema, options)` wraps `toJsonSchema(schema)` in a full JSON Schema document with `$schema`, optional `$id`, `title`, and `description`.
58
64
  - Object properties not wrapped in `S.Optional(...)` are emitted in `required`.
59
65
  - Defaults provided to schema builders are emitted as JSON Schema `default`.
60
66
  - Nested `S.Object(...)` schemas produce nested JSON Schema objects.
@@ -1,4 +1,4 @@
1
- import { S } from "./index.js";
1
+ import { S, toJsonSchemaDocument } from "./index.js";
2
2
  const ignoredStringSchema = S.String({ description: "Name", default: "guest" });
3
3
  const ignoredNumberSchema = S.Number({ description: "Count", default: 1 });
4
4
  const ignoredBooleanSchema = S.Boolean({ description: "Enabled", default: false });
@@ -7,6 +7,11 @@ const ignoredIntegerEnumSchema = S.Enum([1, 2], { jsonType: "integer" });
7
7
  const ignoredArraySchema = S.Array(S.String(), { default: ["a"] });
8
8
  const ignoredObjectSchema = S.Object({
9
9
  name: S.String(),
10
- retries: S.Optional(S.Number()),
10
+ retries: S.Optional(S.Number())
11
11
  });
12
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
+ });
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { Record as RecordBuilder } from "./record.js";
4
4
  import { Union } from "./union.js";
5
5
  import { validate } from "./validate.js";
6
6
  import type { JsonValue, JsonValueSchema } from "./json.js";
7
+ import type { JsonSchemaDocument, JsonSchemaDocumentOptions } from "./json-schema-document.js";
7
8
  import type { OneOfSchema } from "./oneof.js";
8
9
  import type { RecordSchema } from "./record.js";
9
10
  import type { UnionSchema } from "./union.js";
@@ -47,6 +48,7 @@ type InferObject<TShape extends ObjectShape> = {
47
48
  };
48
49
  type SchemaOptions<TDefault> = {
49
50
  description?: string;
51
+ cliAliases?: readonly string[];
50
52
  default?: TDefault;
51
53
  nullable?: boolean;
52
54
  requiredScopes?: readonly SchemaScope[];
@@ -57,6 +59,7 @@ type SchemaOptions<TDefault> = {
57
59
  export interface SchemaBase<TKind extends SchemaKind, TStatic> {
58
60
  readonly kind: TKind;
59
61
  readonly description?: string;
62
+ readonly cliAliases?: readonly string[];
60
63
  readonly default?: TStatic;
61
64
  readonly nullable?: boolean;
62
65
  readonly requiredScopes?: readonly SchemaScope[];
@@ -140,5 +143,7 @@ export declare const S: {
140
143
  readonly Json: typeof Json;
141
144
  };
142
145
  export declare function toJsonSchema(schema: AnySchema): JsonSchema;
146
+ export declare function toJsonSchemaDocument(schema: AnySchema, options?: JsonSchemaDocumentOptions): JsonSchemaDocument;
143
147
  export { Json, OneOf, RecordBuilder as Record, Union, validate };
144
- export type { JsonValue, JsonValueSchema, OneOfSchema, RecordSchema, UnionSchema, ValidationIssue, ValidationResult, };
148
+ export type { JsonSchemaDocument, JsonSchemaDocumentOptions } from "./json-schema-document.js";
149
+ export type { JsonValue, JsonValueSchema, OneOfSchema, RecordSchema, UnionSchema, ValidationIssue, ValidationResult };
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Json } from "./json.js";
2
+ import { createJsonSchemaDocument } from "./json-schema-document.js";
2
3
  import { OneOf } from "./oneof.js";
3
4
  import { Record as RecordBuilder } from "./record.js";
4
5
  import { Union } from "./union.js";
@@ -117,15 +118,15 @@ function withInjectedDiscriminator(schema, discriminator, branchName) {
117
118
  ...(branchJsonSchema.properties ?? {}),
118
119
  [discriminator]: {
119
120
  type: "string",
120
- enum: [branchName],
121
- },
121
+ enum: [branchName]
122
+ }
122
123
  };
123
124
  const required = [...new Set([...(branchJsonSchema.required ?? []), discriminator])];
124
125
  return {
125
126
  ...branchJsonSchema,
126
127
  type: "object",
127
128
  properties,
128
- required,
129
+ required
129
130
  };
130
131
  }
131
132
  export const S = {
@@ -135,36 +136,39 @@ export const S = {
135
136
  assertPattern(options.pattern);
136
137
  return {
137
138
  kind: "string",
138
- ...options,
139
+ ...options
139
140
  };
140
141
  },
141
142
  Number(options = {}) {
142
143
  assertFiniteNumber(options.minimum, "minimum");
143
144
  assertFiniteNumber(options.maximum, "maximum");
144
145
  assertFiniteNumber(options.default, "default");
145
- if (options.jsonType === "integer" && options.default !== undefined && !Number.isInteger(options.default)) {
146
+ if (options.jsonType === "integer" &&
147
+ options.default !== undefined &&
148
+ !Number.isInteger(options.default)) {
146
149
  throw new Error("default must be an integer");
147
150
  }
148
151
  return {
149
152
  kind: "number",
150
- ...options,
153
+ ...options
151
154
  };
152
155
  },
153
156
  Boolean(options = {}) {
154
157
  return {
155
158
  kind: "boolean",
156
- ...options,
159
+ ...options
157
160
  };
158
161
  },
159
162
  Enum(values, options = {}) {
160
163
  assertValidEnumValues(values);
161
- if (options.jsonType === "integer" && values.some((value) => typeof value !== "number" || !Number.isInteger(value))) {
164
+ if (options.jsonType === "integer" &&
165
+ values.some((value) => typeof value !== "number" || !Number.isInteger(value))) {
162
166
  throw new Error("Integer enum values must be integers");
163
167
  }
164
168
  return {
165
169
  kind: "enum",
166
170
  values,
167
- ...options,
171
+ ...options
168
172
  };
169
173
  },
170
174
  Array(item, options = {}) {
@@ -173,26 +177,26 @@ export const S = {
173
177
  return {
174
178
  kind: "array",
175
179
  item,
176
- ...options,
180
+ ...options
177
181
  };
178
182
  },
179
183
  Object(shape, options = {}) {
180
184
  return {
181
185
  kind: "object",
182
186
  shape,
183
- ...options,
187
+ ...options
184
188
  };
185
189
  },
186
190
  Optional(inner) {
187
191
  return {
188
192
  kind: "optional",
189
- inner,
193
+ inner
190
194
  };
191
195
  },
192
196
  OneOf,
193
197
  Union,
194
198
  Record: RecordBuilder,
195
- Json,
199
+ Json
196
200
  };
197
201
  export function toJsonSchema(schema) {
198
202
  const unwrappedSchema = unwrapOptional(schema);
@@ -207,7 +211,7 @@ export function toJsonSchema(schema) {
207
211
  const jsonSchema = {
208
212
  enum: unwrappedSchema.nullable === true
209
213
  ? [...unwrappedSchema.values, null]
210
- : [...unwrappedSchema.values],
214
+ : [...unwrappedSchema.values]
211
215
  };
212
216
  const enumType = unwrappedSchema.jsonType ?? getEnumJsonType(unwrappedSchema.values);
213
217
  if (enumType !== undefined) {
@@ -218,7 +222,7 @@ export function toJsonSchema(schema) {
218
222
  case "array":
219
223
  return withArrayMetadata(unwrappedSchema, {
220
224
  type: "array",
221
- items: toJsonSchema(unwrappedSchema.item),
225
+ items: toJsonSchema(unwrappedSchema.item)
222
226
  });
223
227
  case "object": {
224
228
  const properties = {};
@@ -237,24 +241,27 @@ export function toJsonSchema(schema) {
237
241
  return withObjectMetadata(unwrappedSchema, {
238
242
  type: "object",
239
243
  properties,
240
- required,
244
+ required
241
245
  });
242
246
  }
243
247
  case "oneOf":
244
248
  return withMetadata(unwrappedSchema, {
245
- oneOf: Object.entries(unwrappedSchema.branches).map(([branchName, branchSchema]) => withInjectedDiscriminator(branchSchema, unwrappedSchema.discriminator, branchName)),
249
+ oneOf: Object.entries(unwrappedSchema.branches).map(([branchName, branchSchema]) => withInjectedDiscriminator(branchSchema, unwrappedSchema.discriminator, branchName))
246
250
  });
247
251
  case "union":
248
252
  return withMetadata(unwrappedSchema, {
249
- oneOf: unwrappedSchema.branches.map((branchSchema) => toJsonSchema(branchSchema)),
253
+ oneOf: unwrappedSchema.branches.map((branchSchema) => toJsonSchema(branchSchema))
250
254
  });
251
255
  case "record":
252
256
  return withMetadata(unwrappedSchema, {
253
257
  type: "object",
254
- additionalProperties: toJsonSchema(unwrappedSchema.value),
258
+ additionalProperties: toJsonSchema(unwrappedSchema.value)
255
259
  });
256
260
  case "json":
257
261
  return withMetadata(unwrappedSchema, {});
258
262
  }
259
263
  }
264
+ export function toJsonSchemaDocument(schema, options = {}) {
265
+ return createJsonSchemaDocument(toJsonSchema(schema), options);
266
+ }
260
267
  export { Json, OneOf, RecordBuilder as Record, Union, validate };
@@ -0,0 +1,14 @@
1
+ import type { JsonSchema } from "./index.js";
2
+ export interface JsonSchemaDocumentOptions {
3
+ id?: string;
4
+ title?: string;
5
+ description?: string;
6
+ schema?: string;
7
+ }
8
+ export type JsonSchemaDocument = JsonSchema & {
9
+ $schema: string;
10
+ $id?: string;
11
+ title?: string;
12
+ description?: string;
13
+ };
14
+ export declare function createJsonSchemaDocument(jsonSchema: JsonSchema, options?: JsonSchemaDocumentOptions): JsonSchemaDocument;
@@ -0,0 +1,17 @@
1
+ export function createJsonSchemaDocument(jsonSchema, options = {}) {
2
+ const { id, schema: schemaUri = "https://json-schema.org/draft/2020-12/schema", ...metadata } = options;
3
+ const document = {
4
+ $schema: schemaUri,
5
+ ...jsonSchema
6
+ };
7
+ if (id !== undefined) {
8
+ document.$id = id;
9
+ }
10
+ if (metadata.title !== undefined) {
11
+ document.title = metadata.title;
12
+ }
13
+ if (metadata.description !== undefined) {
14
+ document.description = metadata.description;
15
+ }
16
+ return document;
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-schema",
3
- "version": "0.0.38",
3
+ "version": "0.0.40",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",