@mcp-web/decompose-zod-schema 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js ADDED
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+ import { readFile, writeFile } from 'node:fs/promises';
3
+ import { Command } from 'commander';
4
+ import { z } from 'zod';
5
+ import { decomposeSchema } from './decompose.js';
6
+ import { estimateTokensByJsonSchema } from './utils.js';
7
+ function log(message, verbose) {
8
+ if (verbose) {
9
+ console.log(`[INFO] ${message}`);
10
+ }
11
+ }
12
+ async function extractSchemaFromFile(filePath, schemaName) {
13
+ try {
14
+ // Read the TypeScript file
15
+ await readFile(filePath, 'utf-8');
16
+ // This is a simplified approach - in a real implementation, you might want to:
17
+ // 1. Use a TypeScript parser/AST
18
+ // 2. Dynamically import and evaluate the module
19
+ // 3. Support more complex schema definitions
20
+ // For now, we'll create a simple example schema that users can modify
21
+ const exampleSchema = z.object({
22
+ user: z.object({
23
+ id: z.string(),
24
+ name: z.string(),
25
+ email: z.string().email(),
26
+ age: z.number(),
27
+ profile: z.object({
28
+ bio: z.string(),
29
+ avatar: z.string().url().optional(),
30
+ preferences: z.object({
31
+ theme: z.enum(['light', 'dark', 'auto']),
32
+ language: z.enum([
33
+ 'en',
34
+ 'es',
35
+ 'fr',
36
+ 'de',
37
+ 'it',
38
+ 'pt',
39
+ 'ru',
40
+ 'ja',
41
+ 'ko',
42
+ 'zh',
43
+ ]),
44
+ notifications: z.boolean(),
45
+ newsletter: z.boolean(),
46
+ }),
47
+ }),
48
+ }),
49
+ settings: z.object({
50
+ privacy: z.enum(['public', 'private', 'friends']),
51
+ twoFactor: z.boolean(),
52
+ apiKeys: z.array(z.string()),
53
+ }),
54
+ metadata: z.object({
55
+ createdAt: z.date(),
56
+ updatedAt: z.date(),
57
+ version: z.number(),
58
+ tags: z.array(z.string()),
59
+ categories: z.enum(Array.from({ length: 150 }, (_, i) => `category-${i}`)),
60
+ }),
61
+ });
62
+ // Log warning about simplified extraction
63
+ console.warn(`Warning: Using example schema. In a real implementation, this would parse '${schemaName}' from '${filePath}'.`);
64
+ console.warn('To use your own schema, modify the CLI to properly import and extract your schema definition.');
65
+ return exampleSchema;
66
+ }
67
+ catch (error) {
68
+ throw new Error(`Failed to extract schema from ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
69
+ }
70
+ }
71
+ async function main() {
72
+ const program = new Command();
73
+ program
74
+ .name('decompose-zod-schema')
75
+ .description('Utility for decomposing large Zod schemas into smaller, manageable sub-schemas')
76
+ .version('0.1.0')
77
+ .requiredOption('-i, --input <file>', 'Input TypeScript file containing schema definition')
78
+ .option('-o, --output <file>', 'Output JSON file for decomposed schemas')
79
+ .requiredOption('-s, --schema <name>', 'Name of the schema variable to decompose')
80
+ .option('-t, --tokens <number>', 'Maximum tokens per decomposed schema', '2000')
81
+ .option('-e, --enum-size <number>', 'Maximum enum size before splitting', '200')
82
+ .option('-v, --verbose', 'Verbose output', false)
83
+ .addHelpText('after', `
84
+ Examples:
85
+ $ decompose-zod-schema -i schema.ts -s userSchema -o decomposed.json
86
+ $ decompose-zod-schema -i schema.ts -s configSchema -t 1000 -e 100 -v`);
87
+ program.parse();
88
+ const options = program.opts();
89
+ // Convert string options to numbers
90
+ options.tokens = parseInt(options.tokens, 10);
91
+ options.enumSize = parseInt(options.enumSize, 10);
92
+ try {
93
+ log(`Extracting schema '${options.schema}' from '${options.input}'`, options.verbose);
94
+ const schema = await extractSchemaFromFile(options.input, options.schema);
95
+ log('Estimating token count for original schema', options.verbose);
96
+ const originalTokens = estimateTokensByJsonSchema(schema);
97
+ log(`Original schema estimated tokens: ${originalTokens}`, options.verbose);
98
+ if (originalTokens <= options.tokens) {
99
+ log('Schema is already within token limits, no decomposition needed', options.verbose);
100
+ const result = {
101
+ originalTokens,
102
+ maxTokensPerSchema: options.tokens,
103
+ needsDecomposition: false,
104
+ decomposedSchemas: [
105
+ {
106
+ name: 'complete',
107
+ schema: JSON.parse(JSON.stringify(schema._def)),
108
+ targetPaths: ['*'],
109
+ estimatedTokens: originalTokens,
110
+ },
111
+ ],
112
+ };
113
+ if (options.output) {
114
+ await writeFile(options.output, JSON.stringify(result, null, 2), 'utf-8');
115
+ log(`Results written to ${options.output}`, options.verbose);
116
+ }
117
+ else {
118
+ console.log(JSON.stringify(result, null, 2));
119
+ }
120
+ return;
121
+ }
122
+ log('Decomposing schema', options.verbose);
123
+ const decomposed = decomposeSchema(schema, {
124
+ maxTokensPerSchema: options.tokens,
125
+ maxOptionsPerEnum: options.enumSize,
126
+ });
127
+ log(`Created ${decomposed.length} decomposed schemas`, options.verbose);
128
+ const result = {
129
+ originalTokens,
130
+ maxTokensPerSchema: options.tokens,
131
+ maxOptionsPerEnum: options.enumSize,
132
+ needsDecomposition: true,
133
+ decomposedSchemas: decomposed.map((item) => ({
134
+ name: item.name,
135
+ schema: JSON.parse(JSON.stringify(item.schema._def)),
136
+ targetPaths: item.targetPaths,
137
+ estimatedTokens: estimateTokensByJsonSchema(item.schema),
138
+ })),
139
+ };
140
+ // Summary
141
+ log('\\nDecomposition Summary:', options.verbose);
142
+ log(` Original tokens: ${originalTokens}`, options.verbose);
143
+ log(` Decomposed into: ${decomposed.length} schemas`, options.verbose);
144
+ decomposed.forEach((item, index) => {
145
+ const tokens = estimateTokensByJsonSchema(item.schema);
146
+ log(` Schema ${index + 1} (${item.name}): ${tokens} tokens, ${item.targetPaths.length} paths`, options.verbose);
147
+ });
148
+ if (options.output) {
149
+ await writeFile(options.output, JSON.stringify(result, null, 2), 'utf-8');
150
+ log(`Results written to ${options.output}`, options.verbose);
151
+ }
152
+ else {
153
+ console.log(JSON.stringify(result, null, 2));
154
+ }
155
+ }
156
+ catch (error) {
157
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
158
+ process.exit(1);
159
+ }
160
+ }
161
+ // Run the CLI if this file is being executed directly
162
+ if (import.meta.url === `file://${process.argv[1]}`) {
163
+ main();
164
+ }
@@ -0,0 +1,41 @@
1
+ import { ZodObject, type ZodType } from 'zod';
2
+ import type { DecomposedSchema, DecompositionOptions, SplitPlan } from './types.js';
3
+ /**
4
+ * Decomposes a Zod schema into smaller schemas based on a split plan or options.
5
+ *
6
+ * This function supports two modes:
7
+ * 1. **Manual decomposition**: Pass a SplitPlan array to control exactly how the schema is split
8
+ * 2. **Automatic decomposition**: Pass options to automatically suggest splits based on size
9
+ *
10
+ * @param schema - The Zod object schema to decompose
11
+ * @param planOrOptions - Either a SplitPlan for manual decomposition or DecompositionOptions for automatic
12
+ * @returns Array of decomposed schemas with their target paths
13
+ *
14
+ * @example Manual decomposition with SplitPlan
15
+ * ```typescript
16
+ * const decomposed = decomposeSchema(GameStateSchema, [
17
+ * 'board',
18
+ * ['currentPlayer', 'turn'],
19
+ * 'settings',
20
+ * ]);
21
+ * ```
22
+ *
23
+ * @example Automatic decomposition with options
24
+ * ```typescript
25
+ * const decomposed = decomposeSchema(LargeSchema, {
26
+ * maxTokensPerSchema: 2000,
27
+ * maxOptionsPerEnum: 200,
28
+ * });
29
+ * ```
30
+ */
31
+ export declare function decomposeSchema(schema: ZodObject<Record<string, ZodType>>, planOrOptions: SplitPlan | DecompositionOptions): DecomposedSchema[];
32
+ /**
33
+ * Manually decompose a schema using a predefined split plan.
34
+ * This function takes a schema and a split plan that defines exactly how to split it.
35
+ *
36
+ * @param schema - The Zod schema to decompose
37
+ * @param plan - Array of path specifications defining how to split the schema
38
+ * @returns Array of decomposed schemas with their target paths
39
+ */
40
+ export declare function decomposeSchemaWithPlan(schema: ZodObject<Record<string, ZodType>>, plan: SplitPlan): DecomposedSchema[];
41
+ //# sourceMappingURL=decompose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decompose.d.ts","sourceRoot":"","sources":["../src/decompose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,SAAS,EAA6B,KAAK,OAAO,EAAK,MAAM,KAAK,CAAC;AAE/F,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAoB,SAAS,EAAE,MAAM,YAAY,CAAC;AAStG;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1C,aAAa,EAAE,SAAS,GAAG,oBAAoB,GAC9C,gBAAgB,EAAE,CAepB;AAGD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1C,IAAI,EAAE,SAAS,GACd,gBAAgB,EAAE,CAuRpB"}
@@ -0,0 +1,383 @@
1
+ import { ZodArray, ZodEnum, ZodObject, ZodRecord, z } from 'zod';
2
+ import { suggestDecompositionPlan } from './split-suggestions.js';
3
+ import { evenChunk, extractSchemaForPaths, getSchemaAtPath, parseEnumSplit, } from './utils.js';
4
+ import { validatePlan } from './validate.js';
5
+ /**
6
+ * Decomposes a Zod schema into smaller schemas based on a split plan or options.
7
+ *
8
+ * This function supports two modes:
9
+ * 1. **Manual decomposition**: Pass a SplitPlan array to control exactly how the schema is split
10
+ * 2. **Automatic decomposition**: Pass options to automatically suggest splits based on size
11
+ *
12
+ * @param schema - The Zod object schema to decompose
13
+ * @param planOrOptions - Either a SplitPlan for manual decomposition or DecompositionOptions for automatic
14
+ * @returns Array of decomposed schemas with their target paths
15
+ *
16
+ * @example Manual decomposition with SplitPlan
17
+ * ```typescript
18
+ * const decomposed = decomposeSchema(GameStateSchema, [
19
+ * 'board',
20
+ * ['currentPlayer', 'turn'],
21
+ * 'settings',
22
+ * ]);
23
+ * ```
24
+ *
25
+ * @example Automatic decomposition with options
26
+ * ```typescript
27
+ * const decomposed = decomposeSchema(LargeSchema, {
28
+ * maxTokensPerSchema: 2000,
29
+ * maxOptionsPerEnum: 200,
30
+ * });
31
+ * ```
32
+ */
33
+ export function decomposeSchema(schema, planOrOptions) {
34
+ // Check if it's a split plan (array) or options (object with specific properties)
35
+ if (Array.isArray(planOrOptions)) {
36
+ // Manual decomposition with split plan
37
+ return decomposeSchemaWithPlan(schema, planOrOptions);
38
+ }
39
+ else {
40
+ // Automatic decomposition with size-based suggestions
41
+ // Provide defaults for missing options
42
+ const sizeBasedOptions = {
43
+ maxTokensPerSchema: planOrOptions.maxTokensPerSchema ?? 2000,
44
+ maxOptionsPerEnum: planOrOptions.maxOptionsPerEnum ?? 200,
45
+ };
46
+ const suggestedPlan = suggestDecompositionPlan(schema, sizeBasedOptions);
47
+ return decomposeSchemaWithPlan(schema, suggestedPlan);
48
+ }
49
+ }
50
+ /**
51
+ * Manually decompose a schema using a predefined split plan.
52
+ * This function takes a schema and a split plan that defines exactly how to split it.
53
+ *
54
+ * @param schema - The Zod schema to decompose
55
+ * @param plan - Array of path specifications defining how to split the schema
56
+ * @returns Array of decomposed schemas with their target paths
57
+ */
58
+ export function decomposeSchemaWithPlan(schema, plan) {
59
+ // Validate the split plan first
60
+ const validationErrors = validatePlan(plan, schema);
61
+ if (validationErrors.length > 0) {
62
+ throw new Error(`Invalid split plan: ${validationErrors.join(', ')}`);
63
+ }
64
+ const result = [];
65
+ const processedPaths = new Set();
66
+ const processedItems = new Set();
67
+ const parsedSplits = plan.map(split => ({
68
+ original: split,
69
+ ...parseArrayNotation(split)
70
+ }));
71
+ // Group splits by type
72
+ const arrayGroups = new Map();
73
+ const recordGroups = new Map();
74
+ const regularSplits = [];
75
+ parsedSplits.forEach(({ original, path, isArrayElement }) => {
76
+ const parsed = parseEnumSplit(original);
77
+ if (parsed.isArraySplit) {
78
+ // Handle nested array splits like 'views[].tracks.top[]'
79
+ if (original.includes('[].') && original.match(/^(.+)\[\]\.(.+)\[\]$/)) {
80
+ // This is a nested array split - handle it specially
81
+ if (!arrayGroups.has('nested'))
82
+ arrayGroups.set('nested', []);
83
+ arrayGroups.get('nested')?.push(original);
84
+ }
85
+ else {
86
+ // Regular array split like 'views[]'
87
+ if (!arrayGroups.has(parsed.path))
88
+ arrayGroups.set(parsed.path, []);
89
+ arrayGroups.get(parsed.path)?.push(original);
90
+ }
91
+ }
92
+ else if (parsed.isRecordSplit) {
93
+ if (!recordGroups.has(parsed.path))
94
+ recordGroups.set(parsed.path, []);
95
+ recordGroups.get(parsed.path)?.push(original);
96
+ }
97
+ else if (isArrayElement) {
98
+ if (!arrayGroups.has(path))
99
+ arrayGroups.set(path, []);
100
+ arrayGroups.get(path)?.push(original);
101
+ }
102
+ else {
103
+ regularSplits.push(original);
104
+ }
105
+ });
106
+ // Handle array splits
107
+ arrayGroups.forEach((splits, arrayPath) => {
108
+ if (arrayPath === 'nested') {
109
+ // Handle nested array splits like 'views[].tracks.top[]'
110
+ splits.forEach(splitPath => {
111
+ const nestedMatch = splitPath.match(/^(.+)\[\]\.(.+)\[\]$/);
112
+ if (nestedMatch) {
113
+ const [, basePath, subPath] = nestedMatch;
114
+ // Get the base array schema (e.g., 'views')
115
+ const baseArraySchema = getSchemaAtPath(schema, basePath);
116
+ if (!(baseArraySchema instanceof ZodArray)) {
117
+ console.warn(`Expected array at path: ${basePath}`);
118
+ return;
119
+ }
120
+ // Get the element schema and navigate to the sub-array
121
+ const elementSchema = baseArraySchema.element;
122
+ if (!(elementSchema instanceof ZodObject)) {
123
+ console.warn(`Expected object element in array at path: ${basePath}`);
124
+ return;
125
+ }
126
+ // Get the sub-array schema (e.g., 'tracks.top')
127
+ let subArraySchema = getSchemaAtPath(elementSchema, subPath);
128
+ // Unwrap optional schemas
129
+ if (subArraySchema && typeof subArraySchema === 'object' && 'unwrap' in subArraySchema) {
130
+ subArraySchema = subArraySchema.unwrap();
131
+ }
132
+ if (!(subArraySchema instanceof ZodArray)) {
133
+ console.warn(`Expected array at nested path: ${basePath}[].${subPath}`);
134
+ return;
135
+ }
136
+ // Create the schema for the nested array element
137
+ const nestedElementSchema = createArrayElementSchema(subArraySchema);
138
+ result.push({
139
+ name: `${basePath.replace(/\./g, '-')}-${subPath.replace(/\./g, '-')}-item`,
140
+ schema: nestedElementSchema,
141
+ targetPaths: [splitPath]
142
+ });
143
+ }
144
+ });
145
+ }
146
+ else {
147
+ // Handle regular array splits
148
+ const arraySchema = getSchemaAtPath(schema, arrayPath);
149
+ if (!(arraySchema instanceof ZodArray)) {
150
+ console.warn(`Expected array at path: ${arrayPath}`);
151
+ return;
152
+ }
153
+ splits.forEach(splitPath => {
154
+ const parsed = parseEnumSplit(splitPath);
155
+ if (parsed.isArraySplit) {
156
+ const elementSchema = createArrayElementSchema(arraySchema);
157
+ result.push({
158
+ name: `${arrayPath.replace(/\./g, '-')}-item`,
159
+ schema: elementSchema,
160
+ targetPaths: [splitPath]
161
+ });
162
+ }
163
+ else {
164
+ // Handle legacy array notation with exclusions
165
+ const { excludedSubArrays } = parseArrayNotation(splitPath);
166
+ const elementSchema = createArrayElementSchema(arraySchema, excludedSubArrays);
167
+ result.push({
168
+ name: `${arrayPath.replace(/\./g, '-')}-item`,
169
+ schema: elementSchema,
170
+ targetPaths: [splitPath]
171
+ });
172
+ }
173
+ });
174
+ }
175
+ });
176
+ // Handle record splits
177
+ recordGroups.forEach((splits, recordPath) => {
178
+ const recordSchema = getSchemaAtPath(schema, recordPath);
179
+ if (!(recordSchema instanceof ZodRecord)) {
180
+ console.warn(`Expected record at path: ${recordPath}`);
181
+ return;
182
+ }
183
+ splits.forEach(splitPath => {
184
+ const elementSchema = createRecordElementSchema(recordSchema);
185
+ result.push({
186
+ name: `${recordPath.replace(/\./g, '-')}-entry`,
187
+ schema: elementSchema,
188
+ targetPaths: [splitPath]
189
+ });
190
+ });
191
+ });
192
+ for (const item of plan) {
193
+ const parsed = parseEnumSplit(item);
194
+ // Skip if we've already processed this exact item
195
+ if (processedItems.has(item)) {
196
+ continue;
197
+ }
198
+ // Skip array and record splits as they're handled above
199
+ if (parsed.isArraySplit || parsed.isRecordSplit) {
200
+ processedItems.add(item);
201
+ continue;
202
+ }
203
+ const schemaAtPath = getSchemaAtPath(schema, parsed.path);
204
+ if (!schemaAtPath) {
205
+ continue; // Skip invalid paths (already validated above)
206
+ }
207
+ // Handle enum splitting
208
+ if (schemaAtPath instanceof ZodEnum && parsed.chunkSize) {
209
+ const enumOptions = schemaAtPath.options;
210
+ const chunks = evenChunk(enumOptions, parsed.chunkSize);
211
+ chunks.forEach((chunk, index) => {
212
+ const chunkName = chunks.length > 1 ? `${parsed.path}-${index + 1}` : parsed.path;
213
+ const startIndex = index * Math.ceil(enumOptions.length / chunks.length);
214
+ const endIndex = Math.min(startIndex + chunk.length, enumOptions.length);
215
+ const slicePath = `${parsed.path}[${startIndex}:${endIndex}]`;
216
+ // Create schema with just this enum chunk
217
+ const chunkSchema = z.object({
218
+ [parsed.path.split('.').pop() || parsed.path]: z.enum(chunk),
219
+ });
220
+ result.push({
221
+ name: chunkName,
222
+ schema: chunkSchema,
223
+ targetPaths: [slicePath],
224
+ });
225
+ });
226
+ processedItems.add(item);
227
+ }
228
+ // Handle slice notation (including shorthand)
229
+ else if (schemaAtPath instanceof ZodEnum && parsed.start !== undefined) {
230
+ const enumOptions = schemaAtPath.options;
231
+ const end = parsed.end !== undefined ? parsed.end : enumOptions.length;
232
+ const slicedOptions = enumOptions.slice(parsed.start, end);
233
+ if (slicedOptions.length > 0) {
234
+ const sliceSchema = z.object({
235
+ [parsed.path.split('.').pop() || parsed.path]: z.enum(slicedOptions),
236
+ });
237
+ // Generate appropriate name for the slice
238
+ const sliceName = parsed.end !== undefined
239
+ ? `${parsed.path}-${parsed.start}-${parsed.end}`
240
+ : `${parsed.path}-${parsed.start}-end`;
241
+ result.push({
242
+ name: sliceName,
243
+ schema: sliceSchema,
244
+ targetPaths: [item],
245
+ });
246
+ }
247
+ processedItems.add(item);
248
+ }
249
+ // Handle regular path extraction
250
+ else {
251
+ // Group paths that share the same root for better organization
252
+ const pathsToInclude = plan
253
+ .filter((planItem) => {
254
+ const planParsed = parseEnumSplit(planItem);
255
+ return (planParsed.path === parsed.path ||
256
+ planParsed.path.startsWith(`${parsed.path}.`));
257
+ })
258
+ .map((planItem) => parseEnumSplit(planItem).path)
259
+ .filter((path) => !processedPaths.has(path));
260
+ if (pathsToInclude.length > 0) {
261
+ try {
262
+ const extractedSchema = extractSchemaForPaths(schema, pathsToInclude);
263
+ result.push({
264
+ name: parsed.path.replace(/\./g, '-'),
265
+ schema: extractedSchema,
266
+ targetPaths: pathsToInclude,
267
+ });
268
+ // Mark all included paths as processed
269
+ pathsToInclude.forEach((path) => processedPaths.add(path));
270
+ }
271
+ catch (_error) {
272
+ // If extraction fails, create a simple schema with just this path
273
+ const shape = {};
274
+ const parts = parsed.path.split('.');
275
+ const leafKey = parts[parts.length - 1];
276
+ if (parts.length === 1) {
277
+ shape[leafKey] = schemaAtPath;
278
+ }
279
+ else {
280
+ // For nested paths, create the nested structure
281
+ let current = shape;
282
+ for (let i = 0; i < parts.length - 1; i++) {
283
+ const part = parts[i];
284
+ current[part] = z.object({});
285
+ current = current[part]
286
+ .shape;
287
+ }
288
+ current[leafKey] = schemaAtPath;
289
+ }
290
+ result.push({
291
+ name: parsed.path.replace(/\./g, '-'),
292
+ schema: z.object(shape),
293
+ targetPaths: [parsed.path],
294
+ });
295
+ processedPaths.add(parsed.path);
296
+ }
297
+ }
298
+ }
299
+ }
300
+ return result;
301
+ }
302
+ const parseArrayNotation = (splitPath) => {
303
+ const arrayMatch = splitPath.match(/^(.+)\[\](.*)$/);
304
+ if (arrayMatch) {
305
+ const [, basePath, subPath] = arrayMatch;
306
+ return {
307
+ path: basePath,
308
+ isArrayElement: true,
309
+ excludedSubArrays: subPath ? [subPath.slice(1)] : [] // Remove leading dot
310
+ };
311
+ }
312
+ return { path: splitPath, isArrayElement: false, excludedSubArrays: [] };
313
+ };
314
+ const createArrayElementSchema = (arraySchema, excludedPaths = []) => {
315
+ let elementSchema = arraySchema.element;
316
+ // Unwrap ZodLazy schemas
317
+ if (elementSchema && typeof elementSchema === 'object' && '_def' in elementSchema &&
318
+ elementSchema._def.type === 'lazy') {
319
+ const lazySchema = elementSchema;
320
+ elementSchema = lazySchema._def.getter();
321
+ }
322
+ // For array splits, we need either ZodObject or ZodUnion (for different track types)
323
+ if (!(elementSchema instanceof ZodObject) && elementSchema?.constructor.name !== 'ZodUnion') {
324
+ throw new Error(`Array element must be an object or union for array splitting, got ${elementSchema?.constructor.name}`);
325
+ }
326
+ // Create element schema with exclusions
327
+ let finalElementSchema = elementSchema;
328
+ if (elementSchema instanceof ZodObject) {
329
+ const elementWithExclusions = createSchemaWithExclusions(elementSchema, '', excludedPaths);
330
+ finalElementSchema = elementWithExclusions || elementSchema;
331
+ }
332
+ // For union schemas, we keep them as-is since they represent valid track types
333
+ // Wrap with index
334
+ return z.object({
335
+ index: z.number().min(0),
336
+ value: finalElementSchema
337
+ });
338
+ };
339
+ const createRecordElementSchema = (recordSchema) => {
340
+ // Access internal properties of ZodRecord
341
+ const keySchema = recordSchema._def.keyType || z.string();
342
+ const valueSchema = recordSchema._def.valueType;
343
+ // Transform z.record(K, V) to z.object({ key: K, value: V })
344
+ return z.object({
345
+ key: keySchema,
346
+ value: valueSchema
347
+ });
348
+ };
349
+ const createSchemaWithExclusions = (rootSchema, basePath, exclusions) => {
350
+ const baseSchema = getSchemaAtPath(rootSchema, basePath);
351
+ if (!(baseSchema instanceof ZodObject)) {
352
+ return baseSchema || null;
353
+ }
354
+ // Filter exclusions to only those that are direct children of basePath
355
+ const relevantExclusions = exclusions
356
+ .filter(exc => {
357
+ if (!basePath) {
358
+ // For root level, exclude direct children
359
+ return !exc.includes('.');
360
+ }
361
+ else {
362
+ // For nested paths, exclude direct children of this path
363
+ return exc.startsWith(`${basePath}.`) &&
364
+ !exc.slice(basePath.length + 1).includes('.');
365
+ }
366
+ })
367
+ .map(exc => basePath ? exc.slice(basePath.length + 1) : exc);
368
+ if (relevantExclusions.length === 0) {
369
+ return baseSchema;
370
+ }
371
+ // Create new shape excluding the specified properties
372
+ const newShape = {};
373
+ Object.entries(baseSchema.shape).forEach(([key, schema]) => {
374
+ if (!relevantExclusions.includes(key)) {
375
+ newShape[key] = schema;
376
+ }
377
+ });
378
+ // Return null if all properties were excluded
379
+ if (Object.keys(newShape).length === 0) {
380
+ return null;
381
+ }
382
+ return z.object(newShape);
383
+ };
@@ -0,0 +1,8 @@
1
+ export { applyPartialUpdate } from './apply.js';
2
+ export { decomposeSchema } from './decompose.js';
3
+ export { PlanBuilder } from './plan-builder.js';
4
+ export { defaultStrategyRegistry, SemanticSuggestionStrategy, SizeBasedSuggestionStrategy, SuggestionStrategyRegistry, suggestDecompositionPlan, suggestWithStrategy, } from './split-suggestions.js';
5
+ export type { DecomposedSchema, DecompositionOptions, SizeBasedOptions, Split, SplitPlan, SuggestionStrategy, } from './types.js';
6
+ export { conditionalEnumSplit } from './utils.js';
7
+ export { validatePlan } from './validate.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,EACL,SAAS,EACT,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { applyPartialUpdate } from './apply.js';
2
+ export { decomposeSchema } from './decompose.js';
3
+ export { PlanBuilder } from './plan-builder.js';
4
+ export { defaultStrategyRegistry, SemanticSuggestionStrategy, SizeBasedSuggestionStrategy, SuggestionStrategyRegistry, suggestDecompositionPlan, suggestWithStrategy, } from './split-suggestions.js';
5
+ export { conditionalEnumSplit } from './utils.js';
6
+ export { validatePlan } from './validate.js';