@muze-nl/simplystore 0.4.7 → 0.5.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.
- package/package.json +14 -7
- package/scripts/convert.mjs +25 -0
- package/src/command-worker-module.mjs +100 -0
- package/src/command-worker.mjs +13 -0
- package/src/fastParse.mjs +1036 -0
- package/src/fastStringify.mjs +200 -0
- package/src/load-worker.mjs +33 -0
- package/src/{worker-query.mjs → query-worker-module.mjs} +138 -83
- package/src/query-worker.mjs +12 -0
- package/src/server.mjs +303 -218
- package/src/statusCodes.mjs +70 -0
- package/src/symbols.mjs +6 -0
- package/src/util.mjs +3 -3
- package/src/workerPool.mjs +99 -0
- package/data.jsontag +0 -20
- package/src/share.mjs +0 -142
- package/src/worker-query-init.mjs +0 -14
|
@@ -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
|
+
|