@depup/fast-xml-parser 5.5.11-depup.1 → 5.11.0-depup.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@depup/fast-xml-parser",
3
- "version": "5.5.11-depup.1",
3
+ "version": "5.11.0-depup.0",
4
4
  "description": "Validate XML, Parse XML, Build XML without C/C++ based libraries (with updated dependencies)",
5
5
  "main": "./lib/fxp.cjs",
6
6
  "type": "module",
@@ -23,13 +23,11 @@
23
23
  "test": "c8 --reporter=lcov --reporter=text jasmine spec/*spec.js",
24
24
  "test-types": "tsc --noEmit spec/typings/typings-test.ts",
25
25
  "unit": "jasmine",
26
- "coverage": "nyc report --reporter html --reporter text -t .nyc_output --report-dir .nyc_output/summary",
27
26
  "perf": "node ./benchmark/perfTest3.js",
28
27
  "lint": "eslint src/**/*.js spec/**/*.js benchmark/**/*.js",
29
28
  "bundle": "webpack --config webpack.cjs.config.js",
30
29
  "prettier": "prettier --write src/**/*.js",
31
- "checkReadiness": "publish-please --dry-run",
32
- "publish-please": "publish-please"
30
+ "preversion": "npm test"
33
31
  },
34
32
  "bin": {
35
33
  "fxparser": "src/cli/cli.js"
@@ -75,14 +73,13 @@
75
73
  "@babel/plugin-transform-runtime": "^7.13.10",
76
74
  "@babel/preset-env": "^7.13.10",
77
75
  "@babel/register": "^7.13.8",
76
+ "@byspec/xml": "^0.1.0",
78
77
  "@types/node": "20",
79
78
  "babel-loader": "^8.2.2",
80
79
  "c8": "^10.1.3",
81
80
  "eslint": "^8.3.0",
82
- "he": "^1.2.0",
83
81
  "jasmine": "^5.6.0",
84
82
  "prettier": "^3.5.1",
85
- "publish-please": "^5.5.2",
86
83
  "typescript": "5",
87
84
  "webpack": "^5.64.4",
88
85
  "webpack-cli": "^4.9.1"
@@ -94,21 +91,24 @@
94
91
  }
95
92
  ],
96
93
  "dependencies": {
97
- "fast-xml-builder": "^1.1.4",
98
- "path-expression-matcher": "^1.5.0",
99
- "strnum": "^2.2.3"
94
+ "@nodable/entities": "^3.0.0",
95
+ "fast-xml-builder": "^1.3.1",
96
+ "is-unsafe": "^2.0.0",
97
+ "path-expression-matcher": "^1.6.2",
98
+ "strnum": "^2.4.2",
99
+ "xml-naming": "^0.3.0"
100
100
  },
101
101
  "depup": {
102
102
  "changes": {
103
- "path-expression-matcher": {
104
- "from": "^1.4.0",
105
- "to": "^1.5.0"
103
+ "fast-xml-builder": {
104
+ "from": "^1.2.0",
105
+ "to": "^1.3.1"
106
106
  }
107
107
  },
108
108
  "depsUpdated": 1,
109
109
  "originalPackage": "fast-xml-parser",
110
- "originalVersion": "5.5.11",
111
- "processedAt": "2026-04-10T05:04:43.827Z",
110
+ "originalVersion": "5.11.0",
111
+ "processedAt": "2026-08-16T08:07:19.703Z",
112
112
  "smokeTest": "passed"
113
113
  }
114
114
  }
package/src/fxp.d.ts CHANGED
@@ -66,20 +66,23 @@ export class Expression {
66
66
  }
67
67
 
68
68
  // ---------------------------------------------------------------------------
69
- // ReadonlyMatcher
69
+ // MatcherView
70
70
  // ---------------------------------------------------------------------------
71
71
 
72
72
  /**
73
- * A live read-only view of a Matcher instance, returned by Matcher.readOnly.
73
+ * A lightweight, live read-only view of a Matcher instance.
74
74
  *
75
- * All query and inspection methods work normally and always reflect the current
76
- * state of the underlying matcher. State-mutating methods (`push`, `pop`,
77
- * `reset`, `updateCurrent`, `restore`) are not present — calling them on the
78
- * underlying Proxy throws a `TypeError` at runtime.
75
+ * Returned by `Matcher.readOnly()`. The same instance is reused across every
76
+ * callback invocation — no allocation overhead per call. Reads directly from
77
+ * the parent Matcher's internal state so it always reflects the current parser
78
+ * position with no copying or freezing.
79
+ *
80
+ * Mutation methods (`push`, `pop`, `reset`, `updateCurrent`, `restore`) are
81
+ * simply absent — misuse is caught at compile time by TypeScript.
79
82
  *
80
83
  * This is the type received by all FXP user callbacks when `jPath: false`.
81
84
  */
