projen-pipelines 0.3.22 → 0.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/.jsii +7 -4
  2. package/API.md +420 -0
  3. package/lib/assign-approver/base.js +3 -4
  4. package/lib/assign-approver/github.js +4 -4
  5. package/lib/awscdk/base.js +47 -5
  6. package/lib/awscdk/bash.js +2 -4
  7. package/lib/awscdk/github.js +13 -6
  8. package/lib/awscdk/gitlab.js +11 -6
  9. package/lib/drift/base.js +8 -4
  10. package/lib/drift/bash.js +3 -4
  11. package/lib/drift/detect-drift.js +3 -2
  12. package/lib/drift/github.js +5 -4
  13. package/lib/drift/gitlab.js +5 -4
  14. package/lib/drift/step.js +2 -4
  15. package/lib/release.js +5 -2
  16. package/lib/steps/amplify-deploy.step.js +5 -4
  17. package/lib/steps/artifact-steps.js +5 -6
  18. package/lib/steps/aws-assume-role.step.js +3 -4
  19. package/lib/steps/github-summary.step.js +3 -4
  20. package/lib/steps/package-manager-setup.step.js +4 -6
  21. package/lib/steps/registries.js +7 -8
  22. package/lib/steps/set-env.step.js +2 -1
  23. package/lib/steps/step.js +9 -10
  24. package/lib/versioning/computation.js +6 -8
  25. package/lib/versioning/config.js +3 -6
  26. package/lib/versioning/outputs.js +13 -16
  27. package/lib/versioning/setup.js +4 -4
  28. package/lib/versioning/strategy.js +4 -4
  29. package/lib/versioning/version-info.js +18 -9
  30. package/node_modules/@babel/code-frame/package.json +2 -2
  31. package/node_modules/@babel/helper-validator-identifier/package.json +1 -1
  32. package/node_modules/@nodable/entities/README.md +52 -1
  33. package/node_modules/@nodable/entities/package.json +4 -1
  34. package/node_modules/@nodable/entities/src/EntityDecoder.js +105 -4
  35. package/node_modules/@nodable/entities/src/EntityEncoder.js +79 -34
  36. package/node_modules/@nodable/entities/src/all-entities.js +53 -0
  37. package/node_modules/@nodable/entities/src/entities.js +0 -38
  38. package/node_modules/@nodable/entities/src/entityTries.js +48 -37
  39. package/node_modules/@nodable/entities/src/index.d.ts +93 -2
  40. package/node_modules/@nodable/entities/src/index.js +2 -2
  41. package/node_modules/anynum/LICENSE +21 -0
  42. package/node_modules/anynum/README.md +142 -0
  43. package/node_modules/anynum/anynum.js +135 -0
  44. package/node_modules/anynum/digitTable.js +116 -0
  45. package/node_modules/anynum/package.json +42 -0
  46. package/node_modules/dotgitignore/node_modules/brace-expansion/README.md +23 -1
  47. package/node_modules/dotgitignore/node_modules/brace-expansion/index.js +265 -86
  48. package/node_modules/dotgitignore/node_modules/brace-expansion/package.json +1 -1
  49. package/node_modules/fast-xml-builder/CHANGELOG.md +12 -0
  50. package/node_modules/fast-xml-builder/README.md +4 -1
  51. package/node_modules/fast-xml-builder/lib/fxb.cjs +1 -1
  52. package/node_modules/fast-xml-builder/lib/fxb.min.js +1 -1
  53. package/node_modules/fast-xml-builder/lib/fxb.min.js.map +1 -1
  54. package/node_modules/fast-xml-builder/package.json +6 -10
  55. package/node_modules/fast-xml-builder/src/fxb.js +34 -24
  56. package/node_modules/fast-xml-builder/src/orderedJs2Xml.js +18 -17
  57. package/node_modules/fast-xml-builder/src/util.js +10 -3
  58. package/node_modules/fast-xml-parser/CHANGELOG.md +34 -0
  59. package/node_modules/fast-xml-parser/README.md +10 -6
  60. package/node_modules/fast-xml-parser/lib/fxbuilder.min.js +1 -1
  61. package/node_modules/fast-xml-parser/lib/fxbuilder.min.js.map +1 -1
  62. package/node_modules/fast-xml-parser/lib/fxp.cjs +1 -1
  63. package/node_modules/fast-xml-parser/lib/fxp.d.cts +3 -1
  64. package/node_modules/fast-xml-parser/lib/fxp.min.js +1 -1
  65. package/node_modules/fast-xml-parser/lib/fxp.min.js.map +1 -1
  66. package/node_modules/fast-xml-parser/lib/fxparser.min.js +1 -1
  67. package/node_modules/fast-xml-parser/lib/fxparser.min.js.map +1 -1
  68. package/node_modules/fast-xml-parser/package.json +8 -10
  69. package/node_modules/fast-xml-parser/src/fxp.d.ts +5 -1
  70. package/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js +16 -1
  71. package/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js +2 -1
  72. package/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js +36 -3
  73. package/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js +14 -0
  74. package/node_modules/get-pkg-repo/node_modules/yargs/package.json +27 -26
  75. package/node_modules/get-pkg-repo/node_modules/yargs/yargs +5 -9
  76. package/node_modules/get-pkg-repo/node_modules/yargs/yargs.cjs +15 -0
  77. package/node_modules/hasown/CHANGELOG.md +7 -0
  78. package/node_modules/hasown/index.d.ts +0 -1
  79. package/node_modules/hasown/package.json +4 -5
  80. package/node_modules/is-unsafe/LICENSE +21 -0
  81. package/node_modules/is-unsafe/README.md +599 -0
  82. package/node_modules/is-unsafe/package.json +56 -0
  83. package/node_modules/is-unsafe/src/contexts/html.js +102 -0
  84. package/node_modules/is-unsafe/src/contexts/log.js +98 -0
  85. package/node_modules/is-unsafe/src/contexts/nosql.js +114 -0
  86. package/node_modules/is-unsafe/src/contexts/redos.js +60 -0
  87. package/node_modules/is-unsafe/src/contexts/shell.js +105 -0
  88. package/node_modules/is-unsafe/src/contexts/sql-strict.js +47 -0
  89. package/node_modules/is-unsafe/src/contexts/sql.js +95 -0
  90. package/node_modules/is-unsafe/src/contexts/svg.js +81 -0
  91. package/node_modules/is-unsafe/src/contexts/xml.js +79 -0
  92. package/node_modules/is-unsafe/src/index.cjs +27 -0
  93. package/node_modules/is-unsafe/src/index.d.ts +95 -0
  94. package/node_modules/is-unsafe/src/index.js +265 -0
  95. package/node_modules/path-expression-matcher/README.md +82 -7
  96. package/node_modules/path-expression-matcher/lib/pem.cjs +1 -1
  97. package/node_modules/path-expression-matcher/lib/pem.d.cts +94 -3
  98. package/node_modules/path-expression-matcher/lib/pem.min.js +1 -1
  99. package/node_modules/path-expression-matcher/lib/pem.min.js.map +1 -1
  100. package/node_modules/path-expression-matcher/package.json +2 -1
  101. package/node_modules/path-expression-matcher/src/ExpressionSet.js +18 -2
  102. package/node_modules/path-expression-matcher/src/Matcher.js +109 -16
  103. package/node_modules/path-expression-matcher/src/index.d.ts +67 -1
  104. package/node_modules/path-expression-matcher/src/index.js +3 -2
  105. package/node_modules/semver/classes/range.js +24 -4
  106. package/node_modules/semver/classes/semver.js +19 -2
  107. package/node_modules/semver/package.json +2 -2
  108. package/node_modules/semver/ranges/subset.js +2 -2
  109. package/node_modules/strnum/CHANGELOG.md +14 -0
  110. package/node_modules/strnum/README.md +9 -0
  111. package/node_modules/strnum/package.json +7 -3
  112. package/node_modules/strnum/strnum.js +14 -2
  113. package/node_modules/xml-naming/LICENSE +21 -0
  114. package/node_modules/xml-naming/README.md +99 -1
  115. package/node_modules/xml-naming/package.json +4 -2
  116. package/node_modules/xml-naming/src/index.d.ts +34 -0
  117. package/node_modules/xml-naming/src/index.js +134 -22
  118. package/node_modules/yargs/build/index.cjs +1 -1
  119. package/node_modules/yargs/package.json +6 -4
  120. package/node_modules/yargs/yargs +5 -9
  121. package/node_modules/yargs/yargs.cjs +15 -0
  122. package/package.json +8 -9
  123. package/node_modules/yargs/browser.d.ts +0 -5
