xml-model 2.0.0-beta.7 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2 -2
- package/dist/model.d.ts +2 -2
- package/dist/model.js +5 -3
- package/dist/xml/codec.d.ts +65 -10
- package/dist/xml/codec.js +138 -36
- package/dist/xml/index.d.ts +2 -1
- package/dist/xml/model.js +5 -1
- package/dist/xml/schema-meta.d.ts +0 -1
- package/dist/xml/schema-meta.js +10 -28
- package/dist/xml/xml-js.js +1 -1
- package/package.json +2 -2
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 { XMLCodecError, normalizeCodecOptions, registerDefault, xmlStateSchema } from "./xml/codec.js";
|
|
4
|
+
import { XMLCodecError, normalizeCodecOptions, parseXML, registerDefault, stringifyXML, toXML, xmlCodec, xmlStateSchema } from "./xml/codec.js";
|
|
5
5
|
import { xmlModel } from "./xml/model.js";
|
|
6
6
|
import "./xml/index.js";
|
|
7
|
-
export { DATA, XML, XMLCodecError, ZXMLCommentNode, ZXMLElementNode, ZXMLNode, ZXMLRoot, ZXMLTextNode, isModel, model, normalizeCodecOptions, registerDefault, xml, xmlModel, xmlStateSchema };
|
|
7
|
+
export { DATA, XML, XMLCodecError, ZXMLCommentNode, ZXMLElementNode, ZXMLNode, ZXMLRoot, ZXMLTextNode, isModel, model, normalizeCodecOptions, parseXML, registerDefault, stringifyXML, toXML, xml, xmlCodec, xmlModel, xmlStateSchema };
|
package/dist/model.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z, GlobalMeta } from 'zod';
|
|
2
|
+
/** Stores the raw data object on model instances. */
|
|
3
|
+
export declare const DATA: unique symbol;
|
|
2
4
|
/**
|
|
3
5
|
* Constructor type for model classes.
|
|
4
6
|
*
|
|
@@ -36,8 +38,6 @@ export type ModelConstructor<S extends z.ZodObject<any> = z.ZodObject<any>, Inst
|
|
|
36
38
|
*/
|
|
37
39
|
extend<Self extends ModelConstructor<S, Inst>, U extends z.core.$ZodLooseShape>(this: Self, extension: U, meta?: GlobalMeta): Omit<Self, keyof ModelConstructor<S, Inst>> & ModelConstructor<z.ZodObject<z.util.Extend<S["shape"], U>>, InstanceType<Self> & z.infer<z.ZodObject<z.util.Extend<S["shape"], U>>>>;
|
|
38
40
|
};
|
|
39
|
-
/** Stores the raw data object on model instances. */
|
|
40
|
-
export declare const DATA: unique symbol;
|
|
41
41
|
/** Returns true if `cls` is a class produced by `model()` (or a subclass of one). */
|
|
42
42
|
export declare function isModel(cls: unknown): cls is ModelConstructor;
|
|
43
43
|
/**
|
package/dist/model.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
//#region src/model.ts
|
|
3
|
-
var
|
|
3
|
+
var SCHEMA = Symbol("model:schema");
|
|
4
4
|
/** Marker placed on every class returned by `model()`. Used by `isModel()`. */
|
|
5
5
|
var MODEL_MARKER = Symbol("model:marker");
|
|
6
6
|
/** Stores the raw data object on model instances. */
|
|
@@ -34,8 +34,10 @@ function model(schema) {
|
|
|
34
34
|
class Base {
|
|
35
35
|
static dataSchema = schema;
|
|
36
36
|
static [MODEL_MARKER] = true;
|
|
37
|
+
static [SCHEMA];
|
|
38
|
+
[DATA];
|
|
37
39
|
static schema() {
|
|
38
|
-
if (!Object.prototype.hasOwnProperty.call(this,
|
|
40
|
+
if (!Object.prototype.hasOwnProperty.call(this, SCHEMA)) this[SCHEMA] = z.codec(this.dataSchema, z.instanceof(this), {
|
|
39
41
|
decode: (data) => {
|
|
40
42
|
return this.fromData(data);
|
|
41
43
|
},
|
|
@@ -43,7 +45,7 @@ function model(schema) {
|
|
|
43
45
|
return instance[DATA];
|
|
44
46
|
}
|
|
45
47
|
});
|
|
46
|
-
return this[
|
|
48
|
+
return this[SCHEMA];
|
|
47
49
|
}
|
|
48
50
|
static extend(extension, meta) {
|
|
49
51
|
let extended = this.dataSchema.extend(extension);
|
package/dist/xml/codec.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { XMLElement } from './xml-js';
|
|
2
|
+
import { XMLElement, XMLRoot, StringifyOptions } from './xml-js';
|
|
3
3
|
export declare class XMLCodecError extends Error {
|
|
4
4
|
readonly path: readonly (string | number)[];
|
|
5
5
|
readonly rawMessage: string;
|
|
@@ -12,22 +12,24 @@ export declare function assertSingleRoot(xml: XMLElement[]): asserts xml is [XML
|
|
|
12
12
|
type PropKey<S extends z.ZodObject> = keyof z.input<S> & string;
|
|
13
13
|
export interface CodecOptions<S extends z.ZodType> {
|
|
14
14
|
schema: S;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
/** Resolved options of the wrapped inner schema, if any (e.g. the inner type of ZodOptional). */
|
|
16
|
+
parent: CodecOptions<z.ZodType> | undefined;
|
|
17
|
+
tagname(this: void, ctx: RootEncodingContext<S>): string;
|
|
18
|
+
decode(this: void, ctx: RootDecodingContext<S>): z.input<S>;
|
|
19
|
+
encode(this: void, ctx: RootEncodingContext<S>): XMLElement;
|
|
20
|
+
propertyTagname: (this: void, ctx: {
|
|
19
21
|
name: string;
|
|
20
22
|
options: CodecOptions<z.ZodType>;
|
|
21
23
|
}) => string;
|
|
22
24
|
/** if true, XML representation is not contained in a single XML tag */
|
|
23
25
|
inlineProperty: boolean;
|
|
24
|
-
propertyMatch: (el: XMLElement, ctx: {
|
|
26
|
+
propertyMatch: (this: void, el: XMLElement, ctx: {
|
|
25
27
|
name: string;
|
|
26
28
|
tagname: string;
|
|
27
29
|
options: CodecOptions<z.ZodType>;
|
|
28
30
|
}) => boolean;
|
|
29
|
-
decodeAsProperty(ctx: PropertyDecodingContext): void;
|
|
30
|
-
encodeAsProperty(ctx: PropertyEncodingContext): void;
|
|
31
|
+
decodeAsProperty(this: void, ctx: PropertyDecodingContext): void;
|
|
32
|
+
encodeAsProperty(this: void, ctx: PropertyEncodingContext): void;
|
|
31
33
|
}
|
|
32
34
|
/**
|
|
33
35
|
* Stored in schema meta under the single `@@xml-model` key.
|
|
@@ -72,7 +74,7 @@ export interface PropertyEncodingContext<S extends z.ZodObject = z.ZodObject, K
|
|
|
72
74
|
};
|
|
73
75
|
result: XMLElement;
|
|
74
76
|
}
|
|
75
|
-
export declare function normalizeCodecOptions<S extends z.ZodType>(schema: S, options?: UserCodecOptions<S>): CodecOptions<S>;
|
|
77
|
+
export declare function normalizeCodecOptions<S extends z.ZodType>(schema: S, options?: UserCodecOptions<S>, parent?: CodecOptions<z.ZodType> | undefined): CodecOptions<S>;
|
|
76
78
|
type OrderEntry = string | XMLElement;
|
|
77
79
|
export interface XMLState {
|
|
78
80
|
/** Preserves element ordering and unknown elements across a decode → encode round-trip. */
|
|
@@ -108,10 +110,63 @@ export declare function xmlStateSchema(options: {
|
|
|
108
110
|
}): z.ZodOptional<z.ZodCustom<XMLState & {
|
|
109
111
|
source: XMLElement;
|
|
110
112
|
}>>;
|
|
111
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Converts an `XMLElement` into the **input type** of `schema` (`z.input<S>`).
|
|
115
|
+
*
|
|
116
|
+
* This is a pure XML-to-data adapter: it does not run Zod's parse pipeline, so
|
|
117
|
+
* `z.codec` transforms and default values are **not** applied. The result is the
|
|
118
|
+
* raw decoded value suitable for passing to `schema.parse()`.
|
|
119
|
+
*/
|
|
120
|
+
export declare function decode<S extends z.ZodType>(schema: S, xml: XMLElement | null): z.input<S>;
|
|
121
|
+
/**
|
|
122
|
+
* Converts the **input type** of `schema` (`z.input<S>`) into an `XMLElement`.
|
|
123
|
+
*
|
|
124
|
+
* This is a pure data-to-XML adapter: it expects values at `z.input<S>` level,
|
|
125
|
+
* meaning `z.codec` transforms must already have been reversed (via `schema.encode()`)
|
|
126
|
+
* before calling this function.
|
|
127
|
+
*/
|
|
112
128
|
export declare function encode<S extends z.ZodType>(schema: S, data: z.input<S>): XMLElement;
|
|
129
|
+
/**
|
|
130
|
+
* Parses an XML string, `XMLRoot`, or `XMLElement` into the **output type** of
|
|
131
|
+
* `schema` (`z.output<S>`), running the full pipeline:
|
|
132
|
+
* XML → `decode` → `schema.parse()`.
|
|
133
|
+
*
|
|
134
|
+
* `z.codec` transforms (e.g. string → Date) and default values are applied.
|
|
135
|
+
* Use the lower-level {@link decode} if you need the raw input-type value without
|
|
136
|
+
* running the Zod parse pipeline.
|
|
137
|
+
*/
|
|
138
|
+
export declare function parseXML<S extends z.ZodType>(schema: S, input: string | XMLRoot | XMLElement): z.output<S>;
|
|
139
|
+
/**
|
|
140
|
+
* Converts a value at the **output type** of `schema` (`z.output<S>`) into an
|
|
141
|
+
* `XMLElement`, running the full pipeline: `schema.encode()` → `encode`.
|
|
142
|
+
*
|
|
143
|
+
* `z.codec` transforms are reversed before the XML adapter runs.
|
|
144
|
+
* Use the lower-level {@link encode} if you already have an input-type value and
|
|
145
|
+
* do not need to run the Zod encode pipeline.
|
|
146
|
+
*
|
|
147
|
+
* Does not accept nullable values — check for `null`/`undefined` before calling.
|
|
148
|
+
*/
|
|
149
|
+
export declare function toXML<S extends z.ZodType>(schema: S, data: z.output<S>): XMLElement;
|
|
150
|
+
/**
|
|
151
|
+
* Converts a value at the **output type** of `schema` (`z.output<S>`) into an
|
|
152
|
+
* XML string, running the full pipeline: `schema.encode()` → `encode` → `XML.stringify`.
|
|
153
|
+
*
|
|
154
|
+
* Equivalent to `XML.stringify({ elements: [toXML(schema, data)] }, options)`.
|
|
155
|
+
*/
|
|
156
|
+
export declare function stringifyXML<S extends z.ZodType>(schema: S, data: z.output<S>, options?: StringifyOptions): string;
|
|
113
157
|
type DefaultResolver<S extends z.ZodType = z.ZodType> = (schema: S) => CodecOptions<S> | void;
|
|
114
158
|
export declare function registerDefault(resolve: DefaultResolver): void;
|
|
159
|
+
/**
|
|
160
|
+
* Creates a `z.codec` that converts between an XML string and the **input type**
|
|
161
|
+
* of `schema` (`z.input<S>`).
|
|
162
|
+
*
|
|
163
|
+
* The codec sits at the XML ↔ `z.input<S>` boundary only — it does not run Zod's
|
|
164
|
+
* parse pipeline. `z.codec` transforms (e.g. string → Date) and class instantiation
|
|
165
|
+
* are left to `schema.parse()` / `schema.encode()`, which you call separately if needed.
|
|
166
|
+
*
|
|
167
|
+
* Typical use: `xmlCodec(MyClass.dataSchema)` for standalone encode/decode without
|
|
168
|
+
* going through the full `fromXML` / `toXMLString` class API.
|
|
169
|
+
*/
|
|
115
170
|
export declare function xmlCodec<S extends z.ZodType>(schema: S): z.ZodCodec<z.ZodString, S>;
|
|
116
171
|
export {};
|
|
117
172
|
//# sourceMappingURL=codec.d.ts.map
|
package/dist/xml/codec.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import XML from "./xml-js.js";
|
|
2
|
-
import { isZodType } from "../util/zod.js";
|
|
2
|
+
import { getParentSchema, isZodType } from "../util/zod.js";
|
|
3
3
|
import { kebabCase } from "../util/kebab-case.js";
|
|
4
|
-
import {
|
|
4
|
+
import { getOwnUserOptions, prop, root } from "./schema-meta.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
//#region src/xml/codec.ts
|
|
7
7
|
var XMLCodecError = class extends Error {
|
|
@@ -22,35 +22,37 @@ function rethrow(e, segment) {
|
|
|
22
22
|
function assertSingleElement(xml) {
|
|
23
23
|
if (xml.length !== 1) throw new Error(`Expected single XML element, got ${xml.length}`);
|
|
24
24
|
}
|
|
25
|
-
function normalizeCodecOptions(schema, options = {}) {
|
|
25
|
+
function normalizeCodecOptions(schema, options = {}, parent = void 0) {
|
|
26
26
|
let _defaultOptions;
|
|
27
27
|
const defaultOptions = () => {
|
|
28
28
|
if (!_defaultOptions) {
|
|
29
|
-
|
|
30
|
-
if (!
|
|
29
|
+
const resolved = resolveDefault(schema);
|
|
30
|
+
if (!resolved) throw new Error(`Failed to resolve default codec options for schema of type ${schema.type}`);
|
|
31
|
+
_defaultOptions = resolved;
|
|
31
32
|
}
|
|
32
33
|
return _defaultOptions;
|
|
33
34
|
};
|
|
34
35
|
const userTagname = options.tagname;
|
|
35
|
-
const tagname = typeof userTagname === "string" ? () => userTagname : typeof userTagname === "function" ? userTagname : () => {
|
|
36
|
+
const tagname = typeof userTagname === "string" ? () => userTagname : typeof userTagname === "function" ? userTagname : parent ? (ctx) => parent.tagname(ctx) : () => {
|
|
36
37
|
throw new Error("tagname is not defined");
|
|
37
38
|
};
|
|
38
39
|
const userPropTagname = options.propertyTagname;
|
|
39
|
-
const propertyTagname = typeof userPropTagname === "string" ? () => userPropTagname : typeof userPropTagname === "function" ? userPropTagname : (ctx) => kebabCase(ctx.name);
|
|
40
|
-
const inlineProperty = options.inlineProperty ?? false;
|
|
40
|
+
const propertyTagname = typeof userPropTagname === "string" ? () => userPropTagname : typeof userPropTagname === "function" ? userPropTagname : parent ? (ctx) => parent.propertyTagname(ctx) : (ctx) => kebabCase(ctx.name);
|
|
41
|
+
const inlineProperty = options.inlineProperty ?? parent?.inlineProperty ?? false;
|
|
41
42
|
const userMatch = options.propertyMatch;
|
|
42
|
-
const propertyMatch = userMatch instanceof RegExp ? (el) => userMatch.test(el.name) : typeof userMatch === "function" ? userMatch : (el, ctx) => el.name === ctx.tagname;
|
|
43
|
+
const propertyMatch = userMatch instanceof RegExp ? (el) => userMatch.test(el.name) : typeof userMatch === "function" ? userMatch : parent ? (el, ctx) => parent.propertyMatch(el, ctx) : (el, ctx) => el.name === ctx.tagname;
|
|
43
44
|
const result = {
|
|
44
45
|
schema,
|
|
46
|
+
parent,
|
|
45
47
|
tagname,
|
|
46
|
-
decode: options.decode ? (ctx) => options.decode(ctx, () => defaultOptions().decode(ctx)) :
|
|
47
|
-
encode: options.encode ? (ctx) => options.encode(ctx, () => defaultOptions().encode(ctx)) :
|
|
48
|
+
decode: options.decode ? (ctx) => options.decode(ctx, () => defaultOptions().decode(ctx)) : defaultOptions().decode,
|
|
49
|
+
encode: options.encode ? (ctx) => options.encode(ctx, () => defaultOptions().encode(ctx)) : defaultOptions().encode,
|
|
48
50
|
propertyTagname,
|
|
49
51
|
inlineProperty,
|
|
50
52
|
propertyMatch,
|
|
51
|
-
decodeAsProperty: options.decodeAsProperty ?? function(ctx) {
|
|
52
|
-
const res =
|
|
53
|
-
options:
|
|
53
|
+
decodeAsProperty: options.decodeAsProperty ?? parent?.decodeAsProperty ?? function(ctx) {
|
|
54
|
+
const res = ctx.property.options.decode({
|
|
55
|
+
options: ctx.property.options,
|
|
54
56
|
xml: ctx.property.xml
|
|
55
57
|
});
|
|
56
58
|
ctx.result[ctx.property.name] = res;
|
|
@@ -66,11 +68,13 @@ function normalizeCodecOptions(schema, options = {}) {
|
|
|
66
68
|
data: property.value
|
|
67
69
|
});
|
|
68
70
|
if (XML.isEmpty(res)) return;
|
|
69
|
-
if (property.options.inlineProperty)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
if (property.options.inlineProperty) {
|
|
72
|
+
const elements = res.elements?.map((el) => el.type === "element" ? {
|
|
73
|
+
...el,
|
|
74
|
+
name: property.tagname
|
|
75
|
+
} : el);
|
|
76
|
+
if (elements) (ctx.result.elements ??= []).push(...elements);
|
|
77
|
+
} else (ctx.result.elements ??= []).push(res);
|
|
74
78
|
}
|
|
75
79
|
};
|
|
76
80
|
return result;
|
|
@@ -79,14 +83,21 @@ var cache = /* @__PURE__ */ new Map();
|
|
|
79
83
|
function resolveCodecOptions(schema) {
|
|
80
84
|
const cached = cache.get(schema);
|
|
81
85
|
if (cached) return cached;
|
|
82
|
-
const
|
|
83
|
-
cache.set(schema,
|
|
84
|
-
|
|
86
|
+
const placeholder = {};
|
|
87
|
+
cache.set(schema, placeholder);
|
|
88
|
+
const parentSchema = getParentSchema(schema);
|
|
89
|
+
const parent = parentSchema ? resolveCodecOptions(parentSchema) : void 0;
|
|
90
|
+
const options = normalizeCodecOptions(schema, getOwnUserOptions(schema), parent);
|
|
91
|
+
Object.assign(placeholder, options);
|
|
92
|
+
return placeholder;
|
|
85
93
|
}
|
|
86
94
|
/** Tracks all schemas created by `xmlStateSchema()` for fast detection at setup time. */
|
|
87
95
|
var xmlStateSchemas = /* @__PURE__ */ new WeakSet();
|
|
88
96
|
function xmlStateSchema(options) {
|
|
89
|
-
const result = prop(z.custom()
|
|
97
|
+
const result = prop(root(z.custom(), {
|
|
98
|
+
decode: () => ({}),
|
|
99
|
+
encode: () => ({})
|
|
100
|
+
}).optional(), {
|
|
90
101
|
decode: options?.source ? (ctx, _next) => {
|
|
91
102
|
(ctx.result[ctx.property.name] ??= {}).source = ctx.xml;
|
|
92
103
|
} : () => {},
|
|
@@ -95,6 +106,15 @@ function xmlStateSchema(options) {
|
|
|
95
106
|
xmlStateSchemas.add(result);
|
|
96
107
|
return result;
|
|
97
108
|
}
|
|
109
|
+
/** Returns true if any schema in the wrapper chain has a user-defined tagname. */
|
|
110
|
+
function hasUserTagname(schema) {
|
|
111
|
+
let s = schema;
|
|
112
|
+
while (s) {
|
|
113
|
+
if (getOwnUserOptions(s).tagname) return true;
|
|
114
|
+
s = getParentSchema(s);
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
98
118
|
function resolvePropertiesCodecOptions(schema) {
|
|
99
119
|
const shape = schema.def.shape;
|
|
100
120
|
const options = {};
|
|
@@ -111,6 +131,13 @@ function findXmlStateKey(shape) {
|
|
|
111
131
|
if (keys.length > 1) throw new Error(`Only one xmlStateSchema field is allowed per object schema, found: ${keys.join(", ")}`);
|
|
112
132
|
return keys[0];
|
|
113
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Converts an `XMLElement` into the **input type** of `schema` (`z.input<S>`).
|
|
136
|
+
*
|
|
137
|
+
* This is a pure XML-to-data adapter: it does not run Zod's parse pipeline, so
|
|
138
|
+
* `z.codec` transforms and default values are **not** applied. The result is the
|
|
139
|
+
* raw decoded value suitable for passing to `schema.parse()`.
|
|
140
|
+
*/
|
|
114
141
|
function decode(schema, xml) {
|
|
115
142
|
const options = resolveCodecOptions(schema);
|
|
116
143
|
return options.decode({
|
|
@@ -118,6 +145,13 @@ function decode(schema, xml) {
|
|
|
118
145
|
xml
|
|
119
146
|
});
|
|
120
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Converts the **input type** of `schema` (`z.input<S>`) into an `XMLElement`.
|
|
150
|
+
*
|
|
151
|
+
* This is a pure data-to-XML adapter: it expects values at `z.input<S>` level,
|
|
152
|
+
* meaning `z.codec` transforms must already have been reversed (via `schema.encode()`)
|
|
153
|
+
* before calling this function.
|
|
154
|
+
*/
|
|
121
155
|
function encode(schema, data) {
|
|
122
156
|
const options = resolveCodecOptions(schema);
|
|
123
157
|
return options.encode({
|
|
@@ -125,6 +159,46 @@ function encode(schema, data) {
|
|
|
125
159
|
data
|
|
126
160
|
});
|
|
127
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Parses an XML string, `XMLRoot`, or `XMLElement` into the **output type** of
|
|
164
|
+
* `schema` (`z.output<S>`), running the full pipeline:
|
|
165
|
+
* XML → `decode` → `schema.parse()`.
|
|
166
|
+
*
|
|
167
|
+
* `z.codec` transforms (e.g. string → Date) and default values are applied.
|
|
168
|
+
* Use the lower-level {@link decode} if you need the raw input-type value without
|
|
169
|
+
* running the Zod parse pipeline.
|
|
170
|
+
*/
|
|
171
|
+
function parseXML(schema, input) {
|
|
172
|
+
if (typeof input === "string") input = XML.parse(input);
|
|
173
|
+
if (XML.isRoot(input)) {
|
|
174
|
+
const el = XML.elementFromRoot(input);
|
|
175
|
+
if (!el) throw new Error("Failed to resolve XML element from root");
|
|
176
|
+
input = el;
|
|
177
|
+
}
|
|
178
|
+
return schema.parse(decode(schema, input));
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Converts a value at the **output type** of `schema` (`z.output<S>`) into an
|
|
182
|
+
* `XMLElement`, running the full pipeline: `schema.encode()` → `encode`.
|
|
183
|
+
*
|
|
184
|
+
* `z.codec` transforms are reversed before the XML adapter runs.
|
|
185
|
+
* Use the lower-level {@link encode} if you already have an input-type value and
|
|
186
|
+
* do not need to run the Zod encode pipeline.
|
|
187
|
+
*
|
|
188
|
+
* Does not accept nullable values — check for `null`/`undefined` before calling.
|
|
189
|
+
*/
|
|
190
|
+
function toXML(schema, data) {
|
|
191
|
+
return encode(schema, schema.encode(data));
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Converts a value at the **output type** of `schema` (`z.output<S>`) into an
|
|
195
|
+
* XML string, running the full pipeline: `schema.encode()` → `encode` → `XML.stringify`.
|
|
196
|
+
*
|
|
197
|
+
* Equivalent to `XML.stringify({ elements: [toXML(schema, data)] }, options)`.
|
|
198
|
+
*/
|
|
199
|
+
function stringifyXML(schema, data, options) {
|
|
200
|
+
return XML.stringify({ elements: [toXML(schema, data)] }, options);
|
|
201
|
+
}
|
|
128
202
|
function registerDefault(resolve) {
|
|
129
203
|
defaults.push(resolve);
|
|
130
204
|
}
|
|
@@ -141,12 +215,12 @@ registerDefault((schema) => {
|
|
|
141
215
|
const elSchema = schema.def.element;
|
|
142
216
|
if (!isZodType(elSchema)) throw new Error(`Expected a ZodType, got ${elSchema}`);
|
|
143
217
|
const elOptions = resolveCodecOptions(elSchema);
|
|
144
|
-
const elHasOwnTagname =
|
|
218
|
+
const elHasOwnTagname = hasUserTagname(elSchema);
|
|
145
219
|
return normalizeCodecOptions(schema, {
|
|
146
220
|
decode(ctx) {
|
|
147
221
|
const { xml } = ctx;
|
|
148
222
|
if (!xml) return [];
|
|
149
|
-
return xml.elements.filter((el) => el.type === "element").map((el) => elOptions.decode({
|
|
223
|
+
return (xml.elements ?? []).filter((el) => el.type === "element").map((el) => elOptions.decode({
|
|
150
224
|
options: elOptions,
|
|
151
225
|
xml: el
|
|
152
226
|
}));
|
|
@@ -189,7 +263,7 @@ registerDefault((schema) => {
|
|
|
189
263
|
encodeAsProperty(ctx) {
|
|
190
264
|
if (typeof ctx.property.value !== "undefined") innerOptions.encodeAsProperty(ctx);
|
|
191
265
|
}
|
|
192
|
-
});
|
|
266
|
+
}, innerOptions);
|
|
193
267
|
}
|
|
194
268
|
if (schema instanceof z.ZodDefault) {
|
|
195
269
|
const { innerType: inner, defaultValue } = schema.def;
|
|
@@ -204,7 +278,7 @@ registerDefault((schema) => {
|
|
|
204
278
|
encode(ctx) {
|
|
205
279
|
return innerOptions.encode(ctx);
|
|
206
280
|
}
|
|
207
|
-
});
|
|
281
|
+
}, innerOptions);
|
|
208
282
|
}
|
|
209
283
|
if (schema instanceof z.ZodLazy) {
|
|
210
284
|
const inner = schema.def.getter();
|
|
@@ -232,11 +306,11 @@ registerDefault((schema) => {
|
|
|
232
306
|
data: ctx.data
|
|
233
307
|
});
|
|
234
308
|
}
|
|
235
|
-
});
|
|
309
|
+
}, inputCodecOptions);
|
|
236
310
|
}
|
|
237
311
|
if (schema instanceof z.ZodLiteral) {
|
|
238
312
|
const values = schema.def.values;
|
|
239
|
-
const valuesFromString = Object.fromEntries(values.map((v) => [v
|
|
313
|
+
const valuesFromString = Object.fromEntries(values.map((v) => [v?.toString(), v]));
|
|
240
314
|
return normalizeCodecOptions(schema, {
|
|
241
315
|
decode(ctx) {
|
|
242
316
|
const raw = XML.getContent(ctx.xml);
|
|
@@ -398,16 +472,21 @@ function formatReason(errors) {
|
|
|
398
472
|
function peekDiscriminatorValue(discriminator, propertyOptions, ctx) {
|
|
399
473
|
const errors = [];
|
|
400
474
|
for (const options of propertyOptions) try {
|
|
475
|
+
const tagname = options.propertyTagname({
|
|
476
|
+
name: discriminator,
|
|
477
|
+
options
|
|
478
|
+
});
|
|
401
479
|
const propCtx = {
|
|
402
480
|
name: discriminator,
|
|
403
481
|
options,
|
|
404
|
-
tagname
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
482
|
+
tagname,
|
|
483
|
+
xml: {
|
|
484
|
+
type: "element",
|
|
485
|
+
name: tagname,
|
|
486
|
+
elements: []
|
|
487
|
+
}
|
|
409
488
|
};
|
|
410
|
-
ctx.xml
|
|
489
|
+
ctx.xml?.elements?.forEach((el) => {
|
|
411
490
|
if (el.type !== "element") return;
|
|
412
491
|
if (options.propertyMatch(el, propCtx)) propCtx.xml.elements.push(el);
|
|
413
492
|
});
|
|
@@ -499,7 +578,30 @@ registerDefault((schema) => {
|
|
|
499
578
|
});
|
|
500
579
|
}
|
|
501
580
|
});
|
|
581
|
+
/**
|
|
582
|
+
* Creates a `z.codec` that converts between an XML string and the **input type**
|
|
583
|
+
* of `schema` (`z.input<S>`).
|
|
584
|
+
*
|
|
585
|
+
* The codec sits at the XML ↔ `z.input<S>` boundary only — it does not run Zod's
|
|
586
|
+
* parse pipeline. `z.codec` transforms (e.g. string → Date) and class instantiation
|
|
587
|
+
* are left to `schema.parse()` / `schema.encode()`, which you call separately if needed.
|
|
588
|
+
*
|
|
589
|
+
* Typical use: `xmlCodec(MyClass.dataSchema)` for standalone encode/decode without
|
|
590
|
+
* going through the full `fromXML` / `toXMLString` class API.
|
|
591
|
+
*/
|
|
592
|
+
function xmlCodec(schema) {
|
|
593
|
+
return z.codec(z.string(), schema, {
|
|
594
|
+
decode(xml) {
|
|
595
|
+
const xmlRoot = XML.parse(xml);
|
|
596
|
+
return decode(schema, XML.elementFromRoot(xmlRoot) ?? null);
|
|
597
|
+
},
|
|
598
|
+
encode(value) {
|
|
599
|
+
const xmlEl = encode(schema, value);
|
|
600
|
+
return XML.stringify({ elements: [xmlEl] });
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
}
|
|
502
604
|
//#endregion
|
|
503
|
-
export { XMLCodecError, decode, encode, normalizeCodecOptions, registerDefault, xmlStateSchema };
|
|
605
|
+
export { XMLCodecError, decode, encode, normalizeCodecOptions, parseXML, registerDefault, stringifyXML, toXML, xmlCodec, xmlStateSchema };
|
|
504
606
|
|
|
505
607
|
//# sourceMappingURL=codec.js.map
|
package/dist/xml/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './xml-js';
|
|
2
2
|
export { xml } from './schema-meta';
|
|
3
3
|
export type { UserCodecOptions, XMLState } from './codec';
|
|
4
|
-
export {
|
|
4
|
+
export { xmlCodec, // if not re-exported here, xmlCodec is not exported from "./codex.js" but only from "./codec.d.ts"
|
|
5
|
+
registerDefault, normalizeCodecOptions, XMLCodecError, xmlStateSchema, parseXML, toXML, stringifyXML, } from './codec';
|
|
5
6
|
export { xmlModel, type XmlModelConstructor } from './model';
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/xml/model.js
CHANGED
|
@@ -9,7 +9,11 @@ function xmlModel(schema, options) {
|
|
|
9
9
|
return class extends model(_schema) {
|
|
10
10
|
static fromXML(input) {
|
|
11
11
|
if (typeof input === "string") input = XML.parse(input);
|
|
12
|
-
if (XML.isRoot(input))
|
|
12
|
+
if (XML.isRoot(input)) {
|
|
13
|
+
const el = XML.elementFromRoot(input);
|
|
14
|
+
if (!el) throw new TypeError("No root element");
|
|
15
|
+
input = el;
|
|
16
|
+
}
|
|
13
17
|
const rawData = decode(this.dataSchema, input);
|
|
14
18
|
return this.fromData(this.dataSchema.parse(rawData));
|
|
15
19
|
}
|
|
@@ -67,6 +67,5 @@ export declare const xml: {
|
|
|
67
67
|
attr: typeof attr;
|
|
68
68
|
};
|
|
69
69
|
export declare function getOwnUserOptions<S extends z.ZodType>(schema: S): UserCodecOptions<S>;
|
|
70
|
-
export declare function getUserOptions<S extends z.ZodType>(schema: S): UserCodecOptions<S>;
|
|
71
70
|
export {};
|
|
72
71
|
//# sourceMappingURL=schema-meta.d.ts.map
|
package/dist/xml/schema-meta.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import XML from "./xml-js.js";
|
|
2
|
-
import {
|
|
2
|
+
import { isZodType } from "../util/zod.js";
|
|
3
3
|
import { kebabCase } from "../util/kebab-case.js";
|
|
4
4
|
import "zod";
|
|
5
5
|
//#region src/xml/schema-meta.ts
|
|
@@ -45,11 +45,13 @@ function normalizePropOptions(options) {
|
|
|
45
45
|
data: property.value
|
|
46
46
|
});
|
|
47
47
|
if (XML.isEmpty(res)) return;
|
|
48
|
-
if (property.options.inlineProperty)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
if (property.options.inlineProperty) {
|
|
49
|
+
const elements = res.elements?.map((el) => el.type === "element" ? {
|
|
50
|
+
...el,
|
|
51
|
+
name: property.tagname
|
|
52
|
+
} : el);
|
|
53
|
+
if (elements) (ctx.result.elements ??= []).push(...elements);
|
|
54
|
+
} else (ctx.result.elements ??= []).push({
|
|
53
55
|
...res,
|
|
54
56
|
name: property.tagname
|
|
55
57
|
});
|
|
@@ -79,7 +81,7 @@ function attr(optionsOrSchema, options) {
|
|
|
79
81
|
encodeAsProperty(ctx) {
|
|
80
82
|
const { value } = ctx.property;
|
|
81
83
|
const attrName = opts.name ?? kebabCase(ctx.property.name);
|
|
82
|
-
ctx.result.attributes[attrName] = value
|
|
84
|
+
(ctx.result.attributes ??= {})[attrName] = String(value);
|
|
83
85
|
}
|
|
84
86
|
};
|
|
85
87
|
if (isZodType(optionsOrSchema)) return setMeta(optionsOrSchema, partial);
|
|
@@ -94,27 +96,7 @@ var xml = {
|
|
|
94
96
|
function getOwnUserOptions(schema) {
|
|
95
97
|
return schema.meta()?.[metaKey] ?? {};
|
|
96
98
|
}
|
|
97
|
-
var INHERITABLE_KEYS = [
|
|
98
|
-
"tagname",
|
|
99
|
-
"propertyTagname",
|
|
100
|
-
"inlineProperty",
|
|
101
|
-
"propertyMatch",
|
|
102
|
-
"decodeAsProperty",
|
|
103
|
-
"encodeAsProperty"
|
|
104
|
-
];
|
|
105
|
-
function getUserOptions(schema) {
|
|
106
|
-
const own = getOwnUserOptions(schema);
|
|
107
|
-
const parentSchema = getParentSchema(schema);
|
|
108
|
-
if (!parentSchema) return own;
|
|
109
|
-
const parentOptions = getUserOptions(parentSchema);
|
|
110
|
-
const inherited = {};
|
|
111
|
-
for (const key of INHERITABLE_KEYS) if (parentOptions[key] !== void 0 && own[key] === void 0) inherited[key] = parentOptions[key];
|
|
112
|
-
return {
|
|
113
|
-
...inherited,
|
|
114
|
-
...own
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
99
|
//#endregion
|
|
118
|
-
export {
|
|
100
|
+
export { getOwnUserOptions, prop, root, xml };
|
|
119
101
|
|
|
120
102
|
//# sourceMappingURL=schema-meta.js.map
|
package/dist/xml/xml-js.js
CHANGED
|
@@ -59,7 +59,7 @@ function isEmpty(xml) {
|
|
|
59
59
|
* @throws {TypeError} When the element has multiple or non-text children.
|
|
60
60
|
*/
|
|
61
61
|
function getContent(xml) {
|
|
62
|
-
if (!xml.elements
|
|
62
|
+
if (!xml.elements?.length) return "";
|
|
63
63
|
if (xml.elements.length === 1) {
|
|
64
64
|
const content = xml.elements[0];
|
|
65
65
|
if (content.type === "text") return content.text;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xml-model",
|
|
3
|
-
"version": "2.0.0
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "allows transparent XML <-> Object conversion in typescript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "MathisTLD",
|
|
@@ -53,4 +53,4 @@
|
|
|
53
53
|
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
|
54
54
|
},
|
|
55
55
|
"packageManager": "npm@11.12.0"
|
|
56
|
-
}
|
|
56
|
+
}
|