gingersnap 0.23.11 → 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.
@@ -73,14 +73,9 @@ class Optional {
73
73
  * A Data de/serializer class that manages and validates data as JavaScript Objects
74
74
  */
75
75
  class Model {
76
- /**
77
- * Converts arraybuffer to a model
78
- * @param data arraybuffer
79
- * @param format data format
80
- * @param options configurations for deserializing the data
81
- * @returns new model or an array of models
82
- */
83
- static fromBuffer(data, format = types.DataFormat.JSON, options) {
76
+ static fromBuffer(data, options) {
77
+ var _a;
78
+ const format = (_a = options === null || options === void 0 ? void 0 : options.format) !== null && _a !== void 0 ? _a : types.DataFormat.JSON;
84
79
  switch (format) {
85
80
  case types.DataFormat.MESSAGE_PACK: {
86
81
  const decoder = msgpack.decode;
@@ -89,27 +84,18 @@ class Model {
89
84
  throw new ParsingError.ParsingError([], "expected an array of models");
90
85
  if ((!(options === null || options === void 0 ? void 0 : options.array) && result instanceof Array) || Array.isArray(result))
91
86
  throw new ParsingError.ParsingError([], "expected only one model");
92
- if (result instanceof Array || Array.isArray(result))
93
- return result.map((v) => this.fromJSON(v));
94
- return this.fromJSON(result);
87
+ return this.fromJSON(result, { array: options === null || options === void 0 ? void 0 : options.array, maybeArray: options === null || options === void 0 ? void 0 : options.maybeArray });
95
88
  }
96
89
  case types.DataFormat.CSV: {
97
90
  const text = data instanceof Uint8Array ? new TextDecoder().decode(data.buffer) : data.toString();
98
- return this.fromString(text, format, options);
91
+ return this.fromString(text, options);
99
92
  }
100
93
  case types.DataFormat.XML:
101
94
  case types.DataFormat.JSON:
102
- return this.fromString(data instanceof Uint8Array ? new TextDecoder().decode(data) : data.toString(), format, options);
95
+ return this.fromString(data instanceof Uint8Array ? new TextDecoder().decode(data) : data.toString(), options);
103
96
  }
104
97
  }
105
- /**
106
- * Downloads data from the given source and deserializes the data to one or more models
107
- * @param source URL source to fetch the data
108
- * @param format data format
109
- * @param options configurations for deserializing and/or retrieving the data
110
- * @returns one or more models
111
- */
112
- static fromURL(source, format = types.DataFormat.JSON, options) {
98
+ static fromURL(source, options) {
113
99
  return _tslib.__awaiter(this, void 0, void 0, function* () {
114
100
  const fetcher = fetch;
115
101
  const resp = yield fetcher(source, {
@@ -119,48 +105,37 @@ class Model {
119
105
  });
120
106
  if (!resp.ok)
121
107
  throw new NetworkError.NetworkError(resp.status);
122
- return yield this.fromBlob(yield resp.blob(), format, options);
108
+ return yield this.fromBlob(yield resp.blob(), options);
123
109
  });
124
110
  }
125
- /**
126
- * Deserializes one or more models from a Blob
127
- * @param data blob data
128
- * @param format data format
129
- * @param options configurations for deserializing the data
130
- * @returns one or more models
131
- */
132
- static fromBlob(data, format = types.DataFormat.JSON, options) {
111
+ static fromBlob(data, options) {
133
112
  return _tslib.__awaiter(this, void 0, void 0, function* () {
134
- return this.fromBuffer(new Uint8Array(yield data.arrayBuffer()), format);
113
+ return this.fromBuffer(new Uint8Array(yield data.arrayBuffer()), options);
135
114
  });
136
115
  }
137
- /**
138
- * Deserializes one or more models from given string
139
- *
140
- * @remarks
141
- * If the data format is a binary one, the provided string should be a hexadecimal string.
142
- * @param data string data source
143
- * @param format data format
144
- * @param options
145
- */
146
- static fromString(data, format = types.DataFormat.JSON, options) {
147
- var _a, _b, _c, _d, _e, _f;
116
+ static fromString(data, options) {
117
+ var _a, _b, _c, _d, _e, _f, _g;
118
+ const format = (_a = options === null || options === void 0 ? void 0 : options.format) !== null && _a !== void 0 ? _a : types.DataFormat.JSON;
148
119
  switch (format) {
149
120
  case types.DataFormat.JSON:
150
121
  try {
151
122
  data = JSON.parse(data);
152
123
  }
153
124
  catch (e) {
154
- throw new ParsingError.ParsingError((_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : String(e));
125
+ throw new ParsingError.ParsingError((_b = e === null || e === void 0 ? void 0 : e.message) !== null && _b !== void 0 ? _b : String(e));
155
126
  }
156
- return this.fromJSON(data);
127
+ return this.fromJSON(data, { maybeArray: options === null || options === void 0 ? void 0 : options.maybeArray, array: options === null || options === void 0 ? void 0 : options.array });
157
128
  case types.DataFormat.XML:
158
- return this.fromObject(this.__xmlParser__.xml2js(data), format);
129
+ return this.fromObject(this.__xmlParser__.xml2js(data), {
130
+ format,
131
+ maybeArray: options === null || options === void 0 ? void 0 : options.maybeArray,
132
+ array: options === null || options === void 0 ? void 0 : options.array,
133
+ });
159
134
  case types.DataFormat.CSV: {
160
135
  let result;
161
136
  let text = data;
162
137
  if (options === null || options === void 0 ? void 0 : options.headers)
163
- text = options.headers.join((_b = options === null || options === void 0 ? void 0 : options.delimiter) !== null && _b !== void 0 ? _b : ",") + ((_c = options.newline) !== null && _c !== void 0 ? _c : "\n") + text;
138
+ text = options.headers.join((_c = options === null || options === void 0 ? void 0 : options.delimiter) !== null && _c !== void 0 ? _c : ",") + ((_d = options.newline) !== null && _d !== void 0 ? _d : "\n") + text;
164
139
  try {
165
140
  result = Papa.parse(text, {
166
141
  header: true,
@@ -168,36 +143,34 @@ class Model {
168
143
  });
169
144
  }
170
145
  catch (e) {
171
- throw new ParsingError.ParsingError([], (_d = e === null || e === void 0 ? void 0 : e.message) !== null && _d !== void 0 ? _d : String(e));
146
+ throw new ParsingError.ParsingError([], (_e = e === null || e === void 0 ? void 0 : e.message) !== null && _e !== void 0 ? _e : String(e));
172
147
  }
173
148
  if (!(options === null || options === void 0 ? void 0 : options.ignoreErrors) && result.errors.length > 0)
174
149
  throw new ParsingError.ParsingError(result.errors);
175
150
  if (!(options === null || options === void 0 ? void 0 : options.array) && result.data.length > 0)
176
151
  throw new ParsingError.ParsingError([], "Too many records found");
177
- if (options === null || options === void 0 ? void 0 : options.array)
178
- return result.data.map((v) => this.fromObject(v));
179
- return this.fromObject(result.data[0]);
152
+ if ((options === null || options === void 0 ? void 0 : options.array) || (options === null || options === void 0 ? void 0 : options.maybeArray))
153
+ return this.fromObject(result.data, { array: true });
154
+ if (!(options === null || options === void 0 ? void 0 : options.maybeArray) || (options.maybeArray && result.data.length === 1))
155
+ return this.fromObject(result.data[0]);
156
+ break;
180
157
  }
181
158
  default:
182
- return this.fromBuffer(new Uint8Array((_f = (_e = data.match(/../g)) === null || _e === void 0 ? void 0 : _e.map((h) => parseInt(h, 16))) !== null && _f !== void 0 ? _f : []), format, options);
159
+ return this.fromBuffer(new Uint8Array((_g = (_f = data.match(/../g)) === null || _f === void 0 ? void 0 : _f.map((h) => parseInt(h, 16))) !== null && _g !== void 0 ? _g : []), options);
183
160
  }
184
161
  }
185
- /**
186
- * Converts a JSON object to a model
187
- * @param data JSON Object
188
- * @returns new model
189
- */
190
- static fromJSON(data) {
191
- return this.fromObject(data, types.DataFormat.JSON);
162
+ static fromJSON(data, options) {
163
+ return this.fromObject(data, options);
192
164
  }
193
165
  /**
194
166
  * Converts the current model to a JSON object
195
167
  */
196
- object(removeMissingFields = false) {
168
+ object(options) {
197
169
  var _a, _b;
170
+ const { removeMissingFields = false, keepOptionals = true } = options || {};
198
171
  const serialize = (value) => {
199
172
  if (value instanceof Model) {
200
- return value.object(removeMissingFields);
173
+ return value.object({ removeMissingFields, keepOptionals });
201
174
  }
202
175
  else if (value instanceof Array || Array.isArray(value)) {
203
176
  return value.map((v) => serialize(v));
@@ -217,6 +190,13 @@ class Model {
217
190
  else if (value instanceof Function) {
218
191
  return null;
219
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
+ }
220
200
  return value;
221
201
  };
222
202
  return R__namespace.reduce((acc, [key, fieldProps]) => {
@@ -234,42 +214,42 @@ class Model {
234
214
  /**
235
215
  * Converts the current model to a JSON string
236
216
  */
237
- json(removeMissingFields = false) {
238
- return JSON.stringify(this.object(removeMissingFields));
217
+ json(options) {
218
+ return JSON.stringify(this.object(options));
239
219
  }
240
220
  /**
241
221
  * Converts the current model to an arraybuffer
242
222
  */
243
- buffer(removeMissingFields = false) {
244
- return new TextEncoder().encode(this.json(removeMissingFields));
223
+ buffer(options) {
224
+ return new TextEncoder().encode(this.json(options));
245
225
  }
246
226
  /**
247
227
  * Converts the current model to a blob
248
228
  */
249
- blob(removeMissingFields = false) {
250
- return new Blob([this.buffer(removeMissingFields)]);
229
+ blob(options) {
230
+ return new Blob([this.buffer(options)]);
251
231
  }
252
232
  /**
253
233
  * Converts the current model to an XML string
254
234
  */
255
- xml(removeMissingFields = false) {
256
- return Model.__xmlParser__.js2xml(this.object(removeMissingFields));
235
+ xml(options) {
236
+ return Model.__xmlParser__.js2xml(this.object(options));
257
237
  }
258
238
  /**
259
239
  * Converts the current model to message pack binary format
260
240
  * @returns message pack binary
261
241
  */
262
- messagePack(removeMissingFields = false) {
263
- return msgpack.encode(this.object(removeMissingFields)).buffer;
242
+ messagePack(options) {
243
+ return msgpack.encode(this.object(options)).buffer;
264
244
  }
265
245
  /**
266
246
  * Converts the current model to csv format
267
- * @param removeMissingFields
247
+ * @param options
268
248
  * @param config CSV unparsing configuration
269
249
  * @returns csv string
270
250
  */
271
- csv(removeMissingFields = false, config) {
272
- return Papa.unparse([this.object(removeMissingFields)], config);
251
+ csv(options, config) {
252
+ return Papa.unparse([this.object(options)], config);
273
253
  }
274
254
  clone() {
275
255
  const newModel = new (Object.getPrototypeOf(this))();
@@ -280,10 +260,10 @@ class Model {
280
260
  }
281
261
  /**
282
262
  * Retrieves the schema of the current model based on the selected format
283
- * @param format data format
263
+ * @param includeTitleAndDraft boolean whether the title and JSON schema draft version should be referenced in the schema
284
264
  * @returns the schema for the current model
285
265
  */
286
- static schema(addTitleAndDraftRef = true) {
266
+ static schema(includeTitleAndDraft = true) {
287
267
  const model = new this();
288
268
  const props = this.buildPropTree(Object.getPrototypeOf(model));
289
269
  const handleArrays = (v, entry) => {
@@ -375,24 +355,38 @@ class Model {
375
355
  }
376
356
  return entry;
377
357
  }));
378
- if (addTitleAndDraftRef)
379
- return {
380
- $schema: "https://json-schema.org/draft/2020-12/schema",
381
- 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
+ ? {
382
371
  type: "object",
383
372
  properties: generateProperties(),
384
373
  additionalProperties: false,
385
- };
386
- else {
387
- return {
374
+ required,
375
+ }
376
+ : {
388
377
  type: "object",
389
378
  properties: generateProperties(),
390
379
  additionalProperties: false,
391
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;
392
386
  }
393
387
  }
394
388
  toString() {
395
- return this.json(true);
389
+ return this.json({ removeMissingFields: true, keepOptionals: false });
396
390
  }
397
391
  /**
398
392
  * Constructs the model properties by traversing the inheritance tree of the current Model being instantiated
@@ -421,15 +415,17 @@ class Model {
421
415
  }
422
416
  return tree;
423
417
  }
424
- /**
425
- * Constructs a model from a JSON object
426
- * @param data JSON object
427
- * @param format
428
- * @private
429
- */
430
- static fromObject(data, format = types.DataFormat.JSON) {
431
- if (data instanceof Array || Array.isArray(data) || typeof data !== "object") {
432
- throw new ParsingError.ParsingError([], "Invalid data provided. Must be an object");
418
+ static fromObject(data, options) {
419
+ var _a;
420
+ const format = (_a = options === null || options === void 0 ? void 0 : options.format) !== null && _a !== void 0 ? _a : types.DataFormat.JSON;
421
+ if (Array.isArray(data)) {
422
+ if (!(options === null || options === void 0 ? void 0 : options.array) || !(options === null || options === void 0 ? void 0 : options.maybeArray))
423
+ throw new ParsingError.ParsingError([], "Invalid data provided. Must be an object");
424
+ else
425
+ return data.map((item) => this.fromObject(item, { format }));
426
+ }
427
+ else if (options === null || options === void 0 ? void 0 : options.array) {
428
+ throw new ParsingError.ParsingError([], "Invalid data provided. Must be an array of objects");
433
429
  }
434
430
  const processValue = (fieldProps, value) => {
435
431
  var _a, _b;
@@ -441,7 +437,7 @@ class Model {
441
437
  const model = new this();
442
438
  const props = this.buildPropTree(Object.getPrototypeOf(model));
443
439
  R__namespace.forEach(([key, fieldProps]) => {
444
- 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;
445
441
  let value = data[key];
446
442
  if (value === undefined || value === null) {
447
443
  for (const alias of (_a = fieldProps.aliases) !== null && _a !== void 0 ? _a : []) {
@@ -462,12 +458,101 @@ class Model {
462
458
  else if ((value === undefined || value === null) && !(model[key] === undefined || model[key] === null)) {
463
459
  return;
464
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
+ }
465
540
  if ((_e = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(model), key)) === null || _e === void 0 ? void 0 : _e.set) {
466
541
  model[key] = processValue(fieldProps, value);
467
542
  }
468
543
  else if ((_f = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(model), key)) === null || _f === void 0 ? void 0 : _f.get) {
469
544
  return;
470
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
+ }
471
556
  else if (model[key] instanceof fieldProps.Type &&
472
557
  typeof model[key] === "function" &&
473
558
  fieldProps.Type instanceof Function) {
@@ -577,51 +662,8 @@ class Model {
577
662
  }
578
663
  }));
579
664
  }
580
- else if (((_k = fieldProps.Type) === null || _k === void 0 ? void 0 : _k.name) === "String") {
581
- model[key] = processValue(fieldProps, String(value));
582
- }
583
- else if (((_l = fieldProps.Type) === null || _l === void 0 ? void 0 : _l.name) === "Boolean") {
584
- model[key] = processValue(fieldProps, Boolean(value));
585
- }
586
- else if (((_m = fieldProps.Type) === null || _m === void 0 ? void 0 : _m.name) === "Number") {
587
- model[key] = processValue(fieldProps, Number(value));
588
- }
589
- else if (((_o = fieldProps.Type) === null || _o === void 0 ? void 0 : _o.name) === "Map") {
590
- const valueType = typeof value;
591
- switch (valueType) {
592
- case "object": {
593
- try {
594
- model[key] = processValue(fieldProps, value instanceof Array || Array.isArray(value) ? new Map(value) : new Map(Object.entries(value)));
595
- }
596
- catch (e) {
597
- throw new ParsingError.ParsingError([], (_p = e === null || e === void 0 ? void 0 : e.message) !== null && _p !== void 0 ? _p : String(e));
598
- }
599
- break;
600
- }
601
- case "string":
602
- try {
603
- value = JSON.parse(value);
604
- model[key] = processValue(fieldProps, value instanceof Array || Array.isArray(value) ? new Map(value) : new Map(Object.entries(value)));
605
- }
606
- catch (e) {
607
- throw new ParsingError.ParsingError([], (_q = e === null || e === void 0 ? void 0 : e.message) !== null && _q !== void 0 ? _q : String(e));
608
- }
609
- break;
610
- default:
611
- throw new ParsingError.ParsingError([value], `value for ${key} cannot be converted to a Map`);
612
- }
613
- }
614
- else if (((_r = fieldProps.Type) === null || _r === void 0 ? void 0 : _r.name) === "Set") {
615
- if (value instanceof Array || Array.isArray(value)) {
616
- model[key] = processValue(fieldProps, new Set(value));
617
- }
618
- else if (typeof value === "string") {
619
- model[key] = processValue(fieldProps, new Set(JSON.parse(value)));
620
- }
621
- throw new ParsingError.ParsingError([value], `value for ${key} cannot be converted to a Set`);
622
- }
623
665
  else {
624
- model[key] = processValue(fieldProps, new fieldProps.Type(value));
666
+ model[key] = checkOtherTypes(value, fieldProps.Type, () => processValue(fieldProps, new fieldProps.Type(value)));
625
667
  }
626
668
  if (fieldProps.readonly) {
627
669
  Object.defineProperty(model, key, {
@@ -633,7 +675,7 @@ class Model {
633
675
  R__namespace.forEach(([k, v]) => {
634
676
  if (v.__callback__)
635
677
  v.__callback__(k, v.properties, model, key);
636
- }, R__namespace.toPairs((_s = fieldProps.customTags) !== null && _s !== void 0 ? _s : {}));
678
+ }, R__namespace.toPairs((_k = fieldProps.customTags) !== null && _k !== void 0 ? _k : {}));
637
679
  }, Array.from(props.fields.entries()));
638
680
  return Object.seal(model);
639
681
  }