@@ -5,6 +5,30 @@
5
5
 
6
6
  import { XML as DEFAULT_XML_ENTITIES } from "./entities.js"
7
7
 
8
+ // ---------------------------------------------------------------------------
9
+ // Entity hook action constants
10
+ // ---------------------------------------------------------------------------
11
+
12
+ /**
13
+ * Action constants for `onExternalEntity` and `onInputEntity` hooks.
14
+ *
15
+ * Use these instead of raw strings to avoid typos:
16
+ *
17
+ * @example
18
+ * import EntityDecoder, { ENTITY_ACTION } from './EntityDecoder.js';
19
+ * const dec = new EntityDecoder({
20
+ * onInputEntity: (name, value) => ENTITY_ACTION.BLOCK,
21
+ * });
22
+ */
23
+ export const ENTITY_ACTION = Object.freeze({
24
+ /** Resolve and expand the entity normally. */
25
+ ALLOW: 'allow',
26
+ /** Silently skip this entity — it will not be registered. */
27
+ BLOCK: 'block',
28
+ /** Throw an error, aborting entity registration entirely. */
29
+ THROW: 'throw',
30
+ });
31
+
8
32
  // ---------------------------------------------------------------------------
9
33
  // Helpers
10
34
  // ---------------------------------------------------------------------------
