@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/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { _ as getLocalName, a as loadAndLinkSchema, c as getSubstitutes, d as SchemaTraverser, f as resolveSchemaTypes, g as getChildElements, h as getAttributeValue, i as linkSchema, l as resolveSchema, m as getAllChildElements, n as createSchemaLoader, o as loadSchema, p as parseXsd, r as defaultLoader, s as parseSchemaContent, t as runCodegen, u as SchemaResolver, v as getTextContent } from "./runner-BGNA8Sl-.mjs";
2
- import { n as generateSchemaLiteral, t as generateSchemaFile } from "./generate-DQJ38rjv.mjs";
3
- import { n as deriveRootTypeName, r as generateInterfaces, t as defineConfig } from "./types-CySWjUcM.mjs";
1
+ import { _ as getLocalName, a as loadAndLinkSchema, c as getSubstitutes, d as SchemaTraverser, f as resolveSchemaTypes, g as getChildElements, h as getAttributeValue, i as linkSchema, l as resolveSchema, m as getAllChildElements, n as createSchemaLoader, o as loadSchema, p as parseXsd, r as defaultLoader, s as parseSchemaContent, t as runCodegen, u as SchemaResolver, v as getTextContent } from "./runner-BZNOc_YZ.mjs";
2
+ import { n as generateSchemaLiteral, t as generateSchemaFile } from "./generate-BWLnwHUW.mjs";
3
+ import { n as deriveRootTypeName, r as generateInterfaces, t as defineConfig } from "./types-BAx8yQfm.mjs";
4
4
  import { DOMImplementation, DOMParser, XMLSerializer } from "@xmldom/xmldom";
5
5
 
6
6
  // src/xsd/build.ts
@@ -17,14 +17,12 @@ function buildXsd(schema, options = {}) {
17
17
  level: 0
18
18
  };
19
19
  const lines = [];
20
- lines.push("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
21
- lines.push(buildSchema(schema, ctx));
20
+ lines.push("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", buildSchema(schema, ctx));
22
21
  return lines.join(pretty ? "\n" : "");
23
22
  }
24
23
  function buildSchema(schema, ctx) {
25
24
  const attrs = [];
26
- if (schema.$xmlns) for (const [prefix, uri] of Object.entries(schema.$xmlns)) if (prefix === "") attrs.push(`xmlns="${uri}"`);
27
- else attrs.push(`xmlns:${prefix}="${uri}"`);
25
+ addXmlnsAttrs(attrs, schema.$xmlns);
28
26
  addAttr(attrs, "id", schema.id);
29
27
  addAttr(attrs, "targetNamespace", schema.targetNamespace);
30
28
  addAttr(attrs, "version", schema.version);
@@ -40,19 +38,19 @@ function buildSchema(schema, ctx) {
40
38
  ...ctx,
41
39
  level: ctx.level + 1
42
40
  };
43
- if (schema.annotation) for (const ann of schema.annotation) children.push(buildAnnotation(ann, childCtx));
44
- if (schema.include) for (const inc of schema.include) children.push(buildInclude(inc, childCtx));
45
- if (schema.import) for (const imp of schema.import) children.push(buildImport(imp, childCtx));
46
- if (schema.redefine) for (const red of schema.redefine) children.push(buildRedefine(red, childCtx));
47
- if (schema.override) for (const ovr of schema.override) children.push(buildOverride(ovr, childCtx));
48
- if (schema.defaultOpenContent) children.push(buildDefaultOpenContent(schema.defaultOpenContent, childCtx));
49
- if (schema.simpleType && Array.isArray(schema.simpleType)) for (const st of schema.simpleType) children.push(buildTopLevelSimpleType(st, childCtx));
50
- if (schema.complexType && Array.isArray(schema.complexType)) for (const ct of schema.complexType) children.push(buildTopLevelComplexType(ct, childCtx));
51
- if (schema.group) for (const grp of schema.group) children.push(buildNamedGroup(grp, childCtx));
52
- if (schema.attributeGroup) for (const ag of schema.attributeGroup) children.push(buildNamedAttributeGroup(ag, childCtx));
53
- if (schema.element) for (const el of schema.element) children.push(buildTopLevelElement(el, childCtx));
54
- if (schema.attribute) for (const attr of schema.attribute) children.push(buildTopLevelAttribute(attr, childCtx));
55
- if (schema.notation) for (const not of schema.notation) children.push(buildNotation(not, childCtx));
41
+ appendBuilt(children, schema.annotation, (ann) => buildAnnotation(ann, childCtx));
42
+ appendBuilt(children, schema.include, (inc) => buildInclude(inc, childCtx));
43
+ appendBuilt(children, schema.import, (imp) => buildImport(imp, childCtx));
44
+ appendBuilt(children, schema.redefine, (red) => buildRedefine(red, childCtx));
45
+ appendBuilt(children, schema.override, (ovr) => buildOverride(ovr, childCtx));
46
+ appendBuiltOne(children, schema.defaultOpenContent, (doc) => buildDefaultOpenContent(doc, childCtx));
47
+ appendBuilt(children, Array.isArray(schema.simpleType) ? schema.simpleType : [], (st) => buildTopLevelSimpleType(st, childCtx));
48
+ appendBuilt(children, Array.isArray(schema.complexType) ? schema.complexType : [], (ct) => buildTopLevelComplexType(ct, childCtx));
49
+ appendBuilt(children, schema.group, (grp) => buildNamedGroup(grp, childCtx));
50
+ appendBuilt(children, schema.attributeGroup, (ag) => buildNamedAttributeGroup(ag, childCtx));
51
+ appendBuilt(children, schema.element, (el) => buildTopLevelElement(el, childCtx));
52
+ appendBuilt(children, schema.attribute, (attr) => buildTopLevelAttribute(attr, childCtx));
53
+ appendBuilt(children, schema.notation, (not) => buildNotation(not, childCtx));
56
54
  return buildElement$1(`${ctx.prefix}:schema`, attrs, children, ctx);
57
55
  }
58
56
  function buildAnnotation(ann, ctx) {
@@ -128,14 +126,14 @@ function buildOverride(ovr, ctx) {
128
126
  ...ctx,
129
127
  level: ctx.level + 1
130
128
  };
131
- if (ovr.annotation) for (const ann of ovr.annotation) children.push(buildAnnotation(ann, childCtx));
132
- if (ovr.simpleType) for (const st of ovr.simpleType) children.push(buildTopLevelSimpleType(st, childCtx));
133
- if (ovr.complexType) for (const ct of ovr.complexType) children.push(buildTopLevelComplexType(ct, childCtx));
134
- if (ovr.group) for (const grp of ovr.group) children.push(buildNamedGroup(grp, childCtx));
135
- if (ovr.attributeGroup) for (const ag of ovr.attributeGroup) children.push(buildNamedAttributeGroup(ag, childCtx));
136
- if (ovr.element) for (const el of ovr.element) children.push(buildTopLevelElement(el, childCtx));
137
- if (ovr.attribute) for (const attr of ovr.attribute) children.push(buildTopLevelAttribute(attr, childCtx));
138
- if (ovr.notation) for (const not of ovr.notation) children.push(buildNotation(not, childCtx));
129
+ appendBuilt(children, ovr.annotation, (ann) => buildAnnotation(ann, childCtx));
130
+ appendBuilt(children, ovr.simpleType, (st) => buildTopLevelSimpleType(st, childCtx));
131
+ appendBuilt(children, ovr.complexType, (ct) => buildTopLevelComplexType(ct, childCtx));
132
+ appendBuilt(children, ovr.group, (grp) => buildNamedGroup(grp, childCtx));
133
+ appendBuilt(children, ovr.attributeGroup, (ag) => buildNamedAttributeGroup(ag, childCtx));
134
+ appendBuilt(children, ovr.element, (el) => buildTopLevelElement(el, childCtx));
135
+ appendBuilt(children, ovr.attribute, (attr) => buildTopLevelAttribute(attr, childCtx));
136
+ appendBuilt(children, ovr.notation, (not) => buildNotation(not, childCtx));
139
137
  return buildElement$1(`${ctx.prefix}:override`, attrs, children, ctx);
140
138
  }
