@abapify/ts-xsd 0.0.0 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +405 -2
  2. package/package.json +36 -3
package/README.md CHANGED
@@ -1,3 +1,406 @@
1
- # @abapify/ts-xsd
1
+ # ts-xsd
2
2
 
3
- Placeholder. The real release is published via CI/CD.
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)
4
+
5
+ **Core XSD parser, builder, and type inference** with **1:1 TypeScript representation** of W3C XML Schema Definition (XSD) 1.1.
6
+
7
+ ## Overview
8
+
9
+ `ts-xsd` is a comprehensive TypeScript library for working with W3C XSD schemas. It provides:
10
+
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
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ npm install @abapify/ts-xsd
31
+ # or
32
+ bun add @abapify/ts-xsd
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ### Parse and Build XSD
38
+
39
+ ```typescript
40
+ import { parseXsd, buildXsd } from '@abapify/ts-xsd';
41
+
42
+ // Parse XSD to typed Schema object
43
+ const schema = parseXsd(`
44
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
45
+ <xs:element name="person" type="PersonType"/>
46
+ <xs:complexType name="PersonType">
47
+ <xs:sequence>
48
+ <xs:element name="name" type="xs:string"/>
49
+ <xs:element name="age" type="xs:int" minOccurs="0"/>
50
+ </xs:sequence>
51
+ </xs:complexType>
52
+ </xs:schema>
53
+ `);
54
+
55
+ // Build back to XSD
56
+ const xsd = buildXsd(schema, { pretty: true });
57
+ ```
58
+
59
+ ### Type Inference from Schema Literals
60
+
61
+ ```typescript
62
+ import type { InferSchema } from '@abapify/ts-xsd';
63
+
64
+ // Define schema as const literal
65
+ const personSchema = {
66
+ element: [{ name: 'person', type: 'PersonType' }],
67
+ complexType: [
68
+ {
69
+ name: 'PersonType',
70
+ sequence: {
71
+ element: [
72
+ { name: 'name', type: 'xs:string' },
73
+ { name: 'age', type: 'xs:int', minOccurs: 0 },
74
+ ],
75
+ },
76
+ },
77
+ ],
78
+ } as const;
79
+
80
+ // Infer TypeScript type at compile time
81
+ type Person = InferSchema<typeof personSchema>;
82
+ // Result: { name: string; age?: number }
83
+ ```
84
+
85
+ ### Parse XML with Schema
86
+
87
+ ```typescript
88
+ import { parseXml, buildXml } from '@abapify/ts-xsd';
89
+
90
+ const xml = `<person><name>John</name><age>30</age></person>`;
91
+ const data = parseXml(personSchema, xml);
92
+ // data: { name: 'John', age: 30 }
93
+
94
+ const rebuilt = buildXml(personSchema, data);
95
+ // rebuilt: <person><name>John</name><age>30</age></person>
96
+ ```
97
+
98
+ ## API Reference
99
+
100
+ ### XSD Module
101
+
102
+ ```typescript
103
+ import { parseXsd, buildXsd, type Schema } from '@abapify/ts-xsd';
104
+ ```
105
+
106
+ #### `parseXsd(xsd: string): Schema`
107
+
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
+ ```
115
+
116
+ #### `buildXsd(schema: Schema, options?: BuildOptions): string`
117
+
118
+ Build an XSD XML string from a Schema object.
119
+
120
+ ```typescript
121
+ const xsd = buildXsd(schema, {
122
+ prefix: 'xsd', // Namespace prefix (default: 'xs')
123
+ pretty: true, // Pretty print (default: true)
124
+ indent: ' ', // Indentation (default: ' ')
125
+ });
126
+ ```
127
+
128
+ #### `resolveImports(schema: Schema, resolver: (location: string) => Schema): Schema`
129
+
130
+ Resolve and link imported schemas for cross-schema type resolution.
131
+
132
+ ```typescript
133
+ const linkedSchema = resolveImports(schema, (location) => {
134
+ return parseXsd(fs.readFileSync(location, 'utf-8'));
135
+ });
136
+ ```
137
+
138
+ ### Infer Module
139
+
140
+ ```typescript
141
+ import type { InferSchema, InferElement, SchemaLike } from '@abapify/ts-xsd';
142
+ ```
143
+
144
+ #### `InferSchema<T>`
145
+
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
+ ```
151
+
152
+ #### `InferElement<T, ElementName>`
153
+
154
+ Infer type for a specific element by name.
155
+
156
+ ```typescript
157
+ type Person = InferElement<typeof schema, 'person'>;
158
+ ```
159
+
160
+ #### Built-in Type Mapping
161
+
162
+ | XSD Type | TypeScript |
163
+ | ------------------------------------ | ---------- |
164
+ | `xs:string`, `xs:token`, `xs:NCName` | `string` |
165
+ | `xs:int`, `xs:integer`, `xs:decimal` | `number` |
166
+ | `xs:boolean` | `boolean` |
167
+ | `xs:date`, `xs:dateTime`, `xs:time` | `string` |
168
+ | `xs:anyURI`, `xs:QName` | `string` |
169
+ | `xs:anyType` | `unknown` |
170
+
171
+ ### XML Module
172
+
173
+ ```typescript
174
+ import { parseXml, buildXml } from '@abapify/ts-xsd';
175
+ ```
176
+
177
+ #### `parseXml<T>(schema: SchemaLike, xml: string): T`
178
+
179
+ Parse XML string using schema definition.
180
+
181
+ #### `buildXml<T>(schema: SchemaLike, data: T): string`
182
+
183
+ Build XML string from data using schema definition.
184
+
185
+ ### Codegen Module
186
+
187
+ ```typescript
188
+ import { generateSchemaLiteral, generateInterfaces } from '@abapify/ts-xsd';
189
+ ```
190
+
191
+ #### `generateSchemaLiteral(xsd: string, options?: GenerateOptions): string`
192
+
193
+ Generate TypeScript schema literal from XSD content.
194
+
195
+ ```typescript
196
+ const code = generateSchemaLiteral(xsdContent, {
197
+ name: 'PersonSchema',
198
+ features: { $xmlns: true, $imports: true },
199
+ exclude: ['annotation'],
200
+ });
201
+ // export default { ... } as const;
202
+ ```
203
+
204
+ #### `generateInterfaces(schema: Schema, options?: GenerateInterfacesOptions): GenerateInterfacesResult`
205
+
206
+ Generate TypeScript interfaces from parsed schema. Returns an object with `code` property containing the generated TypeScript code.
207
+
208
+ ```typescript
209
+ const { code } = generateInterfaces(schema, {
210
+ flatten: true, // Inline all nested types (default: false)
211
+ addJsDoc: true, // Add JSDoc comments
212
+ rootTypeName: 'MySchema', // Custom root type name
213
+ });
214
+ ```
215
+
216
+ ## Schema Structure
217
+
218
+ The `Schema` type is a 1:1 TypeScript representation of W3C XSD:
219
+
220
+ ```typescript
221
+ interface Schema {
222
+ // Namespace
223
+ targetNamespace?: string;
224
+ elementFormDefault?: 'qualified' | 'unqualified';
225
+ attributeFormDefault?: 'qualified' | 'unqualified';
226
+
227
+ // Composition
228
+ import?: Import[];
229
+ include?: Include[];
230
+
231
+ // Declarations
232
+ element?: TopLevelElement[];
233
+ complexType?: TopLevelComplexType[];
234
+ simpleType?: TopLevelSimpleType[];
235
+ group?: NamedGroup[];
236
+ attributeGroup?: NamedAttributeGroup[];
237
+
238
+ // Extensions (non-W3C, prefixed with $)
239
+ $xmlns?: { [prefix: string]: string };
240
+ $imports?: Schema[]; // Resolved imported schemas
241
+ $filename?: string; // Source filename
242
+ }
243
+ ```
244
+
245
+ ### Cross-Schema Type Resolution
246
+
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
277
+
278
+ ```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
+ ```
317
+
318
+ ## Architecture
319
+
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
+
364
+ ## Design Principles
365
+
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
398
+
399
+ ## References
400
+
401
+ - [W3C XML Schema 1.1 Part 1: Structures](https://www.w3.org/TR/xmlschema11-1/)
402
+ - [XMLSchema.xsd](https://www.w3.org/TR/xmlschema11-1/XMLSchema.xsd)
403
+
404
+ ## License
405
+
406
+ MIT
package/package.json CHANGED
@@ -1,5 +1,38 @@
1
1
  {
2
2
  "name": "@abapify/ts-xsd",
3
- "version": "0.0.0",
4
- "description": "Placeholder — real package is published via CI/CD (trusted publishing)."
5
- }
3
+ "version": "0.3.5",
4
+ "description": "Core XSD parser and builder - W3C XMLSchema 1:1 TypeScript representation",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "type": "module",
10
+ "types": "./dist/index.d.mts",
11
+ "bin": {
12
+ "ts-xsd": "./dist/codegen/cli.mjs"
13
+ },
14
+ "exports": {
15
+ ".": "./dist/index.mjs",
16
+ "./codegen/cli": "./dist/codegen/cli.mjs",
17
+ "./generators": "./dist/generators/index.mjs",
18
+ "./package.json": "./package.json"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md"
23
+ ],
24
+ "dependencies": {
25
+ "@xmldom/xmldom": "^0.9.9",
26
+ "ts-morph": "*",
27
+ "zod": "^3.24.0 || ^4.0.0"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/abapify/adt-cli.git",
32
+ "directory": "packages/ts-xsd"
33
+ },
34
+ "homepage": "https://github.com/abapify/adt-cli/tree/main/packages/ts-xsd#readme",
35
+ "bugs": {
36
+ "url": "https://github.com/abapify/adt-cli/issues"
37
+ }
38
+ }