schema-to-library 0.0.2 → 0.0.4

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
@@ -6,7 +6,7 @@ npm install -D schema-to-library
6
6
 
7
7
  ## What is schema-to-library?
8
8
 
9
- **schema-to-library** is a CLI tool that converts JSON Schema into code for validation libraries like Zod,
9
+ **[schema-to-library](https://www.npmjs.com/package/schema-to-library)** is a CLI tool that converts JSON Schema into code for validation libraries like Zod,
10
10
  It helps you automatically generate type-safe validation schemas and TypeScript types from your existing schema definitions.
11
11
 
12
12
  ## Upcoming Support
@@ -24,7 +24,7 @@ Support for additional libraries is planned:
24
24
  ```bash
25
25
  npx schema-to-zod path/to/input.{json,yaml} -o path/to/output.ts
26
26
  ```
27
-
27
+ -
28
28
  ### Example
29
29
 
30
30
  input:
@@ -75,4 +75,4 @@ export type Schema = z.infer<typeof Schema>
75
75
 
76
76
  ## License
77
77
 
78
- Distributed under the MIT License. See [LICENSE](https://github.com/nakita628/schema-to-library?tab=MIT-1-ov-file) for more information.
78
+ Distributed under the MIT License. See [LICENSE](https://github.com/nakita628/schema-to-libray?tab=MIT-1-ov-file) for more information.
@@ -0,0 +1,68 @@
1
+ import * as v from 'valibot';
2
+ export declare function cli(fn: (schema: Schema, rootName?: string) => string, helpText: string): Promise<{
3
+ ok: boolean;
4
+ value: string;
5
+ error?: undefined;
6
+ } | {
7
+ ok: boolean;
8
+ error: string | undefined;
9
+ value?: undefined;
10
+ }>;
11
+ export declare function parseYaml(i: string): {
12
+ ok: true;
13
+ value: unknown;
14
+ } | {
15
+ ok: false;
16
+ error: string;
17
+ };
18
+ declare const TypeSchema: v.UnionSchema<[v.LiteralSchema<"string", undefined>, v.LiteralSchema<"number", undefined>, v.LiteralSchema<"integer", undefined>, v.LiteralSchema<"date", undefined>, v.LiteralSchema<"boolean", undefined>, v.LiteralSchema<"array", undefined>, v.LiteralSchema<"object", undefined>, v.LiteralSchema<"null", undefined>], undefined>;
19
+ type Type = v.InferInput<typeof TypeSchema>;
20
+ export declare const FormatSchema: v.UnionSchema<[v.LiteralSchema<"email", undefined>, v.LiteralSchema<"uuid", undefined>, v.LiteralSchema<"uuidv4", undefined>, v.LiteralSchema<"uuidv6", undefined>, v.LiteralSchema<"uuidv7", undefined>, v.LiteralSchema<"uri", undefined>, v.LiteralSchema<"emoji", undefined>, v.LiteralSchema<"base64", undefined>, v.LiteralSchema<"base64url", undefined>, v.LiteralSchema<"nanoid", undefined>, v.LiteralSchema<"cuid", undefined>, v.LiteralSchema<"cuid2", undefined>, v.LiteralSchema<"ulid", undefined>, v.LiteralSchema<"ip", undefined>, v.LiteralSchema<"ipv4", undefined>, v.LiteralSchema<"ipv6", undefined>, v.LiteralSchema<"cidrv4", undefined>, v.LiteralSchema<"cidrv6", undefined>, v.LiteralSchema<"date", undefined>, v.LiteralSchema<"time", undefined>, v.LiteralSchema<"date-time", undefined>, v.LiteralSchema<"duration", undefined>, v.LiteralSchema<"binary", undefined>, v.LiteralSchema<"toLowerCase", undefined>, v.LiteralSchema<"toUpperCase", undefined>, v.LiteralSchema<"trim", undefined>, v.LiteralSchema<"jwt", undefined>, v.LiteralSchema<"int32", undefined>, v.LiteralSchema<"int64", undefined>, v.LiteralSchema<"bigint", undefined>, v.LiteralSchema<"float", undefined>, v.LiteralSchema<"float32", undefined>, v.LiteralSchema<"float64", undefined>, v.LiteralSchema<"double", undefined>, v.LiteralSchema<"decimal", undefined>], undefined>;
21
+ type Format = v.InferInput<typeof FormatSchema>;
22
+ type SchemaType = {
23
+ title?: string;
24
+ definitions?: Record<string, SchemaType>;
25
+ $defs?: Record<string, SchemaType>;
26
+ name?: string;
27
+ description?: string;
28
+ type?: Type | [Type, ...Type[]];
29
+ format?: Format;
30
+ pattern?: string;
31
+ minLength?: number;
32
+ maxLength?: number;
33
+ minimum?: number;
34
+ maximum?: number;
35
+ exclusiveMinimum?: number | boolean;
36
+ exclusiveMaximum?: number | boolean;
37
+ multipleOf?: number;
38
+ minItems?: number;
39
+ maxItems?: number;
40
+ default?: unknown;
41
+ example?: unknown;
42
+ examples?: unknown[];
43
+ properties?: Record<string, SchemaType>;
44
+ required?: string[] | boolean;
45
+ items?: SchemaType;
46
+ enum?: (string | number | boolean | null | (string | number | boolean | null)[])[];
47
+ nullable?: boolean;
48
+ additionalProperties?: SchemaType | boolean;
49
+ $ref?: string;
50
+ xml?: {
51
+ name?: string;
52
+ wrapped?: boolean;
53
+ };
54
+ oneOf?: SchemaType[];
55
+ allOf?: SchemaType[];
56
+ anyOf?: SchemaType[];
57
+ not?: SchemaType;
58
+ discriminator?: {
59
+ propertyName?: string;
60
+ };
61
+ externalDocs?: {
62
+ url?: string;
63
+ };
64
+ const?: unknown;
65
+ };
66
+ declare const Schema: v.GenericSchema<SchemaType>;
67
+ export type Schema = v.InferInput<typeof Schema>;
68
+ export {};
@@ -0,0 +1,320 @@
1
+ import fsp from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { format } from 'prettier';
4
+ import * as v from 'valibot';
5
+ import { parse } from 'yaml';
6
+ const IOSchema = v.object({
7
+ input: v.custom((value) => typeof value === 'string' && (value.endsWith('.yaml') || value.endsWith('.json')), 'Input must be a .json, or .yaml file'),
8
+ output: v.custom((value) => typeof value === 'string' && value.endsWith('.ts'), 'Output must be a .ts file'),
9
+ });
10
+ const IsYAMLSchema = v.custom((value) => typeof value === 'string' && value.endsWith('.yaml'), 'Must end with .yaml');
11
+ const IsJSONSchema = v.custom((value) => typeof value === 'string' && value.endsWith('.json'), 'Must end with .json');
12
+ export async function cli(fn, helpText) {
13
+ // Slice the arguments to remove the first two (node and script path)
14
+ const args = process.argv.slice(2);
15
+ const isHelpRequested = (args) => {
16
+ return args.length === 1 && (args[0] === '--help' || args[0] === '-h');
17
+ };
18
+ if (isHelpRequested(args)) {
19
+ return {
20
+ ok: true,
21
+ value: helpText,
22
+ };
23
+ }
24
+ const i = args[0];
25
+ const oIdx = args.indexOf('-o');
26
+ const o = oIdx !== -1 ? args[oIdx + 1] : undefined;
27
+ const valid = v.safeParse(IOSchema, {
28
+ input: i,
29
+ output: o,
30
+ });
31
+ if (!valid.success) {
32
+ return {
33
+ ok: false,
34
+ error: valid.issues.map((issue) => issue.message)[0],
35
+ };
36
+ }
37
+ const { input, output } = valid.output;
38
+ const schemaResult = await parseSchema(input);
39
+ if (!schemaResult.ok) {
40
+ return {
41
+ ok: false,
42
+ error: schemaResult.error,
43
+ };
44
+ }
45
+ const schemaValid = v.safeParse(Schema, schemaResult.value);
46
+ if (!schemaValid.success) {
47
+ return {
48
+ ok: false,
49
+ error: schemaValid.issues.map((issue) => issue.message)[0],
50
+ };
51
+ }
52
+ const schema = schemaValid.output;
53
+ const result = fn(schema);
54
+ const fmtResult = await fmt(result);
55
+ if (!fmtResult.ok) {
56
+ return {
57
+ ok: false,
58
+ error: fmtResult.error,
59
+ };
60
+ }
61
+ const mkdirResult = await mkdir(path.dirname(output));
62
+ if (!mkdirResult.ok) {
63
+ return {
64
+ ok: false,
65
+ error: mkdirResult.error,
66
+ };
67
+ }
68
+ const writeFileResult = await writeFile(output, fmtResult.value);
69
+ if (!writeFileResult.ok) {
70
+ return {
71
+ ok: false,
72
+ error: writeFileResult.error,
73
+ };
74
+ }
75
+ return {
76
+ ok: true,
77
+ value: `${output} created`,
78
+ };
79
+ }
80
+ async function parseSchema(i) {
81
+ if (i.endsWith('.yaml')) {
82
+ const valid = v.safeParse(IsYAMLSchema, i);
83
+ if (!valid.success) {
84
+ return {
85
+ ok: false,
86
+ error: valid.issues.map((issue) => issue.message)[0],
87
+ };
88
+ }
89
+ const input = valid.output;
90
+ const file = await readFile(input);
91
+ if (!file.ok) {
92
+ return {
93
+ ok: false,
94
+ error: file.error,
95
+ };
96
+ }
97
+ const yaml = parseYaml(file.value);
98
+ if (!yaml.ok) {
99
+ return {
100
+ ok: false,
101
+ error: yaml.error,
102
+ };
103
+ }
104
+ return {
105
+ ok: true,
106
+ value: yaml.value,
107
+ };
108
+ }
109
+ if (i.endsWith('.json')) {
110
+ const valid = v.safeParse(IsJSONSchema, i);
111
+ if (!valid.success) {
112
+ return {
113
+ ok: false,
114
+ error: valid.issues.map((issue) => issue.message)[0],
115
+ };
116
+ }
117
+ const input = valid.output;
118
+ const file = await readFile(input);
119
+ if (!file.ok) {
120
+ return {
121
+ ok: false,
122
+ error: file.error,
123
+ };
124
+ }
125
+ return {
126
+ ok: true,
127
+ value: JSON.parse(file.value),
128
+ };
129
+ }
130
+ return {
131
+ ok: false,
132
+ error: 'Invalid input file type',
133
+ };
134
+ }
135
+ // parseYaml
136
+ export function parseYaml(i) {
137
+ try {
138
+ const yaml = parse(i);
139
+ return {
140
+ ok: true,
141
+ value: yaml,
142
+ };
143
+ }
144
+ catch (e) {
145
+ return {
146
+ ok: false,
147
+ error: String(e),
148
+ };
149
+ }
150
+ }
151
+ // readFile
152
+ async function readFile(path) {
153
+ try {
154
+ const res = await fsp.readFile(path, 'utf-8');
155
+ return { ok: true, value: res };
156
+ }
157
+ catch (e) {
158
+ return {
159
+ ok: false,
160
+ error: e instanceof Error ? e.message : String(e),
161
+ };
162
+ }
163
+ }
164
+ /**
165
+ * Formats TypeScript source with Prettier.
166
+ *
167
+ * @param code - Source code to format.
168
+ * @returns A `Result` containing the formatted code or an error message.
169
+ */
170
+ async function fmt(code) {
171
+ try {
172
+ const formatted = await format(code, {
173
+ parser: 'typescript',
174
+ printWidth: 100,
175
+ singleQuote: true,
176
+ semi: false,
177
+ });
178
+ return { ok: true, value: formatted };
179
+ }
180
+ catch (e) {
181
+ return {
182
+ ok: false,
183
+ error: e instanceof Error ? e.message : String(e),
184
+ };
185
+ }
186
+ }
187
+ /**
188
+ * Creates a directory if it does not already exist.
189
+ *
190
+ * @param dir - Directory path to create.
191
+ * @returns A `Result` that is `ok` on success, otherwise an error message.
192
+ */
193
+ async function mkdir(dir) {
194
+ try {
195
+ await fsp.mkdir(dir, { recursive: true });
196
+ return {
197
+ ok: true,
198
+ value: undefined,
199
+ };
200
+ }
201
+ catch (e) {
202
+ return {
203
+ ok: false,
204
+ error: e instanceof Error ? e.message : String(e),
205
+ };
206
+ }
207
+ }
208
+ /**
209
+ * Writes UTF-8 text to a file, creating it if necessary.
210
+ *
211
+ * @param path - File path to write.
212
+ * @param data - Text data to write.
213
+ * @returns A `Result` that is `ok` on success, otherwise an error message.
214
+ */
215
+ async function writeFile(path, data) {
216
+ try {
217
+ await fsp.writeFile(path, data, 'utf-8');
218
+ return { ok: true, value: undefined };
219
+ }
220
+ catch (e) {
221
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
222
+ }
223
+ }
224
+ const TypeSchema = v.union([
225
+ v.literal('string'),
226
+ v.literal('number'),
227
+ v.literal('integer'),
228
+ v.literal('date'),
229
+ v.literal('boolean'),
230
+ v.literal('array'),
231
+ v.literal('object'),
232
+ v.literal('null'),
233
+ ]);
234
+ export const FormatSchema = v.union([
235
+ v.literal('email'),
236
+ v.literal('uuid'),
237
+ v.literal('uuidv4'),
238
+ v.literal('uuidv6'),
239
+ v.literal('uuidv7'),
240
+ v.literal('uri'),
241
+ v.literal('emoji'),
242
+ v.literal('base64'),
243
+ v.literal('base64url'),
244
+ v.literal('nanoid'),
245
+ v.literal('cuid'),
246
+ v.literal('cuid2'),
247
+ v.literal('ulid'),
248
+ v.literal('ip'),
249
+ v.literal('ipv4'),
250
+ v.literal('ipv6'),
251
+ v.literal('cidrv4'),
252
+ v.literal('cidrv6'),
253
+ v.literal('date'),
254
+ v.literal('time'),
255
+ v.literal('date-time'),
256
+ v.literal('duration'),
257
+ v.literal('binary'),
258
+ v.literal('toLowerCase'),
259
+ v.literal('toUpperCase'),
260
+ v.literal('trim'),
261
+ v.literal('jwt'),
262
+ v.literal('int32'),
263
+ v.literal('int64'),
264
+ v.literal('bigint'),
265
+ v.literal('float'),
266
+ v.literal('float32'),
267
+ v.literal('float64'),
268
+ v.literal('double'),
269
+ v.literal('decimal'),
270
+ ]);
271
+ const Schema = v.looseObject({
272
+ title: v.optional(v.string()),
273
+ definitions: v.optional(v.record(v.string(), v.lazy(() => Schema))),
274
+ $defs: v.optional(v.record(v.string(), v.lazy(() => Schema))),
275
+ name: v.optional(v.string()),
276
+ description: v.optional(v.string()),
277
+ type: v.optional(v.union([TypeSchema, v.tuple([TypeSchema])])),
278
+ format: v.optional(FormatSchema),
279
+ pattern: v.optional(v.string()),
280
+ minLength: v.optional(v.number()),
281
+ maxLength: v.optional(v.number()),
282
+ minimum: v.optional(v.number()),
283
+ maximum: v.optional(v.number()),
284
+ exclusiveMinimum: v.optional(v.union([v.number(), v.boolean()])),
285
+ exclusiveMaximum: v.optional(v.union([v.number(), v.boolean()])),
286
+ multipleOf: v.optional(v.number()),
287
+ minItems: v.optional(v.number()),
288
+ maxItems: v.optional(v.number()),
289
+ default: v.optional(v.unknown()),
290
+ example: v.optional(v.unknown()),
291
+ examples: v.optional(v.array(v.unknown())),
292
+ properties: v.optional(v.record(v.string(), v.lazy(() => Schema))),
293
+ required: v.optional(v.union([v.array(v.string()), v.boolean()])),
294
+ items: v.optional(v.lazy(() => Schema)),
295
+ enum: v.optional(v.array(v.union([
296
+ v.string(),
297
+ v.number(),
298
+ v.boolean(),
299
+ v.null(),
300
+ v.array(v.union([v.string(), v.number(), v.boolean(), v.null()])),
301
+ ]))),
302
+ nullable: v.optional(v.boolean()),
303
+ additionalProperties: v.optional(v.union([v.lazy(() => Schema), v.boolean()])),
304
+ $ref: v.optional(v.string()),
305
+ xml: v.optional(v.object({
306
+ name: v.optional(v.string()),
307
+ wrapped: v.optional(v.boolean()),
308
+ })),
309
+ oneOf: v.optional(v.array(v.lazy(() => Schema))),
310
+ allOf: v.optional(v.array(v.lazy(() => Schema))),
311
+ anyOf: v.optional(v.array(v.lazy(() => Schema))),
312
+ not: v.optional(v.lazy(() => Schema)),
313
+ discriminator: v.optional(v.object({
314
+ propertyName: v.optional(v.string()),
315
+ })),
316
+ externalDocs: v.optional(v.object({
317
+ url: v.optional(v.string()),
318
+ })),
319
+ const: v.optional(v.unknown()),
320
+ });
@@ -0,0 +1,2 @@
1
+ import type { Schema } from '../../cli/index.js';
2
+ export declare function resolveSchemaDependenciesFromSchema(schema: Schema): string[];
@@ -0,0 +1,89 @@
1
+ export function resolveSchemaDependenciesFromSchema(schema) {
2
+ // Merge both definitions and $defs
3
+ const definitions = {
4
+ ...(schema.definitions ?? {}),
5
+ ...(schema.$defs ?? {}),
6
+ };
7
+ const isRecord = (v) => typeof v === 'object' && v !== null;
8
+ const collectRefs = (schema) => {
9
+ const refs = new Set();
10
+ const stack = [schema];
11
+ while (stack.length > 0) {
12
+ const node = stack.pop();
13
+ if (!isRecord(node))
14
+ continue;
15
+ if ('$ref' in node && typeof node.$ref === 'string') {
16
+ const ref = node.$ref;
17
+ if (ref === '#')
18
+ continue;
19
+ // Check for both definitions and $defs refs
20
+ const match = ref.match(/^#\/(?:definitions|\$defs)\/([^/]+)$/);
21
+ if (match) {
22
+ refs.add(match[1]);
23
+ }
24
+ // Check for relative references like #node
25
+ const relativeMatch = ref.match(/^#([^/]+)$/);
26
+ if (relativeMatch) {
27
+ refs.add(relativeMatch[1]);
28
+ }
29
+ // Check for external file references with fragments
30
+ if (ref.includes('#')) {
31
+ const [filePath, fragment] = ref.split('#');
32
+ if (fragment) {
33
+ // Extract the schema name from the fragment
34
+ const fragmentMatch = fragment.match(/^\/(?:definitions|\$defs)\/([^/]+)$/);
35
+ if (fragmentMatch) {
36
+ refs.add(fragmentMatch[1]);
37
+ }
38
+ // Handle simple fragment like "#node"
39
+ const simpleMatch = fragment.match(/^\/([^/]+)$/);
40
+ if (simpleMatch) {
41
+ refs.add(simpleMatch[1]);
42
+ }
43
+ }
44
+ // Skip external references that we can't resolve
45
+ continue;
46
+ }
47
+ }
48
+ for (const value of Object.values(node)) {
49
+ if (Array.isArray(value)) {
50
+ for (const item of value) {
51
+ if (isRecord(item))
52
+ stack.push(item);
53
+ }
54
+ }
55
+ else if (isRecord(value)) {
56
+ stack.push(value);
57
+ }
58
+ }
59
+ }
60
+ return Array.from(refs).sort();
61
+ };
62
+ const sorted = [];
63
+ const perm = new Set();
64
+ const temp = new Set();
65
+ const visit = (name) => {
66
+ if (perm.has(name))
67
+ return;
68
+ if (temp.has(name)) {
69
+ // Circular dependency detected - skip this dependency but continue processing
70
+ // console.warn(`Warning: Circular dependency detected for type "${name}", skipping...`)
71
+ return;
72
+ }
73
+ const schema = definitions[name];
74
+ if (!schema)
75
+ return;
76
+ temp.add(name);
77
+ for (const ref of collectRefs(schema)) {
78
+ if (ref in definitions)
79
+ visit(ref);
80
+ }
81
+ temp.delete(name);
82
+ perm.add(name);
83
+ sorted.push(name);
84
+ };
85
+ for (const name of Object.keys(definitions).sort()) {
86
+ visit(name);
87
+ }
88
+ return sorted;
89
+ }
@@ -0,0 +1,2 @@
1
+ import type { Schema } from '../../cli/index.js';
2
+ export declare function type(schema: Schema, rootName?: string): string;
@@ -0,0 +1,156 @@
1
+ import { normalizeTypes, toPascalCase } from '../utils/index.js';
2
+ export function type(schema, rootName = 'Schema') {
3
+ if (schema === undefined)
4
+ return '';
5
+ // $ref case
6
+ if (schema.$ref) {
7
+ if (schema.$ref === '#' || schema.$ref === '') {
8
+ return `z.infer<typeof ${rootName}>`;
9
+ }
10
+ const TABLE = [
11
+ ['#/components/schemas/', 'Schema'],
12
+ ['#/definitions/', 'Schema'],
13
+ ['#/$defs/', 'Schema'],
14
+ ];
15
+ for (const [prefix] of TABLE) {
16
+ if (schema.$ref?.startsWith(prefix)) {
17
+ const name = schema.$ref.slice(prefix.length);
18
+ const pascalCaseName = toPascalCase(name);
19
+ // For self-references, use the type name directly
20
+ if (pascalCaseName === rootName) {
21
+ return `${pascalCaseName}Type`;
22
+ }
23
+ // For other references, use the type name directly to avoid circular references
24
+ return `${pascalCaseName}Type`;
25
+ }
26
+ }
27
+ // Handle relative references like #animal
28
+ if (schema.$ref?.startsWith('#')) {
29
+ const refName = schema.$ref.slice(1); // Remove the leading #
30
+ if (refName === '') {
31
+ return `z.infer<typeof ${rootName}>`;
32
+ }
33
+ // Only handle simple references like #animal, not complex paths
34
+ if (!refName.includes('/')) {
35
+ const pascalCaseName = toPascalCase(refName);
36
+ return `${pascalCaseName}Type`;
37
+ }
38
+ }
39
+ // Handle external file references with fragments
40
+ if (schema.$ref?.includes('#')) {
41
+ const [filePath, fragment] = schema.$ref.split('#');
42
+ // All external references are unknown
43
+ return 'unknown';
44
+ }
45
+ // Handle HTTP references without fragments
46
+ if (schema.$ref?.startsWith('http')) {
47
+ return 'unknown';
48
+ }
49
+ return 'unknown';
50
+ }
51
+ // combinators
52
+ if (schema.oneOf)
53
+ return union(schema.oneOf, rootName);
54
+ if (schema.anyOf)
55
+ return union(schema.anyOf, rootName);
56
+ if (schema.allOf)
57
+ return intersection(schema.allOf, rootName);
58
+ if (schema.not)
59
+ return 'unknown';
60
+ // const
61
+ if (schema.const !== undefined) {
62
+ return typeof schema.const === 'string' ? `"${schema.const}"` : String(schema.const);
63
+ }
64
+ // enum
65
+ if (schema.enum) {
66
+ if (schema.enum.length === 1) {
67
+ const v = schema.enum[0];
68
+ return typeof v === 'string' ? `"${v}"` : String(v);
69
+ }
70
+ const allStrings = schema.enum.every((v) => typeof v === 'string');
71
+ if (allStrings) {
72
+ return `(${schema.enum.map((v) => `"${v}"`).join(' | ')})`;
73
+ }
74
+ return `(${schema.enum.map((v) => (typeof v === 'string' ? `"${v}"` : String(v))).join(' | ')})`;
75
+ }
76
+ // properties
77
+ if (schema.properties)
78
+ return object(schema, rootName);
79
+ const t = normalizeTypes(schema.type);
80
+ // primitive types
81
+ if (t.includes('string'))
82
+ return 'string';
83
+ if (t.includes('number'))
84
+ return 'number';
85
+ if (t.includes('integer'))
86
+ return 'number';
87
+ if (t.includes('boolean'))
88
+ return 'boolean';
89
+ if (t.includes('array'))
90
+ return array(schema, rootName);
91
+ if (t.includes('object'))
92
+ return object(schema, rootName);
93
+ if (t.includes('date'))
94
+ return 'Date';
95
+ if (t.length === 1 && t[0] === 'null')
96
+ return 'null';
97
+ return 'unknown';
98
+ }
99
+ function union(schemas, rootName) {
100
+ const types = schemas.map((s) => type(s, rootName));
101
+ return `(${types.join(' | ')})`;
102
+ }
103
+ function intersection(schemas, rootName) {
104
+ const types = schemas
105
+ .filter((s) => {
106
+ // null type is excluded
107
+ if (s.type === 'null')
108
+ return false;
109
+ // nullable: true only is excluded
110
+ if (s.nullable === true && Object.keys(s).length === 1)
111
+ return false;
112
+ // simple properties (default, const, etc.) are excluded
113
+ if (Object.keys(s).length === 1 && (s.default !== undefined || s.const !== undefined))
114
+ return false;
115
+ return true;
116
+ })
117
+ .map((s) => type(s, rootName));
118
+ if (types.length === 0)
119
+ return 'unknown';
120
+ if (types.length === 1)
121
+ return types[0];
122
+ return `(${types.join(' & ')})`;
123
+ }
124
+ function array(schema, rootName) {
125
+ if (schema.items) {
126
+ const itemType = type(schema.items, rootName);
127
+ return `${itemType}[]`;
128
+ }
129
+ return 'unknown[]';
130
+ }
131
+ function object(schema, rootName) {
132
+ if (!schema.properties) {
133
+ if (schema.additionalProperties) {
134
+ if (typeof schema.additionalProperties === 'boolean') {
135
+ return schema.additionalProperties ? 'Record<string, unknown>' : 'Record<string, never>';
136
+ }
137
+ const valueType = type(schema.additionalProperties, rootName);
138
+ return `Record<string, ${valueType}>`;
139
+ }
140
+ return 'Record<string, unknown>';
141
+ }
142
+ const properties = [];
143
+ const required = Array.isArray(schema.required) ? schema.required : [];
144
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
145
+ const propType = type(propSchema, rootName);
146
+ const isRequired = required.includes(key);
147
+ const safeKey = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : `"${key}"`;
148
+ if (isRequired) {
149
+ properties.push(`${safeKey}: ${propType}`);
150
+ }
151
+ else {
152
+ properties.push(`${safeKey}?: ${propType}`);
153
+ }
154
+ }
155
+ return `{${properties.join('; ')}}`;
156
+ }
@@ -0,0 +1,3 @@
1
+ export { resolveSchemaDependenciesFromSchema } from './dep/resolve-schema-dependencies-from-shema.js';
2
+ export { type } from './dep/type.js';
3
+ export { normalizeTypes, toPascalCase } from './utils/index.js';
@@ -0,0 +1,3 @@
1
+ export { resolveSchemaDependenciesFromSchema } from './dep/resolve-schema-dependencies-from-shema.js';
2
+ export { type } from './dep/type.js';
3
+ export { normalizeTypes, toPascalCase } from './utils/index.js';
@@ -0,0 +1,15 @@
1
+ /**
2
+ *
3
+ * @param name
4
+ * @returns
5
+ */
6
+ export declare function toPascalCase(name: string): string;
7
+ /**
8
+ *
9
+ * @param t
10
+ * @returns
11
+ */
12
+ export declare function normalizeTypes(t?: 'string' | 'number' | 'integer' | 'date' | 'boolean' | 'array' | 'object' | 'null' | [
13
+ 'string' | 'number' | 'integer' | 'date' | 'boolean' | 'array' | 'object' | 'null',
14
+ ...('string' | 'number' | 'integer' | 'date' | 'boolean' | 'array' | 'object' | 'null')[]
15
+ ]): ("string" | "number" | "boolean" | "object" | "integer" | "date" | "array" | "null")[];