141
139
  function buildTopLevelElement(el, ctx) {
@@ -253,22 +251,24 @@ function buildComplexTypeContent(ct, attrs, ctx) {
253
251
  ...ctx,
254
252
  level: ctx.level + 1
255
253
  };
256
- if (ct.annotation) children.push(buildAnnotation(ct.annotation, childCtx));
254
+ appendBuiltOne(children, ct.annotation, (ann) => buildAnnotation(ann, childCtx));
257
255
  if (ct.simpleContent) children.push(buildSimpleContent(ct.simpleContent, childCtx));
258
256
  else if (ct.complexContent) children.push(buildComplexContent(ct.complexContent, childCtx));
259
- else {
260
- if (ct.openContent) children.push(buildOpenContent(ct.openContent, childCtx));
261
- if (ct.group) children.push(buildGroupRef(ct.group, childCtx));
262
- if (ct.all) children.push(buildAll(ct.all, childCtx));
263
- if (ct.choice) children.push(buildExplicitGroup(ct.choice, "choice", childCtx));
264
- if (ct.sequence) children.push(buildExplicitGroup(ct.sequence, "sequence", childCtx));
265
- if (ct.attribute) for (const attr of ct.attribute) children.push(buildLocalAttribute(attr, childCtx));
266
- if (ct.attributeGroup) for (const ag of ct.attributeGroup) children.push(buildAttributeGroupRef(ag, childCtx));
267
- if (ct.anyAttribute) children.push(buildAnyAttribute(ct.anyAttribute, childCtx));
268
- if (ct.assert) for (const a of ct.assert) children.push(buildAssertion(a, childCtx));
269
- }
257
+ else buildComplexTypeShortForm(ct, children, childCtx);
270
258
  return buildElement$1(`${ctx.prefix}:complexType`, attrs, children, ctx);
271
259
  }
260
+ /** Short form: content model and attributes directly on the complexType. */
261
+ function buildComplexTypeShortForm(ct, children, childCtx) {
262
+ appendBuiltOne(children, ct.openContent, (oc) => buildOpenContent(oc, childCtx));
263
+ appendBuiltOne(children, ct.group, (grp) => buildGroupRef(grp, childCtx));
264
+ appendBuiltOne(children, ct.all, (all) => buildAll(all, childCtx));
265
+ appendBuiltOne(children, ct.choice, (c) => buildExplicitGroup(c, "choice", childCtx));
266
+ appendBuiltOne(children, ct.sequence, (s) => buildExplicitGroup(s, "sequence", childCtx));
267
+ appendBuilt(children, ct.attribute, (attr) => buildLocalAttribute(attr, childCtx));
268
+ appendBuilt(children, ct.attributeGroup, (ag) => buildAttributeGroupRef(ag, childCtx));
269
+ appendBuiltOne(children, ct.anyAttribute, (aa) => buildAnyAttribute(aa, childCtx));
270
+ appendBuilt(children, ct.assert, (a) => buildAssertion(a, childCtx));
271
+ }
272
272
  function buildTopLevelSimpleType(st, ctx) {
273
273
  const attrs = [];
274
274
  addAttr(attrs, "id", st.id);
@@ -308,20 +308,20 @@ function buildSimpleTypeRestriction(r, ctx) {
308
308
  return buildElement$1(`${ctx.prefix}:restriction`, attrs, children, ctx);
309
309
  }
310
310
  function buildFacets(r, children, ctx) {
311
- if (r.minExclusive) for (const f of r.minExclusive) children.push(buildFacet(f, "minExclusive", ctx));
312
- if (r.minInclusive) for (const f of r.minInclusive) children.push(buildFacet(f, "minInclusive", ctx));
313
- if (r.maxExclusive) for (const f of r.maxExclusive) children.push(buildFacet(f, "maxExclusive", ctx));
314
- if (r.maxInclusive) for (const f of r.maxInclusive) children.push(buildFacet(f, "maxInclusive", ctx));
315
- if (r.totalDigits) for (const f of r.totalDigits) children.push(buildFacet(f, "totalDigits", ctx));
316
- if (r.fractionDigits) for (const f of r.fractionDigits) children.push(buildFacet(f, "fractionDigits", ctx));
317
- if (r.length) for (const f of r.length) children.push(buildFacet(f, "length", ctx));
318
- if (r.minLength) for (const f of r.minLength) children.push(buildFacet(f, "minLength", ctx));
319
- if (r.maxLength) for (const f of r.maxLength) children.push(buildFacet(f, "maxLength", ctx));
320
- if (r.enumeration) for (const f of r.enumeration) children.push(buildFacet(f, "enumeration", ctx));
321
- if (r.whiteSpace) for (const f of r.whiteSpace) children.push(buildFacet(f, "whiteSpace", ctx));
322
- if (r.pattern) for (const p of r.pattern) children.push(buildPattern(p, ctx));
323
- if (r.assertion) for (const a of r.assertion) children.push(buildAssertion(a, ctx));
324
- if (r.explicitTimezone) for (const f of r.explicitTimezone) children.push(buildFacet(f, "explicitTimezone", ctx));
311
+ appendBuilt(children, r.minExclusive, (f) => buildFacet(f, "minExclusive", ctx));
312
+ appendBuilt(children, r.minInclusive, (f) => buildFacet(f, "minInclusive", ctx));
313
+ appendBuilt(children, r.maxExclusive, (f) => buildFacet(f, "maxExclusive", ctx));
314
+ appendBuilt(children, r.maxInclusive, (f) => buildFacet(f, "maxInclusive", ctx));
315
+ appendBuilt(children, r.totalDigits, (f) => buildFacet(f, "totalDigits", ctx));
316
+ appendBuilt(children, r.fractionDigits, (f) => buildFacet(f, "fractionDigits", ctx));
317
+ appendBuilt(children, r.length, (f) => buildFacet(f, "length", ctx));
318
+ appendBuilt(children, r.minLength, (f) => buildFacet(f, "minLength", ctx));
319
+ appendBuilt(children, r.maxLength, (f) => buildFacet(f, "maxLength", ctx));
320
+ appendBuilt(children, r.enumeration, (f) => buildFacet(f, "enumeration", ctx));
321
+ appendBuilt(children, r.whiteSpace, (f) => buildFacet(f, "whiteSpace", ctx));
322
+ appendBuilt(children, r.pattern, (p) => buildPattern(p, ctx));
323
+ appendBuilt(children, r.assertion, (a) => buildAssertion(a, ctx));
324
+ appendBuilt(children, r.explicitTimezone, (f) => buildFacet(f, "explicitTimezone", ctx));
325
325
  }
326
326
  function buildFacet(f, name, ctx) {
327
327
  const attrs = [];
@@ -408,16 +408,16 @@ function buildComplexContentRestriction(r, ctx) {
408
408
  ...ctx,
409
409
  level: ctx.level + 1
410
410
  };
411
- if (r.annotation) children.push(buildAnnotation(r.annotation, childCtx));
412
- if (r.openContent) children.push(buildOpenContent(r.openContent, childCtx));
413
- if (r.group) children.push(buildGroupRef(r.group, childCtx));
414
- if (r.all) children.push(buildAll(r.all, childCtx));
415
- if (r.choice) children.push(buildExplicitGroup(r.choice, "choice", childCtx));
416
- if (r.sequence) children.push(buildExplicitGroup(r.sequence, "sequence", childCtx));
417
- if (r.attribute) for (const attr of r.attribute) children.push(buildLocalAttribute(attr, childCtx));
418
- if (r.attributeGroup) for (const ag of r.attributeGroup) children.push(buildAttributeGroupRef(ag, childCtx));
419
- if (r.anyAttribute) children.push(buildAnyAttribute(r.anyAttribute, childCtx));
420
- if (r.assert) for (const a of r.assert) children.push(buildAssertion(a, childCtx));
411
+ appendBuiltOne(children, r.annotation, (ann) => buildAnnotation(ann, childCtx));
412
+ appendBuiltOne(children, r.openContent, (oc) => buildOpenContent(oc, childCtx));
413
+ appendBuiltOne(children, r.group, (grp) => buildGroupRef(grp, childCtx));
414
+ appendBuiltOne(children, r.all, (all) => buildAll(all, childCtx));
415
+ appendBuiltOne(children, r.choice, (c) => buildExplicitGroup(c, "choice", childCtx));
416
+ appendBuiltOne(children, r.sequence, (s) => buildExplicitGroup(s, "sequence", childCtx));
417
+ appendBuilt(children, r.attribute, (attr) => buildLocalAttribute(attr, childCtx));
418
+ appendBuilt(children, r.attributeGroup, (ag) => buildAttributeGroupRef(ag, childCtx));
419
+ appendBuiltOne(children, r.anyAttribute, (aa) => buildAnyAttribute(aa, childCtx));
420
+ appendBuilt(children, r.assert, (a) => buildAssertion(a, childCtx));
421
421
  return buildElement$1(`${ctx.prefix}:restriction`, attrs, children, ctx);
422
422
  }
423
423
  function buildComplexContentExtension(e, ctx) {
@@ -429,16 +429,16 @@ function buildComplexContentExtension(e, ctx) {
429
429
  ...ctx,
430
430
  level: ctx.level + 1
431
431
  };
432
- if (e.annotation) children.push(buildAnnotation(e.annotation, childCtx));
433
- if (e.openContent) children.push(buildOpenContent(e.openContent, childCtx));
434
- if (e.group) children.push(buildGroupRef(e.group, childCtx));
435
- if (e.all) children.push(buildAll(e.all, childCtx));
436
- if (e.choice) children.push(buildExplicitGroup(e.choice, "choice", childCtx));
437
- if (e.sequence) children.push(buildExplicitGroup(e.sequence, "sequence", childCtx));
438
- if (e.attribute) for (const attr of e.attribute) children.push(buildLocalAttribute(attr, childCtx));
439
- if (e.attributeGroup) for (const ag of e.attributeGroup) children.push(buildAttributeGroupRef(ag, childCtx));
440
- if (e.anyAttribute) children.push(buildAnyAttribute(e.anyAttribute, childCtx));
441
- if (e.assert) for (const a of e.assert) children.push(buildAssertion(a, childCtx));
432
+ appendBuiltOne(children, e.annotation, (ann) => buildAnnotation(ann, childCtx));
433
+ appendBuiltOne(children, e.openContent, (oc) => buildOpenContent(oc, childCtx));
434
+ appendBuiltOne(children, e.group, (grp) => buildGroupRef(grp, childCtx));
435
+ appendBuiltOne(children, e.all, (all) => buildAll(all, childCtx));
436
+ appendBuiltOne(children, e.choice, (c) => buildExplicitGroup(c, "choice", childCtx));
437
+ appendBuiltOne(children, e.sequence, (s) => buildExplicitGroup(s, "sequence", childCtx));
438
+ appendBuilt(children, e.attribute, (attr) => buildLocalAttribute(attr, childCtx));
439
+ appendBuilt(children, e.attributeGroup, (ag) => buildAttributeGroupRef(ag, childCtx));
440
+ appendBuiltOne(children, e.anyAttribute, (aa) => buildAnyAttribute(aa, childCtx));
441
+ appendBuilt(children, e.assert, (a) => buildAssertion(a, childCtx));
442
442
  return buildElement$1(`${ctx.prefix}:extension`, attrs, children, ctx);
443
443
  }
444
444
  function buildSimpleContentRestriction(r, ctx) {
@@ -485,12 +485,12 @@ function buildExplicitGroup(g, name, ctx) {
485
485
  ...ctx,
486
486
  level: ctx.level + 1
487
487
  };
488
- if (g.annotation) children.push(buildAnnotation(g.annotation, childCtx));
489
- if (g.element) for (const el of g.element) children.push(buildLocalElement(el, childCtx));
490
- if (g.group) for (const grp of g.group) children.push(buildGroupRef(grp, childCtx));
491
- if (g.choice) for (const c of g.choice) children.push(buildExplicitGroup(c, "choice", childCtx));
492
- if (g.sequence) for (const s of g.sequence) children.push(buildExplicitGroup(s, "sequence", childCtx));
493
- if (g.any) for (const a of g.any) children.push(buildAny(a, childCtx));
488
+ appendBuiltOne(children, g.annotation, (ann) => buildAnnotation(ann, childCtx));
489
+ appendBuilt(children, g.element, (el) => buildLocalElement(el, childCtx));
490
+ appendBuilt(children, g.group, (grp) => buildGroupRef(grp, childCtx));
491
+ appendBuilt(children, g.choice, (c) => buildExplicitGroup(c, "choice", childCtx));
492
+ appendBuilt(children, g.sequence, (s) => buildExplicitGroup(s, "sequence", childCtx));
493
+ appendBuilt(children, g.any, (a) => buildAny(a, childCtx));
494
494
  return buildElement$1(`${ctx.prefix}:${name}`, attrs, children, ctx);
495
495
  }
496
496
  function buildAll(a, ctx) {
@@ -731,17 +731,34 @@ function buildNotation(n, ctx) {
731
731
  }));
