@abapify/ts-xsd 0.4.34 → 0.4.36
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 +17 -320
- package/dist/codegen/cli.mjs +6 -4
- package/dist/codegen/cli.mjs.map +1 -1
- package/dist/{generate-DQJ38rjv.mjs → generate-BWLnwHUW.mjs} +38 -33
- package/dist/generate-BWLnwHUW.mjs.map +1 -0
- package/dist/generators/index.d.mts +1 -1
- package/dist/generators/index.mjs +130 -116
- package/dist/generators/index.mjs.map +1 -1
- package/dist/index.d.mts +6 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +495 -368
- package/dist/index.mjs.map +1 -1
- package/dist/{runner-BGNA8Sl-.mjs → runner-BZNOc_YZ.mjs} +439 -334
- package/dist/runner-BZNOc_YZ.mjs.map +1 -0
- package/dist/{runner-CDEKB5AE.d.mts → runner-x86T9IvM.d.mts} +5 -5
- package/dist/{runner-CDEKB5AE.d.mts.map → runner-x86T9IvM.d.mts.map} +1 -1
- package/dist/{types-CySWjUcM.mjs → types-BAx8yQfm.mjs} +151 -115
- package/dist/types-BAx8yQfm.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/generate-DQJ38rjv.mjs.map +0 -1
- package/dist/runner-BGNA8Sl-.mjs.map +0 -1
- package/dist/types-CySWjUcM.mjs.map +0 -1
|
@@ -40,16 +40,8 @@ function schemaToSourceFile(schema, options = {}) {
|
|
|
40
40
|
basePath: options.basePath,
|
|
41
41
|
importedTypes: /* @__PURE__ */ new Map()
|
|
42
42
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const resolvedSchema = options.schemaLocationResolver(imp.schemaLocation, options.basePath);
|
|
46
|
-
if (resolvedSchema) trackImportedTypes(resolvedSchema, ctx);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (schema.$imports) for (const importedSchema of schema.$imports) trackImportedTypes(importedSchema, ctx);
|
|
50
|
-
for (const ct of schema.complexType ?? []) if (ct.name) generateInterface(ct.name, ct, ctx);
|
|
51
|
-
for (const el of schema.element ?? []) if (el.name && el.complexType) generateInterface(toInterfaceName(el.name), el.complexType, ctx);
|
|
52
|
-
for (const st of schema.simpleType ?? []) if (st.name) generateSimpleType(st.name, st, ctx);
|
|
43
|
+
trackSchemaImports(schema, options, ctx);
|
|
44
|
+
generateSchemaTypes(schema, ctx);
|
|
53
45
|
if (rootTypeName) generateRootType(rootTypeName, ctx);
|
|
54
46
|
addImportStatements(ctx);
|
|
55
47
|
sourceFile.formatText();
|
|
@@ -59,6 +51,23 @@ function schemaToSourceFile(schema, options = {}) {
|
|
|
59
51
|
rootTypeName: rootTypeName ?? void 0
|
|
60
52
|
};
|
|
61
53
|
}
|
|
54
|
+
/** Track types referenced from schemaLocation imports and linked $imports. */
|
|
55
|
+
function trackSchemaImports(schema, options, ctx) {
|
|
56
|
+
if (options.schemaLocationResolver && schema.import) for (const imp of schema.import) trackSchemaLocationImport(imp, options.schemaLocationResolver, options.basePath, ctx);
|
|
57
|
+
if (schema.$imports) for (const importedSchema of schema.$imports) trackImportedTypes(importedSchema, ctx);
|
|
58
|
+
}
|
|
59
|
+
/** Resolve one schemaLocation import and track its types. */
|
|
60
|
+
function trackSchemaLocationImport(imp, resolver, basePath, ctx) {
|
|
61
|
+
if (!imp.schemaLocation) return;
|
|
62
|
+
const resolvedSchema = resolver(imp.schemaLocation, basePath);
|
|
63
|
+
if (resolvedSchema) trackImportedTypes(resolvedSchema, ctx);
|
|
64
|
+
}
|
|
65
|
+
/** Generate interfaces and type aliases for all schema declarations. */
|
|
66
|
+
function generateSchemaTypes(schema, ctx) {
|
|
67
|
+
for (const ct of schema.complexType ?? []) if (ct.name) generateInterface(ct.name, ct, ctx);
|
|
68
|
+
for (const el of schema.element ?? []) if (el.name && el.complexType) generateInterface(toInterfaceName(el.name), el.complexType, ctx);
|
|
69
|
+
for (const st of schema.simpleType ?? []) if (st.name) generateSimpleType(st.name, st, ctx);
|
|
70
|
+
}
|
|
62
71
|
/**
|
|
63
72
|
* Flatten a type from a SourceFile into a new file with all nested types inlined.
|
|
64
73
|
*
|
|
@@ -134,57 +143,74 @@ function expandTypeToString(type, checker, context, indent = "", visited = /* @_
|
|
|
134
143
|
if (elementType) return `${expandTypeToString(elementType, checker, context, indent, visited)}[]`;
|
|
135
144
|
return "unknown[]";
|
|
136
145
|
}
|
|
137
|
-
if (type.isUnion())
|
|
138
|
-
const unionTypes = type.getUnionTypes();
|
|
139
|
-
if (unionTypes.every((t) => isPrimitiveType(t))) {
|
|
140
|
-
const textRepr = type.getText();
|
|
141
|
-
if (textRepr.includes("import(")) return unionTypes.map((t) => t.getText()).join(" | ");
|
|
142
|
-
return textRepr;
|
|
143
|
-
}
|
|
144
|
-
return unionTypes.map((t) => expandTypeToString(t, checker, context, indent, visited)).join(" | ");
|
|
145
|
-
}
|
|
146
|
+
if (type.isUnion()) return expandUnionType(type, checker, context, indent, visited);
|
|
146
147
|
if (type.isIntersection()) return type.getIntersectionTypes().map((t) => expandTypeToString(t, checker, context, indent, visited)).join(" & ");
|
|
147
148
|
const props = type.getProperties();
|
|
148
|
-
if (props.length > 0)
|
|
149
|
-
const typeId = (type.getSymbol() ?? type.getAliasSymbol())?.getName() ?? type.getText();
|
|
150
|
-
if (visited.has(typeId)) return "unknown";
|
|
151
|
-
const newVisited = new Set(visited);
|
|
152
|
-
newVisited.add(typeId);
|
|
153
|
-
return expandObjectLiteral(type, props, checker, context, indent, newVisited);
|
|
154
|
-
}
|
|
149
|
+
if (props.length > 0) return expandPropsObject(type, props, checker, context, indent, visited);
|
|
155
150
|
const indexLine = expandIndexSignature(type, checker, context, indent, visited);
|
|
156
151
|
if (indexLine) return `{\n${indexLine}\n${indent}}`;
|
|
157
152
|
const textRepr = type.getText();
|
|
158
|
-
if (textRepr.includes("import("))
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
153
|
+
if (textRepr.includes("import(")) return expandImportType(type, textRepr, checker, context, indent, visited);
|
|
154
|
+
return textRepr;
|
|
155
|
+
}
|
|
156
|
+
/** Expand a union type to a `A | B | ...` string. */
|
|
157
|
+
function expandUnionType(type, checker, context, indent, visited) {
|
|
158
|
+
const unionTypes = type.getUnionTypes();
|
|
159
|
+
if (unionTypes.every((t) => isPrimitiveType(t))) {
|
|
160
|
+
const textRepr = type.getText();
|
|
161
|
+
if (textRepr.includes("import(")) return unionTypes.map((t) => t.getText()).join(" | ");
|
|
162
|
+
return textRepr;
|
|
163
|
+
}
|
|
164
|
+
return unionTypes.map((t) => expandTypeToString(t, checker, context, indent, visited)).join(" | ");
|
|
165
|
+
}
|
|
166
|
+
/** Expand a type's properties into an object literal with cycle detection. */
|
|
167
|
+
function expandPropsObject(type, props, checker, context, indent, visited) {
|
|
168
|
+
const typeId = (type.getSymbol() ?? type.getAliasSymbol())?.getName() ?? type.getText();
|
|
169
|
+
if (visited.has(typeId)) return "unknown";
|
|
170
|
+
const newVisited = new Set(visited);
|
|
171
|
+
newVisited.add(typeId);
|
|
172
|
+
return expandObjectLiteral(type, props, checker, context, indent, newVisited);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Try to expand a type whose text form is an import() reference:
|
|
176
|
+
* look up the named type in the project, else expand base types,
|
|
177
|
+
* else return unknown to avoid import() in output.
|
|
178
|
+
*/
|
|
179
|
+
function expandImportType(type, textRepr, checker, context, indent, visited) {
|
|
180
|
+
const match = /import\([^)]+\)\.(\w+)/.exec(textRepr);
|
|
181
|
+
if (match) {
|
|
182
|
+
const expanded = expandNamedTypeReference(match[1], checker, context, indent, visited);
|
|
183
|
+
if (expanded) return expanded;
|
|
184
|
+
}
|
|
185
|
+
const baseTypes = type.getBaseTypes();
|
|
186
|
+
if (baseTypes.length > 0) {
|
|
187
|
+
const newVisited = new Set(visited);
|
|
188
|
+
newVisited.add(textRepr);
|
|
189
|
+
return expandTypeToString(baseTypes[0], checker, context, indent, newVisited);
|
|
190
|
+
}
|
|
191
|
+
return "unknown";
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Look up a named type reference in all source files of the project and
|
|
195
|
+
* expand it, or return undefined when it cannot be found.
|
|
196
|
+
*/
|
|
197
|
+
function expandNamedTypeReference(typeName, checker, context, indent, visited) {
|
|
198
|
+
const project = context.getSourceFile().getProject();
|
|
199
|
+
for (const sf of project.getSourceFiles()) {
|
|
200
|
+
const typeAlias = sf.getTypeAlias(typeName);
|
|
201
|
+
if (typeAlias) {
|
|
202
|
+
const typeNode = typeAlias.getTypeNode();
|
|
203
|
+
if (typeNode) return typeNode.getText();
|
|
178
204
|
}
|
|
179
|
-
const
|
|
180
|
-
if (
|
|
205
|
+
const iface = sf.getInterface(typeName);
|
|
206
|
+
if (iface) {
|
|
207
|
+
if (visited.has(typeName)) return "unknown";
|
|
208
|
+
const ifaceType = iface.getType();
|
|
181
209
|
const newVisited = new Set(visited);
|
|
182
|
-
newVisited.add(
|
|
183
|
-
return expandTypeToString(
|
|
210
|
+
newVisited.add(typeName);
|
|
211
|
+
return expandTypeToString(ifaceType, checker, context, indent, newVisited);
|
|
184
212
|
}
|
|
185
|
-
return "unknown";
|
|
186
213
|
}
|
|
187
|
-
return textRepr;
|
|
188
214
|
}
|
|
189
215
|
const XSD_BUILT_IN_TYPES = {
|
|
190
216
|
string: "string",
|
|
@@ -285,53 +311,9 @@ function generateInterface(name, ct, ctx) {
|
|
|
285
311
|
const properties = [];
|
|
286
312
|
const extendsTypes = [];
|
|
287
313
|
let hasAny = false;
|
|
288
|
-
if (ct.complexContent?.extension?.base)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (ctx.importedTypes.has(baseName)) {
|
|
292
|
-
ctx.generatedTypes.add(`import:${baseInterfaceName}`);
|
|
293
|
-
extendsTypes.push(baseInterfaceName);
|
|
294
|
-
} else {
|
|
295
|
-
const baseCt = ctx.schema.complexType?.find((c) => c.name === baseName);
|
|
296
|
-
if (baseCt) {
|
|
297
|
-
generateInterface(baseName, baseCt, ctx);
|
|
298
|
-
extendsTypes.push(baseInterfaceName);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
const ext = ct.complexContent.extension;
|
|
302
|
-
if (ext.sequence) hasAny = collectFromGroup(ext.sequence, properties, ctx, false) || hasAny;
|
|
303
|
-
if (ext.choice) hasAny = collectFromGroup(ext.choice, properties, ctx, true) || hasAny;
|
|
304
|
-
if (ext.all) hasAny = collectFromGroup(ext.all, properties, ctx, false) || hasAny;
|
|
305
|
-
collectAttributes(ext.attribute, properties, ctx);
|
|
306
|
-
} else if (ct.simpleContent?.extension) {
|
|
307
|
-
const ext = ct.simpleContent.extension;
|
|
308
|
-
const baseType = ext.base ? XSD_BUILT_IN_TYPES[stripNsPrefix(ext.base)] ?? "string" : "string";
|
|
309
|
-
properties.push({
|
|
310
|
-
name: "$value",
|
|
311
|
-
type: baseType,
|
|
312
|
-
hasQuestionToken: true
|
|
313
|
-
});
|
|
314
|
-
collectAttributes(ext.attribute, properties, ctx);
|
|
315
|
-
} else {
|
|
316
|
-
if (ct.sequence) hasAny = collectFromGroup(ct.sequence, properties, ctx, false) || hasAny;
|
|
317
|
-
if (ct.choice) hasAny = collectFromGroup(ct.choice, properties, ctx, true) || hasAny;
|
|
318
|
-
if (ct.all) hasAny = collectFromGroup(ct.all, properties, ctx, false) || hasAny;
|
|
319
|
-
collectAttributes(ct.attribute, properties, ctx);
|
|
320
|
-
if (ct.mixed) properties.push({
|
|
321
|
-
name: "_text",
|
|
322
|
-
type: "string",
|
|
323
|
-
hasQuestionToken: true
|
|
324
|
-
});
|
|
325
|
-
if (ct.group?.ref) {
|
|
326
|
-
const groupName = stripNsPrefix(ct.group.ref);
|
|
327
|
-
const group = ctx.schema.group?.find((g) => g.name === groupName);
|
|
328
|
-
if (group) {
|
|
329
|
-
if (group.sequence) hasAny = collectFromGroup(group.sequence, properties, ctx, false) || hasAny;
|
|
330
|
-
if (group.choice) hasAny = collectFromGroup(group.choice, properties, ctx, true) || hasAny;
|
|
331
|
-
if (group.all) hasAny = collectFromGroup(group.all, properties, ctx, false) || hasAny;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
314
|
+
if (ct.complexContent?.extension?.base) hasAny = collectExtensionContent(ct.complexContent.extension, properties, extendsTypes, ctx);
|
|
315
|
+
else if (ct.simpleContent?.extension) collectSimpleContent(ct.simpleContent.extension, properties, ctx);
|
|
316
|
+
else hasAny = collectDirectContent(ct, properties, ctx);
|
|
335
317
|
const interfaceDecl = ctx.sourceFile.addInterface({
|
|
336
318
|
name: interfaceName,
|
|
337
319
|
isExported: true,
|
|
@@ -349,29 +331,83 @@ function generateInterface(name, ct, ctx) {
|
|
|
349
331
|
returnType: "unknown"
|
|
350
332
|
});
|
|
351
333
|
}
|
|
334
|
+
/** Collect properties from a complexContent extension (inheritance). */
|
|
335
|
+
function collectExtensionContent(ext, properties, extendsTypes, ctx) {
|
|
336
|
+
const baseName = stripNsPrefix(ext.base ?? "");
|
|
337
|
+
const baseInterfaceName = toInterfaceName(baseName);
|
|
338
|
+
if (ctx.importedTypes.has(baseName)) {
|
|
339
|
+
ctx.generatedTypes.add(`import:${baseInterfaceName}`);
|
|
340
|
+
extendsTypes.push(baseInterfaceName);
|
|
341
|
+
} else {
|
|
342
|
+
const baseCt = ctx.schema.complexType?.find((c) => c.name === baseName);
|
|
343
|
+
if (baseCt) {
|
|
344
|
+
generateInterface(baseName, baseCt, ctx);
|
|
345
|
+
extendsTypes.push(baseInterfaceName);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
let hasAny = false;
|
|
349
|
+
if (ext.sequence) hasAny = collectFromGroup(ext.sequence, properties, ctx, false) || hasAny;
|
|
350
|
+
if (ext.choice) hasAny = collectFromGroup(ext.choice, properties, ctx, true) || hasAny;
|
|
351
|
+
if (ext.all) hasAny = collectFromGroup(ext.all, properties, ctx, false) || hasAny;
|
|
352
|
+
collectAttributes(ext.attribute, properties, ctx);
|
|
353
|
+
return hasAny;
|
|
354
|
+
}
|
|
355
|
+
/** Collect properties from a simpleContent extension ($value + attributes). */
|
|
356
|
+
function collectSimpleContent(ext, properties, ctx) {
|
|
357
|
+
const baseType = ext.base ? XSD_BUILT_IN_TYPES[stripNsPrefix(ext.base)] ?? "string" : "string";
|
|
358
|
+
properties.push({
|
|
359
|
+
name: "$value",
|
|
360
|
+
type: baseType,
|
|
361
|
+
hasQuestionToken: true
|
|
362
|
+
});
|
|
363
|
+
collectAttributes(ext.attribute, properties, ctx);
|
|
364
|
+
}
|
|
365
|
+
/** Collect properties from direct content (no complexContent/simpleContent). */
|
|
366
|
+
function collectDirectContent(ct, properties, ctx) {
|
|
367
|
+
let hasAny = false;
|
|
368
|
+
if (ct.sequence) hasAny = collectFromGroup(ct.sequence, properties, ctx, false) || hasAny;
|
|
369
|
+
if (ct.choice) hasAny = collectFromGroup(ct.choice, properties, ctx, true) || hasAny;
|
|
370
|
+
if (ct.all) hasAny = collectFromGroup(ct.all, properties, ctx, false) || hasAny;
|
|
371
|
+
collectAttributes(ct.attribute, properties, ctx);
|
|
372
|
+
if (ct.mixed) properties.push({
|
|
373
|
+
name: "_text",
|
|
374
|
+
type: "string",
|
|
375
|
+
hasQuestionToken: true
|
|
376
|
+
});
|
|
377
|
+
if (ct.group?.ref) hasAny = collectNamedGroupRef(ct.group.ref, properties, ctx) || hasAny;
|
|
378
|
+
return hasAny;
|
|
379
|
+
}
|
|
380
|
+
/** Collect properties from a referenced named group (xs:group ref="..."). */
|
|
381
|
+
function collectNamedGroupRef(ref, properties, ctx) {
|
|
382
|
+
const groupName = stripNsPrefix(ref);
|
|
383
|
+
const group = ctx.schema.group?.find((g) => g.name === groupName);
|
|
384
|
+
if (!group) return false;
|
|
385
|
+
let hasAny = false;
|
|
386
|
+
if (group.sequence) hasAny = collectFromGroup(group.sequence, properties, ctx, false) || hasAny;
|
|
387
|
+
if (group.choice) hasAny = collectFromGroup(group.choice, properties, ctx, true) || hasAny;
|
|
388
|
+
if (group.all) hasAny = collectFromGroup(group.all, properties, ctx, false) || hasAny;
|
|
389
|
+
return hasAny;
|
|
390
|
+
}
|
|
352
391
|
function collectFromGroup(group, properties, ctx, forceOptional) {
|
|
353
392
|
let hasAny = false;
|
|
354
393
|
for (const el of group.element ?? []) addElementProperty(el, properties, ctx, forceOptional);
|
|
355
394
|
for (const seq of group.sequence ?? []) if (collectFromGroup(seq, properties, ctx, forceOptional)) hasAny = true;
|
|
356
395
|
for (const ch of group.choice ?? []) if (collectFromGroup(ch, properties, ctx, true)) hasAny = true;
|
|
357
|
-
for (const g of group.group ?? []) if (g.ref)
|
|
358
|
-
const groupName = stripNsPrefix(g.ref);
|
|
359
|
-
const namedGroup = ctx.schema.group?.find((gr) => gr.name === groupName);
|
|
360
|
-
if (namedGroup) {
|
|
361
|
-
if (namedGroup.sequence) {
|
|
362
|
-
if (collectFromGroup(namedGroup.sequence, properties, ctx, forceOptional)) hasAny = true;
|
|
363
|
-
}
|
|
364
|
-
if (namedGroup.choice) {
|
|
365
|
-
if (collectFromGroup(namedGroup.choice, properties, ctx, true)) hasAny = true;
|
|
366
|
-
}
|
|
367
|
-
if (namedGroup.all) {
|
|
368
|
-
if (collectFromGroup(namedGroup.all, properties, ctx, forceOptional)) hasAny = true;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
396
|
+
for (const g of group.group ?? []) if (g.ref && collectNamedGroup(g.ref, properties, ctx, forceOptional)) hasAny = true;
|
|
372
397
|
if (group.any && group.any.length > 0) hasAny = true;
|
|
373
398
|
return hasAny;
|
|
374
399
|
}
|
|
400
|
+
/** Collect properties from a named group reference (xs:group ref="..."). */
|
|
401
|
+
function collectNamedGroup(ref, properties, ctx, forceOptional) {
|
|
402
|
+
const groupName = stripNsPrefix(ref);
|
|
403
|
+
const namedGroup = ctx.schema.group?.find((gr) => gr.name === groupName);
|
|
404
|
+
if (!namedGroup) return false;
|
|
405
|
+
let hasAny = false;
|
|
406
|
+
if (namedGroup.sequence) hasAny = collectFromGroup(namedGroup.sequence, properties, ctx, forceOptional) || hasAny;
|
|
407
|
+
if (namedGroup.choice) hasAny = collectFromGroup(namedGroup.choice, properties, ctx, true) || hasAny;
|
|
408
|
+
if (namedGroup.all) hasAny = collectFromGroup(namedGroup.all, properties, ctx, forceOptional) || hasAny;
|
|
409
|
+
return hasAny;
|
|
410
|
+
}
|
|
375
411
|
/**
|
|
376
412
|
* Resolve the TypeScript type string for a local element.
|
|
377
413
|
*/
|
|
@@ -541,4 +577,4 @@ function defineConfig(config) {
|
|
|
541
577
|
}
|
|
542
578
|
|
|
543
579
|
export { deriveRootTypeName as n, generateInterfaces as r, defineConfig as t };
|
|
544
|
-
//# sourceMappingURL=types-
|
|
580
|
+
//# sourceMappingURL=types-BAx8yQfm.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-BAx8yQfm.mjs","names":["deriveRootTypeName"],"sources":["../src/codegen/ts-morph.ts","../src/codegen/interface-generator.ts","../src/codegen/types.ts"],"sourcesContent":["/**\n * Schema to ts-morph SourceFile converter\n *\n * Converts XSD schemas directly to ts-morph SourceFiles with TypeScript interfaces.\n *\n * Features:\n * - schemaToSourceFile: Convert schema to ts-morph SourceFile with interfaces\n * - If schema has schemaLocation imports -> uses loader to load them\n * - If schema has $imports -> generates import statements and extends\n * - flattenType: Flatten a type from a SourceFile into a new file with inline types\n */\n\nimport {\n Project,\n SourceFile,\n Type,\n TypeChecker,\n Node,\n Symbol as TsSymbol,\n} from 'ts-morph';\nimport type {\n Schema,\n TopLevelComplexType,\n TopLevelSimpleType,\n LocalComplexType,\n LocalElement,\n LocalAttribute,\n ExplicitGroup,\n ComplexContentExtension,\n SimpleContentExtension,\n} from '../xsd/types';\n// =============================================================================\n// Public Types\n// =============================================================================\n\n/**\n * Resolver function for loading schemas by schemaLocation.\n * @param schemaLocation - The schemaLocation path from the import element\n * @param basePath - Optional base path for resolving relative paths\n * @returns The resolved Schema, or undefined if not found\n */\nexport type SchemaLocationResolver = (\n schemaLocation: string,\n basePath?: string,\n) => Schema | undefined;\n\nexport interface SchemaToSourceFileOptions {\n /** Custom root type name. Pass null to disable root type generation. */\n rootTypeName?: string | null;\n /** Add JSDoc comments */\n addJsDoc?: boolean;\n /** Existing project to use */\n project?: Project;\n /** Base path for loading schemaLocation imports */\n basePath?: string;\n /**\n * Resolver function for loading schemas by schemaLocation.\n * If provided, will be called for each `import` element with a schemaLocation.\n * The resolved schemas will be processed like $imports.\n */\n schemaLocationResolver?: SchemaLocationResolver;\n}\n\nexport interface SchemaSourceFileResult {\n /** The ts-morph Project */\n project: Project;\n /** The generated SourceFile with all interfaces */\n sourceFile: SourceFile;\n /** The root type name (e.g., \"DiscoverySchema\") */\n rootTypeName: string | undefined;\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction propertyName(name: string): string {\n return /^[\\p{ID_Start}$_][\\p{ID_Continue}$\\u200C\\u200D]*$/u.test(name)\n ? name\n : JSON.stringify(name);\n}\n\nfunction deriveRootTypeName(filename: string | undefined): string | undefined {\n if (!filename) return undefined;\n const baseName = filename.replace(/\\.xsd$/, '').replace(/^.*\\//, '');\n const pascalCase = baseName.charAt(0).toUpperCase() + baseName.slice(1);\n return `${pascalCase}Schema`;\n}\n\n// =============================================================================\n// schemaToSourceFile - Main Entry Point\n// =============================================================================\n\n/**\n * Convert a schema to a ts-morph SourceFile with TypeScript interfaces.\n *\n * - If schema has `import` with schemaLocation -> loads and processes them\n * - If schema has `$imports` (already linked) -> generates import statements with extends\n */\nexport function schemaToSourceFile(\n schema: Schema,\n options: SchemaToSourceFileOptions = {},\n): SchemaSourceFileResult {\n const project =\n options.project ?? new Project({ useInMemoryFileSystem: true });\n // Only derive rootTypeName if explicitly requested or not explicitly disabled\n // Pass rootTypeName: undefined to disable, or omit to auto-derive\n const rootTypeName =\n options.rootTypeName === undefined\n ? deriveRootTypeName(schema.$filename)\n : options.rootTypeName; // null or explicit name\n const filename = `${\n schema.$filename?.replace('.xsd', '') || 'schema'\n }.types.ts`;\n const sourceFile = project.createSourceFile(filename, '', {\n overwrite: true,\n });\n\n const ctx: GeneratorContext = {\n schema,\n sourceFile,\n project,\n generatedTypes: new Set<string>(),\n addJsDoc: options.addJsDoc,\n basePath: options.basePath,\n importedTypes: new Map<string, string>(), // typeName -> importPath\n };\n\n // Track types from schemaLocation imports and linked $imports\n trackSchemaImports(schema, options, ctx);\n\n // Generate interfaces and type aliases for all schema declarations\n generateSchemaTypes(schema, ctx);\n\n // Generate root schema type from elements\n if (rootTypeName) {\n generateRootType(rootTypeName, ctx);\n }\n\n // Add import statements for imported types\n addImportStatements(ctx);\n\n sourceFile.formatText();\n\n return {\n project,\n sourceFile,\n rootTypeName: rootTypeName ?? undefined,\n };\n}\n\n/** Track types referenced from schemaLocation imports and linked $imports. */\nfunction trackSchemaImports(\n schema: Schema,\n options: SchemaToSourceFileOptions,\n ctx: GeneratorContext,\n): void {\n // Process schemaLocation imports via resolver\n if (options.schemaLocationResolver && schema.import) {\n for (const imp of schema.import) {\n trackSchemaLocationImport(\n imp,\n options.schemaLocationResolver,\n options.basePath,\n ctx,\n );\n }\n }\n\n // Process $imports - track types from already-linked schemas\n if (schema.$imports) {\n for (const importedSchema of schema.$imports) {\n trackImportedTypes(importedSchema, ctx);\n }\n }\n}\n\n/** Resolve one schemaLocation import and track its types. */\nfunction trackSchemaLocationImport(\n imp: { schemaLocation?: string },\n resolver: SchemaLocationResolver,\n basePath: string | undefined,\n ctx: GeneratorContext,\n): void {\n if (!imp.schemaLocation) return;\n\n const resolvedSchema = resolver(imp.schemaLocation, basePath);\n if (resolvedSchema) {\n trackImportedTypes(resolvedSchema, ctx);\n }\n}\n\n/** Generate interfaces and type aliases for all schema declarations. */\nfunction generateSchemaTypes(schema: Schema, ctx: GeneratorContext): void {\n // Generate interfaces for all complex types\n for (const ct of schema.complexType ?? []) {\n if (ct.name) {\n generateInterface(ct.name, ct, ctx);\n }\n }\n\n // Generate interfaces for root elements with inline complexType\n for (const el of schema.element ?? []) {\n if (el.name && el.complexType) {\n // Generate interface for inline complexType (e.g., AbapGitType from abapGit element)\n const interfaceName = toInterfaceName(el.name);\n generateInterface(interfaceName, el.complexType, ctx);\n }\n }\n\n // Generate type aliases for all simple types\n for (const st of schema.simpleType ?? []) {\n if (st.name) {\n generateSimpleType(st.name, st, ctx);\n }\n }\n}\n\n// =============================================================================\n// flattenType - Flatten a type into a new file\n// =============================================================================\n\nexport interface FlattenTypeOptions {\n /** Output filename */\n filename?: string;\n /** Additional source files to include for resolving imports (e.g., base type files) */\n additionalSourceFiles?: SourceFile[];\n}\n\n/**\n * Flatten a type from a SourceFile into a new file with all nested types inlined.\n *\n * @param sourceFile - The source file containing the type\n * @param typeName - Name of the type to flatten\n * @param options - Options\n * @returns New SourceFile with the flattened type\n */\nexport function flattenType(\n sourceFile: SourceFile,\n typeName: string,\n options: FlattenTypeOptions = {},\n): SourceFile {\n const project = sourceFile.getProject();\n\n // Add additional source files to the project for cross-file type resolution\n if (options.additionalSourceFiles) {\n for (const additionalFile of options.additionalSourceFiles) {\n // Copy the content to the same project so types can be resolved\n const existingFile = project.getSourceFile(additionalFile.getFilePath());\n if (!existingFile) {\n project.createSourceFile(\n additionalFile.getFilePath(),\n additionalFile.getFullText(),\n { overwrite: true },\n );\n }\n }\n }\n\n const checker = project.getTypeChecker();\n\n // Find the type alias or interface\n const typeAlias = sourceFile.getTypeAlias(typeName);\n const iface = sourceFile.getInterface(typeName);\n const node = typeAlias ?? iface;\n\n if (!node) {\n throw new Error(`Type \"${typeName}\" not found in source file`);\n }\n\n const type = node.getType();\n const flattened = expandTypeToString(type, checker, node);\n\n const outputFilename =\n options.filename ?? `${typeName.toLowerCase()}.flattened.ts`;\n const flatFile = project.createSourceFile(outputFilename, '', {\n overwrite: true,\n });\n\n flatFile.addTypeAlias({\n name: typeName,\n isExported: true,\n type: flattened,\n });\n\n flatFile.formatText();\n return flatFile;\n}\n\n// =============================================================================\n// Type Expansion (for flattenType)\n// =============================================================================\n\n/**\n * Check if a type is a primitive that shouldn't be expanded.\n */\nexport function isPrimitiveType(type: Type): boolean {\n return (\n type.isString() ||\n type.isNumber() ||\n type.isBoolean() ||\n type.isUndefined() ||\n type.isNull() ||\n type.isUnknown() ||\n type.isStringLiteral() ||\n type.isNumberLiteral() ||\n type.isBooleanLiteral() ||\n type.isAny() ||\n type.isNever() ||\n type.isVoid()\n );\n}\n\n/**\n * Expand a type's string index signature (e.g. from xs:any wildcards)\n * to a `[key: string]: T;` line, or null when absent.\n */\nfunction expandIndexSignature(\n type: Type,\n checker: TypeChecker,\n context: Node,\n indent: string,\n visited: Set<string>,\n): string | null {\n const stringIndexType = type.getStringIndexType();\n if (!stringIndexType) return null;\n const expandedIndex = expandTypeToString(\n stringIndexType,\n checker,\n context,\n indent + ' ',\n visited,\n );\n return `${indent} [key: string]: ${expandedIndex};`;\n}\n\n/**\n * Expand a type's named properties (and any index signature) into an\n * object-literal string. Caller handles cycle detection via `visited`.\n */\nfunction expandObjectLiteral(\n type: Type,\n props: TsSymbol[],\n checker: TypeChecker,\n context: Node,\n indent: string,\n visited: Set<string>,\n): string {\n const lines: string[] = ['{'];\n for (const prop of props) {\n let name = prop.getName();\n\n // Handle namespaced property names like \"xml: base\" -> \"base\"\n // These come from XSD attribute references like xml:base\n if (name.includes(': ')) {\n name = name.split(': ').pop() ?? name;\n }\n\n const declarations = prop.getDeclarations();\n const declNode = declarations[0] ?? context;\n const propType = checker.getTypeOfSymbolAtLocation(prop, declNode);\n const optional = prop.isOptional() ? '?' : '';\n const expanded = expandTypeToString(\n propType,\n checker,\n context,\n indent + ' ',\n visited,\n );\n lines.push(`${indent} ${propertyName(name)}${optional}: ${expanded};`);\n }\n\n // Preserve index signatures (e.g. [key: string]: unknown from xs:any)\n const indexLine = expandIndexSignature(\n type,\n checker,\n context,\n indent,\n visited,\n );\n if (indexLine) lines.push(indexLine);\n\n lines.push(`${indent}}`);\n return lines.join('\\n');\n}\n\n/**\n * Recursively expand a type to an inline string representation.\n * Fully inlines all types - no import() statements in output.\n */\nexport function expandTypeToString(\n type: Type,\n checker: TypeChecker,\n context: Node,\n indent = '',\n visited = new Set<string>(),\n): string {\n if (isPrimitiveType(type)) {\n return type.getText();\n }\n\n if (type.isArray()) {\n const elementType = type.getArrayElementType();\n if (elementType) {\n return `${expandTypeToString(\n elementType,\n checker,\n context,\n indent,\n visited,\n )}[]`;\n }\n return 'unknown[]';\n }\n\n if (type.isUnion()) {\n return expandUnionType(type, checker, context, indent, visited);\n }\n\n if (type.isIntersection()) {\n return type\n .getIntersectionTypes()\n .map((t) => expandTypeToString(t, checker, context, indent, visited))\n .join(' & ');\n }\n\n // Get properties and expand them\n const props = type.getProperties();\n if (props.length > 0) {\n return expandPropsObject(type, props, checker, context, indent, visited);\n }\n\n // Type with only an index signature (no named properties)\n const indexLine = expandIndexSignature(\n type,\n checker,\n context,\n indent,\n visited,\n );\n if (indexLine) {\n return `{\\n${indexLine}\\n${indent}}`;\n }\n\n // For types without properties, check if getText() returns an import() - if so, try to expand\n const textRepr = type.getText();\n if (textRepr.includes('import(')) {\n return expandImportType(type, textRepr, checker, context, indent, visited);\n }\n\n return textRepr;\n}\n\n/** Expand a union type to a `A | B | ...` string. */\nfunction expandUnionType(\n type: Type,\n checker: TypeChecker,\n context: Node,\n indent: string,\n visited: Set<string>,\n): string {\n const unionTypes = type.getUnionTypes();\n if (unionTypes.every((t) => isPrimitiveType(t))) {\n // All primitives - but check if getText() returns import() reference\n const textRepr = type.getText();\n if (textRepr.includes('import(')) {\n // Expand each union member individually to avoid import() in output\n return unionTypes.map((t) => t.getText()).join(' | ');\n }\n return textRepr;\n }\n return unionTypes\n .map((t) => expandTypeToString(t, checker, context, indent, visited))\n .join(' | ');\n}\n\n/** Expand a type's properties into an object literal with cycle detection. */\nfunction expandPropsObject(\n type: Type,\n props: TsSymbol[],\n checker: TypeChecker,\n context: Node,\n indent: string,\n visited: Set<string>,\n): string {\n // Get a stable type identifier for cycle detection\n const symbol = type.getSymbol() ?? type.getAliasSymbol();\n const typeId = symbol?.getName() ?? type.getText();\n\n // Check for cycles ONLY when we're about to expand properties\n // This prevents infinite recursion on self-referential types\n if (visited.has(typeId)) {\n return 'unknown';\n }\n\n // Clone visited set and add current type for this expansion branch\n const newVisited = new Set(visited);\n newVisited.add(typeId);\n\n return expandObjectLiteral(type, props, checker, context, indent, newVisited);\n}\n\n/**\n * Try to expand a type whose text form is an import() reference:\n * look up the named type in the project, else expand base types,\n * else return unknown to avoid import() in output.\n */\nfunction expandImportType(\n type: Type,\n textRepr: string,\n checker: TypeChecker,\n context: Node,\n indent: string,\n visited: Set<string>,\n): string {\n // Extract the type name from import(\"...\").TypeName\n const match = /import\\([^)]+\\)\\.(\\w+)/.exec(textRepr);\n if (match) {\n const expanded = expandNamedTypeReference(\n match[1],\n checker,\n context,\n indent,\n visited,\n );\n if (expanded) return expanded;\n }\n\n // Try base types as fallback - but track the type text to prevent cycles\n const baseTypes = type.getBaseTypes();\n if (baseTypes.length > 0) {\n const newVisited = new Set(visited);\n newVisited.add(textRepr); // Use textRepr as cycle key\n return expandTypeToString(\n baseTypes[0],\n checker,\n context,\n indent,\n newVisited,\n );\n }\n // If still no properties, return unknown to avoid import() in output\n return 'unknown';\n}\n\n/**\n * Look up a named type reference in all source files of the project and\n * expand it, or return undefined when it cannot be found.\n */\nfunction expandNamedTypeReference(\n typeName: string,\n checker: TypeChecker,\n context: Node,\n indent: string,\n visited: Set<string>,\n): string | undefined {\n // Look up the type alias in all source files in the project\n const project = context.getSourceFile().getProject();\n for (const sf of project.getSourceFiles()) {\n const typeAlias = sf.getTypeAlias(typeName);\n if (typeAlias) {\n const typeNode = typeAlias.getTypeNode();\n if (typeNode) {\n // Return the raw type text (e.g., \"'active' | 'inactive' | 'pending'\")\n return typeNode.getText();\n }\n }\n // Also check interfaces\n const iface = sf.getInterface(typeName);\n if (iface) {\n // Check for cycles before expanding interface\n if (visited.has(typeName)) {\n return 'unknown';\n }\n // For interfaces, expand their properties with cycle tracking\n const ifaceType = iface.getType();\n const newVisited = new Set(visited);\n newVisited.add(typeName);\n return expandTypeToString(\n ifaceType,\n checker,\n context,\n indent,\n newVisited,\n );\n }\n }\n return undefined;\n}\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\ninterface GeneratorContext {\n schema: Schema;\n sourceFile: SourceFile;\n project: Project;\n generatedTypes: Set<string>;\n addJsDoc?: boolean;\n basePath?: string;\n importedTypes: Map<string, string>; // typeName -> schemaFilename (for imports)\n}\n\n// =============================================================================\n// Internal: XSD Type Mapping\n// =============================================================================\n\nconst XSD_BUILT_IN_TYPES: Record<string, string> = {\n string: 'string',\n normalizedString: 'string',\n token: 'string',\n language: 'string',\n Name: 'string',\n NCName: 'string',\n ID: 'string',\n IDREF: 'string',\n IDREFS: 'string',\n ENTITY: 'string',\n ENTITIES: 'string',\n NMTOKEN: 'string',\n NMTOKENS: 'string',\n anyURI: 'string',\n QName: 'string',\n NOTATION: 'string',\n integer: 'number',\n int: 'number',\n long: 'number',\n short: 'number',\n byte: 'number',\n decimal: 'number',\n float: 'number',\n double: 'number',\n nonNegativeInteger: 'number',\n positiveInteger: 'number',\n nonPositiveInteger: 'number',\n negativeInteger: 'number',\n unsignedLong: 'number',\n unsignedInt: 'number',\n unsignedShort: 'number',\n unsignedByte: 'number',\n boolean: 'boolean',\n date: 'string',\n time: 'string',\n dateTime: 'string',\n duration: 'string',\n gYear: 'string',\n gYearMonth: 'string',\n gMonth: 'string',\n gMonthDay: 'string',\n gDay: 'string',\n base64Binary: 'string',\n hexBinary: 'string',\n anyType: 'unknown',\n anySimpleType: 'unknown',\n};\n\nfunction stripNsPrefix(name: string): string {\n const idx = name.indexOf(':');\n return idx >= 0 ? name.slice(idx + 1) : name;\n}\n\nfunction toInterfaceName(name: string): string {\n const n = name.charAt(0).toUpperCase() + name.slice(1);\n return n.endsWith('Type') ? n : `${n}Type`;\n}\n\n// =============================================================================\n// Internal: Import Tracking\n// =============================================================================\n\nfunction trackImportedTypes(\n importedSchema: Schema,\n ctx: GeneratorContext,\n): void {\n const schemaName = importedSchema.$filename?.replace('.xsd', '') ?? 'unknown';\n\n for (const ct of importedSchema.complexType ?? []) {\n if (ct.name) {\n ctx.importedTypes.set(ct.name, schemaName);\n }\n }\n for (const st of importedSchema.simpleType ?? []) {\n if (st.name) {\n ctx.importedTypes.set(st.name, schemaName);\n }\n }\n\n // Recursively track nested imports\n if (importedSchema.$imports) {\n for (const nested of importedSchema.$imports) {\n trackImportedTypes(nested, ctx);\n }\n }\n}\n\nfunction addImportStatements(ctx: GeneratorContext): void {\n // Group imports by schema\n const importsBySchema = new Map<string, Set<string>>();\n\n for (const [typeName, schemaName] of ctx.importedTypes) {\n const interfaceName = toInterfaceName(typeName);\n // Only add import if we actually reference this type\n if (ctx.generatedTypes.has(`import:${interfaceName}`)) {\n let types = importsBySchema.get(schemaName);\n if (!types) {\n types = new Set();\n importsBySchema.set(schemaName, types);\n }\n types.add(interfaceName);\n }\n }\n\n // Add import declarations at the top\n for (const [schemaName, types] of importsBySchema) {\n ctx.sourceFile.addImportDeclaration({\n moduleSpecifier: `./${schemaName}.types`,\n namedImports: Array.from(types).sort((a, b) => a.localeCompare(b)),\n });\n }\n}\n\n// =============================================================================\n// Internal: Type Resolution\n// =============================================================================\n\nfunction resolveTypeName(typeName: string, ctx: GeneratorContext): string {\n const stripped = stripNsPrefix(typeName);\n\n // Built-in XSD type?\n if (XSD_BUILT_IN_TYPES[stripped]) {\n return XSD_BUILT_IN_TYPES[stripped];\n }\n\n // Simple type in current schema? Return the type alias name\n const st = ctx.schema.simpleType?.find((s) => s.name === stripped);\n if (st) {\n return toInterfaceName(stripped);\n }\n\n // Complex type in current schema?\n const ct = ctx.schema.complexType?.find((c) => c.name === stripped);\n if (ct) {\n return toInterfaceName(stripped);\n }\n\n // Imported type?\n if (ctx.importedTypes.has(stripped)) {\n const interfaceName = toInterfaceName(stripped);\n ctx.generatedTypes.add(`import:${interfaceName}`); // Mark as used\n return interfaceName;\n }\n\n return 'unknown';\n}\n\n// =============================================================================\n// Internal: Interface Generation\n// =============================================================================\n\nfunction generateInterface(\n name: string,\n ct: TopLevelComplexType | LocalComplexType,\n ctx: GeneratorContext,\n): void {\n const interfaceName = toInterfaceName(name);\n if (ctx.generatedTypes.has(interfaceName)) return;\n ctx.generatedTypes.add(interfaceName);\n\n const properties: Array<{\n name: string;\n type: string;\n hasQuestionToken: boolean;\n }> = [];\n const extendsTypes: string[] = [];\n let hasAny = false;\n\n // Handle complexContent extension (inheritance)\n if (ct.complexContent?.extension?.base) {\n hasAny = collectExtensionContent(\n ct.complexContent.extension,\n properties,\n extendsTypes,\n ctx,\n );\n }\n // Handle simpleContent\n else if (ct.simpleContent?.extension) {\n collectSimpleContent(ct.simpleContent.extension, properties, ctx);\n }\n // Handle direct content (no complexContent/simpleContent)\n else {\n hasAny = collectDirectContent(ct, properties, ctx);\n }\n\n const interfaceDecl = ctx.sourceFile.addInterface({\n name: interfaceName,\n isExported: true,\n extends: extendsTypes.length > 0 ? extendsTypes : undefined,\n properties: properties.map((p) => ({\n name: propertyName(p.name),\n type: p.type,\n hasQuestionToken: p.hasQuestionToken,\n })),\n docs: ctx.addJsDoc\n ? [{ description: `Generated from complexType: ${name}` }]\n : undefined,\n });\n\n // Add index signature for xs:any wildcard\n if (hasAny) {\n interfaceDecl.addIndexSignature({\n keyName: 'key',\n keyType: 'string',\n returnType: 'unknown',\n });\n }\n}\n\ntype PropertyList = Array<{\n name: string;\n type: string;\n hasQuestionToken: boolean;\n}>;\n\n/** Collect properties from a complexContent extension (inheritance). */\nfunction collectExtensionContent(\n ext: ComplexContentExtension,\n properties: PropertyList,\n extendsTypes: string[],\n ctx: GeneratorContext,\n): boolean {\n const baseName = stripNsPrefix(ext.base ?? '');\n const baseInterfaceName = toInterfaceName(baseName);\n\n // Is it an imported type?\n if (ctx.importedTypes.has(baseName)) {\n ctx.generatedTypes.add(`import:${baseInterfaceName}`);\n extendsTypes.push(baseInterfaceName);\n } else {\n // Local type - generate it first\n const baseCt = ctx.schema.complexType?.find((c) => c.name === baseName);\n if (baseCt) {\n generateInterface(baseName, baseCt, ctx);\n extendsTypes.push(baseInterfaceName);\n }\n }\n\n // Collect extension's own properties\n let hasAny = false;\n if (ext.sequence)\n hasAny = collectFromGroup(ext.sequence, properties, ctx, false) || hasAny;\n if (ext.choice)\n hasAny = collectFromGroup(ext.choice, properties, ctx, true) || hasAny;\n if (ext.all)\n hasAny = collectFromGroup(ext.all, properties, ctx, false) || hasAny;\n collectAttributes(ext.attribute, properties, ctx);\n return hasAny;\n}\n\n/** Collect properties from a simpleContent extension ($value + attributes). */\nfunction collectSimpleContent(\n ext: SimpleContentExtension,\n properties: PropertyList,\n ctx: GeneratorContext,\n): void {\n const baseType = ext.base\n ? (XSD_BUILT_IN_TYPES[stripNsPrefix(ext.base)] ?? 'string')\n : 'string';\n properties.push({ name: '$value', type: baseType, hasQuestionToken: true });\n collectAttributes(ext.attribute, properties, ctx);\n}\n\n/** Collect properties from direct content (no complexContent/simpleContent). */\nfunction collectDirectContent(\n ct: TopLevelComplexType | LocalComplexType,\n properties: PropertyList,\n ctx: GeneratorContext,\n): boolean {\n let hasAny = false;\n if (ct.sequence)\n hasAny = collectFromGroup(ct.sequence, properties, ctx, false) || hasAny;\n if (ct.choice)\n hasAny = collectFromGroup(ct.choice, properties, ctx, true) || hasAny;\n if (ct.all)\n hasAny = collectFromGroup(ct.all, properties, ctx, false) || hasAny;\n collectAttributes(ct.attribute, properties, ctx);\n\n if (ct.mixed) {\n properties.push({\n name: '_text',\n type: 'string',\n hasQuestionToken: true,\n });\n }\n\n // Handle group reference\n if (ct.group?.ref) {\n hasAny = collectNamedGroupRef(ct.group.ref, properties, ctx) || hasAny;\n }\n\n return hasAny;\n}\n\n/** Collect properties from a referenced named group (xs:group ref=\"...\"). */\nfunction collectNamedGroupRef(\n ref: string,\n properties: PropertyList,\n ctx: GeneratorContext,\n): boolean {\n const groupName = stripNsPrefix(ref);\n const group = ctx.schema.group?.find((g) => g.name === groupName);\n if (!group) return false;\n\n let hasAny = false;\n if (group.sequence)\n hasAny = collectFromGroup(group.sequence, properties, ctx, false) || hasAny;\n if (group.choice)\n hasAny = collectFromGroup(group.choice, properties, ctx, true) || hasAny;\n if (group.all)\n hasAny = collectFromGroup(group.all, properties, ctx, false) || hasAny;\n return hasAny;\n}\n\ninterface GroupLike {\n element?: readonly LocalElement[];\n sequence?: readonly ExplicitGroup[];\n choice?: readonly ExplicitGroup[];\n group?: readonly { ref?: string }[];\n any?: readonly { namespace?: string; processContents?: string }[];\n}\n\nfunction collectFromGroup(\n group: GroupLike,\n properties: Array<{ name: string; type: string; hasQuestionToken: boolean }>,\n ctx: GeneratorContext,\n forceOptional: boolean,\n): boolean {\n let hasAny = false;\n\n for (const el of group.element ?? []) {\n addElementProperty(el, properties, ctx, forceOptional);\n }\n\n for (const seq of group.sequence ?? []) {\n if (collectFromGroup(seq, properties, ctx, forceOptional)) hasAny = true;\n }\n\n for (const ch of group.choice ?? []) {\n if (collectFromGroup(ch, properties, ctx, true)) hasAny = true;\n }\n\n for (const g of group.group ?? []) {\n if (g.ref && collectNamedGroup(g.ref, properties, ctx, forceOptional)) {\n hasAny = true;\n }\n }\n\n // Check for xs:any wildcard\n if (group.any && group.any.length > 0) {\n hasAny = true;\n }\n\n return hasAny;\n}\n\n/** Collect properties from a named group reference (xs:group ref=\"...\"). */\nfunction collectNamedGroup(\n ref: string,\n properties: Array<{ name: string; type: string; hasQuestionToken: boolean }>,\n ctx: GeneratorContext,\n forceOptional: boolean,\n): boolean {\n const groupName = stripNsPrefix(ref);\n const namedGroup = ctx.schema.group?.find((gr) => gr.name === groupName);\n if (!namedGroup) return false;\n\n let hasAny = false;\n if (namedGroup.sequence)\n hasAny =\n collectFromGroup(namedGroup.sequence, properties, ctx, forceOptional) ||\n hasAny;\n if (namedGroup.choice)\n hasAny =\n collectFromGroup(namedGroup.choice, properties, ctx, true) || hasAny;\n if (namedGroup.all)\n hasAny =\n collectFromGroup(namedGroup.all, properties, ctx, forceOptional) ||\n hasAny;\n return hasAny;\n}\n\n/**\n * Resolve the TypeScript type string for a local element.\n */\nfunction resolveElementType(\n element: LocalElement,\n ctx: GeneratorContext,\n): string {\n if (element.type) {\n return resolveTypeName(element.type, ctx);\n }\n if (element.complexType) {\n // Inline complex type - generate anonymous interface\n return 'unknown'; // NOTE: could generate inline type for complex types\n }\n if (element.simpleType?.restriction?.enumeration) {\n return element.simpleType.restriction.enumeration\n .map((e: { value?: string }) => `'${e.value}'`)\n .join(' | ');\n }\n if (element.simpleType?.restriction?.base) {\n return (\n XSD_BUILT_IN_TYPES[stripNsPrefix(element.simpleType.restriction.base)] ??\n 'string'\n );\n }\n return 'string';\n}\n\n/**\n * Find a referenced element definition by name across the current schema and its imports.\n */\nfunction findRefElement(\n refName: string,\n ctx: GeneratorContext,\n): LocalElement | undefined {\n const found = ctx.schema.element?.find((e) => e.name === refName);\n if (found) return found;\n if (ctx.schema.$imports) {\n for (const imported of ctx.schema.$imports) {\n const fromImport = imported.element?.find((e) => e.name === refName);\n if (fromImport) return fromImport;\n }\n }\n return undefined;\n}\n\nfunction addElementProperty(\n element: LocalElement,\n properties: Array<{ name: string; type: string; hasQuestionToken: boolean }>,\n ctx: GeneratorContext,\n forceOptional: boolean,\n): void {\n // Handle element reference\n if (element.ref) {\n const refName = stripNsPrefix(element.ref);\n const refElement = findRefElement(refName, ctx);\n if (refElement) {\n addElementProperty(\n {\n ...refElement,\n minOccurs: element.minOccurs,\n maxOccurs: element.maxOccurs,\n },\n properties,\n ctx,\n forceOptional,\n );\n }\n return;\n }\n\n if (!element.name) return;\n\n const isArray =\n element.maxOccurs === 'unbounded' ||\n (typeof element.maxOccurs === 'number' && element.maxOccurs > 1);\n const isOptional =\n forceOptional || element.minOccurs === '0' || element.minOccurs === 0;\n\n let tsType = resolveElementType(element, ctx);\n\n if (isArray) {\n tsType = `${tsType}[]`;\n }\n\n properties.push({\n name: element.name,\n type: tsType,\n hasQuestionToken: isOptional,\n });\n}\n\nfunction collectAttributes(\n attributes: readonly LocalAttribute[] | undefined,\n properties: Array<{ name: string; type: string; hasQuestionToken: boolean }>,\n ctx: GeneratorContext,\n): void {\n if (!attributes) return;\n\n for (const attr of attributes) {\n if (attr.ref) {\n // Always use the local name (strip namespace prefix)\n // xml:base -> base, xml:lang -> lang, etc.\n const refName = stripNsPrefix(attr.ref);\n properties.push({\n name: refName,\n type: 'string',\n hasQuestionToken: true,\n });\n continue;\n }\n\n if (!attr.name) continue;\n if (attr.use === 'prohibited') continue;\n\n const isOptional = attr.use !== 'required';\n let tsType = 'string';\n\n if (attr.type) {\n const typeName = stripNsPrefix(attr.type);\n tsType = XSD_BUILT_IN_TYPES[typeName] ?? resolveTypeName(attr.type, ctx);\n }\n\n properties.push({\n name: attr.name,\n type: tsType,\n hasQuestionToken: isOptional,\n });\n }\n}\n\n// =============================================================================\n// Internal: Simple Type Generation\n// =============================================================================\n\nfunction generateSimpleType(\n name: string,\n st: TopLevelSimpleType,\n ctx: GeneratorContext,\n): void {\n const typeName = toInterfaceName(name);\n if (ctx.generatedTypes.has(typeName)) return;\n ctx.generatedTypes.add(typeName);\n\n let tsType = 'string';\n\n if (st.restriction?.enumeration && st.restriction.enumeration.length > 0) {\n tsType = st.restriction.enumeration.map((e) => `'${e.value}'`).join(' | ');\n } else if (st.restriction?.base) {\n tsType = XSD_BUILT_IN_TYPES[stripNsPrefix(st.restriction.base)] ?? 'string';\n }\n\n ctx.sourceFile.addTypeAlias({\n name: typeName,\n isExported: true,\n type: tsType,\n docs: ctx.addJsDoc\n ? [{ description: `Generated from simpleType: ${name}` }]\n : undefined,\n });\n}\n\n// =============================================================================\n// Internal: Root Type Generation\n// =============================================================================\n\nfunction generateRootType(rootTypeName: string, ctx: GeneratorContext): void {\n const elements = ctx.schema.element ?? [];\n\n // Filter to non-abstract elements that have a name\n const concreteElements = elements.filter((el) => el.name && !el.abstract);\n\n if (concreteElements.length === 0) return;\n\n // Build type for each element\n const elementTypes: string[] = [];\n const elementNames: string[] = [];\n\n for (const el of concreteElements) {\n let elementType: string;\n if (el.type) {\n elementType = resolveTypeName(el.type, ctx);\n } else if (el.complexType) {\n // For inline complexType, use the generated interface name\n const interfaceName = toInterfaceName(el.name!);\n elementType = ctx.generatedTypes.has(interfaceName)\n ? interfaceName\n : 'unknown';\n } else {\n elementType = 'string';\n }\n elementTypes.push(elementType);\n elementNames.push(el.name!);\n }\n\n // For single root: wrap with element name { elementName: Type }\n // For multi-root: union of wrapped types { el1: Type1 } | { el2: Type2 }\n // Root type matches what parse() returns - wrapped with element name for type discrimination\n const wrappedTypes = elementNames.map(\n (name, i) => `{ ${propertyName(name)}: ${elementTypes[i]} }`,\n );\n const rootType =\n wrappedTypes.length === 1 ? wrappedTypes[0] : wrappedTypes.join(' | ');\n\n const description =\n elementNames.length === 1\n ? `Root schema type (${elementNames[0]} element)`\n : `Root schema type (${elementNames.join(' | ')} elements)`;\n\n ctx.sourceFile.addTypeAlias({\n name: rootTypeName,\n isExported: true,\n type: rootType,\n docs: ctx.addJsDoc ? [{ description }] : undefined,\n });\n}\n","/**\n * Interface Generator - High-level API for generating TypeScript interfaces from XSD schemas\n *\n * Consumes ts-morph.ts for SourceFile manipulation, handles:\n * - generateInterfaces API with flatten option\n * - Code string output\n * - Root type name derivation\n */\n\nimport type { SourceFile, Project } from 'ts-morph';\nimport type { Schema } from '../xsd/types';\nimport {\n schemaToSourceFile,\n flattenType,\n type SchemaToSourceFileOptions,\n} from './ts-morph';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface GenerateInterfacesOptions extends SchemaToSourceFileOptions {\n /**\n * If true, generates a single flattened type with all nested types inlined.\n * If false (default), generates interfaces with imports and extends.\n */\n flatten?: boolean;\n /**\n * Additional source files to include for resolving imports when flattening.\n * These are typically the generated source files from $imports schemas.\n */\n additionalSourceFiles?: SourceFile[];\n}\n\nexport interface GenerateInterfacesResult {\n /** The generated TypeScript code */\n code: string;\n /** The ts-morph SourceFile */\n sourceFile: SourceFile;\n /** The root type name */\n rootTypeName: string | undefined;\n /** The ts-morph Project (useful for further manipulation) */\n project: Project;\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Derive root type name from schema filename.\n * e.g., 'packagesV1.xsd' -> 'PackagesV1Schema'\n */\nexport function deriveRootTypeName(filename?: string): string | undefined {\n if (!filename) return undefined;\n const baseName = filename.replace(/\\.xsd$/, '').replace(/^.*\\//, '');\n const pascalCase = baseName.charAt(0).toUpperCase() + baseName.slice(1);\n return `${pascalCase}Schema`;\n}\n\n// =============================================================================\n// Main API\n// =============================================================================\n\n/**\n * Generate TypeScript interfaces from a schema.\n *\n * @param schema - The schema to generate interfaces from\n * @param options - Generation options\n * @returns Generated TypeScript code and metadata\n *\n * @example\n * // Generate interfaces with imports and extends\n * const { code } = generateInterfaces(schema);\n *\n * @example\n * // Generate a single flattened type with all nested types inlined\n * const { code } = generateInterfaces(schema, {\n * flatten: true,\n * additionalSourceFiles: [baseSchemaSourceFile]\n * });\n */\nexport function generateInterfaces(\n schema: Schema,\n options: GenerateInterfacesOptions = {},\n): GenerateInterfacesResult {\n const { flatten, additionalSourceFiles, ...schemaOptions } = options;\n\n // Step 1: Generate the source file with interfaces using ts-morph\n const { project, sourceFile, rootTypeName } = schemaToSourceFile(\n schema,\n schemaOptions,\n );\n\n // Step 2: If flatten is requested and we have a root type, flatten it\n if (flatten && rootTypeName) {\n const flattenedFile = flattenType(sourceFile, rootTypeName, {\n additionalSourceFiles,\n filename: `${\n schema.$filename?.replace('.xsd', '') ?? 'schema'\n }.flattened.ts`,\n });\n\n return {\n code: flattenedFile.getFullText(),\n sourceFile: flattenedFile,\n rootTypeName,\n project,\n };\n }\n\n // Return the non-flattened interfaces\n return {\n code: sourceFile.getFullText(),\n sourceFile,\n rootTypeName,\n project,\n };\n}\n","/**\n * Generator Plugin Types\n *\n * Composable generator architecture for ts-xsd codegen.\n * Each generator is a plugin that can transform schemas and produce output files.\n */\n\nimport type { Schema } from '../xsd/types';\n\n// ============================================================================\n// Generated Output\n// ============================================================================\n\n/**\n * A file to be written by the codegen system\n */\nexport interface GeneratedFile {\n /** Relative path from outputDir (e.g., 'intf.ts', 'index.ts') */\n path: string;\n /** File content */\n content: string;\n}\n\n// ============================================================================\n// Context Types\n// ============================================================================\n\n/**\n * Information about a single schema being processed\n */\nexport interface SchemaInfo {\n /** Schema name (e.g., 'intf', 'classes') */\n name: string;\n /** Parsed XSD content */\n xsdContent: string;\n /** Parsed schema object */\n schema: Schema;\n /** Source group name (e.g., 'sap', 'custom', 'abapgit') */\n sourceName: string;\n /** XSD file path */\n xsdPath: string;\n}\n\n/**\n * Information about a source group\n */\nexport interface SourceInfo {\n /** Source name (e.g., 'sap', 'custom') */\n name: string;\n /** Directory containing XSD files */\n xsdDir: string;\n /** Output directory for generated files */\n outputDir: string;\n /** List of schema names to process */\n schemas: string[];\n}\n\n/**\n * Context passed to setup() - before any schemas are processed\n */\nexport interface SetupContext {\n /** All configured sources */\n sources: Record<string, SourceInfo>;\n /** Root directory (where config file is located) */\n rootDir: string;\n}\n\n/**\n * Context passed to transform() - for each schema\n */\nexport interface TransformContext {\n /** Current schema being processed */\n schema: SchemaInfo;\n /** Source this schema belongs to */\n source: SourceInfo;\n /** All schemas in this source (for import resolution) */\n allSchemas: Map<string, SchemaInfo>;\n /** All sources (for cross-source import resolution) */\n allSources: Record<string, SourceInfo>;\n /** Root directory */\n rootDir: string;\n /** Resolve import path for a schema location */\n resolveImport: (schemaLocation: string) => string | null;\n}\n\n/**\n * Context passed to finalize() - after all schemas are processed\n */\nexport interface FinalizeContext {\n /** All processed schemas grouped by source */\n processedSchemas: Map<string, SchemaInfo[]>;\n /** All sources */\n sources: Record<string, SourceInfo>;\n /** Root directory */\n rootDir: string;\n}\n\n// ============================================================================\n// Generator Plugin Interface\n// ============================================================================\n\n/**\n * Generator plugin interface\n *\n * Generators are composable - multiple generators can be used together.\n * Each generator can:\n * - setup(): Initialize state before processing\n * - transform(): Generate files for each schema\n * - finalize(): Generate aggregate files after all schemas\n */\nexport interface GeneratorPlugin {\n /** Unique name for this generator */\n readonly name: string;\n\n /**\n * Called once before processing any schemas.\n * Use for initialization, validation, etc.\n */\n setup?(ctx: SetupContext): void | Promise<void>;\n\n /**\n * Called for each schema file.\n * Return generated files for this schema.\n */\n transform?(ctx: TransformContext): GeneratedFile[] | Promise<GeneratedFile[]>;\n\n /**\n * Called once after all schemas are processed.\n * Use for generating index files, aggregate types, etc.\n */\n finalize?(ctx: FinalizeContext): GeneratedFile[] | Promise<GeneratedFile[]>;\n}\n\n// ============================================================================\n// Config Types\n// ============================================================================\n\n/**\n * Source configuration\n */\nexport interface SourceConfig {\n /** Directory containing XSD files (relative to config file) */\n xsdDir: string;\n /** Output directory for generated files (relative to config file) */\n outputDir: string;\n /** List of schema names to process (without .xsd extension) */\n schemas: string[];\n /**\n * Automatically discover and include all dependent schemas referenced via\n * xs:import, xs:include, xs:redefine schemaLocation attributes.\n * When enabled, you only need to list entry-point schemas - all dependencies\n * will be discovered and generated automatically.\n */\n autoLink?: boolean;\n}\n\n/**\n * Hook context for beforeAll/afterAll\n */\nexport interface HookContext {\n /** All configured sources */\n sources: Record<string, SourceInfo>;\n /** Root directory (where config file is located) */\n rootDir: string;\n}\n\n/**\n * Hook context for afterAll with results\n */\nexport interface AfterAllContext extends HookContext {\n /** All processed schemas grouped by source */\n processedSchemas: Map<string, SchemaInfo[]>;\n /** All generated files */\n generatedFiles: GeneratedFile[];\n}\n\n/**\n * Codegen configuration\n */\nexport interface CodegenConfig {\n /** Schema sources to process */\n sources: Record<string, SourceConfig>;\n /** Generator plugins to run */\n generators: GeneratorPlugin[];\n /** Import extension for generated files: '.ts' for Node.js native, '' for bundlers (default: '' for bundler compatibility) */\n importExtension?: '.ts' | '';\n /** Clean output directories before generation (default: false) */\n clean?: boolean;\n /** Features to enable for all generators */\n features?: {\n /** Include $xmlns in output */\n $xmlns?: boolean;\n /** Include $imports in output */\n $imports?: boolean;\n /** Include $filename in output */\n $filename?: boolean;\n };\n /** Properties to exclude from output */\n exclude?: string[];\n\n /**\n * Hook called before any processing starts.\n * Use for setup, validation, cleaning output dirs, etc.\n */\n beforeAll?(ctx: HookContext): void | Promise<void>;\n\n /**\n * Hook called after all processing is complete.\n * Use for generating aggregate files, post-processing, etc.\n * Return additional files to write.\n */\n afterAll?(\n ctx: AfterAllContext,\n ): GeneratedFile[] | void | Promise<GeneratedFile[] | void>;\n}\n\n/**\n * Define a codegen configuration with type checking\n */\nexport function defineConfig(config: CodegenConfig): CodegenConfig {\n return config;\n}\n"],"mappings":";;;;;;;;;;;;;;AA4EA,SAAS,aAAa,MAAsB;CAC1C,OAAO,qDAAqD,KAAK,IAAI,IACjE,OACA,KAAK,UAAU,IAAI;AACzB;AAEA,SAASA,qBAAmB,UAAkD;CAC5E,IAAI,CAAC,UAAU,OAAO;CACtB,MAAM,WAAW,SAAS,QAAQ,UAAU,EAAE,CAAC,CAAC,QAAQ,SAAS,EAAE;CAEnE,OAAO,GADY,SAAS,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,SAAS,MAAM,CAAC,EACjD;AACvB;;;;;;;AAYA,SAAgB,mBACd,QACA,UAAqC,CAAC,GACd;CACxB,MAAM,UACJ,QAAQ,WAAW,IAAI,QAAQ,EAAE,uBAAuB,KAAK,CAAC;CAGhE,MAAM,eACJ,QAAQ,iBAAiB,SACrBA,qBAAmB,OAAO,SAAS,IACnC,QAAQ;CACd,MAAM,WAAW,GACf,OAAO,WAAW,QAAQ,QAAQ,EAAE,KAAK,SAC1C;CACD,MAAM,aAAa,QAAQ,iBAAiB,UAAU,IAAI,EACxD,WAAW,KACb,CAAC;CAED,MAAM,MAAwB;EAC5B;EACA;EACA;EACA,gCAAgB,IAAI,IAAY;EAChC,UAAU,QAAQ;EAClB,UAAU,QAAQ;EAClB,+BAAe,IAAI,IAAoB;CACzC;CAGA,mBAAmB,QAAQ,SAAS,GAAG;CAGvC,oBAAoB,QAAQ,GAAG;CAG/B,IAAI,cACF,iBAAiB,cAAc,GAAG;CAIpC,oBAAoB,GAAG;CAEvB,WAAW,WAAW;CAEtB,OAAO;EACL;EACA;EACA,cAAc,gBAAgB;CAChC;AACF;;AAGA,SAAS,mBACP,QACA,SACA,KACM;CAEN,IAAI,QAAQ,0BAA0B,OAAO,QAC3C,KAAK,MAAM,OAAO,OAAO,QACvB,0BACE,KACA,QAAQ,wBACR,QAAQ,UACR,GACF;CAKJ,IAAI,OAAO,UACT,KAAK,MAAM,kBAAkB,OAAO,UAClC,mBAAmB,gBAAgB,GAAG;AAG5C;;AAGA,SAAS,0BACP,KACA,UACA,UACA,KACM;CACN,IAAI,CAAC,IAAI,gBAAgB;CAEzB,MAAM,iBAAiB,SAAS,IAAI,gBAAgB,QAAQ;CAC5D,IAAI,gBACF,mBAAmB,gBAAgB,GAAG;AAE1C;;AAGA,SAAS,oBAAoB,QAAgB,KAA6B;CAExE,KAAK,MAAM,MAAM,OAAO,eAAe,CAAC,GACtC,IAAI,GAAG,MACL,kBAAkB,GAAG,MAAM,IAAI,GAAG;CAKtC,KAAK,MAAM,MAAM,OAAO,WAAW,CAAC,GAClC,IAAI,GAAG,QAAQ,GAAG,aAGhB,kBADsB,gBAAgB,GAAG,IACX,GAAG,GAAG,aAAa,GAAG;CAKxD,KAAK,MAAM,MAAM,OAAO,cAAc,CAAC,GACrC,IAAI,GAAG,MACL,mBAAmB,GAAG,MAAM,IAAI,GAAG;AAGzC;;;;;;;;;AAqBA,SAAgB,YACd,YACA,UACA,UAA8B,CAAC,GACnB;CACZ,MAAM,UAAU,WAAW,WAAW;CAGtC,IAAI,QAAQ,uBACV;OAAK,MAAM,kBAAkB,QAAQ,uBAGnC,IAAI,CADiB,QAAQ,cAAc,eAAe,YAAY,CACtD,GACd,QAAQ,iBACN,eAAe,YAAY,GAC3B,eAAe,YAAY,GAC3B,EAAE,WAAW,KAAK,CACpB;CAEJ;CAGF,MAAM,UAAU,QAAQ,eAAe;CAGvC,MAAM,YAAY,WAAW,aAAa,QAAQ;CAClD,MAAM,QAAQ,WAAW,aAAa,QAAQ;CAC9C,MAAM,OAAO,aAAa;CAE1B,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,SAAS,SAAS,2BAA2B;CAI/D,MAAM,YAAY,mBADL,KAAK,QACsB,GAAG,SAAS,IAAI;CAExD,MAAM,iBACJ,QAAQ,YAAY,GAAG,SAAS,YAAY,EAAE;CAChD,MAAM,WAAW,QAAQ,iBAAiB,gBAAgB,IAAI,EAC5D,WAAW,KACb,CAAC;CAED,SAAS,aAAa;EACpB,MAAM;EACN,YAAY;EACZ,MAAM;CACR,CAAC;CAED,SAAS,WAAW;CACpB,OAAO;AACT;;;;AASA,SAAgB,gBAAgB,MAAqB;CACnD,OACE,KAAK,SAAS,KACd,KAAK,SAAS,KACd,KAAK,UAAU,KACf,KAAK,YAAY,KACjB,KAAK,OAAO,KACZ,KAAK,UAAU,KACf,KAAK,gBAAgB,KACrB,KAAK,gBAAgB,KACrB,KAAK,iBAAiB,KACtB,KAAK,MAAM,KACX,KAAK,QAAQ,KACb,KAAK,OAAO;AAEhB;;;;;AAMA,SAAS,qBACP,MACA,SACA,SACA,QACA,SACe;CACf,MAAM,kBAAkB,KAAK,mBAAmB;CAChD,IAAI,CAAC,iBAAiB,OAAO;CAQ7B,OAAO,GAAG,OAAO,mBAPK,mBACpB,iBACA,SACA,SACA,SAAS,MACT,OAE8C,EAAE;AACpD;;;;;AAMA,SAAS,oBACP,MACA,OACA,SACA,SACA,QACA,SACQ;CACR,MAAM,QAAkB,CAAC,GAAG;CAC5B,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,OAAO,KAAK,QAAQ;EAIxB,IAAI,KAAK,SAAS,IAAI,GACpB,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK;EAInC,MAAM,WADe,KAAK,gBACE,CAAC,CAAC,MAAM;EACpC,MAAM,WAAW,QAAQ,0BAA0B,MAAM,QAAQ;EACjE,MAAM,WAAW,KAAK,WAAW,IAAI,MAAM;EAC3C,MAAM,WAAW,mBACf,UACA,SACA,SACA,SAAS,MACT,OACF;EACA,MAAM,KAAK,GAAG,OAAO,IAAI,aAAa,IAAI,IAAI,SAAS,IAAI,SAAS,EAAE;CACxE;CAGA,MAAM,YAAY,qBAChB,MACA,SACA,SACA,QACA,OACF;CACA,IAAI,WAAW,MAAM,KAAK,SAAS;CAEnC,MAAM,KAAK,GAAG,OAAO,EAAE;CACvB,OAAO,MAAM,KAAK,IAAI;AACxB;;;;;AAMA,SAAgB,mBACd,MACA,SACA,SACA,SAAS,IACT,0BAAU,IAAI,IAAY,GAClB;CACR,IAAI,gBAAgB,IAAI,GACtB,OAAO,KAAK,QAAQ;CAGtB,IAAI,KAAK,QAAQ,GAAG;EAClB,MAAM,cAAc,KAAK,oBAAoB;EAC7C,IAAI,aACF,OAAO,GAAG,mBACR,aACA,SACA,SACA,QACA,OACF,EAAE;EAEJ,OAAO;CACT;CAEA,IAAI,KAAK,QAAQ,GACf,OAAO,gBAAgB,MAAM,SAAS,SAAS,QAAQ,OAAO;CAGhE,IAAI,KAAK,eAAe,GACtB,OAAO,KACJ,qBAAqB,CAAC,CACtB,KAAK,MAAM,mBAAmB,GAAG,SAAS,SAAS,QAAQ,OAAO,CAAC,CAAC,CACpE,KAAK,KAAK;CAIf,MAAM,QAAQ,KAAK,cAAc;CACjC,IAAI,MAAM,SAAS,GACjB,OAAO,kBAAkB,MAAM,OAAO,SAAS,SAAS,QAAQ,OAAO;CAIzE,MAAM,YAAY,qBAChB,MACA,SACA,SACA,QACA,OACF;CACA,IAAI,WACF,OAAO,MAAM,UAAU,IAAI,OAAO;CAIpC,MAAM,WAAW,KAAK,QAAQ;CAC9B,IAAI,SAAS,SAAS,SAAS,GAC7B,OAAO,iBAAiB,MAAM,UAAU,SAAS,SAAS,QAAQ,OAAO;CAG3E,OAAO;AACT;;AAGA,SAAS,gBACP,MACA,SACA,SACA,QACA,SACQ;CACR,MAAM,aAAa,KAAK,cAAc;CACtC,IAAI,WAAW,OAAO,MAAM,gBAAgB,CAAC,CAAC,GAAG;EAE/C,MAAM,WAAW,KAAK,QAAQ;EAC9B,IAAI,SAAS,SAAS,SAAS,GAE7B,OAAO,WAAW,KAAK,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK;EAEtD,OAAO;CACT;CACA,OAAO,WACJ,KAAK,MAAM,mBAAmB,GAAG,SAAS,SAAS,QAAQ,OAAO,CAAC,CAAC,CACpE,KAAK,KAAK;AACf;;AAGA,SAAS,kBACP,MACA,OACA,SACA,SACA,QACA,SACQ;CAGR,MAAM,UADS,KAAK,UAAU,KAAK,KAAK,eAAe,EAClC,EAAE,QAAQ,KAAK,KAAK,QAAQ;CAIjD,IAAI,QAAQ,IAAI,MAAM,GACpB,OAAO;CAIT,MAAM,aAAa,IAAI,IAAI,OAAO;CAClC,WAAW,IAAI,MAAM;CAErB,OAAO,oBAAoB,MAAM,OAAO,SAAS,SAAS,QAAQ,UAAU;AAC9E;;;;;;AAOA,SAAS,iBACP,MACA,UACA,SACA,SACA,QACA,SACQ;CAER,MAAM,QAAQ,yBAAyB,KAAK,QAAQ;CACpD,IAAI,OAAO;EACT,MAAM,WAAW,yBACf,MAAM,IACN,SACA,SACA,QACA,OACF;EACA,IAAI,UAAU,OAAO;CACvB;CAGA,MAAM,YAAY,KAAK,aAAa;CACpC,IAAI,UAAU,SAAS,GAAG;EACxB,MAAM,aAAa,IAAI,IAAI,OAAO;EAClC,WAAW,IAAI,QAAQ;EACvB,OAAO,mBACL,UAAU,IACV,SACA,SACA,QACA,UACF;CACF;CAEA,OAAO;AACT;;;;;AAMA,SAAS,yBACP,UACA,SACA,SACA,QACA,SACoB;CAEpB,MAAM,UAAU,QAAQ,cAAc,CAAC,CAAC,WAAW;CACnD,KAAK,MAAM,MAAM,QAAQ,eAAe,GAAG;EACzC,MAAM,YAAY,GAAG,aAAa,QAAQ;EAC1C,IAAI,WAAW;GACb,MAAM,WAAW,UAAU,YAAY;GACvC,IAAI,UAEF,OAAO,SAAS,QAAQ;EAE5B;EAEA,MAAM,QAAQ,GAAG,aAAa,QAAQ;EACtC,IAAI,OAAO;GAET,IAAI,QAAQ,IAAI,QAAQ,GACtB,OAAO;GAGT,MAAM,YAAY,MAAM,QAAQ;GAChC,MAAM,aAAa,IAAI,IAAI,OAAO;GAClC,WAAW,IAAI,QAAQ;GACvB,OAAO,mBACL,WACA,SACA,SACA,QACA,UACF;EACF;CACF;AAEF;AAoBA,MAAM,qBAA6C;CACjD,QAAQ;CACR,kBAAkB;CAClB,OAAO;CACP,UAAU;CACV,MAAM;CACN,QAAQ;CACR,IAAI;CACJ,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,SAAS;CACT,UAAU;CACV,QAAQ;CACR,OAAO;CACP,UAAU;CACV,SAAS;CACT,KAAK;CACL,MAAM;CACN,OAAO;CACP,MAAM;CACN,SAAS;CACT,OAAO;CACP,QAAQ;CACR,oBAAoB;CACpB,iBAAiB;CACjB,oBAAoB;CACpB,iBAAiB;CACjB,cAAc;CACd,aAAa;CACb,eAAe;CACf,cAAc;CACd,SAAS;CACT,MAAM;CACN,MAAM;CACN,UAAU;CACV,UAAU;CACV,OAAO;CACP,YAAY;CACZ,QAAQ;CACR,WAAW;CACX,MAAM;CACN,cAAc;CACd,WAAW;CACX,SAAS;CACT,eAAe;AACjB;AAEA,SAAS,cAAc,MAAsB;CAC3C,MAAM,MAAM,KAAK,QAAQ,GAAG;CAC5B,OAAO,OAAO,IAAI,KAAK,MAAM,MAAM,CAAC,IAAI;AAC1C;AAEA,SAAS,gBAAgB,MAAsB;CAC7C,MAAM,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC;CACrD,OAAO,EAAE,SAAS,MAAM,IAAI,IAAI,GAAG,EAAE;AACvC;AAMA,SAAS,mBACP,gBACA,KACM;CACN,MAAM,aAAa,eAAe,WAAW,QAAQ,QAAQ,EAAE,KAAK;CAEpE,KAAK,MAAM,MAAM,eAAe,eAAe,CAAC,GAC9C,IAAI,GAAG,MACL,IAAI,cAAc,IAAI,GAAG,MAAM,UAAU;CAG7C,KAAK,MAAM,MAAM,eAAe,cAAc,CAAC,GAC7C,IAAI,GAAG,MACL,IAAI,cAAc,IAAI,GAAG,MAAM,UAAU;CAK7C,IAAI,eAAe,UACjB,KAAK,MAAM,UAAU,eAAe,UAClC,mBAAmB,QAAQ,GAAG;AAGpC;AAEA,SAAS,oBAAoB,KAA6B;CAExD,MAAM,kCAAkB,IAAI,IAAyB;CAErD,KAAK,MAAM,CAAC,UAAU,eAAe,IAAI,eAAe;EACtD,MAAM,gBAAgB,gBAAgB,QAAQ;EAE9C,IAAI,IAAI,eAAe,IAAI,UAAU,eAAe,GAAG;GACrD,IAAI,QAAQ,gBAAgB,IAAI,UAAU;GAC1C,IAAI,CAAC,OAAO;IACV,wBAAQ,IAAI,IAAI;IAChB,gBAAgB,IAAI,YAAY,KAAK;GACvC;GACA,MAAM,IAAI,aAAa;EACzB;CACF;CAGA,KAAK,MAAM,CAAC,YAAY,UAAU,iBAChC,IAAI,WAAW,qBAAqB;EAClC,iBAAiB,KAAK,WAAW;EACjC,cAAc,MAAM,KAAK,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;CACnE,CAAC;AAEL;AAMA,SAAS,gBAAgB,UAAkB,KAA+B;CACxE,MAAM,WAAW,cAAc,QAAQ;CAGvC,IAAI,mBAAmB,WACrB,OAAO,mBAAmB;CAK5B,IADW,IAAI,OAAO,YAAY,MAAM,MAAM,EAAE,SAAS,QAAQ,GAE/D,OAAO,gBAAgB,QAAQ;CAKjC,IADW,IAAI,OAAO,aAAa,MAAM,MAAM,EAAE,SAAS,QAAQ,GAEhE,OAAO,gBAAgB,QAAQ;CAIjC,IAAI,IAAI,cAAc,IAAI,QAAQ,GAAG;EACnC,MAAM,gBAAgB,gBAAgB,QAAQ;EAC9C,IAAI,eAAe,IAAI,UAAU,eAAe;EAChD,OAAO;CACT;CAEA,OAAO;AACT;AAMA,SAAS,kBACP,MACA,IACA,KACM;CACN,MAAM,gBAAgB,gBAAgB,IAAI;CAC1C,IAAI,IAAI,eAAe,IAAI,aAAa,GAAG;CAC3C,IAAI,eAAe,IAAI,aAAa;CAEpC,MAAM,aAID,CAAC;CACN,MAAM,eAAyB,CAAC;CAChC,IAAI,SAAS;CAGb,IAAI,GAAG,gBAAgB,WAAW,MAChC,SAAS,wBACP,GAAG,eAAe,WAClB,YACA,cACA,GACF;MAGG,IAAI,GAAG,eAAe,WACzB,qBAAqB,GAAG,cAAc,WAAW,YAAY,GAAG;MAIhE,SAAS,qBAAqB,IAAI,YAAY,GAAG;CAGnD,MAAM,gBAAgB,IAAI,WAAW,aAAa;EAChD,MAAM;EACN,YAAY;EACZ,SAAS,aAAa,SAAS,IAAI,eAAe;EAClD,YAAY,WAAW,KAAK,OAAO;GACjC,MAAM,aAAa,EAAE,IAAI;GACzB,MAAM,EAAE;GACR,kBAAkB,EAAE;EACtB,EAAE;EACF,MAAM,IAAI,WACN,CAAC,EAAE,aAAa,+BAA+B,OAAO,CAAC,IACvD;CACN,CAAC;CAGD,IAAI,QACF,cAAc,kBAAkB;EAC9B,SAAS;EACT,SAAS;EACT,YAAY;CACd,CAAC;AAEL;;AASA,SAAS,wBACP,KACA,YACA,cACA,KACS;CACT,MAAM,WAAW,cAAc,IAAI,QAAQ,EAAE;CAC7C,MAAM,oBAAoB,gBAAgB,QAAQ;CAGlD,IAAI,IAAI,cAAc,IAAI,QAAQ,GAAG;EACnC,IAAI,eAAe,IAAI,UAAU,mBAAmB;EACpD,aAAa,KAAK,iBAAiB;CACrC,OAAO;EAEL,MAAM,SAAS,IAAI,OAAO,aAAa,MAAM,MAAM,EAAE,SAAS,QAAQ;EACtE,IAAI,QAAQ;GACV,kBAAkB,UAAU,QAAQ,GAAG;GACvC,aAAa,KAAK,iBAAiB;EACrC;CACF;CAGA,IAAI,SAAS;CACb,IAAI,IAAI,UACN,SAAS,iBAAiB,IAAI,UAAU,YAAY,KAAK,KAAK,KAAK;CACrE,IAAI,IAAI,QACN,SAAS,iBAAiB,IAAI,QAAQ,YAAY,KAAK,IAAI,KAAK;CAClE,IAAI,IAAI,KACN,SAAS,iBAAiB,IAAI,KAAK,YAAY,KAAK,KAAK,KAAK;CAChE,kBAAkB,IAAI,WAAW,YAAY,GAAG;CAChD,OAAO;AACT;;AAGA,SAAS,qBACP,KACA,YACA,KACM;CACN,MAAM,WAAW,IAAI,OAChB,mBAAmB,cAAc,IAAI,IAAI,MAAM,WAChD;CACJ,WAAW,KAAK;EAAE,MAAM;EAAU,MAAM;EAAU,kBAAkB;CAAK,CAAC;CAC1E,kBAAkB,IAAI,WAAW,YAAY,GAAG;AAClD;;AAGA,SAAS,qBACP,IACA,YACA,KACS;CACT,IAAI,SAAS;CACb,IAAI,GAAG,UACL,SAAS,iBAAiB,GAAG,UAAU,YAAY,KAAK,KAAK,KAAK;CACpE,IAAI,GAAG,QACL,SAAS,iBAAiB,GAAG,QAAQ,YAAY,KAAK,IAAI,KAAK;CACjE,IAAI,GAAG,KACL,SAAS,iBAAiB,GAAG,KAAK,YAAY,KAAK,KAAK,KAAK;CAC/D,kBAAkB,GAAG,WAAW,YAAY,GAAG;CAE/C,IAAI,GAAG,OACL,WAAW,KAAK;EACd,MAAM;EACN,MAAM;EACN,kBAAkB;CACpB,CAAC;CAIH,IAAI,GAAG,OAAO,KACZ,SAAS,qBAAqB,GAAG,MAAM,KAAK,YAAY,GAAG,KAAK;CAGlE,OAAO;AACT;;AAGA,SAAS,qBACP,KACA,YACA,KACS;CACT,MAAM,YAAY,cAAc,GAAG;CACnC,MAAM,QAAQ,IAAI,OAAO,OAAO,MAAM,MAAM,EAAE,SAAS,SAAS;CAChE,IAAI,CAAC,OAAO,OAAO;CAEnB,IAAI,SAAS;CACb,IAAI,MAAM,UACR,SAAS,iBAAiB,MAAM,UAAU,YAAY,KAAK,KAAK,KAAK;CACvE,IAAI,MAAM,QACR,SAAS,iBAAiB,MAAM,QAAQ,YAAY,KAAK,IAAI,KAAK;CACpE,IAAI,MAAM,KACR,SAAS,iBAAiB,MAAM,KAAK,YAAY,KAAK,KAAK,KAAK;CAClE,OAAO;AACT;AAUA,SAAS,iBACP,OACA,YACA,KACA,eACS;CACT,IAAI,SAAS;CAEb,KAAK,MAAM,MAAM,MAAM,WAAW,CAAC,GACjC,mBAAmB,IAAI,YAAY,KAAK,aAAa;CAGvD,KAAK,MAAM,OAAO,MAAM,YAAY,CAAC,GACnC,IAAI,iBAAiB,KAAK,YAAY,KAAK,aAAa,GAAG,SAAS;CAGtE,KAAK,MAAM,MAAM,MAAM,UAAU,CAAC,GAChC,IAAI,iBAAiB,IAAI,YAAY,KAAK,IAAI,GAAG,SAAS;CAG5D,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC,GAC9B,IAAI,EAAE,OAAO,kBAAkB,EAAE,KAAK,YAAY,KAAK,aAAa,GAClE,SAAS;CAKb,IAAI,MAAM,OAAO,MAAM,IAAI,SAAS,GAClC,SAAS;CAGX,OAAO;AACT;;AAGA,SAAS,kBACP,KACA,YACA,KACA,eACS;CACT,MAAM,YAAY,cAAc,GAAG;CACnC,MAAM,aAAa,IAAI,OAAO,OAAO,MAAM,OAAO,GAAG,SAAS,SAAS;CACvE,IAAI,CAAC,YAAY,OAAO;CAExB,IAAI,SAAS;CACb,IAAI,WAAW,UACb,SACE,iBAAiB,WAAW,UAAU,YAAY,KAAK,aAAa,KACpE;CACJ,IAAI,WAAW,QACb,SACE,iBAAiB,WAAW,QAAQ,YAAY,KAAK,IAAI,KAAK;CAClE,IAAI,WAAW,KACb,SACE,iBAAiB,WAAW,KAAK,YAAY,KAAK,aAAa,KAC/D;CACJ,OAAO;AACT;;;;AAKA,SAAS,mBACP,SACA,KACQ;CACR,IAAI,QAAQ,MACV,OAAO,gBAAgB,QAAQ,MAAM,GAAG;CAE1C,IAAI,QAAQ,aAEV,OAAO;CAET,IAAI,QAAQ,YAAY,aAAa,aACnC,OAAO,QAAQ,WAAW,YAAY,YACnC,KAAK,MAA0B,IAAI,EAAE,MAAM,EAAE,CAAC,CAC9C,KAAK,KAAK;CAEf,IAAI,QAAQ,YAAY,aAAa,MACnC,OACE,mBAAmB,cAAc,QAAQ,WAAW,YAAY,IAAI,MACpE;CAGJ,OAAO;AACT;;;;AAKA,SAAS,eACP,SACA,KAC0B;CAC1B,MAAM,QAAQ,IAAI,OAAO,SAAS,MAAM,MAAM,EAAE,SAAS,OAAO;CAChE,IAAI,OAAO,OAAO;CAClB,IAAI,IAAI,OAAO,UACb,KAAK,MAAM,YAAY,IAAI,OAAO,UAAU;EAC1C,MAAM,aAAa,SAAS,SAAS,MAAM,MAAM,EAAE,SAAS,OAAO;EACnE,IAAI,YAAY,OAAO;CACzB;AAGJ;AAEA,SAAS,mBACP,SACA,YACA,KACA,eACM;CAEN,IAAI,QAAQ,KAAK;EAEf,MAAM,aAAa,eADH,cAAc,QAAQ,GACE,GAAG,GAAG;EAC9C,IAAI,YACF,mBACE;GACE,GAAG;GACH,WAAW,QAAQ;GACnB,WAAW,QAAQ;EACrB,GACA,YACA,KACA,aACF;EAEF;CACF;CAEA,IAAI,CAAC,QAAQ,MAAM;CAEnB,MAAM,UACJ,QAAQ,cAAc,eACrB,OAAO,QAAQ,cAAc,YAAY,QAAQ,YAAY;CAChE,MAAM,aACJ,iBAAiB,QAAQ,cAAc,OAAO,QAAQ,cAAc;CAEtE,IAAI,SAAS,mBAAmB,SAAS,GAAG;CAE5C,IAAI,SACF,SAAS,GAAG,OAAO;CAGrB,WAAW,KAAK;EACd,MAAM,QAAQ;EACd,MAAM;EACN,kBAAkB;CACpB,CAAC;AACH;AAEA,SAAS,kBACP,YACA,YACA,KACM;CACN,IAAI,CAAC,YAAY;CAEjB,KAAK,MAAM,QAAQ,YAAY;EAC7B,IAAI,KAAK,KAAK;GAGZ,MAAM,UAAU,cAAc,KAAK,GAAG;GACtC,WAAW,KAAK;IACd,MAAM;IACN,MAAM;IACN,kBAAkB;GACpB,CAAC;GACD;EACF;EAEA,IAAI,CAAC,KAAK,MAAM;EAChB,IAAI,KAAK,QAAQ,cAAc;EAE/B,MAAM,aAAa,KAAK,QAAQ;EAChC,IAAI,SAAS;EAEb,IAAI,KAAK,MAAM;GACb,MAAM,WAAW,cAAc,KAAK,IAAI;GACxC,SAAS,mBAAmB,aAAa,gBAAgB,KAAK,MAAM,GAAG;EACzE;EAEA,WAAW,KAAK;GACd,MAAM,KAAK;GACX,MAAM;GACN,kBAAkB;EACpB,CAAC;CACH;AACF;AAMA,SAAS,mBACP,MACA,IACA,KACM;CACN,MAAM,WAAW,gBAAgB,IAAI;CACrC,IAAI,IAAI,eAAe,IAAI,QAAQ,GAAG;CACtC,IAAI,eAAe,IAAI,QAAQ;CAE/B,IAAI,SAAS;CAEb,IAAI,GAAG,aAAa,eAAe,GAAG,YAAY,YAAY,SAAS,GACrE,SAAS,GAAG,YAAY,YAAY,KAAK,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,KAAK;MACpE,IAAI,GAAG,aAAa,MACzB,SAAS,mBAAmB,cAAc,GAAG,YAAY,IAAI,MAAM;CAGrE,IAAI,WAAW,aAAa;EAC1B,MAAM;EACN,YAAY;EACZ,MAAM;EACN,MAAM,IAAI,WACN,CAAC,EAAE,aAAa,8BAA8B,OAAO,CAAC,IACtD;CACN,CAAC;AACH;AAMA,SAAS,iBAAiB,cAAsB,KAA6B;CAI3E,MAAM,oBAHW,IAAI,OAAO,WAAW,CAAC,EAGP,CAAC,QAAQ,OAAO,GAAG,QAAQ,CAAC,GAAG,QAAQ;CAExE,IAAI,iBAAiB,WAAW,GAAG;CAGnC,MAAM,eAAyB,CAAC;CAChC,MAAM,eAAyB,CAAC;CAEhC,KAAK,MAAM,MAAM,kBAAkB;EACjC,IAAI;EACJ,IAAI,GAAG,MACL,cAAc,gBAAgB,GAAG,MAAM,GAAG;OACrC,IAAI,GAAG,aAAa;GAEzB,MAAM,gBAAgB,gBAAgB,GAAG,IAAK;GAC9C,cAAc,IAAI,eAAe,IAAI,aAAa,IAC9C,gBACA;EACN,OACE,cAAc;EAEhB,aAAa,KAAK,WAAW;EAC7B,aAAa,KAAK,GAAG,IAAK;CAC5B;CAKA,MAAM,eAAe,aAAa,KAC/B,MAAM,MAAM,KAAK,aAAa,IAAI,EAAE,IAAI,aAAa,GAAG,GAC3D;CACA,MAAM,WACJ,aAAa,WAAW,IAAI,aAAa,KAAK,aAAa,KAAK,KAAK;CAEvE,MAAM,cACJ,aAAa,WAAW,IACpB,qBAAqB,aAAa,GAAG,aACrC,qBAAqB,aAAa,KAAK,KAAK,EAAE;CAEpD,IAAI,WAAW,aAAa;EAC1B,MAAM;EACN,YAAY;EACZ,MAAM;EACN,MAAM,IAAI,WAAW,CAAC,EAAE,YAAY,CAAC,IAAI;CAC3C,CAAC;AACH;;;;;;;ACloCA,SAAgB,mBAAmB,UAAuC;CACxE,IAAI,CAAC,UAAU,OAAO;CACtB,MAAM,WAAW,SAAS,QAAQ,UAAU,EAAE,CAAC,CAAC,QAAQ,SAAS,EAAE;CAEnE,OAAO,GADY,SAAS,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,SAAS,MAAM,CAAC,EACjD;AACvB;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,mBACd,QACA,UAAqC,CAAC,GACZ;CAC1B,MAAM,EAAE,SAAS,uBAAuB,GAAG,kBAAkB;CAG7D,MAAM,EAAE,SAAS,YAAY,iBAAiB,mBAC5C,QACA,aACF;CAGA,IAAI,WAAW,cAAc;EAC3B,MAAM,gBAAgB,YAAY,YAAY,cAAc;GAC1D;GACA,UAAU,GACR,OAAO,WAAW,QAAQ,QAAQ,EAAE,KAAK,SAC1C;EACH,CAAC;EAED,OAAO;GACL,MAAM,cAAc,YAAY;GAChC,YAAY;GACZ;GACA;EACF;CACF;CAGA,OAAO;EACL,MAAM,WAAW,YAAY;EAC7B;EACA;EACA;CACF;AACF;;;;;;ACqGA,SAAgB,aAAa,QAAsC;CACjE,OAAO;AACT"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generate-DQJ38rjv.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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"}
|