bpmn-js 18.25.0 → 18.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * bpmn-js - bpmn-navigated-viewer v18.25.0
2
+ * bpmn-js - bpmn-navigated-viewer v18.26.0
3
3
  *
4
4
  * Copyright (c) 2014-present, camunda Services GmbH
5
5
  *
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Source Code: https://github.com/bpmn-io/bpmn-js
10
10
  *
11
- * Date: 2026-08-19
11
+ * Date: 2026-08-28
12
12
  */
13
13
  (function (global, factory) {
14
14
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -8048,14 +8048,7 @@
8048
8048
  return [];
8049
8049
  }
8050
8050
 
8051
- var title = escapeHTML(parent.name || parent.id);
8052
- var link = domify('<li><span class="bjs-crumb"><a title="' + title + '">' + title + '</a></span></li>');
8053
-
8054
- link.addEventListener('click', function() {
8055
- canvas.setRootElement(parentPlane);
8056
- });
8057
-
8058
- return link;
8051
+ return [ { parent: parent, parentPlane: parentPlane } ];
8059
8052
  });
8060
8053
 
8061
8054
  breadcrumbs.innerHTML = '';
@@ -8065,8 +8058,39 @@
8065
8058
 
8066
8059
  containerClasses.toggle(OPEN_CLASS, visible);
8067
8060
 
