xml-model 0.1.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.
Files changed (65) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.js +6 -0
  3. package/.gitignore +2 -0
  4. package/.vscode/launch.json +21 -0
  5. package/.vscode/settings.json +3 -0
  6. package/README.md +7 -0
  7. package/build/main/defaults/index.d.ts +14 -0
  8. package/build/main/defaults/index.js +165 -0
  9. package/build/main/defaults/models.d.ts +1 -0
  10. package/build/main/defaults/models.js +55 -0
  11. package/build/main/index.d.ts +5 -0
  12. package/build/main/index.js +31 -0
  13. package/build/main/middleware.d.ts +9 -0
  14. package/build/main/middleware.js +65 -0
  15. package/build/main/model/index.d.ts +26 -0
  16. package/build/main/model/index.js +304 -0
  17. package/build/main/model/property.d.ts +5 -0
  18. package/build/main/model/property.js +95 -0
  19. package/build/main/model/types.d.ts +69 -0
  20. package/build/main/model/types.js +214 -0
  21. package/build/main/model.spec.d.ts +1 -0
  22. package/build/main/model.spec.js +261 -0
  23. package/build/main/types.d.ts +8 -0
  24. package/build/main/types.js +18 -0
  25. package/build/main/xml.d.ts +31 -0
  26. package/build/main/xml.js +95 -0
  27. package/build/module/defaults/index.d.ts +13 -0
  28. package/build/module/defaults/index.js +71 -0
  29. package/build/module/defaults/models.d.ts +1 -0
  30. package/build/module/defaults/models.js +27 -0
  31. package/build/module/errors.d.ts +3 -0
  32. package/build/module/errors.js +37 -0
  33. package/build/module/index.d.ts +4 -0
  34. package/build/module/index.js +19 -0
  35. package/build/module/middleware.d.ts +9 -0
  36. package/build/module/middleware.js +60 -0
  37. package/build/module/model.d.ts +71 -0
  38. package/build/module/model.js +466 -0
  39. package/build/module/model.spec.d.ts +1 -0
  40. package/build/module/model.spec.js +76 -0
  41. package/build/module/types.d.ts +13 -0
  42. package/build/module/types.js +40 -0
  43. package/build/module/xml-from-object.d.ts +11 -0
  44. package/build/module/xml-from-object.js +58 -0
  45. package/build/module/xml-from-object.spec.d.ts +1 -0
  46. package/build/module/xml-from-object.spec.js +104 -0
  47. package/build/module/xml-to-object.d.ts +11 -0
  48. package/build/module/xml-to-object.js +55 -0
  49. package/build/module/xml.d.ts +15 -0
  50. package/build/module/xml.js +74 -0
  51. package/package.json +50 -0
  52. package/register-ts-node.js +9 -0
  53. package/src/defaults/index.ts +181 -0
  54. package/src/defaults/models.ts +45 -0
  55. package/src/index.ts +6 -0
  56. package/src/middleware.ts +34 -0
  57. package/src/model/index.ts +245 -0
  58. package/src/model/property.ts +104 -0
  59. package/src/model/types.ts +99 -0
  60. package/src/model.spec.ts +178 -0
  61. package/src/types.ts +8 -0
  62. package/src/xml.ts +80 -0
  63. package/tsconfig.json +106 -0
  64. package/tsconfig.module.json +9 -0
  65. package/yarn.lock +2217 -0
