gingersnap 0.23.12 → 0.23.13

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.
@@ -165,11 +165,12 @@ class Model {
165
165
  /**
166
166
  * Converts the current model to a JSON object
167
167
  */
168
- object(removeMissingFields = false) {
168
+ object(options) {
169
169
  var _a, _b;
170
+ const { removeMissingFields = false, keepOptionals = true } = options || {};
170
171
  const serialize = (value) => {
171
172
  if (value instanceof Model) {
172
- return value.object(removeMissingFields);
173
+ return value.object({ removeMissingFields, keepOptionals });
173
174
  }
174
175
  else if (value instanceof Array || Array.isArray(value)) {
175
176
  return value.map((v) => serialize(v));
@@ -189,6 +190,13 @@ class Model {
189
190
  else if (value instanceof Function) {
190
191
  return null;
191
192
  }
193
+ else if (value instanceof Optional) {
194
+ if (keepOptionals)
195
+ return value;
196
+ if (value.isPresent())
197
+ return value.get();
198
+ return null;
199
+ }
192
200
  return value;
193
201
  };
194
202
  return R__namespace.reduce((acc, [key, fieldProps]) => {
@@ -203,46 +211,45 @@ class Model {
203
211
  return acc;
204
212
  }, {}, Array.from(((_b = (_a = Model.buildPropTree(Object.getPrototypeOf(this))) === null || _a === void 0 ? void 0 : _a.fields) !== null && _b !== void 0 ? _b : new Map()).entries()));
205
213
  }
206
- // TODO add support for union fields in serialization and deserialization, and later date include schema generation
207
214
  /**
208
215
  * Converts the current model to a JSON string
209
216
  */
210
- json(removeMissingFields = false) {
211
- return JSON.stringify(this.object(removeMissingFields));
217
+ json(options) {
218
+ return JSON.stringify(this.object(options));
212
219
  }
213
220
  /**
214
221
  * Converts the current model to an arraybuffer
215
222
  */
216
- buffer(removeMissingFields = false) {
217
- return new TextEncoder().encode(this.json(removeMissingFields));
223
+ buffer(options) {
224
+ return new TextEncoder().encode(this.json(options));
218
225
  }
219
226
  /**
220
227
  * Converts the current model to a blob
221
228
  */
222
- blob(removeMissingFields = false) {
223
- return new Blob([this.buffer(removeMissingFields)]);
229
+ blob(options) {
230
+ return new Blob([this.buffer(options)]);
224
231
  }
225
232
  /**
226
233
  * Converts the current model to an XML string
227
234
  */
228
- xml(removeMissingFields = false) {
229
- return Model.__xmlParser__.js2xml(this.object(removeMissingFields));
235
+ xml(options) {
236
+ return Model.__xmlParser__.js2xml(this.object(options));
230
237
  }
231
238
  /**
232
239
  * Converts the current model to message pack binary format
233
240
  * @returns message pack binary
234
241
  */
235
- messagePack(removeMissingFields = false) {
236
- return msgpack.encode(this.object(removeMissingFields)).buffer;
242
+ messagePack(options) {
243
+ return msgpack.encode(this.object(options)).buffer;
237
244
  }
238
245
  /**
239
246
  * Converts the current model to csv format
240
- * @param removeMissingFields
247
+ * @param options
241
248
  * @param config CSV unparsing configuration
242
249
  * @returns csv string
243
250
  */
244
- csv(removeMissingFields = false, config) {
245
- return Papa.unparse([this.object(removeMissingFields)], config);
251
+ csv(options, config) {
252
+ return Papa.unparse([this.object(options)], config);
246
253
  }
247
254
  clone() {
248
255
  const newModel = new (Object.getPrototypeOf(this))();
@@ -253,10 +260,10 @@ class Model {
253
260
  }
254
261
  /**
255
262
  * Retrieves the schema of the current model based on the selected format
256
- * @param format data format
263
+ * @param includeTitleAndDraft boolean whether the title and JSON schema draft version should be referenced in the schema
257
264
  * @returns the schema for the current model
258
265
  */
259
- static schema(addTitleAndDraftRef = true) {
266
+ static schema(includeTitleAndDraft = true) {
260
267
  const model = new this();
261
268
  const props = this.buildPropTree(Object.getPrototypeOf(model));
262
269
  const handleArrays = (v, entry) => {
@@ -348,24 +355,38 @@ class Model {
348
355
  }
349
356
  return entry;
350
357
  }));
351
- if (addTitleAndDraftRef)
352
- return {
353
- $schema: "https://json-schema.org/draft/2020-12/schema",
354
- title: this.name,
358
+ const fieldEntries = Array.from(props.fields.entries());
359
+ const optionals = Object.getOwnPropertyNames(this.prototype)
360
+ .filter((key) => key !== "constructor")
361
+ .map((key) => [key, model[key]])
362
+ .map(([key, modelValue]) => ({
363
+ modelValue,
364
+ fieldProps: fieldEntries.find(([k, v]) => { var _a; return k === key || ((_a = v.aliases) === null || _a === void 0 ? void 0 : _a.includes(key)); }),
365
+ }))
366
+ .filter(({ modelValue, fieldProps }) => { var _a, _b, _c; return modelValue !== undefined || ((_b = (_a = fieldProps[1].schema) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.optional) || ((_c = fieldProps[1].ignore) === null || _c === void 0 ? void 0 : _c.deserialize); })
367
+ .map(({ fieldProps }) => fieldProps[0]);
368
+ const required = fieldEntries.filter(([k]) => !optionals.includes(k)).map(([k]) => k);
369
+ const result = required.length
370
+ ? {
355
371
  type: "object",
356
372
  properties: generateProperties(),
357
373
  additionalProperties: false,
358
- };
359
- else {
360
- return {
374
+ required,
375
+ }
376
+ : {
361
377
  type: "object",
362
378
  properties: generateProperties(),
363
379
  additionalProperties: false,
364
380
  };
381
+ if (includeTitleAndDraft) {
382
+ return Object.assign(Object.assign({}, result), { $schema: "https://json-schema.org/draft/2020-12/schema", title: this.name });
383
+ }
384
+ else {
385
+ return result;
365
386
  }
366
387
  }
367
388
  toString() {
368
- return this.json(true);
389
+ return this.json({ removeMissingFields: true, keepOptionals: false });
369
390
  }
370
391
  /**
371
392
  * Constructs the model properties by traversing the inheritance tree of the current Model being instantiated
@@ -416,7 +437,7 @@ class Model {
416
437
  const model = new this();
417
438
  const props = this.buildPropTree(Object.getPrototypeOf(model));
418
439
  R__namespace.forEach(([key, fieldProps]) => {
419
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
440
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
420
441
  let value = data[key];
421
442
  if (value === undefined || value === null) {
422
443
  for (const alias of (_a = fieldProps.aliases) !== null && _a !== void 0 ? _a : []) {
@@ -437,12 +458,101 @@ class Model {
437
458
  else if ((value === undefined || value === null) && !(model[key] === undefined || model[key] === null)) {
438
459
  return;
439
460
  }
461
+ const checkOtherTypes = (value, Type, fallback) => {
462
+ var _a, _b;
463
+ if ((Type === null || Type === void 0 ? void 0 : Type.name) === "String") {
464
+ return processValue(fieldProps, String(value));
465
+ }
466
+ else if ((Type === null || Type === void 0 ? void 0 : Type.name) === "Boolean") {
467
+ return processValue(fieldProps, Boolean(value));
468
+ }
469
+ else if ((Type === null || Type === void 0 ? void 0 : Type.name) === "Number") {
470
+ const v = Number(value);
471
+ if (isNaN(v))
472
+ throw new ParsingError.ParsingError([], `value for ${key} is not a valid number`);
473
+ return processValue(fieldProps, v);
474
+ }
475
+ else if ((Type === null || Type === void 0 ? void 0 : Type.name) === "Map") {
476
+ const valueType = typeof value;
477
+ switch (valueType) {
478
+ case "object": {
479
+ try {
480
+ return processValue(fieldProps, value instanceof Array || Array.isArray(value) ? new Map(value) : new Map(Object.entries(value)));
481
+ }
482
+ catch (e) {
483
+ throw new ParsingError.ParsingError([], (_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : String(e));
484
+ }
485
+ }
486
+ case "string":
487
+ try {
488
+ value = JSON.parse(value);
489
+ return processValue(fieldProps, value instanceof Array || Array.isArray(value) ? new Map(value) : new Map(Object.entries(value)));
490
+ }
491
+ catch (e) {
492
+ throw new ParsingError.ParsingError([], (_b = e === null || e === void 0 ? void 0 : e.message) !== null && _b !== void 0 ? _b : String(e));
493
+ }
494
+ default:
495
+ throw new ParsingError.ParsingError([value], `value for ${key} cannot be converted to a Map`);
496
+ }
497
+ }
498
+ else if ((Type === null || Type === void 0 ? void 0 : Type.name) === "Set") {
499
+ if (value instanceof Array || Array.isArray(value)) {
500
+ return processValue(fieldProps, new Set(value));
501
+ }
502
+ else if (typeof value === "string") {
503
+ return processValue(fieldProps, new Set(JSON.parse(value)));
504
+ }
505
+ throw new ParsingError.ParsingError([value], `value for ${key} cannot be converted to a Set`);
506
+ }
507
+ else {
508
+ return fallback();
509
+ }
510
+ };
511
+ function processUnionField(value) {
512
+ let foundMatch = false;
513
+ let result;
514
+ for (const unionType of fieldProps.unionTypes) {
515
+ try {
516
+ result = checkOtherTypes(value, unionType, () => {
517
+ if (unionType.prototype instanceof Model) {
518
+ const parser$1 = parser.parse({
519
+ string: (v) => unionType.fromString(v, format),
520
+ object: (v) => unionType.fromObject(v, format),
521
+ default: () => {
522
+ throw new ParsingError.ParsingError([], `value for ${key} is expected to be an Array, instead received ${typeof value}`);
523
+ },
524
+ supportArray: true,
525
+ });
526
+ return processValue(fieldProps, parser$1(value));
527
+ }
528
+ throw new ParsingError.ParsingError([], `value for ${key} type is unsupported ${typeof value}`);
529
+ });
530
+ foundMatch = true;
531
+ break;
532
+ }
533
+ catch (e) { }
534
+ }
535
+ if (!foundMatch) {
536
+ throw new ParsingError.ParsingError([], `value for ${key} does not match any of the union types expected`);
537
+ }
538
+ return result;
539
+ }
440
540
  if ((_e = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(model), key)) === null || _e === void 0 ? void 0 : _e.set) {
441
541
  model[key] = processValue(fieldProps, value);
442
542
  }
443
543
  else if ((_f = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(model), key)) === null || _f === void 0 ? void 0 : _f.get) {
444
544
  return;
445
545
  }
546
+ else if (fieldProps.isUnion) {
547
+ if (!fieldProps.isArray) {
548
+ model[key] = processUnionField(value);
549
+ }
550
+ else if (fieldProps.isArray && Array.isArray(value)) {
551
+ model[key] = value.map((item) => processUnionField(item));
552
+ }
553
+ else
554
+ throw new ParsingError.ParsingError([], `value for ${key} is expected to be an array`);
555
+ }
446
556
  else if (model[key] instanceof fieldProps.Type &&
447
557
  typeof model[key] === "function" &&
448
558
  fieldProps.Type instanceof Function) {
@@ -552,51 +662,8 @@ class Model {
552
662
  }
553
663
  }));
554
664
  }
555
- else if (((_k = fieldProps.Type) === null || _k === void 0 ? void 0 : _k.name) === "String") {
556
- model[key] = processValue(fieldProps, String(value));
557
- }
558
- else if (((_l = fieldProps.Type) === null || _l === void 0 ? void 0 : _l.name) === "Boolean") {
559
- model[key] = processValue(fieldProps, Boolean(value));
560
- }
561
- else if (((_m = fieldProps.Type) === null || _m === void 0 ? void 0 : _m.name) === "Number") {
562
- model[key] = processValue(fieldProps, Number(value));
563
- }
564
- else if (((_o = fieldProps.Type) === null || _o === void 0 ? void 0 : _o.name) === "Map") {
565
- const valueType = typeof value;
566
- switch (valueType) {
567
- case "object": {
568
- try {
569
- model[key] = processValue(fieldProps, value instanceof Array || Array.isArray(value) ? new Map(value) : new Map(Object.entries(value)));
570
- }
571
- catch (e) {
572
- throw new ParsingError.ParsingError([], (_p = e === null || e === void 0 ? void 0 : e.message) !== null && _p !== void 0 ? _p : String(e));
573
- }
574
- break;
575
- }
576
- case "string":
577
- try {
578
- value = JSON.parse(value);
579
- model[key] = processValue(fieldProps, value instanceof Array || Array.isArray(value) ? new Map(value) : new Map(Object.entries(value)));
580
- }
581
- catch (e) {
582
- throw new ParsingError.ParsingError([], (_q = e === null || e === void 0 ? void 0 : e.message) !== null && _q !== void 0 ? _q : String(e));
583
- }
584
- break;
585
- default:
586
- throw new ParsingError.ParsingError([value], `value for ${key} cannot be converted to a Map`);
587
- }
588
- }
589
- else if (((_r = fieldProps.Type) === null || _r === void 0 ? void 0 : _r.name) === "Set") {
590
- if (value instanceof Array || Array.isArray(value)) {
591
- model[key] = processValue(fieldProps, new Set(value));
592
- }
593
- else if (typeof value === "string") {
594
- model[key] = processValue(fieldProps, new Set(JSON.parse(value)));
595
- }
596
- throw new ParsingError.ParsingError([value], `value for ${key} cannot be converted to a Set`);
597
- }
598
665
  else {
599
- model[key] = processValue(fieldProps, new fieldProps.Type(value));
666
+ model[key] = checkOtherTypes(value, fieldProps.Type, () => processValue(fieldProps, new fieldProps.Type(value)));
600
667
  }
601
668
  if (fieldProps.readonly) {
602
669
  Object.defineProperty(model, key, {
@@ -608,7 +675,7 @@ class Model {
608
675
  R__namespace.forEach(([k, v]) => {
609
676
  if (v.__callback__)
610
677
  v.__callback__(k, v.properties, model, key);
611
- }, R__namespace.toPairs((_s = fieldProps.customTags) !== null && _s !== void 0 ? _s : {}));
678
+ }, R__namespace.toPairs((_k = fieldProps.customTags) !== null && _k !== void 0 ? _k : {}));
612
679
  }, Array.from(props.fields.entries()));
613
680
  return Object.seal(model);
614
681
  }
@@ -95,6 +95,10 @@ export interface ModelURLConversionOptions extends ModelAdvanceConversionOptions
95
95
  requestHeaders?: any;
96
96
  mode?: any;
97
97
  }
98
+ export interface ModelExportOptions {
99
+ removeMissingFields?: boolean;
100
+ keepOptionals?: boolean;
101
+ }
98
102
  export type ModelJSONConversionOptions = Omit<ModelConversionOptions, "format">;
99
103
  /**
100
104
  * A Data de/serializer class that manages and validates data as JavaScript Objects
@@ -148,7 +152,6 @@ export declare class Model {
148
152
  * @remarks
149
153
  * If the data format is a binary one, the provided string should be a hexadecimal string.
150
154
  * @param data string data source
151
- * @param format data format
152
155
  * @param options
153
156
  */
154
157
  static fromString<T extends Model>(this: ModelConstructor<T>, data: string, options?: Omit<ModelAdvanceConversionOptions, "array" | "maybeArray">): T;
@@ -171,44 +174,44 @@ export declare class Model {
171
174
  /**
172
175
  * Converts the current model to a JSON object
173
176
  */
174
- object(removeMissingFields?: boolean): {
177
+ object(options?: ModelExportOptions): {
175
178
  [string: string]: any;
176
179
  };
177
180
  /**
178
181
  * Converts the current model to a JSON string
179
182
  */
180
- json(removeMissingFields?: boolean): string;
183
+ json(options?: ModelExportOptions): string;
181
184
  /**
182
185
  * Converts the current model to an arraybuffer
183
186
  */
184
- buffer(removeMissingFields?: boolean): ArrayBuffer;
187
+ buffer(options?: ModelExportOptions): ArrayBuffer;
185
188
  /**
186
189
  * Converts the current model to a blob
187
190
  */
188
- blob(removeMissingFields?: boolean): Blob;
191
+ blob(options?: ModelExportOptions): Blob;
189
192
  /**
190
193
  * Converts the current model to an XML string
191
194
  */
192
- xml(removeMissingFields?: boolean): string;
195
+ xml(options?: ModelExportOptions): string;
193
196
  /**
194
197
  * Converts the current model to message pack binary format
195
198
  * @returns message pack binary
196
199
  */
197
- messagePack(removeMissingFields?: boolean): Buffer;
200
+ messagePack(options?: ModelExportOptions): Buffer;
198
201
  /**
199
202
  * Converts the current model to csv format
200
- * @param removeMissingFields
203
+ * @param options
201
204
  * @param config CSV unparsing configuration
202
205
  * @returns csv string
203
206
  */
204
- csv(removeMissingFields?: boolean, config?: Papa.UnparseConfig): string;
207
+ csv(options?: ModelExportOptions, config?: Papa.UnparseConfig): string;
205
208
  clone(): any;
206
209
  /**
207
210
  * Retrieves the schema of the current model based on the selected format
208
- * @param format data format
211
+ * @param includeTitleAndDraft boolean whether the title and JSON schema draft version should be referenced in the schema
209
212
  * @returns the schema for the current model
210
213
  */
211
- static schema(addTitleAndDraftRef?: boolean): {
214
+ static schema(includeTitleAndDraft?: boolean): {
212
215
  [string: string]: any;
213
216
  };
214
217
  toString(): string;
@@ -144,11 +144,12 @@ class Model {
144
144
  /**
145
145
  * Converts the current model to a JSON object
146
146
  */
147
- object(removeMissingFields = false) {
147
+ object(options) {
148
148
  var _a, _b;
149
+ const { removeMissingFields = false, keepOptionals = true } = options || {};
149
150
  const serialize = (value) => {
150
151
  if (value instanceof Model) {
151
- return value.object(removeMissingFields);
152
+ return value.object({ removeMissingFields, keepOptionals });
152
153
  }
153
154
  else if (value instanceof Array || Array.isArray(value)) {
154
155
  return value.map((v) => serialize(v));
@@ -168,6 +169,13 @@ class Model {
168
169
  else if (value instanceof Function) {
169
170
  return null;
170
171
  }
172
+ else if (value instanceof Optional) {
173
+ if (keepOptionals)
174
+ return value;
175
+ if (value.isPresent())
176
+ return value.get();
177
+ return null;
178
+ }
171
179
  return value;
172
180
  };
173
181
  return R.reduce((acc, [key, fieldProps]) => {
@@ -182,46 +190,45 @@ class Model {
182
190
  return acc;
183
191
  }, {}, Array.from(((_b = (_a = Model.buildPropTree(Object.getPrototypeOf(this))) === null || _a === void 0 ? void 0 : _a.fields) !== null && _b !== void 0 ? _b : new Map()).entries()));
184
192
  }
185
- // TODO add support for union fields in serialization and deserialization, and later date include schema generation
186
193
  /**
187
194
  * Converts the current model to a JSON string
188
195
  */
189
- json(removeMissingFields = false) {
190
- return JSON.stringify(this.object(removeMissingFields));
196
+ json(options) {
197
+ return JSON.stringify(this.object(options));
191
198
  }
192
199
  /**
193
200
  * Converts the current model to an arraybuffer
194
201
  */
195
- buffer(removeMissingFields = false) {
196
- return new TextEncoder().encode(this.json(removeMissingFields));
202
+ buffer(options) {
203
+ return new TextEncoder().encode(this.json(options));
197
204
  }
198
205
  /**
199
206
  * Converts the current model to a blob
200
207
  */
201
- blob(removeMissingFields = false) {
202
- return new Blob([this.buffer(removeMissingFields)]);
208
+ blob(options) {
209
+ return new Blob([this.buffer(options)]);
203
210
  }
204
211
  /**
205
212
  * Converts the current model to an XML string
206
213
  */
207
- xml(removeMissingFields = false) {
208
- return Model.__xmlParser__.js2xml(this.object(removeMissingFields));
214
+ xml(options) {
215
+ return Model.__xmlParser__.js2xml(this.object(options));
209
216
  }
210
217
  /**
211
218
  * Converts the current model to message pack binary format
212
219
  * @returns message pack binary
213
220
  */
214
- messagePack(removeMissingFields = false) {
215
- return encode(this.object(removeMissingFields)).buffer;
221
+ messagePack(options) {
222
+ return encode(this.object(options)).buffer;
216
223
  }
217
224
  /**
218
225
  * Converts the current model to csv format
219
- * @param removeMissingFields
226
+ * @param options
220
227
  * @param config CSV unparsing configuration
221
228
  * @returns csv string
222
229
  */
223
- csv(removeMissingFields = false, config) {
224
- return Papa.unparse([this.object(removeMissingFields)], config);
230
+ csv(options, config) {
231
+ return Papa.unparse([this.object(options)], config);
225
232
  }
226
233
  clone() {
227
234
  const newModel = new (Object.getPrototypeOf(this))();
@@ -232,10 +239,10 @@ class Model {
232
239
  }
233
240
  /**
234
241
  * Retrieves the schema of the current model based on the selected format
235
- * @param format data format
242
+ * @param includeTitleAndDraft boolean whether the title and JSON schema draft version should be referenced in the schema
236
243
  * @returns the schema for the current model
237
244
  */
238
- static schema(addTitleAndDraftRef = true) {
245
+ static schema(includeTitleAndDraft = true) {
239
246
  const model = new this();
240
247
  const props = this.buildPropTree(Object.getPrototypeOf(model));
241
248
  const handleArrays = (v, entry) => {
@@ -327,24 +334,38 @@ class Model {
327
334
  }
328
335
  return entry;
329
336
  }));
330
- if (addTitleAndDraftRef)
331
- return {
332
- $schema: "https://json-schema.org/draft/2020-12/schema",
333
- title: this.name,
337
+ const fieldEntries = Array.from(props.fields.entries());
338
+ const optionals = Object.getOwnPropertyNames(this.prototype)
339
+ .filter((key) => key !== "constructor")
340
+ .map((key) => [key, model[key]])
341
+ .map(([key, modelValue]) => ({
342
+ modelValue,
343
+ fieldProps: fieldEntries.find(([k, v]) => { var _a; return k === key || ((_a = v.aliases) === null || _a === void 0 ? void 0 : _a.includes(key)); }),
344
+ }))
345
+ .filter(({ modelValue, fieldProps }) => { var _a, _b, _c; return modelValue !== undefined || ((_b = (_a = fieldProps[1].schema) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.optional) || ((_c = fieldProps[1].ignore) === null || _c === void 0 ? void 0 : _c.deserialize); })
346
+ .map(({ fieldProps }) => fieldProps[0]);
347
+ const required = fieldEntries.filter(([k]) => !optionals.includes(k)).map(([k]) => k);
348
+ const result = required.length
349
+ ? {
334
350
  type: "object",
335
351
  properties: generateProperties(),
336
352
  additionalProperties: false,
337
- };
338
- else {
339
- return {
353
+ required,
354
+ }
355
+ : {
340
356
  type: "object",
341
357
  properties: generateProperties(),
342
358
  additionalProperties: false,
343
359
  };
360
+ if (includeTitleAndDraft) {
361
+ return Object.assign(Object.assign({}, result), { $schema: "https://json-schema.org/draft/2020-12/schema", title: this.name });
362
+ }
363
+ else {
364
+ return result;
344
365
  }
345
366
  }
346
367
  toString() {
347
- return this.json(true);
368
+ return this.json({ removeMissingFields: true, keepOptionals: false });
348
369
  }
349
370
  /**
350
371
  * Constructs the model properties by traversing the inheritance tree of the current Model being instantiated
@@ -395,7 +416,7 @@ class Model {
395
416
  const model = new this();
396
417
  const props = this.buildPropTree(Object.getPrototypeOf(model));
397
418
  R.forEach(([key, fieldProps]) => {
398
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
419
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
399
420
  let value = data[key];
400
421
  if (value === undefined || value === null) {
401
422
  for (const alias of (_a = fieldProps.aliases) !== null && _a !== void 0 ? _a : []) {
@@ -416,12 +437,101 @@ class Model {
416
437
  else if ((value === undefined || value === null) && !(model[key] === undefined || model[key] === null)) {
417
438
  return;
418
439
  }
440
+ const checkOtherTypes = (value, Type, fallback) => {
441
+ var _a, _b;
442
+ if ((Type === null || Type === void 0 ? void 0 : Type.name) === "String") {
443
+ return processValue(fieldProps, String(value));
444
+ }
445
+ else if ((Type === null || Type === void 0 ? void 0 : Type.name) === "Boolean") {
446
+ return processValue(fieldProps, Boolean(value));
447
+ }
448
+ else if ((Type === null || Type === void 0 ? void 0 : Type.name) === "Number") {
449
+ const v = Number(value);
450
+ if (isNaN(v))
451
+ throw new ParsingError([], `value for ${key} is not a valid number`);
452
+ return processValue(fieldProps, v);
453
+ }
454
+ else if ((Type === null || Type === void 0 ? void 0 : Type.name) === "Map") {
455
+ const valueType = typeof value;
456
+ switch (valueType) {
457
+ case "object": {
458
+ try {
459
+ return processValue(fieldProps, value instanceof Array || Array.isArray(value) ? new Map(value) : new Map(Object.entries(value)));
460
+ }
461
+ catch (e) {
462
+ throw new ParsingError([], (_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : String(e));
463
+ }
464
+ }
465
+ case "string":
466
+ try {
467
+ value = JSON.parse(value);
468
+ return processValue(fieldProps, value instanceof Array || Array.isArray(value) ? new Map(value) : new Map(Object.entries(value)));
469
+ }
470
+ catch (e) {
471
+ throw new ParsingError([], (_b = e === null || e === void 0 ? void 0 : e.message) !== null && _b !== void 0 ? _b : String(e));
472
+ }
473
+ default:
474
+ throw new ParsingError([value], `value for ${key} cannot be converted to a Map`);
475
+ }
476
+ }
477
+ else if ((Type === null || Type === void 0 ? void 0 : Type.name) === "Set") {
478
+ if (value instanceof Array || Array.isArray(value)) {
479
+ return processValue(fieldProps, new Set(value));
480
+ }
481
+ else if (typeof value === "string") {
482
+ return processValue(fieldProps, new Set(JSON.parse(value)));
483
+ }
484
+ throw new ParsingError([value], `value for ${key} cannot be converted to a Set`);
485
+ }
486
+ else {
487
+ return fallback();
488
+ }
489
+ };
490
+ function processUnionField(value) {
491
+ let foundMatch = false;
492
+ let result;
493
+ for (const unionType of fieldProps.unionTypes) {
494
+ try {
495
+ result = checkOtherTypes(value, unionType, () => {
496
+ if (unionType.prototype instanceof Model) {
497
+ const parser = parse({
498
+ string: (v) => unionType.fromString(v, format),
499
+ object: (v) => unionType.fromObject(v, format),
500
+ default: () => {
501
+ throw new ParsingError([], `value for ${key} is expected to be an Array, instead received ${typeof value}`);
502
+ },
503
+ supportArray: true,
504
+ });
505
+ return processValue(fieldProps, parser(value));
506
+ }
507
+ throw new ParsingError([], `value for ${key} type is unsupported ${typeof value}`);
508
+ });
509
+ foundMatch = true;
510
+ break;
511
+ }
512
+ catch (e) { }
513
+ }
514
+ if (!foundMatch) {
515
+ throw new ParsingError([], `value for ${key} does not match any of the union types expected`);
516
+ }
517
+ return result;
518
+ }
419
519
  if ((_e = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(model), key)) === null || _e === void 0 ? void 0 : _e.set) {
420
520
  model[key] = processValue(fieldProps, value);
421
521
  }
422
522
  else if ((_f = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(model), key)) === null || _f === void 0 ? void 0 : _f.get) {
423
523
  return;
424
524
  }
525
+ else if (fieldProps.isUnion) {
526
+ if (!fieldProps.isArray) {
527
+ model[key] = processUnionField(value);
528
+ }
529
+ else if (fieldProps.isArray && Array.isArray(value)) {
530
+ model[key] = value.map((item) => processUnionField(item));
531
+ }
532
+ else
533
+ throw new ParsingError([], `value for ${key} is expected to be an array`);
534
+ }
425
535
  else if (model[key] instanceof fieldProps.Type &&
426
536
  typeof model[key] === "function" &&
427
537
  fieldProps.Type instanceof Function) {
@@ -531,51 +641,8 @@ class Model {
531
641
  }
532
642
  }));
533
643
  }
534
- else if (((_k = fieldProps.Type) === null || _k === void 0 ? void 0 : _k.name) === "String") {
535
- model[key] = processValue(fieldProps, String(value));
536
- }
537
- else if (((_l = fieldProps.Type) === null || _l === void 0 ? void 0 : _l.name) === "Boolean") {
538
- model[key] = processValue(fieldProps, Boolean(value));
539
- }
540
- else if (((_m = fieldProps.Type) === null || _m === void 0 ? void 0 : _m.name) === "Number") {
541
- model[key] = processValue(fieldProps, Number(value));
542
- }
543
- else if (((_o = fieldProps.Type) === null || _o === void 0 ? void 0 : _o.name) === "Map") {
544
- const valueType = typeof value;
545
- switch (valueType) {
546
- case "object": {
547
- try {
548
- model[key] = processValue(fieldProps, value instanceof Array || Array.isArray(value) ? new Map(value) : new Map(Object.entries(value)));
549
- }
550
- catch (e) {
551
- throw new ParsingError([], (_p = e === null || e === void 0 ? void 0 : e.message) !== null && _p !== void 0 ? _p : String(e));
552
- }
553
- break;
554
- }
555
- case "string":
556
- try {
557
- value = JSON.parse(value);
558
- model[key] = processValue(fieldProps, value instanceof Array || Array.isArray(value) ? new Map(value) : new Map(Object.entries(value)));
559
- }
560
- catch (e) {
561
- throw new ParsingError([], (_q = e === null || e === void 0 ? void 0 : e.message) !== null && _q !== void 0 ? _q : String(e));
562
- }
563
- break;
564
- default:
565
- throw new ParsingError([value], `value for ${key} cannot be converted to a Map`);
566
- }
567
- }
568
- else if (((_r = fieldProps.Type) === null || _r === void 0 ? void 0 : _r.name) === "Set") {
569
- if (value instanceof Array || Array.isArray(value)) {
570
- model[key] = processValue(fieldProps, new Set(value));
571
- }
572
- else if (typeof value === "string") {
573
- model[key] = processValue(fieldProps, new Set(JSON.parse(value)));
574
- }
575
- throw new ParsingError([value], `value for ${key} cannot be converted to a Set`);
576
- }
577
644
  else {
578
- model[key] = processValue(fieldProps, new fieldProps.Type(value));
645
+ model[key] = checkOtherTypes(value, fieldProps.Type, () => processValue(fieldProps, new fieldProps.Type(value)));
579
646
  }
580
647
  if (fieldProps.readonly) {
581
648
  Object.defineProperty(model, key, {
@@ -587,7 +654,7 @@ class Model {
587
654
  R.forEach(([k, v]) => {
588
655
  if (v.__callback__)
589
656
  v.__callback__(k, v.properties, model, key);
590
- }, R.toPairs((_s = fieldProps.customTags) !== null && _s !== void 0 ? _s : {}));
657
+ }, R.toPairs((_k = fieldProps.customTags) !== null && _k !== void 0 ? _k : {}));
591
658
  }, Array.from(props.fields.entries()));
592
659
  return Object.seal(model);
593
660
  }
@@ -141,6 +141,7 @@ const UnionField = (types, name) => (target, key) => {
141
141
  fieldProp.isUnion = true;
142
142
  fieldProp.schema = schema;
143
143
  fieldProp.unionTypes = types;
144
+ fieldProp.Type = {};
144
145
  props.fields.set(key, fieldProp);
145
146
  if (typeof name === "string" && name) {
146
147
  if (fieldProp.aliases) {
@@ -168,8 +169,9 @@ const ArrayField = (type, name) => (target, key) => {
168
169
  const schema = { dataType: types.DataType.ARRAY };
169
170
  const dtype = Reflect.getMetadata("design:type", target, key);
170
171
  const optional = dtype === model.Optional;
172
+ const fieldProp = (_b = props.fields.get(key)) !== null && _b !== void 0 ? _b : {};
171
173
  schema.options = { useOptionalObj: optional, optional };
172
- schema.description = (_b = name === null || name === void 0 ? void 0 : name.description) !== null && _b !== void 0 ? _b : "";
174
+ schema.description = (_c = name === null || name === void 0 ? void 0 : name.description) !== null && _c !== void 0 ? _c : "";
173
175
  if (type instanceof Number) {
174
176
  schema.itemType = types.DataType.DOUBLE;
175
177
  }
@@ -182,7 +184,10 @@ const ArrayField = (type, name) => (target, key) => {
182
184
  else if (type instanceof model.Model) {
183
185
  schema.options.recordClass = type;
184
186
  }
185
- const fieldProp = (_c = props.fields.get(key)) !== null && _c !== void 0 ? _c : {};
187
+ else if (Array.isArray(type)) {
188
+ fieldProp.isUnion = true;
189
+ fieldProp.unionTypes = type;
190
+ }
186
191
  fieldProp.name = key;
187
192
  fieldProp.Type = type;
188
193
  fieldProp.isArray = true;
@@ -1,4 +1,4 @@
1
- import { IgnoreProps, Model } from "./model";
1
+ import { IgnoreProps } from "./model";
2
2
  import { DataType } from "./types";
3
3
  /**
4
4
  * A property that exist in the incoming data
@@ -11,7 +11,7 @@ export declare const Field: (name?: string | {
11
11
  name: string;
12
12
  description: string;
13
13
  }, type?: any) => (target: any, key: string) => void;
14
- export declare const UnionField: <T extends new (...args: any[]) => Model>(types: T[], name?: string | {
14
+ export declare const UnionField: (types: any[], name?: string | {
15
15
  name: string;
16
16
  description: string;
17
17
  }) => (target: any, key: string) => void;
@@ -22,7 +22,7 @@ export declare const UnionField: <T extends new (...args: any[]) => Model>(types
22
22
  * property name
23
23
  * @constructor
24
24
  */
25
- export declare const ArrayField: (type: any, name?: string | {
25
+ export declare const ArrayField: (type: any | any[], name?: string | {
26
26
  name: string;
27
27
  description: string;
28
28
  }) => (target: any, key: string) => void;
@@ -120,6 +120,7 @@ const UnionField = (types, name) => (target, key) => {
120
120
  fieldProp.isUnion = true;
121
121
  fieldProp.schema = schema;
122
122
  fieldProp.unionTypes = types;
123
+ fieldProp.Type = {};
123
124
  props.fields.set(key, fieldProp);
124
125
  if (typeof name === "string" && name) {
125
126
  if (fieldProp.aliases) {
@@ -147,8 +148,9 @@ const ArrayField = (type, name) => (target, key) => {
147
148
  const schema = { dataType: DataType.ARRAY };
148
149
  const dtype = Reflect.getMetadata("design:type", target, key);
149
150
  const optional = dtype === Optional;
151
+ const fieldProp = (_b = props.fields.get(key)) !== null && _b !== void 0 ? _b : {};
150
152
  schema.options = { useOptionalObj: optional, optional };
151
- schema.description = (_b = name === null || name === void 0 ? void 0 : name.description) !== null && _b !== void 0 ? _b : "";
153
+ schema.description = (_c = name === null || name === void 0 ? void 0 : name.description) !== null && _c !== void 0 ? _c : "";
152
154
  if (type instanceof Number) {
153
155
  schema.itemType = DataType.DOUBLE;
154
156
  }
@@ -161,7 +163,10 @@ const ArrayField = (type, name) => (target, key) => {
161
163
  else if (type instanceof Model) {
162
164
  schema.options.recordClass = type;
163
165
  }
164
- const fieldProp = (_c = props.fields.get(key)) !== null && _c !== void 0 ? _c : {};
166
+ else if (Array.isArray(type)) {
167
+ fieldProp.isUnion = true;
168
+ fieldProp.unionTypes = type;
169
+ }
165
170
  fieldProp.name = key;
166
171
  fieldProp.Type = type;
167
172
  fieldProp.isArray = true;
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"gingersnap","version":"0.23.12","description":"A general purpose library built with the aim of filling the gaps of javascript shortcomings","dependencies":{"@msgpack/msgpack":"^3.0.0-beta2","browser-or-node":"^3.0.0-pre.0","modern-isomorphic-ws":"^1.0.5","object-hash":"^3.0.0","papaparse":"^5.4.1","protobufjs":"^7.2.6","ramda":"^0.30.1","reflect-metadata":"^0.2.2","tslib":"^2.6.3","uuid":"^9.0.1","ws":"^8.16.0","x2js":"^3.4.4"},"author":"CookieNerds LLC","exports":{"./synchronize":{"import":{"types":"./synchronize.d.ts","default":"./synchronize.mjs"},"require":{"types":"./synchronize.d.ts","default":"./synchronize.cjs"}},"./mocks":{"import":{"types":"./mocks.d.ts","default":"./mocks.mjs"},"require":{"types":"./mocks.d.ts","default":"./mocks.cjs"}},"./socket":{"import":{"types":"./socket.d.ts","default":"./socket.mjs"},"require":{"types":"./socket.d.ts","default":"./socket.cjs"}},"./typing":{"import":{"types":"./typing.d.ts","default":"./typing.mjs"},"require":{"types":"./typing.d.ts","default":"./typing.cjs"}},"./stream":{"import":{"types":"./stream/index.d.ts","default":"./stream.mjs"},"require":{"types":"./stream/index.d.ts","default":"./stream.cjs"}},"./reflection/injector":{"import":{"types":"./reflection/injector.d.ts","default":"./reflection/injector.mjs"},"require":{"types":"./reflection/injector.d.ts","default":"./reflection/injector.cjs"}},"./stream/call":{"import":{"types":"./stream/call.d.ts","default":"./stream/call.mjs"},"require":{"types":"./stream/call.d.ts","default":"./stream/call.cjs"}},"./stream/state":{"import":{"types":"./stream/state.d.ts","default":"./stream/state.mjs"},"require":{"types":"./stream/state.d.ts","default":"./stream/state.cjs"}},"./stream/collector":{"import":{"types":"./stream/collector.d.ts","default":"./stream/collector.mjs"},"require":{"types":"./stream/collector.d.ts","default":"./stream/collector.cjs"}},"./stream/observable":{"import":{"types":"./stream/observable.d.ts","default":"./stream/observable.mjs"},"require":{"types":"./stream/observable.d.ts","default":"./stream/observable.cjs"}},"./networking":{"import":{"types":"./networking/index.d.ts","default":"./networking.mjs"},"require":{"types":"./networking/index.d.ts","default":"./networking.cjs"}},"./managers":{"import":{"types":"./managers/index.d.ts","default":"./managers.mjs"},"require":{"types":"./managers/index.d.ts","default":"./managers.cjs"}},"./future":{"import":{"types":"./future/index.d.ts","default":"./future.mjs"},"require":{"types":"./future/index.d.ts","default":"./future.cjs"}},"./errors":{"import":{"types":"./errors/index.d.ts","default":"./errors.mjs"},"require":{"types":"./errors/index.d.ts","default":"./errors.cjs"}},"./data-structures/array":{"import":{"types":"./data-structures/array/index.d.ts","default":"./data-structures/array.mjs"},"require":{"types":"./data-structures/array/index.d.ts","default":"./data-structures/array.cjs"}},"./data-structures/object":{"import":{"types":"./data-structures/object/index.d.ts","default":"./data-structures/object.mjs"},"require":{"types":"./data-structures/object/index.d.ts","default":"./data-structures/object.cjs"}},"./data/decoders":{"import":{"types":"./data/decoders/index.d.ts","default":"./data/decoders.mjs"},"require":{"types":"./data/decoders/index.d.ts","default":"./data/decoders.cjs"}},"./data/model":{"import":{"types":"./data/model/index.d.ts","default":"./data/model.mjs"},"require":{"types":"./data/model/index.d.ts","default":"./data/model.cjs"}},"./data/bus":{"import":{"types":"./data/bus.d.ts","default":"./data/bus.mjs"},"require":{"types":"./data/bus.d.ts","default":"./data/bus.cjs"}},"./data/AtomicValue":{"import":{"types":"./data/AtomicValue.d.ts","default":"./data/AtomicValue.mjs"},"require":{"types":"./data/AtomicValue.d.ts","default":"./data/AtomicValue.cjs"}},"./data/store":{"import":{"types":"./data/store/index.d.ts","default":"./data/store.mjs"},"require":{"types":"./data/store/index.d.ts","default":"./data/store.cjs"}},"./functools":{"import":{"types":"./functools/index.d.ts","default":"./functools.mjs"},"require":{"types":"./functools/index.d.ts","default":"./functools.cjs"}}}}
1
+ {"name":"gingersnap","version":"0.23.13","description":"A general purpose library built with the aim of filling the gaps of javascript shortcomings","dependencies":{"@msgpack/msgpack":"^3.0.0-beta2","browser-or-node":"^3.0.0-pre.0","modern-isomorphic-ws":"^1.0.5","object-hash":"^3.0.0","papaparse":"^5.4.1","protobufjs":"^7.2.6","ramda":"^0.30.1","reflect-metadata":"^0.2.2","tslib":"^2.6.3","uuid":"^9.0.1","ws":"^8.16.0","x2js":"^3.4.4"},"author":"CookieNerds LLC","exports":{"./synchronize":{"import":{"types":"./synchronize.d.ts","default":"./synchronize.mjs"},"require":{"types":"./synchronize.d.ts","default":"./synchronize.cjs"}},"./mocks":{"import":{"types":"./mocks.d.ts","default":"./mocks.mjs"},"require":{"types":"./mocks.d.ts","default":"./mocks.cjs"}},"./socket":{"import":{"types":"./socket.d.ts","default":"./socket.mjs"},"require":{"types":"./socket.d.ts","default":"./socket.cjs"}},"./typing":{"import":{"types":"./typing.d.ts","default":"./typing.mjs"},"require":{"types":"./typing.d.ts","default":"./typing.cjs"}},"./stream":{"import":{"types":"./stream/index.d.ts","default":"./stream.mjs"},"require":{"types":"./stream/index.d.ts","default":"./stream.cjs"}},"./reflection/injector":{"import":{"types":"./reflection/injector.d.ts","default":"./reflection/injector.mjs"},"require":{"types":"./reflection/injector.d.ts","default":"./reflection/injector.cjs"}},"./stream/call":{"import":{"types":"./stream/call.d.ts","default":"./stream/call.mjs"},"require":{"types":"./stream/call.d.ts","default":"./stream/call.cjs"}},"./stream/state":{"import":{"types":"./stream/state.d.ts","default":"./stream/state.mjs"},"require":{"types":"./stream/state.d.ts","default":"./stream/state.cjs"}},"./stream/collector":{"import":{"types":"./stream/collector.d.ts","default":"./stream/collector.mjs"},"require":{"types":"./stream/collector.d.ts","default":"./stream/collector.cjs"}},"./stream/observable":{"import":{"types":"./stream/observable.d.ts","default":"./stream/observable.mjs"},"require":{"types":"./stream/observable.d.ts","default":"./stream/observable.cjs"}},"./networking":{"import":{"types":"./networking/index.d.ts","default":"./networking.mjs"},"require":{"types":"./networking/index.d.ts","default":"./networking.cjs"}},"./managers":{"import":{"types":"./managers/index.d.ts","default":"./managers.mjs"},"require":{"types":"./managers/index.d.ts","default":"./managers.cjs"}},"./future":{"import":{"types":"./future/index.d.ts","default":"./future.mjs"},"require":{"types":"./future/index.d.ts","default":"./future.cjs"}},"./errors":{"import":{"types":"./errors/index.d.ts","default":"./errors.mjs"},"require":{"types":"./errors/index.d.ts","default":"./errors.cjs"}},"./data-structures/array":{"import":{"types":"./data-structures/array/index.d.ts","default":"./data-structures/array.mjs"},"require":{"types":"./data-structures/array/index.d.ts","default":"./data-structures/array.cjs"}},"./data-structures/object":{"import":{"types":"./data-structures/object/index.d.ts","default":"./data-structures/object.mjs"},"require":{"types":"./data-structures/object/index.d.ts","default":"./data-structures/object.cjs"}},"./data/decoders":{"import":{"types":"./data/decoders/index.d.ts","default":"./data/decoders.mjs"},"require":{"types":"./data/decoders/index.d.ts","default":"./data/decoders.cjs"}},"./data/model":{"import":{"types":"./data/model/index.d.ts","default":"./data/model.mjs"},"require":{"types":"./data/model/index.d.ts","default":"./data/model.cjs"}},"./data/bus":{"import":{"types":"./data/bus.d.ts","default":"./data/bus.mjs"},"require":{"types":"./data/bus.d.ts","default":"./data/bus.cjs"}},"./data/AtomicValue":{"import":{"types":"./data/AtomicValue.d.ts","default":"./data/AtomicValue.mjs"},"require":{"types":"./data/AtomicValue.d.ts","default":"./data/AtomicValue.cjs"}},"./data/store":{"import":{"types":"./data/store/index.d.ts","default":"./data/store.mjs"},"require":{"types":"./data/store/index.d.ts","default":"./data/store.cjs"}},"./functools":{"import":{"types":"./functools/index.d.ts","default":"./functools.mjs"},"require":{"types":"./functools/index.d.ts","default":"./functools.cjs"}}}}