flatpack-json 9.7.0 → 10.0.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.
package/dist/Flatpack.mjs CHANGED
@@ -1,598 +1,19 @@
1
- import assert from 'node:assert';
2
1
  import { FlatpackedWrapper } from './flatpackUtil.mjs';
3
- import { proxyDate, proxyObject, proxySet } from './proxy.mjs';
4
- import { ArrayRefElement, BigIntRefElement, DateRefElement, isStringRefElements, MapRefElement, ObjectRefElement, ObjectWrapperRefElement, PrimitiveRefElement, PrimitiveRefElementBase, RegExpRefElement, SetRefElement, StringConcatRefElement, StringPrimitiveRefElement, SubStringRefElement, } from './RefElements.mjs';
2
+ import { FlatpackStoreV2 } from './FlatpackV2.mjs';
5
3
  import { stringifyFlatpacked } from './stringify.mjs';
6
- import { Trie } from './Trie.mjs';
7
- import { blockSplitRegex, dataHeader } from './types.mjs';
8
4
  import { fromJSON } from './unpack.mjs';
9
- const collator = new Intl.Collator('en', {
10
- usage: 'sort',
11
- numeric: true,
12
- sensitivity: 'variant',
13
- caseFirst: 'upper',
14
- ignorePunctuation: false,
15
- });
16
- const compare = collator.compare;
17
- const forceStringPrimitives = false;
18
- const minSubStringLen = 8;
19
- const minSubStringSuffixLen = 16;
20
- const useSuffix = true;
21
- const maxCachedStringLen = 256;
22
5
  export class FlatpackStore {
23
6
  options;
24
- knownElements = new Set();
25
- assignedElements = new Map();
26
- elements = [undefined];
27
- root = undefined;
28
- dedupe = true;
29
- sortKeys = true;
30
- emptyObjIdx = undefined;
31
- ids = 0;
32
- /**
33
- * Cache of primitives and objects that have been added to the data.
34
- */
35
- cache = new Map();
36
- /**
37
- * Set of elements that have been referenced by other indexes.
38
- */
39
- referenced = new Set();
40
- /**
41
- * Cache of arrays that have been deduped.
42
- * The key is a hash of the array elements as a function of the index of the element.
43
- */
44
- cachedArrays = new Map();
45
- cachedObjects = new Map();
46
- cachedSets = new Map();
47
- cachedMaps = new Map();
48
- cachedProxies = new WeakMap();
49
- /**
50
- * Cache of strings that have been deduped and stored in the data array.
51
- */
52
- knownStrings = new Trie();
53
- /**
54
- * Cache of reversed strings that have been deduped and stored in the data array.
55
- * This is used to find matching suffixes.
56
- */
57
- knownStringsRev = new Trie();
58
- refUndefined;
7
+ #flatpackApi;
59
8
  constructor(value, options) {
60
9
  this.options = options;
61
- this.dedupe = options?.dedupe ?? true;
62
- this.sortKeys = options?.sortKeys || this.dedupe;
63
- this.refUndefined = this.addValueAndElement(undefined, new PrimitiveRefElement(undefined));
64
- if (value instanceof FlatpackedWrapper) {
65
- this.#fromWrapper(value);
66
- }
67
- else {
68
- this.#setValue(value);
69
- }
70
- }
71
- #fromWrapper(wrapper) {
72
- this.elements = wrapper.toRefElements();
73
- this.root = this.elements[1];
74
- for (let i = 1; i < this.elements.length; i++) {
75
- const element = this.elements[i];
76
- if (!element)
77
- continue;
78
- this.knownElements.add(element);
79
- if (element instanceof PrimitiveRefElement) {
80
- this.addValueAndElement(element.value, element);
81
- }
82
- if (isStringRefElements(element)) {
83
- this.addStringElement(element.value, element);
84
- }
85
- }
86
- this.ids = this.elements.length;
87
- this.#resolveRefs();
10
+ this.#flatpackApi = new FlatpackStoreV2(value, options);
88
11
  }
