@muze-nl/simplystore 0.4.6 → 0.5.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.
@@ -0,0 +1,1036 @@
1
+ import JSONTag from '@muze-nl/jsontag';
2
+ import Null from '@muze-nl/jsontag/src/lib/Null.mjs'
3
+ import fastStringify from './fastStringify.mjs'
4
+ import {source,isProxy,getBuffer,getIndex,isChanged} from './symbols.mjs'
5
+
6
+ const decoder = new TextDecoder()
7
+ const encoder = new TextEncoder()
8
+
9
+ export default function parse(input, meta, immutable=true)
10
+ {
11
+ if (!meta) {
12
+ meta = {}
13
+ }
14
+ if (!meta.index) {
15
+ meta.index = {}
16
+ }
17
+ if (!meta.index.id) {
18
+ meta.index.id = new Map()
19
+ }
20
+ if (!meta.unresolved) {
21
+ meta.unresolved = new Map()
22
+ }
23
+ if (!meta.baseURL) {
24
+ meta.baseURL = 'http://localhost/'
25
+ }
26
+
27
+ let at, ch, value, result;
28
+ let escapee = {
29
+ '"': '"',
30
+ "\\":"\\",
31
+ '/': '/',
32
+ b: "\b",
33
+ f: "\f",
34
+ n: "\n",
35
+ r: "\r",
36
+ t: "\t"
37
+ }
38
+ let offsetArray = []
39
+
40
+ let error = function(m)
41
+ {
42
+ let context
43
+ try {
44
+ context = decoder.decode(input.slice(at-100,at+100));
45
+ } catch(err) {}
46
+ throw {
47
+ name: 'SyntaxError',
48
+ message: m,
49
+ at: at,
50
+ input: context
51
+ }
52
+ }
53
+
54
+ if (!(input instanceof Uint8Array)) {
55
+ error('fast parse only accepts Uint8Array as input')
56
+ }
57
+
58
+ let next = function(c)
59
+ {
60
+ if (c && c!==ch) {
61
+ error("Expected '"+c+"' instead of '"+ch+"'")
62
+ }
63
+ ch = String.fromCharCode(input.at(at))
64
+ at+=1
65
+ return ch
66
+ }
67
+
68
+ let number = function(tagName)
69
+ {
70
+ let numString = ''
71
+ if (ch==='-') {
72
+ numString = '-'
73
+ next('-')
74
+ }
75
+ while(ch>='0' && ch<='9') {
76
+ numString += ch
77
+ next()
78
+ }
79
+ if (ch==='.') {
80
+ numString+='.'
81
+ while(next() && ch >= '0' && ch <= '9') {
82
+ numString += ch
83
+ }
84
+ }
85
+ if (ch === 'e' || ch === 'E') {
86
+ numString += ch
87
+ next()
88
+ if (ch === '-' || ch === '+') {
89
+ numString += ch
90
+ next()
91
+ }
92
+ while (ch >= '0' && ch <= '9') {
93
+ numString += ch
94
+ next()
95
+ }
96
+ }
97
+ let result = new Number(numString).valueOf()
98
+ if (tagName) {
99
+ switch(tagName) {
100
+ case "int":
101
+ isInt(numString)
102
+ break
103
+ case "uint":
104
+ isInt(numString, [0,Infinity])
105
+ break
106
+ case "int8":
107
+ isInt(numString, [-128,127])
108
+ break
109
+ case "uint8":
110
+ isInt(numString, [0,255])
111
+ break
112
+ case "int16":
113
+ isInt(numString, [-32768,32767])
114
+ break
115
+ case "uint16":
116
+ isInt(numString, [0,65535])
117
+ break
118
+ case "int32":
119
+ isInt(numString, [-2147483648, 2147483647])
120
+ break
121
+ case "uint32":
122
+ isInt(numString, [0,4294967295])
123
+ break
124
+ case "timestamp":
125
+ case "int64":
126
+ isInt(numString, [-9223372036854775808,9223372036854775807])
127
+ break
128
+ case "uint64":
129
+ isInt(numString, [0,18446744073709551615])
130
+ break
131
+ case "float":
132
+ isFloat(numString)
133
+ break
134
+ case "float32":
135
+ isFloat(numString, [-3.4e+38,3.4e+38])
136
+ break
137
+ case "float64":
138
+ isFloat(numString, [-1.7e+308,+1.7e+308])
139
+ break
140
+ case "number":
141
+ //FIXME: what to check? should already be covered by JSON parsing rules?
142
+ break
143
+ default:
144
+ isTypeError(tagName, numString)
145
+ break
146
+ }
147
+ }
148
+ return result
149
+ }
150
+
151
+ let isTypeError = function(type, value)
152
+ {
153
+ error('Syntax error, expected '+type+', got: '+value)
154
+ }
155
+
156
+ const regexes = {
157
+ color: /^(rgb|hsl)a?\((\d+%?(deg|rad|grad|turn)?[,\s]+){2,3}[\s\/]*[\d\.]+%?\)$/i,
158
+ email: /^[A-Za-z0-9_!#$%&'*+\/=?`{|}~^.-]+@[A-Za-z0-9.-]+$/,
159
+ uuid: /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/,
160
+ decimal: /^\d*\.?\d*$/,
161
+ money: /^[A-Z]+\$\d*\.?\d*$/,
162
+ duration: /^(-?)P(?=\d|T\d)(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)([DW]))?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/,
163
+ phone: /^[+]?(?:\(\d+(?:\.\d+)?\)|\d+(?:\.\d+)?)(?:[ -]?(?:\(\d+(?:\.\d+)?\)|\d+(?:\.\d+)?))*(?:[ ]?(?:x|ext)\.?[ ]?\d{1,5})?$/,
164
+ time: /^(\d{2}):(\d{2})(?::(\d{2}(?:\.\d+)?))?$/,
165
+ date: /^-?[1-9][0-9]{3,}-([0][1-9]|[1][0-2])-([1-2][0-9]|[0][1-9]|[3][0-1])$/,
166
+ datetime: /^(\d{4,})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}(?:\.\d+)?))?$/,
167
+ range: /^\[-?(\d+\.)?\d+\,-?(\d+\.)?\d+\]$/
168
+ }
169
+
170
+ let isFloat = function(float, range)
171
+ {
172
+ let test = new Number(parseFloat(float))
173
+ let str = test.toString()
174
+ if (float!==str) {
175
+ error('Syntax Error: expected float value')
176
+ }
177
+ if (range) {
178
+ if (typeof range[0] === 'number') {
179
+ if (test<range[0]) {
180
+ error('Syntax Error: float value out of range')
181
+ }
182
+ }
183
+ if (typeof range[1] === 'number') {
184
+ if (test>range[1]) {
185
+ error('Syntax Error: float value out of range')
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ let isInt = function(int, range)
192
+ {
193
+ let test = new Number(parseInt(int))
194
+ let str = test.toString()
195
+ if (int!==str) {
196
+ error('Syntax Error: expected integer value')
197
+ }
198
+ if (range) {
199
+ if (typeof range[0] === 'number') {
200
+ if (test<range[0]) {
201
+ error('Syntax Error: integer value out of range')
202
+ }
203
+ }
204
+ if (typeof range[1] === 'number') {
205
+ if (test>range[1]) {
206
+ error('Syntax Error: integer value out of range')
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ let isColor = function(color)
213
+ {
214
+ let result = false
215
+ if (color.charAt(0) === "#") {
216
+ color = color.substring(1)
217
+ result = ([3, 4, 6, 8].indexOf(color.length) > -1) && !isNaN(parseInt(color, 16))
218
+ if (result.toString(16)!==color) {
219
+ isTypeError('color', color)
220
+ }
221
+ } else {
222
+ result = regexes.color.test(color)
223
+ }
224
+ if (!result) {
225
+ isTypeError('color',color)
226
+ }
227
+ return true
228
+ }
229
+
230
+ let isEmail = function(email)
231
+ {
232
+ let result = regexes.email.test(email)
233
+ if (!result) {
234
+ isTypeError('email',email)
235
+ }
236
+ return true
237
+ }
238
+
239
+ let isUuid = function(uuid)
240
+ {
241
+ let result = regexes.uuid.test(uuid)
242
+ if (!result) {
243
+ isTypeError('uuid',uuid)
244
+ }
245
+ return true
246
+ }
247
+
248
+ let isDecimal = function(decimal)
249
+ {
250
+ let result = regexes.decimal.test(decimal)
251
+ if (!result) {
252
+ isTypeError('decimal',decimal)
253
+ }
254
+ return true
255
+ }
256
+
257
+ let isMoney = function(money)
258
+ {
259
+ let result = regexes.money.test(money)
260
+ if (!result) {
261
+ isTypeError('money',money)
262
+ }
263
+ return true
264
+ }
265
+
266
+ let isUrl = function(url)
267
+ {
268
+ try {
269
+ return Boolean(new URL(url, meta.baseURL))
270
+ } catch(e) {
271
+ isTypeError('url',url)
272
+ }
273
+ }
274
+
275
+ let isDuration = function(duration)
276
+ {
277
+ let result = regexes.duration.test(duration)
278
+ if (!result) {
279
+ isTypeError('duration',duration)
280
+ }
281
+ return true
282
+ }
283
+
284
+ let isPhone = function(phone)
285
+ {
286
+ let result = regexes.phone.test(phone)
287
+ if (!result) {
288
+ isTypeError('phone',phone)
289
+ }
290
+ return true
291
+ }
292
+
293
+ let isRange = function(range)
294
+ {
295
+ let result = regexes.range.test(range)
296
+ if (!result) {
297
+ isTypeError('range',range)
298
+ }
299
+ return true
300
+ }
301
+
302
+ let isTime = function(time)
303
+ {
304
+ let result = regexes.time.test(time)
305
+ if (!result) {
306
+ isTypeError('time',time)
307
+ }
308
+ return true
309
+ }
310
+
311
+ let isDate = function(date)
312
+ {
313
+ let result = regexes.date.test(date)
314
+ if (!result) {
315
+ isTypeError('date',date)
316
+ }
317
+ return true
318
+ }
319
+
320
+ let isDatetime = function(datetime)
321
+ {
322
+ let result = regexes.datetime.test(datetime)
323
+ if (!result) {
324
+ isTypeError('datetime',datetime)
325
+ }
326
+ return true
327
+ }
328
+
329
+ let checkStringType = function(tagName, value)
330
+ {
331
+ if (!tagName) {
332
+ return
333
+ }
334
+ switch(tagName){
335
+ case "object":
336
+ case "array":
337
+ case "int8":
338
+ case "uint8":
339
+ case "int16":
340
+ case "uint16":
341
+ case "int32":
342
+ case "uint32":
343
+ case "int64":
344
+ case "uint64":
345
+ case "int":
346
+ case "uint":
347
+ case "float32":
348
+ case "float64":
349
+ case "float":
350
+ case "timestamp":
351
+ isTypeError(tagName, value)
352
+ break
353
+ case "uuid":
354
+ return isUuid(value)
355
+ case "decimal":
356
+ return isDecimal(value)
357
+ case "money":
358
+ return isMoney(value)
359
+ case "url":
360
+ return isUrl(value)
361
+ case "link":
362
+ case "string":
363
+ case "text":
364
+ case "blob":
365
+ case "hash":
366
+ //anything goes
367
+ return true
368
+ case "color":
369
+ return isColor(value)
370
+ case "email":
371
+ return isEmail(value)
372
+ case "duration":
373
+ return isDuration(value)
374
+ case "phone":
375
+ return isPhone(value)
376
+ case "range":
377
+ return isRange(value)
378
+ case "time":
379
+ return isTime(value)
380
+ case "date":
381
+ return isDate(value)
382
+ case "datetime":
383
+ return isDatetime(value)
384
+ }
385
+ error('Syntax error: unknown tagName '+tagName)
386
+ }
387
+
388
+ let string = function(tagName)
389
+ {
390
+ let value = [], hex, i, uffff;
391
+ if (ch !== '"') {
392
+ error("Syntax Error")
393
+ }
394
+ next('"')
395
+ while(ch) {
396
+ if (ch==='"') {
397
+ next()
398
+ let bytes = new Uint8Array(value)
399
+ value = decoder.decode(bytes)
400
+ checkStringType(tagName, value)
401
+ return value
402
+ }
403
+ if (ch==='\\') {
404
+ next()
405
+ if (ch==='u') {
406
+ for (i=0; i<4; i++) {
407
+ hex = parseInt(next(), 16)
408
+ if (!isFinite(hex)) {
409
+ break
410
+ }
411
+ uffff = uffff * 16 + hex
412
+ }
413
+ let str = String.fromCharCode(uffff)
414
+ let bytes = encoder.encode(str)
415
+ value.push.apply(value, bytes)
416
+ next()
417
+ } else if (typeof escapee[ch] === 'string') {
418
+ value.push(escapee[ch].charCodeAt(0))
419
+ next()
420
+ } else {
421
+ break
422
+ }
423
+ } else {
424
+ value.push(ch.charCodeAt(0))
425
+ next()
426
+ }
427
+ }
428
+ error("Syntax error: incomplete string")
429
+ }
430
+
431
+ let tag = function()
432
+ {
433
+ let key, val, tagOb={
434
+ attributes: {}
435
+ }
436
+ if (ch !== '<') {
437
+ error("Syntax Error")
438
+ }
439
+ next('<')
440
+ key = word()
441
+ if (!key) {
442
+ error('Syntax Error: expected tag name')
443
+ }
444
+ tagOb.tagName = key
445
+ whitespace()
446
+ while(ch) {
447
+ if (ch==='>') {
448
+ next('>')
449
+ return tagOb
450
+ }
451
+ key = word()
452
+ if (!key) {
453
+ error('Syntax Error: expected attribute name')
454
+ }
455
+ whitespace()
456
+ next('=')
457
+ whitespace()
458
+ val = string()
459
+ tagOb.attributes[key] = val
460
+ whitespace()
461
+ }
462
+ error('Syntax Error: unexpected end of input')
463
+ }
464
+
465
+ let whitespace = function()
466
+ {
467
+ while (ch) {
468
+ switch(ch) {
469
+ case ' ':
470
+ case "\t":
471
+ case "\r":
472
+ case "\n":
473
+ next()
474
+ break
475
+ default:
476
+ return
477
+ break
478
+ }
479
+ }
480
+ }
481
+
482
+ let word = function()
483
+ {
484
+ //[a-z][a-z0-9_]*
485
+ let val='';
486
+ if ((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')) {
487
+ val += ch
488
+ next()
489
+ } else {
490
+ error('Syntax Error: expected word')
491
+ }
492
+ while((ch>='a' && ch<='z') || (ch>='A' && ch<='Z') || (ch>='0' && ch<='9') || ch=='_') {
493
+ val += ch
494
+ next()
495
+ }
496
+ return val
497
+ }
498
+
499
+ let boolOrNull = function(tagName)
500
+ {
501
+ let w = word()
502
+ if (!w || typeof w !== 'string') {
503
+ error('Syntax error: expected boolean or null, got "'+w+'"')
504
+ }
505
+ switch(w.toLowerCase()) {
506
+ case 'true':
507
+ if (tagName && tagName!=='boolean') {
508
+ isTypeError(tagName,w)
509
+ }
510
+ return true
511
+ break
512
+ case 'false':
513
+ if (tagName && tagName!=='boolean') {
514
+ isTypeError(tagName,w)
515
+ }
516
+ return false
517
+ break
518
+ case 'null':
519
+ return null
520
+ break
521
+ default:
522
+ error('Syntax error: expected boolean or null, got "'+w+'"')
523
+ break
524
+ }
525
+ }
526
+
527
+ let checkUnresolved = function(item, object, key)
528
+ {
529
+ if (JSONTag.getType(item)==='link') {
530
+ let link = ''+item
531
+ let links = meta.unresolved.get(link)
532
+ if (typeof links === 'undefined') {
533
+ meta.unresolved.set(link,[])
534
+ links = meta.unresolved.get(link)
535
+ }
536
+ let count = links.push({
537
+ src: new WeakRef(object),
538
+ key: key
539
+ })
540
+ }
541
+ }
542
+
543
+ let array = function()
544
+ {
545
+ let item, array = []
546
+ if (ch !== '[') {
547
+ error("Syntax error")
548
+ }
549
+ next('[')
550
+ whitespace()
551
+ if (ch===']') {
552
+ next(']')
553
+ return array
554
+ }
555
+ while(ch) {
556
+ item = value()
557
+ checkUnresolved(item, array, array.length)
558
+ array.push(item)
559
+ whitespace()
560
+ if (ch===']') {
561
+ next(']')
562
+ return array
563
+ }
564
+ next(',')
565
+ whitespace()
566
+ }
567
+ error("Input stopped early")
568
+ }
569
+
570
+ let object = function()
571
+ {
572
+ let key, val, object={}
573
+ if (ch !== '{') {
574
+ error("Syntax Error")
575
+ }
576
+ next('{')
577
+ whitespace()
578
+ if (ch==='}') {
579
+ next('}')
580
+ return object
581
+ }
582
+ let enumerable = true
583
+ while(ch) {
584
+ if (ch==='#') {
585
+ enumerable = false
586
+ next()
587
+ } else {
588
+ enumerable = true
589
+ }
590
+ key = string()
591
+ if (key==='__proto__') {
592
+ error("Attempt at prototype pollution")
593
+ }
594
+ whitespace()
595
+ next(':')
596
+ val = value()
597
+ if (!enumerable) {
598
+ Object.defineProperty(object,key, { value: val})
599
+ } else {
600
+ object[key] = val
601
+ }
602
+ checkUnresolved(val, object, key)
603
+ whitespace()
604
+ if (ch==='}') {
605
+ next('}')
606
+ return object
607
+ }
608
+ next(',')
609
+ whitespace()
610
+ }
611
+ error("Input stopped early")
612
+ }
613
+
614
+ let length = function()
615
+ {
616
+ whitespace()
617
+ next('(')
618
+ let numString=''
619
+ while(ch>='0' && ch<='9') {
620
+ numString += ch
621
+ next()
622
+ }
623
+ if (ch!==')') {
624
+ error('Syntax error: not a length')
625
+ }
626
+ next()
627
+ return parseInt(numString)
628
+ }
629
+
630
+ let offset = function()
631
+ {
632
+ next('~')
633
+ let numString = ''
634
+ while(ch>='0' && ch<='9') {
635
+ numString += ch
636
+ next()
637
+ }
638
+ return parseInt(numString)
639
+ }
640
+
641
+ let parseValue = function(position) {
642
+ at = position.start
643
+ next()
644
+ return value()
645
+ }
646
+
647
+ const makeChildProxies = function(parent) {
648
+ Object.entries(parent).forEach(([key,entry]) => {
649
+ if (Array.isArray(entry)) {
650
+ makeChildProxies(entry)
651
+ } else if (JSONTag.getType(entry)==='object') {
652
+ if (entry[isProxy]) {
653
+ // do nothing
654
+ } else {
655
+ parent[key] = getNewValueProxy(entry)
656
+ }
657
+ }
658
+ })
659
+ }
660
+
661
+ const getNewValueProxy = function(value) {
662
+ let index = resultArray.length
663
+ resultArray.push('')
664
+ let arrayHandler = {
665
+ get(target, prop) {
666
+ if (target[prop] instanceof Function) {
667
+ return (...args) => {
668
+ args = args.map(arg => {
669
+ if (JSONTag.getType(arg)==='object' && !arg[isProxy]) {
670
+ arg = getNewValueProxy(arg)
671
+ }
672
+ return arg
673
+ })
674
+ target[prop].apply(target, args)
675
+ }
676
+ } else if (prop===isChanged) {
677
+ return true
678
+ } else {
679
+ if (Array.isArray(target[prop])) {
680
+ return new Proxy(target[prop], arrayHandler)
681
+ }
682
+ return target[prop]
683
+ }
684
+ },
685
+ set(target, prop, value) {
686
+ if (JSONTag.getType(value)==='object' && !value[isProxy]) {
687
+ value = getNewValueProxy(value)
688
+ }
689
+ target[prop] = value
690
+ return true
691
+ }
692
+ }
693
+ let newValueHandler = {
694
+ get(target, prop, receiver) {
695
+ switch(prop) {
696
+ case source:
697
+ return target
698
+ break
699
+ case isProxy:
700
+ return true
701
+ break
702
+ case getBuffer:
703
+ return (i) => {
704
+ if (i != index) {
705
+ return encoder.encode('~'+index)
706
+ }
707
+ // return newly stringified contents of cache
708
+ return encoder.encode(fastStringify(target, meta, true, i))
709
+ }
710
+ break
711
+ case getIndex:
712
+ return index
713
+ break
714
+ case isChanged:
715
+ return true
716
+ break
717
+ default:
718
+ if (Array.isArray(target[prop])) {
719
+ return new Proxy(target[prop], arrayHandler)
720
+ }
721
+ return target[prop]
722
+ break
723
+ }
724
+ },
725
+ set(target, prop, value) {
726
+ if (JSONTag.getType(value)==='object' && !value[isProxy]) {
727
+ value = getNewValueProxy(value)
728
+ }
729
+ cache[prop] = val
730
+ return true
731
+ }
732
+ }
733
+
734
+ makeChildProxies(value)
735
+ let result = new Proxy(value, newValueHandler)
736
+ resultArray[index] = result
737
+ return result
738
+ }
739
+
740
+ let valueProxy = function(length, index)
741
+ {
742
+ // current offset + length contains jsontag of this value
743
+ let position = {
744
+ start: at-1,
745
+ end: at-1+length
746
+ }
747
+ let cache = {} // use a single global dummy cache object here to preserve memory
748
+ let targetIsChanged = false
749
+ let parsed = false
750
+ at += length
751
+ next()
752
+ // newValueHandler makes sure that value[getBuffer] runs stringify
753
+ // arrayHandler makes sure that changes in the array set targetIsChanged to true
754
+ let arrayHandler = {
755
+ get(target, prop) {
756
+ if (target[prop] instanceof Function) {
757
+ if (['copyWithin','fill','pop','push','reverse','shift','sort','splice','unshift'].indexOf(prop)!==-1) {
758
+ targetIsChanged = true
759
+ }
760
+ return (...args) => {
761
+ args = args.map(arg => {
762
+ if (JSONTag.getType(arg)==='object' && !arg[isProxy]) {
763
+ arg = getNewValueProxy(arg)
764
+ }
765
+ return arg
766
+ })
767
+ return target[prop].apply(target, args)
768
+ }
769
+ } else if (prop===isChanged) {
770
+ return targetIsChanged
771
+ } else {
772
+ if (!immutable && Array.isArray(target[prop])) {
773
+ return new Proxy(target[prop], arrayHandler)
774
+ }
775
+ return target[prop]
776
+ }
777
+ },
778
+ set(target, prop, value) {
779
+ if (JSONTag.getType(value)==='object' && !value[isProxy]) {
780
+ value = getNewValueProxy(value)
781
+ }
782
+ target[prop] = value
783
+ targetIsChanged = true
784
+ return true
785
+ },
786
+ deleteProperty(target, prop) {
787
+ //FIXME: if target[prop] was the last reference to an object
788
+ //that object should be deleted so that its line will become empty
789
+ //when stringifying resultArray again
790
+ delete target[prop]
791
+ targetIsChanged = true
792
+ return true
793
+ }
794
+ }
795
+ let handler = {
796
+ get(target, prop, receiver) {
797
+ if (!parsed) {
798
+ cache = parseValue(position)
799
+ parsed = true
800
+ }
801
+ switch(prop) {
802
+ case source:
803
+ return cache
804
+ break
805
+ case isProxy:
806
+ return true
807
+ break
808
+ case getBuffer:
809
+ return (i) => {
810
+ if (i != index) {
811
+ return encoder.encode('~'+index)
812
+ }
813
+ if (targetIsChanged) {
814
+ // return newly stringified contents of cache
815
+ let temp = fastStringify(cache, null, true)
816
+ return encoder.encode(fastStringify(cache, null, true))
817
+ }
818
+ return input.slice(position.start,position.end)
819
+ }
820
+ break
821
+ case getIndex:
822
+ return index
823
+ break
824
+ case isChanged:
825
+ return targetIsChanged
826
+ break
827
+ default:
828
+ if (!immutable && Array.isArray(cache[prop])) {
829
+ return new Proxy(cache[prop], arrayHandler)
830
+ }
831
+ return cache[prop] // FIXME: make arrays immutable as well
832
+ break
833
+ }
834
+ },
835
+ has(target, prop) {
836
+ if (!parsed) {
837
+ cache = parseValue(position)
838
+ parsed = true
839
+ }
840
+ return typeof cache[prop] !== 'undefined'
841
+ },
842
+ ownKeys() {
843
+ if (!parsed) {
844
+ cache = parseValue(position)
845
+ parsed = true
846
+ }
847
+ return Reflect.ownKeys(cache)
848
+ }
849
+ }
850
+ if (!immutable) {
851
+ Object.assign(handler, {
852
+ set: (target, prop, val) => {
853
+ if (!parsed) {
854
+ cache = parseValue(position)
855
+ parsed = true
856
+ }
857
+ if (JSONTag.getType(val)==='object' && !val[isProxy]) {
858
+ val = getNewValueProxy(val)
859
+ }
860
+ cache[prop] = val
861
+ targetIsChanged = true
862
+ return true
863
+ },
864
+ deleteProperty: (target, prop) => {
865
+ if (!parsed) {
866
+ cache = parseValue(position)
867
+ parsed = true
868
+ }
869
+ delete cache[prop]
870
+ targetIsChanged = true
871
+ return true
872
+ }
873
+ })
874
+ }
875
+ return new Proxy(cache, handler)
876
+ }
877
+
878
+ value = function()
879
+ {
880
+ let tagOb, result, tagName;
881
+ whitespace()
882
+ if (ch==='~') {
883
+ let vOffset = offset()
884
+ return resultArray[vOffset]
885
+ }
886
+ if (ch==='<') {
887
+ tagOb = tag()
888
+ tagName = tagOb.tagName
889
+ whitespace()
890
+ }
891
+ switch(ch) {
892
+ case '{':
893
+ if (tagName && tagName!=='object') {
894
+ isTypeError(tagName, ch)
895
+ }
896
+ result = object()
897
+ break
898
+ case '[':
899
+ if (tagName && tagName!=='array') {
900
+ isTypeError(tagName, ch)
901
+ }
902
+ result = array()
903
+ break
904
+ case '"':
905
+ result = string(tagName)
906
+ break
907
+ case '-':
908
+ result = number(tagName)
909
+ break
910
+ default:
911
+ if (ch>='0' && ch<='9') {
912
+ result = number(tagName)
913
+ } else {
914
+ result = boolOrNull(tagName)
915
+ }
916
+ break
917
+ }
918
+ if (tagOb) {
919
+ if (result === null) {
920
+ result = new Null()
921
+ }
922
+ if (typeof result !== 'object') {
923
+ switch(typeof result) {
924
+ case 'string':
925
+ result = new String(result)
926
+ break
927
+ case 'number':
928
+ result = new Number(result)
929
+ break
930
+ default:
931
+ error('Syntax Error: unexpected type '+(typeof result))
932
+ break
933
+ }
934
+ }
935
+ if (tagOb.tagName) {
936
+ JSONTag.setType(result, tagOb.tagName)
937
+ }
938
+ if (tagOb.attributes) {
939
+ JSONTag.setAttributes(result, tagOb.attributes)
940
+ /* if (tagOb.attributes?.id) {
941
+ meta.index.id.set(tagOb.attributes.id, result))
942
+ }
943
+ */
944
+ }
945
+ }
946
+ return result
947
+ }
948
+
949
+ function lengthValue(i) {
950
+ let l = length()
951
+ let v = valueProxy(l,i)
952
+ return [l, v]
953
+ }
954
+
955
+ at = 0
956
+ ch = " "
957
+ let resultArray = []
958
+ while(ch && at<input.length) {
959
+ result = lengthValue(resultArray.length)
960
+ whitespace()
961
+ offsetArray.push(at)
962
+ resultArray.push(result[1])
963
+ }
964
+
965
+ if (typeof reviver === 'function') {
966
+ function walk(holder, key)
967
+ {
968
+ var k;
969
+ var v;
970
+ var value = holder[key];
971
+ if (value !== null
972
+ && typeof value === "object"
973
+ && !(value instanceof String
974
+ || value instanceof Number
975
+ || value instanceof Boolean)
976
+ ) {
977
+ for (k in value) {
978
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
979
+ v = walk(value, k);
980
+ if (v !== undefined
981
+ && ( typeof value[k] === 'undefined' || value[k]!==v) )
982
+ {
983
+ value[k] = v;
984
+ if (JSONTag.getType(v)==='link') {
985
+ checkUnresolved(v, value, k)
986
+ }
987
+ } else if (v === undefined) {
988
+ delete value[k];
989
+ }
990
+ }
991
+ }
992
+ }
993
+ return reviver.call(holder, key, value, meta);
994
+ }
995
+
996
+ walk({"":result}, "")
997
+ }
998
+
999
+ let replaceLink = function(u,value)
1000
+ {
1001
+ if (typeof value !== 'undefined') {
1002
+ let src = u.src.deref()
1003
+ if (typeof src!== 'undefined' && JSONTag.getType(src[u.key])==='link') {
1004
+ src[u.key] = value
1005
+ return true
1006
+ }
1007
+ }
1008
+ }
1009
+
1010
+ if (meta.index.id.size>meta.unresolved.size) {
1011
+ meta.unresolved.forEach((links,id) => {
1012
+ let value = meta.index.id.get(id)?.deref()
1013
+ if (value!==undefined) {
1014
+ links.forEach((u,i) => {
1015
+ if (replaceLink(u,value)) {
1016
+ delete links[i]
1017
+ }
1018
+ })
1019
+ }
1020
+ })
1021
+ } else {
1022
+ meta.index.id.forEach((ref,id) => {
1023
+ let value = ref.deref()
1024
+ let links = meta.unresolved.get(id)
1025
+ if (value!==undefined && typeof links !== 'undefined') {
1026
+ links.forEach((u,i) => {
1027
+ replaceLink(u,value)
1028
+ })
1029
+ meta.unresolved.delete(id)
1030
+ }
1031
+ })
1032
+ }
1033
+
1034
+ return resultArray
1035
+ }
1036
+