@abapify/ts-xsd 0.4.1 → 0.4.3

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
@@ -1,37 +1,32 @@
1
1
  # ts-xsd
2
2
 
3
- [![version](https://img.shields.io/github/package-json/v/abapify/adt-cli?filename=packages/ts-xsd/package.json)](https://github.com/abapify/adt-cli/pkgs/npm/%40abapify%2Fts-xsd)
3
+ [![npm version](https://img.shields.io/npm/v/@abapify/ts-xsd)](https://www.npmjs.com/package/@abapify/ts-xsd)
4
+ [![license](https://img.shields.io/npm/l/@abapify/ts-xsd)](https://github.com/abapify/adt-cli/blob/main/LICENSE)
4
5
 
5
- **Core XSD parser, builder, and type inference** with **1:1 TypeScript representation** of W3C XML Schema Definition (XSD) 1.1.
6
+ **W3C XSD 1.1 parser, builder, and TypeScript type inference** — a 1:1 TypeScript representation of XML Schema Definition.
6
7
 
7
- ## Overview
8
+ ## Why
8
9
 
9
- `ts-xsd` is a comprehensive TypeScript library for working with W3C XSD schemas. It provides:
10
+ Working with XSD schemas in TypeScript usually means hand-maintaining types that drift from the source of truth. `ts-xsd` closes that gap:
10
11
 
11
- | Module | Purpose |
12
- | ----------- | ------------------------------------------------------------------- |
13
- | **xsd** | Parse XSD files into typed `Schema` objects, build XSD from objects |
14
- | **infer** | Compile-time TypeScript type inference from schema literals |
15
- | **xml** | Parse/build XML documents using schema definitions |
16
- | **codegen** | Generate TypeScript schema literals from XSD files |
17
-
18
- ### Key Features
19
-
20
- - **Pure W3C XSD 1.1** - Types match the official [XMLSchema.xsd](https://www.w3.org/TR/xmlschema11-1/XMLSchema.xsd) exactly
21
- - **Full roundtrip** - `XSD → Schema → XSD` with semantic preservation
22
- - **Type inference** - `InferSchema<T>` extracts TypeScript types from schema literals
23
- - **Shared types** - Cross-schema type resolution via `$imports`
24
- - **Tree-shakeable** - Only import what you need
25
- - **Zero runtime dependencies** - Only `@xmldom/xmldom` for DOM parsing
12
+ - **Parse** any W3C XSD 1.1 schema into a typed `Schema` object — no invented properties, no shortcuts.
13
+ - **Infer** TypeScript types at compile time from schema literals via `InferSchema<T>`.
14
+ - **Generate** TypeScript interfaces at build time when schemas are too complex for compile-time inference.
15
+ - **Round-trip** `XSD → Schema → XSD` with semantic preservation.
16
+ - **Parse/build XML** documents against a schema definition.
26
17
 
27
18
  ## Installation
28
19
 
29
20
  ```bash
30
21
  npm install @abapify/ts-xsd
31
22
  # or
23
+ pnpm add @abapify/ts-xsd
24
+ # or
32
25
  bun add @abapify/ts-xsd
33
26
  ```
34
27
 
28
+ > **Runtime dependency:** `@xmldom/xmldom` (DOM parsing). `ts-morph` and `zod` are required for codegen features.
29
+
35
30
  ## Quick Start
36
31
 
37
32
  ### Parse and Build XSD
@@ -61,7 +56,6 @@ const xsd = buildXsd(schema, { pretty: true });
61
56
  ```typescript
62
57
  import type { InferSchema } from '@abapify/ts-xsd';
63
58
 
64
- // Define schema as const literal
65
59
  const personSchema = {
66
60
  element: [{ name: 'person', type: 'PersonType' }],
67
61
  complexType: [
@@ -77,22 +71,38 @@ const personSchema = {
77
71
  ],
78
72
  } as const;
79
73
 
80
- // Infer TypeScript type at compile time
74
+ // Infer TypeScript type at compile time — no runtime cost
81
75
  type Person = InferSchema<typeof personSchema>;
82
- // Result: { name: string; age?: number }
76
+ // => { name: string; age?: number }
83
77
  ```
84
78
 
85
- ### Parse XML with Schema
79
+ ### Parse and Build XML with a Schema
86
80
 
87
81
  ```typescript
88
82
  import { parseXml, buildXml } from '@abapify/ts-xsd';
89
83
 
90
84
  const xml = `<person><name>John</name><age>30</age></person>`;
91
85
  const data = parseXml(personSchema, xml);
92
- // data: { name: 'John', age: 30 }
86
+ // => { person: { name: 'John', age: 30 } }
87
+
88
+ const rebuilt = buildXml(personSchema, data, { xmlDecl: false });
89
+ // => <person><name>John</name><age>30</age></person>
90
+ ```
91
+
92
+ ### Generate TypeScript Interfaces from XSD
93
+
94
+ ```typescript
95
+ import { parseXsd, generateInterfaces } from '@abapify/ts-xsd';
96
+
97
+ const schema = parseXsd(xsdContent);
98
+ const { code } = generateInterfaces(schema, {
99
+ flatten: true, // Inline all nested types into one
100
+ addJsDoc: true, // Add JSDoc comments
101
+ rootTypeName: 'MySchema',
102
+ });
93
103
 
94
- const rebuilt = buildXml(personSchema, data);
95
- // rebuilt: <person><name>John</name><age>30</age></person>
104
+ // code is a ready-to-write .ts file:
105
+ // export type MySchema = { person: { name: string; age?: number } };
96
106
  ```
97
107
 
98
108
  ## API Reference
@@ -100,39 +110,49 @@ const rebuilt = buildXml(personSchema, data);
100
110
  ### XSD Module
101
111
 
102
112
  ```typescript
103
- import { parseXsd, buildXsd, type Schema } from '@abapify/ts-xsd';
113
+ import {
114
+ parseXsd,
115
+ buildXsd,
116
+ resolveImports,
117
+ loadSchema,
118
+ type Schema,
119
+ } from '@abapify/ts-xsd';
104
120
  ```
105
121
 
106
122
  #### `parseXsd(xsd: string): Schema`
107
123
 
108
- Parse an XSD XML string into a typed Schema object.
109
-
110
- ```typescript
111
- const schema = parseXsd(xsdString);
112
- console.log(schema.targetNamespace);
113
- console.log(schema.element?.[0].name);
114
- ```
124
+ Parse an XSD XML string into a typed `Schema` object.
115
125
 
116
126
  #### `buildXsd(schema: Schema, options?: BuildOptions): string`
117
127
 
118
- Build an XSD XML string from a Schema object.
128
+ Build an XSD XML string from a `Schema` object.
119
129
 
120
130
  ```typescript
121
131
  const xsd = buildXsd(schema, {
122
- prefix: 'xsd', // Namespace prefix (default: 'xs')
132
+ prefix: 'xs', // Namespace prefix (default: 'xs')
123
133
  pretty: true, // Pretty print (default: true)
124
- indent: ' ', // Indentation (default: ' ')
134
+ indent: ' ', // Indentation string
125
135
  });
126
136
  ```
127
137
 
128
- #### `resolveImports(schema: Schema, resolver: (location: string) => Schema): Schema`
138
+ #### `resolveImports(schema, availableSchemas): Schema`
129
139
 
130
- Resolve and link imported schemas for cross-schema type resolution.
140
+ Resolve and link imported schemas by matching `import.schemaLocation` to `$filename`. Returns a new schema with `$imports` populated.
131
141
 
132
142
  ```typescript
133
- const linkedSchema = resolveImports(schema, (location) => {
134
- return parseXsd(fs.readFileSync(location, 'utf-8'));
135
- });
143
+ const base = { ...parseXsd(baseXsd), $filename: 'base.xsd' };
144
+ const orders = { ...parseXsd(ordersXsd), $filename: 'orders.xsd' };
145
+
146
+ const linked = resolveImports(orders, [base]);
147
+ // linked.$imports = [base]
148
+ ```
149
+
150
+ #### `loadSchema(schemaPath: string, options?: LoaderOptions): Schema`
151
+
152
+ Load and parse an XSD file from disk. Synchronous. Set `autoLink: true` to automatically resolve imports, or `autoResolve: true` to merge everything into one schema.
153
+
154
+ ```typescript
155
+ const schema = loadSchema('/path/to/schema.xsd', { autoLink: true });
136
156
  ```
137
157
 
138
158
  ### Infer Module
@@ -143,15 +163,11 @@ import type { InferSchema, InferElement, SchemaLike } from '@abapify/ts-xsd';
143
163
 
144
164
  #### `InferSchema<T>`
145
165
 
146
- Infer TypeScript type from a schema literal. Returns union of all root element types.
147
-
148
- ```typescript
149
- type Data = InferSchema<typeof mySchema>;
150
- ```
166
+ Infer a TypeScript type from a schema literal (`as const`). Returns a union of all root element types.
151
167
 
152
- #### `InferElement<T, ElementName>`
168
+ #### `InferElement<T, Name>`
153
169
 
154
- Infer type for a specific element by name.
170
+ Infer the type for a specific root element by name.
155
171
 
156
172
  ```typescript
157
173
  type Person = InferElement<typeof schema, 'person'>;
@@ -174,13 +190,13 @@ type Person = InferElement<typeof schema, 'person'>;
174
190
  import { parseXml, buildXml } from '@abapify/ts-xsd';
175
191
  ```
176
192
 
177
- #### `parseXml<T>(schema: SchemaLike, xml: string): T`
193
+ #### `parseXml<T>(schema, xml: string): T`
178
194
 
179
- Parse XML string using schema definition.
195
+ Parse an XML string into a typed object using a schema definition.
180
196
 
181
- #### `buildXml<T>(schema: SchemaLike, data: T): string`
197
+ #### `buildXml<T>(schema, data: T, options?: XmlBuildOptions): string`
182
198
 
183
- Build XML string from data using schema definition.
199
+ Build an XML string from a typed object using a schema definition. Defaults to `xmlDecl: true` (includes `<?xml ...?>` declaration); pass `{ xmlDecl: false }` for bare output.
184
200
 
185
201
  ### Codegen Module
186
202
 
@@ -188,41 +204,88 @@ Build XML string from data using schema definition.
188
204
  import { generateSchemaLiteral, generateInterfaces } from '@abapify/ts-xsd';
189
205
  ```
190
206
 
191
- #### `generateSchemaLiteral(xsd: string, options?: GenerateOptions): string`
207
+ #### `generateSchemaLiteral(xsd: string, options?): string`
192
208
 
193
- Generate TypeScript schema literal from XSD content.
209
+ Generate a TypeScript `as const` schema literal from XSD content — for use with `InferSchema<T>`.
194
210
 
195
211
  ```typescript
196
212
  const code = generateSchemaLiteral(xsdContent, {
197
213
  name: 'PersonSchema',
198
- features: { $xmlns: true, $imports: true },
214
+ features: { $xmlns: true, $filename: true },
199
215
  exclude: ['annotation'],
200
216
  });
201
- // export default { ... } as const;
217
+ // export const PersonSchema = { ... } as const;
202
218
  ```
203
219
 
204
- #### `generateInterfaces(schema: Schema, options?: GenerateInterfacesOptions): GenerateInterfacesResult`
220
+ #### `generateInterfaces(schema: Schema, options?): { code: string }`
205
221
 
206
- Generate TypeScript interfaces from parsed schema. Returns an object with `code` property containing the generated TypeScript code.
222
+ Generate TypeScript interfaces from a parsed schema. Useful when schemas are too complex for compile-time `InferSchema<T>` (TypeScript TS2589 recursion limits).
207
223
 
208
224
  ```typescript
209
225
  const { code } = generateInterfaces(schema, {
210
226
  flatten: true, // Inline all nested types (default: false)
211
- addJsDoc: true, // Add JSDoc comments
212
- rootTypeName: 'MySchema', // Custom root type name
227
+ addJsDoc: true, // JSDoc comments
228
+ rootTypeName: 'MySchema',
229
+ });
230
+ ```
231
+
232
+ | Option | Type | Default | Description |
233
+ | -------------- | --------- | ------- | -------------------------------- |
234
+ | `flatten` | `boolean` | `false` | Inline all nested types into one |
235
+ | `addJsDoc` | `boolean` | `false` | Add JSDoc comments |
236
+ | `rootTypeName` | `string` | auto | Custom name for the root type |
237
+
238
+ ### CLI
239
+
240
+ The package ships a CLI for code generation:
241
+
242
+ ```bash
243
+ # Config-based (recommended) — uses ts-xsd.config.ts in cwd
244
+ npx ts-xsd codegen
245
+
246
+ # Single-file mode
247
+ npx ts-xsd codegen person.xsd
248
+ npx ts-xsd codegen person.xsd ./generated/person-schema.ts
249
+ npx ts-xsd codegen person.xsd --name=PersonSchema
250
+ ```
251
+
252
+ ### Config-Based Generation
253
+
254
+ For multi-schema projects, use the composable generator system:
255
+
256
+ ```typescript
257
+ import {
258
+ defineConfig,
259
+ rawSchema,
260
+ interfaces,
261
+ } from '@abapify/ts-xsd/generators';
262
+
263
+ export default defineConfig({
264
+ sources: {
265
+ base: {
266
+ xsdDir: 'schemas',
267
+ outputDir: 'src/generated',
268
+ schemas: ['base'],
269
+ },
270
+ orders: {
271
+ xsdDir: 'schemas',
272
+ outputDir: 'src/generated',
273
+ schemas: ['orders'],
274
+ autoLink: true,
275
+ },
276
+ },
277
+ generators: [rawSchema(), interfaces({ flatten: true })],
213
278
  });
214
279
  ```
215
280
 
216
281
  ## Schema Structure
217
282
 
218
- The `Schema` type is a 1:1 TypeScript representation of W3C XSD:
283
+ The `Schema` type is a 1:1 TypeScript representation of W3C XSD. Non-W3C extension properties are prefixed with `$`:
219
284
 
220
285
  ```typescript
221
286
  interface Schema {
222
- // Namespace
223
287
  targetNamespace?: string;
224
288
  elementFormDefault?: 'qualified' | 'unqualified';
225
- attributeFormDefault?: 'qualified' | 'unqualified';
226
289
 
227
290
  // Composition
228
291
  import?: Import[];
@@ -236,171 +299,40 @@ interface Schema {
236
299
  attributeGroup?: NamedAttributeGroup[];
237
300
 
238
301
  // Extensions (non-W3C, prefixed with $)
239
- $xmlns?: { [prefix: string]: string };
240
- $imports?: Schema[]; // Resolved imported schemas
241
- $filename?: string; // Source filename
302
+ $xmlns?: { [prefix: string]: string }; // Namespace prefix mappings
303
+ $imports?: Schema[]; // Linked imported schemas
304
+ $filename?: string; // Source filename (for round-trip)
242
305
  }
243
306
  ```
244
307
 
245
308
  ### Cross-Schema Type Resolution
246
309
 
247
- Link schemas together for cross-schema type resolution:
248
-
249
- ```typescript
250
- const adtcore = parseXsd(adtcoreXsd);
251
- const classes = parseXsd(classesXsd);
252
-
253
- // Link schemas via $imports
254
- const linkedClasses = {
255
- ...classes,
256
- $imports: [adtcore],
257
- };
258
-
259
- // Now InferSchema can resolve types from adtcore
260
- type AbapClass = InferSchema<typeof linkedClasses>;
261
- ```
262
-
263
- ## Type Inference Deep Dive
264
-
265
- ### How It Works
266
-
267
- The type inference system uses TypeScript's conditional types to:
268
-
269
- 1. **Find root elements** - Extract element declarations from schema
270
- 2. **Resolve type references** - Look up `complexType` and `simpleType` by name
271
- 3. **Handle inheritance** - Process `complexContent/extension` for type inheritance
272
- 4. **Map XSD to TS** - Convert XSD types to TypeScript equivalents
273
- 5. **Handle optionality** - `minOccurs="0"` → optional property
274
- 6. **Handle arrays** - `maxOccurs="unbounded"` → array type
275
-
276
- ### Example: Complex Schema
310
+ Link schemas via `$imports` to resolve types across schema boundaries:
277
311
 
278
312
  ```typescript
279
- const schema = {
280
- $imports: [baseSchema],
281
- element: [{ name: 'order', type: 'OrderType' }],
282
- complexType: [
283
- {
284
- name: 'OrderType',
285
- complexContent: {
286
- extension: {
287
- base: 'base:BaseEntity', // Inherits from imported schema
288
- sequence: {
289
- element: [
290
- { name: 'items', type: 'ItemType', maxOccurs: 'unbounded' },
291
- { name: 'total', type: 'xs:decimal' },
292
- ],
293
- },
294
- },
295
- },
296
- },
297
- {
298
- name: 'ItemType',
299
- sequence: {
300
- element: [
301
- { name: 'sku', type: 'xs:string' },
302
- { name: 'quantity', type: 'xs:int' },
303
- ],
304
- },
305
- },
306
- ],
307
- } as const;
308
-
309
- type Order = InferSchema<typeof schema>;
310
- // Result:
311
- // {
312
- // ...BaseEntity, // Inherited properties
313
- // items: { sku: string; quantity: number }[];
314
- // total: number;
315
- // }
316
- ```
313
+ const base = { ...parseXsd(baseXsd), $filename: 'base.xsd' };
314
+ const orders = { ...parseXsd(ordersXsd), $filename: 'orders.xsd' };
317
315
 
318
- ## Architecture
316
+ const linked = resolveImports(orders, [base]);
319
317
 
318
+ // InferSchema can now resolve types from `base`
319
+ type OrderData = InferSchema<typeof linked>;
320
320
  ```
321
- @abapify/ts-xsd
322
- ├── src/
323
- │ ├── index.ts # Main exports
324
- │ ├── xsd/ # XSD parsing, building, resolution
325
- │ │ ├── types.ts # W3C 1:1 type definitions
326
- │ │ ├── parse.ts # XSD XML → Schema parser
327
- │ │ ├── build.ts # Schema → XSD XML builder
328
- │ │ ├── resolve.ts # Schema resolver (merges imports, expands inheritance)
329
- │ │ ├── traverser.ts # OO schema traversal with W3C types
330
- │ │ ├── loader.ts # XSD file loading with import resolution
331
- │ │ ├── schema-like.ts # Runtime schema type guards
332
- │ │ └── helpers.ts # Utility functions
333
- │ ├── infer/ # Compile-time type inference
334
- │ │ └── types.ts # InferSchema<T>, InferElement<T>
335
- │ ├── xml/ # XML parsing/building with schemas
336
- │ │ ├── parse.ts # XML → Object parser
337
- │ │ ├── build.ts # Object → XML builder
338
- │ │ ├── typed.ts # Typed schema wrapper
339
- │ │ └── dom-utils.ts # DOM manipulation utilities
340
- │ ├── walker/ # Schema traversal utilities
341
- │ │ └── index.ts # walkElements, walkComplexTypes, findSubstitutes
342
- │ ├── codegen/ # Code generation
343
- │ │ ├── generate.ts # Schema literal generator
344
- │ │ ├── interface-generator.ts # Interface generator API
345
- │ │ ├── ts-morph.ts # TypeScript AST manipulation
346
- │ │ ├── runner.ts # Config-based codegen runner
347
- │ │ └── cli.ts # CLI interface
348
- │ └── generators/ # Generator plugins
349
- │ ├── raw-schema.ts # Schema literal generator
350
- │ ├── interfaces.ts # TypeScript interfaces generator
351
- │ ├── typed-schemas.ts # Typed schema exports
352
- │ └── index-barrel.ts # Index file generator
353
- ```
354
-
355
- ### Key Components
356
-
357
- | Component | Purpose |
358
- | ------------- | ---------------------------------------------------------------------------------- |
359
- | **Resolver** | Merges `$imports`, expands `complexContent/extension`, handles `substitutionGroup` |
360
- | **Traverser** | OO traversal with real W3C XSD types (SchemaTraverser class) |
361
- | **Walker** | Functional iteration over schema elements, types, groups |
362
- | **Loader** | File-based XSD loading with automatic import resolution |
363
321
 
364
322
  ## Design Principles
365
323
 
366
- 1. **Pure W3C XSD** - No invented properties or conveniences
367
- 2. **Type safety** - Full TypeScript support with inference
368
- 3. **Minimal dependencies** - Only `@xmldom/xmldom`
369
- 4. **Tree-shakeable** - Import only what you need
370
- 5. **Tested against W3C** - Verified with official XMLSchema.xsd
371
-
372
- ## Testing
373
-
374
- ```bash
375
- # Run all tests
376
- npx nx test ts-xsd
377
-
378
- # Run with coverage
379
- npx nx test:coverage ts-xsd
380
- ```
381
-
382
- Tests include:
383
-
384
- - Unit tests for parser, builder, and inference
385
- - Integration tests with real XSD files
386
- - W3C XMLSchema.xsd roundtrip verification
387
-
388
- ## Related Packages
389
-
390
- - **[@abapify/adt-schemas](../adt-schemas)** - SAP ADT schemas using ts-xsd
391
- - **[@abapify/adt-contracts](../adt-contracts)** - REST contracts for SAP ADT APIs
392
- - **[@abapify/adt-plugin-abapgit](../adt-plugin-abapgit)** - abapGit plugin schemas
393
-
394
- ## Documentation
395
-
396
- - **[Codegen Guide](./docs/codegen.md)** - Comprehensive code generation documentation
397
- - **[AGENTS.md](./AGENTS.md)** - AI agent guidelines
324
+ 1. **Pure W3C XSD 1.1** — types match the official [XMLSchema.xsd](https://www.w3.org/TR/xmlschema11-1/XMLSchema.xsd) exactly; no invented properties.
325
+ 2. **Type-safe** — full TypeScript support with compile-time inference.
326
+ 3. **Minimal dependencies** — `@xmldom/xmldom` for DOM parsing, `ts-morph` and `zod` for codegen.
327
+ 4. **Tree-shakeable** — import only what you need.
328
+ 5. **Round-trip verified** — tested against the official W3C XMLSchema.xsd.
398
329
 
399
330
  ## References
400
331
 
401
332
  - [W3C XML Schema 1.1 Part 1: Structures](https://www.w3.org/TR/xmlschema11-1/)
402
333
  - [XMLSchema.xsd](https://www.w3.org/TR/xmlschema11-1/XMLSchema.xsd)
334
+ - [Codegen Guide](https://github.com/abapify/adt-cli/tree/main/packages/ts-xsd/docs/codegen.md)
403
335
 
404
336
  ## License
405
337
 
406
- MIT
338
+ [MIT](https://github.com/abapify/adt-cli/blob/main/LICENSE)
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { t as runCodegen } from "../runner-CRhT3smd.mjs";
3
- import { t as generateSchemaFile } from "../generate-DnX2dJZe.mjs";
2
+ import { t as runCodegen } from "../runner-BSGqj8LM.mjs";
3
+ import { t as generateSchemaFile } from "../generate-Br6rW6sE.mjs";
4
4
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { basename, dirname, resolve } from "node:path";
6
6
  import { pathToFileURL } from "node:url";
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../../src/codegen/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * ts-xsd Codegen CLI\n *\n * Usage:\n * ts-xsd codegen [--config=ts-xsd.config.ts] [--verbose]\n * ts-xsd codegen <input.xsd> [output.ts] [--name=SchemaName]\n *\n * Config-based (recommended):\n * ts-xsd codegen # Uses ts-xsd.config.ts in cwd\n * ts-xsd codegen --config=my-config.ts # Uses custom config\n * ts-xsd codegen --verbose # Verbose output\n *\n * Single-file (legacy):\n * ts-xsd codegen person.xsd\n * ts-xsd codegen person.xsd ./generated/person-schema.ts\n * ts-xsd codegen person.xsd --name=PersonSchema\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname, basename, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { generateSchemaFile } from './generate';\nimport { runCodegen } from './runner';\nimport type { CodegenConfig } from './types';\n\ninterface ConfigModeOptions {\n mode: 'config';\n config: string;\n verbose?: boolean;\n}\n\ninterface SingleFileModeOptions {\n mode: 'single';\n input: string;\n output: string;\n name: string;\n verbose?: boolean;\n}\n\ntype CliOptions = ConfigModeOptions | SingleFileModeOptions;\n\nfunction parseArgs(args: string[]): CliOptions {\n const positional: string[] = [];\n let config: string | undefined;\n let verbose = false;\n let name: string | undefined;\n\n for (const arg of args) {\n if (arg.startsWith('--config=')) {\n config = arg.slice(9);\n } else if (arg === '--verbose' || arg === '-v') {\n verbose = true;\n } else if (arg.startsWith('--name=')) {\n name = arg.slice(7);\n } else if (!arg.startsWith('-')) {\n positional.push(arg);\n }\n }\n\n // If first positional arg is an XSD file, use single-file mode\n if (positional[0]?.endsWith('.xsd')) {\n const input = positional[0];\n const defaultOutput = input.replace(/\\.xsd$/i, '-schema.ts');\n const output = positional[1] || defaultOutput;\n const defaultName =\n basename(input, '.xsd')\n .replaceAll(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\n .replace(/^./, (s) => s.toLowerCase()) + 'Schema';\n\n return {\n mode: 'single',\n input: resolve(input),\n output: resolve(output),\n name: name || defaultName,\n verbose,\n };\n }\n\n // Config-based mode\n return {\n mode: 'config',\n config: config || 'ts-xsd.config.ts',\n verbose,\n };\n}\n\nasync function runConfigMode(options: ConfigModeOptions) {\n const configPath = resolve(options.config);\n\n if (!existsSync(configPath)) {\n console.error(`❌ Config file not found: ${configPath}`);\n console.error(\n '\\nCreate a ts-xsd.config.ts file or specify --config=path/to/config.ts',\n );\n process.exit(1);\n }\n\n console.log(`📦 Loading config: ${configPath}\\n`);\n\n // Dynamic import of config file\n const configUrl = pathToFileURL(configPath).href;\n const configModule = await import(configUrl);\n const config: CodegenConfig = configModule.default;\n\n const result = await runCodegen(config, {\n rootDir: dirname(configPath),\n verbose: options.verbose,\n });\n\n console.log(`\\n📊 Summary:`);\n console.log(` - Schemas processed: ${result.schemas.length}`);\n console.log(` - Files generated: ${result.files.length}`);\n console.log(` - Errors: ${result.errors.length}`);\n\n if (result.errors.length > 0) {\n console.error('\\n❌ Errors:');\n for (const err of result.errors) {\n console.error(` - ${err.schema || 'unknown'}: ${err.error.message}`);\n }\n process.exit(1);\n }\n\n console.log('\\n✅ Codegen complete!');\n}\n\nfunction runSingleFileMode(options: SingleFileModeOptions) {\n console.log(`Reading: ${options.input}`);\n const xsdContent = readFileSync(options.input, 'utf-8');\n\n console.log(`Generating schema: ${options.name}`);\n const tsContent = generateSchemaFile(xsdContent, {\n name: options.name,\n comment: `Source: ${basename(options.input)}`,\n });\n\n mkdirSync(dirname(options.output), { recursive: true });\n\n console.log(`Writing: ${options.output}`);\n writeFileSync(options.output, tsContent);\n\n console.log(`Done! Generated ${tsContent.length} characters`);\n console.log(`\\nUsage in TypeScript:`);\n console.log(\n ` import { ${options.name} } from './${basename(options.output, '.ts')}';`,\n );\n console.log(` import type { InferSchema } from '@abapify/ts-xsd';`);\n console.log(` type MyType = InferSchema<typeof ${options.name}>;`);\n}\n\nfunction showHelp() {\n console.log(`\nts-xsd Codegen CLI\n\nUsage:\n ts-xsd codegen [options] Config-based generation (recommended)\n ts-xsd codegen <input.xsd> [output] Single-file generation (legacy)\n\nOptions:\n --config=FILE Config file path (default: ts-xsd.config.ts)\n --verbose, -v Verbose output\n --name=NAME Schema variable name (single-file mode only)\n --help, -h Show this help\n\nExamples:\n # Config-based (recommended)\n ts-xsd codegen # Uses ts-xsd.config.ts\n ts-xsd codegen --config=my-config.ts # Custom config\n ts-xsd codegen --verbose # With verbose output\n\n # Single-file (legacy)\n ts-xsd codegen person.xsd\n ts-xsd codegen person.xsd ./out/person.ts --name=PersonSchema\n`);\n}\n\nasync function main() {\n const args = process.argv.slice(2);\n\n if (args.includes('--help') || args.includes('-h')) {\n showHelp();\n process.exit(0);\n }\n\n const options = parseArgs(args);\n\n if (options.mode === 'config') {\n await runConfigMode(options);\n } else {\n runSingleFileMode(options);\n }\n}\n\nmain().catch((err) => {\n console.error('Fatal error:', err);\n process.exit(1);\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../../src/codegen/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * ts-xsd Codegen CLI\n *\n * Usage:\n * ts-xsd codegen [--config=ts-xsd.config.ts] [--verbose]\n * ts-xsd codegen <input.xsd> [output.ts] [--name=SchemaName]\n *\n * Config-based (recommended):\n * ts-xsd codegen # Uses ts-xsd.config.ts in cwd\n * ts-xsd codegen --config=my-config.ts # Uses custom config\n * ts-xsd codegen --verbose # Verbose output\n *\n * Single-file (legacy):\n * ts-xsd codegen person.xsd\n * ts-xsd codegen person.xsd ./generated/person-schema.ts\n * ts-xsd codegen person.xsd --name=PersonSchema\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname, basename, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { generateSchemaFile } from './generate';\nimport { runCodegen } from './runner';\nimport type { CodegenConfig } from './types';\n\ninterface ConfigModeOptions {\n mode: 'config';\n config: string;\n verbose?: boolean;\n}\n\ninterface SingleFileModeOptions {\n mode: 'single';\n input: string;\n output: string;\n name: string;\n verbose?: boolean;\n}\n\ntype CliOptions = ConfigModeOptions | SingleFileModeOptions;\n\nfunction parseArgs(args: string[]): CliOptions {\n const positional: string[] = [];\n let config: string | undefined;\n let verbose = false;\n let name: string | undefined;\n\n for (const arg of args) {\n if (arg.startsWith('--config=')) {\n config = arg.slice(9);\n } else if (arg === '--verbose' || arg === '-v') {\n verbose = true;\n } else if (arg.startsWith('--name=')) {\n name = arg.slice(7);\n } else if (!arg.startsWith('-')) {\n positional.push(arg);\n }\n }\n\n // If first positional arg is an XSD file, use single-file mode\n if (positional[0]?.endsWith('.xsd')) {\n const input = positional[0];\n const defaultOutput = input.replace(/\\.xsd$/i, '-schema.ts');\n const output = positional[1] || defaultOutput;\n const defaultName =\n basename(input, '.xsd')\n .replaceAll(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\n .replace(/^./, (s) => s.toLowerCase()) + 'Schema';\n\n return {\n mode: 'single',\n input: resolve(input),\n output: resolve(output),\n name: name || defaultName,\n verbose,\n };\n }\n\n // Config-based mode\n return {\n mode: 'config',\n config: config || 'ts-xsd.config.ts',\n verbose,\n };\n}\n\nasync function runConfigMode(options: ConfigModeOptions) {\n const configPath = resolve(options.config);\n\n if (!existsSync(configPath)) {\n console.error(`❌ Config file not found: ${configPath}`);\n console.error(\n '\\nCreate a ts-xsd.config.ts file or specify --config=path/to/config.ts',\n );\n process.exit(1);\n }\n\n console.log(`📦 Loading config: ${configPath}\\n`);\n\n // Dynamic import of config file\n const configUrl = pathToFileURL(configPath).href;\n const configModule = await import(configUrl);\n const config: CodegenConfig = configModule.default;\n\n const result = await runCodegen(config, {\n rootDir: dirname(configPath),\n verbose: options.verbose,\n });\n\n console.log(`\\n📊 Summary:`);\n console.log(` - Schemas processed: ${result.schemas.length}`);\n console.log(` - Files generated: ${result.files.length}`);\n console.log(` - Errors: ${result.errors.length}`);\n\n if (result.errors.length > 0) {\n console.error('\\n❌ Errors:');\n for (const err of result.errors) {\n console.error(` - ${err.schema || 'unknown'}: ${err.error.message}`);\n }\n process.exit(1);\n }\n\n console.log('\\n✅ Codegen complete!');\n}\n\nfunction runSingleFileMode(options: SingleFileModeOptions) {\n console.log(`Reading: ${options.input}`);\n const xsdContent = readFileSync(options.input, 'utf-8');\n\n console.log(`Generating schema: ${options.name}`);\n const tsContent = generateSchemaFile(xsdContent, {\n name: options.name,\n comment: `Source: ${basename(options.input)}`,\n });\n\n mkdirSync(dirname(options.output), { recursive: true });\n\n console.log(`Writing: ${options.output}`);\n writeFileSync(options.output, tsContent);\n\n console.log(`Done! Generated ${tsContent.length} characters`);\n console.log(`\\nUsage in TypeScript:`);\n console.log(\n ` import { ${options.name} } from './${basename(options.output, '.ts')}';`,\n );\n console.log(` import type { InferSchema } from '@abapify/ts-xsd';`);\n console.log(` type MyType = InferSchema<typeof ${options.name}>;`);\n}\n\nfunction showHelp() {\n console.log(`\nts-xsd Codegen CLI\n\nUsage:\n ts-xsd codegen [options] Config-based generation (recommended)\n ts-xsd codegen <input.xsd> [output] Single-file generation (legacy)\n\nOptions:\n --config=FILE Config file path (default: ts-xsd.config.ts)\n --verbose, -v Verbose output\n --name=NAME Schema variable name (single-file mode only)\n --help, -h Show this help\n\nExamples:\n # Config-based (recommended)\n ts-xsd codegen # Uses ts-xsd.config.ts\n ts-xsd codegen --config=my-config.ts # Custom config\n ts-xsd codegen --verbose # With verbose output\n\n # Single-file (legacy)\n ts-xsd codegen person.xsd\n ts-xsd codegen person.xsd ./out/person.ts --name=PersonSchema\n`);\n}\n\nasync function main() {\n const args = process.argv.slice(2);\n\n if (args.includes('--help') || args.includes('-h')) {\n showHelp();\n process.exit(0);\n }\n\n const options = parseArgs(args);\n\n if (options.mode === 'config') {\n await runConfigMode(options);\n } else {\n runSingleFileMode(options);\n }\n}\n\nmain().catch((err) => {\n console.error('Fatal error:', err);\n process.exit(1);\n});\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { g as parseXsd } from "./runner-CRhT3smd.mjs";
1
+ import { p as parseXsd } from "./runner-BSGqj8LM.mjs";
2
2
 
3
3
  //#region src/codegen/generate.ts
4
4
  /**
@@ -44,6 +44,7 @@ function applyTransforms(schema, features, exclude) {
44
44
  * Special marker for schema references (not serialized as strings).
45
45
  */
46
46
  var SchemaRef = class {
47
+ name;
47
48
  constructor(name) {
48
49
  this.name = name;
49
50
  }
@@ -172,8 +173,7 @@ function objectToLiteral(value, pretty, indent, depth, singleQuote = false) {
172
173
  return `[${items.join(", ")}]`;
173
174
  }
174
175
  if (typeof value === "object") {
175
- const obj = value;
176
- const entries = Object.entries(obj).filter(([, v]) => v !== void 0);
176
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0);
177
177
  if (entries.length === 0) return "{}";
178
178
  const props = entries.map(([key, val]) => {
179
179
  let keyStr = key;
@@ -196,7 +196,7 @@ function objectToLiteral(value, pretty, indent, depth, singleQuote = false) {
196
196
  /**
197
197
  * JavaScript reserved words that cannot be used as variable names.
198
198
  */
199
- const RESERVED_WORDS = new Set([
199
+ const RESERVED_WORDS = /* @__PURE__ */ new Set([
200
200
  "break",
201
201
  "case",
202
202
  "catch",
@@ -259,4 +259,4 @@ function pascalCase(str) {
259
259
 
260
260
  //#endregion
261
261
  export { generateSchemaLiteral as n, generateSchemaFile as t };
262
- //# sourceMappingURL=generate-DnX2dJZe.mjs.map
262
+ //# sourceMappingURL=generate-Br6rW6sE.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-Br6rW6sE.mjs","names":["obj"],"sources":["../src/codegen/generate.ts"],"sourcesContent":["/**\n * Schema Literal Generator\n *\n * Transforms a parsed Schema object into a TypeScript literal string\n * that preserves type information for InferSchema<T>.\n */\n\nimport { parseXsd } from '../xsd';\nimport type { Schema } from '../xsd/types';\n\nexport interface GenerateOptions {\n /** Variable name for the exported schema */\n name?: string;\n /** Add JSDoc comment */\n comment?: string;\n /** Pretty print with indentation */\n pretty?: boolean;\n /** Indentation string (default: ' ') */\n indent?: string;\n /**\n * Enable specific $ metadata features in output.\n * - `$xmlns`: Keep `$xmlns` in output (already parsed as $xmlns)\n * - `$imports`: Rename `import` → `$imports` for schema linking\n * - `$filename`: Include `$filename` (uses `name` option + '.xsd')\n *\n * @example\n * ```typescript\n * features: { $xmlns: true, $imports: true, $filename: true }\n * ```\n */\n features?: {\n /** Keep `$xmlns` in output */\n $xmlns?: boolean;\n /** Rename `import` → `$imports` for schema linking */\n $imports?: boolean;\n /** Include `$filename` (uses `name` + '.xsd') */\n $filename?: boolean;\n };\n /**\n * Property names to exclude from output.\n * Useful for removing annotations, documentation, etc.\n *\n * @example\n * ```typescript\n * exclude: ['annotation'] // Remove all annotation elements\n * ```\n */\n exclude?: string[];\n /** Resolver for import paths (schemaLocation -> module path) */\n importResolver?: (schemaLocation: string) => string | null;\n /**\n * Generate isolatedDeclarations-compatible output.\n * Uses `satisfies Schema as const` instead of just `as const`.\n */\n isolatedDeclarations?: boolean;\n /** Use single quotes for string literals and property names */\n singleQuote?: boolean;\n}\n\n/**\n * Generate a TypeScript literal string from XSD content.\n *\n * @param xsdContent - XSD file content as string\n * @param options - Generation options\n * @returns TypeScript code with schema as const literal\n *\n * @example\n * ```typescript\n * const xsd = `<xs:schema>...</xs:schema>`;\n * const code = generateSchemaLiteral(xsd, { name: 'PersonSchema' });\n * // export const PersonSchema = { ... } as const;\n * ```\n */\nexport function generateSchemaLiteral(\n xsdContent: string,\n options: GenerateOptions = {},\n): string {\n const schema = parseXsd(xsdContent);\n const features = options.features ?? {};\n const exclude = new Set(options.exclude ?? []);\n\n // Transform schema\n let outputSchema: Record<string, unknown> = { ...schema };\n\n // Add $filename when enabled (uses name option)\n if (features.$filename && options.name) {\n outputSchema = { $filename: `${options.name}.xsd`, ...outputSchema };\n }\n\n // Apply transformations (features + exclude)\n outputSchema = applyTransforms(outputSchema, features, exclude);\n\n return schemaToLiteral(outputSchema as Schema, options);\n}\n\n/**\n * Apply transformations: features ($xmlns, $imports) and exclude filter.\n * Used by generateSchemaLiteral (without imports).\n */\nfunction applyTransforms(\n schema: Record<string, unknown>,\n features: NonNullable<GenerateOptions['features']>,\n exclude: Set<string>,\n): Record<string, unknown> {\n return applyTransformsWithImports(schema, features, exclude, []);\n}\n\n/**\n * Special marker for schema references (not serialized as strings).\n */\nclass SchemaRef {\n constructor(public readonly name: string) {}\n}\n\n/**\n * Apply transformations with schema imports as references.\n */\nfunction applyTransformsWithImports(\n schema: Record<string, unknown>,\n features: NonNullable<GenerateOptions['features']>,\n exclude: Set<string>,\n importedSchemaNames: string[],\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(schema)) {\n // Skip excluded properties\n if (exclude.has(key)) {\n continue;\n }\n\n if (key === '$xmlns') {\n // $xmlns is already parsed - keep only if feature enabled\n if (features.$xmlns) {\n result[key] = value;\n }\n } else if (key === 'import') {\n // Convert import to $imports with schema references\n if (features.$imports && importedSchemaNames.length > 0) {\n result['$imports'] = importedSchemaNames.map(\n (name) => new SchemaRef(name),\n );\n }\n // If no imports resolved, skip the import property entirely\n } else {\n // Recursively filter nested objects/arrays for exclude\n result[key] = filterDeep(value, exclude);\n }\n }\n\n return result;\n}\n\n/**\n * Recursively filter excluded properties from nested structures.\n */\nfunction filterDeep(value: unknown, exclude: Set<string>): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => filterDeep(item, exclude));\n }\n\n if (value !== null && typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n if (!exclude.has(k)) {\n result[k] = filterDeep(v, exclude);\n }\n }\n return result;\n }\n\n return value;\n}\n\n/**\n * Generate a complete TypeScript file from XSD content.\n *\n * @param xsdContent - XSD file content as string\n * @param options - Generation options\n * @returns Complete TypeScript file content\n */\nexport function generateSchemaFile(\n xsdContent: string,\n options: GenerateOptions = {},\n): string {\n const {\n name = 'schema',\n comment,\n features = {},\n importResolver,\n isolatedDeclarations = false,\n } = options;\n\n // Parse to extract imports\n const schema = parseXsd(xsdContent);\n const xsdImports = (schema.import ?? []) as Array<{\n schemaLocation?: string;\n namespace?: string;\n }>;\n\n // Generate TypeScript import statements if $imports feature enabled\n const tsImports: string[] = [];\n const importedSchemaNames: string[] = [];\n\n // Add Schema type import for isolatedDeclarations mode\n if (isolatedDeclarations) {\n tsImports.push(\"import type { Schema } from '@abapify/ts-xsd';\");\n }\n\n if (features.$imports && importResolver) {\n for (const imp of xsdImports) {\n if (imp.schemaLocation) {\n const modulePath = importResolver(imp.schemaLocation);\n if (modulePath) {\n // Derive schema name from schemaLocation basename (e.g., \"../sap/adtcore.xsd\" → \"adtcore\")\n const schemaName = imp.schemaLocation\n .replace(/\\.xsd$/, '')\n .replace(/^.*\\//, '');\n tsImports.push(`import ${schemaName} from '${modulePath}';`);\n importedSchemaNames.push(schemaName);\n }\n }\n }\n }\n\n const lines: string[] = [\n '/**',\n ' * Auto-generated schema literal from XSD',\n ' * ',\n ' * DO NOT EDIT - Generated by ts-xsd codegen',\n comment ? ` * ${comment}` : null,\n ' */',\n '',\n // Add TypeScript imports\n ...tsImports,\n tsImports.length > 0 ? '' : null,\n // Generate schema with $imports as array of schema references\n generateSchemaLiteralWithImports(xsdContent, options, importedSchemaNames),\n '',\n `export type ${pascalCase(name)}Type = typeof ${name};`,\n '',\n ].filter((line): line is string => line !== null);\n\n return lines.join('\\n');\n}\n\n/**\n * Generate schema literal with $imports as schema references.\n */\nfunction generateSchemaLiteralWithImports(\n xsdContent: string,\n options: GenerateOptions,\n importedSchemaNames: string[],\n): string {\n const schema = parseXsd(xsdContent);\n const features = options.features ?? {};\n const exclude = new Set(options.exclude ?? []);\n\n // Transform schema\n let outputSchema: Record<string, unknown> = { ...schema };\n\n // Add $filename when enabled (uses name option)\n if (features.$filename && options.name) {\n outputSchema = { $filename: `${options.name}.xsd`, ...outputSchema };\n }\n\n // Apply transformations (features + exclude), passing imported schema names\n outputSchema = applyTransformsWithImports(\n outputSchema,\n features,\n exclude,\n importedSchemaNames,\n );\n\n return schemaToLiteral(outputSchema as Schema, options);\n}\n\n/**\n * Escape reserved words by adding underscore suffix.\n */\nfunction escapeReservedWord(name: string): string {\n return RESERVED_WORDS.has(name) ? `${name}_` : name;\n}\n\n/**\n * Convert a Schema object to a TypeScript literal string.\n */\nfunction schemaToLiteral(\n schema: Schema,\n options: GenerateOptions = {},\n): string {\n const {\n name = 'schema',\n pretty = true,\n indent = ' ',\n isolatedDeclarations = false,\n singleQuote = false,\n } = options;\n\n const safeName = escapeReservedWord(name);\n const literal = objectToLiteral(schema, pretty, indent, 0, singleQuote);\n\n // For isolatedDeclarations compatibility, use `satisfies Schema as const`\n // This makes the type explicit so TypeScript can emit declarations\n const typeAssertion = isolatedDeclarations\n ? 'satisfies Schema as const'\n : 'as const';\n\n return `export const ${safeName} = ${literal} ${typeAssertion};`;\n}\n\n/**\n * Format a string value as a single-quoted literal, unescaping JSON double quotes\n * and escaping any single quotes.\n */\nfunction toSingleQuoteLiteral(jsonStr: string): string {\n const inner = jsonStr\n .slice(1, -1)\n .replaceAll('\\\\\"', '\"')\n .replaceAll(\"'\", \"\\\\'\");\n return `'${inner}'`;\n}\n\n/**\n * Convert any value to a TypeScript literal string.\n */\nfunction objectToLiteral(\n value: unknown,\n pretty: boolean,\n indent: string,\n depth: number,\n singleQuote = false,\n): string {\n if (value === null || value === undefined) {\n return 'undefined';\n }\n\n // Handle SchemaRef - output as bare identifier (not quoted)\n if (value instanceof SchemaRef) {\n return value.name;\n }\n\n if (typeof value === 'string') {\n const json = JSON.stringify(value);\n if (singleQuote) {\n return toSingleQuoteLiteral(json);\n }\n return json;\n }\n\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return '[]';\n }\n\n const items = value.map((item) =>\n objectToLiteral(item, pretty, indent, depth + 1, singleQuote),\n );\n\n if (pretty) {\n const itemIndent = indent.repeat(depth + 1);\n const closeIndent = indent.repeat(depth);\n return `[\\n${items\n .map((item) => `${itemIndent}${item}`)\n .join(',\\n')},\\n${closeIndent}]`;\n }\n\n return `[${items.join(', ')}]`;\n }\n\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n const entries = Object.entries(obj).filter(([, v]) => v !== undefined);\n\n if (entries.length === 0) {\n return '{}';\n }\n\n const props = entries.map(([key, val]) => {\n let keyStr = key;\n if (!isValidIdentifier(key)) {\n const json = JSON.stringify(key);\n keyStr = singleQuote ? toSingleQuoteLiteral(json) : json;\n }\n const valStr = objectToLiteral(\n val,\n pretty,\n indent,\n depth + 1,\n singleQuote,\n );\n return `${keyStr}: ${valStr}`;\n });\n\n if (pretty) {\n const propIndent = indent.repeat(depth + 1);\n const closeIndent = indent.repeat(depth);\n return `{\\n${props\n .map((prop) => `${propIndent}${prop}`)\n .join(',\\n')},\\n${closeIndent}}`;\n }\n\n return `{ ${props.join(', ')} }`;\n }\n\n return 'undefined';\n}\n\n/**\n * JavaScript reserved words that cannot be used as variable names.\n */\nconst RESERVED_WORDS = new Set([\n 'break',\n 'case',\n 'catch',\n 'continue',\n 'debugger',\n 'default',\n 'delete',\n 'do',\n 'else',\n 'finally',\n 'for',\n 'function',\n 'if',\n 'in',\n 'instanceof',\n 'new',\n 'return',\n 'switch',\n 'this',\n 'throw',\n 'try',\n 'typeof',\n 'var',\n 'void',\n 'while',\n 'with',\n 'class',\n 'const',\n 'enum',\n 'export',\n 'extends',\n 'import',\n 'super',\n 'implements',\n 'interface',\n 'let',\n 'package',\n 'private',\n 'protected',\n 'public',\n 'static',\n 'yield',\n 'await',\n 'null',\n 'true',\n 'false',\n]);\n\n/**\n * Check if a string is a valid JavaScript identifier.\n */\nfunction isValidIdentifier(str: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str) && !RESERVED_WORDS.has(str);\n}\n\n/**\n * Convert string to PascalCase.\n */\nfunction pascalCase(str: string): string {\n return str\n .replaceAll(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\n .replace(/^./, (s) => s.toUpperCase());\n}\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
- import { _ as TransformContext, c as CodegenConfig, d as GeneratorPlugin, f as HookContext, g as SourceInfo, h as SourceConfig, i as ResolveOptions, l as FinalizeContext, m as SetupContext, n as RunnerResult, p as SchemaInfo, r as runCodegen, s as AfterAllContext, t as RunnerOptions, u as GeneratedFile, v as defineConfig } from "../runner-C4_p2y5e.mjs";
2
-
1
+ import { _ as TransformContext, c as CodegenConfig, d as GeneratorPlugin, f as HookContext, g as SourceInfo, h as SourceConfig, i as ResolveOptions, l as FinalizeContext, m as SetupContext, n as RunnerResult, p as SchemaInfo, r as runCodegen, s as AfterAllContext, t as RunnerOptions, u as GeneratedFile, v as defineConfig } from "../runner-D1RCU_go.mjs";
3
2
  //#region src/generators/raw-schema.d.ts
4
3
  interface RawSchemaOptions {
5
4
  /** Use default export (default: true) */