89
12
  setValue(value) {
90
- this.#setValue(value);
91
- }
92
- #setValue(value) {
93
- this.softReset();
94
- this.root = this.valueToRef(value);
95
- this.#resolveRefs();
96
- return this.root;
97
- }
98
- nextId() {
99
- return this.ids++;
100
- }
101
- addElement(element) {
102
- if (this.knownElements.has(element))
103
- return element;
104
- element.setId(this.nextId());
105
- this.knownElements.add(element);
106
- return element;
107
- }
108
- addValueAndElement(value, element, cache = true) {
109
- this.addElement(element);
110
- if (cache) {
111
- this.cache.set(value, element);
112
- }
113
- return element;
114
- }
115
- primitiveToRef(value) {
116
- if (typeof value === 'string')
117
- return this.stringToRef(value);
118
- if (typeof value === 'bigint')
119
- return this.cvtBigintToRef(value);
120
- const found = this.cache.get(value);
121
- if (found !== undefined) {
122
- return found;
123
- }
124
- return this.addValueAndElement(value, new PrimitiveRefElement(value));
125
- }
126
- createSubStringRef(baseString, value, offset) {
127
- const found = this.cache.get(value);
128
- if (found !== undefined) {
129
- return found;
130
- }
131
- return this.addStringElement(value, new SubStringRefElement(baseString, value.length, offset));
132
- }
133
- addKnownString(ref, value) {
134
- if (value.length >= minSubStringLen) {
135
- this.knownStrings.add(prefix(value, maxCachedStringLen), ref);
136
- const rev = reverse(suffix(value, maxCachedStringLen));
137
- this.knownStringsRev.add(rev, ref);
138
- }
139
- }
140
- addStringPrimitive(value) {
141
- return this.addStringElement(value, new StringPrimitiveRefElement(value));
142
- }
143
- addStringElement(value, element) {
144
- this.addKnownString(element, value);
145
- return this.addValueAndElement(value, element);
146
- }
147
- stringPrefix(value) {
148
- // if (value.length < maxCachedStringLen * 2) return undefined;
149
- const trieFound = this.knownStrings.find(value);
150
- if (!trieFound || !trieFound.data || trieFound.found.length < minSubStringLen) {
151
- return undefined;
152
- }
153
- const { data: tData, found: subStr } = trieFound;
154
- // assert(subStr === value.slice(0, subStr.length));
155
- return this.createSubStringRef(tData, subStr);
156
- }
157
- stringSuffix(value) {
158
- if (!useSuffix)
159
- return undefined;
160
- const rev = reverse(value);
161
- const trieFound = this.knownStringsRev.find(rev);
162
- if (!trieFound || !trieFound.data || trieFound.found.length < minSubStringSuffixLen) {
163
- return undefined;
164
- }
165
- const { data: tData, found: subStr } = trieFound;
166
- // assert(subStr === value.slice(0, subStr.length));
167
- return this.createSubStringRef(tData, value.slice(-subStr.length), tData.length - subStr.length);
168
- }
169
- stringToRef(value) {
170
- const found = this.cache.get(value);
171
- if (found !== undefined) {
172
- return found;
173
- }
174
- if (forceStringPrimitives || value.length < minSubStringLen || blockSplitRegex.test(value)) {
175
- return this.addStringPrimitive(value);
176
- }
177
- const partialsPfx = [];
178
- const partialsSfx = [];
179
- let subStr = value;
180
- while (subStr.length) {
181
- const prefix = this.stringPrefix(subStr);
182
- const suffix = this.stringSuffix(subStr);
183
- if (!prefix && !suffix)
184
- break;
185
- if (prefix && prefix.length >= (suffix?.length || 0)) {
186
- partialsPfx.push(prefix);
187
- subStr = subStr.slice(prefix.length);
188
- }
189
- else {
190
- const sfx = suffix;
191
- partialsSfx.push(sfx);
192
- subStr = subStr.slice(0, -sfx.length);
193
- }
194
- }
195
- partialsSfx.reverse();
196
- if (!partialsPfx.length && !partialsSfx.length) {
197
- return this.addStringPrimitive(value);
198
- }
199
- if (subStr.length) {
200
- partialsPfx.push(this.stringToRef(subStr));
201
- }
202
- const partials = [...partialsPfx, ...partialsSfx];
203
- return this.addStringElement(value, partials.length === 1 ? partials[0] : new StringConcatRefElement(partials));
204
- }
205
- cvtSetToRef(value) {
206
- const found = this.cache.get(value);
207
- if (found !== undefined) {
208
- this.referenced.add(found);
209
- return found;
210
- }
211
- const element = this.addValueAndElement(value, new SetRefElement());
212
- element.setValues(this.createUniqueKeys([...value]));
213
- return this.dedupeSetRefs(value, element);
214
- }
215
- dedupeSetRefs(value, element) {
216
- if (!this.dedupe)
217
- return element;
218
- const values = element.valueRefs();
219
- const found = this.cachedSets.get(values);
220
- if (!found) {
221
- this.cachedSets.set(values, element);
222
- return element;
223
- }
224
- if (this.referenced.has(element))
225
- return element;
226
- this.knownElements.delete(element);
227
- this.cache.set(value, found);
228
- return found;
229
- }
230
- proxySetRef(ref) {
231
- return proxySet(new Set(this.#toValue(ref.valueRefs())), () => { });
232
- }
233
- createUniqueKeys(keys) {
234
- const cacheValue = false;
235
- let k = this.arrToRef(keys, cacheValue);
236
- const uniqueKeys = new Set(k.valueRefs());
237
- if (uniqueKeys.size !== keys.length) {
238
- // one or more of the keys got deduped. We need to duplicate it.
239
- uniqueKeys.clear();
240
- const values = k.valueRefs().map((ref) => {
241
- if (uniqueKeys.has(ref)) {
242
- return this.addElement(ref.clone());
243
- }
244
- uniqueKeys.add(ref);
245
- return ref;
246
- });
247
- k = this.addValueAndElement(keys, new ArrayRefElement(values), cacheValue);
248
- }
249
- return k;
250
- }
251
- cvtMapToRef(value) {
252
- const found = this.cache.get(value);
253
- if (found !== undefined) {
254
- this.referenced.add(found);
255
- return found;
256
- }
257
- const element = this.addValueAndElement(value, new MapRefElement());
258
- element.setKeysAndValues(this.createUniqueKeys([...value.keys()]), this.arrToRef([...value.values()], false));
259
- return this.dedupeMapRefs(value, element);
260
- }
261
- dedupeMapRefs(value, element) {
262
- if (!this.dedupe)
263
- return element;
264
- const keys = element.keyRefs();
265
- const values = element.valueRefs();
266
- let found = this.cachedMaps.get(keys);
267
- if (!found) {
268
- found = new Map();
269
- found.set(values, element);
270
- this.cachedMaps.set(keys, found);
271
- return element;
272
- }
273
- const foundValue = found.get(values);
274
- if (foundValue) {
275
- if (this.referenced.has(element))
276
- return element;
277
- this.knownElements.delete(element);
278
- this.cache.set(value, foundValue);
279
- return foundValue;
280
- }
281
- found.set(values, element);
282
- return element;
283
- }
284
- proxyMapRef(_ref) {
285
- return new Map();
286
- }
287
- cvtRegExpToRef(value) {
288
- const found = this.cache.get(value);
289
- if (found !== undefined) {
290
- return found;
291
- }
292
- return this.addValueAndElement(value, new RegExpRefElement(this.stringToRef(value.source), this.stringToRef(value.flags)));
293
- }
294
- cvtDateToRef(value) {
295
- const found = this.cache.get(value);
296
- if (found !== undefined) {
297
- return found;
298
- }
299
- return this.addValueAndElement(value, new DateRefElement(value.getTime()));
300
- }
301
- proxyDateRef(ref) {
302
- return proxyDate(ref.value, (date) => ref.setTime(date.getTime()));
303
- }
304
- cvtBigintToRef(value) {
305
- const found = this.cache.get(value);
306
- if (found !== undefined) {
307
- return found;
308
- }
309
- const valElement = this.primitiveToRef(value <= Number.MAX_SAFE_INTEGER && value >= -Number.MAX_SAFE_INTEGER ? Number(value) : value.toString());
310
- return this.addValueAndElement(value, new BigIntRefElement(valElement));
311
- }
312
- cvtObjToRef(value) {
313
- const found = this.cache.get(value);
314
- if (found !== undefined) {
315
- this.referenced.add(found);
316
- return found;
317
- }
318
- if (isObjectWrapper(value)) {
319
- const element = this.addValueAndElement(value, new ObjectWrapperRefElement());
320
- element.setValue(this.valueToRef(value.valueOf()));
321
- return element;
322
- }
323
- const entries = Object.entries(value);
324
- if (!entries.length) {
325
- if (this.emptyObjIdx) {
326
- return this.emptyObjIdx;
327
- }
328
- this.emptyObjIdx = this.addValueAndElement(value, new ObjectRefElement());
329
- return this.emptyObjIdx;
330
- }
331
- if (this.sortKeys) {
332
- entries.sort((a, b) => compare(a[0], b[0]));
333
- }
334
- const element = this.addValueAndElement(value, new ObjectRefElement());
335
- const k = this.arrToRef(entries.map(([key]) => key), false);
336
- const v = this.arrToRef(entries.map(([, value]) => value), false);
337
- element.setKeysAndValues(k, v);
338
- return this.dedupeObject(value, element);
339
- }
340
- dedupeObject(value, element) {
341
- if (!this.dedupe)
342
- return element;
343
- const keys = element.keyRefs();
344
- const values = element.valueRefs();
345
- let found = this.cachedObjects.get(keys);
346
- if (!found) {
347
- found = new Map();
348
- found.set(values, element);
349
- this.cachedObjects.set(keys, found);
350
- return element;
351
- }
352
- const foundValue = found.get(values);
353
- if (foundValue) {
354
- if (this.referenced.has(element))
355
- return element;
356
- this.knownElements.delete(element);
357
- this.cache.set(value, foundValue);
358
- return foundValue;
359
- }
360
- found.set(values, element);
361
- return element;
362
- }
363
- proxyObjectRef(ref) {
364
- const keys = this.#toValue(ref.keyRefs());
365
- const values = this.#toValue(ref.valueRefs());
366
- const obj = keys && values ? Object.fromEntries(keys.map((key, i) => [key, values[i]])) : {};
367
- return proxyObject(obj, (_value) => { });
368
- }
369
- proxyObjectWrapperRef(ref) {
370
- const value = Object(this.#toValue(ref.valueRef()));
371
- return proxyObject(value, (_value) => { });
372
- }
373
- /**
374
- *
375
- * @param value - The array converted to an ArrayRefElement.
376
- * @param element - the element to dedupe.
377
- * @param cacheValue - Whether to cache the value. It is false when it is a dynamic array, like object keys,
378
- * in that case, we want to dedupe the keys and values.
379
- * @returns the element to use.
380
- */
381
- dedupeArray(value, element, cacheValue) {
382
- if (cacheValue && !this.dedupe)
383
- return element;
384
- const indexHash = element.hash;
385
- let cached = this.cachedArrays.get(indexHash);
386
- if (!cached) {
387
- cached = [];
388
- this.cachedArrays.set(indexHash, cached);
389
- }
390
- const found = cached.find((entry) => element.isEqual(entry));
391
- if (found) {
392
- if (this.referenced.has(element))
393
- return element;
394
- this.knownElements.delete(element);
395
- if (cacheValue || this.cache.has(value)) {
396
- this.cache.set(value, found);
397
- }
398
- return found;
399
- }
400
- cached.push(element);
401
- return element;
402
- }
403
- /**
404
- * Convert an array to an index.
405
- * @param value - The array to convert to an index.
406
- * @param cacheValue - Whether to cache the value.
407
- * @returns the index of the array.
408
- */
409
- arrToRef(value, cacheValue = true) {
410
- const found = this.cache.get(value);
411
- if (found !== undefined) {
412
- this.referenced.add(found);
413
- return found;
414
- }
415
- const element = this.addValueAndElement(value, new ArrayRefElement(), cacheValue);
416
- element.setValues(value.map((v) => this.valueToRef(v)));
417
- return this.dedupeArray(value, element, cacheValue);
418
- }
419
- proxyArrayRef(ref) {
420
- const arr = ref.valueRefs().map((v) => this.#toValue(v));
421
- return proxyObject(arr, (_value) => { });
422
- }
423
- valueToRef(value) {
424
- if (value === null) {
425
- return this.primitiveToRef(value);
426
- }
427
- if (typeof value === 'object') {
428
- if (value instanceof Set) {
429
- return this.cvtSetToRef(value);
430
- }
431
- if (value instanceof Map) {
432
- return this.cvtMapToRef(value);
433
- }
434
- if (value instanceof RegExp) {
435
- return this.cvtRegExpToRef(value);
436
- }
437
- if (Array.isArray(value)) {
438
- return this.arrToRef(value);
439
- }
440
- if (value instanceof Date) {
441
- return this.cvtDateToRef(value);
442
- }
443
- return this.cvtObjToRef(value);
444
- }
445
- return this.primitiveToRef(value);
446
- }
447
- /**
448
- * Reset things in a way that allows for reuse.
449
- */
450
- softReset() {
451
- this.referenced.clear();
452
- if (this.root) {
453
- const idx = this.assignedElements.get(this.root);
454
- if (idx) {
455
- this.elements[idx] = undefined;
456
- this.assignedElements.delete(this.root);
457
- }
458
- }
459
- this.root = undefined;
460
- }
461
- #resolveRefs() {
462
- if (!this.root)
463
- return;
464
- const elements = this.elements;
465
- const assigned = this.assignedElements;
466
- const referenced = this.referenced;
467
- const availableIndexes = [];
468
- function addElement(ref) {
469
- if (assigned.has(ref))
470
- return false;
471
- const emptyCell = availableIndexes.pop();
472
- if (emptyCell) {
473
- assigned.set(ref, emptyCell);
474
- elements[emptyCell] = ref;
475
- return true;
476
- }
477
- const i = elements.push(ref) - 1;
478
- assigned.set(ref, i);
479
- return true;
480
- }
481
- function walk(ref, action) {
482
- function _walk(ref) {
483
- if (!action(ref))
484
- return;
485
- const deps = ref.getDependencies();
486
- if (!deps)
487
- return;
488
- for (const dep of deps) {
489
- _walk(dep);
490
- }
491
- }
492
- _walk(ref);
493
- }
494
- function calcReferences(root) {
495
- referenced.clear();
496
- walk(root, (ref) => {
497
- if (referenced.has(ref))
498
- return false;
499
- referenced.add(ref);
500
- return true;
501
- });
502
- }
503
- function calcAvailableIndexes() {
504
- availableIndexes.length = 0;
505
- for (let i = 1; i < elements.length; i++) {
506
- const ref = elements[i];
507
- if (!ref || !referenced.has(ref)) {
508
- availableIndexes.push(i);
509
- if (ref) {
510
- assigned.delete(ref);
511
- }
512
- elements[i] = undefined;
513
- }
514
- else {
515
- assigned.set(ref, i);
516
- }
517
- }
518
- availableIndexes.reverse();
519
- }
520
- function addElements(root) {
521
- walk(root, addElement);
522
- let i = elements.length - 1;
523
- for (; i > 0 && elements[i] === undefined; i--) {
524
- // empty
525
- }
526
- elements.length = i + 1;
527
- }
528
- calcReferences(this.root);
529
- elements[0] = this.refUndefined;
530
- assigned.set(this.refUndefined, 0);
531
- calcAvailableIndexes();
532
- addElements(this.root);
533
- this.#cleanCache();
534
- }
535
- /**
536
- * Remove objects from the cache after the FlatpackStore has been built.
537
- */
538
- #cleanCache() {
539
- const toRemove = new Set();
540
- for (const key of this.cache.keys()) {
541
- if (key && typeof key === 'object') {
542
- toRemove.add(key);
543
- }
544
- }
545
- for (const key of toRemove) {
546
- this.cache.delete(key);
547
- }
548
- }
549
- #resolveToValueProxy(ref) {
550
- if (!ref)
551
- return undefined;
552
- if (ref instanceof ArrayRefElement)
553
- return this.proxyArrayRef(ref);
554
- if (ref instanceof ObjectRefElement)
555
- return this.proxyObjectRef(ref);
556
- if (ref instanceof PrimitiveRefElementBase)
557
- return ref.value;
558
- if (isStringRefElements(ref))
559
- return ref.value;
560
- if (ref instanceof MapRefElement)
561
- return this.proxyMapRef(ref);
562
- if (ref instanceof SetRefElement)
563
- return this.proxySetRef(ref);
564
- if (ref instanceof BigIntRefElement)
565
- return ref.value;
566
- if (ref instanceof RegExpRefElement)
567
- return ref.value;
568
- if (ref instanceof DateRefElement)
569
- return this.proxyDateRef(ref);
570
- if (ref instanceof ObjectWrapperRefElement)
571
- return this.proxyObjectWrapperRef(ref);
572
- assert(false, 'Unknown ref type');
573
- }
574
- #toValue(ref) {
575
- if (!ref)
576
- return undefined;
577
- return getOrResolve(this.cachedProxies, ref, (ref) => this.#resolveToValueProxy(ref));
13
+ this.#flatpackApi.setValue(value);
578
14
  }
579
15
  toJSON() {
580
- const data = [dataHeader];
581
- const idxLookup = this.assignedElements;
582
- const lookup = (ref) => (ref && idxLookup.get(ref)) || 0;
583
- const elements = this.elements;
584
- for (let i = 1; i < elements.length; i++) {
585
- const element = elements[i];
586
- if (!element) {
587
- data.push(0);
588
- continue;
589
- }
590
- const value = element.toElement(lookup);
591
- if (value === undefined)
592
- continue;
593
- data.push(value);
594
- }
595
- return data;
16
+ return this.#flatpackApi.toJSON();
596
17
  }
597
18
  static fromJSON(data) {
598
19
  return new FlatpackStore(new FlatpackedWrapper(data));
@@ -606,24 +27,6 @@ export class FlatpackStore {
606
27
  toValue() {
607
28
  return fromJSON(this.toJSON());
608
29
  }
609
- _toValueProxy() {
610
- return this.#toValue(this.root);
611
- }
612
- }
613
- function isObjectWrapper(value) {
614
- return (typeof value === 'object' &&
615
- value !== null &&
616
- typeof value.valueOf === 'function' &&
617
- value.valueOf() !== value);
618
- }
619
- function prefix(value, len) {
620
- return value.length > len ? [...value].slice(0, len) : value;
621
- }
622
- function suffix(value, len) {
623
- return value.length > len ? [...value].slice(-len) : value;
624
- }
625
- function reverse(value) {
626
- return [...value].reverse();
627
30
  }
628
31
  export function toJSON(json, options) {
629
32
  return new FlatpackStore(json, options).toJSON();
@@ -631,12 +34,4 @@ export function toJSON(json, options) {
631
34
  export function stringify(data, pretty = true) {
632
35
  return pretty ? stringifyFlatpacked(toJSON(data)) : JSON.stringify(toJSON(data));
633
36
  }
634
- function getOrResolve(map, key, resolver) {
635
- let value = map.get(key);
636
- if (value === undefined && !map.has(key)) {
637
- value = resolver(key);
638
- map.set(key, value);
639
- }
640
- return value;
641
- }
642
37
  //# sourceMappingURL=Flatpack.mjs.map
@@ -0,0 +1,24 @@
1
+ import { StringTableBuilder } from './stringTable.mjs';
2
+ import type { Flatpacked, FlatpackIndex, FlattenedElement } from './types.mjs';
3
+ export declare class FlatpackData {
4
+ #private;
5
+ flatpack: Flatpacked;
6
+ stringTable: StringTableBuilder;
7
+ rootIndex: FlatpackIndex;
8
+ used: Set<FlatpackIndex>;
9
+ ownedBy: Map<FlatpackIndex, FlatpackIndex>;
10
+ available: FlatpackIndex[];
11
+ constructor(flatpack: Flatpacked | undefined);
12
+ add(element: FlattenedElement): FlatpackIndex;
13
+ reserve(): FlatpackIndex;
14
+ set(idx: FlatpackIndex, element: FlattenedElement): void;
15
+ get(idx: FlatpackIndex): FlattenedElement;
16
+ markUsed(idx: FlatpackIndex): void;
17
+ isUsed(idx: FlatpackIndex): boolean;
18
+ duplicateIndex(idx: FlatpackIndex): FlatpackIndex;
19
+ delete(idx: FlatpackIndex): void;
20
+ claimOwnership(idx: FlatpackIndex, ownerIdx: FlatpackIndex): FlatpackIndex;
21
+ markUnusedAsAvailable(): void;
22
+ finalize(): Flatpacked;
23
+ }
24
+ //# sourceMappingURL=FlatpackData.d.mts.map