732
732
  return buildElement$1(`${ctx.prefix}:notation`, attrs, children, ctx);
733
733
  }
734
+ /** Add xmlns:* namespace declaration attributes. */
735
+ function addXmlnsAttrs(attrs, xmlns) {
736
+ if (!xmlns) return;
737
+ for (const [prefix, uri] of Object.entries(xmlns)) if (prefix === "") attrs.push(`xmlns="${uri}"`);
738
+ else attrs.push(`xmlns:${prefix}="${uri}"`);
739
+ }
740
+ /** Append one built child element per item, skipped when `items` is absent. */
741
+ function appendBuilt(children, items, build) {
742
+ if (items) for (const item of items) children.push(build(item));
743
+ }
744
+ /** Append a single built child element when `item` is present. */
745
+ function appendBuiltOne(children, item, build) {
746
+ if (item) children.push(build(item));
747
+ }
734
748
  function addAttr(attrs, name, value) {
735
749
  if (value !== void 0) attrs.push(`${name}="${escapeXml(value)}"`);
736
750
  }
737
- function addBoolAttr(attrs, name, value) {
751
+ function addPlainAttr(attrs, name, value) {
738
752
  if (value !== void 0) attrs.push(`${name}="${value}"`);
739
753
  }
754
+ function addBoolAttr(attrs, name, value) {
755
+ addPlainAttr(attrs, name, value);
756
+ }
740
757
  function addOccursAttr(attrs, name, value) {
741
- if (value !== void 0) attrs.push(`${name}="${value}"`);
758
+ addPlainAttr(attrs, name, value);
742
759
  }
743
760
  function escapeXml(str) {
744
- return str.replaceAll(/&/g, "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
761
+ return str.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
745
762
  }
746
763
  function buildElement$1(tag, attrs, children, ctx) {
747
764
  const indent = ctx.pretty ? ctx.indent.repeat(ctx.level) : "";
@@ -823,67 +840,86 @@ function linkSchemas(schemas) {
823
840
  * ```
824
841
  */
825
842
  function* walkComplexTypes(schema, options = {}) {
843
+ yield* ownComplexTypes(schema);
844
+ yield* redefineComplexTypes(schema);
845
+ yield* overrideComplexTypes(schema);
846
+ yield* walkNestedSchemas(schema, options, walkComplexTypes);
847
+ }
848
+ /** Yield complexTypes declared directly in the schema. */
849
+ function* ownComplexTypes(schema) {
826
850
  const complexTypes = schema.complexType;
827
- if (complexTypes) {
828
- if (Array.isArray(complexTypes)) for (const ct of complexTypes) yield {
829
- ct,
830
- schema
831
- };
832
- else for (const [name, ct] of Object.entries(complexTypes)) yield {
833
- ct: {
834
- ...ct,
835
- name
836
- },
837
- schema
838
- };
839
- }
840
- if (schema.redefine) {
841
- for (const redef of schema.redefine) if (redef.complexType) for (const ct of redef.complexType) yield {
842
- ct,
843
- schema
844
- };
845
- }
846
- if (schema.override) {
847
- for (const ovr of schema.override) if (ovr.complexType) for (const ct of ovr.complexType) yield {
848
- ct,
849
- schema
850
- };
851
- }
852
- if (schema.$includes) for (const included of schema.$includes) yield* walkComplexTypes(included, options);
853
- if (!options.includesOnly && schema.$imports) for (const imported of schema.$imports) yield* walkComplexTypes(imported, options);
851
+ if (!complexTypes) return;
852
+ if (Array.isArray(complexTypes)) for (const ct of complexTypes) yield {
853
+ ct,
854
+ schema
855
+ };
856
+ else for (const [name, ct] of Object.entries(complexTypes)) yield {
857
+ ct: {
858
+ ...ct,
859
+ name
860
+ },
861
+ schema
862
+ };
863
+ }
864
+ /** Yield complexTypes from redefine blocks. */
865
+ function* redefineComplexTypes(schema) {
866
+ for (const redef of schema.redefine ?? []) for (const ct of redef.complexType ?? []) yield {
867
+ ct,
868
+ schema
869
+ };
870
+ }
871
+ /** Yield complexTypes from override blocks (XSD 1.1). */
872
+ function* overrideComplexTypes(schema) {
873
+ for (const ovr of schema.override ?? []) for (const ct of ovr.complexType ?? []) yield {
874
+ ct,
875
+ schema
876
+ };
877
+ }
878
+ /**
879
+ * Recurse into $includes (same namespace), then $imports (different namespace).
880
+ */
881
+ function* walkNestedSchemas(schema, options, walker) {
882
+ if (schema.$includes) for (const included of schema.$includes) yield* walker(included, options);
883
+ if (!options.includesOnly && schema.$imports) for (const imported of schema.$imports) yield* walker(imported, options);
854
884
  }
855
885
  /**
856
886
  * Walk all simpleTypes in a schema and its $imports/$includes.
857
887
  */
858
888
  function* walkSimpleTypes(schema, options = {}) {
889
+ yield* ownSimpleTypes(schema);
890
+ yield* redefineSimpleTypes(schema);
891
+ yield* overrideSimpleTypes(schema);
892
+ yield* walkNestedSchemas(schema, options, walkSimpleTypes);
893
+ }
894
+ /** Yield simpleTypes declared directly in the schema. */
895
+ function* ownSimpleTypes(schema) {
859
896
  const simpleTypes = schema.simpleType;
860
- if (simpleTypes) {
861
- if (Array.isArray(simpleTypes)) for (const st of simpleTypes) yield {
862
- st,
863
- schema
864
- };
865
- else for (const [name, st] of Object.entries(simpleTypes)) yield {
866
- st: {
867
- ...st,
868
- name
869
- },
870
- schema
871
- };
872
- }
873
- if (schema.redefine) {
874
- for (const redef of schema.redefine) if (redef.simpleType) for (const st of redef.simpleType) yield {
875
- st,
876
- schema
877
- };
878
- }
879
- if (schema.override) {
880
- for (const ovr of schema.override) if (ovr.simpleType) for (const st of ovr.simpleType) yield {
881
- st,
882
- schema
883
- };
884
- }
885
- if (schema.$includes) for (const included of schema.$includes) yield* walkSimpleTypes(included, options);
886
- if (!options.includesOnly && schema.$imports) for (const imported of schema.$imports) yield* walkSimpleTypes(imported, options);
897
+ if (!simpleTypes) return;
898
+ if (Array.isArray(simpleTypes)) for (const st of simpleTypes) yield {
899
+ st,
900
+ schema
901
+ };
902
+ else for (const [name, st] of Object.entries(simpleTypes)) yield {
903
+ st: {
904
+ ...st,
905
+ name
906
+ },
907
+ schema
908
+ };
909
+ }
910
+ /** Yield simpleTypes from redefine blocks. */
911
+ function* redefineSimpleTypes(schema) {
912
+ for (const redef of schema.redefine ?? []) for (const st of redef.simpleType ?? []) yield {
913
+ st,
914
+ schema
915
+ };
916
+ }
917
+ /** Yield simpleTypes from override blocks (XSD 1.1). */
918
+ function* overrideSimpleTypes(schema) {
919
+ for (const ovr of schema.override ?? []) for (const st of ovr.simpleType ?? []) yield {
920
+ st,
921
+ schema
922
+ };
887
923
  }
888
924
  /**
889
925
  * Walk all top-level elements in a schema and its $imports/$includes.
@@ -960,46 +996,52 @@ function* walkGroup(group, source, schema, parentOptional = false, parentArray =
960
996
  const groupOptional = parentOptional || group.minOccurs === 0 || group.minOccurs === "0";
961
997
  const groupArray = parentArray || isArray(group);
962
998
  const choiceOptional = source === "choice";
963
- if (group.element) for (const element of group.element) {
964
- const optional = choiceOptional || groupOptional || element.minOccurs === 0 || element.minOccurs === "0";
965
- const array = groupArray || isArray(element);
966
- if (element.ref) {
967
- const refName = stripNsPrefix(element.ref);
968
- const refEntry = findElement(refName, schema);
969
- if (refEntry && isAbstractElement(refEntry.element)) {
970
- const substitutes = findSubstitutes(refName, schema);
971
- if (substitutes.length > 0) {
972
- for (const sub of substitutes) yield {
973
- element: sub,
974
- optional,
975
- array,
976
- source,
977
- schema
978
- };
979
- continue;
980
- }
981
- }
982
- }
983
- yield {
984
- element,
999
+ for (const element of group.element ?? []) yield* elementEntries(element, choiceOptional || groupOptional || element.minOccurs === 0 || element.minOccurs === "0", groupArray || isArray(element), source, schema);
1000
+ for (const nested of asGroupArray(group.sequence)) yield* walkGroup(nested, "sequence", schema, groupOptional, groupArray);
1001
+ for (const nested of asGroupArray(group.choice)) yield* walkGroup(nested, "choice", schema, true, groupArray || isArray(nested));
1002
+ for (const ref of asGroupArray(group.group)) if (ref.ref) yield* walkGroupRef(ref.ref, schema, groupOptional || choiceOptional, groupArray || isArray(ref));
1003
+ }
1004
+ /** Normalize a possibly single group value to an array. */
1005
+ function asGroupArray(value) {
1006
+ if (value == null) return [];
1007
+ return Array.isArray(value) ? value : [value];
1008
+ }
1009
+ /**
1010
+ * Yield the entries for one direct group element: substitutes of an abstract
1011
+ * element ref when present, otherwise the element itself.
1012
+ */
1013
+ function* elementEntries(element, optional, array, source, schema) {
1014
+ const substitutes = abstractSubstitutes(element, schema);
1015
+ if (substitutes) {
1016
+ for (const sub of substitutes) yield {
1017
+ element: sub,
985
1018
  optional,
986
1019
  array,
987
1020
  source,
988
1021
  schema
989
1022
  };
1023
+ return;
990
1024
  }
991
- if (group.sequence) {
992
- const sequences = Array.isArray(group.sequence) ? group.sequence : [group.sequence];
993
- for (const nested of sequences) yield* walkGroup(nested, "sequence", schema, groupOptional, groupArray);
994
- }
995
- if (group.choice) {
996
- const choices = Array.isArray(group.choice) ? group.choice : [group.choice];
997
- for (const nested of choices) yield* walkGroup(nested, "choice", schema, true, groupArray || isArray(nested));
998
- }
999
- if (group.group) {
1000
- const refs = Array.isArray(group.group) ? group.group : [group.group];
1001
- for (const ref of refs) if (ref.ref) yield* walkGroupRef(ref.ref, schema, groupOptional || choiceOptional, groupArray || isArray(ref));
1002
- }
1025
+ yield {
1026
+ element,
1027
+ optional,
1028
+ array,
1029
+ source,
1030
+ schema
1031
+ };
1032
+ }
1033
+ /**
1034
+ * Substitutes for an element ref to an abstract element, or undefined when
1035
+ * the element needs no substitution handling. No substitutes in this schema
1036
+ * yields undefined so the parser can handle substitution via the root schema.
1037
+ */
1038
+ function abstractSubstitutes(element, schema) {
1039
+ if (!element.ref) return void 0;
1040
+ const refName = stripNsPrefix(element.ref);
1041
+ const refEntry = findElement(refName, schema);
1042
+ if (!refEntry || !isAbstractElement(refEntry.element)) return void 0;
1043
+ const substitutes = findSubstitutes(refName, schema);
1044
+ return substitutes.length > 0 ? substitutes : void 0;
1003
1045
  }
1004
1046
  /**
1005
1047
  * Walk elements from a group reference (xs:group ref="...").
@@ -1023,35 +1065,29 @@ function* walkGroupRef(ref, schema, parentOptional = false, parentArray = false)
1023
1065
  function* walkAttributes(ct, schema) {
1024
1066
  if (ct.complexContent?.extension) {
1025
1067
  const ext = ct.complexContent.extension;
1026
- if (ext.base) {
1027
- const baseEntry = findComplexType(stripNsPrefix(ext.base), schema);
1028
- if (baseEntry) yield* walkAttributes(baseEntry.ct, baseEntry.schema);
1029
- }
1030
- if (ext.attribute) for (const attr of ext.attribute) {
1031
- const resolved = resolveAttribute(attr, schema);
1032
- if (resolved) yield {
1033
- attribute: resolved,
1034
- required: resolved.use === "required",
1035
- schema
1036
- };
1037
- }
1068
+ yield* walkBaseAttributes(ext.base, schema);
1069
+ yield* resolvedAttributes(ext.attribute, schema);
1038
1070
  yield* walkAttributeGroupRefs(ext.attributeGroup, schema);
1039
1071
  return;
1040
1072
  }
1041
1073
  if (ct.simpleContent?.extension) {
1042
1074
  const ext = ct.simpleContent.extension;
1043
- if (ext.attribute) for (const attr of ext.attribute) {
1044
- const resolved = resolveAttribute(attr, schema);
1045
- if (resolved) yield {
1046
- attribute: resolved,
1047
- required: resolved.use === "required",
1048
- schema
1049
- };
1050
- }
1075
+ yield* resolvedAttributes(ext.attribute, schema);
1051
1076
  yield* walkAttributeGroupRefs(ext.attributeGroup, schema);
1052
1077
  return;
1053
1078
  }
1054
- if (ct.attribute) for (const attr of ct.attribute) {
1079
+ yield* resolvedAttributes(ct.attribute, schema);
1080
+ yield* walkAttributeGroupRefs(ct.attributeGroup, schema);
1081
+ }
1082
+ /** Yield attributes inherited from the base type of an extension. */
1083
+ function* walkBaseAttributes(base, schema) {
1084
+ if (!base) return;
1085
+ const baseEntry = findComplexType(stripNsPrefix(base), schema);
1086
+ if (baseEntry) yield* walkAttributes(baseEntry.ct, baseEntry.schema);
1087
+ }
1088
+ /** Yield declared attributes with attribute refs resolved. */
1089
+ function* resolvedAttributes(attributes, schema) {
1090
+ for (const attr of attributes ?? []) {
1055
1091
  const resolved = resolveAttribute(attr, schema);
1056
1092
  if (resolved) yield {
1057
1093
  attribute: resolved,
@@ -1059,7 +1095,6 @@ function* walkAttributes(ct, schema) {
1059
1095
  schema
1060
1096
  };
1061
1097
  }
1062
- yield* walkAttributeGroupRefs(ct.attributeGroup, schema);
1063
1098
  }
1064
1099
  /**
1065
1100
  * Walk attributes from attributeGroup references.
@@ -1079,38 +1114,41 @@ function* walkAttributeGroupRefs(refs, schema) {
1079
1114
  }
1080
1115
  }
1081
1116
  /**
1117
+ * Search a name in the current schema's declarations, then in $includes
1118
+ * (same namespace) and $imports (different namespace), depth-first.
1119
+ */
1120
+ function findInSchemas(name, schema, searchOwn) {
1121
+ const found = searchOwn(name, schema);
1122
+ if (found) return found;
1123
+ const linkedSchemas = [];
1124
+ if (schema.$includes) linkedSchemas.push(...schema.$includes);
1125
+ if (schema.$imports) linkedSchemas.push(...schema.$imports);
1126
+ for (const linked of linkedSchemas) {
1127
+ const result = findInSchemas(name, linked, searchOwn);
1128
+ if (result) return result;
1129
+ }
1130
+ }
1131
+ /**
1082
1132
  * Find a named group (xs:group) by name.
1083
1133
  */
1084
1134
  function findGroup(name, schema) {
1085
- if (schema.group) {
1086
- for (const g of schema.group) if (g.name === name) return g;
1087
- }
1088
- if (schema.$includes) for (const included of schema.$includes) {
1089
- const found = findGroup(name, included);
1090
- if (found) return found;
1091
- }
1092
- if (schema.$imports) for (const imported of schema.$imports) {
1093
- const found = findGroup(name, imported);
1094
- if (found) return found;
1095
- }
1135
+ return findInSchemas(name, schema, findGroupInSchema);
1136
+ }
1137
+ /** Search a named group in a single schema's declarations. */
1138
+ function findGroupInSchema(name, schema) {
1139
+ for (const g of schema.group ?? []) if (g.name === name) return g;
1096
1140
  }
1097
1141
  /**
1098
1142
  * Find a top-level attribute by name.
1099
1143
  * Searches current schema and $imports.
1100
1144
  */
1101
1145
  function findAttribute(name, schema) {
1146
+ return findInSchemas(name, schema, findAttributeInSchema);
1147
+ }
1148
+ /** Search a top-level attribute in a single schema's declarations. */
1149
+ function findAttributeInSchema(name, schema) {
1102
1150
  const schemaWithAttrs = schema;
1103
- if (schemaWithAttrs.attribute) {
1104
- for (const attr of schemaWithAttrs.attribute) if (attr.name === name) return attr;
1105
- }
1106
- if (schema.$includes) for (const included of schema.$includes) {
1107
- const found = findAttribute(name, included);
1108
- if (found) return found;
1109
- }
1110
- if (schema.$imports) for (const imported of schema.$imports) {
1111
- const found = findAttribute(name, imported);
1112
- if (found) return found;
1113
- }
1151
+ for (const attr of schemaWithAttrs.attribute ?? []) if (attr.name === name) return attr;
1114
1152
  }
1115
1153
  /**
1116
1154
  * Resolve an attribute - handles both direct attributes and refs.
@@ -1136,17 +1174,11 @@ function resolveAttribute(attribute, schema) {
1136
1174
  * Find an attributeGroup by name.
1137
1175
  */
1138
1176
  function findAttributeGroup(name, schema) {
1139
- if (schema.attributeGroup) {
1140
- for (const g of schema.attributeGroup) if (g.name === name) return g;
1141
- }
1142
- if (schema.$includes) for (const included of schema.$includes) {
1143
- const found = findAttributeGroup(name, included);
1144
- if (found) return found;
1145
- }
1146
- if (schema.$imports) for (const imported of schema.$imports) {
1147
- const found = findAttributeGroup(name, imported);
1148
- if (found) return found;
1149
- }
1177
+ return findInSchemas(name, schema, findAttributeGroupInSchema);
1178
+ }
1179
+ /** Search an attributeGroup in a single schema's declarations. */
1180
+ function findAttributeGroupInSchema(name, schema) {
1181
+ for (const g of schema.attributeGroup ?? []) if (g.name === name) return g;
1150
1182
  }
1151
1183
  /**
1152
1184
  * Strip namespace prefix from a QName (e.g., "tns:PersonType" -> "PersonType")
@@ -1323,26 +1355,51 @@ function findElementByName(schema, name) {
1323
1355
  function parseElement(node, typeDef, schema, rootSchema) {
1324
1356
  const result = {};
1325
1357
  if (typeDef.simpleContent?.extension) return parseSimpleContent(node, typeDef, schema, rootSchema);
1358
+ parseAttributes(node, typeDef, schema, result);
1359
+ parseChildElements(node, typeDef, schema, rootSchema, result);
1360
+ if (hasWildcard(typeDef, schema)) parseWildcardChildren(node, result);
1361
+ return result;
1362
+ }
1363
+ /**
1364
+ * Parse declared attributes of an element into the result object.
1365
+ */
1366
+ function parseAttributes(node, typeDef, schema, result) {
1326
1367
  for (const { attribute } of walkAttributes(typeDef, schema)) {
1327
1368
  if (!attribute.name) continue;
1369
+ const type = attribute.type || "string";
1328
1370
  const value = getAttributeValue(node, attribute.name);
1329
- if (value !== null) result[attribute.name] = convertValue(value, attribute.type || "string");
1330
- else if (attribute.default !== void 0) result[attribute.name] = convertValue(String(attribute.default), attribute.type || "string");
1371
+ if (value !== null) {
1372
+ result[attribute.name] = convertValue(value, type);
1373
+ continue;
1374
+ }
1375
+ if (attribute.default !== void 0) result[attribute.name] = convertValue(String(attribute.default), type);
1331
1376
  }
1377
+ }
1378
+ /**
1379
+ * Parse declared child elements of an element into the result object.
1380
+ */
1381
+ function parseChildElements(node, typeDef, schema, rootSchema, result) {
1332
1382
  for (const { element, array } of walkElements(typeDef, schema)) {
1333
1383
  const resolved = resolveElementInfo$1(element, schema);
1334
1384
  if (!resolved) continue;
1335
- if (isAbstractElement$1(resolved.name, rootSchema)) parseSubstitutionChildren(node, resolved.name, rootSchema, result);
1336
- else {
1337
- const children = getChildElements(node, resolved.name);
1338
- if (array || children.length > 1) {
1339
- const values = children.map((child) => parseChildValue(child, resolved.typeName, schema, rootSchema));
1340
- if (values.length > 0) result[resolved.name] = values;
1341
- } else if (children.length === 1) result[resolved.name] = parseChildValue(children[0], resolved.typeName, schema, rootSchema);
1385
+ if (isAbstractElement$1(resolved.name, rootSchema)) {
1386
+ parseSubstitutionChildren(node, resolved.name, rootSchema, result);
1387
+ continue;
1342
1388
  }
1389
+ parseRegularChildren(node, resolved, array, schema, rootSchema, result);
1343
1390
  }
1344
- if (hasWildcard(typeDef, schema)) parseWildcardChildren(node, result);
1345
- return result;
1391
+ }
1392
+ /**
1393
+ * Parse non-abstract declared children: arrays and single elements.
1394
+ */
1395
+ function parseRegularChildren(node, resolved, array, schema, rootSchema, result) {
1396
+ const children = getChildElements(node, resolved.name);
1397
+ if (array || children.length > 1) {
1398
+ const values = children.map((child) => parseChildValue(child, resolved.typeName, schema, rootSchema));
1399
+ if (values.length > 0) result[resolved.name] = values;
1400
+ return;
1401
+ }
1402
+ if (children.length === 1) result[resolved.name] = parseChildValue(children[0], resolved.typeName, schema, rootSchema);
1346
1403
  }
1347
1404
  /**
1348
1405
  * Parse children that substitute for an abstract element — substitutes can
@@ -1353,7 +1410,7 @@ function parseSubstitutionChildren(node, headName, rootSchema, result) {
1353
1410
  for (const child of getAllChildElements(node)) {
1354
1411
  const childName = getLocalName(child);
1355
1412
  const substitute = substitutes.find((s) => s.element.name === childName);
1356
- if (substitute && substitute.element.name) {
1413
+ if (substitute?.element.name) {
1357
1414
  const subTypeName = substitute.element.type ? stripNsPrefix(substitute.element.type) : void 0;
1358
1415
  result[substitute.element.name] = parseChildValue(child, subTypeName, substitute.schema, rootSchema);
1359
1416
  }
@@ -1383,10 +1440,7 @@ function parseWildcardChildren(node, result) {
1383
1440
  */
1384
1441
  function parseAnyValue(node) {
1385
1442
  const result = {};
1386
- for (let i = 0; i < node.attributes.length; i++) {
1387
- const attr = node.attributes[i];
1388
- result[`@${attr.name}`] = attr.value;
1389
- }
1443
+ for (const attr of node.attributes) result[`@${attr.name}`] = attr.value;
1390
1444
  const children = getAllChildElements(node);
1391
1445
  if (children.length === 0) {
1392
1446
  if (Object.keys(result).length === 0) return getTextContent(node);
@@ -1607,11 +1661,15 @@ function formatValue(value, type) {
1607
1661
  * Only adds newlines for nested elements
1608
1662
  */
1609
1663
  function formatXml(xml) {
1610
- let formatted = "";
1611
- let indent = 0;
1664
+ return renderXmlParts(tokenizeXml(xml)).trim();
1665
+ }
1666
+ /**
1667
+ * Tokenize XML into tags and text using linear scan (avoids regex backtracking).
1668
+ */
1669
+ function tokenizeXml(xml) {
1612
1670
  const parts = [];
1613
1671
  let pos = 0;
1614
- while (pos < xml.length) if (xml.charCodeAt(pos) === 60) {
1672
+ while (pos < xml.length) if (xml.codePointAt(pos) === 60) {
1615
1673
  const end = xml.indexOf(">", pos + 1);
1616
1674
  if (end === -1) {
1617
1675
  parts.push(xml.slice(pos));
@@ -1628,6 +1686,14 @@ function formatXml(xml) {
1628
1686
  if (next > pos) parts.push(xml.slice(pos, next));
1629
1687
  pos = next;
1630
1688
  }
1689
+ return parts;
1690
+ }
1691
+ /**
1692
+ * Render tokenized XML parts with indentation.
1693
+ */
1694
+ function renderXmlParts(parts) {
1695
+ let formatted = "";
1696
+ let indent = 0;
1631
1697
  let lastWasText = false;
1632
1698
  for (let i = 0; i < parts.length; i++) {
1633
1699
  const part = parts[i];
@@ -1637,16 +1703,14 @@ function formatXml(xml) {
1637
1703
  lastWasText = false;
1638
1704
  } else if (part.startsWith("</")) {
1639
1705
  indent = Math.max(0, indent - 1);
1640
- if (lastWasText) formatted += part + "\n";
1641
- else formatted += " ".repeat(indent) + part + "\n";
1706
+ formatted += renderClosingTag(part, indent, lastWasText);
1642
1707
  lastWasText = false;
1643
1708
  } else if (part.startsWith("<") && part.endsWith("/>")) {
1644
1709
  formatted += " ".repeat(indent) + part + "\n";
1645
1710
  lastWasText = false;
1646
1711
  } else if (part.startsWith("<")) {
1647
- formatted += " ".repeat(indent) + part;
1712
+ formatted += renderOpeningTag(part, nextPart, indent);
1648
1713
  if (!part.includes("</")) indent++;
1649
- if (nextPart && !nextPart.startsWith("<")) {} else formatted += "\n";
1650
1714
  lastWasText = false;
1651
1715
  } else {
1652
1716
  const trimmed = part.trim();
@@ -1656,7 +1720,16 @@ function formatXml(xml) {
1656
1720
  }
1657
1721
  }
1658
1722
  }
1659
- return formatted.trim();
1723
+ return formatted;
1724
+ }
1725
+ /** Render a closing tag, kept inline when text content precedes it. */
1726
+ function renderClosingTag(part, indent, lastWasText) {
1727
+ return (lastWasText ? "" : " ".repeat(indent)) + part + "\n";
1728
+ }
1729
+ /** Render an opening tag; no newline when text content follows. */
1730
+ function renderOpeningTag(part, nextPart, indent) {
1731
+ const text = " ".repeat(indent) + part;
1732
+ return nextPart && !nextPart.startsWith("<") ? text : text + "\n";
1660
1733
  }
1661
1734
 
1662
1735
  // src/xml/build.ts
@@ -1666,6 +1739,8 @@ function formatXml(xml) {
1666
1739
  * Build XML string from typed JavaScript object using W3C-compliant Schema definition.
1667
1740
  * Uses the walker module for schema traversal.
1668
1741
  */
1742
+ /** Explicit `String()` coercion for untyped values. */
1743
+ const asText = String;
1669
1744
  /**
1670
1745
  * Collect all namespace prefixes from a schema and its $imports chain
1671
1746
  */
@@ -1696,41 +1771,8 @@ function build(schema, data, options = {}) {
1696
1771
  const { xmlDecl = true, encoding = "utf-8", pretty = false } = options;
1697
1772
  const prefix = options.prefix ?? getSchemaPrefix(schema);
1698
1773
  const doc = createXmlDocument(schema);
1699
- let elementDecl;
1700
- let elementSchema = schema;
1701
- let elementData = data;
1702
- if (options.rootElement) {
1703
- const found = findElement(options.rootElement, schema);
1704
- if (!found) throw new Error(`Element '${options.rootElement}' not found in schema`);
1705
- elementDecl = found.element;
1706
- elementSchema = found.schema;
1707
- if (elementData[options.rootElement] !== void 0) elementData = elementData[options.rootElement];
1708
- } else {
1709
- const dataKeys = Object.keys(elementData);
1710
- if (dataKeys.length === 1) {
1711
- const potentialElementName = dataKeys[0];
1712
- const found = findElement(potentialElementName, schema);
1713
- if (found) {
1714
- elementDecl = found.element;
1715
- elementSchema = found.schema;
1716
- elementData = elementData[potentialElementName];
1717
- }
1718
- }
1719
- if (!elementDecl) {
1720
- elementDecl = findMatchingElement(elementData, schema);
1721
- if (!elementDecl) throw new Error("Schema has no element declarations");
1722
- }
1723
- }
1724
- let rootType;
1725
- let rootSchema = elementSchema;
1726
- if (elementDecl.complexType) rootType = elementDecl.complexType;
1727
- else if (elementDecl.type) {
1728
- const typeName = stripNsPrefix(elementDecl.type);
1729
- const rootTypeEntry = findComplexType(typeName, elementSchema);
1730
- if (!rootTypeEntry) throw new Error(`Schema missing complexType for: ${typeName}`);
1731
- rootType = rootTypeEntry.ct;
1732
- rootSchema = rootTypeEntry.schema;
1733
- } else throw new Error(`Element ${elementDecl.name} has no type or inline complexType`);
1774
+ const { elementDecl, elementSchema, elementData } = resolveRootElement(schema, data, options.rootElement);
1775
+ const { rootType, rootSchema } = resolveRootType(elementDecl, elementSchema);
1734
1776
  const elementName = elementDecl.name ?? elementDecl.ref;
1735
1777
  if (!elementName) throw new Error("Element declaration has no name or ref");
1736
1778
  const root = createRootElement(doc, elementName, schema, prefix);
@@ -1744,6 +1786,63 @@ function build(schema, data, options = {}) {
1744
1786
  return xml;
1745
1787
  }
1746
1788
  /**
1789
+ * Find the element declaration for the root - either by name or by matching data.
1790
+ */
1791
+ function resolveRootElement(schema, data, rootElement) {
1792
+ if (rootElement) return resolveNamedRootElement(schema, data, rootElement);
1793
+ return resolveInferredRootElement(schema, data);
1794
+ }
1795
+ /** Resolve the root element by its explicit name. */
1796
+ function resolveNamedRootElement(schema, data, rootElement) {
1797
+ const found = findElement(rootElement, schema);
1798
+ if (!found) throw new Error(`Element '${rootElement}' not found in schema`);
1799
+ const elementData = data[rootElement] !== void 0 ? data[rootElement] : data;
1800
+ return {
1801
+ elementDecl: found.element,
1802
+ elementSchema: found.schema,
1803
+ elementData
1804
+ };
1805
+ }
1806
+ /** Infer the root element from the wrapped data key or the data structure. */
1807
+ function resolveInferredRootElement(schema, data) {
1808
+ const dataKeys = Object.keys(data);
1809
+ if (dataKeys.length === 1) {
1810
+ const potentialElementName = dataKeys[0];
1811
+ const found = findElement(potentialElementName, schema);
1812
+ if (found) return {
1813
+ elementDecl: found.element,
1814
+ elementSchema: found.schema,
1815
+ elementData: data[potentialElementName]
1816
+ };
1817
+ }
1818
+ const elementDecl = findMatchingElement(data, schema);
1819
+ if (!elementDecl) throw new Error("Schema has no element declarations");
1820
+ return {
1821
+ elementDecl,
1822
+ elementSchema: schema,
1823
+ elementData: data
1824
+ };
1825
+ }
1826
+ /**
1827
+ * Get the complexType of the root element - either inline or by reference.
1828
+ */
1829
+ function resolveRootType(elementDecl, elementSchema) {
1830
+ if (elementDecl.complexType) return {
1831
+ rootType: elementDecl.complexType,
1832
+ rootSchema: elementSchema
1833
+ };
1834
+ if (elementDecl.type) {
1835
+ const typeName = stripNsPrefix(elementDecl.type);
1836
+ const rootTypeEntry = findComplexType(typeName, elementSchema);
1837
+ if (!rootTypeEntry) throw new Error(`Schema missing complexType for: ${typeName}`);
1838
+ return {
1839
+ rootType: rootTypeEntry.ct,
1840
+ rootSchema: rootTypeEntry.schema
1841
+ };
1842
+ }
1843
+ throw new Error(`Element ${elementDecl.name} has no type or inline complexType`);
1844
+ }
1845
+ /**
1747
1846
  * Find the element declaration that best matches the data structure.
1748
1847
  * Searches in the main schema first, then in $imports.
1749
1848
  */
@@ -1751,50 +1850,62 @@ function findMatchingElement(data, schema) {
1751
1850
  const dataKeys = new Set(Object.keys(data));
1752
1851
  let bestMatch;
1753
1852
  let bestScore = -1;
1754
- const scoreElement = (element, searchSchema) => {
1755
- let ct;
1756
- let ctSchema = searchSchema;
1757
- if (element.complexType) ct = element.complexType;
1758
- else if (element.type) {
1759
- const typeName = stripNsPrefix(element.type);
1760
- const typeEntry = findComplexType(typeName, searchSchema);
1761
- if (typeEntry) {
1762
- ct = typeEntry.ct;
1763
- ctSchema = typeEntry.schema;
1764
- }
1765
- }
1766
- if (!ct) return 0;
1767
- const typeFields = /* @__PURE__ */ new Set();
1768
- for (const { element: el } of walkElements(ct, ctSchema)) if (el.name) typeFields.add(el.name);
1769
- for (const { attribute } of walkAttributes(ct, ctSchema)) if (attribute.name) typeFields.add(attribute.name);
1770
- let score = 0;
1771
- dataKeys.forEach((key) => {
1772
- if (typeFields.has(key)) score++;
1773
- });
1774
- return score;
1775
- };
1776
- const elements = schema.element;
1777
- if (elements && elements.length > 0) for (const element of elements) {
1778
- const score = scoreElement(element, schema);
1853
+ for (const { element, schema: candidateSchema } of collectCandidateElements(schema)) {
1854
+ const score = scoreElement(element, candidateSchema, dataKeys);
1779
1855
  if (score > bestScore) {
1780
1856
  bestScore = score;
1781
1857
  bestMatch = element;
1782
1858
  }
1783
1859
  }
1860
+ const elements = schema.element;
1861
+ if (!bestMatch && elements && elements.length > 0) return elements[0];
1862
+ return bestMatch;
1863
+ }
1864
+ /**
1865
+ * Collect candidate element declarations: main schema first, then $imports.
1866
+ */
1867
+ function collectCandidateElements(schema) {
1868
+ const candidates = [];
1869
+ const elements = schema.element;
1870
+ if (elements && elements.length > 0) for (const element of elements) candidates.push({
1871
+ element,
1872
+ schema
1873
+ });
1784
1874
  const imports = schema.$imports;
1785
1875
  if (imports) for (const importedSchema of imports) {
1786
1876
  const importedElements = importedSchema.element;
1787
1877
  if (!importedElements) continue;
1788
- for (const element of importedElements) {
1789
- const score = scoreElement(element, importedSchema);
1790
- if (score > bestScore) {
1791
- bestScore = score;
1792
- bestMatch = element;
1793
- }
1878
+ for (const element of importedElements) candidates.push({
1879
+ element,
1880
+ schema: importedSchema
1881
+ });
1882
+ }
1883
+ return candidates;
1884
+ }
1885
+ /**
1886
+ * Score an element against the data keys: how many keys its type covers.
1887
+ */
1888
+ function scoreElement(element, searchSchema, dataKeys) {
1889
+ let ct;
1890
+ let ctSchema = searchSchema;
1891
+ if (element.complexType) ct = element.complexType;
1892
+ else if (element.type) {
1893
+ const typeName = stripNsPrefix(element.type);
1894
+ const typeEntry = findComplexType(typeName, searchSchema);
1895
+ if (typeEntry) {
1896
+ ct = typeEntry.ct;
1897
+ ctSchema = typeEntry.schema;
1794
1898
  }
1795
1899
  }
1796
- if (!bestMatch && elements && elements.length > 0) return elements[0];
1797
- return bestMatch;
1900
+ if (!ct) return 0;
1901
+ const typeFields = /* @__PURE__ */ new Set();
1902
+ for (const { element: el } of walkElements(ct, ctSchema)) if (el.name) typeFields.add(el.name);
1903
+ for (const { attribute } of walkAttributes(ct, ctSchema)) if (attribute.name) typeFields.add(attribute.name);
1904
+ let score = 0;
1905
+ dataKeys.forEach((key) => {
1906
+ if (typeFields.has(key)) score++;
1907
+ });
1908
+ return score;
1798
1909
  }
1799
1910
  /**
1800
1911
  * Build element content using its complexType definition
@@ -1805,7 +1916,11 @@ function buildElement(doc, node, data, typeDef, schema, rootSchema, prefix) {
1805
1916
  const wildcard = hasWildcard(typeDef, schema);
1806
1917
  const consumed = /* @__PURE__ */ new Set();
1807
1918
  buildAttributes(node, data, typeDef, schema, rootSchema, consumed);
1808
- buildDeclaredElements(doc, node, data, typeDef, schema, rootSchema, prefix, consumed);
1919
+ buildDeclaredElements({
1920
+ doc,
1921
+ rootSchema,
1922
+ prefix
1923
+ }, node, data, typeDef, schema, consumed);
1809
1924
  if (wildcard) for (const key of Object.keys(data)) {
1810
1925
  if (consumed.has(key)) continue;
1811
1926
  buildAnyField(doc, node, key, data[key]);
@@ -1836,12 +1951,12 @@ function buildAttributes(node, data, typeDef, schema, rootSchema, consumed) {
1836
1951
  * Build declared child elements via the walker (handles inheritance,
1837
1952
  * groups, refs, substitution). Adds each declared key to `consumed`.
1838
1953
  */
1839
- function buildDeclaredElements(doc, node, data, typeDef, schema, rootSchema, prefix, consumed) {
1954
+ function buildDeclaredElements(env, node, data, typeDef, schema, consumed) {
1955
+ const { doc, rootSchema, prefix } = env;
1840
1956
  for (const { element, schema: elementDefSchema } of walkElements(typeDef, schema)) {
1841
1957
  if (element.ref) {
1842
1958
  const refName = stripNsPrefix(element.ref);
1843
- const refElement = findElement(refName, elementDefSchema);
1844
- if (refElement && refElement.element.abstract) {
1959
+ if (findElement(refName, elementDefSchema)?.element.abstract) {
1845
1960
  buildSubstituteElements(doc, node, data, refName, rootSchema, prefix, consumed);
1846
1961
  continue;
1847
1962
  }
@@ -1850,7 +1965,14 @@ function buildDeclaredElements(doc, node, data, typeDef, schema, rootSchema, pre
1850
1965
  if (!resolved) continue;
1851
1966
  consumed.add(resolved.dataKey);
1852
1967
  const value = data[resolved.dataKey];
1853
- if (value != null) buildFieldWithTagName(doc, node, value, resolved.tagName, resolved.typeName, resolved.inlineComplexType, resolved.elementSchema, rootSchema, prefix, resolved.form);
1968
+ if (value != null) buildFieldWithTagName(env, node, {
1969
+ value,
1970
+ tagName: resolved.tagName,
1971
+ typeName: resolved.typeName,
1972
+ inlineComplexType: resolved.inlineComplexType,
1973
+ elementSchema: resolved.elementSchema,
1974
+ elementForm: resolved.form
1975
+ });
1854
1976
  }
1855
1977
  }
1856
1978
  /**
@@ -1865,7 +1987,14 @@ function buildSubstituteElements(doc, node, data, refName, rootSchema, prefix, c
1865
1987
  if (!subName) continue;
1866
1988
  consumed.add(subName);
1867
1989
  const value = data[subName];
1868
- if (value != null) buildField(doc, node, value, subName, substitute.element.type ? stripNsPrefix(substitute.element.type) : void 0, substitute.schema, rootSchema, prefix);
1990
+ if (value != null) {
1991
+ const typeName = substitute.element.type ? stripNsPrefix(substitute.element.type) : void 0;
1992
+ buildField({
1993
+ doc,
1994
+ rootSchema,
1995
+ prefix
1996
+ }, node, value, subName, typeName, substitute.schema);
1997
+ }
1869
1998
  }
1870
1999
  }
1871
2000
  /**
@@ -1880,13 +2009,17 @@ function buildAnyField(doc, parent, tagName, value) {
1880
2009
  return;
1881
2010
  }
1882
2011
  const el = doc.createElement(tagName);
1883
- if (typeof value === "object") for (const [key, nested] of Object.entries(value)) if (key.startsWith("@")) {
1884
- if (nested !== void 0 && nested !== null) el.setAttribute(key.slice(1), String(nested));
1885
- } else if (key === "_text") el.textContent = String(nested ?? "");
1886
- else buildAnyField(doc, el, key, nested);
1887
- else el.textContent = String(value);
2012
+ if (typeof value === "object") buildAnyEntries(doc, el, value);
2013
+ else el.textContent = asText(value);
1888
2014
  parent.appendChild(el);
1889
2015
  }
2016
+ /** Build the nested entries of a wildcard object element. */
2017
+ function buildAnyEntries(doc, el, value) {
2018
+ for (const [key, nested] of Object.entries(value)) if (key.startsWith("@")) {
2019
+ if (nested !== void 0 && nested !== null) el.setAttribute(key.slice(1), asText(nested));
2020
+ } else if (key === "_text") el.textContent = asText(nested ?? "");
2021
+ else buildAnyField(doc, el, key, nested);
2022
+ }
1890
2023
  /**
1891
2024
  * Resolve element info (name and type), handling ref
1892
2025
  *
@@ -1935,11 +2068,19 @@ function resolveElementInfo(element, schema) {
1935
2068
  * - "qualified": local elements get namespace prefix
1936
2069
  * - "unqualified" (default): local elements do NOT get namespace prefix
1937
2070
  */
1938
- function buildField(doc, parent, value, elementName, typeName, schema, rootSchema, prefix) {
2071
+ function buildField(env, parent, value, elementName, typeName, schema) {
2072
+ const { rootSchema, prefix } = env;
1939
2073
  const nestedType = typeName ? findComplexType(typeName, schema) : void 0;
1940
2074
  const usePrefix = rootSchema.elementFormDefault === "qualified" ? prefix : void 0;
1941
- const tagName = usePrefix ? `${usePrefix}:${elementName}` : elementName;
1942
- if (Array.isArray(value)) for (const item of value) {
2075
+ emitFieldChildren(env, parent, value, usePrefix ? `${usePrefix}:${elementName}` : elementName, typeName, nestedType);
2076
+ }
2077
+ /**
2078
+ * Emit the child element(s) of a field value: arrays become repeated siblings.
2079
+ */
2080
+ function emitFieldChildren(env, parent, value, tagName, typeName, nestedType) {
2081
+ const { doc, rootSchema, prefix } = env;
2082
+ const items = Array.isArray(value) ? value : [value];
2083
+ for (const item of items) {
1943
2084
  const child = doc.createElement(tagName);
1944
2085
  if (nestedType) buildElement(doc, child, item, nestedType.ct, nestedType.schema, rootSchema, prefix);
1945
2086
  else {
@@ -1948,15 +2089,6 @@ function buildField(doc, parent, value, elementName, typeName, schema, rootSchem
1948
2089
  }
1949
2090
  parent.appendChild(child);
1950
2091
  }
1951
- else {
1952
- const child = doc.createElement(tagName);
1953
- if (nestedType) buildElement(doc, child, value, nestedType.ct, nestedType.schema, rootSchema, prefix);
1954
- else {
1955
- const text = formatValue(value, typeName || "string");
1956
- if (text) child.appendChild(doc.createTextNode(text));
1957
- }
1958
- parent.appendChild(child);
1959
- }
1960
2092
  }
1961
2093
  /**
1962
2094
  * Build a field with an explicit tag name (used for element refs with prefix)
@@ -1966,39 +2098,34 @@ function buildField(doc, parent, value, elementName, typeName, schema, rootSchem
1966
2098
  * 2. Elements with form="unqualified" - never use prefix
1967
2099
  * 3. Direct elements - apply elementFormDefault logic
1968
2100
  */
1969
- function buildFieldWithTagName(doc, parent, value, tagName, typeName, inlineComplexType, elementSchema, rootSchema, prefix, elementForm) {
2101
+ function buildFieldWithTagName(env, parent, spec) {
2102
+ const { rootSchema, prefix } = env;
2103
+ const { value, tagName, typeName, inlineComplexType, elementSchema, elementForm } = spec;
1970
2104
  let nestedType;
1971
2105
  if (inlineComplexType) nestedType = {
1972
2106
  ct: inlineComplexType,
1973
2107
  schema: elementSchema
1974
2108
  };
1975
2109
  else if (typeName) nestedType = findComplexType(typeName, elementSchema);
1976
- let actualTagName = tagName;
1977
- if (!tagName.includes(":")) {
1978
- let usePrefix;
1979
- if (elementForm === "unqualified") usePrefix = void 0;
1980
- else if (elementForm === "qualified") usePrefix = getPrefixForSchema(elementSchema, rootSchema) ?? prefix;
1981
- else usePrefix = (elementSchema.elementFormDefault ?? rootSchema.elementFormDefault) === "qualified" ? getPrefixForSchema(elementSchema, rootSchema) ?? prefix : void 0;
1982
- actualTagName = usePrefix ? `${usePrefix}:${tagName}` : tagName;
1983
- }
1984
- if (Array.isArray(value)) for (const item of value) {
1985
- const child = doc.createElement(actualTagName);
1986
- if (nestedType) buildElement(doc, child, item, nestedType.ct, nestedType.schema, rootSchema, prefix);
1987
- else {
1988
- const text = formatValue(item, typeName || "string");
1989
- if (text) child.appendChild(doc.createTextNode(text));
1990
- }
1991
- parent.appendChild(child);
1992
- }
1993
- else {
1994
- const child = doc.createElement(actualTagName);
1995
- if (nestedType) buildElement(doc, child, value, nestedType.ct, nestedType.schema, rootSchema, prefix);
1996
- else {
1997
- const text = formatValue(value, typeName || "string");
1998
- if (text) child.appendChild(doc.createTextNode(text));
1999
- }
2000
- parent.appendChild(child);
2001
- }
2110
+ emitFieldChildren(env, parent, value, resolveTagName(tagName, elementSchema, rootSchema, prefix, elementForm), typeName, nestedType);
2111
+ }
2112
+ /**
2113
+ * Determine the actual tag name to use.
2114
+ * Priority: 1. Per-element form attribute, 2. Schema elementFormDefault
2115
+ * If tagName already has a prefix (e.g., "asx:abap"), use it directly
2116
+ */
2117
+ function resolveTagName(tagName, elementSchema, rootSchema, prefix, elementForm) {
2118
+ if (tagName.includes(":")) return tagName;
2119
+ const usePrefix = resolveElementPrefix(elementSchema, rootSchema, prefix, elementForm);
2120
+ return usePrefix ? `${usePrefix}:${tagName}` : tagName;
2121
+ }
2122
+ /**
2123
+ * Determine the namespace prefix for a local tag name.
2124
+ */
2125
+ function resolveElementPrefix(elementSchema, rootSchema, prefix, elementForm) {
2126
+ if (elementForm === "unqualified") return;
2127
+ if (elementForm === "qualified") return getPrefixForSchema(elementSchema, rootSchema) ?? prefix;
2128
+ return (elementSchema.elementFormDefault ?? rootSchema.elementFormDefault) === "qualified" ? getPrefixForSchema(elementSchema, rootSchema) ?? prefix : void 0;
2002
2129
  }
2003
2130
  /**
2004
2131
  * Find all elements that substitute for a given abstract element name.