@es-joy/jsoe 0.0.1

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 (43) hide show
  1. package/.editorconfig +15 -0
  2. package/.eslintignore +7 -0
  3. package/.eslintrc.cjs +120 -0
  4. package/CHANGES.md +5 -0
  5. package/LICENSE-MIT.txt +22 -0
  6. package/README.md +82 -0
  7. package/package.json +101 -0
  8. package/rollup.config.js +29 -0
  9. package/server.js +25 -0
  10. package/src/deepEqual.js +72 -0
  11. package/src/formats/indexedDBKey.js +66 -0
  12. package/src/formats/json.js +64 -0
  13. package/src/formats/schemaAndArbitrary.js +20 -0
  14. package/src/formats/schemaOnly.js +529 -0
  15. package/src/formats/structuredCloning.js +372 -0
  16. package/src/formats.js +229 -0
  17. package/src/fundamentalTypes/BooleanObjectType.js +55 -0
  18. package/src/fundamentalTypes/NumberObjectType.js +41 -0
  19. package/src/fundamentalTypes/StringObjectType.js +37 -0
  20. package/src/fundamentalTypes/arrayReferenceType.js +203 -0
  21. package/src/fundamentalTypes/arrayType.js +898 -0
  22. package/src/fundamentalTypes/bigintType.js +53 -0
  23. package/src/fundamentalTypes/dateType.js +129 -0
  24. package/src/fundamentalTypes/nullType.js +33 -0
  25. package/src/fundamentalTypes/numberType.js +56 -0
  26. package/src/fundamentalTypes/objectReferenceType.js +51 -0
  27. package/src/fundamentalTypes/objectType.js +30 -0
  28. package/src/fundamentalTypes/regexpType.js +95 -0
  29. package/src/fundamentalTypes/sparseUndefinedType.js +43 -0
  30. package/src/fundamentalTypes/stringType.js +31 -0
  31. package/src/fundamentalTypes/undefinedType.js +37 -0
  32. package/src/index.js +7 -0
  33. package/src/subTypes/blobHTMLType.js +160 -0
  34. package/src/subTypes/falseType.js +42 -0
  35. package/src/subTypes/trueType.js +42 -0
  36. package/src/superTypes/InfinitiesSuperType.js +49 -0
  37. package/src/superTypes/SpecialNumberSuperType.js +62 -0
  38. package/src/typeChoices.js +125 -0
  39. package/src/types.js +665 -0
  40. package/src/utils/dialogs.js +115 -0
  41. package/src/utils/jsonPointer.js +89 -0
  42. package/src/utils/templateUtils.js +106 -0
  43. package/src/utils/types.js +6 -0
