xml-model 2.0.0-beta.5 → 2.0.0-beta.7
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/dist/index.js +3 -3
- package/dist/xml/codec.d.ts +22 -19
- package/dist/xml/codec.js +192 -45
- package/dist/xml/examples.d.ts +208 -14
- package/dist/xml/index.d.ts +2 -2
- package/dist/xml/model.d.ts +0 -32
- package/dist/xml/model.js +7 -30
- package/dist/xml/schema-meta.d.ts +15 -0
- package/dist/xml/schema-meta.js +6 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DATA, isModel, model } from "./model.js";
|
|
2
2
|
import XML, { ZXMLCommentNode, ZXMLElementNode, ZXMLNode, ZXMLRoot, ZXMLTextNode } from "./xml/xml-js.js";
|
|
3
3
|
import { xml } from "./xml/schema-meta.js";
|
|
4
|
-
import { normalizeCodecOptions, registerDefault } from "./xml/codec.js";
|
|
5
|
-
import {
|
|
4
|
+
import { XMLCodecError, normalizeCodecOptions, registerDefault, xmlStateSchema } from "./xml/codec.js";
|
|
5
|
+
import { xmlModel } from "./xml/model.js";
|
|
6
6
|
import "./xml/index.js";
|
|
7
|
-
export { DATA, XML,
|
|
7
|
+
export { DATA, XML, XMLCodecError, ZXMLCommentNode, ZXMLElementNode, ZXMLNode, ZXMLRoot, ZXMLTextNode, isModel, model, normalizeCodecOptions, registerDefault, xml, xmlModel, xmlStateSchema };
|
package/dist/xml/codec.d.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { XMLElement } from './xml-js';
|
|
3
|
+
export declare class XMLCodecError extends Error {
|
|
4
|
+
readonly path: readonly (string | number)[];
|
|
5
|
+
readonly rawMessage: string;
|
|
6
|
+
constructor(rawMessage: string, path?: readonly (string | number)[], options?: ErrorOptions);
|
|
7
|
+
}
|
|
3
8
|
export declare function assertSingleElement(xml: XMLElement[]): asserts xml is [XMLElement];
|
|
4
9
|
export declare function assertSingleRoot(xml: XMLElement[]): asserts xml is [XMLElement & {
|
|
5
10
|
elements: XMLElement[];
|
|
6
11
|
}];
|
|
7
|
-
type PropKey<S extends z.ZodObject> = keyof z.input<S> &
|
|
12
|
+
type PropKey<S extends z.ZodObject> = keyof z.input<S> & string;
|
|
8
13
|
export interface CodecOptions<S extends z.ZodType> {
|
|
9
14
|
schema: S;
|
|
10
15
|
tagname(ctx: RootEncodingContext<S>): string;
|
|
@@ -46,7 +51,7 @@ export interface RootDecodingContext<S extends z.ZodType> {
|
|
|
46
51
|
}
|
|
47
52
|
export interface RootEncodingContext<S extends z.ZodType> {
|
|
48
53
|
options: CodecOptions<S>;
|
|
49
|
-
data: z.
|
|
54
|
+
data: z.input<S>;
|
|
50
55
|
}
|
|
51
56
|
export interface PropertyDecodingContext<S extends z.ZodObject = z.ZodObject, K extends PropKey<S> = PropKey<S>> extends RootDecodingContext<S> {
|
|
52
57
|
property: {
|
|
@@ -63,7 +68,7 @@ export interface PropertyEncodingContext<S extends z.ZodObject = z.ZodObject, K
|
|
|
63
68
|
name: K;
|
|
64
69
|
options: CodecOptions<z.ZodType>;
|
|
65
70
|
tagname: string;
|
|
66
|
-
value: z.
|
|
71
|
+
value: z.input<S>[K];
|
|
67
72
|
};
|
|
68
73
|
result: XMLElement;
|
|
69
74
|
}
|
|
@@ -75,29 +80,27 @@ export interface XMLState {
|
|
|
75
80
|
/** Present when xmlStateSchema({ source: true }) is used: the original XMLElement. */
|
|
76
81
|
source?: XMLElement;
|
|
77
82
|
}
|
|
78
|
-
/**
|
|
79
|
-
* String key used to store XML round-trip state on decoded data objects.
|
|
80
|
-
* Using a string (rather than a Symbol) allows Zod's schema.parse() to
|
|
81
|
-
* preserve it naturally when the key is included in the schema via xmlStateSchema().
|
|
82
|
-
*/
|
|
83
|
-
export declare const XML_STATE_KEY: "__xml_state";
|
|
84
83
|
/**
|
|
85
84
|
* Schema for the XML round-trip state field.
|
|
86
85
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
86
|
+
* Add a field with this schema to any `xmlModel` ZodObject to opt in to:
|
|
87
|
+
* - **Element ordering** — elements are re-emitted in source order, not schema order.
|
|
88
|
+
* - **Unknown elements** — unrecognised elements are passed through verbatim on re-encode.
|
|
89
89
|
*
|
|
90
|
-
*
|
|
90
|
+
* The field can be named anything; the codec detects it automatically.
|
|
91
|
+
* Pass `{ source: true }` to additionally store the original `XMLElement` on the instance.
|
|
91
92
|
*
|
|
92
93
|
* @example
|
|
93
|
-
* class
|
|
94
|
-
*
|
|
95
|
-
*
|
|
94
|
+
* class Device extends xmlModel(z.object({
|
|
95
|
+
* _xmlState: xmlStateSchema(),
|
|
96
|
+
* name: z.string(),
|
|
97
|
+
* }), { tagname: "device" }) {}
|
|
96
98
|
*
|
|
97
99
|
* // With source recording:
|
|
98
|
-
* class
|
|
99
|
-
*
|
|
100
|
-
*
|
|
100
|
+
* class Device extends xmlModel(z.object({
|
|
101
|
+
* _xmlState: xmlStateSchema({ source: true }),
|
|
102
|
+
* name: z.string(),
|
|
103
|
+
* }), { tagname: "device" }) {}
|
|
101
104
|
*/
|
|
102
105
|
export declare function xmlStateSchema(): z.ZodOptional<z.ZodCustom<XMLState>>;
|
|
103
106
|
export declare function xmlStateSchema(options: {
|
|
@@ -106,7 +109,7 @@ export declare function xmlStateSchema(options: {
|
|
|
106
109
|
source: XMLElement;
|
|
107
110
|
}>>;
|
|
108
111
|
export declare function decode<S extends z.ZodType>(schema: S, xml: XMLElement): z.input<S>;
|
|
109
|
-
export declare function encode<S extends z.ZodType>(schema: S, data: z.
|
|
112
|
+
export declare function encode<S extends z.ZodType>(schema: S, data: z.input<S>): XMLElement;
|
|
110
113
|
type DefaultResolver<S extends z.ZodType = z.ZodType> = (schema: S) => CodecOptions<S> | void;
|
|
111
114
|
export declare function registerDefault(resolve: DefaultResolver): void;
|
|
112
115
|
export declare function xmlCodec<S extends z.ZodType>(schema: S): z.ZodCodec<z.ZodString, S>;
|
package/dist/xml/codec.js
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
import XML from "./xml-js.js";
|
|
2
2
|
import { isZodType } from "../util/zod.js";
|
|
3
|
-
import { getUserOptions, prop } from "./schema-meta.js";
|
|
4
3
|
import { kebabCase } from "../util/kebab-case.js";
|
|
4
|
+
import { getUserOptions, prop } from "./schema-meta.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
//#region src/xml/codec.ts
|
|
7
|
+
var XMLCodecError = class extends Error {
|
|
8
|
+
path;
|
|
9
|
+
rawMessage;
|
|
10
|
+
constructor(rawMessage, path = [], options) {
|
|
11
|
+
super(path.length ? `[${path.join(".")}] ${rawMessage}` : rawMessage, options);
|
|
12
|
+
this.name = "XMLCodecError";
|
|
13
|
+
this.path = path;
|
|
14
|
+
this.rawMessage = rawMessage;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
function rethrow(e, segment) {
|
|
18
|
+
const cause = e instanceof XMLCodecError ? e.cause : e;
|
|
19
|
+
const path = e instanceof XMLCodecError ? [segment, ...e.path] : [segment];
|
|
20
|
+
throw new XMLCodecError(e instanceof XMLCodecError ? e.rawMessage : e instanceof Error ? e.message : String(e), path, { cause });
|
|
21
|
+
}
|
|
7
22
|
function assertSingleElement(xml) {
|
|
8
23
|
if (xml.length !== 1) throw new Error(`Expected single XML element, got ${xml.length}`);
|
|
9
24
|
}
|
|
@@ -68,19 +83,17 @@ function resolveCodecOptions(schema) {
|
|
|
68
83
|
cache.set(schema, options);
|
|
69
84
|
return options;
|
|
70
85
|
}
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
* Using a string (rather than a Symbol) allows Zod's schema.parse() to
|
|
74
|
-
* preserve it naturally when the key is included in the schema via xmlStateSchema().
|
|
75
|
-
*/
|
|
76
|
-
var XML_STATE_KEY = "__xml_state";
|
|
86
|
+
/** Tracks all schemas created by `xmlStateSchema()` for fast detection at setup time. */
|
|
87
|
+
var xmlStateSchemas = /* @__PURE__ */ new WeakSet();
|
|
77
88
|
function xmlStateSchema(options) {
|
|
78
|
-
|
|
89
|
+
const result = prop(z.custom().optional(), {
|
|
79
90
|
decode: options?.source ? (ctx, _next) => {
|
|
80
|
-
(ctx.result[
|
|
91
|
+
(ctx.result[ctx.property.name] ??= {}).source = ctx.xml;
|
|
81
92
|
} : () => {},
|
|
82
93
|
encode: () => {}
|
|
83
94
|
});
|
|
95
|
+
xmlStateSchemas.add(result);
|
|
96
|
+
return result;
|
|
84
97
|
}
|
|
85
98
|
function resolvePropertiesCodecOptions(schema) {
|
|
86
99
|
const shape = schema.def.shape;
|
|
@@ -88,6 +101,16 @@ function resolvePropertiesCodecOptions(schema) {
|
|
|
88
101
|
for (const [prop, fieldSchema] of Object.entries(shape)) options[prop] = resolveCodecOptions(fieldSchema);
|
|
89
102
|
return options;
|
|
90
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Scans a ZodObject shape for a field created with `xmlStateSchema()`.
|
|
106
|
+
* Returns the field key if found, `undefined` if absent.
|
|
107
|
+
* Throws if more than one such field is present (not supported).
|
|
108
|
+
*/
|
|
109
|
+
function findXmlStateKey(shape) {
|
|
110
|
+
const keys = Object.keys(shape).filter((k) => xmlStateSchemas.has(shape[k]));
|
|
111
|
+
if (keys.length > 1) throw new Error(`Only one xmlStateSchema field is allowed per object schema, found: ${keys.join(", ")}`);
|
|
112
|
+
return keys[0];
|
|
113
|
+
}
|
|
91
114
|
function decode(schema, xml) {
|
|
92
115
|
const options = resolveCodecOptions(schema);
|
|
93
116
|
return options.decode({
|
|
@@ -190,30 +213,23 @@ registerDefault((schema) => {
|
|
|
190
213
|
}
|
|
191
214
|
if (schema instanceof z.ZodCodec) {
|
|
192
215
|
const inSchema = schema.def.in;
|
|
193
|
-
const outSchema = schema.def.out;
|
|
194
216
|
if (!isZodType(inSchema)) throw new Error(`Expected schema.def.in to be a ZodType, got ${inSchema}`);
|
|
195
|
-
if (!isZodType(outSchema)) throw new Error(`Expected schema.def.out to be a ZodType, got ${outSchema}`);
|
|
196
217
|
const inputCodecOptions = resolveCodecOptions(inSchema);
|
|
197
218
|
return normalizeCodecOptions(schema, {
|
|
198
219
|
decode({ xml }) {
|
|
199
|
-
|
|
220
|
+
return inputCodecOptions.decode({
|
|
200
221
|
options: inputCodecOptions,
|
|
201
222
|
xml
|
|
202
223
|
});
|
|
203
|
-
return schema.def.transform(input, {
|
|
204
|
-
value: input,
|
|
205
|
-
issues: []
|
|
206
|
-
});
|
|
207
224
|
},
|
|
208
225
|
encode(ctx) {
|
|
209
|
-
const data = outSchema.encode(ctx.data);
|
|
210
226
|
const innerOpts = ctx.options.tagname !== inputCodecOptions.tagname ? {
|
|
211
227
|
...inputCodecOptions,
|
|
212
228
|
tagname: ctx.options.tagname
|
|
213
229
|
} : inputCodecOptions;
|
|
214
230
|
return innerOpts.encode({
|
|
215
231
|
options: innerOpts,
|
|
216
|
-
data
|
|
232
|
+
data: ctx.data
|
|
217
233
|
});
|
|
218
234
|
}
|
|
219
235
|
});
|
|
@@ -260,10 +276,12 @@ registerDefault((schema) => {
|
|
|
260
276
|
registerDefault((schema) => {
|
|
261
277
|
if (schema instanceof z.ZodObject) {
|
|
262
278
|
const options = resolvePropertiesCodecOptions(schema);
|
|
279
|
+
const stateKey = findXmlStateKey(schema.def.shape);
|
|
263
280
|
return normalizeCodecOptions(schema, {
|
|
264
281
|
decode(ctx) {
|
|
265
|
-
const sequence = [];
|
|
266
|
-
const result = {
|
|
282
|
+
const sequence = stateKey ? [] : void 0;
|
|
283
|
+
const result = {};
|
|
284
|
+
if (stateKey) result[stateKey] = { sequence };
|
|
267
285
|
const propContexts = Object.fromEntries(Object.entries(options).map(([name, propOpts]) => {
|
|
268
286
|
return [name, {
|
|
269
287
|
name,
|
|
@@ -287,19 +305,19 @@ registerDefault((schema) => {
|
|
|
287
305
|
}
|
|
288
306
|
}
|
|
289
307
|
if (!matches.length) {
|
|
290
|
-
sequence.push(el);
|
|
308
|
+
if (sequence) sequence.push(el);
|
|
291
309
|
continue;
|
|
292
310
|
} else if (matches.length === 1) {
|
|
293
311
|
const propName = matches[0];
|
|
294
312
|
if (seenProperties.has(propName)) {
|
|
295
313
|
if (!options[propName].inlineProperty) throw new Error("Matching multiple elements for a single property is only supported when `inlineProperty` is true");
|
|
296
314
|
} else {
|
|
297
|
-
sequence.push(propName);
|
|
315
|
+
if (sequence) sequence.push(propName);
|
|
298
316
|
seenProperties.add(propName);
|
|
299
317
|
}
|
|
300
318
|
} else throw new Error(`Same element was matched by multiple properties: ${matches.join(", ")}`);
|
|
301
319
|
}
|
|
302
|
-
for (const propName in options) if (!seenProperties.has(propName)) sequence.push(propName);
|
|
320
|
+
for (const propName in options) if (!seenProperties.has(propName) && sequence) sequence.push(propName);
|
|
303
321
|
for (const prop in options) {
|
|
304
322
|
const o = options[prop];
|
|
305
323
|
const propCtx = propContexts[prop];
|
|
@@ -311,12 +329,16 @@ registerDefault((schema) => {
|
|
|
311
329
|
propCtx.xml = matches[0];
|
|
312
330
|
}
|
|
313
331
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
332
|
+
try {
|
|
333
|
+
o.decodeAsProperty({
|
|
334
|
+
options: ctx.options,
|
|
335
|
+
xml: ctx.xml,
|
|
336
|
+
property: propCtx,
|
|
337
|
+
result
|
|
338
|
+
});
|
|
339
|
+
} catch (e) {
|
|
340
|
+
rethrow(e, prop);
|
|
341
|
+
}
|
|
320
342
|
}
|
|
321
343
|
return result;
|
|
322
344
|
},
|
|
@@ -328,31 +350,156 @@ registerDefault((schema) => {
|
|
|
328
350
|
attributes: {},
|
|
329
351
|
elements: []
|
|
330
352
|
};
|
|
331
|
-
const
|
|
332
|
-
for (const item of
|
|
353
|
+
const iterOrder = (stateKey ? data[stateKey]?.sequence : void 0) ?? Object.keys(options);
|
|
354
|
+
for (const item of iterOrder) if (typeof item === "string") {
|
|
333
355
|
const o = options[item];
|
|
334
356
|
if (!o) throw new Error(`Failed to resolve property options for sequence item ${item}`);
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
options: o,
|
|
341
|
-
tagname: o.propertyTagname({
|
|
357
|
+
try {
|
|
358
|
+
o.encodeAsProperty({
|
|
359
|
+
options: ctx.options,
|
|
360
|
+
data,
|
|
361
|
+
property: {
|
|
342
362
|
name: item,
|
|
343
|
-
options: o
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
363
|
+
options: o,
|
|
364
|
+
tagname: o.propertyTagname({
|
|
365
|
+
name: item,
|
|
366
|
+
options: o
|
|
367
|
+
}),
|
|
368
|
+
value: data[item]
|
|
369
|
+
},
|
|
370
|
+
result
|
|
371
|
+
});
|
|
372
|
+
} catch (e) {
|
|
373
|
+
rethrow(e, item);
|
|
374
|
+
}
|
|
349
375
|
} else result.elements.push(item);
|
|
350
376
|
return result;
|
|
351
377
|
}
|
|
352
378
|
});
|
|
353
379
|
}
|
|
354
380
|
});
|
|
381
|
+
/**
|
|
382
|
+
* Recursively extracts the set of literal values from a schema,
|
|
383
|
+
* unwrapping ZodCodec, ZodOptional, etc. as needed.
|
|
384
|
+
*/
|
|
385
|
+
function getLiteralValues(schema) {
|
|
386
|
+
if (schema instanceof z.ZodLiteral) return schema.def.values;
|
|
387
|
+
if (schema instanceof z.ZodCodec) return getLiteralValues(schema.def.in);
|
|
388
|
+
if (schema instanceof z.ZodOptional) return getLiteralValues(schema.def.innerType);
|
|
389
|
+
return [];
|
|
390
|
+
}
|
|
391
|
+
function formatReason(errors) {
|
|
392
|
+
return errors.map((e) => e instanceof Error ? e.message : String(e)).join("; ");
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Reads the discriminator field value from an XML element without a full decode.
|
|
396
|
+
* Handles both XML-attribute discriminators (xml.attr) and child-element discriminators.
|
|
397
|
+
*/
|
|
398
|
+
function peekDiscriminatorValue(discriminator, propertyOptions, ctx) {
|
|
399
|
+
const errors = [];
|
|
400
|
+
for (const options of propertyOptions) try {
|
|
401
|
+
const propCtx = {
|
|
402
|
+
name: discriminator,
|
|
403
|
+
options,
|
|
404
|
+
tagname: options.propertyTagname({
|
|
405
|
+
name: discriminator,
|
|
406
|
+
options
|
|
407
|
+
}),
|
|
408
|
+
xml: { elements: [] }
|
|
409
|
+
};
|
|
410
|
+
ctx.xml.elements.forEach((el) => {
|
|
411
|
+
if (el.type !== "element") return;
|
|
412
|
+
if (options.propertyMatch(el, propCtx)) propCtx.xml.elements.push(el);
|
|
413
|
+
});
|
|
414
|
+
if (propCtx.xml.elements.length === 0) propCtx.xml = null;
|
|
415
|
+
else if (propCtx.xml.elements.length !== 1) throw new Error("Matched multiple elements for a single property");
|
|
416
|
+
else propCtx.xml = propCtx.xml.elements[0];
|
|
417
|
+
const result = {};
|
|
418
|
+
options.decodeAsProperty({
|
|
419
|
+
options: ctx.options,
|
|
420
|
+
xml: ctx.xml,
|
|
421
|
+
property: propCtx,
|
|
422
|
+
result
|
|
423
|
+
});
|
|
424
|
+
return result[discriminator];
|
|
425
|
+
} catch (e) {
|
|
426
|
+
errors.push(e);
|
|
427
|
+
}
|
|
428
|
+
throw new XMLCodecError(`union: no option matched for decoding (${formatReason(errors)})`);
|
|
429
|
+
}
|
|
430
|
+
registerDefault((schema) => {
|
|
431
|
+
if (schema instanceof z.ZodDiscriminatedUnion) {
|
|
432
|
+
const discriminator = schema.def.discriminator;
|
|
433
|
+
const options = schema.def.options;
|
|
434
|
+
const optionCodecs = /* @__PURE__ */ new Map();
|
|
435
|
+
const discriminatorSchemas = [];
|
|
436
|
+
for (const option of options) {
|
|
437
|
+
const inSchema = option instanceof z.ZodCodec ? option.def.in : option;
|
|
438
|
+
if (!(inSchema instanceof z.ZodObject)) throw new TypeError(`Discriminated union members are supposed to be objects, got ${inSchema.type}`);
|
|
439
|
+
const discriminatorSchema = inSchema.shape[discriminator];
|
|
440
|
+
if (!discriminatorSchema) throw new TypeError(`Missing discriminator field "${discriminator}" in schema`);
|
|
441
|
+
discriminatorSchemas.push(discriminatorSchema);
|
|
442
|
+
const optCodec = resolveCodecOptions(inSchema);
|
|
443
|
+
for (const val of getLiteralValues(discriminatorSchema)) optionCodecs.set(val, optCodec);
|
|
444
|
+
}
|
|
445
|
+
const discriminatorOptions = discriminatorSchemas.map(resolveCodecOptions);
|
|
446
|
+
return normalizeCodecOptions(schema, {
|
|
447
|
+
decode(ctx) {
|
|
448
|
+
const { xml } = ctx;
|
|
449
|
+
if (!xml) throw new XMLCodecError(`discriminated union requires an XML element`);
|
|
450
|
+
const discValue = peekDiscriminatorValue(discriminator, discriminatorOptions, ctx);
|
|
451
|
+
const matched = optionCodecs.get(discValue);
|
|
452
|
+
if (!matched) throw new XMLCodecError(`no variant matched discriminator "${discriminator}" = "${String(discValue)}"`);
|
|
453
|
+
return matched.decode({
|
|
454
|
+
options: matched,
|
|
455
|
+
xml
|
|
456
|
+
});
|
|
457
|
+
},
|
|
458
|
+
encode(ctx) {
|
|
459
|
+
const discValue = ctx.data[discriminator];
|
|
460
|
+
const matched = optionCodecs.get(discValue);
|
|
461
|
+
if (!matched) throw new XMLCodecError(`no variant matched discriminator "${discriminator}" = "${String(discValue)}"`);
|
|
462
|
+
return matched.encode({
|
|
463
|
+
options: matched,
|
|
464
|
+
data: ctx.data
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
if (schema instanceof z.ZodUnion) {
|
|
470
|
+
const codecOptions = schema.def.options.map((option) => {
|
|
471
|
+
const inSchema = option instanceof z.ZodCodec ? option.def.in : option;
|
|
472
|
+
return resolveCodecOptions(inSchema instanceof z.ZodObject ? inSchema : option);
|
|
473
|
+
});
|
|
474
|
+
return normalizeCodecOptions(schema, {
|
|
475
|
+
decode(ctx) {
|
|
476
|
+
const errors = [];
|
|
477
|
+
for (const options of codecOptions) try {
|
|
478
|
+
return options.decode({
|
|
479
|
+
options,
|
|
480
|
+
xml: ctx.xml
|
|
481
|
+
});
|
|
482
|
+
} catch (e) {
|
|
483
|
+
errors.push(e);
|
|
484
|
+
}
|
|
485
|
+
throw new XMLCodecError(`union: no option matched for decoding (${formatReason(errors)})`);
|
|
486
|
+
},
|
|
487
|
+
encode(ctx) {
|
|
488
|
+
const errors = [];
|
|
489
|
+
for (const options of codecOptions) try {
|
|
490
|
+
return options.encode({
|
|
491
|
+
options,
|
|
492
|
+
data: ctx.data
|
|
493
|
+
});
|
|
494
|
+
} catch (e) {
|
|
495
|
+
errors.push(e);
|
|
496
|
+
}
|
|
497
|
+
throw new XMLCodecError(`union: no option matched for encoding (${formatReason(errors)})`);
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
});
|
|
355
502
|
//#endregion
|
|
356
|
-
export {
|
|
503
|
+
export { XMLCodecError, decode, encode, normalizeCodecOptions, registerDefault, xmlStateSchema };
|
|
357
504
|
|
|
358
505
|
//# sourceMappingURL=codec.js.map
|
package/dist/xml/examples.d.ts
CHANGED
|
@@ -1,41 +1,125 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Recommended base class for all xmlModel classes.
|
|
4
|
+
*
|
|
5
|
+
* Extending `XMLBase` instead of calling `xmlModel()` directly opts every subclass
|
|
6
|
+
* into round-trip preservation at no extra cost:
|
|
7
|
+
* - **Element ordering** — elements are re-emitted in source document order, not schema order.
|
|
8
|
+
* - **Unknown elements** — elements with no matching schema field are passed through verbatim.
|
|
9
|
+
*
|
|
10
|
+
* This matters whenever you read XML produced by a third party, modify a subset of fields,
|
|
11
|
+
* and write it back — plain `xmlModel()` would silently reorder elements and drop extensions.
|
|
12
|
+
*
|
|
13
|
+
* The `_xmlState` field holds the tracking state; it is intentionally excluded from XML output.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* class Device extends XMLBase.extend(
|
|
17
|
+
* { name: z.string() },
|
|
18
|
+
* xml.root({ tagname: "device" }),
|
|
19
|
+
* ) {}
|
|
20
|
+
*/
|
|
21
|
+
export declare const XMLBase: import('./model').XmlModelConstructor<z.ZodObject<{
|
|
22
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
23
|
+
}, z.core.$strip>, {
|
|
24
|
+
_xmlState?: import('./codec').XMLState;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Like {@link XMLBase}, but also records the original `XMLElement` as `._xmlState.source`
|
|
28
|
+
* on each instance.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const device = Device.fromXML(`<device>…</device>`);
|
|
32
|
+
* device._xmlState?.source; // XMLElement
|
|
33
|
+
*/
|
|
34
|
+
export declare const XMLBaseWithSource: import('./model').XmlModelConstructor<z.ZodObject<{
|
|
35
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState & {
|
|
36
|
+
source: import('./xml-js').XMLElement;
|
|
37
|
+
}, unknown>>;
|
|
38
|
+
}, z.core.$strip>, {
|
|
39
|
+
_xmlState?: import('./codec').XMLState & {
|
|
40
|
+
source: import('./xml-js').XMLElement;
|
|
41
|
+
};
|
|
42
|
+
}>;
|
|
43
|
+
declare const Event_base: Omit<import('./model').XmlModelConstructor<z.ZodObject<{
|
|
44
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
45
|
+
}, z.core.$strip>, {
|
|
46
|
+
_xmlState?: import('./codec').XMLState;
|
|
47
|
+
}>, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
48
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
49
|
+
title: z.ZodString;
|
|
50
|
+
publishedAt: z.ZodCodec<z.ZodString, z.ZodDate>;
|
|
51
|
+
}, z.core.$strip>, {
|
|
52
|
+
_xmlState?: import('./codec').XMLState;
|
|
53
|
+
} & {
|
|
54
|
+
title: string;
|
|
55
|
+
_xmlState?: import('./codec').XMLState;
|
|
56
|
+
publishedAt?: Date;
|
|
57
|
+
}>;
|
|
58
|
+
/**
|
|
59
|
+
* An event with a typed `Date` field stored as an ISO 8601 string in XML.
|
|
60
|
+
* Extends `XMLBase` so element order and unknown elements are preserved across round-trips.
|
|
61
|
+
* Demonstrates using `z.codec` to transform a raw XML string into a native JS type.
|
|
62
|
+
*/
|
|
63
|
+
export declare class Event extends Event_base {
|
|
64
|
+
}
|
|
65
|
+
declare const Engine_base: Omit<import('./model').XmlModelConstructor<z.ZodObject<{
|
|
66
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
67
|
+
}, z.core.$strip>, {
|
|
68
|
+
_xmlState?: import('./codec').XMLState;
|
|
69
|
+
}>, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
70
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
3
71
|
type: z.ZodString;
|
|
4
72
|
horsepower: z.ZodNumber;
|
|
5
73
|
}, z.core.$strip>, {
|
|
74
|
+
_xmlState?: import('./codec').XMLState;
|
|
75
|
+
} & {
|
|
6
76
|
type: string;
|
|
7
77
|
horsepower: number;
|
|
78
|
+
_xmlState?: import('./codec').XMLState;
|
|
8
79
|
}>;
|
|
9
80
|
/**
|
|
10
|
-
* A car engine.
|
|
11
|
-
* (
|
|
81
|
+
* A car engine. Extends `XMLBase` so unknown vendor elements inside `<engine>`
|
|
82
|
+
* (e.g. manufacturer extensions) survive a read-modify-write cycle.
|
|
83
|
+
* Demonstrates a basic nested class with one XML attribute (`type`) and one
|
|
84
|
+
* child element (`horsepower`).
|
|
12
85
|
*/
|
|
13
86
|
export declare class Engine extends Engine_base {
|
|
14
87
|
}
|
|
15
|
-
declare const Vehicle_base: import('./model').XmlModelConstructor<z.ZodObject<{
|
|
88
|
+
declare const Vehicle_base: Omit<import('./model').XmlModelConstructor<z.ZodObject<{
|
|
89
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
90
|
+
}, z.core.$strip>, {
|
|
91
|
+
_xmlState?: import('./codec').XMLState;
|
|
92
|
+
}>, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
93
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
16
94
|
vin: z.ZodString;
|
|
17
95
|
make: z.ZodString;
|
|
18
96
|
year: z.ZodNumber;
|
|
19
97
|
}, z.core.$strip>, {
|
|
98
|
+
_xmlState?: import('./codec').XMLState;
|
|
99
|
+
} & {
|
|
20
100
|
vin: string;
|
|
21
101
|
make: string;
|
|
22
102
|
year: number;
|
|
103
|
+
_xmlState?: import('./codec').XMLState;
|
|
23
104
|
}>;
|
|
24
105
|
/**
|
|
25
|
-
* Base vehicle class.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
106
|
+
* Base vehicle class. Extends `XMLBase` so all vehicle subclasses inherit
|
|
107
|
+
* round-trip preservation — element order and unknown extensions are kept
|
|
108
|
+
* intact without any per-subclass ceremony.
|
|
109
|
+
* Demonstrates `xml.attr()` for identifier fields and custom instance methods.
|
|
28
110
|
*/
|
|
29
111
|
export declare class Vehicle extends Vehicle_base {
|
|
30
112
|
/** Returns a human-readable label for this vehicle. */
|
|
31
113
|
label(): string;
|
|
32
114
|
}
|
|
33
115
|
declare const Car_base: Omit<typeof Vehicle, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
116
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
34
117
|
vin: z.ZodString;
|
|
35
118
|
make: z.ZodString;
|
|
36
119
|
year: z.ZodNumber;
|
|
37
120
|
doors: z.ZodNumber;
|
|
38
121
|
engine: z.ZodCodec<z.ZodObject<{
|
|
122
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
39
123
|
type: z.ZodString;
|
|
40
124
|
horsepower: z.ZodNumber;
|
|
41
125
|
}, z.core.$strip>, z.ZodCustom<Engine, Engine>>;
|
|
@@ -44,6 +128,7 @@ declare const Car_base: Omit<typeof Vehicle, keyof import('..').ModelConstructor
|
|
|
44
128
|
make: string;
|
|
45
129
|
year: number;
|
|
46
130
|
doors: number;
|
|
131
|
+
_xmlState?: import('./codec').XMLState;
|
|
47
132
|
engine?: Engine;
|
|
48
133
|
}>;
|
|
49
134
|
/**
|
|
@@ -57,11 +142,13 @@ declare const Car_base: Omit<typeof Vehicle, keyof import('..').ModelConstructor
|
|
|
57
142
|
export declare class Car extends Car_base {
|
|
58
143
|
}
|
|
59
144
|
declare const SportCar_base: Omit<typeof Car, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
145
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
60
146
|
vin: z.ZodString;
|
|
61
147
|
make: z.ZodString;
|
|
62
148
|
year: z.ZodNumber;
|
|
63
149
|
doors: z.ZodNumber;
|
|
64
150
|
engine: z.ZodCodec<z.ZodObject<{
|
|
151
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
65
152
|
type: z.ZodString;
|
|
66
153
|
horsepower: z.ZodNumber;
|
|
67
154
|
}, z.core.$strip>, z.ZodCustom<Engine, Engine>>;
|
|
@@ -72,6 +159,7 @@ declare const SportCar_base: Omit<typeof Car, keyof import('..').ModelConstructo
|
|
|
72
159
|
year: number;
|
|
73
160
|
doors: number;
|
|
74
161
|
topSpeed: number;
|
|
162
|
+
_xmlState?: import('./codec').XMLState;
|
|
75
163
|
engine?: Engine;
|
|
76
164
|
}>;
|
|
77
165
|
/**
|
|
@@ -81,6 +169,7 @@ declare const SportCar_base: Omit<typeof Car, keyof import('..').ModelConstructo
|
|
|
81
169
|
export declare class SportCar extends SportCar_base {
|
|
82
170
|
}
|
|
83
171
|
declare const Motorcycle_base: Omit<typeof Vehicle, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
172
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
84
173
|
vin: z.ZodString;
|
|
85
174
|
make: z.ZodString;
|
|
86
175
|
year: z.ZodNumber;
|
|
@@ -89,6 +178,7 @@ declare const Motorcycle_base: Omit<typeof Vehicle, keyof import('..').ModelCons
|
|
|
89
178
|
vin: string;
|
|
90
179
|
make: string;
|
|
91
180
|
year: number;
|
|
181
|
+
_xmlState?: import('./codec').XMLState;
|
|
92
182
|
sidecar?: boolean;
|
|
93
183
|
}>;
|
|
94
184
|
/**
|
|
@@ -98,28 +188,39 @@ declare const Motorcycle_base: Omit<typeof Vehicle, keyof import('..').ModelCons
|
|
|
98
188
|
*/
|
|
99
189
|
export declare class Motorcycle extends Motorcycle_base {
|
|
100
190
|
}
|
|
101
|
-
declare const Fleet_base: import('./model').XmlModelConstructor<z.ZodObject<{
|
|
191
|
+
declare const Fleet_base: Omit<import('./model').XmlModelConstructor<z.ZodObject<{
|
|
192
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
193
|
+
}, z.core.$strip>, {
|
|
194
|
+
_xmlState?: import('./codec').XMLState;
|
|
195
|
+
}>, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
196
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
102
197
|
name: z.ZodString;
|
|
103
198
|
cars: z.ZodArray<z.ZodCodec<z.ZodObject<{
|
|
199
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
104
200
|
vin: z.ZodString;
|
|
105
201
|
make: z.ZodString;
|
|
106
202
|
year: z.ZodNumber;
|
|
107
203
|
doors: z.ZodNumber;
|
|
108
204
|
engine: z.ZodCodec<z.ZodObject<{
|
|
205
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
109
206
|
type: z.ZodString;
|
|
110
207
|
horsepower: z.ZodNumber;
|
|
111
208
|
}, z.core.$strip>, z.ZodCustom<Engine, Engine>>;
|
|
112
209
|
}, z.core.$strip>, z.ZodCustom<Car, Car>>>;
|
|
113
210
|
motorcycles: z.ZodArray<z.ZodCodec<z.ZodObject<{
|
|
211
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
114
212
|
vin: z.ZodString;
|
|
115
213
|
make: z.ZodString;
|
|
116
214
|
year: z.ZodNumber;
|
|
117
215
|
sidecar: z.ZodOptional<z.ZodBoolean>;
|
|
118
216
|
}, z.core.$strip>, z.ZodCustom<Motorcycle, Motorcycle>>>;
|
|
119
217
|
}, z.core.$strip>, {
|
|
218
|
+
_xmlState?: import('./codec').XMLState;
|
|
219
|
+
} & {
|
|
120
220
|
name: string;
|
|
121
221
|
cars: Car[];
|
|
122
222
|
motorcycles: Motorcycle[];
|
|
223
|
+
_xmlState?: import('./codec').XMLState;
|
|
123
224
|
}>;
|
|
124
225
|
/**
|
|
125
226
|
* Fleet demonstrates **inline arrays** of multiple vehicle types.
|
|
@@ -130,12 +231,20 @@ export declare class Fleet extends Fleet_base {
|
|
|
130
231
|
/** Total number of vehicles across all types in this fleet. */
|
|
131
232
|
totalVehicles(): number;
|
|
132
233
|
}
|
|
133
|
-
declare const Showroom_base: import('./model').XmlModelConstructor<z.ZodObject<{
|
|
234
|
+
declare const Showroom_base: Omit<import('./model').XmlModelConstructor<z.ZodObject<{
|
|
235
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
236
|
+
}, z.core.$strip>, {
|
|
237
|
+
_xmlState?: import('./codec').XMLState;
|
|
238
|
+
}>, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
239
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
134
240
|
name: z.ZodString;
|
|
135
241
|
models: z.ZodArray<z.ZodString>;
|
|
136
242
|
}, z.core.$strip>, {
|
|
243
|
+
_xmlState?: import('./codec').XMLState;
|
|
244
|
+
} & {
|
|
137
245
|
name: string;
|
|
138
246
|
models: string[];
|
|
247
|
+
_xmlState?: import('./codec').XMLState;
|
|
139
248
|
}>;
|
|
140
249
|
/**
|
|
141
250
|
* A showroom holds an inventory of car model names.
|
|
@@ -157,28 +266,113 @@ declare const Showroom_base: import('./model').XmlModelConstructor<z.ZodObject<{
|
|
|
157
266
|
*/
|
|
158
267
|
export declare class Showroom extends Showroom_base {
|
|
159
268
|
}
|
|
160
|
-
declare const
|
|
269
|
+
declare const PetrolEngine_base: Omit<import('./model').XmlModelConstructor<z.ZodObject<{
|
|
270
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
271
|
+
}, z.core.$strip>, {
|
|
272
|
+
_xmlState?: import('./codec').XMLState;
|
|
273
|
+
}>, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
274
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
275
|
+
type: z.ZodLiteral<"petrol">;
|
|
276
|
+
horsepower: z.ZodNumber;
|
|
277
|
+
}, z.core.$strip>, {
|
|
278
|
+
_xmlState?: import('./codec').XMLState;
|
|
279
|
+
} & {
|
|
280
|
+
type: "petrol";
|
|
281
|
+
horsepower: number;
|
|
282
|
+
_xmlState?: import('./codec').XMLState;
|
|
283
|
+
}>;
|
|
284
|
+
/**
|
|
285
|
+
* A petrol engine, discriminated by `type="petrol"`.
|
|
286
|
+
*/
|
|
287
|
+
export declare class PetrolEngine extends PetrolEngine_base {
|
|
288
|
+
}
|
|
289
|
+
declare const ElectricEngine_base: Omit<import('./model').XmlModelConstructor<z.ZodObject<{
|
|
290
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
291
|
+
}, z.core.$strip>, {
|
|
292
|
+
_xmlState?: import('./codec').XMLState;
|
|
293
|
+
}>, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
294
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
295
|
+
type: z.ZodLiteral<"electric">;
|
|
296
|
+
range: z.ZodNumber;
|
|
297
|
+
}, z.core.$strip>, {
|
|
298
|
+
_xmlState?: import('./codec').XMLState;
|
|
299
|
+
} & {
|
|
300
|
+
type: "electric";
|
|
301
|
+
range: number;
|
|
302
|
+
_xmlState?: import('./codec').XMLState;
|
|
303
|
+
}>;
|
|
304
|
+
/**
|
|
305
|
+
* An electric engine, discriminated by `type="electric"`.
|
|
306
|
+
*/
|
|
307
|
+
export declare class ElectricEngine extends ElectricEngine_base {
|
|
308
|
+
}
|
|
309
|
+
declare const UnknownEngine_base: Omit<import('./model').XmlModelConstructor<z.ZodObject<{
|
|
310
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
311
|
+
}, z.core.$strip>, {
|
|
312
|
+
_xmlState?: import('./codec').XMLState;
|
|
313
|
+
}>, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
314
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
315
|
+
type: z.ZodString;
|
|
316
|
+
}, z.core.$strip>, {
|
|
317
|
+
_xmlState?: import('./codec').XMLState;
|
|
318
|
+
} & {
|
|
319
|
+
type: string;
|
|
320
|
+
_xmlState?: import('./codec').XMLState;
|
|
321
|
+
}>;
|
|
322
|
+
/**
|
|
323
|
+
* Fallback for unrecognised engine types. Unknown child elements are preserved
|
|
324
|
+
* through round-trips via XMLBase's state tracking.
|
|
325
|
+
*/
|
|
326
|
+
export declare class UnknownEngine extends UnknownEngine_base {
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* A union that matches known engine types by discriminator and falls back to
|
|
330
|
+
* `UnknownEngine` for any unrecognised `type` value.
|
|
331
|
+
*/
|
|
332
|
+
export declare const AnyEngine: z.ZodUnion<readonly [z.ZodDiscriminatedUnion<[z.ZodCodec<z.ZodObject<{
|
|
333
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
334
|
+
type: z.ZodLiteral<"petrol">;
|
|
335
|
+
horsepower: z.ZodNumber;
|
|
336
|
+
}, z.core.$strip>, z.ZodCustom<PetrolEngine, PetrolEngine>>, z.ZodCodec<z.ZodObject<{
|
|
337
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
338
|
+
type: z.ZodLiteral<"electric">;
|
|
339
|
+
range: z.ZodNumber;
|
|
340
|
+
}, z.core.$strip>, z.ZodCustom<ElectricEngine, ElectricEngine>>], "type">, z.ZodCodec<z.ZodObject<{
|
|
341
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
342
|
+
type: z.ZodString;
|
|
343
|
+
}, z.core.$strip>, z.ZodCustom<UnknownEngine, UnknownEngine>>]>;
|
|
344
|
+
declare const CarStandalone_base: Omit<import('./model').XmlModelConstructor<z.ZodObject<{
|
|
345
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
346
|
+
}, z.core.$strip>, {
|
|
347
|
+
_xmlState?: import('./codec').XMLState;
|
|
348
|
+
}>, keyof import('..').ModelConstructor<S, Inst>> & import('..').ModelConstructor<z.ZodObject<{
|
|
349
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
161
350
|
vin: z.ZodString;
|
|
162
351
|
make: z.ZodString;
|
|
163
352
|
year: z.ZodNumber;
|
|
164
353
|
doors: z.ZodNumber;
|
|
165
354
|
engine: z.ZodCodec<z.ZodObject<{
|
|
355
|
+
_xmlState: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
166
356
|
type: z.ZodString;
|
|
167
357
|
horsepower: z.ZodNumber;
|
|
168
358
|
}, z.core.$strip>, z.ZodCustom<Engine, Engine>>;
|
|
169
359
|
}, z.core.$strip>, {
|
|
360
|
+
_xmlState?: import('./codec').XMLState;
|
|
361
|
+
} & {
|
|
170
362
|
vin: string;
|
|
171
363
|
make: string;
|
|
172
364
|
year: number;
|
|
173
365
|
doors: number;
|
|
366
|
+
_xmlState?: import('./codec').XMLState;
|
|
174
367
|
engine?: Engine;
|
|
175
368
|
}>;
|
|
176
369
|
/**
|
|
177
|
-
* Demonstrates the alternative to
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* methods are unavailable.
|
|
370
|
+
* Demonstrates the alternative to `Vehicle.extend()`: listing all fields
|
|
371
|
+
* manually inside `XMLBase.extend()`. This produces a class with the same
|
|
372
|
+
* XML shape as `Car` but **no prototype link to `Vehicle`** — instances are
|
|
373
|
+
* **not** `instanceof Vehicle` and Vehicle's methods are unavailable.
|
|
181
374
|
*
|
|
375
|
+
* Round-trip preservation still applies because the class extends `XMLBase`.
|
|
182
376
|
* Use this pattern when you want a standalone class that reuses a schema shape
|
|
183
377
|
* but does not need to be part of the parent class hierarchy.
|
|
184
378
|
*/
|
package/dist/xml/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from './xml-js';
|
|
2
2
|
export { xml } from './schema-meta';
|
|
3
3
|
export type { UserCodecOptions, XMLState } from './codec';
|
|
4
|
-
export { registerDefault, normalizeCodecOptions } from './codec';
|
|
5
|
-
export { xmlModel,
|
|
4
|
+
export { registerDefault, normalizeCodecOptions, XMLCodecError, xmlStateSchema } from './codec';
|
|
5
|
+
export { xmlModel, type XmlModelConstructor } from './model';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/xml/model.d.ts
CHANGED
|
@@ -14,37 +14,5 @@ export type XmlModelConstructor<S extends z.ZodObject<any> = z.ZodObject<any>, I
|
|
|
14
14
|
/** Converts an instance to an XML string. */
|
|
15
15
|
toXMLString(instance: z.infer<S>, options?: StringifyOptions): string;
|
|
16
16
|
};
|
|
17
|
-
/**
|
|
18
|
-
* Base class for xmlModel classes. Preserves element ordering and unknown elements
|
|
19
|
-
* through `schema.parse()` for nested model instances.
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* class Device extends XMLBase.extend(
|
|
23
|
-
* { name: z.string() },
|
|
24
|
-
* xml.root({ tagname: "device" }),
|
|
25
|
-
* ) {}
|
|
26
|
-
*/
|
|
27
|
-
export declare const XMLBase: XmlModelConstructor<z.ZodObject<{
|
|
28
|
-
__xml_state: z.ZodOptional<z.ZodCustom<import('./codec').XMLState, unknown>>;
|
|
29
|
-
}, z.core.$strip>, {
|
|
30
|
-
__xml_state?: import('./codec').XMLState;
|
|
31
|
-
}>;
|
|
32
|
-
/**
|
|
33
|
-
* Like {@link XMLBase}, but also records the original `XMLElement` as `.source`
|
|
34
|
-
* on each instance's XML state.
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* const device = Device.fromXML(`<device>…</device>`);
|
|
38
|
-
* device[XML_STATE_KEY]?.source; // XMLElement
|
|
39
|
-
*/
|
|
40
|
-
export declare const XMLBaseWithSource: XmlModelConstructor<z.ZodObject<{
|
|
41
|
-
__xml_state: z.ZodOptional<z.ZodCustom<import('./codec').XMLState & {
|
|
42
|
-
source: XMLElement;
|
|
43
|
-
}, unknown>>;
|
|
44
|
-
}, z.core.$strip>, {
|
|
45
|
-
__xml_state?: import('./codec').XMLState & {
|
|
46
|
-
source: XMLElement;
|
|
47
|
-
};
|
|
48
|
-
}>;
|
|
49
17
|
export declare function xmlModel<S extends z.ZodObject<any>>(schema: S, options?: UserCodecOptions<S>): XmlModelConstructor<S>;
|
|
50
18
|
//# sourceMappingURL=model.d.ts.map
|
package/dist/xml/model.js
CHANGED
|
@@ -1,45 +1,22 @@
|
|
|
1
1
|
import { model } from "../model.js";
|
|
2
2
|
import XML from "./xml-js.js";
|
|
3
3
|
import { root } from "./schema-meta.js";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
4
|
+
import { decode, encode } from "./codec.js";
|
|
5
|
+
import "zod";
|
|
6
6
|
//#region src/xml/model.ts
|
|
7
|
-
/**
|
|
8
|
-
* Base class for xmlModel classes. Preserves element ordering and unknown elements
|
|
9
|
-
* through `schema.parse()` for nested model instances.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* class Device extends XMLBase.extend(
|
|
13
|
-
* { name: z.string() },
|
|
14
|
-
* xml.root({ tagname: "device" }),
|
|
15
|
-
* ) {}
|
|
16
|
-
*/
|
|
17
|
-
var XMLBase = xmlModel(z.object({ [XML_STATE_KEY]: xmlStateSchema() }));
|
|
18
|
-
/**
|
|
19
|
-
* Like {@link XMLBase}, but also records the original `XMLElement` as `.source`
|
|
20
|
-
* on each instance's XML state.
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* const device = Device.fromXML(`<device>…</device>`);
|
|
24
|
-
* device[XML_STATE_KEY]?.source; // XMLElement
|
|
25
|
-
*/
|
|
26
|
-
var XMLBaseWithSource = xmlModel(z.object({ [XML_STATE_KEY]: xmlStateSchema({ source: true }) }));
|
|
27
7
|
function xmlModel(schema, options) {
|
|
28
8
|
const _schema = options ? root(schema, options) : schema;
|
|
29
9
|
return class extends model(_schema) {
|
|
30
10
|
static fromXML(input) {
|
|
31
11
|
if (typeof input === "string") input = XML.parse(input);
|
|
32
12
|
if (XML.isRoot(input)) input = XML.elementFromRoot(input);
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const xmlState = inputData[XML_STATE_KEY];
|
|
36
|
-
const parsed = schema.parse(inputData);
|
|
37
|
-
parsed[XML_STATE_KEY] = xmlState;
|
|
38
|
-
return this.fromData(parsed);
|
|
13
|
+
const rawData = decode(this.dataSchema, input);
|
|
14
|
+
return this.fromData(this.dataSchema.parse(rawData));
|
|
39
15
|
}
|
|
40
16
|
static toXML(instance) {
|
|
41
17
|
const data = this.toData(instance);
|
|
42
|
-
|
|
18
|
+
const rawData = this.dataSchema.encode(data);
|
|
19
|
+
return { elements: [encode(this.dataSchema, rawData)] };
|
|
43
20
|
}
|
|
44
21
|
static toXMLString(instance, options = {}) {
|
|
45
22
|
const xml = this.toXML(instance);
|
|
@@ -48,6 +25,6 @@ function xmlModel(schema, options) {
|
|
|
48
25
|
};
|
|
49
26
|
}
|
|
50
27
|
//#endregion
|
|
51
|
-
export {
|
|
28
|
+
export { xmlModel };
|
|
52
29
|
|
|
53
30
|
//# sourceMappingURL=model.js.map
|
|
@@ -41,6 +41,21 @@ export declare function root(options: UserRootOptions): z.GlobalMeta;
|
|
|
41
41
|
export declare function prop<PS extends z.ZodType>(schema: PS, options: UserPropOptions): PS;
|
|
42
42
|
export declare function prop(options: UserPropOptions): z.GlobalMeta;
|
|
43
43
|
type AttributePropOptions = {
|
|
44
|
+
/**
|
|
45
|
+
* XML attribute name. Defaults to the field key in kebab-case (the same
|
|
46
|
+
* conversion applied to child element tag names). Omit `name` when the
|
|
47
|
+
* attribute name is already the kebab-cased field key.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* // field key "vin" → attribute "vin" — no name needed
|
|
51
|
+
* vin: xml.attr(z.string())
|
|
52
|
+
*
|
|
53
|
+
* // field key "vehicleId" → attribute "vehicle-id" — no name needed (kebab-case default)
|
|
54
|
+
* vehicleId: xml.attr(z.string())
|
|
55
|
+
*
|
|
56
|
+
* // field key "vehicleId" → attribute "vehicle" — name required to override
|
|
57
|
+
* vehicleId: xml.attr(z.string(), { name: "vehicle" })
|
|
58
|
+
*/
|
|
44
59
|
name?: string;
|
|
45
60
|
};
|
|
46
61
|
export declare function attr<PS extends z.ZodType>(schema: PS, options?: AttributePropOptions): PS;
|
package/dist/xml/schema-meta.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import XML from "./xml-js.js";
|
|
2
2
|
import { getParentSchema, isZodType } from "../util/zod.js";
|
|
3
|
+
import { kebabCase } from "../util/kebab-case.js";
|
|
3
4
|
import "zod";
|
|
4
5
|
//#region src/xml/schema-meta.ts
|
|
5
6
|
var metaKey = "@@xml-model";
|
|
@@ -70,14 +71,14 @@ function attr(optionsOrSchema, options) {
|
|
|
70
71
|
const opts = isZodType(optionsOrSchema) ? options ?? {} : optionsOrSchema ?? {};
|
|
71
72
|
const partial = {
|
|
72
73
|
decodeAsProperty(ctx) {
|
|
73
|
-
const {
|
|
74
|
-
const attrName = opts.name ?? name;
|
|
74
|
+
const { options: propOptions } = ctx.property;
|
|
75
|
+
const attrName = opts.name ?? kebabCase(ctx.property.name);
|
|
75
76
|
const attrValue = ctx.xml?.attributes?.[attrName];
|
|
76
|
-
ctx.result[name] = propOptions.schema.parse(attrValue);
|
|
77
|
+
ctx.result[ctx.property.name] = propOptions.schema.parse(attrValue);
|
|
77
78
|
},
|
|
78
79
|
encodeAsProperty(ctx) {
|
|
79
|
-
const { value
|
|
80
|
-
const attrName = opts.name ?? name;
|
|
80
|
+
const { value } = ctx.property;
|
|
81
|
+
const attrName = opts.name ?? kebabCase(ctx.property.name);
|
|
81
82
|
ctx.result.attributes[attrName] = value.toString();
|
|
82
83
|
}
|
|
83
84
|
};
|