@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
package/src/xml-error.ts
CHANGED
|
@@ -6,38 +6,38 @@
|
|
|
6
6
|
*
|
|
7
7
|
* ## Why one error with a `reason`, and not one error per cause
|
|
8
8
|
*
|
|
9
|
-
* The causes would mean a class each, and a caller handling "any of these" would need `catchTags` with all of them.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* The causes would mean a class each, and a caller handling "any of these" would need `catchTags` with all of them. A caller does not usually want to
|
|
10
|
+
* decide between the causes separately. They are all "this XML input was not acceptable", and the useful split is coarse: a bad _configuration_
|
|
11
|
+
* versus a bad _document_. So there is one error, and `reason` narrows to the specific cause. Recovery is a `catchReason` away:
|
|
12
12
|
*
|
|
13
13
|
* @example
|
|
14
14
|
* ```typescript
|
|
15
15
|
* import { Effect } from 'effect';
|
|
16
|
-
* import { EntityDecoder, XmlError } from '@endevops/effect-xml
|
|
16
|
+
* import { EntityDecoder, XmlError } from '@endevops/effect-codec-xml';
|
|
17
17
|
*
|
|
18
18
|
* const limited = new EntityDecoder({ limit: { maxTotalExpansions: 2 } }).decode('&&&').pipe(
|
|
19
19
|
* Effect.catchReason('XmlError', 'ExpansionLimitExceeded', reason => Effect.succeed(`gave up after ${reason.actual}`)),
|
|
20
20
|
* );
|
|
21
21
|
* ```;
|
|
22
22
|
*
|
|
23
|
-
* The message is kept alongside `reason` and is part of the schema, because the text
|
|
24
|
-
* `[EntityReplacer]` prefix in particular is documented as something callers match on, so
|
|
25
|
-
* rather than
|
|
23
|
+
* The message is kept alongside `reason` and is part of the schema, because callers depend on the text. The
|
|
24
|
+
* `[EntityReplacer]` prefix in particular is documented as something callers match on, so the codec reproduces it exactly
|
|
25
|
+
* rather than rewording it.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { Schema } from 'effect';
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* @description The specific cause of an {@link XmlError}, as a tagged union. The `_tag` on each member is the discriminant `Effect.catchReason` matches on, and
|
|
32
|
-
* the payload is what a handler needs in order to decide or to report. Every member is a case the decoder or the name validators actually raise
|
|
33
|
-
*
|
|
32
|
+
* the payload is what a handler needs in order to decide or to report. Every member is a case the decoder or the name validators actually raise.
|
|
33
|
+
* There is no catch-all member, so an exhaustive `match` stays exhaustive as causes are added.
|
|
34
34
|
*/
|
|
35
35
|
export const XmlErrorReason = Schema.TaggedUnion({
|
|
36
36
|
/**
|
|
37
37
|
* @description A required argument was `null` or another non-value where the package requires a real one. Raised by the factories that take caller-supplied
|
|
38
|
-
* input
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* input, {@link EntityDecoder.make} among them, when they are handed `null` for an options object that has no meaningful default. It is a distinct
|
|
39
|
+
* case from the rest because the argument is not _wrong_, it is _absent_, and a caller who wrote `make(null)` meant something the type system does
|
|
40
|
+
* not allow. A decoder with every default is `make({})`, and saying so here is more useful than silently producing one.
|
|
41
41
|
*/
|
|
42
42
|
MissingOptions: {
|
|
43
43
|
/**
|
|
@@ -48,7 +48,7 @@ export const XmlErrorReason = Schema.TaggedUnion({
|
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
50
|
* @description A name was checked against one of the five XML name productions and given a different one. Unreachable from TypeScript, where `Production` is a
|
|
51
|
-
* closed union
|
|
51
|
+
* closed union. It is the guard for untyped JavaScript callers and for values that crossed a boundary as `unknown`.
|
|
52
52
|
*/
|
|
53
53
|
InvalidProduction: {
|
|
54
54
|
production: Schema.String,
|
|
@@ -78,7 +78,7 @@ export const XmlErrorReason = Schema.TaggedUnion({
|
|
|
78
78
|
*/
|
|
79
79
|
EntityRejected: {
|
|
80
80
|
/**
|
|
81
|
-
* @description Which registration was in progress.
|
|
81
|
+
* @description Which registration was in progress. The runtime injects both, so they share a tier for limit accounting.
|
|
82
82
|
*/
|
|
83
83
|
context: Schema.Literals(['external', 'input']),
|
|
84
84
|
/**
|
|
@@ -93,7 +93,7 @@ export const XmlErrorReason = Schema.TaggedUnion({
|
|
|
93
93
|
*/
|
|
94
94
|
ExpansionLimitExceeded: {
|
|
95
95
|
/**
|
|
96
|
-
* @description The count that tripped the limit.
|
|
96
|
+
* @description The count that tripped the limit. The counter is not reset on failure, so this is the real over-limit total rather than the ceiling.
|
|
97
97
|
*/
|
|
98
98
|
actual: Schema.Number,
|
|
99
99
|
/**
|
|
@@ -139,14 +139,14 @@ export const XmlErrorReason = Schema.TaggedUnion({
|
|
|
139
139
|
export type XmlErrorReason = typeof XmlErrorReason.Type;
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
|
-
* @description Every failure this package can report, in the `E` channel of the effects that can fail. Carries both a `reason
|
|
142
|
+
* @description Every failure this package can report, in the `E` channel of the effects that can fail. Carries both a `reason`, the typed and matchable cause, and
|
|
143
143
|
* a `message`, which is the human-readable form the package has always produced. Both are part of the schema, so an error survives a round-trip
|
|
144
144
|
* through a serialised boundary without losing either.
|
|
145
145
|
*
|
|
146
146
|
* @example
|
|
147
147
|
* ```typescript
|
|
148
148
|
* import { Effect } from 'effect';
|
|
149
|
-
* import { EntityDecoder } from '@endevops/effect-xml
|
|
149
|
+
* import { EntityDecoder } from '@endevops/effect-codec-xml';
|
|
150
150
|
*
|
|
151
151
|
* const program = Effect.gen(function*() {
|
|
152
152
|
* const decoder = yield* EntityDecoder.make({});
|
|
@@ -162,7 +162,7 @@ export class XmlError extends Schema.TaggedError<XmlError>()('XmlError', {
|
|
|
162
162
|
|
|
163
163
|
/**
|
|
164
164
|
* @description Human-readable description. The `[EntityReplacer]` and `[EntityDecoder]` prefixes are reproduced verbatim from the original throw sites, because
|
|
165
|
-
*
|
|
165
|
+
* anything matching on them depends on them.
|
|
166
166
|
*/
|
|
167
167
|
message: Schema.String,
|
|
168
168
|
}) {}
|
package/src/xml-value.ts
CHANGED
|
@@ -11,10 +11,10 @@ export interface XmlRecord {
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @description One value in an XML document: nothing at all, character data, a repeated run of children, or a record of attributes, text and child elements.
|
|
14
|
-
* `undefined` is a value of its own rather than an omission, because that is how an absent optional field survives a round trip
|
|
15
|
-
* value stays distinguishable from a field whose value is the empty string, and the renderer writes neither of them. The shape is
|
|
16
|
-
*
|
|
17
|
-
* re-implementing the derivation.
|
|
14
|
+
* `undefined` is a value of its own rather than an omission, because that is how an absent optional field survives a round trip. A field with no
|
|
15
|
+
* value stays distinguishable from a field whose value is the empty string, and the renderer writes neither of them. The shape is the same one
|
|
16
|
+
* `Schema.toCodecStringTree` derives, and sharing that shape is what lets every schema feature Effect supports round-trip through this package
|
|
17
|
+
* without re-implementing the derivation.
|
|
18
18
|
*/
|
|
19
19
|
export type XmlValue = string | undefined | ReadonlyArray<XmlValue> | XmlRecord;
|
|
20
20
|
|
|
@@ -34,10 +34,9 @@ const MAX_GUARD_DEPTH = 512;
|
|
|
34
34
|
export const isXmlValue = (input: unknown): input is XmlValue => check(input, 0);
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* @description One level of {@link isXmlValue}, with the depth it was reached at. The depth is the whole defence against a value built to be hostile
|
|
38
|
-
* self-referential object would otherwise recurse until the stack gave out, and a value nested thousands deep would take it with it.
|
|
39
|
-
*
|
|
40
|
-
* it.
|
|
37
|
+
* @description One level of {@link isXmlValue}, with the depth it was reached at. The depth is the whole defence against a value built to be hostile. A
|
|
38
|
+
* self-referential object would otherwise recurse until the stack gave out, and a value nested thousands deep would take it with it. This rejects
|
|
39
|
+
* both, so it is a bounded recursion rather than a loop. The model is a tree, and walking a tree means walking it.
|
|
41
40
|
*
|
|
42
41
|
* @param input - The candidate value.
|
|
43
42
|
* @param depth - How many levels down this value sits.
|
|
@@ -90,13 +89,13 @@ const everyFieldIs = (input: object, depth: number): boolean => {
|
|
|
90
89
|
};
|
|
91
90
|
|
|
92
91
|
/**
|
|
93
|
-
* @description A schema for {@link XmlValue}, so a value can be validated on its own
|
|
94
|
-
* and the schema it belongs to is not in hand.
|
|
92
|
+
* @description A schema for {@link XmlValue}, so a value can be validated on its own. This helps when it arrives from a store or a queue rather than from
|
|
93
|
+
* {@link parseXml}, and the schema it belongs to is not in hand.
|
|
95
94
|
*
|
|
96
95
|
* @example
|
|
97
96
|
* ```typescript
|
|
98
97
|
* import { Schema } from 'effect';
|
|
99
|
-
* import { XmlValue } from '@endevops/effect-xml
|
|
98
|
+
* import { XmlValue } from '@endevops/effect-codec-xml';
|
|
100
99
|
*
|
|
101
100
|
* Schema.decodeUnknownSync(XmlValue)({ book: { '@id': '1', title: 'Dune' } }); // => { book: { '@id': '1', title: 'Dune' } }
|
|
102
101
|
* Schema.decodeUnknownSync(XmlValue)({ book: { title: 42 } }); // => throws XmlValue
|