82
- export interface ReadonlyMatcher {
85
+ export class MatcherView {
83
86
  readonly separator: string;
84
87
 
85
88
  /** Check if current path matches an Expression. */
@@ -111,14 +114,14 @@ export interface ReadonlyMatcher {
111
114
 
112
115
  /** Current path as an array of tag names. */
113
116
  toArray(): string[];
114
-
115
- /**
116
- * Create a snapshot of the current state.
117
- * The snapshot can be passed to the real Matcher.restore if needed.
118
- */
119
- snapshot(): MatcherSnapshot;
120
117
  }
121
118
 
119
+ /**
120
+ * @deprecated Use {@link MatcherView} instead.
121
+ * Alias kept for backward compatibility.
122
+ */
123
+ export type ReadonlyMatcher = MatcherView;
124
+
122
125
  /** Internal node structure — exposed via snapshot only. */
123
126
  export interface PathNode {
124
127
  tag: string;
@@ -141,8 +144,8 @@ export interface MatcherSnapshot {
141
144
  **********************************************************************/
142
145
 
143
146
  // jPath: true → string
144
- // jPath: false → ReadonlyMatcher
145
- type JPathOrMatcher = string | ReadonlyMatcher;
147
+ // jPath: false → MatcherView
148
+ type JPathOrMatcher = string | MatcherView;
146
149
  type JPathOrExpression = string | Expression;
147
150
 
148
151
  export type ProcessEntitiesOptions = {
@@ -208,6 +211,14 @@ export type ProcessEntitiesOptions = {
208
211
  tagFilter?: ((tagName: string, jPathOrMatcher: JPathOrMatcher) => boolean) | null;
209
212
  };
210
213
 
214
+ export type EntityDecoderOptions = {
215
+ setExternalEntities: (entities: Record<string, string>) => void;
216
+ addInputEntities: (entities: Record<string, string>) => void;
217
+ reset: () => void;
218
+ decode: (text: string) => string;
219
+ setXmlVersion: (version: string) => void;
220
+ }
221
+
211
222
  export type X2jOptions = {
212
223
  /**
213
224
  * Preserve the order of tags in resulting JS object
@@ -394,9 +405,14 @@ export type X2jOptions = {
394
405
  * Whether to process HTML entities
395
406
  *
396
407
  * Defaults to `false`
408
+ * @deprecated Use `entityDecoder` instead
397
409
  */
398
410
  htmlEntities?: boolean;
399
411
 
412
+ /**
413
+ * Custom entity decoder
414
+ */
415
+ entityDecoder?: EntityDecoderOptions;
400
416
  /**
401
417
  * Whether to ignore the declaration tag from output
402
418
  *
@@ -487,7 +503,9 @@ export type strnumOptions = {
487
503
  hex: boolean;
488
504
  leadingZeros: boolean,
489
505
  skipLike?: RegExp,
490
- eNotation?: boolean
506
+ eNotation?: boolean,
507
+ infinity?: string,
508
+ unicode?: boolean
491
509
  }
492
510
 
493
511
  export type validationOptions = {
@@ -680,11 +698,19 @@ export type ValidationError = {
680
698
 
681
699
  export class XMLParser {
682
700
  constructor(options?: X2jOptions);
683
- parse(xmlData: string | Uint8Array, validationOptions?: validationOptions | boolean): any;
701
+
702
+ parse(xmlData: string | Uint8Array): any;
703
+ /**
704
+ * @deprecated The `validationOptions` parameter is deprecated.
705
+ * Use the `fast-xml-validator` package instead for XML validation.
706
+ * @see https://www.npmjs.com/package/fast-xml-validator
707
+ */
708
+ parse(xmlData: string | Uint8Array, validationOptions: validationOptions | boolean): any;
684
709
  /**
685
710
  * Add Entity which is not by default supported by this library
686
711
  * @param entityIdentifier {string} Eg: 'ent' for &ent;
687
712
  * @param entityValue {string} Eg: '\r'
713
+ * @deprecated Use `entityDecoder` instead
688
714
  */
689
715
  addEntity(entityIdentifier: string, entityValue: string): void;
690
716
 
@@ -701,9 +727,16 @@ export class XMLParser {
701
727
  static getMetaDataSymbol(): Symbol;
702
728
  }
703
729
 
730
+ /**
731
+ * @deprecated Use fast-xml-validator instead
732
+ * @see https://www.npmjs.com/package/fast-xml-validator
733
+ */
704
734
  export class XMLValidator {
705
735
  static validate(xmlData: string, options?: validationOptions): true | ValidationError;
706
736
  }
737
+ /**
738
+ * @deprecated Use npm package 'fast-xml-builder' instead
739
+ */
707
740
  export class XMLBuilder {
708
741
  constructor(options?: XmlBuilderOptions);
709
742
  build(jObj: any): string;
@@ -716,4 +749,6 @@ export class XMLBuilder {
716
749
  export interface XMLMetaData {
717
750
  /** The index, if available, of the character where the XML node began in the input stream. */
718
751
  startIndex?: number;
752
+ /** The index, if available, of the character where the XML node ended in the input stream. */
753
+ endIndex?: number;
719
754
  }
@@ -1,11 +1,15 @@
1
- import { isName } from '../util.js';
1
+ import { qName as isName } from 'xml-naming';
2
2
 
3
3
  export default class DocTypeReader {
4
- constructor(options) {
4
+ constructor(options, xmlVersion) {
5
5
  this.suppressValidationErr = !options;
6
6
  this.options = options;
7
+ this.xmlVersion = xmlVersion || 1.0;
7
8
  }
8
9
 
10
+ setXmlVersion(xmlVersion = 1.0) {
11
+ this.xmlVersion = xmlVersion;
12
+ }
9
13
  readDocType(xmlData, i) {
10
14
  const entities = Object.create(null);
11
15
  let entityCount = 0;
@@ -19,8 +23,23 @@ export default class DocTypeReader {
19
23
  i = i + 9;
20
24
  let angleBracketsCount = 1;
21
25
  let hasBody = false, comment = false;
26
+ let quoteChar = null; // tracks an open SYSTEM/PUBLIC literal before the '[' body
22
27
  let exp = "";
23
28
  for (; i < xmlData.length; i++) {
29
+ // Inside a quoted external-identifier literal — XML allows '<'
30
+ // and '>' as plain data here, so they must not be interpreted
31
+ // as DOCTYPE structure until the matching quote closes.
32
+ if (quoteChar !== null) {
33
+ if (xmlData[i] === quoteChar) quoteChar = null;
34
+ exp += xmlData[i];
35
+ continue;
36
+ }
37
+ if (!hasBody && !comment && (xmlData[i] === '"' || xmlData[i] === "'")) {
38
+ quoteChar = xmlData[i];
39
+ exp += xmlData[i];
40
+ continue;
41
+ }
42
+
24
43
  if (xmlData[i] === '<' && !comment) { //Determine the tag type
25
44
  if (hasBody && hasSeq(xmlData, "!ENTITY", i)) {
26
45
  i += 7;
@@ -35,11 +54,8 @@ export default class DocTypeReader {
35
54
  );
36
55
  }
37
56
  //const escaped = entityName.replace(/[.\-+*:]/g, '\\.');
38
- const escaped = entityName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
39
- entities[entityName] = {
40
- regx: RegExp(`&${escaped};`, "g"),
41
- val: val
42
- };
57
+ //const escaped = entityName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
58
+ entities[entityName] = val;
43
59
  entityCount++;
44
60
  }
45
61
  }
@@ -78,7 +94,7 @@ export default class DocTypeReader {
78
94
  exp += xmlData[i];
79
95
  }
80
96
  }
81
- if (angleBracketsCount !== 0) {
97
+ if (quoteChar !== null || angleBracketsCount !== 0) {
82
98
  throw new Error(`Unclosed DOCTYPE`);
83
99
  }
84
100
  } else {
@@ -106,7 +122,7 @@ export default class DocTypeReader {
106
122
  }
107
123
  let entityName = xmlData.substring(startIndex, i);
108
124
 
109
- validateEntityName(entityName);
125
+ validateEntityName(entityName, { xmlVersion: this.xmlVersion });
110
126
 
111
127
  // Skip whitespace after entity name
112
128
  i = skipWhitespace(xmlData, i);
@@ -149,7 +165,7 @@ export default class DocTypeReader {
149
165
  }
150
166
  let notationName = xmlData.substring(startIndex, i);
151
167
 
152
- !this.suppressValidationErr && validateEntityName(notationName);
168
+ !this.suppressValidationErr && validateEntityName(notationName, { xmlVersion: this.xmlVersion });
153
169
 
154
170
  // Skip whitespace after notation name
155
171
  i = skipWhitespace(xmlData, i);
@@ -229,7 +245,7 @@ export default class DocTypeReader {
229
245
  let elementName = xmlData.substring(startIndex, i);
230
246
 
231
247
  // Validate element name
232
- if (!this.suppressValidationErr && !isName(elementName)) {
248
+ if (!this.suppressValidationErr && !isName(elementName, { xmlVersion: this.xmlVersion })) {
233
249
  throw new Error(`Invalid element name: "${elementName}"`);
234
250
  }
235
251
 
@@ -276,7 +292,7 @@ export default class DocTypeReader {
276
292
  let elementName = xmlData.substring(startIndex, i);
277
293
 
278
294
  // Validate element name
279
- validateEntityName(elementName)
295
+ validateEntityName(elementName, { xmlVersion: this.xmlVersion })
280
296
 
281
297
  // Skip whitespace after element name
282
298
  i = skipWhitespace(xmlData, i);
@@ -289,7 +305,7 @@ export default class DocTypeReader {
289
305
  let attributeName = xmlData.substring(startIndex, i);
290
306
 
291
307
  // Validate attribute name
292
- if (!validateEntityName(attributeName)) {
308
+ if (!validateEntityName(attributeName, { xmlVersion: this.xmlVersion })) {
293
309
  throw new Error(`Invalid attribute name: "${attributeName}"`);
294
310
  }
295
311
 
@@ -324,7 +340,7 @@ export default class DocTypeReader {
324
340
 
325
341
  // Validate notation name
326
342
  notation = notation.trim();
327
- if (!validateEntityName(notation)) {
343
+ if (!validateEntityName(notation, { xmlVersion: this.xmlVersion })) {
328
344
  throw new Error(`Invalid notation name: "${notation}"`);
329
345
  }
330
346
 
@@ -402,8 +418,8 @@ function hasSeq(data, seq, i) {
402
418
  return true;
403
419
  }
404
420
 
405
- function validateEntityName(name) {
406
- if (isName(name))
421
+ function validateEntityName(name, xmlVersion) {
422
+ if (isName(name, { xmlVersion: xmlVersion }))
407
423
  return name;
408
424
  else
409
425
  throw new Error(`Invalid entity name ${name}`);
@@ -1,4 +1,5 @@
1
1
  import { DANGEROUS_PROPERTY_NAMES, criticalProperties } from "../util.js";
2
+ import { COMMON_HTML, CURRENCY } from '@nodable/entities';
2
3
 
3
4
  const defaultOnDangerousProperty = (name) => {
4
5
  if (DANGEROUS_PROPERTY_NAMES.includes(name)) {
@@ -24,7 +25,8 @@ export const defaultOptions = {
24
25
  numberParseOptions: {
25
26
  hex: true,
26
27
  leadingZeros: true,
27
- eNotation: true
28
+ eNotation: true,
29
+ unicode: false
28
30
  },
29
31
  tagValueProcessor: function (tagName, val) {
30
32
  return val;
@@ -39,6 +41,7 @@ export const defaultOptions = {
39
41
  unpairedTags: [],
40
42
  processEntities: true,
41
43
  htmlEntities: false,
44
+ entityDecoder: null,
42
45
  ignoreDeclaration: false,
43
46
  ignorePiTags: false,
44
47
  transformTagName: false,
@@ -85,18 +88,19 @@ function validatePropertyName(propertyName, optionName) {
85
88
  * @param {boolean|object} value
86
89
  * @returns {object} Always returns normalized object
87
90
  */
88
- function normalizeProcessEntities(value) {
91
+ function normalizeProcessEntities(value, htmlEntities) {
89
92
  // Boolean backward compatibility
90
93
  if (typeof value === 'boolean') {
91
94
  return {
92
95
  enabled: value, // true or false
93
96
  maxEntitySize: 10000,
94
- maxExpansionDepth: 10,
95
- maxTotalExpansions: 1000,
97
+ maxExpansionDepth: 10000,
98
+ maxTotalExpansions: Infinity,
96
99
  maxExpandedLength: 100000,
97
- maxEntityCount: 100,
100
+ maxEntityCount: 1000,
98
101
  allowedTags: null,
99
- tagFilter: null
102
+ tagFilter: null,
103
+ appliesTo: "all",
100
104
  };
101
105
  }
102
106
 
@@ -110,7 +114,8 @@ function normalizeProcessEntities(value) {
110
114
  maxExpandedLength: Math.max(1, value.maxExpandedLength ?? 100000),
111
115
  maxEntityCount: Math.max(1, value.maxEntityCount ?? 1000),
112
116
  allowedTags: value.allowedTags ?? null,
113
- tagFilter: value.tagFilter ?? null
117
+ tagFilter: value.tagFilter ?? null,
118
+ appliesTo: value.appliesTo ?? "all",
114
119
  };
115
120
  }
116
121
 
@@ -141,7 +146,7 @@ export const buildOptions = function (options) {
141
146
  }
142
147
 
143
148
  // Always normalize processEntities for backward compatibility and validation
144
- built.processEntities = normalizeProcessEntities(built.processEntities);
149
+ built.processEntities = normalizeProcessEntities(built.processEntities, built.htmlEntities);
145
150
  built.unpairedTagsSet = new Set(built.unpairedTags);
146
151
  // Convert old-style stopNodes for backward compatibility
147
152
  if (built.stopNodes && Array.isArray(built.stopNodes)) {