@@ -0,0 +1,245 @@
1
+ import "reflect-metadata";
2
+ import type { Constructor } from "typescript-rtti";
3
+ import { reflect } from "typescript-rtti";
4
+
5
+ import { MiddlewareChain, resolve } from "../middleware";
6
+ import {
7
+ XMLModelProperty,
8
+ XMLModelOptions,
9
+ XMLModelPropertyOptions,
10
+ PropertiesRecord,
11
+ XMLPropertiesRecord,
12
+ } from "./types";
13
+ import { getPropertyConversionOptions } from "./property";
14
+ import { XMLRoot } from "../types";
15
+ import XML from "../xml";
16
+ import { defaults } from "../defaults";
17
+
18
+ function* ParentChain(constructor: Constructor<unknown>) {
19
+ let parent = Object.getPrototypeOf(constructor);
20
+ if (parent === constructor) {
21
+ return;
22
+ }
23
+ while (parent) {
24
+ yield parent as Constructor<unknown>;
25
+ const _parent = Object.getPrototypeOf(constructor);
26
+ if (parent === _parent) {
27
+ return;
28
+ }
29
+ parent = _parent;
30
+ }
31
+ return;
32
+ }
33
+
34
+ function getParentModel(model: XMLModel<any>) {
35
+ for (const constructor of ParentChain(model.type)) {
36
+ const model = findModel(constructor);
37
+ if (model) {
38
+ return model as XMLModel<any>;
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+
44
+ export interface XMLModelConversionOptions<T> {
45
+ fromXML?: XMLModelOptions<T>["fromXML"]["middlewares"][number];
46
+ tagname?: string;
47
+ toXML?: XMLModelOptions<T>["toXML"]["middlewares"][number];
48
+ }
49
+
50
+ export class XMLModel<T = any> {
51
+ options: XMLModelOptions<T>;
52
+ constructor(
53
+ readonly type: Constructor<T>,
54
+ options: XMLModelConversionOptions<T>
55
+ ) {
56
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
57
+ const model = this;
58
+ let parent: XMLModel<any> | null | undefined = undefined;
59
+ const getParent = () => {
60
+ if (typeof parent === "undefined") parent = getParentModel(this);
61
+ return parent;
62
+ };
63
+ let propertiesLoaded = false;
64
+ const properties: XMLModelOptions<T>["properties"] = {
65
+ options: new Map<XMLModelProperty<T>, XMLModelPropertyOptions<T>>(),
66
+ fromXML: {
67
+ get parent() {
68
+ return getParent()?.options.properties.fromXML || null;
69
+ },
70
+ middlewares: [
71
+ (context, next) => {
72
+ const record: PropertiesRecord<T> = getParent() ? next() : {};
73
+ properties.options.forEach((property) => {
74
+ const xml = context.xml;
75
+ const elements = property.resolveElements({
76
+ model,
77
+ xml,
78
+ property,
79
+ });
80
+ record[property.name] = property.fromXML({
81
+ model,
82
+ xml: context.xml,
83
+ property,
84
+ elements,
85
+ });
86
+ });
87
+ return record;
88
+ },
89
+ ],
90
+ },
91
+ toXML: {
92
+ get parent() {
93
+ return getParent()?.options.properties.toXML || null;
94
+ },
95
+ middlewares: [
96
+ (context, next) => {
97
+ const record: XMLPropertiesRecord<T> = getParent() ? next() : {};
98
+ properties.options.forEach((options) => {
99
+ record[options.name] = options.toXML({
100
+ model,
101
+ object: context.object,
102
+
103
+ property: options,
104
+ value: context.object[options.name],
105
+ });
106
+ });
107
+ return record;
108
+ },
109
+ ],
110
+ },
111
+ };
112
+ const loadProperties = () => {
113
+ const props = reflect(this.type).ownProperties.filter(
114
+ (prop) =>
115
+ typeof prop.host.constructor.prototype[prop.name] !== "function"
116
+ ); // filter out methods like String.prototype.concat etc... that are seen as properties
117
+
118
+ props.forEach((property) => {
119
+ const options = getPropertyConversionOptions(
120
+ this.type,
121
+ property.name as XMLModelProperty<T>
122
+ );
123
+ if (!options.ignored) {
124
+ properties.options.set(property.name as XMLModelProperty<T>, options);
125
+ }
126
+ });
127
+ propertiesLoaded = true;
128
+ };
129
+
130
+ this.options = {
131
+ get properties() {
132
+ if (!propertiesLoaded) loadProperties();
133
+ return properties;
134
+ },
135
+ fromXML: {
136
+ middlewares: [],
137
+ get parent() {
138
+ return getParent()?.options.fromXML || null;
139
+ },
140
+ },
141
+ toXML: {
142
+ middlewares: [],
143
+ get parent() {
144
+ return getParent()?.options.toXML || null;
145
+ },
146
+ },
147
+ get tagname() {
148
+ return options.tagname || defaults.tagnameFromModel(model);
149
+ },
150
+ };
151
+ if (!getParent()) {
152
+ this.options.fromXML.middlewares.push((...args) =>
153
+ defaults.fromXML(...args)
154
+ );
155
+ this.options.toXML.middlewares.push((...args) => defaults.toXML(...args));
156
+ }
157
+ if (options.fromXML) this.options.fromXML.middlewares.push(options.fromXML);
158
+ if (options.toXML) this.options.toXML.middlewares.push(options.toXML);
159
+ }
160
+ fromXML(xml: XMLRoot | string) {
161
+ const _xml = typeof xml === "string" ? XML.parse(xml) : xml;
162
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
163
+ const model = this;
164
+ const context = {
165
+ xml: _xml,
166
+ get properties() {
167
+ const propContext = {
168
+ xml: _xml,
169
+ model,
170
+ };
171
+ return resolve(
172
+ MiddlewareChain(model.options.properties.fromXML),
173
+ propContext
174
+ );
175
+ },
176
+ model,
177
+ };
178
+ return resolve(MiddlewareChain(this.options.fromXML), context);
179
+ }
180
+ toXML(instance: object) {
181
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
182
+ const model = this;
183
+ if (instance instanceof this.type || instance.constructor === this.type) {
184
+ // intanceof won't work with type "String" for example
185
+ const context = {
186
+ object: instance as unknown as T,
187
+ get properties() {
188
+ const propContext = {
189
+ object: instance,
190
+ model,
191
+ };
192
+ return resolve(
193
+ MiddlewareChain(model.options.properties.toXML),
194
+ propContext as any
195
+ );
196
+ },
197
+ model: this,
198
+ };
199
+ return resolve(MiddlewareChain(this.options.toXML), context);
200
+ } else {
201
+ throw new TypeError(
202
+ `provided object is not an instance of ${this.type.name}`
203
+ );
204
+ }
205
+ }
206
+ get reflectedClass() {
207
+ return reflect(this.type);
208
+ }
209
+ }
210
+
211
+ export function createModel<T>(
212
+ type: Constructor<T>,
213
+ options: XMLModelConversionOptions<T>
214
+ ): XMLModel<T> {
215
+ if (findModel(type)) {
216
+ throw new TypeError(`a model for type ${type.name} already exists`);
217
+ }
218
+ const model = new XMLModel(type, options);
219
+ Models.set(type, model as XMLModel<unknown>);
220
+ return model;
221
+ }
222
+
223
+ type ModelID<T> = Constructor<T>;
224
+ export const Models = new Map<ModelID<unknown>, XMLModel<unknown>>();
225
+
226
+ export function findModel<T>(id: ModelID<T>) {
227
+ return Models.get(id) as XMLModel<T> | undefined;
228
+ }
229
+
230
+ export function getModel<T>(id: ModelID<T>) {
231
+ const model = findModel(id);
232
+ if (model) return model;
233
+ else throw new TypeError(`couln't find model for type ${id.name}`);
234
+ }
235
+
236
+ // Model decorator
237
+ function ModelDecoratorFactory<T>(options?: XMLModelConversionOptions<T>) {
238
+ return function (constructor: Constructor<T>): void {
239
+ findModel<T>(constructor) || createModel<T>(constructor, options || {});
240
+ };
241
+ }
242
+ export { ModelDecoratorFactory as Model };
243
+ export { Prop } from "./property";
244
+
245
+ import "../defaults/models";
@@ -0,0 +1,104 @@
1
+ import type { Constructor } from "typescript-rtti";
2
+ import { reflect } from "typescript-rtti";
3
+
4
+ import type {
5
+ XMLModelProperty,
6
+ XMLModelPropertyOptions,
7
+ CreateXMLModelPropertyOptions,
8
+ } from "./types";
9
+ import { defaults } from "../defaults";
10
+
11
+ function resolvePropertyConversionOptions<T>(
12
+ options: CreateXMLModelPropertyOptions<T>,
13
+ constructor: Constructor<T>,
14
+ property: XMLModelProperty<T>
15
+ ) {
16
+ const _options: XMLModelPropertyOptions<T> = {
17
+ name: property as keyof T,
18
+ get reflected() {
19
+ return reflect(constructor).getProperty(property);
20
+ },
21
+ get tagname() {
22
+ return options.tagname || defaults.tagnameFromProperty(this);
23
+ },
24
+ inline: !!options.inline,
25
+ ignored: !!options.ignore,
26
+ isSourceElement: (...args) =>
27
+ defaults.propertySourceElementsFilter(...args),
28
+ resolveElements: options.resolveElements
29
+ ? options.resolveElements
30
+ : (...args) => defaults.propertyResolveSourceElements(...args),
31
+ fromXML: (context) =>
32
+ (options.fromXML || defaults.propertyFromXML)(context),
33
+ toXML: (context) => (options.toXML || defaults.propertyToXML)(context),
34
+ };
35
+
36
+ if (options?.sourceElements) {
37
+ const _sourceElements = options.sourceElements;
38
+ if (typeof _sourceElements === "string") {
39
+ _options.isSourceElement = (element) => element.name === _sourceElements;
40
+ } else if (_sourceElements && _sourceElements instanceof RegExp) {
41
+ _options.isSourceElement = (element) =>
42
+ _sourceElements.test(element.name || "");
43
+ } else {
44
+ _options.isSourceElement = _sourceElements;
45
+ }
46
+ }
47
+
48
+ return _options;
49
+ }
50
+
51
+ const PropertyOptions = new Map<
52
+ Constructor<any>,
53
+ Map<XMLModelProperty<any>, XMLModelPropertyOptions<any>>
54
+ >();
55
+
56
+ function storePropertyConversionOptions<T>(
57
+ constructor: Constructor<T>,
58
+ property: XMLModelProperty<T>,
59
+ options: XMLModelPropertyOptions<T>
60
+ ) {
61
+ let map = PropertyOptions.get(constructor);
62
+ if (!map) {
63
+ map = new Map<
64
+ XMLModelProperty<unknown>,
65
+ XMLModelPropertyOptions<unknown>
66
+ >();
67
+ PropertyOptions.set(constructor, map);
68
+ }
69
+ map.set(property, options);
70
+ }
71
+
72
+ function findPropertyConversionOptions<T>(
73
+ constructor: Constructor<T>,
74
+ property: XMLModelProperty<T>
75
+ ) {
76
+ const options = PropertyOptions.get(constructor) as
77
+ | Map<XMLModelProperty<T>, XMLModelPropertyOptions<T>>
78
+ | undefined;
79
+ if (options) {
80
+ return options.get(property);
81
+ }
82
+ }
83
+
84
+ export function getPropertyConversionOptions<T>(
85
+ constructor: Constructor<T>,
86
+ property: XMLModelProperty<T>
87
+ ) {
88
+ const options = findPropertyConversionOptions(constructor, property);
89
+ return options || resolvePropertyConversionOptions({}, constructor, property);
90
+ }
91
+
92
+ function PropDecoratorFactory<T = any>(
93
+ options?: CreateXMLModelPropertyOptions<T>
94
+ ) {
95
+ return function (prototype: any, property: XMLModelProperty<T>) {
96
+ const _options = resolvePropertyConversionOptions(
97
+ options || {},
98
+ prototype.constructor,
99
+ property
100
+ );
101
+ storePropertyConversionOptions(prototype.constructor, property, _options);
102
+ };
103
+ }
104
+ export { PropDecoratorFactory as Prop };
@@ -0,0 +1,99 @@
1
+ import type { ReflectedProperty } from "typescript-rtti";
2
+ import type { XMLRoot, XMLElement } from "../types";
3
+ import type { Middleware } from "../middleware";
4
+ import type { XMLModel } from "./index";
5
+
6
+ /* PROPERTIES */
7
+ export type XMLModelProperty<T> = Extract<keyof T, string>;
8
+
9
+ export type PropertiesRecord<T> = {
10
+ [key in keyof T]?: T[key];
11
+ };
12
+
13
+ export type XMLPropertiesRecord<T> = {
14
+ [key in keyof T]?: XMLRoot;
15
+ };
16
+
17
+ export interface PropertyToXMLContext<T>
18
+ extends Omit<toXMLContext<T>, "properties"> {
19
+ property: XMLModelPropertyOptions<T>;
20
+ value: T[keyof T]; // FIXME ???
21
+ }
22
+
23
+ export interface PropertyFromXMLContext<T>
24
+ extends Omit<fromXMLContext<T>, "properties"> {
25
+ property: XMLModelPropertyOptions<T>;
26
+ elements: XMLElement[];
27
+ }
28
+ export interface XMLModelPropertyOptions<T> {
29
+ name: keyof T;
30
+ reflected: ReflectedProperty;
31
+ tagname: string;
32
+ ignored: boolean;
33
+ inline: boolean;
34
+ // from XML
35
+ isSourceElement: (
36
+ element: XMLElement,
37
+ context: Omit<PropertyFromXMLContext<T>, "elements">
38
+ ) => boolean;
39
+ resolveElements: (
40
+ context: Omit<PropertyFromXMLContext<T>, "elements">
41
+ ) => XMLElement[];
42
+ fromXML: (context: PropertyFromXMLContext<T>) => T[keyof T];
43
+ // to XML
44
+ toXML: (context: PropertyToXMLContext<T>) => XMLRoot;
45
+ }
46
+
47
+ export interface CreateXMLModelPropertyOptions<T> {
48
+ tagname?: string;
49
+ sourceElements?:
50
+ | string
51
+ | RegExp
52
+ | XMLModelPropertyOptions<T>["isSourceElement"];
53
+ resolveElements?: XMLModelPropertyOptions<T>["resolveElements"];
54
+ toXML?: XMLModelPropertyOptions<T>["toXML"];
55
+ fromXML?: XMLModelPropertyOptions<T>["fromXML"];
56
+ inline?: boolean;
57
+ ignore?: boolean;
58
+ }
59
+
60
+ /* MODEL */
61
+ interface ConversionOptions<C, T> {
62
+ parent: ConversionOptions<C, T> | null;
63
+ middlewares: Middleware<C, T>[];
64
+ }
65
+ export interface fromXMLContext<T> {
66
+ xml: XMLRoot;
67
+ properties: PropertiesRecord<T>;
68
+ model: XMLModel<T>;
69
+ }
70
+
71
+ export interface toXMLContext<T> {
72
+ object: T;
73
+ properties: XMLPropertiesRecord<T>;
74
+ model: XMLModel<T>;
75
+ }
76
+ export interface XMLModelOptions<T> {
77
+ properties: {
78
+ fromXML: ConversionOptions<
79
+ Omit<fromXMLContext<T>, "properties">,
80
+ PropertiesRecord<T>
81
+ >;
82
+ toXML: ConversionOptions<
83
+ Omit<toXMLContext<T>, "properties">,
84
+ XMLPropertiesRecord<T>
85
+ >;
86
+ options: Map<XMLModelProperty<T>, XMLModelPropertyOptions<T>>;
87
+ };
88
+ fromXML: ConversionOptions<fromXMLContext<T>, T>;
89
+ toXML: ConversionOptions<toXMLContext<T>, XMLRoot>;
90
+ tagname: string;
91
+ }
92
+
93
+ export interface CreateXMLModelOptions<T> {
94
+ fromXML?: XMLModelOptions<T>["fromXML"]["middlewares"][number];
95
+ tagname?: string;
96
+ toXML?: XMLModelOptions<T>["toXML"]["middlewares"][number];
97
+ }
98
+
99
+ export { XMLModel };
@@ -0,0 +1,178 @@
1
+ import "mocha";
2
+ import { expect, assert } from "chai";
3
+
4
+ import { Model, Prop, getModel } from "./model";
5
+ import XML from "./xml";
6
+ import { reflect, ReflectedClass } from "typescript-rtti";
7
+ import { UnknownRecord } from "./types";
8
+
9
+ @Model({
10
+ fromXML({ model, properties }) {
11
+ return new model.type(properties);
12
+ },
13
+ })
14
+ class Book {
15
+ name: string;
16
+ nbPages: number;
17
+ constructor(options: { name: string; nbPages: number }) {
18
+ this.name = options.name;
19
+ this.nbPages = options.nbPages;
20
+ }
21
+ equals(book: Book) {
22
+ if (this.name !== book.name) return false;
23
+ if (this.nbPages !== book.nbPages) return false;
24
+ return true;
25
+ }
26
+ }
27
+
28
+ @Model<Library>({
29
+ fromXML({ model, properties }) {
30
+ return new model.type(properties.name, ...(properties.books as Book[]));
31
+ },
32
+ })
33
+ class Library {
34
+ name: string;
35
+ books: Book[] = [];
36
+ constructor(name: string, ...books: Book[]) {
37
+ this.name = name;
38
+ this.books.push(...books);
39
+ }
40
+ equals(library: Library) {
41
+ if (this.name !== library.name) return false;
42
+ if (this.books.length !== library.books.length) return false;
43
+ for (let index = 0; index < this.books.length; index++) {
44
+ if (!this.books[index].equals(library.books[index])) return false;
45
+ }
46
+ return true;
47
+ }
48
+ }
49
+
50
+ describe("Library Example", () => {
51
+ const library = new Library("test");
52
+ for (let i = 1; i <= 4; i++) {
53
+ const book = new Book({ name: `Book #${i}`, nbPages: Math.pow(10, i) });
54
+ library.books.push(book);
55
+ }
56
+ const libraryXMLString = XML.stringify(
57
+ XML.parse(
58
+ `<library>
59
+ <name>${library.name}</name>
60
+ <books>
61
+ ${library.books
62
+ .map(
63
+ (book) =>
64
+ ` <book>
65
+ <name>${book.name}</name>
66
+ <nb-pages>${book.nbPages}</nb-pages>
67
+ </book>`
68
+ )
69
+ .join("")}
70
+ </books>
71
+ </library>`
72
+ )
73
+ );
74
+
75
+ it("Object -> XML", () => {
76
+ const xml = getModel(Library).toXML(library);
77
+ expect(XML.stringify(xml)).to.equal(libraryXMLString);
78
+ });
79
+ it("XML -> Object", () => {
80
+ const parsedLibrary = getModel(Library).fromXML(libraryXMLString);
81
+ expect(parsedLibrary instanceof Library).to.be.true;
82
+ expect(library.equals(parsedLibrary as Library)).to.be.true;
83
+ });
84
+ });
85
+
86
+ @Model({
87
+ fromXML({ model, properties }) {
88
+ return new model.type(properties);
89
+ },
90
+ })
91
+ class A {
92
+ propA: string = "";
93
+ propB: boolean = true;
94
+ @Prop({ tagname: "b", inline: true })
95
+ propC: B[] = [];
96
+ @Prop({ tagname: "propd" })
97
+ propD: 0 | 1 = 0;
98
+ equals(a: A) {
99
+ //
100
+ if (
101
+ this.propA !== a.propA ||
102
+ this.propB !== a.propB ||
103
+ this.propC.length !== a.propC.length ||
104
+ this.propD !== a.propD
105
+ )
106
+ return false;
107
+ for (let i = 0; i < this.propC.length; i++) {
108
+ if (!this.propC[i].equals(a.propC[i])) return false;
109
+ }
110
+ return true;
111
+ }
112
+ constructor(record?: UnknownRecord) {
113
+ if (record)
114
+ Object.entries(record).forEach(([key, val]) => {
115
+ (this[key as keyof A] as any) = val as any;
116
+ });
117
+ }
118
+ }
119
+
120
+ @Model({
121
+ fromXML({ model, properties }) {
122
+ return new model.type(properties);
123
+ },
124
+ })
125
+ class B {
126
+ propA: number = 0;
127
+ equals(b: B) {
128
+ return this.propA === b.propA;
129
+ }
130
+ constructor(record?: UnknownRecord) {
131
+ if (record)
132
+ Object.entries(record).forEach(([key, val]) => {
133
+ (this[key as keyof B] as any) = val as any;
134
+ });
135
+ }
136
+ }
137
+
138
+ describe("Edgy Cases", () => {
139
+ const instance = new A();
140
+ for (let i = 0; i < 8; i++) {
141
+ const b = new B();
142
+ b.propA = i;
143
+ instance.propC.push(b);
144
+ }
145
+
146
+ const instanceXMLString = XML.stringify(
147
+ XML.parse(`<a>
148
+ <prop-a>${instance.propA}</prop-a>
149
+ <prop-b>${instance.propB}</prop-b>
150
+ ${instance.propC.map((b) => `<b><prop-a>${b.propA}</prop-a></b>`).join("")}
151
+ <propd>${instance.propD}</propd>
152
+ </a>`)
153
+ );
154
+
155
+ it("should give right type infos", () => {
156
+ const reflectedA = reflect(A) as unknown as ReflectedClass;
157
+ assert(reflectedA === <ReflectedClass>(<unknown>reflect(A)));
158
+ expect(reflectedA.getProperty("propA").type.isClass(String)).to.be.true;
159
+ expect(reflectedA.getProperty("propB").type.isClass(Boolean)).to.be.true;
160
+ const ModelAPropCType = reflectedA.getProperty("propC").type;
161
+ expect(
162
+ ModelAPropCType.is("array") && ModelAPropCType.elementType.isClass(B)
163
+ ).to.be.true;
164
+ const ModelAPropDType = reflectedA.getProperty("propD").type;
165
+ expect(ModelAPropDType.is("union")).to.be.true;
166
+ });
167
+
168
+ it("XML -> Object", () => {
169
+ const parsed = getModel(A).fromXML(instanceXMLString);
170
+ expect(parsed instanceof A).to.be.true;
171
+ const equals = instance.equals(parsed as A);
172
+ expect(equals).to.be.true;
173
+ });
174
+ it("Object -> XML", () => {
175
+ const xml = getModel(A).toXML(instance);
176
+ expect(XML.stringify(xml)).to.equal(instanceXMLString);
177
+ });
178
+ });
package/src/types.ts ADDED
@@ -0,0 +1,8 @@
1
+ export type { Constructor } from "typescript-rtti";
2
+
3
+ export type UnknownRecord = Record<string | number | symbol, unknown>;
4
+ export type UnknownObject = object; // Record<string | number | symbol, unknown>; // don't works with class' instances
5
+
6
+ import type { Element as _XMLElement } from "xml-js";
7
+ export type XMLElement = _XMLElement;
8
+ export type XMLRoot = { elements: XMLElement[] };