hyperstack-typescript 0.2.5 → 0.3.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/index.d.ts +75 -37
- package/dist/index.esm.js +212 -185
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +214 -185
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var pako = require('pako');
|
|
4
|
+
|
|
3
5
|
const DEFAULT_MAX_ENTRIES_PER_VIEW = 10000;
|
|
4
6
|
const DEFAULT_CONFIG = {
|
|
5
7
|
reconnectIntervals: [1000, 2000, 4000, 8000, 16000],
|
|
@@ -15,16 +17,40 @@ class HyperStackError extends Error {
|
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
function isCompressedFrame(obj) {
|
|
21
|
+
return (typeof obj === 'object' &&
|
|
22
|
+
obj !== null &&
|
|
23
|
+
obj.compressed === 'gzip' &&
|
|
24
|
+
typeof obj.data === 'string');
|
|
25
|
+
}
|
|
26
|
+
function decompressGzip(base64Data) {
|
|
27
|
+
const binaryString = atob(base64Data);
|
|
28
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
29
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
30
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
31
|
+
}
|
|
32
|
+
const decompressed = pako.inflate(bytes);
|
|
33
|
+
return new TextDecoder().decode(decompressed);
|
|
34
|
+
}
|
|
35
|
+
function parseAndDecompress(jsonString) {
|
|
36
|
+
const parsed = JSON.parse(jsonString);
|
|
37
|
+
if (isCompressedFrame(parsed)) {
|
|
38
|
+
const decompressedJson = decompressGzip(parsed.data);
|
|
39
|
+
const frame = JSON.parse(decompressedJson);
|
|
40
|
+
return frame;
|
|
41
|
+
}
|
|
42
|
+
return parsed;
|
|
43
|
+
}
|
|
18
44
|
function isSnapshotFrame(frame) {
|
|
19
45
|
return frame.op === 'snapshot';
|
|
20
46
|
}
|
|
21
47
|
function parseFrame(data) {
|
|
22
48
|
if (typeof data === 'string') {
|
|
23
|
-
return
|
|
49
|
+
return parseAndDecompress(data);
|
|
24
50
|
}
|
|
25
51
|
const decoder = new TextDecoder('utf-8');
|
|
26
52
|
const jsonString = decoder.decode(data);
|
|
27
|
-
return
|
|
53
|
+
return parseAndDecompress(jsonString);
|
|
28
54
|
}
|
|
29
55
|
async function parseFrameFromBlob(blob) {
|
|
30
56
|
const arrayBuffer = await blob.arrayBuffer();
|
|
@@ -121,6 +147,7 @@ class ConnectionManager {
|
|
|
121
147
|
this.notifyFrameHandlers(frame);
|
|
122
148
|
}
|
|
123
149
|
catch (error) {
|
|
150
|
+
console.error('[hyperstack] Error parsing frame:', error);
|
|
124
151
|
this.updateState('error', 'Failed to parse frame from server');
|
|
125
152
|
}
|
|
126
153
|
};
|
|
@@ -253,57 +280,6 @@ class ConnectionManager {
|
|
|
253
280
|
}
|
|
254
281
|
}
|
|
255
282
|
|
|
256
|
-
class ViewData {
|
|
257
|
-
constructor() {
|
|
258
|
-
this.entities = new Map();
|
|
259
|
-
this.accessOrder = [];
|
|
260
|
-
}
|
|
261
|
-
get(key) {
|
|
262
|
-
return this.entities.get(key);
|
|
263
|
-
}
|
|
264
|
-
set(key, value) {
|
|
265
|
-
if (!this.entities.has(key)) {
|
|
266
|
-
this.accessOrder.push(key);
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
269
|
-
this.touch(key);
|
|
270
|
-
}
|
|
271
|
-
this.entities.set(key, value);
|
|
272
|
-
}
|
|
273
|
-
delete(key) {
|
|
274
|
-
const idx = this.accessOrder.indexOf(key);
|
|
275
|
-
if (idx !== -1) {
|
|
276
|
-
this.accessOrder.splice(idx, 1);
|
|
277
|
-
}
|
|
278
|
-
return this.entities.delete(key);
|
|
279
|
-
}
|
|
280
|
-
has(key) {
|
|
281
|
-
return this.entities.has(key);
|
|
282
|
-
}
|
|
283
|
-
values() {
|
|
284
|
-
return this.entities.values();
|
|
285
|
-
}
|
|
286
|
-
keys() {
|
|
287
|
-
return this.entities.keys();
|
|
288
|
-
}
|
|
289
|
-
get size() {
|
|
290
|
-
return this.entities.size;
|
|
291
|
-
}
|
|
292
|
-
touch(key) {
|
|
293
|
-
const idx = this.accessOrder.indexOf(key);
|
|
294
|
-
if (idx !== -1) {
|
|
295
|
-
this.accessOrder.splice(idx, 1);
|
|
296
|
-
this.accessOrder.push(key);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
evictOldest() {
|
|
300
|
-
const oldest = this.accessOrder.shift();
|
|
301
|
-
if (oldest !== undefined) {
|
|
302
|
-
this.entities.delete(oldest);
|
|
303
|
-
}
|
|
304
|
-
return oldest;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
283
|
function isObject(item) {
|
|
308
284
|
return item !== null && typeof item === 'object' && !Array.isArray(item);
|
|
309
285
|
}
|
|
@@ -333,189 +309,234 @@ function deepMergeWithAppend(target, source, appendPaths, currentPath = '') {
|
|
|
333
309
|
}
|
|
334
310
|
return result;
|
|
335
311
|
}
|
|
336
|
-
class
|
|
337
|
-
constructor(config = {}) {
|
|
338
|
-
this.
|
|
339
|
-
this.updateCallbacks = new Set();
|
|
340
|
-
this.richUpdateCallbacks = new Set();
|
|
312
|
+
class FrameProcessor {
|
|
313
|
+
constructor(storage, config = {}) {
|
|
314
|
+
this.storage = storage;
|
|
341
315
|
this.maxEntriesPerView = config.maxEntriesPerView === undefined
|
|
342
316
|
? DEFAULT_MAX_ENTRIES_PER_VIEW
|
|
343
317
|
: config.maxEntriesPerView;
|
|
344
318
|
}
|
|
345
|
-
enforceMaxEntries(viewData) {
|
|
346
|
-
if (this.maxEntriesPerView === null)
|
|
347
|
-
return;
|
|
348
|
-
while (viewData.size > this.maxEntriesPerView) {
|
|
349
|
-
viewData.evictOldest();
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
319
|
handleFrame(frame) {
|
|
353
320
|
if (isSnapshotFrame(frame)) {
|
|
354
321
|
this.handleSnapshotFrame(frame);
|
|
355
|
-
return;
|
|
356
322
|
}
|
|
357
|
-
|
|
323
|
+
else {
|
|
324
|
+
this.handleEntityFrame(frame);
|
|
325
|
+
}
|
|
358
326
|
}
|
|
359
327
|
handleSnapshotFrame(frame) {
|
|
360
328
|
const viewPath = frame.entity;
|
|
361
|
-
let viewData = this.views.get(viewPath);
|
|
362
|
-
if (!viewData) {
|
|
363
|
-
viewData = new ViewData();
|
|
364
|
-
this.views.set(viewPath, viewData);
|
|
365
|
-
}
|
|
366
329
|
for (const entity of frame.data) {
|
|
367
|
-
const previousValue =
|
|
368
|
-
|
|
369
|
-
this.notifyUpdate(viewPath, entity.key, {
|
|
330
|
+
const previousValue = this.storage.get(viewPath, entity.key);
|
|
331
|
+
this.storage.set(viewPath, entity.key, entity.data);
|
|
332
|
+
this.storage.notifyUpdate(viewPath, entity.key, {
|
|
370
333
|
type: 'upsert',
|
|
371
334
|
key: entity.key,
|
|
372
335
|
data: entity.data,
|
|
373
336
|
});
|
|
374
|
-
this.
|
|
337
|
+
this.emitRichUpdate(viewPath, entity.key, previousValue, entity.data, 'upsert');
|
|
375
338
|
}
|
|
376
|
-
this.enforceMaxEntries(
|
|
339
|
+
this.enforceMaxEntries(viewPath);
|
|
377
340
|
}
|
|
378
341
|
handleEntityFrame(frame) {
|
|
379
342
|
const viewPath = frame.entity;
|
|
380
|
-
|
|
381
|
-
if (!viewData) {
|
|
382
|
-
viewData = new ViewData();
|
|
383
|
-
this.views.set(viewPath, viewData);
|
|
384
|
-
}
|
|
385
|
-
const previousValue = viewData.get(frame.key);
|
|
343
|
+
const previousValue = this.storage.get(viewPath, frame.key);
|
|
386
344
|
switch (frame.op) {
|
|
387
345
|
case 'create':
|
|
388
346
|
case 'upsert':
|
|
389
|
-
|
|
390
|
-
this.enforceMaxEntries(
|
|
391
|
-
this.notifyUpdate(viewPath, frame.key, {
|
|
347
|
+
this.storage.set(viewPath, frame.key, frame.data);
|
|
348
|
+
this.enforceMaxEntries(viewPath);
|
|
349
|
+
this.storage.notifyUpdate(viewPath, frame.key, {
|
|
392
350
|
type: 'upsert',
|
|
393
351
|
key: frame.key,
|
|
394
352
|
data: frame.data,
|
|
395
353
|
});
|
|
396
|
-
this.
|
|
354
|
+
this.emitRichUpdate(viewPath, frame.key, previousValue, frame.data, frame.op);
|
|
397
355
|
break;
|
|
398
356
|
case 'patch': {
|
|
399
|
-
const existing =
|
|
357
|
+
const existing = this.storage.get(viewPath, frame.key);
|
|
400
358
|
const appendPaths = frame.append ?? [];
|
|
401
359
|
const merged = existing
|
|
402
360
|
? deepMergeWithAppend(existing, frame.data, appendPaths)
|
|
403
361
|
: frame.data;
|
|
404
|
-
|
|
405
|
-
this.enforceMaxEntries(
|
|
406
|
-
this.notifyUpdate(viewPath, frame.key, {
|
|
362
|
+
this.storage.set(viewPath, frame.key, merged);
|
|
363
|
+
this.enforceMaxEntries(viewPath);
|
|
364
|
+
this.storage.notifyUpdate(viewPath, frame.key, {
|
|
407
365
|
type: 'patch',
|
|
408
366
|
key: frame.key,
|
|
409
367
|
data: frame.data,
|
|
410
368
|
});
|
|
411
|
-
this.
|
|
369
|
+
this.emitRichUpdate(viewPath, frame.key, previousValue, merged, 'patch', frame.data);
|
|
412
370
|
break;
|
|
413
371
|
}
|
|
414
372
|
case 'delete':
|
|
415
|
-
|
|
416
|
-
this.notifyUpdate(viewPath, frame.key, {
|
|
373
|
+
this.storage.delete(viewPath, frame.key);
|
|
374
|
+
this.storage.notifyUpdate(viewPath, frame.key, {
|
|
417
375
|
type: 'delete',
|
|
418
376
|
key: frame.key,
|
|
419
377
|
});
|
|
420
|
-
if (previousValue !==
|
|
421
|
-
|
|
378
|
+
if (previousValue !== null) {
|
|
379
|
+
const richUpdate = { type: 'deleted', key: frame.key, lastKnown: previousValue };
|
|
380
|
+
this.storage.notifyRichUpdate(viewPath, frame.key, richUpdate);
|
|
422
381
|
}
|
|
423
382
|
break;
|
|
424
383
|
}
|
|
425
384
|
}
|
|
426
|
-
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
385
|
+
emitRichUpdate(viewPath, key, before, after, _op, patch) {
|
|
386
|
+
const richUpdate = before === null
|
|
387
|
+
? { type: 'created', key, data: after }
|
|
388
|
+
: { type: 'updated', key, before, after, patch };
|
|
389
|
+
this.storage.notifyRichUpdate(viewPath, key, richUpdate);
|
|
390
|
+
}
|
|
391
|
+
enforceMaxEntries(viewPath) {
|
|
392
|
+
if (this.maxEntriesPerView === null)
|
|
393
|
+
return;
|
|
394
|
+
if (!this.storage.evictOldest)
|
|
395
|
+
return;
|
|
396
|
+
while (this.storage.size(viewPath) > this.maxEntriesPerView) {
|
|
397
|
+
this.storage.evictOldest(viewPath);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
class ViewData {
|
|
403
|
+
constructor() {
|
|
404
|
+
this.entities = new Map();
|
|
405
|
+
this.accessOrder = [];
|
|
406
|
+
}
|
|
407
|
+
get(key) {
|
|
408
|
+
return this.entities.get(key);
|
|
409
|
+
}
|
|
410
|
+
set(key, value) {
|
|
411
|
+
if (!this.entities.has(key)) {
|
|
412
|
+
this.accessOrder.push(key);
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
this.touch(key);
|
|
416
|
+
}
|
|
417
|
+
this.entities.set(key, value);
|
|
418
|
+
}
|
|
419
|
+
delete(key) {
|
|
420
|
+
const idx = this.accessOrder.indexOf(key);
|
|
421
|
+
if (idx !== -1) {
|
|
422
|
+
this.accessOrder.splice(idx, 1);
|
|
423
|
+
}
|
|
424
|
+
return this.entities.delete(key);
|
|
425
|
+
}
|
|
426
|
+
has(key) {
|
|
427
|
+
return this.entities.has(key);
|
|
428
|
+
}
|
|
429
|
+
values() {
|
|
430
|
+
return this.entities.values();
|
|
431
|
+
}
|
|
432
|
+
keys() {
|
|
433
|
+
return this.entities.keys();
|
|
434
|
+
}
|
|
435
|
+
get size() {
|
|
436
|
+
return this.entities.size;
|
|
437
|
+
}
|
|
438
|
+
touch(key) {
|
|
439
|
+
const idx = this.accessOrder.indexOf(key);
|
|
440
|
+
if (idx !== -1) {
|
|
441
|
+
this.accessOrder.splice(idx, 1);
|
|
442
|
+
this.accessOrder.push(key);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
evictOldest() {
|
|
446
|
+
const oldest = this.accessOrder.shift();
|
|
447
|
+
if (oldest !== undefined) {
|
|
448
|
+
this.entities.delete(oldest);
|
|
449
|
+
}
|
|
450
|
+
return oldest;
|
|
451
|
+
}
|
|
452
|
+
clear() {
|
|
453
|
+
this.entities.clear();
|
|
454
|
+
this.accessOrder = [];
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
class MemoryAdapter {
|
|
458
|
+
constructor(_config = {}) {
|
|
459
|
+
this.views = new Map();
|
|
460
|
+
this.updateCallbacks = new Set();
|
|
461
|
+
this.richUpdateCallbacks = new Set();
|
|
431
462
|
}
|
|
432
463
|
get(viewPath, key) {
|
|
433
|
-
const
|
|
434
|
-
if (!
|
|
464
|
+
const view = this.views.get(viewPath);
|
|
465
|
+
if (!view)
|
|
435
466
|
return null;
|
|
436
|
-
const value =
|
|
467
|
+
const value = view.get(key);
|
|
437
468
|
return value !== undefined ? value : null;
|
|
438
469
|
}
|
|
470
|
+
getAll(viewPath) {
|
|
471
|
+
const view = this.views.get(viewPath);
|
|
472
|
+
if (!view)
|
|
473
|
+
return [];
|
|
474
|
+
return Array.from(view.values());
|
|
475
|
+
}
|
|
439
476
|
getAllSync(viewPath) {
|
|
440
|
-
const
|
|
441
|
-
if (!
|
|
477
|
+
const view = this.views.get(viewPath);
|
|
478
|
+
if (!view)
|
|
442
479
|
return undefined;
|
|
443
|
-
return Array.from(
|
|
480
|
+
return Array.from(view.values());
|
|
444
481
|
}
|
|
445
482
|
getSync(viewPath, key) {
|
|
446
|
-
const
|
|
447
|
-
if (!
|
|
483
|
+
const view = this.views.get(viewPath);
|
|
484
|
+
if (!view)
|
|
448
485
|
return undefined;
|
|
449
|
-
const value =
|
|
486
|
+
const value = view.get(key);
|
|
450
487
|
return value !== undefined ? value : null;
|
|
451
488
|
}
|
|
489
|
+
has(viewPath, key) {
|
|
490
|
+
return this.views.get(viewPath)?.has(key) ?? false;
|
|
491
|
+
}
|
|
452
492
|
keys(viewPath) {
|
|
453
|
-
const
|
|
454
|
-
if (!
|
|
493
|
+
const view = this.views.get(viewPath);
|
|
494
|
+
if (!view)
|
|
455
495
|
return [];
|
|
456
|
-
return Array.from(
|
|
496
|
+
return Array.from(view.keys());
|
|
457
497
|
}
|
|
458
498
|
size(viewPath) {
|
|
459
|
-
|
|
460
|
-
return viewData?.size ?? 0;
|
|
499
|
+
return this.views.get(viewPath)?.size ?? 0;
|
|
461
500
|
}
|
|
462
|
-
|
|
463
|
-
this.views.
|
|
501
|
+
set(viewPath, key, data) {
|
|
502
|
+
let view = this.views.get(viewPath);
|
|
503
|
+
if (!view) {
|
|
504
|
+
view = new ViewData();
|
|
505
|
+
this.views.set(viewPath, view);
|
|
506
|
+
}
|
|
507
|
+
view.set(key, data);
|
|
508
|
+
}
|
|
509
|
+
delete(viewPath, key) {
|
|
510
|
+
this.views.get(viewPath)?.delete(key);
|
|
464
511
|
}
|
|
465
|
-
|
|
466
|
-
|
|
512
|
+
clear(viewPath) {
|
|
513
|
+
if (viewPath) {
|
|
514
|
+
this.views.get(viewPath)?.clear();
|
|
515
|
+
this.views.delete(viewPath);
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
this.views.clear();
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
evictOldest(viewPath) {
|
|
522
|
+
return this.views.get(viewPath)?.evictOldest();
|
|
467
523
|
}
|
|
468
524
|
onUpdate(callback) {
|
|
469
525
|
this.updateCallbacks.add(callback);
|
|
470
|
-
return () =>
|
|
471
|
-
this.updateCallbacks.delete(callback);
|
|
472
|
-
};
|
|
526
|
+
return () => this.updateCallbacks.delete(callback);
|
|
473
527
|
}
|
|
474
528
|
onRichUpdate(callback) {
|
|
475
529
|
this.richUpdateCallbacks.add(callback);
|
|
476
|
-
return () =>
|
|
477
|
-
this.richUpdateCallbacks.delete(callback);
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
subscribe(viewPath, callback) {
|
|
481
|
-
const handler = (path, _key, update) => {
|
|
482
|
-
if (path === viewPath) {
|
|
483
|
-
callback(update);
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
this.updateCallbacks.add(handler);
|
|
487
|
-
return () => {
|
|
488
|
-
this.updateCallbacks.delete(handler);
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
subscribeToKey(viewPath, key, callback) {
|
|
492
|
-
const handler = (path, updateKey, update) => {
|
|
493
|
-
if (path === viewPath && updateKey === key) {
|
|
494
|
-
callback(update);
|
|
495
|
-
}
|
|
496
|
-
};
|
|
497
|
-
this.updateCallbacks.add(handler);
|
|
498
|
-
return () => {
|
|
499
|
-
this.updateCallbacks.delete(handler);
|
|
500
|
-
};
|
|
530
|
+
return () => this.richUpdateCallbacks.delete(callback);
|
|
501
531
|
}
|
|
502
532
|
notifyUpdate(viewPath, key, update) {
|
|
503
533
|
for (const callback of this.updateCallbacks) {
|
|
504
534
|
callback(viewPath, key, update);
|
|
505
535
|
}
|
|
506
536
|
}
|
|
507
|
-
notifyRichUpdate(viewPath, key,
|
|
508
|
-
const richUpdate = before === undefined
|
|
509
|
-
? { type: 'created', key, data: after }
|
|
510
|
-
: { type: 'updated', key, before, after, patch };
|
|
511
|
-
for (const callback of this.richUpdateCallbacks) {
|
|
512
|
-
callback(viewPath, key, richUpdate);
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
notifyRichDelete(viewPath, key, lastKnown) {
|
|
516
|
-
const richUpdate = { type: 'deleted', key, lastKnown };
|
|
537
|
+
notifyRichUpdate(viewPath, key, update) {
|
|
517
538
|
for (const callback of this.richUpdateCallbacks) {
|
|
518
|
-
callback(viewPath, key,
|
|
539
|
+
callback(viewPath, key, update);
|
|
519
540
|
}
|
|
520
541
|
}
|
|
521
542
|
}
|
|
@@ -571,12 +592,12 @@ class SubscriptionRegistry {
|
|
|
571
592
|
}
|
|
572
593
|
|
|
573
594
|
const MAX_QUEUE_SIZE = 1000;
|
|
574
|
-
function createUpdateStream(
|
|
595
|
+
function createUpdateStream(storage, subscriptionRegistry, subscription, keyFilter) {
|
|
575
596
|
return {
|
|
576
597
|
[Symbol.asyncIterator]() {
|
|
577
598
|
const queue = [];
|
|
578
599
|
let waitingResolve = null;
|
|
579
|
-
let
|
|
600
|
+
let unsubscribeStorage = null;
|
|
580
601
|
let unsubscribeRegistry = null;
|
|
581
602
|
let done = false;
|
|
582
603
|
const handler = (viewPath, key, update) => {
|
|
@@ -601,12 +622,12 @@ function createUpdateStream(store, subscriptionRegistry, subscription, keyFilter
|
|
|
601
622
|
}
|
|
602
623
|
};
|
|
603
624
|
const start = () => {
|
|
604
|
-
|
|
625
|
+
unsubscribeStorage = storage.onUpdate(handler);
|
|
605
626
|
unsubscribeRegistry = subscriptionRegistry.subscribe(subscription);
|
|
606
627
|
};
|
|
607
628
|
const cleanup = () => {
|
|
608
629
|
done = true;
|
|
609
|
-
|
|
630
|
+
unsubscribeStorage?.();
|
|
610
631
|
unsubscribeRegistry?.();
|
|
611
632
|
};
|
|
612
633
|
start();
|
|
@@ -635,12 +656,12 @@ function createUpdateStream(store, subscriptionRegistry, subscription, keyFilter
|
|
|
635
656
|
},
|
|
636
657
|
};
|
|
637
658
|
}
|
|
638
|
-
function createRichUpdateStream(
|
|
659
|
+
function createRichUpdateStream(storage, subscriptionRegistry, subscription, keyFilter) {
|
|
639
660
|
return {
|
|
640
661
|
[Symbol.asyncIterator]() {
|
|
641
662
|
const queue = [];
|
|
642
663
|
let waitingResolve = null;
|
|
643
|
-
let
|
|
664
|
+
let unsubscribeStorage = null;
|
|
644
665
|
let unsubscribeRegistry = null;
|
|
645
666
|
let done = false;
|
|
646
667
|
const handler = (viewPath, key, update) => {
|
|
@@ -665,12 +686,12 @@ function createRichUpdateStream(store, subscriptionRegistry, subscription, keyFi
|
|
|
665
686
|
}
|
|
666
687
|
};
|
|
667
688
|
const start = () => {
|
|
668
|
-
|
|
689
|
+
unsubscribeStorage = storage.onRichUpdate(handler);
|
|
669
690
|
unsubscribeRegistry = subscriptionRegistry.subscribe(subscription);
|
|
670
691
|
};
|
|
671
692
|
const cleanup = () => {
|
|
672
693
|
done = true;
|
|
673
|
-
|
|
694
|
+
unsubscribeStorage?.();
|
|
674
695
|
unsubscribeRegistry?.();
|
|
675
696
|
};
|
|
676
697
|
start();
|
|
@@ -700,48 +721,48 @@ function createRichUpdateStream(store, subscriptionRegistry, subscription, keyFi
|
|
|
700
721
|
};
|
|
701
722
|
}
|
|
702
723
|
|
|
703
|
-
function createTypedStateView(viewDef,
|
|
724
|
+
function createTypedStateView(viewDef, storage, subscriptionRegistry) {
|
|
704
725
|
return {
|
|
705
726
|
watch(key) {
|
|
706
|
-
return createUpdateStream(
|
|
727
|
+
return createUpdateStream(storage, subscriptionRegistry, { view: viewDef.view, key }, key);
|
|
707
728
|
},
|
|
708
729
|
watchRich(key) {
|
|
709
|
-
return createRichUpdateStream(
|
|
730
|
+
return createRichUpdateStream(storage, subscriptionRegistry, { view: viewDef.view, key }, key);
|
|
710
731
|
},
|
|
711
732
|
async get(key) {
|
|
712
|
-
return
|
|
733
|
+
return storage.get(viewDef.view, key);
|
|
713
734
|
},
|
|
714
735
|
getSync(key) {
|
|
715
|
-
return
|
|
736
|
+
return storage.getSync(viewDef.view, key);
|
|
716
737
|
},
|
|
717
738
|
};
|
|
718
739
|
}
|
|
719
|
-
function createTypedListView(viewDef,
|
|
740
|
+
function createTypedListView(viewDef, storage, subscriptionRegistry) {
|
|
720
741
|
return {
|
|
721
742
|
watch() {
|
|
722
|
-
return createUpdateStream(
|
|
743
|
+
return createUpdateStream(storage, subscriptionRegistry, { view: viewDef.view });
|
|
723
744
|
},
|
|
724
745
|
watchRich() {
|
|
725
|
-
return createRichUpdateStream(
|
|
746
|
+
return createRichUpdateStream(storage, subscriptionRegistry, { view: viewDef.view });
|
|
726
747
|
},
|
|
727
748
|
async get() {
|
|
728
|
-
return
|
|
749
|
+
return storage.getAll(viewDef.view);
|
|
729
750
|
},
|
|
730
751
|
getSync() {
|
|
731
|
-
return
|
|
752
|
+
return storage.getAllSync(viewDef.view);
|
|
732
753
|
},
|
|
733
754
|
};
|
|
734
755
|
}
|
|
735
|
-
function createTypedViews(stack,
|
|
756
|
+
function createTypedViews(stack, storage, subscriptionRegistry) {
|
|
736
757
|
const views = {};
|
|
737
758
|
for (const [viewName, viewGroup] of Object.entries(stack.views)) {
|
|
738
759
|
const group = viewGroup;
|
|
739
760
|
const typedGroup = {};
|
|
740
761
|
if (group.state) {
|
|
741
|
-
typedGroup.state = createTypedStateView(group.state,
|
|
762
|
+
typedGroup.state = createTypedStateView(group.state, storage, subscriptionRegistry);
|
|
742
763
|
}
|
|
743
764
|
if (group.list) {
|
|
744
|
-
typedGroup.list = createTypedListView(group.list,
|
|
765
|
+
typedGroup.list = createTypedListView(group.list, storage, subscriptionRegistry);
|
|
745
766
|
}
|
|
746
767
|
views[viewName] = typedGroup;
|
|
747
768
|
}
|
|
@@ -751,7 +772,10 @@ function createTypedViews(stack, store, subscriptionRegistry) {
|
|
|
751
772
|
class HyperStack {
|
|
752
773
|
constructor(url, options) {
|
|
753
774
|
this.stack = options.stack;
|
|
754
|
-
this.
|
|
775
|
+
this.storage = options.storage ?? new MemoryAdapter();
|
|
776
|
+
this.processor = new FrameProcessor(this.storage, {
|
|
777
|
+
maxEntriesPerView: options.maxEntriesPerView,
|
|
778
|
+
});
|
|
755
779
|
this.connection = new ConnectionManager({
|
|
756
780
|
websocketUrl: url,
|
|
757
781
|
reconnectIntervals: options.reconnectIntervals,
|
|
@@ -759,9 +783,9 @@ class HyperStack {
|
|
|
759
783
|
});
|
|
760
784
|
this.subscriptionRegistry = new SubscriptionRegistry(this.connection);
|
|
761
785
|
this.connection.onFrame((frame) => {
|
|
762
|
-
this.
|
|
786
|
+
this.processor.handleFrame(frame);
|
|
763
787
|
});
|
|
764
|
-
this._views = createTypedViews(this.stack, this.
|
|
788
|
+
this._views = createTypedViews(this.stack, this.storage, this.subscriptionRegistry);
|
|
765
789
|
}
|
|
766
790
|
static async connect(url, options) {
|
|
767
791
|
if (!url) {
|
|
@@ -785,6 +809,9 @@ class HyperStack {
|
|
|
785
809
|
get stackName() {
|
|
786
810
|
return this.stack.name;
|
|
787
811
|
}
|
|
812
|
+
get store() {
|
|
813
|
+
return this.storage;
|
|
814
|
+
}
|
|
788
815
|
onConnectionStateChange(callback) {
|
|
789
816
|
return this.connection.onStateChange(callback);
|
|
790
817
|
}
|
|
@@ -802,10 +829,10 @@ class HyperStack {
|
|
|
802
829
|
return this.connection.isConnected();
|
|
803
830
|
}
|
|
804
831
|
clearStore() {
|
|
805
|
-
this.
|
|
832
|
+
this.storage.clear();
|
|
806
833
|
}
|
|
807
834
|
getStore() {
|
|
808
|
-
return this.
|
|
835
|
+
return this.storage;
|
|
809
836
|
}
|
|
810
837
|
getConnection() {
|
|
811
838
|
return this.connection;
|
|
@@ -817,9 +844,11 @@ class HyperStack {
|
|
|
817
844
|
|
|
818
845
|
exports.ConnectionManager = ConnectionManager;
|
|
819
846
|
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
820
|
-
exports.
|
|
847
|
+
exports.DEFAULT_MAX_ENTRIES_PER_VIEW = DEFAULT_MAX_ENTRIES_PER_VIEW;
|
|
848
|
+
exports.FrameProcessor = FrameProcessor;
|
|
821
849
|
exports.HyperStack = HyperStack;
|
|
822
850
|
exports.HyperStackError = HyperStackError;
|
|
851
|
+
exports.MemoryAdapter = MemoryAdapter;
|
|
823
852
|
exports.SubscriptionRegistry = SubscriptionRegistry;
|
|
824
853
|
exports.createRichUpdateStream = createRichUpdateStream;
|
|
825
854
|
exports.createTypedListView = createTypedListView;
|