package/src/types.js ADDED
@@ -0,0 +1,665 @@
1
+ import {jml} from '../node_modules/jamilih/dist/jml-es.js';
2
+ import {
3
+ Typeson, getJSONType, structuredCloningThrowing
4
+ } from '../node_modules/typeson-registry/dist/index.js';
5
+
6
+ import Formats from './formats.js';
7
+
8
+ import {$e, $$e} from './utils/templateUtils.js';
9
+
10
+ import nullType from './fundamentalTypes/nullType.js';
11
+ import trueType from './subTypes/trueType.js';
12
+ import falseType from './subTypes/falseType.js';
13
+ import blobHTMLType from './subTypes/blobHTMLType.js';
14
+ import numberType from './fundamentalTypes/numberType.js';
15
+ import bigintType from './fundamentalTypes/bigintType.js';
16
+ import stringType from './fundamentalTypes/stringType.js';
17
+ import arrayReferenceType from './fundamentalTypes/arrayReferenceType.js';
18
+ import objectReferenceType from './fundamentalTypes/objectReferenceType.js';
19
+ import arrayType from './fundamentalTypes/arrayType.js';
20
+ import objectType from './fundamentalTypes/objectType.js';
21
+ import dateType from './fundamentalTypes/dateType.js';
22
+ import undefinedType from './fundamentalTypes/undefinedType.js';
23
+ import regexpType from './fundamentalTypes/regexpType.js';
24
+ import BooleanObjectType from './fundamentalTypes/BooleanObjectType.js';
25
+ import NumberObjectType from './fundamentalTypes/NumberObjectType.js';
26
+ import StringObjectType from './fundamentalTypes/StringObjectType.js';
27
+ import sparseUndefinedType from './fundamentalTypes/sparseUndefinedType.js';
28
+ import InfinitiesSuperType from './superTypes/InfinitiesSuperType.js';
29
+ import SpecialNumberSuperType from './superTypes/SpecialNumberSuperType.js';
30
+
31
+ /**
32
+ * @param {HTMLLegendElement} legend
33
+ * @returns {string}
34
+ */
35
+ export const getPropertyValueFromLegend = (legend) => {
36
+ const propElem = $e(legend, '*[data-prop="true"]');
37
+ return propElem.nodeName.toLowerCase() === 'input'
38
+ ? propElem.value
39
+ : String(Number.parseInt(propElem.textContent) - 1); // 1-based to 0-based
40
+ };
41
+
42
+ const Types = {};
43
+
44
+ /**
45
+ * @typedef {object} TypeObject
46
+ * @property {JamilihArray} option Creates the option HTML. May set an option
47
+ * `title` or `value`
48
+ * @property {boolean} [array] Private context variable. Whether or not
49
+ * it is an array. Do not use in other types.
50
+ * @property {string[]} [regexEndings] Used for string parsing.
51
+ * @property {RegExp} [stringRegex] Used for string parsing. If not
52
+ * present, use `stringRegexBegin` and `stringRegexEnd`
53
+ * @property {RegExp} [stringRegexBegin] Used for string parsing. If not
54
+ * present, use `stringRegex`
55
+ * @property {RegExp} [stringRegexEnd] Used for string parsing. If not
56
+ * present, use `stringRegex`
57
+ * @property {
58
+ * (ArbitraryValue) => boolean
59
+ * } [valueMatch] Function to check whether this subtype matches
60
+ * @property {string} [superType] The greater fundamental type to which
61
+ * the type belongs
62
+ * @property {(s: string) => ArbitraryValue} toValue Converts from
63
+ * string to value. May use `stringRegex` to find components.
64
+ * @property {
65
+ * (info: {root?: HTMLDivElement}) => ArbitraryValue
66
+ * } getValue Gets the value for the type
67
+ * @property {
68
+ * (info: {root?: HTMLDivElement}) => void
69
+ * } [setValue] Should set the value of the form's `getInput` element
70
+ * @property {
71
+ * (info: {
72
+ * value?: ArbitraryValue,
73
+ * typeNamespace?: string,
74
+ * type?: string,
75
+ * topRoot?: HTMLDivElement,
76
+ * resultType?: "keys"|"values"|"both",
77
+ * format?: string
78
+ * }) => JamilihArray
79
+ * } viewUI
80
+ * @property {
81
+ * (info: {
82
+ * value?: ArbitraryValue,
83
+ * typeNamespace?: string,
84
+ * }) => JamilihArray
85
+ * } editUI
86
+ * @property {
87
+ * (info: {root: HTMLDivElement}) =>
88
+ * HTMLInputElement|HTMLTextareaElement|HTMLSelectElement
89
+ * } getInput Gets the form control (with `value`)
90
+ * @property {
91
+ * (path: string, value: ArbitraryValue) => ArbitraryValue
92
+ * } [resolveReference] Gets the reference. For array and object
93
+ * references types only
94
+ * @property {(info: {root: HTMLDivElement, topRoot?: HTMLDivElement}) => {
95
+ * message: string,
96
+ * valid: boolean
97
+ * }} [validate] Message will be used if validity is false.
98
+ * @property {(info: {topRoot: HTMLDivElement}) => void} [validateAll] For
99
+ * validation of array and object references only.
100
+ * @property {{
101
+ * structuredCloning: {
102
+ * after: string,
103
+ * contexts: string[]
104
+ * }
105
+ * }} [stateDependent] The type after which it should be placed and its
106
+ * context types
107
+ */
108
+
109
+ Types.availableTypes = {
110
+ null: nullType,
111
+ true: trueType,
112
+ false: falseType,
113
+ number: numberType,
114
+ bigint: bigintType,
115
+ string: stringType,
116
+ arrayReference: arrayReferenceType,
117
+ objectReference: objectReferenceType,
118
+ array: arrayType,
119
+ // Note: We don't do for BooleanObject/NumberObject/StringObject, date,
120
+ // regexp, as added properties on them are not being cloned (in Chrome
121
+ // at least)
122
+ object: objectType,
123
+ date: dateType,
124
+
125
+ // This type is only for throwing upon cloning errors:
126
+ // 'checkDataCloneException'
127
+ // This type might be supported by evaluable JS or config
128
+ // passed in:
129
+ userObject: ['User objects'],
130
+ undef: undefinedType,
131
+ Infinities: InfinitiesSuperType,
132
+ SpecialNumber: SpecialNumberSuperType,
133
+
134
+ regexp: regexpType,
135
+ BooleanObject: BooleanObjectType,
136
+ NumberObject: NumberObjectType,
137
+ StringObject: StringObjectType,
138
+
139
+ map: {
140
+ option: ['Map']
141
+ },
142
+ set: {
143
+ option: ['Set']
144
+ },
145
+
146
+ file: {
147
+ option: ['File']
148
+ },
149
+ filelist: {
150
+ option: ['FileList']
151
+ },
152
+ blobHTML: blobHTMLType,
153
+ arraybuffer: {
154
+ option: ['ArrayBuffer']
155
+ },
156
+ arraybufferview: {
157
+ option: ['ArrayBufferView']
158
+ },
159
+ dataview: {
160
+ option: ['DataView']
161
+ },
162
+ imagedata: {
163
+ option: ['ImageData']
164
+ },
165
+ imagebitmap: {
166
+ option: ['ImageBitmap']
167
+ },
168
+
169
+ // Typed Arrays
170
+ int8array: {
171
+ option: ['Int8Array']
172
+ },
173
+ uint8array: {
174
+ option: ['Uint8Array']
175
+ },
176
+ uint8clampedarray: {
177
+ option: ['Uint8ClampedArray']
178
+ },
179
+ int16array: {
180
+ option: ['Int16Array']
181
+ },
182
+ uint16array: {
183
+ option: ['Uint16Array']
184
+ },
185
+ int32array: {
186
+ option: ['Int32Array']
187
+ },
188
+ uint32array: {
189
+ option: ['Uint32Array']
190
+ },
191
+ float32array: {
192
+ option: ['Float32Array']
193
+ },
194
+ float64array: {
195
+ option: ['Float64Array']
196
+ },
197
+
198
+ // Intl (imperfect)
199
+ IntlCollator: {
200
+ option: ['Intl.Collator']
201
+ },
202
+ IntlDateTimeFormat: {
203
+ option: ['Intl.DateTimeFormat']
204
+ },
205
+ IntlNumberFormat: {
206
+ option: ['Intl.NumberFormat']
207
+ },
208
+
209
+ // We're catching this instead of using this
210
+ sparseUndefined: sparseUndefinedType
211
+ };
212
+ Types.availableTypes.ValidDate = {
213
+ valid: true
214
+ };
215
+ /*
216
+ Types.availableTypes.sparseArrays = {
217
+ sparse: true
218
+ };
219
+ */
220
+ Types.availableTypes.arrayNonindexKeys = {
221
+ sparse: true
222
+ };
223
+
224
+ /**
225
+ * @param {[copyTo: string, copyFrom: string]} replacements
226
+ * @returns {void}
227
+ */
228
+ const copyTypeObjs = (replacements) => {
229
+ replacements.forEach(([copyFrom, copyTo]) => {
230
+ Object.assign(Types.availableTypes[copyTo], Types.availableTypes[copyFrom]);
231
+ });
232
+ };
233
+ copyTypeObjs([
234
+ ['date', 'ValidDate'],
235
+ [
236
+ 'array',
237
+ 'arrayNonindexKeys'
238
+ // 'sparseArrays'
239
+ ]
240
+ ]);
241
+
242
+ /**
243
+ * @public
244
+ * @param {RootElement} root
245
+ * @returns {string|boolean} Why would it not exist?
246
+ */
247
+ Types.getTypeForRoot = (root) => {
248
+ return root && root.dataset.type;
249
+ };
250
+
251
+ /**
252
+ * @public
253
+ * @param {RootElement} root
254
+ * @param {StateObject} stateObj
255
+ * @param {string} currentPath
256
+ * @returns {StructuredCloneValue}
257
+ */
258
+ Types.getValueForRoot = (root, stateObj, currentPath) => {
259
+ return Types.availableTypes[Types.getTypeForRoot(root)].getValue({
260
+ root, stateObj, currentPath
261
+ });
262
+ };
263
+
264
+ /**
265
+ * @public
266
+ * @param {RootElement} root
267
+ * @returns {null|HTMLInputElement}
268
+ */
269
+ Types.getFormControlForRoot = (root) => {
270
+ const typeObj = Types.availableTypes[Types.getTypeForRoot(root)];
271
+ /* istanbul ignore if -- All have except aliases */
272
+ if (!typeObj.getInput) {
273
+ return null;
274
+ }
275
+ return typeObj.getInput({root});
276
+ };
277
+
278
+ /**
279
+ * @public
280
+ * @param {string|Element} selOrEl
281
+ * @param {StateObject} stateObj
282
+ * @returns {StructuredCloneValue}
283
+ */
284
+ Types.getValueFromRootAncestor = (selOrEl, stateObj) => {
285
+ return Types.getValueForRoot($e(selOrEl, 'div[data-type]'), stateObj);
286
+ };
287
+
288
+ /**
289
+ * @public
290
+ * @param {string|Element} selOrEl
291
+ * @returns {null|HTMLInputElement}
292
+ */
293
+ Types.getFormControlFromRootAncestor = (selOrEl) => {
294
+ const root = $e(selOrEl, 'div[data-type]');
295
+ if (!root) {
296
+ return null;
297
+ }
298
+ return Types.getFormControlForRoot(root);
299
+ };
300
+
301
+ /**
302
+ * @param {string} format
303
+ * @param {string} state
304
+ * @returns {string[]}
305
+ */
306
+ Types.getTypesForFormatAndState = (
307
+ format, state
308
+ ) => Formats.availableFormats[format].getTypesForState(state);
309
+
310
+ /**
311
+ * @public
312
+ * @param {string} type
313
+ * @returns {JamilihArray}
314
+ */
315
+ Types.getOptionForType = (type) => {
316
+ const optInfo = [...Types.availableTypes[type].option];
317
+ optInfo[1] = {value: type, ...optInfo[1]};
318
+ return optInfo;
319
+ };
320
+
321
+ /**
322
+ * @public
323
+ * @param {string} format
324
+ * @param {string} parserState
325
+ * @returns {JamilihArray[]}
326
+ */
327
+ Types.getTypeOptionsForFormatAndState = (format, parserState) => {
328
+ const typesForFormatAndState = Types.getTypesForFormatAndState(
329
+ format, parserState
330
+ );
331
+ return typesForFormatAndState.map((type) => {
332
+ return Types.getOptionForType(type);
333
+ });
334
+ };
335
+
336
+ /**
337
+ * @public
338
+ * @param {object} cfg
339
+ * @param {boolean} cfg."readonly"
340
+ * @param {"both"|"keys"|"values"} cfg.resultType
341
+ * @param {string} cfg.typeNamespace
342
+ * @param {string} cfg.type
343
+ * @param {RootElement} cfg.topRoot
344
+ * @param {boolean} cfg.bringIntoFocus
345
+ * @param {BuildTypeChoices} cfg.buildTypeChoices
346
+ * @param {string} cfg.format
347
+ * @param {string} cfg.schemaContent
348
+ * @param {StateObject} cfg.schemaState Not currently in use and may
349
+ * need to change the type
350
+ * @param {StructuredCloneValue} cfg.value
351
+ * @param {boolean} cfg.hasValue
352
+ * @returns {Element}
353
+ */
354
+ Types.getUIForModeAndType = ({
355
+ readonly, resultType, typeNamespace, type, topRoot, bringIntoFocus,
356
+ buildTypeChoices, format, schemaContent, schemaState, value, hasValue
357
+ }) => {
358
+ const typeObj = Types.availableTypes[type];
359
+ const root = jml(
360
+ ...typeObj[readonly ? 'viewUI' : 'editUI'](
361
+ hasValue
362
+ ? {
363
+ typeNamespace, type, buildTypeChoices,
364
+ format, schemaContent, schemaState,
365
+ resultType, topRoot, bringIntoFocus, value
366
+ }
367
+ : {
368
+ typeNamespace, type, buildTypeChoices,
369
+ format, schemaContent, schemaState,
370
+ resultType, topRoot, bringIntoFocus
371
+ }
372
+ )
373
+ );
374
+ if (!readonly && typeObj.validate) {
375
+ const formControl = typeObj.getInput({root});
376
+ formControl.addEventListener('input', () => {
377
+ Types.validate({type, root, topRoot});
378
+ });
379
+ }
380
+ return root;
381
+ };
382
+
383
+ Types.contexts = {};
384
+ Object.entries(Types.availableTypes).forEach(([type, {stateDependent}]) => {
385
+ if (stateDependent) {
386
+ Object.entries(stateDependent).forEach(([format, formatStateDependent]) => {
387
+ if (!Types.contexts[format]) {
388
+ Types.contexts[format] = {};
389
+ }
390
+ const {contexts, after} = formatStateDependent;
391
+ contexts.forEach((context) => {
392
+ if (!Types.contexts[format][context]) {
393
+ Types.contexts[format][context] = [];
394
+ }
395
+ Types.contexts[format][context].push({type, after});
396
+ });
397
+ });
398
+ }
399
+ });
400
+
401
+ /**
402
+ * @public
403
+ * @param {object} cfg
404
+ * @param {HTMLFormElement} cfg.form
405
+ * @param {string} cfg.typeNamespace
406
+ * @param {string} cfg.keySelectClass
407
+ * @returns {boolean}
408
+ */
409
+ Types.validValuesSet = ({form, typeNamespace, keySelectClass}) => {
410
+ // If form is hidden, don't list errors by default
411
+ if (!form.offsetParent ||
412
+ // Not an invalid form (bad key or value)
413
+ // May be redundant as re-validating below
414
+ !form.checkValidity()
415
+ ) {
416
+ return false;
417
+ }
418
+
419
+ const typeChoices = $$e(
420
+ form,
421
+ keySelectClass ? `.${keySelectClass}` : `.typeChoices-${typeNamespace}`
422
+ );
423
+ return (
424
+ // Specific value type set if present (any descendant, not
425
+ // only the first) chosen
426
+ typeChoices.every((sel) => {
427
+ // console.log('sel', sel.value !== '' && sel.$validate());
428
+ // Hidden are ok
429
+ return !sel.offsetParent ||
430
+ // If present, must be valid
431
+ (sel.value !== '' && sel.$validate());
432
+ })
433
+ // Container of a specific type added (should always be present
434
+ // if typeChoices non-empty)
435
+ // $e(form, '.typeContainer')
436
+ );
437
+ };
438
+
439
+ /**
440
+ * Any other possibilities than `div`?
441
+ * @typedef {HTMLDivElement} RootElement
442
+ */
443
+
444
+ /**
445
+ * @public
446
+ * @param {object} cfg
447
+ * @param {RootElement} cfg.topRoot
448
+ * @returns {void}
449
+ */
450
+ Types.validateAllReferences = ({topRoot}) => {
451
+ /* istanbul ignore if -- Unreachable? */
452
+ if (!topRoot) {
453
+ console.log('No references present');
454
+ return;
455
+ }
456
+
457
+ // Could just hard-code arrayReference and objectReference,
458
+ // but we'll try to avoid depending on specific types
459
+ Object.values(Types.availableTypes).forEach((typeObject) => {
460
+ if (typeObject.validateAll) {
461
+ typeObject.validateAll({topRoot});
462
+ }
463
+ });
464
+
465
+ if (Types.customValidateAllReferences) {
466
+ Types.customValidateAllReferences({topRoot});
467
+ }
468
+ };
469
+
470
+ /**
471
+ * @public
472
+ * @param {object} cfg
473
+ * @param {string} cfg.type
474
+ * @param {RootElement} cfg.root
475
+ * @param {RootElement} cfg.topRoot
476
+ * @returns {boolean}
477
+ */
478
+ Types.validate = ({type, root, topRoot}) => {
479
+ const typeObj = Types.availableTypes[type];
480
+ // Todo (low): We limit for now to input boxes which have `validate`
481
+ if (typeObj.validate) {
482
+ const {valid, message} = typeObj.validate({root, topRoot});
483
+ const formControl = typeObj.getInput({root});
484
+ formControl.setCustomValidity(
485
+ valid
486
+ ? ''
487
+ /* istanbul ignore next -- Should always have a message */
488
+ : message || 'Invalid'
489
+ );
490
+ formControl.reportValidity();
491
+ return valid;
492
+ }
493
+ return true;
494
+ };
495
+
496
+ /**
497
+ * @param {object} cfg
498
+ * @param {string} cfg.type
499
+ * @param {RootElement} cfg.root
500
+ * @param {StructuredCloneValue} cfg.value
501
+ * @returns {void}
502
+ */
503
+ Types.setValue = ({type, root, value}) => {
504
+ const typeObj = Types.availableTypes[type];
505
+ if (typeObj.setValue) {
506
+ typeObj.setValue({root, value});
507
+ }
508
+ };
509
+
510
+ /**
511
+ *
512
+ * @param {string} str
513
+ * @returns {string}
514
+ */
515
+ function escapeRegex (str) {
516
+ return String(str)
517
+ .replace(/[.\\+*?^[\]$(){}=!<>|:-]/gu, '\\$&');
518
+ }
519
+
520
+ // Todo (low): Should really add real parser
521
+ // Todo (low): Implement `getStringForValue` (e.g., to expose feature for
522
+ // bookmarking object value currently in view); would not be
523
+ // enough to iterate DOM to get string URL as we'd also like
524
+ // the ability to have arbitrary JSON/structuredCloning sent to this
525
+ // URL from other sites/programs (can currently pass in JSON
526
+ // format to the URL, but that is still expecting our Router
527
+ // string syntax)
528
+
529
+ /**
530
+ * @public
531
+ * @param {string} s
532
+ * @param {object} cfg
533
+ * @param {string} cfg.format
534
+ * @param {string} cfg.state
535
+ * @param {TypeObject[]} [cfg.endMatchTypeObjs=[]]
536
+ * @param {boolean} [cfg.firstRun=true]
537
+ * @param {
538
+ * [
539
+ * type: string,
540
+ * parent: array|object,
541
+ * parentPath: string,
542
+ * path: string
543
+ * ]
544
+ * } [cfg.rootHolder=[]]
545
+ * @param {ArbitraryArray|ArbitraryObject} cfg.parent
546
+ * @param {string} cfg.parentPath
547
+ * @returns {{
548
+ * value: ArbitraryValue,
549
+ * remnant: string,
550
+ * beginOnly: boolean,
551
+ * assign: boolean
552
+ * }}
553
+ */
554
+ Types.getValueForString = (s, {
555
+ format, state, endMatchTypeObjs = [], firstRun = true,
556
+ rootHolder = [], parent, parentPath
557
+ }) => {
558
+ let assign = true;
559
+ let match;
560
+ const allowedTypes = Types.getTypesForFormatAndState(format, state);
561
+ const allowedTypeObjs = Object.entries(
562
+ Types.availableTypes
563
+ ).filter(([type]) => allowedTypes.includes(type));
564
+ const allowedTypeObjsVals = allowedTypeObjs.map(([, arr]) => arr);
565
+
566
+ const endings = '|' + allowedTypeObjsVals.reduce((arr, typeObj) => {
567
+ if (typeObj.regexEndings) {
568
+ arr.push(...typeObj.regexEndings);
569
+ arr = [...new Set(arr)];
570
+ }
571
+ return arr;
572
+ }, []).map((str) => escapeRegex(str)).join('|');
573
+
574
+ let found = allowedTypeObjs.find(([_type, typeObj]) => {
575
+ let {stringRegex} = typeObj;
576
+ if (typeof stringRegex === 'function') {
577
+ stringRegex = typeObj.stringRegex(true);
578
+ }
579
+ stringRegex = stringRegex
580
+ // Strip off terminal (dollar sign) when matching substrings
581
+ ? new RegExp(
582
+ stringRegex.source.slice(0, -1) + '(?=$' + endings + ')',
583
+ 'u'
584
+ )
585
+ : stringRegex;
586
+ match = stringRegex && s && s.match(stringRegex);
587
+ return match;
588
+ });
589
+
590
+ let beginOnly;
591
+ if (found === undefined) {
592
+ found = allowedTypeObjs.find(([_type, typeObj]) => {
593
+ const {stringRegexBegin} = typeObj;
594
+ match = stringRegexBegin && s && s.match(stringRegexBegin);
595
+ if (match) {
596
+ beginOnly = true;
597
+ endMatchTypeObjs.push(typeObj);
598
+ }
599
+ return match;
600
+ });
601
+ }
602
+ if (found !== undefined) {
603
+ let remnant = s.slice(match[0].length);
604
+ s = s.slice(0, match[0].length);
605
+ // console.log('s0', s, '::', remnant, match);
606
+ let valObj;
607
+ try {
608
+ valObj = found[1].toValue(match[1] || s, {
609
+ format,
610
+ endMatchTypeObjs,
611
+ remnant,
612
+ rootHolder,
613
+ parent,
614
+ parentPath
615
+ });
616
+ } catch (e) {
617
+ console.log('eee', e);
618
+ throw e;
619
+ }
620
+ if (valObj.assign === false) {
621
+ assign = false;
622
+ }
623
+ const {value} = valObj;
624
+ if (valObj.remnant !== undefined) {
625
+ ({remnant} = valObj);
626
+ }
627
+
628
+ if (beginOnly && endMatchTypeObjs.length) {
629
+ const endMatch = remnant.match(
630
+ endMatchTypeObjs.slice(-1)[0].stringRegexEnd
631
+ );
632
+ if (endMatch) {
633
+ endMatchTypeObjs.pop(); // Safe now to extract
634
+ remnant = remnant.slice(endMatch[0].length);
635
+ }
636
+ }
637
+ if (firstRun) {
638
+ const typeson = new Typeson().register(
639
+ structuredCloningThrowing
640
+ );
641
+ try {
642
+ const topRoot = typeson.revive(value);
643
+ rootHolder.forEach(([type, parent, parentPath, path]) => {
644
+ const val = Types.availableTypes[type + 'Reference']
645
+ .resolveReference(path, topRoot);
646
+ const basicType = getJSONType(val);
647
+ // eslint-disable-next-line max-len -- Long
648
+ /* istanbul ignore else -- Successful reference always an object/array? */
649
+ if (
650
+ ['array', 'object'].includes(type) && basicType === type
651
+ ) {
652
+ parent[parentPath] = val;
653
+ }
654
+ });
655
+ return [topRoot, remnant, beginOnly, assign];
656
+ } catch (err) {
657
+ console.log('failed Typeson revival', err);
658
+ }
659
+ }
660
+ return [value, remnant, beginOnly, assign];
661
+ }
662
+ throw new Error('Bad parsing data');
663
+ };
664
+
665
+ export default Types;