8068
- path.forEach(function(element) {
8069
- breadcrumbs.appendChild(element);
8061
+ path.forEach(function(entry, index) {
8062
+ var parent = entry.parent,
8063
+ parentPlane = entry.parentPlane,
8064
+ title = escapeHTML(parent.name || parent.id);
8065
+
8066
+ // the last crumb represents the current plane; render it as
8067
+ // plain, non-interactive text
8068
+ var isCurrent = index === path.length - 1;
8069
+
8070
+ var crumb;
8071
+
8072
+ if (isCurrent) {
8073
+ crumb = domify(
8074
+ '<li><span class="bjs-crumb" title="' + title + '">' + title + '</span></li>'
8075
+ );
8076
+ } else {
8077
+
8078
+ // render navigable crumbs as local links so they are focusable
8079
+ // and keyboard-operable
8080
+ var href = '#' + escapeHTML(parent.id);
8081
+
8082
+ crumb = domify(
8083
+ '<li><a class="bjs-crumb" href="' + href + '" title="' + title + '">' + title + '</a></li>'
8084
+ );
8085
+
8086
+ crumb.addEventListener('click', function(event) {
8087
+ event.preventDefault();
8088
+
8089
+ canvas.setRootElement(parentPlane);
8090
+ });
8091
+ }
8092
+
8093
+ breadcrumbs.appendChild(crumb);
8070
8094
  });
8071
8095
  }
8072
8096
 
@@ -14145,14 +14169,109 @@
14145
14169
  */
14146
14170
  function Base() { }
14147
14171
 
14172
+ /**
14173
+ * @template { keyof this } K
14174
+ *
14175
+ * Get property value (typed)
14176
+ *
14177
+ * @overload
14178
+ *
14179
+ * @param {K} name
14180
+ *
14181
+ * @return { this[K] }
14182
+ */
14183
+ /**
14184
+ * @template T
14185
+ *
14186
+ * Get property value
14187
+ *
14188
+ * @overload
14189
+ *
14190
+ * @param {string} name
14191
+ *
14192
+ * @return {T}
14193
+ */
14194
+ /**
14195
+ * Get property value
14196
+ *
14197
+ * @overload
14198
+ *
14199
+ * @param {string} name
14200
+ *
14201
+ * @return {unknown}
14202
+ */
14148
14203
  Base.prototype.get = function(name) {
14149
14204
  return this.$model.properties.get(this, name);
14150
14205
  };
14151
14206
 
14207
+ /**
14208
+ * @template { keyof this } K
14209
+ * @template { this[K] } V
14210
+ *
14211
+ * Set property value
14212
+ *
14213
+ * @overload
14214
+ *
14215
+ * @param {K} name
14216
+ * @param {V} value
14217
+ */
14218
+ /**
14219
+ * @template { string } S
14220
+ *
14221
+ * Set property value
14222
+ *
14223
+ * @overload
14224
+ *
14225
+ * @param { S extends keyof this ? never : S } name
14226
+ * @param { any } value
14227
+ */
14152
14228
  Base.prototype.set = function(name, value) {
14153
14229
  this.$model.properties.set(this, name, value);
14154
14230
  };
14155
14231
 
14232
+ /**
14233
+ * @typedef {import('./ns.js').Namespace} Namespace
14234
+ * @typedef {import('./moddle.js').default} Moddle
14235
+ * @typedef {import('./properties.js').default} Properties
14236
+ * @typedef {import('./registry.js').EffectiveDescriptor} EffectiveDescriptor
14237
+ * @typedef {import('./base.js').default} BaseElement
14238
+ * @typedef {import('./descriptor-builder.js').AnyTypeDescriptor} AnyTypeDescriptor
14239
+ */
14240
+
14241
+ /**
14242
+ * @template [T=Record<string,any>]
14243
+ * @typedef {{
14244
+ * new(attrs?: Partial<T>): ModdleElement<T>;
14245
+ * prototype: ModdleElement<T>;
14246
+ * readonly $model: Moddle;
14247
+ * readonly $descriptor: EffectiveDescriptor;
14248
+ * }} ModdleElementType
14249
+ */
14250
+
14251
+ /**
14252
+ * @template [T=Record<string,any>]
14253
+ * @typedef {BaseElement & T & {
14254
+ * readonly $model: Moddle;
14255
+ * readonly $descriptor: EffectiveDescriptor;
14256
+ * readonly $type: Namespace['name'];
14257
+ * readonly $attrs: Record<string, any>;
14258
+ * $parent?: ModdleElement | AnyModdleElement;
14259
+ * hasType: Moddle['hasType'];
14260
+ * $instanceOf: Moddle['hasType'];
14261
+ * }} ModdleElement
14262
+ */
14263
+
14264
+ /**
14265
+ * @template [T=Record<string,any>]
14266
+ * @typedef {BaseElement & T & {
14267
+ * $type: string;
14268
+ * $instanceOf: (type: string) => boolean;
14269
+ * $parent?: ModdleElement | AnyModdleElement;
14270
+ * readonly $model: Moddle;
14271
+ * readonly $descriptor: AnyTypeDescriptor;
14272
+ * }} AnyModdleElement
14273
+ */
14274
+
14156
14275
  /**
14157
14276
  * A model element factory.
14158
14277
  *
@@ -14160,11 +14279,23 @@
14160
14279
  * @param {Properties} properties
14161
14280
  */
14162
14281
  function Factory(model, properties) {
14282
+
14283
+ /**
14284
+ * @private
14285
+ */
14163
14286
  this.model = model;
14287
+
14288
+ /**
14289
+ * @private
14290
+ */
14164
14291
  this.properties = properties;
14165
14292
  }
14166
14293
 
14167
-
14294
+ /**
14295
+ * @template [T=Record<string,any>]
14296
+ * @param {EffectiveDescriptor} descriptor
14297
+ * @return {ModdleElementType<T>}
14298
+ */
14168
14299
  Factory.prototype.createType = function(descriptor) {
14169
14300
 
14170
14301
  var model = this.model;
@@ -14186,6 +14317,8 @@
14186
14317
 
14187
14318
  /**
14188
14319
  * The new type constructor
14320
+ *
14321
+ * @type { ModdleElementType }
14189
14322
  */
14190
14323
  function ModdleElement(attrs) {
14191
14324
  props.define(this, '$type', { value: name, enumerable: true });
@@ -14211,26 +14344,62 @@
14211
14344
  /**
14212
14345
  * Built-in moddle types
14213
14346
  */
14214
- var BUILTINS = {
14347
+ var BUILTINS = assign$1(Object.create(null), {
14215
14348
  String: true,
14216
14349
  Boolean: true,
14217
14350
  Integer: true,
14218
14351
  Real: true,
14219
14352
  Element: true
14220
- };
14353
+ });
14221
14354
 
14222
14355
  /**
14223
- * Converters for built in types from string representations
14356
+ * Converters for built-in types from string representations
14224
14357
  */
14225
- var TYPE_CONVERTERS = {
14358
+ var TYPE_CONVERTERS = assign$1(Object.create(null), {
14226
14359
  String: function(s) { return s; },
14227
14360
  Boolean: function(s) { return s === 'true'; },
14228
14361
  Integer: function(s) { return parseInt(s, 10); },
14229
14362
  Real: function(s) { return parseFloat(s); }
14230
- };
14363
+ });
14364
+
14365
+ /**
14366
+ * @typedef {'String'} StringType
14367
+ * @typedef {'Boolean'} BooleanType
14368
+ * @typedef {'Integer'} IntegerType
14369
+ * @typedef {'Real'} RealType
14370
+ * @typedef {'Element'} ElementType
14371
+ * @typedef {StringType | BooleanType | IntegerType | RealType} BuiltInSimpleType
14372
+ * @typedef {BuiltInSimpleType | ElementType} BuiltInType
14373
+ */
14231
14374
 
14375
+ /**
14376
+ * Convert given value to string
14377
+ * @overlord
14378
+ * @param {StringType} type
14379
+ * @param {any} value
14380
+ * @return {string}
14381
+ */
14382
+ /**
14383
+ * Convert given value to boolean
14384
+ * @overlord
14385
+ * @param {BooleanType} type
14386
+ * @param {any} value
14387
+ * @return {boolean}
14388
+ */
14389
+ /**
14390
+ * Convert given value to number
14391
+ * @overlord
14392
+ * @param {IntegerType | RealType} type
14393
+ * @param {any} value
14394
+ * @return {number}
14395
+ */
14232
14396
  /**
14233
14397
  * Convert a type to its real representation
14398
+ * @template T
14399
+ * @overlord
14400
+ * @param {Exclude<string,BuiltInSimpleType>} type
14401
+ * @param {T} value
14402
+ * @return {T}
14234
14403
  */
14235
14404
  function coerceType(type, value) {
14236
14405
 
@@ -14245,18 +14414,44 @@
14245
14414
 
14246
14415
  /**
14247
14416
  * Return whether the given type is built-in
14417
+ * @overload
14418
+ * @param {BuiltInType} type
14419
+ * @return {true}
14420
+ */
14421
+ /**
14422
+ * Return whether the given type is built-in
14423
+ * @overload
14424
+ * @param {Exclude<string,BuiltInType>} type
14425
+ * @return {false}
14248
14426
  */
14249
14427
  function isBuiltIn(type) {
14250
14428
  return !!BUILTINS[type];
14251
14429
  }
14252
14430
 
14253
14431
  /**
14254
- * Return whether the given type is simple
14432
+ * Return true if the given type is simple
14433
+ * @overload
14434
+ * @param {BuiltInSimpleType} type
14435
+ * @return {true}
14436
+ */
14437
+ /**
14438
+ * Return false the given type is not simple
14439
+ * @overload
14440
+ * @param {Exclude<string,BuiltInSimpleType>} type
14441
+ * @return {false}
14255
14442
  */
14256
14443
  function isSimple(type) {
14257
14444
  return !!TYPE_CONVERTERS[type];
14258
14445
  }
14259
14446
 
14447
+ /**
14448
+ * @typedef {{
14449
+ * name: string;
14450
+ * prefix: string;
14451
+ * localName: string;
14452
+ * }} Namespace
14453
+ */
14454
+
14260
14455
  /**
14261
14456
  * Parses a namespaced attribute name of the form (ns:)localName to an object,
14262
14457
  * given a default prefix to assume in case no explicit namespace is given.
@@ -14264,7 +14459,7 @@
14264
14459
  * @param {String} name
14265
14460
  * @param {String} [defaultPrefix] the default prefix to take, if none is present.
14266
14461
  *
14267
- * @return {Object} the parsed name
14462
+ * @return {Namespace} the parsed name
14268
14463
  */
14269
14464
  function parseName(name, defaultPrefix) {
14270
14465
  var parts = name.split(/:/),
@@ -14295,19 +14490,98 @@
14295
14490
  };
14296
14491
  }
14297
14492
 
14493
+ /**
14494
+ * @typedef {import('./ns.js').Namespace} Namespace
14495
+ * @typedef {import('./registry.js').RegisteredPackage} RegisteredPackage
14496
+ * @typedef {import('./registry.js').RegisteredTypeDef} RegisteredTypeDef
14497
+ * @typedef {import('./registry.js').RegisteredPropertyDef} RegisteredPropertyDef
14498
+ */
14499
+
14500
+ /**
14501
+ * Effective element descriptor
14502
+ * aka element type descriptor
14503
+ * aka element descriptor
14504
+ * @typedef {{
14505
+ * readonly ns: Namespace;
14506
+ * readonly name: Namespace['name'];
14507
+ * readonly allTypes: Array<RegisteredTypeDef>;
14508
+ * readonly allTypesByName: Record<string, RegisteredTypeDef>;
14509
+ * readonly properties: Array<PropertyDescriptor>;
14510
+ * readonly propertiesByName: Record<string, PropertyDescriptor>;
14511
+ * readonly bodyProperty?: PropertyDescriptor;
14512
+ * readonly idProperty?: PropertyDescriptor;
14513
+ * readonly $pkg?: RegisteredPackage;
14514
+ * }} EffectiveDescriptor
14515
+ */
14516
+
14517
+ /**
14518
+ * Property descriptor
14519
+ * @typedef {RegisteredPropertyDef & {
14520
+ * localName: Namespace['localName'];
14521
+ * inherited?: boolean;
14522
+ * definedBy?: RegisteredTypeDef;
14523
+ * }} PropertyDescriptor
14524
+ */
14525
+
14526
+ /**
14527
+ * @typedef {{
14528
+ * name: string;
14529
+ * isGeneric: true;
14530
+ * ns: {
14531
+ * prefix: string;
14532
+ * localName: string;
14533
+ * uri: string;
14534
+ * };
14535
+ * }} AnyTypeDescriptor
14536
+ */
14537
+
14298
14538
  /**
14299
14539
  * A utility to build element descriptors.
14540
+ * @class DescriptorBuilder
14541
+ * @param {Namespace} nameNs
14300
14542
  */
14301
14543
  function DescriptorBuilder(nameNs) {
14544
+
14545
+ /**
14546
+ * @private
14547
+ * @type {Namespace}
14548
+ */
14302
14549
  this.ns = nameNs;
14550
+
14551
+ /**
14552
+ * @private
14553
+ * @type {Namespace['name']}
14554
+ */
14303
14555
  this.name = nameNs.name;
14556
+
14557
+ /**
14558
+ * @private
14559
+ * @type {Array<RegisteredTypeDef>}
14560
+ */
14304
14561
  this.allTypes = [];
14305
- this.allTypesByName = {};
14562
+
14563
+ /**
14564
+ * @private
14565
+ * @type {Record<string, RegisteredTypeDef>}
14566
+ */
14567
+ this.allTypesByName = Object.create(null);
14568
+
14569
+ /**
14570
+ * @private
14571
+ * @type {Array<PropertyDescriptor>}
14572
+ */
14306
14573
  this.properties = [];
14307
- this.propertiesByName = {};
14308
- }
14309
14574
 
14575
+ /**
14576
+ * @private
14577
+ * @type {Record<string, PropertyDescriptor>}
14578
+ */
14579
+ this.propertiesByName = Object.create(null);
14580
+ }
14310
14581
 
14582
+ /**
14583
+ * @return {EffectiveDescriptor}
14584
+ */
14311
14585
  DescriptorBuilder.prototype.build = function() {
14312
14586
  return pick(this, [
14313
14587
  'ns',
@@ -14324,7 +14598,7 @@
14324
14598
  /**
14325
14599
  * Add property at given index.
14326
14600
  *
14327
- * @param {Object} p
14601
+ * @param {PropertyDescriptor} p
14328
14602
  * @param {Number} [idx]
14329
14603
  * @param {Boolean} [validate=true]
14330
14604
  */
@@ -14346,7 +14620,11 @@
14346
14620
  }
14347
14621
  };
14348
14622
 
14349
-
14623
+ /**
14624
+ * @param {PropertyDescriptor} oldProperty
14625
+ * @param {PropertyDescriptor} newProperty
14626
+ * @param {string} replace
14627
+ */
14350
14628
  DescriptorBuilder.prototype.replaceProperty = function(oldProperty, newProperty, replace) {
14351
14629
  var oldNameNs = oldProperty.ns;
14352
14630
 
@@ -14396,7 +14674,11 @@
14396
14674
  propertiesByName[oldNameNs.name] = propertiesByName[oldNameNs.localName] = newProperty;
14397
14675
  };
14398
14676
 
14399
-
14677
+ /**
14678
+ * @param {PropertyDescriptor} p
14679
+ * @param {string} targetPropertyName
14680
+ * @param {string} replace
14681
+ */
14400
14682
  DescriptorBuilder.prototype.redefineProperty = function(p, targetPropertyName, replace) {
14401
14683
 
14402
14684
  var nsPrefix = p.ns.prefix;
@@ -14415,6 +14697,10 @@
14415
14697
  delete p.redefines;
14416
14698
  };
14417
14699
 
14700
+ /**
14701
+ * @param {PropertyDescriptor} p
14702
+ * @param {boolean} validate
14703
+ */
14418
14704
  DescriptorBuilder.prototype.addNamedProperty = function(p, validate) {
14419
14705
  var ns = p.ns,
14420
14706
  propsByName = this.propertiesByName;
@@ -14427,6 +14713,9 @@
14427
14713
  propsByName[ns.name] = propsByName[ns.localName] = p;
14428
14714
  };
14429
14715
 
14716
+ /**
14717
+ * @param {RegisteredPropertyDef} p
14718
+ */
14430
14719
  DescriptorBuilder.prototype.removeNamedProperty = function(p) {
14431
14720
  var ns = p.ns,
14432
14721
  propsByName = this.propertiesByName;
@@ -14435,6 +14724,10 @@
14435
14724
  delete propsByName[ns.localName];
14436
14725
  };
14437
14726
 
14727
+ /**
14728
+ * @param {PropertyDescriptor} p
14729
+ * @param {boolean} [validate]
14730
+ */
14438
14731
  DescriptorBuilder.prototype.setBodyProperty = function(p, validate) {
14439
14732
 
14440
14733
  if (validate && this.bodyProperty) {
@@ -14446,6 +14739,10 @@
14446
14739
  this.bodyProperty = p;
14447
14740
  };
14448
14741
 
14742
+ /**
14743
+ * @param {PropertyDescriptor} p
14744
+ * @param {boolean} [validate]
14745
+ */
14449
14746
  DescriptorBuilder.prototype.setIdProperty = function(p, validate) {
14450
14747
 
14451
14748
  if (validate && this.idProperty) {
@@ -14457,6 +14754,9 @@
14457
14754
  this.idProperty = p;
14458
14755
  };
14459
14756
 
14757
+ /**
14758
+ * @param {RegisteredTypeDef} typeDescriptor
14759
+ */
14460
14760
  DescriptorBuilder.prototype.assertNotTrait = function(typeDescriptor) {
14461
14761
 
14462
14762
  const _extends = typeDescriptor.extends || [];
@@ -14468,6 +14768,9 @@
14468
14768
  }
14469
14769
  };
14470
14770
 
14771
+ /**
14772
+ * @param {PropertyDescriptor} p
14773
+ */
14471
14774
  DescriptorBuilder.prototype.assertNotDefined = function(p, name) {
14472
14775
  var propertyName = p.name,
14473
14776
  definedProperty = this.propertiesByName[propertyName];
@@ -14480,10 +14783,18 @@
14480
14783
  }
14481
14784
  };
14482
14785
 
14786
+ /**
14787
+ * @param {string} name
14788
+ * @return {PropertyDescriptor}
14789
+ */
14483
14790
  DescriptorBuilder.prototype.hasProperty = function(name) {
14484
14791
  return this.propertiesByName[name];
14485
14792
  };
14486
14793
 
14794
+ /**
14795
+ * @param {RegisteredTypeDef} t
14796
+ * @param {boolean} inherited
14797
+ */
14487
14798
  DescriptorBuilder.prototype.addTrait = function(t, inherited) {
14488
14799
 
14489
14800
  if (inherited) {
@@ -14532,33 +14843,94 @@
14532
14843
  typesByName[typeName] = t;
14533
14844
  };
14534
14845
 
14846
+ /**
14847
+ * @typedef {import('./ns.js').Namespace} Namespace
14848
+ * @typedef {import('./moddle.js').PackageDefinition} PackageDefinition
14849
+ * @typedef {import('./moddle.js').TypeDefinition} TypeDefinition
14850
+ * @typedef {import('./moddle.js').PropertyDefinition} PropertyDefinition
14851
+ * @typedef {import('./properties.js').default} Properties
14852
+ * @typedef {import('./descriptor-builder.js').EffectiveDescriptor} EffectiveDescriptor
14853
+ */
14854
+
14855
+ /**
14856
+ * Registered package definition
14857
+ * @typedef {Omit<PackageDefinition, 'types'> & {
14858
+ * types?: Array<RegisteredTypeDef>;
14859
+ * }} RegisteredPackage
14860
+ */
14861
+
14862
+ /**
14863
+ * Registered type definition
14864
+ * @typedef {Omit<TypeDefinition, 'properties'> & {
14865
+ * properties?: Array<RegisteredPropertyDef>;
14866
+ * propertiesByName?: Record<string, RegisteredPropertyDef>;
14867
+ * superClass?: Array<string>;
14868
+ * extends?: Array<string>;
14869
+ * meta?: Record<string, *>;
14870
+ * traits?: Array<string>;
14871
+ * ns?: Namespace;
14872
+ * readonly $pkg?: RegisteredPackage;
14873
+ * }} RegisteredTypeDef
14874
+ */
14875
+
14876
+ /**
14877
+ * Registered property definition
14878
+ * @typedef {PropertyDefinition & { ns: Namespace }} RegisteredPropertyDef
14879
+ */
14880
+
14535
14881
  /**
14536
14882
  * A registry of Moddle packages.
14537
14883
  *
14538
- * @param {Array<Package>} packages
14884
+ * @param {Array<PackageDefinition> | Record<string,PackageDefinition>} packages
14539
14885
  * @param {Properties} properties
14540
14886
  */
14541
14887
  function Registry(packages, properties) {
14542
- this.packageMap = {};
14543
- this.typeMap = {};
14544
14888
 
14889
+ /**
14890
+ * @private
14891
+ * @type {Record<string, RegisteredPackage>} registered packages map
14892
+ */
14893
+ this.packageMap = Object.create(null);
14894
+
14895
+ /**
14896
+ * @type {Record<string,RegisteredTypeDef>}
14897
+ */
14898
+ this.typeMap = Object.create(null);
14899
+
14900
+ /**
14901
+ * @private
14902
+ * @type {Array<RegisteredPackage>} all registered packages
14903
+ */
14545
14904
  this.packages = [];
14546
14905
 
14906
+ /**
14907
+ * @private
14908
+ * @type {Properties}
14909
+ */
14547
14910
  this.properties = properties;
14548
14911
 
14549
14912
  forEach(packages, bind$1(this.registerPackage, this));
14550
14913
  }
14551
14914
 
14552
-
14915
+ /**
14916
+ * @param {string} uriOrPrefix uri or prefix of package
14917
+ * @return {RegisteredPackage} registered package
14918
+ */
14553
14919
  Registry.prototype.getPackage = function(uriOrPrefix) {
14554
14920
  return this.packageMap[uriOrPrefix];
14555
14921
  };
14556
14922
 
14923
+ /**
14924
+ * @return {Array<RegisteredPackage>} all registered packages
14925
+ */
14557
14926
  Registry.prototype.getPackages = function() {
14558
14927
  return this.packages;
14559
14928
  };
14560
14929
 
14561
-
14930
+ /**
14931
+ * @private
14932
+ * @param {PackageDefinition} pkg registering package
14933
+ */
14562
14934
  Registry.prototype.registerPackage = function(pkg) {
14563
14935
 
14564
14936
  // copy package
@@ -14578,22 +14950,23 @@
14578
14950
  this.packages.push(pkg);
14579
14951
  };
14580
14952
 
14581
-
14582
14953
  /**
14954
+ * @private
14583
14955
  * Register a type from a specific package with us
14956
+ * @param {TypeDefinition} type
14957
+ * @param {RegisteredPackage} pkg
14584
14958
  */
14585
14959
  Registry.prototype.registerType = function(type, pkg) {
14586
-
14587
14960
  type = assign$1({}, type, {
14588
14961
  superClass: (type.superClass || []).slice(),
14589
14962
  extends: (type.extends || []).slice(),
14590
14963
  properties: (type.properties || []).slice(),
14591
- meta: assign$1((type.meta || {}))
14964
+ meta: assign$1({}, type.meta || {})
14592
14965
  });
14593
14966
 
14594
14967
  var ns = parseName(type.name, pkg.prefix),
14595
14968
  name = ns.name,
14596
- propertiesByName = {};
14969
+ /** @type {Record<string, RegisteredPropertyDef>} */ propertiesByName = Object.create(null);
14597
14970
 
14598
14971
  // parse properties
14599
14972
  forEach(type.properties, bind$1(function(p) {
@@ -14638,18 +15011,24 @@
14638
15011
  this.typeMap[name] = type;
14639
15012
  };
14640
15013
 
15014
+ /**
15015
+ * @callback IteratorFn
15016
+ * @param {RegisteredTypeDef} type
15017
+ * @param {boolean} inherited
15018
+ */
14641
15019
 
14642
15020
  /**
14643
15021
  * Traverse the type hierarchy from bottom to top,
14644
15022
  * calling iterator with (type, inherited) for all elements in
14645
15023
  * the inheritance chain.
14646
- *
14647
- * @param {Object} nsName
14648
- * @param {Function} iterator
15024
+ * @private
15025
+ * @param {Namespace} nsName
15026
+ * @param {IteratorFn} iterator
14649
15027
  * @param {Boolean} [trait=false]
14650
15028
  */
14651
15029
  Registry.prototype.mapTypes = function(nsName, iterator, trait) {
14652
15030
 
15031
+ /** @type {RegisteredTypeDef} */
14653
15032
  var type = isBuiltIn(nsName.name) ? { name: nsName.name } : this.typeMap[nsName.name];
14654
15033
 
14655
15034
  var self = this;
@@ -14695,13 +15074,10 @@
14695
15074
  forEach(type.traits, traverseTrait);
14696
15075
  };
14697
15076
 
14698
-
14699
15077
  /**
14700
15078
  * Returns the effective descriptor for a type.
14701
- *
14702
- * @param {String} type the namespaced name (ns:localName) of the type
14703
- *
14704
- * @return {Descriptor} the resulting effective descriptor
15079
+ * @param {Namespace['name']} name the namespaced name (ns:localName) of the type
15080
+ * @return {EffectiveDescriptor} the resulting effective descriptor
14705
15081
  */
14706
15082
  Registry.prototype.getEffectiveDescriptor = function(name) {
14707
15083
 
@@ -14721,15 +15097,23 @@
14721
15097
  return descriptor;
14722
15098
  };
14723
15099
 
14724
-
15100
+ /**
15101
+ * @private
15102
+ * @param {RegisteredTypeDef | EffectiveDescriptor} target
15103
+ * @param {RegisteredPackage} pkg
15104
+ */
14725
15105
  Registry.prototype.definePackage = function(target, pkg) {
14726
15106
  this.properties.define(target, '$pkg', { value: pkg });
14727
15107
  };
14728
15108
 
14729
-
14730
-
14731
15109
  // helpers ////////////////////////////
14732
15110
 
15111
+ /**
15112
+ * Checking already defined packages
15113
+ * @param {Record<string, RegisteredPackage>} packageMap
15114
+ * @param {PackageDefinition} pkg
15115
+ * @param {'prefix' | 'uri'} identifierKey
15116
+ */
14733
15117
  function ensureAvailable(packageMap, pkg, identifierKey) {
14734
15118
 
14735
15119
  var value = pkg[identifierKey];
@@ -14739,21 +15123,30 @@
14739
15123
  }
14740
15124
  }
14741
15125
 
15126
+ /**
15127
+ * @typedef {import('./moddle.js').default} Moddle
15128
+ * @typedef {import('./descriptor-builder.js').PropertyDescriptor} PropertyDesc
15129
+ * @typedef {import('./registry.js').EffectiveDescriptor} EffectiveDescriptor
15130
+ * @typedef {import('./factory.js').ModdleElement} ModdleElement
15131
+ * @typedef {import('./descriptor-builder.js').AnyTypeDescriptor} AnyTypeDescriptor
15132
+ */
15133
+
14742
15134
  /**
14743
15135
  * A utility that gets and sets properties of model elements.
14744
15136
  *
14745
- * @param {Model} model
15137
+ * @param {Moddle} model
14746
15138
  */
14747
15139
  function Properties(model) {
15140
+
15141
+ /** @type {Moddle} */
14748
15142
  this.model = model;
14749
15143
  }
14750
15144
 
14751
-
14752
15145
  /**
14753
15146
  * Sets a named property on the target element.
14754
15147
  * If the value is undefined, the property gets deleted.
14755
15148
  *
14756
- * @param {Object} target
15149
+ * @param {ModdleElement} target
14757
15150
  * @param {String} name
14758
15151
  * @param {Object} value
14759
15152
  */
@@ -14795,7 +15188,7 @@
14795
15188
  /**
14796
15189
  * Returns the named property of the given element
14797
15190
  *
14798
- * @param {Object} target
15191
+ * @param {ModdleElement} target
14799
15192
  * @param {String} name
14800
15193
  *
14801
15194
  * @return {Object}
@@ -14818,13 +15211,12 @@
14818
15211
  return target[propertyName];
14819
15212
  };
14820
15213
 
14821
-
14822
15214
  /**
14823
15215
  * Define a property on the target element
14824
- *
14825
- * @param {Object} target
15216
+ * @template [T=any]
15217
+ * @param {NonNullable<T>} target
14826
15218
  * @param {String} name
14827
- * @param {Object} options
15219
+ * @param {PropertyDescriptor} options
14828
15220
  */
14829
15221
  Properties.prototype.define = function(target, name, options) {
14830
15222
 
@@ -14844,9 +15236,11 @@
14844
15236
  Object.defineProperty(target, name, options);
14845
15237
  };
14846
15238
 
14847
-
14848
15239
  /**
14849
15240
  * Define the descriptor for an element
15241
+ * @template [T=any]
15242
+ * @param {NonNullable<T>} target
15243
+ * @param {EffectiveDescriptor | AnyTypeDescriptor} descriptor
14850
15244
  */
14851
15245
  Properties.prototype.defineDescriptor = function(target, descriptor) {
14852
15246
  this.define(target, '$descriptor', { value: descriptor });
@@ -14854,6 +15248,9 @@
14854
15248
 
14855
15249
  /**
14856
15250
  * Define the model for an element
15251
+ * @template [T=any]
15252
+ * @param {NonNullable<T>} target
15253
+ * @param {Moddle} model
14857
15254
  */
14858
15255
  Properties.prototype.defineModel = function(target, model) {
14859
15256
  this.define(target, '$model', { value: model });
@@ -14862,10 +15259,10 @@
14862
15259
  /**
14863
15260
  * Return property with the given name on the element.
14864
15261
  *
14865
- * @param {any} target
15262
+ * @param {ModdleElement} target
14866
15263
  * @param {string} name
14867
15264
  *
14868
- * @return {object | null} property
15265
+ * @return {PropertyDesc | null} property
14869
15266
  */
14870
15267
  Properties.prototype.getProperty = function(target, name) {
14871
15268
 
@@ -14914,6 +15311,83 @@
14914
15311
  return name.replace(/^:/, '');
14915
15312
  }
14916
15313
 
15314
+ /**
15315
+ * @typedef {import('./registry.js').RegisteredTypeDef} RegisteredTypeDef
15316
+ * @typedef {import('./registry.js').RegisteredPackage} RegisteredPackage
15317
+ * @typedef {import('./base.js').default} BaseElement
15318
+ * @typedef {import('./descriptor-builder.js').EffectiveDescriptor} EffectiveDescriptor
15319
+ * @typedef {import('./descriptor-builder.js').AnyTypeDescriptor} AnyTypeDescriptor
15320
+ * @typedef {import('./descriptor-builder.js').PropertyDescriptor} PropertyDescriptor
15321
+ */
15322
+
15323
+ /**
15324
+ * @template [T=Record<string,any>]
15325
+ * @typedef {import('./factory.js').ModdleElement<T>} ModdleElement
15326
+ * @typedef {import('./factory.js').ModdleElementType<T>} ModdleElementType
15327
+ * @typedef {import('./factory.js').AnyModdleElement<T>} AnyModdleElement
15328
+ */
15329
+
15330
+ /**
15331
+ * Package definition
15332
+ * @typedef {{
15333
+ * $schema?: string;
15334
+ * name: string;
15335
+ * prefix: string;
15336
+ * types?: Array<TypeDefinition>;
15337
+ * [key: string]: any;
15338
+ * } & PackageDefinitionXmlExtension} PackageDefinition
15339
+ */
15340
+
15341
+ /**
15342
+ * Set of extended parameters for package definition used in moddle-xml.
15343
+ * @typedef {{
15344
+ * uri?: string;
15345
+ * xml?: {
15346
+ * tagAlias?: 'lowerCase';
15347
+ * typePrefix?: string;
15348
+ * };
15349
+ * }} PackageDefinitionXmlExtension
15350
+ */
15351
+
15352
+ /**
15353
+ * Type definition in declaration in package
15354
+ * @typedef {{
15355
+ * name: string;
15356
+ * isAbstract?: boolean;
15357
+ * properties?: Array<PropertyDefinition>;
15358
+ * superClass?: Array<string>;
15359
+ * extends?: Array<string>;
15360
+ * meta?: Record<string, *>;
15361
+ * [key: string]: any;
15362
+ * }} TypeDefinition
15363
+ */
15364
+
15365
+ /**
15366
+ * Set of extended parameters for property definition used in moddle-xml.
15367
+ * @typedef {{
15368
+ * isBody?: boolean;
15369
+ * isAttr?: boolean;
15370
+ * xml?: {
15371
+ * serialize?: string;
15372
+ * };
15373
+ * }} PropertyDefinitionXmlExtension
15374
+ */
15375
+
15376
+ /**
15377
+ * Property definition of type definition
15378
+ * @typedef {{
15379
+ * name: string;
15380
+ * type: 'String' | 'Boolean' | 'Integer' | 'Real' | string;
15381
+ * default?: string | boolean | number;
15382
+ * isMany?: boolean;
15383
+ * isReference?: boolean;
15384
+ * isId?: boolean;
15385
+ * redefines?: string;
15386
+ * replaces?: string;
15387
+ * [key: string]: any;
15388
+ * } & PropertyDefinitionXmlExtension} PropertyDefinition
15389
+ */
15390
+
14917
15391
  // Moddle implementation /////////////////////////////////////////////////
14918
15392
 
14919
15393
  /**
@@ -14923,7 +15397,7 @@
14923
15397
  *
14924
15398
  * @example
14925
15399
  *
14926
- * var Moddle = require('moddle');
15400
+ * import Moddle from 'moddle';
14927
15401
  *
14928
15402
  * var pkg = {
14929
15403
  * name: 'mypackage',
@@ -14935,23 +15409,31 @@
14935
15409
  *
14936
15410
  * var moddle = new Moddle([pkg]);
14937
15411
  *
14938
- * @param {Array<Package>} packages the packages to contain
14939
- *
14940
- * @param { { strict?: boolean } } [config] moddle configuration
15412
+ * @param {Array<PackageDefinition> | Record<string,PackageDefinition>} packages the packages to contain
15413
+ * @param {{ strict?: boolean }} [config={}] moddle configuration
14941
15414
  */
14942
15415
  function Moddle(packages, config = {}) {
14943
15416
 
15417
+ /** @type Readonly<Properties> */
14944
15418
  this.properties = new Properties(this);
14945
15419
 
15420
+ /** @type Readonly<Factory> */
14946
15421
  this.factory = new Factory(this, this.properties);
14947
- this.registry = new Registry(packages, this.properties);
14948
15422
 
14949
- this.typeCache = {};
15423
+ /** @type Readonly<Registry> */
15424
+ this.registry = new Registry(packages, this.properties);
14950
15425
 
15426
+ /**
15427
+ * @type {Record<string,ModdleElementType>}
15428
+ */
15429
+ this.typeCache = Object.create(null);
15430
+
15431
+ /**
15432
+ * @type {Readonly<{readonly strict?: boolean}>}
15433
+ */
14951
15434
  this.config = config;
14952
15435
  }
14953
15436
 
14954
-
14955
15437
  /**
14956
15438
  * Create an instance of the specified type.
14957
15439
  *
@@ -14962,9 +15444,10 @@
14962
15444
  * var foo = moddle.create('my:Foo');
14963
15445
  * var bar = moddle.create('my:Bar', { id: 'BAR_1' });
14964
15446
  *
14965
- * @param {String|Object} descriptor the type descriptor or name know to the model
14966
- * @param {Object} attrs a number of attributes to initialize the model instance with
14967
- * @return {Object} model instance
15447
+ * @template [T=Record<string,any>]
15448
+ * @param {String|EffectiveDescriptor} descriptor the type descriptor or name know to the model
15449
+ * @param {Partial<T>} [attrs] a number of attributes to initialize the model instance with
15450
+ * @return {ModdleElement<T>} model instance
14968
15451
  */
14969
15452
  Moddle.prototype.create = function(descriptor, attrs) {
14970
15453
  var Type = this.getType(descriptor);
@@ -14976,7 +15459,6 @@
14976
15459
  return new Type(attrs);
14977
15460
  };
14978
15461
 
14979
-
14980
15462
  /**
14981
15463
  * Returns the type representing a given descriptor
14982
15464
  *
@@ -14987,8 +15469,9 @@
14987
15469
  * var Foo = moddle.getType('my:Foo');
14988
15470
  * var foo = new Foo({ 'id' : 'FOO_1' });
14989
15471
  *
14990
- * @param {String|Object} descriptor the type descriptor or name know to the model
14991
- * @return {Object} the type representing the descriptor
15472
+ * @template [T=Record<string,any>]
15473
+ * @param {String|EffectiveDescriptor} descriptor the type descriptor or name know to the model
15474
+ * @return {ModdleElementType<T>} the type representing the descriptor
14992
15475
  */
14993
15476
  Moddle.prototype.getType = function(descriptor) {
14994
15477
 
@@ -15006,7 +15489,6 @@
15006
15489
  return type;
15007
15490
  };
15008
15491
 
15009
-
15010
15492
  /**
15011
15493
  * Creates an any-element type to be used within model instances.
15012
15494
  *
@@ -15028,16 +15510,17 @@
15028
15510
  *
15029
15511
  * // go ahead and serialize the stuff
15030
15512
  *
15031
- *
15513
+ * @template [T=Record<string, any>]
15032
15514
  * @param {String} name the name of the element
15033
15515
  * @param {String} nsUri the namespace uri of the element
15034
- * @param {Object} [properties] a map of properties to initialize the instance with
15035
- * @return {Object} the any type instance
15516
+ * @param {T} [properties] a map of properties to initialize the instance with
15517
+ * @return {AnyModdleElement<T>} the any type instance
15036
15518
  */
15037
15519
  Moddle.prototype.createAny = function(name, nsUri, properties) {
15038
15520
 
15039
15521
  var nameNs = parseName(name);
15040
15522
 
15523
+ /** @type AnyModdleElement */
15041
15524
  var element = {
15042
15525
  $type: name,
15043
15526
  $instanceOf: function(type) {
@@ -15051,6 +15534,7 @@
15051
15534
  }
15052
15535
  };
15053
15536
 
15537
+ /** @type AnyTypeDescriptor */
15054
15538
  var descriptor = {
15055
15539
  name: name,
15056
15540
  isGeneric: true,
@@ -15081,8 +15565,8 @@
15081
15565
 
15082
15566
  /**
15083
15567
  * Returns a registered package by uri or prefix
15084
- *
15085
- * @return {Object} the package
15568
+ * @param {string} uriOrPrefix
15569
+ * @return {RegisteredPackage} the package
15086
15570
  */
15087
15571
  Moddle.prototype.getPackage = function(uriOrPrefix) {
15088
15572
  return this.registry.getPackage(uriOrPrefix);
@@ -15091,7 +15575,7 @@
15091
15575
  /**
15092
15576
  * Returns a snapshot of all known packages
15093
15577
  *
15094
- * @return {Object} the package
15578
+ * @return {Readonly<Array<RegisteredPackage>>} the package
15095
15579
  */
15096
15580
  Moddle.prototype.getPackages = function() {
15097
15581
  return this.registry.getPackages();
@@ -15099,16 +15583,25 @@
15099
15583
 
15100
15584
  /**
15101
15585
  * Returns the descriptor for an element
15586
+ * @param {ModdleElement | ModdleElementType} element
15587
+ * @return {EffectiveDescriptor}
15102
15588
  */
15103
15589
  Moddle.prototype.getElementDescriptor = function(element) {
15104
15590
  return element.$descriptor;
15105
15591
  };
15106
15592
 
15107
15593
  /**
15594
+ * @overload
15108
15595
  * Returns true if the given descriptor or instance
15109
15596
  * represents the given type.
15110
- *
15111
- * May be applied to this, if element is omitted.
15597
+ * @param {ModdleElement | ModdleElementType} element
15598
+ * @param {string} type
15599
+ * @return {boolean}
15600
+ */
15601
+ /**
15602
+ * @overload
15603
+ * @param {string} type
15604
+ * @return {boolean}
15112
15605
  */
15113
15606
  Moddle.prototype.hasType = function(element, type) {
15114
15607
  if (type === undefined) {
@@ -15123,13 +15616,18 @@
15123
15616
 
15124
15617
  /**
15125
15618
  * Returns the descriptor of an elements named property
15619
+ * @param {ModdleElement | ModdleElementType} element
15620
+ * @param {string} property
15621
+ * @return {PropertyDescriptor}
15126
15622
  */
15127
15623
  Moddle.prototype.getPropertyDescriptor = function(element, property) {
15128
15624
  return this.getElementDescriptor(element).propertiesByName[property];
15129
15625
  };
15130
15626
 
15131
15627
  /**
15132
- * Returns a mapped type's descriptor
15628
+ * Return registered type definition
15629
+ * @param {string} type
15630
+ * @return {RegisteredTypeDef}
15133
15631
  */
15134
15632
  Moddle.prototype.getTypeDescriptor = function(type) {
15135
15633
  return this.registry.typeMap[type];
@@ -15213,19 +15711,24 @@
15213
15711
  }
15214
15712
 
15215
15713
  function cloneNsMatrix(nsMatrix) {
15216
- var clone = {}, key;
15714
+ var clone = Object.create(null), key;
15217
15715
  for (key in nsMatrix) {
15218
15716
  clone[key] = nsMatrix[key];
15219
15717
  }
15220
15718
  return clone;
15221
15719
  }
15222
15720
 
15721
+ // per-matrix element name cache; stored under a symbol so it is
15722
+ // invisible to the `for (key in nsMatrix)` clone above and gets
15723
+ // collected together with the matrix it belongs to
15724
+ var NAME_CACHE = Symbol('nameCache');
15725
+
15223
15726
  function uriPrefix(prefix) {
15224
15727
  return prefix + '$uri';
15225
15728
  }
15226
15729
 
15227
15730
  function buildNsMatrix(nsUriToPrefix) {
15228
- var nsMatrix = {},
15731
+ var nsMatrix = Object.create(null),
15229
15732
  uri,
15230
15733
  prefix;
15231
15734
 
@@ -15273,6 +15776,33 @@
15273
15776
 
15274
15777
  var getContext = noopGetContext;
15275
15778
 
15779
+ /**
15780
+ * Are we currently consuming a chunked stream of XML,
15781
+ * i.e. is a `write` call in progress that may be followed
15782
+ * by more chunks?
15783
+ *
15784
+ * @type {boolean}
15785
+ */
15786
+ var streaming = false;
15787
+
15788
+ /**
15789
+ * Did we already encounter the root tag?
15790
+ *
15791
+ * Persisted across `write` calls so we can detect a missing
15792
+ * start tag once the stream ends.
15793
+ *
15794
+ * @type {boolean}
15795
+ */
15796
+ var rootTagFound = false;
15797
+
15798
+ /**
15799
+ * Not yet parsed remainder of the previously written chunk,
15800
+ * i.e. an incomplete token that awaits more input.
15801
+ *
15802
+ * @type {string}
15803
+ */
15804
+ var leftoverXml = '';
15805
+
15276
15806
  /**
15277
15807
  * Do we need to parse the current elements attributes for namespaces?
15278
15808
  *
@@ -15301,6 +15831,13 @@
15301
15831
  */
15302
15832
  var parseStop = false;
15303
15833
 
15834
+ /**
15835
+ * Namespace + node state shared across (streamed) parse runs.
15836
+ */
15837
+ var nsMatrixStack,
15838
+ nsMatrix,
15839
+ nodeStack;
15840
+
15304
15841
  /**
15305
15842
  * A map of { uri: prefix } used by the parser.
15306
15843
  *
@@ -15399,7 +15936,7 @@
15399
15936
  throw error$1('required args <nsMap={}>');
15400
15937
  }
15401
15938
 
15402
- var _nsUriToPrefix = {}, k;
15939
+ var _nsUriToPrefix = Object.create(null), k;
15403
15940
 
15404
15941
  for (k in nsMap) {
15405
15942
  _nsUriToPrefix[k] = nsMap[k];
@@ -15412,7 +15949,22 @@
15412
15949
  };
15413
15950
 
15414
15951
  /**
15415
- * Parse xml string.
15952
+ * Reset the parser state before a (streamed) parse run.
15953
+ */
15954
+ function resetState() {
15955
+ nsMatrixStack = isNamespace ? [] : null;
15956
+ nsMatrix = isNamespace ? buildNsMatrix(nsUriToPrefix) : null;
15957
+ nodeStack = [];
15958
+
15959
+ getContext = noopGetContext;
15960
+ parseStop = false;
15961
+ returnError = null;
15962
+ rootTagFound = false;
15963
+ leftoverXml = '';
15964
+ }
15965
+
15966
+ /**
15967
+ * Parse a complete xml string.
15416
15968
  *
15417
15969
  * @param {string} xml
15418
15970
  *
@@ -15423,7 +15975,11 @@
15423
15975
  throw error$1('required args <xml=string>');
15424
15976
  }
15425
15977
 
15426
- returnError = null;
15978
+ if (streaming) {
15979
+ throw error$1('parse during stream; call end() first');
15980
+ }
15981
+
15982
+ resetState();
15427
15983
 
15428
15984
  parse(xml);
15429
15985
 
@@ -15433,6 +15989,60 @@
15433
15989
  return returnError;
15434
15990
  };
15435
15991
 
15992
+ /**
15993
+ * Write the next chunk of a streamed xml string.
15994
+ *
15995
+ * Parse events are emitted for all complete tokens; an
15996
+ * incomplete trailing token is buffered until the next
15997
+ * `write` or, ultimately, reported as an error on `end`.
15998
+ *
15999
+ * @param {string} xml
16000
+ *
16001
+ * @return {Parser} self
16002
+ */
16003
+ this['write'] = function(xml) {
16004
+ if (typeof xml !== 'string') {
16005
+ throw error$1('required args <xml=string>');
16006
+ }
16007
+
16008
+ if (!streaming) {
16009
+ resetState();
16010
+ streaming = true;
16011
+ }
16012
+
16013
+ if (!returnError) {
16014
+ leftoverXml = parse(leftoverXml + xml, true) || '';
16015
+ }
16016
+
16017
+ return this;
16018
+ };
16019
+
16020
+ /**
16021
+ * Finish parsing a streamed xml string, i.e. signal that
16022
+ * no more chunks will be written.
16023
+ *
16024
+ * @return {Error} returnError, if not thrown
16025
+ */
16026
+ this['end'] = function() {
16027
+ if (!streaming) {
16028
+ resetState();
16029
+ }
16030
+
16031
+ // flush the buffered remainder, now treating an incomplete
16032
+ // trailing token as a hard error
16033
+ streaming = false;
16034
+
16035
+ if (!returnError) {
16036
+ parse(leftoverXml);
16037
+ }
16038
+
16039
+ leftoverXml = '';
16040
+ getContext = noopGetContext;
16041
+ parseStop = false;
16042
+
16043
+ return returnError;
16044
+ };
16045
+
15436
16046
  /**
15437
16047
  * Stop parsing.
15438
16048
  */
@@ -15443,13 +16053,26 @@
15443
16053
  /**
15444
16054
  * Parse string, invoking configured listeners on element.
15445
16055
  *
15446
- * @param {string} xml
16056
+ * Namespace and node stack state is shared across (streamed)
16057
+ * invocations via the enclosing parser scope.
16058
+ *
16059
+ * Internal note: while `streaming`, an incomplete trailing token
16060
+ * is not treated as an error but returned so `write` can buffer it
16061
+ * and prepend it to the next chunk. This buffered remainder is an
16062
+ * implementation detail of the streaming buffer, not a public API;
16063
+ * outside of that case the return value is `undefined`.
16064
+ *
16065
+ * @param {string} xml
16066
+ * @param {boolean} streaming
16067
+ *
16068
+ * @return {string|undefined} buffered remainder, streaming internal use only
15447
16069
  */
15448
- function parse(xml) {
15449
- var nsMatrixStack = isNamespace ? [] : null,
15450
- nsMatrix = isNamespace ? buildNsMatrix(nsUriToPrefix) : null,
15451
- _nsMatrix,
15452
- nodeStack = [],
16070
+ function parse(xml, streaming = false) {
16071
+
16072
+ var elNameCache = null,
16073
+ elNameCacheMatrix = null;
16074
+
16075
+ var _nsMatrix,
15453
16076
  anonymousNsCount = 0,
15454
16077
  tagStart = false,
15455
16078
  tagEnd = false,
@@ -15466,6 +16089,36 @@
15466
16089
  cachedAttrs // false = parsed with errors, null = needs parsing
15467
16090
  ;
15468
16091
 
16092
+ /**
16093
+ * Normalize a namespaced attribute name against the current
16094
+ * namespace matrix.
16095
+ *
16096
+ * @param {string} name
16097
+ * @param {string|boolean} defaultAlias
16098
+ *
16099
+ * @return {string|null} normalized name, or `null` when the prefix
16100
+ * has no namespace (a warning is emitted)
16101
+ */
16102
+ function normalizeAttrName(name, defaultAlias) {
16103
+ var w = name.indexOf(':');
16104
+
16105
+ if (w === -1) {
16106
+ return name;
16107
+ }
16108
+
16109
+ // normalize ns attribute name
16110
+ var nsName = nsMatrix[name.substring(0, w)];
16111
+
16112
+ if (!nsName) {
16113
+ handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
16114
+ return null;
16115
+ }
16116
+
16117
+ return defaultAlias === nsName
16118
+ ? name.substr(w + 1)
16119
+ : nsName + name.substr(w);
16120
+ }
16121
+
15469
16122
  /**
15470
16123
  * Parse attributes on demand and returns the parsed attributes.
15471
16124
  *
@@ -15481,7 +16134,6 @@
15481
16134
 
15482
16135
  var nsUri,
15483
16136
  nsUriPrefix,
15484
- nsName,
15485
16137
  defaultAlias = isNamespace && nsMatrix['xmlns'],
15486
16138
  attrList = isNamespace && maybeNS ? [] : null,
15487
16139
  i = attrsStart,
@@ -15493,7 +16145,7 @@
15493
16145
  alias,
15494
16146
  name,
15495
16147
  attrs = {},
15496
- seenAttrs = {},
16148
+ seenAttrs = new Set(),
15497
16149
  skipAttr,
15498
16150
  w,
15499
16151
  j;
@@ -15633,12 +16285,12 @@
15633
16285
  }
15634
16286
 
15635
16287
  // check attribute re-declaration
15636
- if (name in seenAttrs) {
16288
+ if (seenAttrs.has(name)) {
15637
16289
  handleWarning('attribute <' + name + '> already defined');
15638
16290
  continue;
15639
16291
  }
15640
16292
 
15641
- seenAttrs[name] = true;
16293
+ seenAttrs.add(name);
15642
16294
 
15643
16295
  if (!isNamespace) {
15644
16296
  attrs[name] = value;
@@ -15710,24 +16362,12 @@
15710
16362
 
15711
16363
  // handle attributes on element without
15712
16364
  // namespace declarations
15713
- w = name.indexOf(':');
15714
- if (w === -1) {
15715
- attrs[name] = value;
15716
- continue;
15717
- }
16365
+ name = normalizeAttrName(name, defaultAlias);
15718
16366
 
15719
- // normalize ns attribute name
15720
- if (!(nsName = nsMatrix[name.substring(0, w)])) {
15721
- handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
16367
+ if (name === null) {
15722
16368
  continue;
15723
16369
  }
15724
16370
 
15725
- name = defaultAlias === nsName
15726
- ? name.substr(w + 1)
15727
- : nsName + name.substr(w);
15728
-
15729
- // end: normalize ns attribute name
15730
-
15731
16371
  attrs[name] = value;
15732
16372
  }
15733
16373
 
@@ -15738,24 +16378,11 @@
15738
16378
  // normalize captured attributes
15739
16379
  for (i = 0, l = attrList.length; i < l; i++) {
15740
16380
 
15741
- name = attrList[i++];
16381
+ name = normalizeAttrName(attrList[i++], defaultAlias);
15742
16382
  value = attrList[i];
15743
16383
 
15744
- w = name.indexOf(':');
15745
-
15746
- if (w !== -1) {
15747
-
15748
- // normalize ns attribute name
15749
- if (!(nsName = nsMatrix[name.substring(0, w)])) {
15750
- handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
15751
- continue;
15752
- }
15753
-
15754
- name = defaultAlias === nsName
15755
- ? name.substr(w + 1)
15756
- : nsName + name.substr(w);
15757
-
15758
- // end: normalize ns attribute name
16384
+ if (name === null) {
16385
+ continue;
15759
16386
  }
15760
16387
 
15761
16388
  attrs[name] = value;
@@ -15855,13 +16482,19 @@
15855
16482
  i = xml.indexOf('<', j);
15856
16483
  }
15857
16484
 
15858
- // parse end
16485
+ // no (more) tags found
15859
16486
  if (i === -1) {
16487
+
16488
+ // buffer the remainder for the next chunk
16489
+ if (streaming) {
16490
+ return xml.substring(j);
16491
+ }
16492
+
15860
16493
  if (nodeStack.length) {
15861
16494
  return handleError('unexpected end of file');
15862
16495
  }
15863
16496
 
15864
- if (j === 0) {
16497
+ if (!rootTagFound) {
15865
16498
  return handleError('missing start tag');
15866
16499
  }
15867
16500
 
@@ -15874,6 +16507,11 @@
15874
16507
  return;
15875
16508
  }
15876
16509
 
16510
+ // remember that we saw the root tag
16511
+ if (!rootTagFound) {
16512
+ rootTagFound = true;
16513
+ }
16514
+
15877
16515
  // parse text
15878
16516
  if (j !== i) {
15879
16517
 
@@ -15906,6 +16544,9 @@
15906
16544
  if (q === 91 && xml.substr(i + 3, 6) === 'CDATA[') { // 91 == "["
15907
16545
  j = xml.indexOf(']]>', i);
15908
16546
  if (j === -1) {
16547
+ if (streaming) {
16548
+ return xml.substring(i);
16549
+ }
15909
16550
  return handleError('unclosed cdata');
15910
16551
  }
15911
16552
 
@@ -15924,6 +16565,9 @@
15924
16565
  if (q === 45 && xml.charCodeAt(i + 3) === 45) { // 45 == "-"
15925
16566
  j = xml.indexOf('-->', i);
15926
16567
  if (j === -1) {
16568
+ if (streaming) {
16569
+ return xml.substring(i);
16570
+ }
15927
16571
  return handleError('unclosed comment');
15928
16572
  }
15929
16573
 
@@ -15944,6 +16588,9 @@
15944
16588
  if (w === 63) { // "?"
15945
16589
  j = xml.indexOf('?>', i);
15946
16590
  if (j === -1) {
16591
+ if (streaming) {
16592
+ return xml.substring(i);
16593
+ }
15947
16594
  return handleError('unclosed question');
15948
16595
  }
15949
16596
 
@@ -15964,6 +16611,9 @@
15964
16611
  for (x = i + 1; ; x++) {
15965
16612
  v = xml.charCodeAt(x);
15966
16613
  if (isNaN(v)) {
16614
+ if (streaming) {
16615
+ return xml.substring(i);
16616
+ }
15967
16617
  j = -1;
15968
16618
  return handleError('unclosed tag');
15969
16619
  }
@@ -16103,30 +16753,49 @@
16103
16753
 
16104
16754
  _elementName = elementName;
16105
16755
 
16106
- w = elementName.indexOf(':');
16107
- if (w !== -1) {
16108
- xmlns = nsMatrix[elementName.substring(0, w)];
16109
-
16110
- // prefix given; namespace must exist
16111
- if (!xmlns) {
16112
- return handleError('missing namespace on <' + _elementName + '>');
16756
+ // memoize normalized names per namespace matrix; tag names
16757
+ // typically repeat heavily and the matrix rarely changes.
16758
+ if (elNameCacheMatrix !== nsMatrix) {
16759
+ elNameCache = nsMatrix[NAME_CACHE];
16760
+ if (elNameCache === undefined) {
16761
+ elNameCache = nsMatrix[NAME_CACHE] = Object.create(null);
16113
16762
  }
16763
+ elNameCacheMatrix = nsMatrix;
16764
+ }
16765
+
16766
+ var _cachedName = elNameCache[elementName];
16114
16767
 
16115
- elementName = elementName.substr(w + 1);
16768
+ if (_cachedName !== undefined) {
16769
+ elementName = _cachedName;
16116
16770
  } else {
16117
- xmlns = nsMatrix['xmlns'];
16118
-
16119
- // if no default namespace is defined,
16120
- // we'll import the element as anonymous.
16121
- //
16122
- // it is up to users to correct that to the document defined
16123
- // targetNamespace, or whatever their undersanding of the
16124
- // XML spec mandates.
16125
- }
16126
16771
 
16127
- // adjust namespace prefixs as configured
16128
- if (xmlns) {
16129
- elementName = xmlns + ':' + elementName;
16772
+ w = elementName.indexOf(':');
16773
+ if (w !== -1) {
16774
+ xmlns = nsMatrix[elementName.substring(0, w)];
16775
+
16776
+ // prefix given; namespace must exist
16777
+ if (!xmlns) {
16778
+ return handleError('missing namespace on <' + _elementName + '>');
16779
+ }
16780
+
16781
+ elementName = elementName.substr(w + 1);
16782
+ } else {
16783
+ xmlns = nsMatrix['xmlns'];
16784
+
16785
+ // if no default namespace is defined,
16786
+ // we'll import the element as anonymous.
16787
+ //
16788
+ // it is up to users to correct that to the document defined
16789
+ // targetNamespace, or whatever their undersanding of the
16790
+ // XML spec mandates.
16791
+ }
16792
+
16793
+ // adjust namespace prefixs as configured
16794
+ if (xmlns) {
16795
+ elementName = xmlns + ':' + elementName;
16796
+ }
16797
+
16798
+ elNameCache[_elementName] = elementName;
16130
16799
  }
16131
16800
 
16132
16801
  }
@@ -17150,10 +17819,48 @@
17150
17819
  return this.defaultPrefixMap[prefix];
17151
17820
  };
17152
17821
 
17822
+ /**
17823
+ * Return the first truthy `fn(scope)` walking this scope and its ancestors.
17824
+ *
17825
+ * @param {(scope: Namespaces) => any} fn
17826
+ *
17827
+ * @return {any}
17828
+ */
17829
+ Namespaces.prototype.resolve = function(fn) {
17830
+
17831
+ var scope = this;
17832
+
17833
+ while (scope) {
17834
+ var resolved = fn(scope);
17835
+
17836
+ if (resolved) {
17837
+ return resolved;
17838
+ }
17839
+
17840
+ scope = scope.parent;
17841
+ }
17842
+ };
17843
+
17844
+ /**
17845
+ * Call `fn(scope)` for this scope and each of its ancestors.
17846
+ *
17847
+ * @param {(scope: Namespaces) => void} fn
17848
+ */
17849
+ Namespaces.prototype.eachScope = function(fn) {
17850
+
17851
+ var scope = this;
17852
+
17853
+ while (scope) {
17854
+ fn(scope);
17855
+
17856
+ scope = scope.parent;
17857
+ }
17858
+ };
17859
+
17153
17860
  Namespaces.prototype.byUri = function(uri) {
17154
- return this.uriMap[uri] || (
17155
- this.parent && this.parent.byUri(uri)
17156
- );
17861
+ return this.resolve(function(scope) {
17862
+ return scope.uriMap[uri];
17863
+ });
17157
17864
  };
17158
17865
 
17159
17866
  Namespaces.prototype.add = function(ns, isWellknown) {
@@ -17170,9 +17877,11 @@
17170
17877
  };
17171
17878
 
17172
17879
  Namespaces.prototype.uriByPrefix = function(prefix) {
17173
- return this.prefixMap[prefix || 'xmlns'] || (
17174
- this.parent && this.parent.uriByPrefix(prefix)
17175
- );
17880
+ var key = prefix || 'xmlns';
17881
+
17882
+ return this.resolve(function(scope) {
17883
+ return scope.prefixMap[key];
17884
+ });
17176
17885
  };
17177
17886
 
17178
17887
  Namespaces.prototype.mapPrefix = function(prefix, uri) {
@@ -17188,12 +17897,9 @@
17188
17897
  var uri = ns.uri;
17189
17898
  var nsKey = this.getNSKey(ns);
17190
17899
 
17191
- this.used[nsKey] = this.byUri(uri);
17192
-
17193
- // Inform parent recursively about the usage of this NS
17194
- if (this.parent) {
17195
- this.parent.logUsed(ns);
17196
- }
17900
+ this.eachScope(function(scope) {
17901
+ scope.used[nsKey] = scope.byUri(uri);
17902
+ });
17197
17903
  };
17198
17904
 
17199
17905
  Namespaces.prototype.getUsed = function(ns) {
@@ -17410,20 +18116,32 @@
17410
18116
  }
17411
18117
 
17412
18118
  ElementSerializer.prototype.build = function(element) {
18119
+ buildTree(this, element);
18120
+
18121
+ return this;
18122
+ };
18123
+
18124
+ /**
18125
+ * Enter the element during the build walk: register its namespaces, tag
18126
+ * name and attributes, then return the deferred, ordered work to build its
18127
+ * child nodes (empty when it has none).
18128
+ *
18129
+ * @param {Object} element
18130
+ *
18131
+ * @return {Array<Object>} ordered, deferred work items to build the children
18132
+ */
18133
+ ElementSerializer.prototype.enter = function(element) {
17413
18134
  this.element = element;
17414
18135
 
17415
18136
  var elementDescriptor = element.$descriptor,
17416
18137
  propertyDescriptor = this.propertyDescriptor;
17417
18138
 
17418
- var otherAttrs,
17419
- properties;
17420
-
17421
18139
  var isGeneric = elementDescriptor.isGeneric;
17422
18140
 
17423
18141
  if (isGeneric) {
17424
- otherAttrs = this.parseGenericNsAttributes(element);
18142
+ this.otherAttrs = this.parseGenericNsAttributes(element);
17425
18143
  } else {
17426
- otherAttrs = this.parseNsAttributes(element);
18144
+ this.otherAttrs = this.parseNsAttributes(element);
17427
18145
  }
17428
18146
 
17429
18147
  if (propertyDescriptor) {
@@ -17432,21 +18150,26 @@
17432
18150
  this.ns = this.nsTagName(elementDescriptor);
17433
18151
  }
17434
18152
 
17435
- // compute tag name
17436
18153
  this.tagName = this.addTagName(this.ns);
17437
18154
 
17438
18155
  if (isGeneric) {
17439
- this.parseGenericContainments(element);
17440
- } else {
17441
- properties = getSerializableProperties(element);
17442
-
17443
- this.parseAttributes(filterAttributes(properties));
17444
- this.parseContainments(filterContained(properties));
18156
+ return this.collectGenericContainments(element);
17445
18157
  }
17446
18158
 
17447
- this.parseGenericAttributes(element, otherAttrs);
18159
+ var properties = getSerializableProperties(element);
17448
18160
 
17449
- return this;
18161
+ this.parseAttributes(filterAttributes(properties));
18162
+
18163
+ return this.collectContainments(filterContained(properties));
18164
+ };
18165
+
18166
+ /**
18167
+ * Exit the element during the build walk: register its generic attributes,
18168
+ * which must happen after the children so namespace usage is logged in
18169
+ * document order.
18170
+ */
18171
+ ElementSerializer.prototype.exit = function() {
18172
+ this.parseGenericAttributes(this.element, this.otherAttrs);
17450
18173
  };
17451
18174
 
17452
18175
  ElementSerializer.prototype.nsTagName = function(descriptor) {
@@ -17509,20 +18232,34 @@
17509
18232
  );
17510
18233
  };
17511
18234
 
17512
- ElementSerializer.prototype.parseGenericContainments = function(element) {
17513
- var body = element.$body;
18235
+ ElementSerializer.prototype.collectGenericContainments = function(element) {
18236
+ var self = this,
18237
+ children = [];
18238
+
18239
+ var bodyText = element.$body;
17514
18240
 
17515
- if (body) {
17516
- this.body.push(new BodySerializer().build({ type: 'String' }, body));
18241
+ if (bodyText) {
18242
+ children.push({
18243
+ create: function() {
18244
+ return new BodySerializer().build({ type: 'String' }, bodyText);
18245
+ }
18246
+ });
17517
18247
  }
17518
18248
 
17519
- var children = element.$children;
18249
+ var genericChildren = element.$children;
17520
18250
 
17521
- if (children) {
17522
- forEach(children, child => {
17523
- this.body.push(new ElementSerializer(this).build(child));
18251
+ if (genericChildren) {
18252
+ forEach(genericChildren, function(child) {
18253
+ children.push({
18254
+ create: function() {
18255
+ return new ElementSerializer(self);
18256
+ },
18257
+ element: child
18258
+ });
17524
18259
  });
17525
18260
  }
18261
+
18262
+ return children;
17526
18263
  };
17527
18264
 
17528
18265
  ElementSerializer.prototype.parseNsAttribute = function(element, name, value) {
@@ -17601,7 +18338,6 @@
17601
18338
  self.addAttribute(self.nsAttributeName(attr.name), attr.value);
17602
18339
  } catch (e) {
17603
18340
 
17604
- // eslint-disable-next-line no-undef
17605
18341
  typeof console !== 'undefined' && console.warn(
17606
18342
  `missing namespace information for <${
17607
18343
  attr.name
@@ -17611,11 +18347,11 @@
17611
18347
  });
17612
18348
  };
17613
18349
 
17614
- ElementSerializer.prototype.parseContainments = function(properties) {
18350
+ ElementSerializer.prototype.collectContainments = function(properties) {
17615
18351
 
17616
18352
  var self = this,
17617
- body = this.body,
17618
- element = this.element;
18353
+ element = this.element,
18354
+ children = [];
17619
18355
 
17620
18356
  forEach(properties, function(p) {
17621
18357
  var value = element.get(p.name),
@@ -17627,14 +18363,26 @@
17627
18363
  }
17628
18364
 
17629
18365
  if (p.isBody) {
17630
- body.push(new BodySerializer().build(p, value[0]));
18366
+ children.push({
18367
+ create: function() {
18368
+ return new BodySerializer().build(p, value[0]);
18369
+ }
18370
+ });
17631
18371
  } else if (isSimple(p.type)) {
17632
18372
  forEach(value, function(v) {
17633
- body.push(new ValueSerializer(self.addTagName(self.nsPropertyTagName(p))).build(p, v));
18373
+ children.push({
18374
+ create: function() {
18375
+ return new ValueSerializer(self.addTagName(self.nsPropertyTagName(p))).build(p, v);
18376
+ }
18377
+ });
17634
18378
  });
17635
18379
  } else if (isReference) {
17636
18380
  forEach(value, function(v) {
17637
- body.push(new ReferenceSerializer(self.addTagName(self.nsPropertyTagName(p))).build(v));
18381
+ children.push({
18382
+ create: function() {
18383
+ return new ReferenceSerializer(self.addTagName(self.nsPropertyTagName(p))).build(v);
18384
+ }
18385
+ });
17638
18386
  });
17639
18387
  } else {
17640
18388
 
@@ -17643,32 +18391,33 @@
17643
18391
  var serialization = getSerialization(p);
17644
18392
 
17645
18393
  forEach(value, function(v) {
17646
- var serializer;
18394
+ children.push({
18395
+ create: function() {
18396
+ if (serialization) {
18397
+ if (serialization === SERIALIZE_PROPERTY) {
18398
+ return new ElementSerializer(self, p);
18399
+ }
17647
18400
 
17648
- if (serialization) {
17649
- if (serialization === SERIALIZE_PROPERTY) {
17650
- serializer = new ElementSerializer(self, p);
17651
- } else {
17652
- serializer = new TypeSerializer(self, p, serialization);
17653
- }
17654
- } else {
17655
- serializer = new ElementSerializer(self);
17656
- }
18401
+ return new TypeSerializer(self, p, serialization);
18402
+ }
17657
18403
 
17658
- body.push(serializer.build(v));
18404
+ return new ElementSerializer(self);
18405
+ },
18406
+ element: v
18407
+ });
17659
18408
  });
17660
18409
  }
17661
18410
  });
18411
+
18412
+ return children;
17662
18413
  };
17663
18414
 
17664
18415
  ElementSerializer.prototype.getNamespaces = function(local) {
17665
18416
 
17666
- var namespaces = this.namespaces,
17667
- parent = this.parent,
17668
- parentNamespaces;
18417
+ var namespaces = this.namespaces;
17669
18418
 
17670
18419
  if (!namespaces) {
17671
- parentNamespaces = parent && parent.getNamespaces();
18420
+ var parentNamespaces = this.getParentNamespaces();
17672
18421
 
17673
18422
  if (local || !parentNamespaces) {
17674
18423
  this.namespaces = namespaces = new Namespaces(parentNamespaces);
@@ -17680,6 +18429,38 @@
17680
18429
  return namespaces;
17681
18430
  };
17682
18431
 
18432
+ /**
18433
+ * Resolve, and memoize, the namespaces scope owned by the closest
18434
+ * ancestor serializer.
18435
+ *
18436
+ * @return {Namespaces|null}
18437
+ */
18438
+ ElementSerializer.prototype.getParentNamespaces = function() {
18439
+
18440
+ if (this.parentNamespaces !== undefined) {
18441
+ return this.parentNamespaces;
18442
+ }
18443
+
18444
+ var scope = null,
18445
+ ancestor = this.parent;
18446
+
18447
+ while (ancestor) {
18448
+ if (ancestor.namespaces) {
18449
+ scope = ancestor.namespaces;
18450
+ break;
18451
+ }
18452
+
18453
+ if (ancestor.parentNamespaces !== undefined) {
18454
+ scope = ancestor.parentNamespaces;
18455
+ break;
18456
+ }
18457
+
18458
+ ancestor = ancestor.parent;
18459
+ }
18460
+
18461
+ return (this.parentNamespaces = scope);
18462
+ };
18463
+
17683
18464
  ElementSerializer.prototype.logNamespace = function(ns, wellknown, local) {
17684
18465
  var namespaces = this.getNamespaces(local);
17685
18466
 
@@ -17828,40 +18609,77 @@
17828
18609
  };
17829
18610
 
17830
18611
  ElementSerializer.prototype.serializeTo = function(writer) {
17831
- var firstBody = this.body[0],
17832
- indent = firstBody && firstBody.constructor !== BodySerializer;
18612
+ serializeTree(this, writer);
18613
+ };
17833
18614
 
17834
- writer
17835
- .appendIndent()
17836
- .append('<' + this.tagName);
18615
+ /**
18616
+ * Serialize the serializer tree rooted at the given element serializer to
18617
+ * the writer, using an explicit stack to stay flat for nested documents.
18618
+ *
18619
+ * @param {ElementSerializer} root
18620
+ * @param {Object} writer
18621
+ */
18622
+ function serializeTree(root, writer) {
17837
18623
 
17838
- this.serializeAttributes(writer);
18624
+ var stack = [ { serializer: root, index: 0, opened: false, indent: false } ];
17839
18625
 
17840
- writer.append(firstBody ? '>' : ' />');
18626
+ while (stack.length) {
18627
+ var frame = stack[stack.length - 1],
18628
+ serializer = frame.serializer;
17841
18629
 
17842
- if (firstBody) {
18630
+ if (!frame.opened) {
18631
+ var firstBody = serializer.body[0];
18632
+
18633
+ frame.indent = firstBody && firstBody.constructor !== BodySerializer;
17843
18634
 
17844
- if (indent) {
17845
18635
  writer
17846
- .appendNewLine()
17847
- .indent();
18636
+ .appendIndent()
18637
+ .append('<' + serializer.tagName);
18638
+
18639
+ serializer.serializeAttributes(writer);
18640
+
18641
+ writer.append(firstBody ? '>' : ' />');
18642
+
18643
+ frame.opened = true;
18644
+
18645
+ if (!firstBody) {
18646
+ writer.appendNewLine();
18647
+ stack.pop();
18648
+ continue;
18649
+ }
18650
+
18651
+ if (frame.indent) {
18652
+ writer
18653
+ .appendNewLine()
18654
+ .indent();
18655
+ }
17848
18656
  }
17849
18657
 
17850
- forEach(this.body, function(b) {
17851
- b.serializeTo(writer);
17852
- });
18658
+ if (frame.index < serializer.body.length) {
18659
+ var child = serializer.body[frame.index++];
17853
18660
 
17854
- if (indent) {
18661
+ if (child instanceof ElementSerializer) {
18662
+ stack.push({ serializer: child, index: 0, opened: false, indent: false });
18663
+ } else {
18664
+ child.serializeTo(writer);
18665
+ }
18666
+
18667
+ continue;
18668
+ }
18669
+
18670
+ if (frame.indent) {
17855
18671
  writer
17856
18672
  .unindent()
17857
18673
  .appendIndent();
17858
18674
  }
17859
18675
 
17860
- writer.append('</' + this.tagName + '>');
17861
- }
18676
+ writer
18677
+ .append('</' + serializer.tagName + '>')
18678
+ .appendNewLine();
17862
18679
 
17863
- writer.appendNewLine();
17864
- };
18680
+ stack.pop();
18681
+ }
18682
+ }
17865
18683
 
17866
18684
  /**
17867
18685
  * A serializer for types that handles serialization of data types
@@ -17993,6 +18811,51 @@
17993
18811
 
17994
18812
  // helpers ///////////
17995
18813
 
18814
+ /**
18815
+ * Build the serializer tree for the given root element, iterating an
18816
+ * explicit stack rather than recursing so deeply nested documents stay
18817
+ * flat.
18818
+ *
18819
+ * Elements are visited pre-order so namespaces register in document order:
18820
+ * `enter` opens an element and returns its children, `exit` runs once its
18821
+ * subtree is built.
18822
+ *
18823
+ * @param {ElementSerializer} rootSerializer
18824
+ * @param {Object} rootElement
18825
+ */
18826
+ function buildTree(rootSerializer, rootElement) {
18827
+
18828
+ var stack = [ {
18829
+ serializer: rootSerializer,
18830
+ children: rootSerializer.enter(rootElement),
18831
+ index: 0
18832
+ } ];
18833
+
18834
+ while (stack.length) {
18835
+ var frame = stack[stack.length - 1];
18836
+
18837
+ if (frame.index >= frame.children.length) {
18838
+ frame.serializer.exit();
18839
+ stack.pop();
18840
+ continue;
18841
+ }
18842
+
18843
+ var child = frame.children[frame.index++];
18844
+
18845
+ var childSerializer = child.create();
18846
+
18847
+ frame.serializer.body.push(childSerializer);
18848
+
18849
+ if (child.element) {
18850
+ stack.push({
18851
+ serializer: childSerializer,
18852
+ children: childSerializer.enter(child.element),
18853
+ index: 0
18854
+ });
18855
+ }
18856
+ }
18857
+ }
18858
+
17996
18859
  /**
17997
18860
  * @param {Moddle} model
17998
18861
  *