@@ -169,6 +193,14 @@ export default class EntityDecoder {
169
193
  * the effective action is max(onNCR, rangeMinimum).
170
194
  * @param {'remove'|'throw'} [options.ncr.nullNCR='remove']
171
195
  * Action for U+0000 (null). 'allow' and 'leave' are clamped to 'remove' since null is never safe.
196
+ * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onExternalEntity=null]
197
+ * Hook called when an external entity is registered via `setExternalEntities()` or
198
+ * `addExternalEntity()`. Return `ENTITY_ACTION.ALLOW` to accept the entity,
199
+ * `ENTITY_ACTION.BLOCK` to silently skip it, or `ENTITY_ACTION.THROW` to abort with an error.
200
+ * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onInputEntity=null]
201
+ * Hook called when an input entity is registered via `addInputEntities()`. Return
202
+ * `ENTITY_ACTION.ALLOW` to accept, `ENTITY_ACTION.BLOCK` to silently skip, or
203
+ * `ENTITY_ACTION.THROW` to abort with an error.
172
204
  */
173
205
  constructor(options = {}) {
174
206
  this._limit = options.limit || {};
@@ -204,6 +236,43 @@ export default class EntityDecoder {
204
236
  this._ncrXmlVersion = ncrCfg.xmlVersion;
205
237
  this._ncrOnLevel = ncrCfg.onLevel;
206
238
  this._ncrNullLevel = ncrCfg.nullLevel;
239
+
240
+ // --- Registration hooks ---
241
+ /** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */
242
+ this._onExternalEntity = typeof options.onExternalEntity === 'function'
243
+ ? options.onExternalEntity
244
+ : null;
245
+ /** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */
246
+ this._onInputEntity = typeof options.onInputEntity === 'function'
247
+ ? options.onInputEntity
248
+ : null;
249
+ }
250
+
251
+ // -------------------------------------------------------------------------
252
+ // Private: registration hook dispatch
253
+ // -------------------------------------------------------------------------
254
+
255
+ /**
256
+ * Invoke a registration hook for a single entity name/value pair.
257
+ * Returns true when the entity should be accepted, false when it should be
258
+ * silently skipped (BLOCK), and throws when the hook returns THROW.
259
+ *
260
+ * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} hook
261
+ * @param {string} name
262
+ * @param {string} value
263
+ * @param {string} context — used in error messages ('external' | 'input')
264
+ * @returns {boolean} true = accept, false = skip
265
+ */
266
+ _applyRegistrationHook(hook, name, value, context) {
267
+ if (!hook) return true; // no hook → always accept
268
+ const action = hook(name, value);
269
+ if (action === ENTITY_ACTION.BLOCK) return false;
270
+ if (action === ENTITY_ACTION.THROW) {
271
+ throw new Error(
272
+ `[EntityDecoder] Registration of ${context} entity "&${name};" was rejected by hook`
273
+ );
274
+ }
275
+ return true; // ALLOW or any unknown return value → accept
207
276
  }
208
277
 
209
278
  // -------------------------------------------------------------------------
@@ -213,6 +282,9 @@ export default class EntityDecoder {
213
282
  /**
214
283
  * Replace the full set of persistent external entities.
215
284
  * All keys are validated — throws on invalid characters.
285
+ * If `onExternalEntity` is set, it is called once per entry; entries that
286
+ * return `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW`
287
+ * aborts the whole call.
216
288
  * @param {Record<string, string | { regex?: RegExp, val: string }>} map
217
289
  */
218
290
  setExternalEntities(map) {
@@ -221,18 +293,34 @@ export default class EntityDecoder {
221
293
  validateEntityName(key);
222
294
  }
223
295
  }
224
- this._externalMap = mergeEntityMaps(map);
296
+ if (!this._onExternalEntity) {
297
+ this._externalMap = mergeEntityMaps(map);
298
+ return;
299
+ }
300
+ // Hook present — resolve values first, then filter
301
+ const flat = mergeEntityMaps(map);
302
+ const filtered = Object.create(null);
303
+ for (const [name, value] of Object.entries(flat)) {
304
+ if (this._applyRegistrationHook(this._onExternalEntity, name, value, 'external')) {
305
+ filtered[name] = value;
306
+ }
307
+ }
308
+ this._externalMap = filtered;
225
309
  }
226
310
 
227
311
  /**
228
312
  * Add a single persistent external entity.
313
+ * If `onExternalEntity` is set it is called before the entity is stored;
314
+ * `ENTITY_ACTION.BLOCK` silently skips storage, `ENTITY_ACTION.THROW` raises.
229
315
  * @param {string} key
230
316
  * @param {string} value
231
317
  */
232
318
  addExternalEntity(key, value) {
233
319
  validateEntityName(key);
234
320
  if (typeof value === 'string' && value.indexOf('&') === -1) {
235
- this._externalMap[key] = value;
321
+ if (this._applyRegistrationHook(this._onExternalEntity, key, value, 'external')) {
322
+ this._externalMap[key] = value;
323
+ }
236
324
  }
237
325
  }
238
326
 
@@ -243,12 +331,25 @@ export default class EntityDecoder {
243
331
  /**
244
332
  * Inject DOCTYPE entities for the current document.
245
333
  * Also resets per-document expansion counters.
334
+ * If `onInputEntity` is set it is called once per entry; entries returning
335
+ * `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW` aborts.
246
336
  * @param {Record<string, string | { regx?: RegExp, regex?: RegExp, val: string }>} map
247
337
  */
248
338
  addInputEntities(map) {
249
339
  this._totalExpansions = 0;
250
340
  this._expandedLength = 0;
251
- this._inputMap = mergeEntityMaps(map);
341
+ if (!this._onInputEntity) {
342
+ this._inputMap = mergeEntityMaps(map);
343
+ return;
344
+ }
345
+ const flat = mergeEntityMaps(map);
346
+ const filtered = Object.create(null);
347
+ for (const [name, value] of Object.entries(flat)) {
348
+ if (this._applyRegistrationHook(this._onInputEntity, name, value, 'input')) {
349
+ filtered[name] = value;
350
+ }
351
+ }
352
+ this._inputMap = filtered;
252
353
  }
253
354
 
254
355
  // -------------------------------------------------------------------------
@@ -293,7 +394,7 @@ export default class EntityDecoder {
293
394
  decode(str) {
294
395
  if (typeof str !== 'string' || str.length === 0) return str;
295
396
  //TODO: check if needed
296
- //if (str.indexOf('&') === -1) return str; // fast path — no entities at all
397
+ if (str.indexOf('&') === -1) return str; // fast path — no entities at all
297
398
 
298
399
  const original = str;
299
400
  const chunks = [];
@@ -1,5 +1,7 @@
1
- // EntityDecoder.js
2
- import { trie1, trie2, trie3 } from './entityTries.js';
1
+ // EntityEncoder.js
2
+ import { buildTries } from './entityTries.js';
3
+ // NOTE: no static import of ALL_ENTITIES / entities.js here — that import
4
+ // was the actual tree-shaking leak (see constructor comment below).
3
5
 
4
6
  // Replacement strings indexed by char code — direct array access, no hashing
5
7
  const XML_UNSAFE_REPLACEMENT = new Array(128);
@@ -26,6 +28,42 @@ export default class EntityEncoder {
26
28
  this.encodeAllNamed = options.encodeAllNamed !== false;
27
29
  this.maxReplacements = options.maxReplacements || 0;
28
30
  this.replacementsCount = 0;
31
+
32
+ // Tries are only built when named-entity encoding is actually requested,
33
+ // and only from what the caller explicitly hands in. There is no
34
+ // built-in default entity set imported here — that static import was
35
+ // exactly what pinned the entire ALL_ENTITIES data (all categories) into
36
+ // every consumer's bundle regardless of what they actually used. A `||`
37
+ // fallback to a statically-imported default does NOT tree-shake: the
38
+ // import is still "used" from the bundler's point of view even if the
39
+ // runtime branch never executes.
40
+ //
41
+ // BREAKING CHANGE from earlier versions: `new EntityEncoder()` with no
42
+ // `namedEntities` and `encodeAllNamed !== false` now throws instead of
43
+ // silently encoding the full built-in set. Callers who want the old
44
+ // "encode everything" behavior must opt in explicitly:
45
+ // import { ALL_ENTITIES } from '@nodable/entities';
46
+ // new EntityEncoder({ namedEntities: ALL_ENTITIES });
47
+ if (this.encodeAllNamed) {
48
+ if (!options.namedEntities) {
49
+ throw new Error(
50
+ '[EntityEncoder] encodeAllNamed is true but no `namedEntities` was ' +
51
+ 'provided. Pass a specific set (e.g. { ...COMMON_HTML, ...CURRENCY }) ' +
52
+ 'or the full built-in set via `namedEntities: ALL_ENTITIES` imported ' +
53
+ 'from \'@nodable/entities\'. This is required so bundlers can ' +
54
+ 'tree-shake unused entity categories out of your build; set ' +
55
+ '`encodeAllNamed: false` if you don\'t need named-entity encoding at all.'
56
+ );
57
+ }
58
+ const tries = buildTries(options.namedEntities);
59
+ this.trie1 = tries.trie1;
60
+ this.trie2 = tries.trie2;
61
+ this.trie3 = tries.trie3;
62
+ } else {
63
+ this.trie1 = null;
64
+ this.trie2 = null;
65
+ this.trie3 = null;
66
+ }
29
67
  }
30
68
 
31
69
  encode(str) {
@@ -38,6 +76,9 @@ export default class EntityEncoder {
38
76
  // Hoist to locals — avoids `this` property lookup inside the hot loop
39
77
  const encodeXmlSafe = this.encodeXmlSafe;
40
78
  const encodeAllNamed = this.encodeAllNamed;
79
+ const trie1 = this.trie1;
80
+ const trie2 = this.trie2;
81
+ const trie3 = this.trie3;
41
82
 
42
83
  const len = str.length;
43
84
 
@@ -84,32 +125,34 @@ export default class EntityEncoder {
84
125
  let matchedEntity = null;
85
126
  let advance = 1;
86
127
 
87
- // Try 3-char match first (longest wins)
88
- const mid3 = trie3.get(c0);
89
- if (mid3 !== undefined) {
90
- const c1 = str.charCodeAt(i + 1);
91
- const inner3 = mid3.get(c1);
92
- if (inner3 !== undefined) {
93
- const c2 = str.charCodeAt(i + 2);
94
- const candidate = inner3.get(c2);
95
- if (candidate !== undefined) { matchedEntity = candidate; advance = 3; }
128
+ if (encodeAllNamed) {
129
+ // Try 3-char match first (longest wins)
130
+ const mid3 = trie3.get(c0);
131
+ if (mid3 !== undefined) {
132
+ const c1 = str.charCodeAt(i + 1);
133
+ const inner3 = mid3.get(c1);
134
+ if (inner3 !== undefined) {
135
+ const c2 = str.charCodeAt(i + 2);
136
+ const candidate = inner3.get(c2);
137
+ if (candidate !== undefined) { matchedEntity = candidate; advance = 3; }
138
+ }
96
139
  }
97
- }
98
140
 
99
- // Try 2-char match
100
- if (matchedEntity === null) {
101
- const inner2 = trie2.get(c0);
102
- if (inner2 !== undefined) {
103
- const c1 = str.charCodeAt(i + 1);
104
- const candidate = inner2.get(c1);
105
- if (candidate !== undefined) { matchedEntity = candidate; advance = 2; }
141
+ // Try 2-char match
142
+ if (matchedEntity === null) {
143
+ const inner2 = trie2.get(c0);
144
+ if (inner2 !== undefined) {
145
+ const c1 = str.charCodeAt(i + 1);
146
+ const candidate = inner2.get(c1);
147
+ if (candidate !== undefined) { matchedEntity = candidate; advance = 2; }
148
+ }
106
149
  }
107
- }
108
150
 
109
- // Try 1-char match
110
- if (matchedEntity === null && encodeAllNamed) {
111
- const candidate = trie1.get(c0);
112
- if (candidate !== undefined) { matchedEntity = candidate; }
151
+ // Try 1-char match
152
+ if (matchedEntity === null) {
153
+ const candidate = trie1.get(c0);
154
+ if (candidate !== undefined) { matchedEntity = candidate; }
155
+ }
113
156
  }
114
157
 
115
158
  if (matchedEntity !== null) {
@@ -153,18 +196,20 @@ export default class EntityEncoder {
153
196
  let matchedEntity = null;
154
197
  let advance = 1;
155
198
 
156
- if (i + 1 < len) {
157
- const inner2 = trie2.get(c0);
158
- if (inner2 !== undefined) {
159
- const c1 = str.charCodeAt(i + 1);
160
- const candidate = inner2.get(c1);
161
- if (candidate !== undefined) { matchedEntity = candidate; advance = 2; }
199
+ if (encodeAllNamed) {
200
+ if (i + 1 < len) {
201
+ const inner2 = trie2.get(c0);
202
+ if (inner2 !== undefined) {
203
+ const c1 = str.charCodeAt(i + 1);
204
+ const candidate = inner2.get(c1);
205
+ if (candidate !== undefined) { matchedEntity = candidate; advance = 2; }
206
+ }
162
207
  }
163
- }
164
208
 
165
- if (matchedEntity === null && encodeAllNamed) {
166
- const candidate = trie1.get(c0);
167
- if (candidate !== undefined) { matchedEntity = candidate; }
209
+ if (matchedEntity === null) {
210
+ const candidate = trie1.get(c0);
211
+ if (candidate !== undefined) { matchedEntity = candidate; }
212
+ }
168
213
  }
169
214
 
170
215
  if (matchedEntity !== null) {
@@ -0,0 +1,53 @@
1
+ // all-entities.js
2
+ //
3
+ // ALL_ENTITIES lives in its OWN module, deliberately separate from
4
+ // entities.js. An object spread like `{ ...GREEK, ...CYRILLIC, ... }` is
5
+ // treated conservatively by bundlers (esbuild, Rollup, webpack) as a
6
+ // potential side effect — spreading can invoke getters or Symbol.iterator —
7
+ // so a module containing such a spread cannot be safely tree-shaken. If that
8
+ // spread lived inside entities.js alongside the individual category exports,
9
+ // its mere presence would pin every category into any bundle that imports
10
+ // even one of them, regardless of whether ALL_ENTITIES itself is ever used.
11
+ //
12
+ // Keeping it isolated here means:
13
+ // - `import { COMMON_HTML } from './entities.js'` tree-shakes cleanly —
14
+ // no other category is dragged in.
15
+ // - Only consumers who explicitly `import { ALL_ENTITIES } from
16
+ // './all-entities.js'` (re-exported from index.js for convenience) pay
17
+ // for the full merged set.
18
+
19
+ import {
20
+ BASIC_LATIN,
21
+ LATIN_ACCENTS,
22
+ LATIN_EXTENDED,
23
+ GREEK,
24
+ CYRILLIC,
25
+ MATH,
26
+ MATH_ADVANCED,
27
+ ARROWS,
28
+ SHAPES,
29
+ PUNCTUATION,
30
+ CURRENCY,
31
+ FRACTIONS,
32
+ MISC_SYMBOLS,
33
+ } from './entities.js';
34
+
35
+ /**
36
+ * All entities combined (if you need everything).
37
+ * @type {Record<string, string>}
38
+ */
39
+ export const ALL_ENTITIES = {
40
+ ...BASIC_LATIN,
41
+ ...LATIN_ACCENTS,
42
+ ...LATIN_EXTENDED,
43
+ ...GREEK,
44
+ ...CYRILLIC,
45
+ ...MATH,
46
+ ...MATH_ADVANCED,
47
+ ...ARROWS,
48
+ ...SHAPES,
49
+ ...PUNCTUATION,
50
+ ...CURRENCY,
51
+ ...FRACTIONS,
52
+ ...MISC_SYMBOLS,
53
+ };
@@ -34,7 +34,6 @@ export const BASIC_LATIN = {
34
34
  num: '#',
35
35
  dollar: '$',
36
36
  percent: '%',
37
- amp: '&',
38
37
  ast: '*',
39
38
  commat: '@',
40
39
  lowbar: '_',
@@ -466,9 +465,6 @@ export const CYRILLIC = {
466
465
  */
467
466
  export const MATH = {
468
467
  plus: '+',
469
- minus: '−',
470
- mnplus: '∓',
471
- mp: '∓',
472
468
  pm: '±',
473
469
  times: '×',
474
470
  div: '÷',
@@ -541,10 +537,6 @@ export const MATH = {
541
537
  bumpe: '≏',
542
538
  bumpeq: '≏',
543
539
  HumpEqual: '≏',
544
- dotminus: '∸',
545
- minusd: '∸',
546
- plusdo: '∔',
547
- dotplus: '∔',
548
540
  le: '≤',
549
541
  LessEqual: '≤',
550
542
  ge: '≥',
@@ -661,7 +653,6 @@ export const MATH_ADVANCED = {
661
653
  wreath: '≀',
662
654
  nsime: '≄',
663
655
  nsimeq: '≄',
664
- nsimeq: '≄',
665
656
  ncong: '≇',
666
657
  simne: '≆',
667
658
  ncongdot: '⩭̸',
@@ -778,10 +769,6 @@ export const ARROWS = {
778
769
  mapsto: '↦',
779
770
  mapstodown: '↧',
780
771
  crarr: '↵',
781
- nwarrow: '↖',
782
- nearrow: '↗',
783
- searrow: '↘',
784
- swarrow: '↙',
785
772
  nleftarrow: '↚',
786
773
  nleftrightarrow: '↮',
787
774
  nrightarrow: '↛',
@@ -822,7 +809,6 @@ export const ARROWS = {
822
809
  ldrushar: '⥋',
823
810
  rdldhar: '⥩',
824
811
  lrhard: '⥭',
825
- rlhar: '⇌',
826
812
  uharr: '↾',
827
813
  uharl: '↿',
828
814
  dharr: '⇂',
@@ -838,7 +824,6 @@ export const ARROWS = {
838
824
  nhArr: '⇎',
839
825
  nlarr: '↚',
840
826
  nlArr: '⇍',
841
- nrarr: '↛',
842
827
  nrArr: '⇏',
843
828
  larrb: '⇤',
844
829
  LeftArrowBar: '⇤',
@@ -1000,7 +985,6 @@ export const PUNCTUATION = {
1000
985
  DiacriticalDot: '˙',
1001
986
  DiacriticalDoubleAcute: '˝',
1002
987
  grave: '`',
1003
- acute: '´',
1004
988
  };
1005
989
 
1006
990
  /**
@@ -1014,7 +998,6 @@ export const CURRENCY = {
1014
998
  yen: '¥',
1015
999
  euro: '€',
1016
1000
  dollar: '$',
1017
- euro: '€',
1018
1001
  fnof: 'ƒ',
1019
1002
  inr: '₹',
1020
1003
  af: '؋',
@@ -1114,7 +1097,6 @@ export const MISC_SYMBOLS = {
1114
1097
  Vdash: '⊩',
1115
1098
  dashv: '⊣',
1116
1099
  vDash: '⊨',
1117
- Vdash: '⊩',
1118
1100
  Vvdash: '⊪',
1119
1101
  nvdash: '⊬',
1120
1102
  nvDash: '⊭',
@@ -1122,26 +1104,6 @@ export const MISC_SYMBOLS = {
1122
1104
  nVDash: '⊯',
1123
1105
  };
1124
1106
 
1125
- /**
1126
- * All entities combined (if you need everything)
1127
- * @type {Record<string, string>}
1128
- */
1129
- export const ALL_ENTITIES = {
1130
- ...BASIC_LATIN,
1131
- ...LATIN_ACCENTS,
1132
- ...LATIN_EXTENDED,
1133
- ...GREEK,
1134
- ...CYRILLIC,
1135
- ...MATH,
1136
- ...MATH_ADVANCED,
1137
- ...ARROWS,
1138
- ...SHAPES,
1139
- ...PUNCTUATION,
1140
- ...CURRENCY,
1141
- ...FRACTIONS,
1142
- ...MISC_SYMBOLS,
1143
- };
1144
-
1145
1107
  export const XML = {
1146
1108
  amp: "&",
1147
1109
  apos: "'",
@@ -1,49 +1,60 @@
1
1
  // entityTries.js
2
- // Builds integer-keyed tries so the decoder never allocates a string object
2
+ // Builds integer-keyed tries so the encoder never allocates a string object
3
3
  // during lookup — every key is a plain charCode number.
4
4
  //
5
5
  // trie1: Map<code0, entity>
6
6
  // trie2: Map<code0, Map<code1, entity>>
7
7
  // trie3: Map<code0, Map<code1, Map<code2, entity>>>
8
+ //
9
+ // Trie construction is now a pure function, called on-demand by whoever
10
+ // needs it (EntityEncoder, at instantiation time) instead of running
11
+ // eagerly at module-import time against the full ALL_ENTITIES set. This
12
+ // keeps the cost — and the entity data itself — out of any bundle/consumer
13
+ // that never actually asks for it.
8
14
 
9
- import { ALL_ENTITIES } from './entities.js';
10
-
11
- // Reverse map: character sequence → "&name;"
12
- const CHAR_TO_ENTITY = new Map();
13
- for (const [name, chars] of Object.entries(ALL_ENTITIES)) {
14
- CHAR_TO_ENTITY.set(chars, `&${name};`);
15
- }
16
-
17
- export const trie1 = new Map(); // code0 → entity string
18
- export const trie2 = new Map(); // code0 → Map → entity string
19
- export const trie3 = new Map(); // code0 → Map → Map → entity string
15
+ /**
16
+ * Build integer-keyed tries from a name → chars entity map.
17
+ * @param {Record<string, string>} entityMap
18
+ * @returns {{ trie1: Map, trie2: Map, trie3: Map }}
19
+ */
20
+ export function buildTries(entityMap) {
21
+ const trie1 = new Map(); // code0 → entity string
22
+ const trie2 = new Map(); // code0 → Map → entity string
23
+ const trie3 = new Map(); // code0 → Map → Map → entity string
20
24
 
21
- for (const [chars, entity] of CHAR_TO_ENTITY) {
22
- const len = chars.length;
25
+ // Reverse map: character sequence → "&name;"
26
+ // (built inline, not kept around — only the tries need to survive)
27
+ for (const name in entityMap) {
28
+ const chars = entityMap[name];
29
+ const entity = `&${name};`;
30
+ const len = chars.length;
23
31
 
24
- if (len === 1) {
25
- const c0 = chars.charCodeAt(0);
26
- // Keep shortest match only if no longer match already claimed this code
27
- // (longer matches are inserted in the same pass so we just overwrite —
28
- // trie1 is only consulted after trie2/trie3 both miss, so no conflict)
29
- trie1.set(c0, entity);
32
+ if (len === 1) {
33
+ const c0 = chars.charCodeAt(0);
34
+ // Keep shortest match only if no longer match already claimed this code
35
+ // (longer matches are inserted in the same pass so we just overwrite —
36
+ // trie1 is only consulted after trie2/trie3 both miss, so no conflict)
37
+ trie1.set(c0, entity);
30
38
 
31
- } else if (len === 2) {
32
- const c0 = chars.charCodeAt(0);
33
- const c1 = chars.charCodeAt(1);
34
- let inner = trie2.get(c0);
35
- if (inner === undefined) { inner = new Map(); trie2.set(c0, inner); }
36
- inner.set(c1, entity);
39
+ } else if (len === 2) {
40
+ const c0 = chars.charCodeAt(0);
41
+ const c1 = chars.charCodeAt(1);
42
+ let inner = trie2.get(c0);
43
+ if (inner === undefined) { inner = new Map(); trie2.set(c0, inner); }
44
+ inner.set(c1, entity);
37
45
 
38
- } else if (len === 3) {
39
- const c0 = chars.charCodeAt(0);
40
- const c1 = chars.charCodeAt(1);
41
- const c2 = chars.charCodeAt(2);
42
- let mid = trie3.get(c0);
43
- if (mid === undefined) { mid = new Map(); trie3.set(c0, mid); }
44
- let inner = mid.get(c1);
45
- if (inner === undefined) { inner = new Map(); mid.set(c1, inner); }
46
- inner.set(c2, entity);
46
+ } else if (len === 3) {
47
+ const c0 = chars.charCodeAt(0);
48
+ const c1 = chars.charCodeAt(1);
49
+ const c2 = chars.charCodeAt(2);
50
+ let mid = trie3.get(c0);
51
+ if (mid === undefined) { mid = new Map(); trie3.set(c0, mid); }
52
+ let inner = mid.get(c1);
53
+ if (inner === undefined) { inner = new Map(); mid.set(c1, inner); }
54
+ inner.set(c2, entity);
55
+ }
56
+ // HTML5 has no named entity whose character sequence is longer than 3 chars
47
57
  }
48
- // HTML5 has no named entity whose character sequence is longer than 3 chars
49
- }
58
+
59
+ return { trie1, trie2, trie3 };
60
+ }