@endevops/effect-codec-xml 0.0.1 → 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -62
- package/dist/codec.d.ts +17 -9
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +25 -16
- package/dist/codec.js.map +1 -1
- package/dist/conventions.d.ts +4 -4
- package/dist/conventions.js +7 -7
- package/dist/conventions.js.map +1 -1
- package/dist/entities/entity-decoder.d.ts +33 -33
- package/dist/entities/entity-decoder.d.ts.map +1 -1
- package/dist/entities/entity-decoder.js +63 -64
- package/dist/entities/entity-decoder.js.map +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/namespaces.js +2 -2
- package/dist/namespaces.js.map +1 -1
- package/dist/naming.d.ts +6 -6
- package/dist/naming.d.ts.map +1 -1
- package/dist/naming.js +3 -3
- package/dist/naming.js.map +1 -1
- package/dist/parse.d.ts +5 -5
- package/dist/parse.js +14 -14
- package/dist/parse.js.map +1 -1
- package/dist/render.d.ts +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +28 -28
- package/dist/render.js.map +1 -1
- package/dist/xml-error.d.ts +9 -9
- package/dist/xml-error.js +18 -18
- package/dist/xml-error.js.map +1 -1
- package/dist/xml-value.d.ts +7 -7
- package/dist/xml-value.d.ts.map +1 -1
- package/dist/xml-value.js +6 -7
- package/dist/xml-value.js.map +1 -1
- package/package.json +1 -1
- package/src/codec.ts +91 -71
- package/src/conventions.ts +7 -7
- package/src/entities/entity-decoder.ts +98 -99
- package/src/errors.ts +3 -3
- package/src/index.ts +3 -3
- package/src/namespaces.ts +16 -16
- package/src/naming.ts +34 -35
- package/src/parse.ts +26 -26
- package/src/render.ts +44 -44
- package/src/xml-error.ts +18 -18
- package/src/xml-value.ts +10 -11
|
@@ -7,8 +7,8 @@ import { Effect } from "effect";
|
|
|
7
7
|
*/
|
|
8
8
|
export type EntityHookAction = 'allow' | 'block' | 'throw';
|
|
9
9
|
/**
|
|
10
|
-
* @description A function-valued entity replacement: the `val` of the legacy `{ regex, val }` form when it is not a string. This decoder cannot use one
|
|
11
|
-
* function has no meaning without the regex it was meant to be matched against
|
|
10
|
+
* @description A function-valued entity replacement: the `val` of the legacy `{ regex, val }` form when it is not a string. This decoder cannot use one. A
|
|
11
|
+
* function has no meaning without the regex it was meant to be matched against, so such an entry is dropped at registration rather than expanded.
|
|
12
12
|
*/
|
|
13
13
|
export type EntityValFn = (match: string, captured: string, ...rest: Array<unknown>) => string;
|
|
14
14
|
/**
|
|
@@ -41,15 +41,15 @@ export declare const ENTITY_ACTION: Readonly<{
|
|
|
41
41
|
/**
|
|
42
42
|
* @description Which entity categories count toward the expansion limits.
|
|
43
43
|
*
|
|
44
|
-
* - `'external'
|
|
45
|
-
* - `'base'
|
|
46
|
-
* - `'all'
|
|
47
|
-
* - `Array<'external' | 'base'
|
|
44
|
+
* - `'external'`: only input/runtime + persistent external entities. The default, and the only one that ignores the built-in XML entities.
|
|
45
|
+
* - `'base'`: only the built-in XML entities, the caller's `namedEntities`, and numeric references.
|
|
46
|
+
* - `'all'`: every entity regardless of tier.
|
|
47
|
+
* - `Array<'external' | 'base'>`: an explicit combination. An empty array is honoured literally: nothing counts, so the limits can never trip.
|
|
48
48
|
*/
|
|
49
49
|
export type ApplyLimitsTo = 'external' | 'base' | 'all' | Array<'external' | 'base'>;
|
|
50
50
|
/**
|
|
51
51
|
* @description Ceilings on what a single document's entity references may cost. Both are cumulative across {@link EntityDecoder.decode} calls until
|
|
52
|
-
* {@link EntityDecoder.reset}, and both default to `0`, meaning unlimited. `0
|
|
52
|
+
* {@link EntityDecoder.reset}, and both default to `0`, meaning unlimited. `0`, and any negative or non-numeric value, is unlimited, because the
|
|
53
53
|
* runtime tests `> 0` rather than truthiness of the configured number.
|
|
54
54
|
*/
|
|
55
55
|
export interface EntityDecoderLimitOptions {
|
|
@@ -68,8 +68,8 @@ export interface EntityDecoderLimitOptions {
|
|
|
68
68
|
*/
|
|
69
69
|
maxExpandedLength?: number;
|
|
70
70
|
/**
|
|
71
|
-
* @description Which tiers count against both limits. Defaults to `'external'`, which
|
|
72
|
-
*
|
|
71
|
+
* @description Which tiers count against both limits. Defaults to `'external'`, which keeps the built-in entities, including every numeric reference, from being
|
|
72
|
+
* able to trip a limit on a document the caller already trusts.
|
|
73
73
|
*
|
|
74
74
|
* @default 'external'
|
|
75
75
|
*/
|
|
@@ -81,15 +81,15 @@ export interface EntityDecoderLimitOptions {
|
|
|
81
81
|
*/
|
|
82
82
|
export interface EntityDecoderNCROptions {
|
|
83
83
|
/**
|
|
84
|
-
* @description The XML version whose codepoint restrictions apply. `1.0` prohibits the C0 controls U+0001
|
|
84
|
+
* @description The XML version whose codepoint restrictions apply. `1.0` prohibits the C0 controls U+0001 to U+001F other than tab, newline and carriage return;
|
|
85
85
|
* `1.1` does not, since it permits them when written as references. Any value other than `1.1` is read as `1.0`.
|
|
86
86
|
*
|
|
87
87
|
* @default 1.0
|
|
88
88
|
*/
|
|
89
89
|
xmlVersion?: 1.0 | 1.1;
|
|
90
90
|
/**
|
|
91
|
-
* @description The base action for every numeric reference. Codepoint ranges that carry a minimum
|
|
92
|
-
* null under `nullNCR`
|
|
91
|
+
* @description The base action for every numeric reference. Codepoint ranges that carry a minimum (surrogates always, the XML 1.0 C0 controls under `1.0`, and
|
|
92
|
+
* null under `nullNCR`) take the stricter of the two, so this is a floor and not an override.
|
|
93
93
|
*
|
|
94
94
|
* @default 'allow'
|
|
95
95
|
*/
|
|
@@ -107,10 +107,10 @@ export interface EntityDecoderNCROptions {
|
|
|
107
107
|
export interface EntityDecoderOptions {
|
|
108
108
|
/**
|
|
109
109
|
* @description Extra named entities merged into the `base` map alongside the five XML predefined ones. A string value is used directly; a `{ regex, val }` or `{
|
|
110
|
-
* regx, val }` envelope is unwrapped to its `val`. Anything else
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
110
|
+
* regx, val }` envelope is unwrapped to its `val`. Anything else (a number, `null`, a function, an envelope whose `val` is a function) is dropped,
|
|
111
|
+
* leaving the name unresolvable rather than failing the construction. Upstream's documentation says a value containing `&` is skipped here, to
|
|
112
|
+
* prevent recursive expansion. It is not: the code stores the value unchanged, and only {@link EntityDecoder.addExternalEntity} checks for `&`.
|
|
113
|
+
* Preserved as-is; see the note on the class.
|
|
114
114
|
*
|
|
115
115
|
* @default null
|
|
116
116
|
*/
|
|
@@ -120,7 +120,7 @@ export interface EntityDecoderOptions {
|
|
|
120
120
|
}> | null;
|
|
121
121
|
/**
|
|
122
122
|
* @description Called once on the finished string. Receives `(resolved, original)` and must return a string; return `original` to reject the expansion outright,
|
|
123
|
-
* or a sanitised form of `resolved` to clean it. It is _not_ called for a string that never reaches the scanning loop
|
|
123
|
+
* or a sanitised form of `resolved` to clean it. It is _not_ called for a string that never reaches the scanning loop: an empty string, a
|
|
124
124
|
* non-string, or any string with no `&` in it. A caller relying on `postCheck` to sanitise therefore has to know that a string with no ampersand is
|
|
125
125
|
* never inspected.
|
|
126
126
|
*
|
|
@@ -128,8 +128,8 @@ export interface EntityDecoderOptions {
|
|
|
128
128
|
*/
|
|
129
129
|
postCheck?: ((resolved: string, original: string) => string) | null;
|
|
130
130
|
/**
|
|
131
|
-
* @description Whether numeric references expand at all. Turning it off leaves every one of them in the output verbatim
|
|
132
|
-
* minimum action of `remove` or stricter, which are still handled, because that classification runs first
|
|
131
|
+
* @description Whether numeric references expand at all. Turning it off leaves every one of them in the output verbatim, _except_ the codepoints that carry a
|
|
132
|
+
* minimum action of `remove` or stricter, which are still handled, because that classification runs first. This is why the option is safe to rely
|
|
133
133
|
* on.
|
|
134
134
|
*
|
|
135
135
|
* @default true
|
|
@@ -144,7 +144,7 @@ export interface EntityDecoderOptions {
|
|
|
144
144
|
/**
|
|
145
145
|
* @description Names to delete outright, matched the same way as {@link EntityDecoderOptions.leave}. A removed reference is charged to the `external` tier even
|
|
146
146
|
* when the name is a built-in one, so a document full of removed built-ins can trip an `applyLimitsTo: 'external'` limit it would not otherwise be
|
|
147
|
-
* subject to. Preserved as-is; the only in-code comment claims the charge is for unknown references, which
|
|
147
|
+
* subject to. Preserved as-is; the only in-code comment claims the charge is for unknown references, which does not distinguish them.
|
|
148
148
|
*
|
|
149
149
|
* @default [ ]
|
|
150
150
|
*/
|
|
@@ -167,7 +167,7 @@ export interface EntityDecoderOptions {
|
|
|
167
167
|
onExternalEntity?: EntityRegistrationHook | null;
|
|
168
168
|
/**
|
|
169
169
|
* @description Called once per entity as it is registered through {@link EntityDecoder.addInputEntities}. Same contract as
|
|
170
|
-
* {@link EntityDecoderOptions.onExternalEntity}, and unlike it the hook is not the only filter
|
|
170
|
+
* {@link EntityDecoderOptions.onExternalEntity}, and unlike it the hook is not the only filter. See the class note on name validation.
|
|
171
171
|
*
|
|
172
172
|
* @default null
|
|
173
173
|
*/
|
|
@@ -178,25 +178,25 @@ export interface EntityDecoderOptions {
|
|
|
178
178
|
*
|
|
179
179
|
* ### Entity lookup priority
|
|
180
180
|
*
|
|
181
|
-
* 1. **input / runtime
|
|
182
|
-
* 2. **persistent external
|
|
181
|
+
* 1. **input / runtime**: injected per document through {@link EntityDecoder.addInputEntities}
|
|
182
|
+
* 2. **persistent external**: set through {@link EntityDecoder.setExternalEntities} and {@link EntityDecoder.addExternalEntity}, surviving
|
|
183
183
|
* {@link EntityDecoder.reset}
|
|
184
|
-
* 3. **base
|
|
184
|
+
* 3. **base**: the five XML predefined entities plus the constructor's `namedEntities` Both input and external resolve as the `external` tier for limit
|
|
185
185
|
* purposes, because both are injected at runtime. Numeric references (`&#NNN;`, `&#xHH;`) resolve directly through `String.fromCodePoint` and are
|
|
186
186
|
* always `base` tier: they cannot recurse, so a limit that counted them would only punish a document that spells its characters out.
|
|
187
187
|
*
|
|
188
188
|
* ### Upstream behaviour preserved
|
|
189
189
|
*
|
|
190
|
-
* Several quirks of the original are kept
|
|
190
|
+
* Several quirks of the original are kept intentionally, because a consumer's output already depends on them:
|
|
191
191
|
*
|
|
192
192
|
* - A value containing `&` is **not** filtered from `namedEntities` or `setExternalEntities`, contrary to the documentation. Only
|
|
193
193
|
* {@link EntityDecoder.addExternalEntity} checks, and it drops the entry rather than storing it, so the same name registered either way can resolve
|
|
194
194
|
* to nothing.
|
|
195
|
-
* - {@link EntityDecoderOptions.postCheck} is skipped entirely for input that never reaches the scan
|
|
195
|
+
* - {@link EntityDecoderOptions.postCheck} is skipped entirely for input that never reaches the scan: an empty string, a non-string, or a string with
|
|
196
196
|
* no `&`.
|
|
197
197
|
* - {@link EntityDecoder.decode} returns a non-string argument unchanged, despite being typed `string`.
|
|
198
198
|
* - The expansion-limit errors are prefixed `EntityReplacer`, not `EntityDecoder`.
|
|
199
|
-
* - Nothing in XML 1.0 §2.2 is enforced for U+007F
|
|
199
|
+
* - Nothing in XML 1.0 §2.2 is enforced for U+007F to U+009F or for the U+FFFE/U+FFFF noncharacters, and the sweep for `&` leaves a name of
|
|
200
200
|
* {@link MAX_TOKEN_LENGTH} + 1 characters unresolvable.
|
|
201
201
|
* - Numeric references are parsed with `parseInt`, so a leading space, sign, or trailing garbage is accepted: `&# 41;`, `&#x+41;` and `)zz;` all
|
|
202
202
|
* decode, and `�x41;` parses as a null reference rather than `A`.
|
|
@@ -210,7 +210,7 @@ export interface EntityDecoderOptions {
|
|
|
210
210
|
* decoder.addInputEntities({ version: '1.0' });
|
|
211
211
|
*
|
|
212
212
|
* decoder.decode('&brand; v&version; ©'); // 'Acme v1.0 ©'
|
|
213
|
-
* decoder.decode('&#38;'); // '&&'
|
|
213
|
+
* decoder.decode('&#38;'); // '&&', one pass, the output is never re-scanned
|
|
214
214
|
*
|
|
215
215
|
* decoder.reset(); // drops the input entities and the counters, keeps the external ones
|
|
216
216
|
* ```;
|
|
@@ -219,7 +219,7 @@ export declare class EntityDecoder {
|
|
|
219
219
|
#private;
|
|
220
220
|
/**
|
|
221
221
|
* @description Create a decoder. A factory rather than a constructor, because it refuses a `null` options object. Every field is optional, so `null` is not "a
|
|
222
|
-
* decoder with the defaults"
|
|
222
|
+
* decoder with the defaults". A caller who wrote it meant something the signature does not allow, and a decoder built from it would be
|
|
223
223
|
* indistinguishable from one built from `{}` while hiding the mistake. Saying so is worth a factory; `EntityDecoderOptions` is a plain object and
|
|
224
224
|
* nothing else about construction can fail.
|
|
225
225
|
*
|
|
@@ -236,8 +236,8 @@ export declare class EntityDecoder {
|
|
|
236
236
|
static make: (options?: EntityDecoderOptions) => Effect.Effect<EntityDecoder, XmlError>;
|
|
237
237
|
/**
|
|
238
238
|
* @description Create a decoder. Every option is resolved here into the flat fields the decode loop reads, so nothing per-reference has to re-derive it. The
|
|
239
|
-
* options whose wrong type disables them rather than failing the construction
|
|
240
|
-
*
|
|
239
|
+
* options whose wrong type disables them rather than failing the construction (the two hooks, the two name lists) are read through {@link readHook}
|
|
240
|
+
* and {@link readNameList}, so that rule is written once instead of four times.
|
|
241
241
|
*
|
|
242
242
|
* @param resolved - Configuration, already checked. See {@link EntityDecoderOptions}.
|
|
243
243
|
*/
|
|
@@ -305,14 +305,14 @@ export declare class EntityDecoder {
|
|
|
305
305
|
/**
|
|
306
306
|
* @description Expand every entity reference in a string, in one pass. The output is never re-scanned, so no expansion can produce a _second_ one: a registered
|
|
307
307
|
* value that itself contains reference text reaches the caller as that literal text, unexpanded. What the limits bound is the growth of this single
|
|
308
|
-
* pass
|
|
308
|
+
* pass, meaning how much one round of expansion can add. Three inputs return before the scan and therefore never reach
|
|
309
309
|
* {@link EntityDecoderOptions.postCheck}: a non-string, the empty string, and any string with no `&` in it. The scan itself is `#expandAll`; what
|
|
310
310
|
* this method adds is the three inputs that skip it and the single join of what it collected.
|
|
311
311
|
*
|
|
312
312
|
* @example
|
|
313
313
|
* ```typescript
|
|
314
314
|
* import { Effect } from 'effect';
|
|
315
|
-
* import { EntityDecoder } from '@endevops/effect-xml
|
|
315
|
+
* import { EntityDecoder } from '@endevops/effect-codec-xml';
|
|
316
316
|
*
|
|
317
317
|
* const decoder = new EntityDecoder({ namedEntities: { copy: '©' } });
|
|
318
318
|
* Effect.runSync(Effect.orElseSucceed(decoder.addExternalEntity('brand', 'Acme'), () => undefined));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-decoder.d.ts","names":[],"sources":["../../src/entities/entity-decoder.ts"],"mappings":";;;;;;;YA6HY;;;;;YAMA,eAAe,eAAe,qBAAqB,MAAM;;;;;;;;;;;YAYzD,0BAA0B,cAAc,kBAAkB;;;;;;;;;;;;qBAazD,eAAe;EAAW;EAAgB;EAAgB;;;;;;;;;;YAkB3D,8CAA8C;;;;;;iBAOzC;;;;;;;EAOf;;;;;;;EAQA;;;;;;;EAQA,gBAAgB;;;;;;iBAOD;;;;;;;EAOf;;;;;;;EAQA;;;;;;EAOA;;;;;iBAMe;;;;;;;;;;EAUf,gBAAgB;IAA0B,OAAO;IAAQ,cAAc;;;;;;;;;;EAUvE,cAAc,kBAAkB;;;;;;;;EAShC;;;;;;EAOA,QAAQ;;;;;;;;EASR,SAAS;;;;EAKT,QAAQ;;;;EAKR,MAAM;;;;;;;;EASN,mBAAmB;;;;;;;EAQnB,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"entity-decoder.d.ts","names":[],"sources":["../../src/entities/entity-decoder.ts"],"mappings":";;;;;;;YA6HY;;;;;YAMA,eAAe,eAAe,qBAAqB,MAAM;;;;;;;;;;;YAYzD,0BAA0B,cAAc,kBAAkB;;;;;;;;;;;;qBAazD,eAAe;EAAW;EAAgB;EAAgB;;;;;;;;;;YAkB3D,8CAA8C;;;;;;iBAOzC;;;;;;;EAOf;;;;;;;EAQA;;;;;;;EAQA,gBAAgB;;;;;;iBAOD;;;;;;;EAOf;;;;;;;EAQA;;;;;;EAOA;;;;;iBAMe;;;;;;;;;;EAUf,gBAAgB;IAA0B,OAAO;IAAQ,cAAc;;;;;;;;;;EAUvE,cAAc,kBAAkB;;;;;;;;EAShC;;;;;;EAOA,QAAQ;;;;;;;;EASR,SAAS;;;;EAKT,QAAQ;;;;EAKR,MAAM;;;;;;;;EASN,mBAAmB;;;;;;;EAQnB,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAmSL;;;;;;;;;;;;;;;;;;SAiHJ,OAAI,UAAa,yBAA4B,OAAO,OAAO,eAAe;;;;;;;;UAiB1E;;;;;;;;;;;EAkEP,sBAAmB,MAAA,eAAA,KAAA;IAEqB,OAAA;IAAa,cAAS;SAoB3D,OAAA,aAAA;;;;;;;;;;;;;EAcH,oBAAiB,MAAA,eAAA,aAAA,kBAAA,OAAA,aAAA;;;;;;;;;;;EAqBjB,mBAAgB,MAAA,eAAA,KAAA;IAEuB,MAAA;IAAa,cAAS;;IAAyB,OAAA;IAAa,cAAS;SAkBzG,OAAA,aAAA;;;;;;;EAQH;;;;;;;;;EAeA,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;EA6Bd,SAAM,MAAA,eAAA,gBAAA,OAAA,eAAA"}
|
|
@@ -9,8 +9,8 @@ const CODE_LOWER_X = 120;
|
|
|
9
9
|
const CODE_UPPER_X = 88;
|
|
10
10
|
/**
|
|
11
11
|
* @description The widest entity name {@link EntityDecoder.decode} will look for, in characters. The forward scan for `;` stops once more than this many characters
|
|
12
|
-
* have passed since the `&`, so a longer run is not treated as one entity
|
|
13
|
-
*
|
|
12
|
+
* have passed since the `&`, so a longer run is not treated as one entity. It is copied through as literal text. The bound keeps a document with a
|
|
13
|
+
* megabyte of non-entity text between two ampersands from being sliced.
|
|
14
14
|
*/
|
|
15
15
|
const MAX_TOKEN_LENGTH = 32;
|
|
16
16
|
/**
|
|
@@ -19,14 +19,14 @@ const MAX_TOKEN_LENGTH = 32;
|
|
|
19
19
|
*/
|
|
20
20
|
const MAX_CODE_POINT = 1114111;
|
|
21
21
|
/**
|
|
22
|
-
* @description Returned by `#classifyNCR` for a codepoint that carries no minimum action level
|
|
23
|
-
*
|
|
22
|
+
* @description Returned by `#classifyNCR` for a codepoint that carries no minimum action level. This value distinguishes "no restriction" from `NCR_LEVEL.allow`:
|
|
23
|
+
* both end up expanding, but only the first lets `numericAllowed: false` short-circuit the whole pipeline.
|
|
24
24
|
*/
|
|
25
25
|
const NO_MINIMUM_LEVEL = -1;
|
|
26
26
|
/**
|
|
27
27
|
* @description Characters that may not appear in an entity name registered through {@link EntityDecoder.setExternalEntities} or
|
|
28
28
|
* {@link EntityDecoder.addExternalEntity}. A name carrying one of these cannot be written as `&name;` at all, so registration refuses it rather than
|
|
29
|
-
* storing a name no document could ever reference. The set is the upstream string verbatim, including its duplicated backslash
|
|
29
|
+
* storing a name no document could ever reference. The set is the upstream string verbatim, including its duplicated backslash. A `Set` discards the
|
|
30
30
|
* duplicate, so the effective set is the eighteen characters below.
|
|
31
31
|
*/
|
|
32
32
|
const SPECIAL_CHARS = /* @__PURE__ */ new Set("!?\\/[]$%{}^&*()<>|+");
|
|
@@ -46,7 +46,7 @@ const LIMIT_TIER_BASE = "base";
|
|
|
46
46
|
const LIMIT_TIER_ALL = "all";
|
|
47
47
|
/**
|
|
48
48
|
* @description The NCR action levels, in severity order. A higher number is a stricter action, and the resolver takes the maximum of the configured level and the
|
|
49
|
-
* minimum a codepoint range imposes
|
|
49
|
+
* minimum a codepoint range imposes. A range can therefore only make an entity stricter than the caller asked for, never more lenient.
|
|
50
50
|
*/
|
|
51
51
|
const NCR_LEVEL = Object.freeze({
|
|
52
52
|
allow: 0,
|
|
@@ -55,7 +55,7 @@ const NCR_LEVEL = Object.freeze({
|
|
|
55
55
|
throw: 3
|
|
56
56
|
});
|
|
57
57
|
/**
|
|
58
|
-
* @description The C0 control codes XML 1.0 §2.2 permits as literal characters. Every other code in U+0001
|
|
58
|
+
* @description The C0 control codes XML 1.0 §2.2 permits as literal characters. Every other code in U+0001 to U+001F is prohibited.
|
|
59
59
|
*/
|
|
60
60
|
const XML10_ALLOWED_C0 = /* @__PURE__ */ new Set([
|
|
61
61
|
9,
|
|
@@ -86,7 +86,7 @@ const ENTITY_ACTION = Object.freeze({
|
|
|
86
86
|
*
|
|
87
87
|
* @returns An effect producing the name, unchanged, so the call can be inlined. Fails with {@link XmlError} and the `InvalidEntityName` reason,
|
|
88
88
|
* carrying the offending character. The `[EntityReplacer]` prefix in the message is preserved verbatim from the original throw, despite naming a
|
|
89
|
-
* class this decoder does not have
|
|
89
|
+
* class this decoder does not have. It is relied on by anything matching the message text.
|
|
90
90
|
*/
|
|
91
91
|
const checkEntityName = (name) => {
|
|
92
92
|
if (name.charCodeAt(0) === CODE_HASH) return Effect.fail(new XmlError({
|
|
@@ -109,11 +109,11 @@ const checkEntityName = (name) => {
|
|
|
109
109
|
};
|
|
110
110
|
/**
|
|
111
111
|
* @description Flatten registration maps into one name to string map, later maps winning over earlier ones for the same name. The result is a null-prototype
|
|
112
|
-
* object, not a `Map`. That is
|
|
113
|
-
*
|
|
114
|
-
*
|
|
112
|
+
* object, not a `Map`. That is intentional. A `Map` iterates in pure insertion order, while `Object.keys` lifts array-index-like names to the front
|
|
113
|
+
* in numeric order, and the registration hooks observe that order. A name of `"2"` registered after `"brand"` reaches the hook first here and second
|
|
114
|
+
* in a `Map`.
|
|
115
115
|
*
|
|
116
|
-
* @param maps - The maps to merge. A falsy entry
|
|
116
|
+
* @param maps - The maps to merge. A falsy entry (`null`, `undefined`, `''`, `0`) contributes nothing rather than throwing.
|
|
117
117
|
*
|
|
118
118
|
* @returns A null-prototype object of own string-valued entries. Nothing from `Object.prototype` can be read out of it, so a document naming
|
|
119
119
|
* `constructor` or `toString` finds nothing. Each entry is read through {@link flattenEntityValue}, so an entry that cannot be reduced to a string
|
|
@@ -132,15 +132,15 @@ function mergeEntityMaps(...maps) {
|
|
|
132
132
|
}
|
|
133
133
|
/**
|
|
134
134
|
* @description Reduce one registration entry to the string a reference to it expands to, or to nothing when the entry is a form the scanner has no use for. Three
|
|
135
|
-
* shapes survive: the string itself, and a `{ regex | regx, val }` envelope whose `val` is a string. Everything else
|
|
136
|
-
* a bare function, an envelope whose `val` is a function
|
|
137
|
-
*
|
|
135
|
+
* shapes survive: the string itself, and a `{ regex | regx, val }` envelope whose `val` is a string. Everything else (a number, `null`, `undefined`,
|
|
136
|
+
* a bare function, an envelope whose `val` is a function) has no string to substitute, so the name is dropped and a reference to it comes back out as
|
|
137
|
+
* the text it was written as. Dropping is silent on purpose: the runtime inspects whatever it is handed, and failing the construction over one
|
|
138
138
|
* unreadable entry would take every other entity in the table down with it.
|
|
139
139
|
*
|
|
140
|
-
* @param raw - The entry as it arrived, in whatever shape the caller supplied it
|
|
140
|
+
* @param raw - The entry as it arrived, in whatever shape the caller supplied it, including no entry at all, which a table with a hole in it
|
|
141
141
|
* produces.
|
|
142
142
|
*
|
|
143
|
-
* @returns The replacement string, or `undefined` when the entry cannot be read. A name registered to the empty string yields `''
|
|
143
|
+
* @returns The replacement string, or `undefined` when the entry cannot be read. A name registered to the empty string yields `''`. That is why
|
|
144
144
|
* callers compare against `undefined` rather than testing for emptiness.
|
|
145
145
|
*/
|
|
146
146
|
function flattenEntityValue(raw) {
|
|
@@ -155,7 +155,7 @@ function flattenEntityValue(raw) {
|
|
|
155
155
|
* @param map - The map to read.
|
|
156
156
|
* @param key - The entity name.
|
|
157
157
|
*
|
|
158
|
-
* @returns The registered string, or `undefined` when the name is not an own key. A name registered to the empty string returns `''
|
|
158
|
+
* @returns The registered string, or `undefined` when the name is not an own key. A name registered to the empty string returns `''`. That is why
|
|
159
159
|
* callers must compare against `undefined` rather than test for emptiness.
|
|
160
160
|
*/
|
|
161
161
|
function ownEntity(map, key) {
|
|
@@ -168,8 +168,7 @@ function ownEntity(map, key) {
|
|
|
168
168
|
* @param raw - The configured value.
|
|
169
169
|
*
|
|
170
170
|
* @returns The tier set. An unrecognised string falls back to `external` rather than to no filtering at all, so a typo cannot silently disable the
|
|
171
|
-
* limits. An array is taken as given
|
|
172
|
-
* `external`.
|
|
171
|
+
* limits. An array is taken as given. An empty array therefore disables limit accounting entirely, while an empty string falls back to `external`.
|
|
173
172
|
*/
|
|
174
173
|
function parseLimitTiers(raw) {
|
|
175
174
|
if (!raw || raw === LIMIT_TIER_EXTERNAL) return /* @__PURE__ */ new Set([LIMIT_TIER_EXTERNAL]);
|
|
@@ -229,7 +228,7 @@ function readPostCheck(raw) {
|
|
|
229
228
|
*
|
|
230
229
|
* @param raw - The configured hook, or nothing.
|
|
231
230
|
*
|
|
232
|
-
* @returns The hook itself, or `null` for an absent option and for a value that is not a function. `null`
|
|
231
|
+
* @returns The hook itself, or `null` for an absent option and for a value that is not a function. `null` lets every registration path ask
|
|
233
232
|
* unconditionally: a hook that is not there accepts.
|
|
234
233
|
*/
|
|
235
234
|
function readHook(raw) {
|
|
@@ -238,9 +237,9 @@ function readHook(raw) {
|
|
|
238
237
|
}
|
|
239
238
|
/**
|
|
240
239
|
* @description Read one of the two entity-name lists as a set, under the same missing-value rule as the other options: absent is empty, not an error. The
|
|
241
|
-
* `Array.isArray` test rather than a truthiness one
|
|
242
|
-
*
|
|
243
|
-
*
|
|
240
|
+
* `Array.isArray` test rather than a truthiness one keeps a mistyped list from reaching `new Set` and throwing there, so a caller's typo disables the
|
|
241
|
+
* list instead of taking the decoder down. Matching against a set also means a name in both lists is decided by the order {@link EntityDecoder.decode}
|
|
242
|
+
* consults them in, not by the order the caller wrote them in.
|
|
244
243
|
*
|
|
245
244
|
* @param raw - The configured list, or nothing.
|
|
246
245
|
*
|
|
@@ -272,25 +271,25 @@ function scanTokenEnd(str, ampersand) {
|
|
|
272
271
|
*
|
|
273
272
|
* ### Entity lookup priority
|
|
274
273
|
*
|
|
275
|
-
* 1. **input / runtime
|
|
276
|
-
* 2. **persistent external
|
|
274
|
+
* 1. **input / runtime**: injected per document through {@link EntityDecoder.addInputEntities}
|
|
275
|
+
* 2. **persistent external**: set through {@link EntityDecoder.setExternalEntities} and {@link EntityDecoder.addExternalEntity}, surviving
|
|
277
276
|
* {@link EntityDecoder.reset}
|
|
278
|
-
* 3. **base
|
|
277
|
+
* 3. **base**: the five XML predefined entities plus the constructor's `namedEntities` Both input and external resolve as the `external` tier for limit
|
|
279
278
|
* purposes, because both are injected at runtime. Numeric references (`&#NNN;`, `&#xHH;`) resolve directly through `String.fromCodePoint` and are
|
|
280
279
|
* always `base` tier: they cannot recurse, so a limit that counted them would only punish a document that spells its characters out.
|
|
281
280
|
*
|
|
282
281
|
* ### Upstream behaviour preserved
|
|
283
282
|
*
|
|
284
|
-
* Several quirks of the original are kept
|
|
283
|
+
* Several quirks of the original are kept intentionally, because a consumer's output already depends on them:
|
|
285
284
|
*
|
|
286
285
|
* - A value containing `&` is **not** filtered from `namedEntities` or `setExternalEntities`, contrary to the documentation. Only
|
|
287
286
|
* {@link EntityDecoder.addExternalEntity} checks, and it drops the entry rather than storing it, so the same name registered either way can resolve
|
|
288
287
|
* to nothing.
|
|
289
|
-
* - {@link EntityDecoderOptions.postCheck} is skipped entirely for input that never reaches the scan
|
|
288
|
+
* - {@link EntityDecoderOptions.postCheck} is skipped entirely for input that never reaches the scan: an empty string, a non-string, or a string with
|
|
290
289
|
* no `&`.
|
|
291
290
|
* - {@link EntityDecoder.decode} returns a non-string argument unchanged, despite being typed `string`.
|
|
292
291
|
* - The expansion-limit errors are prefixed `EntityReplacer`, not `EntityDecoder`.
|
|
293
|
-
* - Nothing in XML 1.0 §2.2 is enforced for U+007F
|
|
292
|
+
* - Nothing in XML 1.0 §2.2 is enforced for U+007F to U+009F or for the U+FFFE/U+FFFF noncharacters, and the sweep for `&` leaves a name of
|
|
294
293
|
* {@link MAX_TOKEN_LENGTH} + 1 characters unresolvable.
|
|
295
294
|
* - Numeric references are parsed with `parseInt`, so a leading space, sign, or trailing garbage is accepted: `&# 41;`, `&#x+41;` and `)zz;` all
|
|
296
295
|
* decode, and `�x41;` parses as a null reference rather than `A`.
|
|
@@ -304,7 +303,7 @@ function scanTokenEnd(str, ampersand) {
|
|
|
304
303
|
* decoder.addInputEntities({ version: '1.0' });
|
|
305
304
|
*
|
|
306
305
|
* decoder.decode('&brand; v&version; ©'); // 'Acme v1.0 ©'
|
|
307
|
-
* decoder.decode('&#38;'); // '&&'
|
|
306
|
+
* decoder.decode('&#38;'); // '&&', one pass, the output is never re-scanned
|
|
308
307
|
*
|
|
309
308
|
* decoder.reset(); // drops the input entities and the counters, keeps the external ones
|
|
310
309
|
* ```;
|
|
@@ -320,7 +319,7 @@ var EntityDecoder = class EntityDecoder {
|
|
|
320
319
|
*/
|
|
321
320
|
#maxExpandedLength;
|
|
322
321
|
/**
|
|
323
|
-
* @description {@link EntityDecoderOptions.postCheck}, or the identity function
|
|
322
|
+
* @description {@link EntityDecoderOptions.postCheck}, or the identity function. That lets the decode loop call it unconditionally on the path that actually
|
|
324
323
|
* scanned, and never on the two fast paths that return early.
|
|
325
324
|
*/
|
|
326
325
|
#postCheck;
|
|
@@ -339,7 +338,7 @@ var EntityDecoder = class EntityDecoder {
|
|
|
339
338
|
#baseMap;
|
|
340
339
|
/**
|
|
341
340
|
* @description Persistent external entities, as a null-prototype object. Replaced wholesale by {@link EntityDecoder.setExternalEntities} and added to by
|
|
342
|
-
* {@link EntityDecoder.addExternalEntity}, and never touched by {@link EntityDecoder.reset}
|
|
341
|
+
* {@link EntityDecoder.addExternalEntity}, and never touched by {@link EntityDecoder.reset}. That is the whole distinction from the input map.
|
|
343
342
|
*/
|
|
344
343
|
#externalMap;
|
|
345
344
|
/**
|
|
@@ -348,8 +347,8 @@ var EntityDecoder = class EntityDecoder {
|
|
|
348
347
|
*/
|
|
349
348
|
#inputMap;
|
|
350
349
|
/**
|
|
351
|
-
* @description Tracked expansions since the last reset. Cumulative across {@link EntityDecoder.decode} calls, which
|
|
352
|
-
*
|
|
350
|
+
* @description Tracked expansions since the last reset. Cumulative across {@link EntityDecoder.decode} calls, which makes a limit a per-document budget rather
|
|
351
|
+
* than a per-call one. Intentionally not reset by a thrown limit error, so the error message reports the over-limit count.
|
|
353
352
|
*/
|
|
354
353
|
#totalExpansions;
|
|
355
354
|
/**
|
|
@@ -391,7 +390,7 @@ var EntityDecoder = class EntityDecoder {
|
|
|
391
390
|
#onInputEntity;
|
|
392
391
|
/**
|
|
393
392
|
* @description Create a decoder. A factory rather than a constructor, because it refuses a `null` options object. Every field is optional, so `null` is not "a
|
|
394
|
-
* decoder with the defaults"
|
|
393
|
+
* decoder with the defaults". A caller who wrote it meant something the signature does not allow, and a decoder built from it would be
|
|
395
394
|
* indistinguishable from one built from `{}` while hiding the mistake. Saying so is worth a factory; `EntityDecoderOptions` is a plain object and
|
|
396
395
|
* nothing else about construction can fail.
|
|
397
396
|
*
|
|
@@ -414,8 +413,8 @@ var EntityDecoder = class EntityDecoder {
|
|
|
414
413
|
})) : Effect.succeed(new EntityDecoder(options));
|
|
415
414
|
/**
|
|
416
415
|
* @description Create a decoder. Every option is resolved here into the flat fields the decode loop reads, so nothing per-reference has to re-derive it. The
|
|
417
|
-
* options whose wrong type disables them rather than failing the construction
|
|
418
|
-
*
|
|
416
|
+
* options whose wrong type disables them rather than failing the construction (the two hooks, the two name lists) are read through {@link readHook}
|
|
417
|
+
* and {@link readNameList}, so that rule is written once instead of four times.
|
|
419
418
|
*
|
|
420
419
|
* @param resolved - Configuration, already checked. See {@link EntityDecoderOptions}.
|
|
421
420
|
*/
|
|
@@ -443,7 +442,7 @@ var EntityDecoder = class EntityDecoder {
|
|
|
443
442
|
/**
|
|
444
443
|
* @description Ask a registration hook about one name and value.
|
|
445
444
|
*
|
|
446
|
-
* @param hook - The hook, or `null`. A `null` hook accepts,
|
|
445
|
+
* @param hook - The hook, or `null`. A `null` hook accepts, so {@link EntityDecoder.addExternalEntity} can call this unconditionally.
|
|
447
446
|
* @param name - The entity name, without `&` or `;`.
|
|
448
447
|
* @param value - The resolved value, after any `{ regex, val }` envelope was unwrapped.
|
|
449
448
|
* @param context - Which registration is in progress, for the error message.
|
|
@@ -552,14 +551,14 @@ var EntityDecoder = class EntityDecoder {
|
|
|
552
551
|
/**
|
|
553
552
|
* @description Expand every entity reference in a string, in one pass. The output is never re-scanned, so no expansion can produce a _second_ one: a registered
|
|
554
553
|
* value that itself contains reference text reaches the caller as that literal text, unexpanded. What the limits bound is the growth of this single
|
|
555
|
-
* pass
|
|
554
|
+
* pass, meaning how much one round of expansion can add. Three inputs return before the scan and therefore never reach
|
|
556
555
|
* {@link EntityDecoderOptions.postCheck}: a non-string, the empty string, and any string with no `&` in it. The scan itself is `#expandAll`; what
|
|
557
556
|
* this method adds is the three inputs that skip it and the single join of what it collected.
|
|
558
557
|
*
|
|
559
558
|
* @example
|
|
560
559
|
* ```typescript
|
|
561
560
|
* import { Effect } from 'effect';
|
|
562
|
-
* import { EntityDecoder } from '@endevops/effect-xml
|
|
561
|
+
* import { EntityDecoder } from '@endevops/effect-codec-xml';
|
|
563
562
|
*
|
|
564
563
|
* const decoder = new EntityDecoder({ namedEntities: { copy: '©' } });
|
|
565
564
|
* Effect.runSync(Effect.orElseSucceed(decoder.addExternalEntity('brand', 'Acme'), () => undefined));
|
|
@@ -584,15 +583,15 @@ var EntityDecoder = class EntityDecoder {
|
|
|
584
583
|
/**
|
|
585
584
|
* @description Walk the string once and collect the pieces of every reference that resolved. Two advance rules make the walk terminate and keep it correct: an
|
|
586
585
|
* `&` that turns out to open nothing moves the cursor by one character rather than to the end of its run, so a second `&` in the same text is still
|
|
587
|
-
* found; and a reference that did resolve moves it to just past the `;`, so the text that was substituted for it is never looked at again
|
|
588
|
-
*
|
|
589
|
-
* decide and what it costs is `#chargeExpansion`'s to apply, which leaves the scanning here as the only thing with a rule of its own.
|
|
586
|
+
* found; and a reference that did resolve moves it to just past the `;`, so the text that was substituted for it is never looked at again. The pass
|
|
587
|
+
* stays single because of that, and a registered value containing `&` cannot expand a second level. What a reference becomes is `#resolveToken`'s
|
|
588
|
+
* to decide and what it costs is `#chargeExpansion`'s to apply, which leaves the scanning here as the only thing with a rule of its own.
|
|
590
589
|
*
|
|
591
590
|
* @param str - The string to expand. It always holds at least one `&` and is never empty, or the caller would have returned before reaching the
|
|
592
591
|
* walk.
|
|
593
592
|
*
|
|
594
593
|
* @returns An effect producing the pieces in order. The array is empty exactly when nothing was replaced, which the caller reads as "the input is
|
|
595
|
-
* its own result". Fails with {@link XmlError} and the reason the offending reference carries
|
|
594
|
+
* its own result". Fails with {@link XmlError} and the reason the offending reference carries: `ProhibitedCharacterReference`,
|
|
596
595
|
* `ExpansionLimitExceeded` or `ExpandedLengthLimitExceeded`.
|
|
597
596
|
*/
|
|
598
597
|
#expandAll = Effect.fnUntraced(function* (str) {
|
|
@@ -628,19 +627,19 @@ var EntityDecoder = class EntityDecoder {
|
|
|
628
627
|
/**
|
|
629
628
|
* @description Decide what one reference expands to. The lists and maps are consulted in the one order the runtime uses, and the first that matches wins:
|
|
630
629
|
*
|
|
631
|
-
* 1. `remove
|
|
632
|
-
* 2. `leave
|
|
633
|
-
* 3. A `#`-prefixed token
|
|
630
|
+
* 1. `remove`: deleted outright, without the name ever being resolved, so the name need not exist.
|
|
631
|
+
* 2. `leave`: emitted as the original `&token;`, and charged to nothing.
|
|
632
|
+
* 3. A `#`-prefixed token: the numeric pipeline, which is the only one of the four that can fail. Classification runs before any decision about
|
|
634
633
|
* `numericAllowed`, because the ranges that carry a minimum have to be caught whichever way that option is set.
|
|
635
|
-
* 4. Anything else
|
|
634
|
+
* 4. Anything else: resolved against the input map, then the external map, then the base map.
|
|
636
635
|
*
|
|
637
636
|
* @param token - The reference's token, e.g. `brand` or `#38`, with the `&` and the `;` already stripped. Never empty: the scanner drops `&;`
|
|
638
637
|
* before calling.
|
|
639
638
|
*
|
|
640
639
|
* @returns An effect producing what the reference expands to and the tier to charge it to, or `undefined` to leave it as written and charge it
|
|
641
|
-
* nothing. `undefined` covers all three ways of leaving a reference alone
|
|
642
|
-
*
|
|
643
|
-
*
|
|
640
|
+
* nothing. `undefined` covers all three ways of leaving a reference alone (a listed `leave` name, a numeric reference that is out of range, and a
|
|
641
|
+
* name registered nowhere), and none of them is distinguishable from outside. Fails with {@link XmlError} and the `ProhibitedCharacterReference`
|
|
642
|
+
* reason when the numeric policy throws on the codepoint.
|
|
644
643
|
*/
|
|
645
644
|
#resolveToken = Effect.fnUntraced(function* (token) {
|
|
646
645
|
if (this.#removeSet.has(token)) return {
|
|
@@ -681,9 +680,9 @@ var EntityDecoder = class EntityDecoder {
|
|
|
681
680
|
});
|
|
682
681
|
/**
|
|
683
682
|
* @description Add one expansion to the running total and compare it against {@link EntityDecoderLimitOptions.maxTotalExpansions}. The comparison is `>` rather
|
|
684
|
-
* than `>=`, so a limit of `n` allows exactly `n` expansions and throws on the `n + 1`th. That is a contract
|
|
685
|
-
* states it
|
|
686
|
-
*
|
|
683
|
+
* than `>=`, so a limit of `n` allows exactly `n` expansions and throws on the `n + 1`th. That is a contract, and the option's own documentation
|
|
684
|
+
* states it. It is also the kind of off-by-one a tidy-up changes by accident. The counter is intentionally not reset before failing: the over-limit
|
|
685
|
+
* total reports the over-limit total, and {@link EntityDecoder.reset} is the caller's way to start a new document.
|
|
687
686
|
*
|
|
688
687
|
* @returns An effect that fails with {@link XmlError} and the `ExpansionLimitExceeded` reason once the count is past the ceiling, and succeeds
|
|
689
688
|
* otherwise. The `EntityReplacer` prefix in the message is preserved verbatim from the original throw, despite naming a class this decoder does
|
|
@@ -704,7 +703,7 @@ var EntityDecoder = class EntityDecoder {
|
|
|
704
703
|
/**
|
|
705
704
|
* @description Add one expansion's surplus to the running total and compare it against {@link EntityDecoderLimitOptions.maxExpandedLength}. Only the surplus
|
|
706
705
|
* counts, and only upward: a reference whose replacement is no longer than the `&token;` it replaces contributes zero, and a shrinking one
|
|
707
|
-
* contributes nothing and cannot trip the limit at all. That
|
|
706
|
+
* contributes nothing and cannot trip the limit at all. That keeps the ceiling a bound on growth rather than on document size.
|
|
708
707
|
*
|
|
709
708
|
* @param token - The reference's token, with the `&` and `;` stripped. The two delimiters count towards what the expansion displaced.
|
|
710
709
|
* @param replacement - What the reference expanded to, including `''` for a removal.
|
|
@@ -730,8 +729,8 @@ var EntityDecoder = class EntityDecoder {
|
|
|
730
729
|
/**
|
|
731
730
|
* @description Decide whether an entity of a given tier is charged against the limits.
|
|
732
731
|
*
|
|
733
|
-
* @param tier - The tier the replacement is charged to. Every expansion that reaches here carries one
|
|
734
|
-
* still carries the `external` tier
|
|
732
|
+
* @param tier - The tier the replacement is charged to. Every expansion that reaches here carries one (a name deleted before it was ever resolved
|
|
733
|
+
* still carries the `external` tier), so there is no absent case to answer.
|
|
735
734
|
*
|
|
736
735
|
* @returns `true` when it counts. `'all'` short-circuits, so a filter naming every tier charges everything regardless of which map it came from.
|
|
737
736
|
*/
|
|
@@ -767,9 +766,9 @@ var EntityDecoder = class EntityDecoder {
|
|
|
767
766
|
/**
|
|
768
767
|
* @description Find the strictest action a codepoint's range requires. Checked in this order:
|
|
769
768
|
*
|
|
770
|
-
* 1. U+0000
|
|
771
|
-
* 2. U+D800
|
|
772
|
-
* 3. U+0001
|
|
769
|
+
* 1. U+0000: governed by `nullNCR`, already clamped to `remove` or stricter
|
|
770
|
+
* 2. U+D800 to U+DFFF: surrogates, always `remove`, under every policy and both XML versions
|
|
771
|
+
* 3. U+0001 to U+001F other than tab, newline, carriage return: XML 1.0 only, `remove` Nothing else is classified. U+007F to U+009F (C1) and the
|
|
773
772
|
* U+FFFE/U+FFFF noncharacters are not checked, even though XML 1.0 §2.2 prohibits them and the `xmlVersion` option's own documentation claims C1
|
|
774
773
|
* is only permitted under 1.1. Both gaps are upstream's and are kept.
|
|
775
774
|
*
|
|
@@ -787,11 +786,11 @@ var EntityDecoder = class EntityDecoder {
|
|
|
787
786
|
* @description Turn a resolved action level into a replacement.
|
|
788
787
|
*
|
|
789
788
|
* @param action - A level from {@link NCR_LEVEL}. A level outside the four known ones falls through to the allow behaviour, so a bad level cannot
|
|
790
|
-
* produce a wrong string
|
|
789
|
+
* produce a wrong string. It can only fail open.
|
|
791
790
|
* @param token - The raw token, e.g. `#38`, for the error message.
|
|
792
791
|
* @param cp - The codepoint, for the error message.
|
|
793
792
|
*
|
|
794
|
-
* @returns An effect producing the character for `allow`, `''` for `remove`, and `undefined` for `leave
|
|
793
|
+
* @returns An effect producing the character for `allow`, `''` for `remove`, and `undefined` for `leave`, which the caller reads as "emit the
|
|
795
794
|
* original `&token;`". Fails with {@link XmlError} and the `ProhibitedCharacterReference` reason for `throw`, naming both the token and the
|
|
796
795
|
* codepoint.
|
|
797
796
|
*/
|
|
@@ -820,7 +819,7 @@ var EntityDecoder = class EntityDecoder {
|
|
|
820
819
|
*
|
|
821
820
|
* @param token - The raw token without `&` and `;`, e.g. `#38`, `#x26`, `#X26`.
|
|
822
821
|
*
|
|
823
|
-
* @returns An effect producing the replacement
|
|
822
|
+
* @returns An effect producing the replacement (the empty string meaning "delete") or `undefined` to leave the reference as written. Fails with
|
|
824
823
|
* {@link XmlError} and the `ProhibitedCharacterReference` reason when the effective action is `throw`.
|
|
825
824
|
*/
|
|
826
825
|
#resolveNCR(token) {
|