hyperstack-react 0.3.15 → 0.4.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/dist/index.d.ts +18 -20
- package/dist/index.esm.js +381 -342
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +381 -342
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -7385,6 +7385,198 @@ class MemoryAdapter {
|
|
|
7385
7385
|
}
|
|
7386
7386
|
}
|
|
7387
7387
|
|
|
7388
|
+
function getNestedValue$1(obj, path) {
|
|
7389
|
+
let current = obj;
|
|
7390
|
+
for (const segment of path) {
|
|
7391
|
+
if (current === null || current === undefined)
|
|
7392
|
+
return undefined;
|
|
7393
|
+
if (typeof current !== 'object')
|
|
7394
|
+
return undefined;
|
|
7395
|
+
current = current[segment];
|
|
7396
|
+
}
|
|
7397
|
+
return current;
|
|
7398
|
+
}
|
|
7399
|
+
function compareSortValues$1(a, b) {
|
|
7400
|
+
if (a === b)
|
|
7401
|
+
return 0;
|
|
7402
|
+
if (a === undefined || a === null)
|
|
7403
|
+
return -1;
|
|
7404
|
+
if (b === undefined || b === null)
|
|
7405
|
+
return 1;
|
|
7406
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
7407
|
+
return a - b;
|
|
7408
|
+
}
|
|
7409
|
+
if (typeof a === 'string' && typeof b === 'string') {
|
|
7410
|
+
return a.localeCompare(b);
|
|
7411
|
+
}
|
|
7412
|
+
if (typeof a === 'boolean' && typeof b === 'boolean') {
|
|
7413
|
+
return (a ? 1 : 0) - (b ? 1 : 0);
|
|
7414
|
+
}
|
|
7415
|
+
return String(a).localeCompare(String(b));
|
|
7416
|
+
}
|
|
7417
|
+
class SortedStorageDecorator {
|
|
7418
|
+
constructor(inner) {
|
|
7419
|
+
this.sortConfigs = new Map();
|
|
7420
|
+
this.sortedKeysMap = new Map();
|
|
7421
|
+
this.inner = inner;
|
|
7422
|
+
}
|
|
7423
|
+
get(viewPath, key) {
|
|
7424
|
+
return this.inner.get(viewPath, key);
|
|
7425
|
+
}
|
|
7426
|
+
getAll(viewPath) {
|
|
7427
|
+
const sortedKeys = this.sortedKeysMap.get(viewPath);
|
|
7428
|
+
if (sortedKeys && sortedKeys.length > 0) {
|
|
7429
|
+
return sortedKeys
|
|
7430
|
+
.map(k => this.inner.get(viewPath, k))
|
|
7431
|
+
.filter((v) => v !== null);
|
|
7432
|
+
}
|
|
7433
|
+
return this.inner.getAll(viewPath);
|
|
7434
|
+
}
|
|
7435
|
+
getAllSync(viewPath) {
|
|
7436
|
+
const sortedKeys = this.sortedKeysMap.get(viewPath);
|
|
7437
|
+
if (sortedKeys && sortedKeys.length > 0) {
|
|
7438
|
+
return sortedKeys
|
|
7439
|
+
.map(k => this.inner.getSync(viewPath, k))
|
|
7440
|
+
.filter((v) => v !== null && v !== undefined);
|
|
7441
|
+
}
|
|
7442
|
+
return this.inner.getAllSync(viewPath);
|
|
7443
|
+
}
|
|
7444
|
+
getSync(viewPath, key) {
|
|
7445
|
+
return this.inner.getSync(viewPath, key);
|
|
7446
|
+
}
|
|
7447
|
+
has(viewPath, key) {
|
|
7448
|
+
return this.inner.has(viewPath, key);
|
|
7449
|
+
}
|
|
7450
|
+
keys(viewPath) {
|
|
7451
|
+
const sortedKeys = this.sortedKeysMap.get(viewPath);
|
|
7452
|
+
if (sortedKeys)
|
|
7453
|
+
return [...sortedKeys];
|
|
7454
|
+
return this.inner.keys(viewPath);
|
|
7455
|
+
}
|
|
7456
|
+
size(viewPath) {
|
|
7457
|
+
return this.inner.size(viewPath);
|
|
7458
|
+
}
|
|
7459
|
+
set(viewPath, key, data) {
|
|
7460
|
+
this.inner.set(viewPath, key, data);
|
|
7461
|
+
const sortConfig = this.sortConfigs.get(viewPath);
|
|
7462
|
+
if (sortConfig) {
|
|
7463
|
+
this.updateSortedPosition(viewPath, key, data, sortConfig);
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
delete(viewPath, key) {
|
|
7467
|
+
const sortedKeys = this.sortedKeysMap.get(viewPath);
|
|
7468
|
+
if (sortedKeys) {
|
|
7469
|
+
const idx = sortedKeys.indexOf(key);
|
|
7470
|
+
if (idx !== -1) {
|
|
7471
|
+
sortedKeys.splice(idx, 1);
|
|
7472
|
+
}
|
|
7473
|
+
}
|
|
7474
|
+
this.inner.delete(viewPath, key);
|
|
7475
|
+
}
|
|
7476
|
+
clear(viewPath) {
|
|
7477
|
+
if (viewPath) {
|
|
7478
|
+
this.sortedKeysMap.delete(viewPath);
|
|
7479
|
+
this.sortConfigs.delete(viewPath);
|
|
7480
|
+
}
|
|
7481
|
+
else {
|
|
7482
|
+
this.sortedKeysMap.clear();
|
|
7483
|
+
this.sortConfigs.clear();
|
|
7484
|
+
}
|
|
7485
|
+
this.inner.clear(viewPath);
|
|
7486
|
+
}
|
|
7487
|
+
evictOldest(viewPath) {
|
|
7488
|
+
const sortedKeys = this.sortedKeysMap.get(viewPath);
|
|
7489
|
+
if (sortedKeys && sortedKeys.length > 0) {
|
|
7490
|
+
const oldest = sortedKeys.pop();
|
|
7491
|
+
this.inner.delete(viewPath, oldest);
|
|
7492
|
+
return oldest;
|
|
7493
|
+
}
|
|
7494
|
+
return this.inner.evictOldest?.(viewPath);
|
|
7495
|
+
}
|
|
7496
|
+
setViewConfig(viewPath, config) {
|
|
7497
|
+
if (config.sort && !this.sortConfigs.has(viewPath)) {
|
|
7498
|
+
this.sortConfigs.set(viewPath, config.sort);
|
|
7499
|
+
this.rebuildSortedKeys(viewPath, config.sort);
|
|
7500
|
+
}
|
|
7501
|
+
this.inner.setViewConfig?.(viewPath, config);
|
|
7502
|
+
}
|
|
7503
|
+
getViewConfig(viewPath) {
|
|
7504
|
+
const sortConfig = this.sortConfigs.get(viewPath);
|
|
7505
|
+
if (sortConfig)
|
|
7506
|
+
return { sort: sortConfig };
|
|
7507
|
+
return this.inner.getViewConfig?.(viewPath);
|
|
7508
|
+
}
|
|
7509
|
+
onUpdate(callback) {
|
|
7510
|
+
return this.inner.onUpdate(callback);
|
|
7511
|
+
}
|
|
7512
|
+
onRichUpdate(callback) {
|
|
7513
|
+
return this.inner.onRichUpdate(callback);
|
|
7514
|
+
}
|
|
7515
|
+
notifyUpdate(viewPath, key, update) {
|
|
7516
|
+
this.inner.notifyUpdate(viewPath, key, update);
|
|
7517
|
+
}
|
|
7518
|
+
notifyRichUpdate(viewPath, key, update) {
|
|
7519
|
+
this.inner.notifyRichUpdate(viewPath, key, update);
|
|
7520
|
+
}
|
|
7521
|
+
updateSortedPosition(viewPath, key, data, sortConfig) {
|
|
7522
|
+
let sortedKeys = this.sortedKeysMap.get(viewPath);
|
|
7523
|
+
if (!sortedKeys) {
|
|
7524
|
+
sortedKeys = [];
|
|
7525
|
+
this.sortedKeysMap.set(viewPath, sortedKeys);
|
|
7526
|
+
}
|
|
7527
|
+
const existingIdx = sortedKeys.indexOf(key);
|
|
7528
|
+
if (existingIdx !== -1) {
|
|
7529
|
+
sortedKeys.splice(existingIdx, 1);
|
|
7530
|
+
}
|
|
7531
|
+
const insertIdx = this.binarySearchInsertPosition(viewPath, sortedKeys, sortConfig, key, data);
|
|
7532
|
+
sortedKeys.splice(insertIdx, 0, key);
|
|
7533
|
+
}
|
|
7534
|
+
binarySearchInsertPosition(viewPath, sortedKeys, sortConfig, newKey, newValue) {
|
|
7535
|
+
const newSortValue = getNestedValue$1(newValue, sortConfig.field);
|
|
7536
|
+
const isDesc = sortConfig.order === 'desc';
|
|
7537
|
+
let low = 0;
|
|
7538
|
+
let high = sortedKeys.length;
|
|
7539
|
+
while (low < high) {
|
|
7540
|
+
const mid = Math.floor((low + high) / 2);
|
|
7541
|
+
const midKey = sortedKeys[mid];
|
|
7542
|
+
const midEntity = this.inner.get(viewPath, midKey);
|
|
7543
|
+
const midValue = getNestedValue$1(midEntity, sortConfig.field);
|
|
7544
|
+
let cmp = compareSortValues$1(newSortValue, midValue);
|
|
7545
|
+
if (isDesc)
|
|
7546
|
+
cmp = -cmp;
|
|
7547
|
+
if (cmp === 0) {
|
|
7548
|
+
cmp = newKey.localeCompare(midKey);
|
|
7549
|
+
}
|
|
7550
|
+
if (cmp < 0) {
|
|
7551
|
+
high = mid;
|
|
7552
|
+
}
|
|
7553
|
+
else {
|
|
7554
|
+
low = mid + 1;
|
|
7555
|
+
}
|
|
7556
|
+
}
|
|
7557
|
+
return low;
|
|
7558
|
+
}
|
|
7559
|
+
rebuildSortedKeys(viewPath, sortConfig) {
|
|
7560
|
+
const allKeys = this.inner.keys(viewPath);
|
|
7561
|
+
if (allKeys.length === 0)
|
|
7562
|
+
return;
|
|
7563
|
+
const isDesc = sortConfig.order === 'desc';
|
|
7564
|
+
const entries = allKeys.map(k => [k, this.inner.get(viewPath, k)]);
|
|
7565
|
+
entries.sort((a, b) => {
|
|
7566
|
+
const aValue = getNestedValue$1(a[1], sortConfig.field);
|
|
7567
|
+
const bValue = getNestedValue$1(b[1], sortConfig.field);
|
|
7568
|
+
let cmp = compareSortValues$1(aValue, bValue);
|
|
7569
|
+
if (isDesc)
|
|
7570
|
+
cmp = -cmp;
|
|
7571
|
+
if (cmp === 0) {
|
|
7572
|
+
cmp = a[0].localeCompare(b[0]);
|
|
7573
|
+
}
|
|
7574
|
+
return cmp;
|
|
7575
|
+
});
|
|
7576
|
+
this.sortedKeysMap.set(viewPath, entries.map(([k]) => k));
|
|
7577
|
+
}
|
|
7578
|
+
}
|
|
7579
|
+
|
|
7388
7580
|
class SubscriptionRegistry {
|
|
7389
7581
|
constructor(connection) {
|
|
7390
7582
|
this.subscriptions = new Map();
|
|
@@ -8177,9 +8369,10 @@ function createInstructionExecutor(wallet) {
|
|
|
8177
8369
|
class HyperStack {
|
|
8178
8370
|
constructor(url, options) {
|
|
8179
8371
|
this.stack = options.stack;
|
|
8180
|
-
this.storage = options.storage ?? new MemoryAdapter();
|
|
8372
|
+
this.storage = new SortedStorageDecorator(options.storage ?? new MemoryAdapter());
|
|
8181
8373
|
this.processor = new FrameProcessor(this.storage, {
|
|
8182
8374
|
maxEntriesPerView: options.maxEntriesPerView,
|
|
8375
|
+
flushIntervalMs: options.flushIntervalMs,
|
|
8183
8376
|
});
|
|
8184
8377
|
this.connection = new ConnectionManager({
|
|
8185
8378
|
websocketUrl: url,
|
|
@@ -8213,6 +8406,7 @@ class HyperStack {
|
|
|
8213
8406
|
stack,
|
|
8214
8407
|
storage: options?.storage,
|
|
8215
8408
|
maxEntriesPerView: options?.maxEntriesPerView,
|
|
8409
|
+
flushIntervalMs: options?.flushIntervalMs,
|
|
8216
8410
|
autoReconnect: options?.autoReconnect,
|
|
8217
8411
|
reconnectIntervals: options?.reconnectIntervals,
|
|
8218
8412
|
maxReconnectAttempts: options?.maxReconnectAttempts,
|
|
@@ -8268,67 +8462,212 @@ class HyperStack {
|
|
|
8268
8462
|
}
|
|
8269
8463
|
}
|
|
8270
8464
|
|
|
8271
|
-
const
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8465
|
+
const DEFAULT_FLUSH_INTERVAL_MS = 16;
|
|
8466
|
+
|
|
8467
|
+
class ZustandAdapter {
|
|
8468
|
+
constructor(_config = {}) {
|
|
8469
|
+
this.updateCallbacks = new Set();
|
|
8470
|
+
this.richUpdateCallbacks = new Set();
|
|
8471
|
+
this.accessOrder = new Map();
|
|
8472
|
+
this.store = zustand.create((set) => ({
|
|
8473
|
+
entities: new Map(),
|
|
8474
|
+
viewConfigs: new Map(),
|
|
8475
|
+
connectionState: 'disconnected',
|
|
8476
|
+
lastError: undefined,
|
|
8477
|
+
_set: (viewPath, key, data) => {
|
|
8478
|
+
set((state) => {
|
|
8479
|
+
const newEntities = new Map(state.entities);
|
|
8480
|
+
const viewMap = new Map(newEntities.get(viewPath) ?? new Map());
|
|
8481
|
+
viewMap.set(key, data);
|
|
8482
|
+
newEntities.set(viewPath, viewMap);
|
|
8483
|
+
return { entities: newEntities };
|
|
8484
|
+
});
|
|
8485
|
+
},
|
|
8486
|
+
_delete: (viewPath, key) => {
|
|
8487
|
+
set((state) => {
|
|
8488
|
+
const newEntities = new Map(state.entities);
|
|
8489
|
+
const viewMap = newEntities.get(viewPath);
|
|
8490
|
+
if (viewMap) {
|
|
8491
|
+
const newViewMap = new Map(viewMap);
|
|
8492
|
+
newViewMap.delete(key);
|
|
8493
|
+
newEntities.set(viewPath, newViewMap);
|
|
8494
|
+
}
|
|
8495
|
+
return { entities: newEntities };
|
|
8496
|
+
});
|
|
8497
|
+
},
|
|
8498
|
+
_clear: (viewPath) => {
|
|
8499
|
+
set((state) => {
|
|
8500
|
+
if (viewPath) {
|
|
8501
|
+
const newEntities = new Map(state.entities);
|
|
8502
|
+
newEntities.delete(viewPath);
|
|
8503
|
+
return { entities: newEntities };
|
|
8504
|
+
}
|
|
8505
|
+
return { entities: new Map() };
|
|
8506
|
+
});
|
|
8507
|
+
},
|
|
8508
|
+
_setConnectionState: (connectionState, lastError) => {
|
|
8509
|
+
set({ connectionState, lastError });
|
|
8510
|
+
},
|
|
8511
|
+
_setViewConfig: (viewPath, config) => {
|
|
8512
|
+
set((state) => {
|
|
8513
|
+
const newConfigs = new Map(state.viewConfigs);
|
|
8514
|
+
newConfigs.set(viewPath, config);
|
|
8515
|
+
return { viewConfigs: newConfigs };
|
|
8516
|
+
});
|
|
8517
|
+
},
|
|
8518
|
+
}));
|
|
8278
8519
|
}
|
|
8279
|
-
|
|
8280
|
-
|
|
8520
|
+
get(viewPath, key) {
|
|
8521
|
+
const viewMap = this.store.getState().entities.get(viewPath);
|
|
8522
|
+
if (!viewMap)
|
|
8523
|
+
return null;
|
|
8524
|
+
const value = viewMap.get(key);
|
|
8525
|
+
return value !== undefined ? value : null;
|
|
8281
8526
|
}
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8527
|
+
getAll(viewPath) {
|
|
8528
|
+
const viewMap = this.store.getState().entities.get(viewPath);
|
|
8529
|
+
if (!viewMap)
|
|
8530
|
+
return [];
|
|
8531
|
+
return Array.from(viewMap.values());
|
|
8287
8532
|
}
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8533
|
+
getAllSync(viewPath) {
|
|
8534
|
+
const viewMap = this.store.getState().entities.get(viewPath);
|
|
8535
|
+
if (!viewMap)
|
|
8536
|
+
return undefined;
|
|
8537
|
+
return Array.from(viewMap.values());
|
|
8293
8538
|
}
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8539
|
+
getSync(viewPath, key) {
|
|
8540
|
+
const viewMap = this.store.getState().entities.get(viewPath);
|
|
8541
|
+
if (!viewMap)
|
|
8542
|
+
return undefined;
|
|
8543
|
+
const value = viewMap.get(key);
|
|
8544
|
+
return value !== undefined ? value : null;
|
|
8545
|
+
}
|
|
8546
|
+
has(viewPath, key) {
|
|
8547
|
+
return this.store.getState().entities.get(viewPath)?.has(key) ?? false;
|
|
8548
|
+
}
|
|
8549
|
+
keys(viewPath) {
|
|
8550
|
+
const viewMap = this.store.getState().entities.get(viewPath);
|
|
8551
|
+
if (!viewMap)
|
|
8552
|
+
return [];
|
|
8553
|
+
return Array.from(viewMap.keys());
|
|
8554
|
+
}
|
|
8555
|
+
size(viewPath) {
|
|
8556
|
+
return this.store.getState().entities.get(viewPath)?.size ?? 0;
|
|
8557
|
+
}
|
|
8558
|
+
set(viewPath, key, data) {
|
|
8559
|
+
let order = this.accessOrder.get(viewPath);
|
|
8560
|
+
if (!order) {
|
|
8561
|
+
order = [];
|
|
8562
|
+
this.accessOrder.set(viewPath, order);
|
|
8563
|
+
}
|
|
8564
|
+
const existingIdx = order.indexOf(key);
|
|
8565
|
+
if (existingIdx !== -1) {
|
|
8566
|
+
order.splice(existingIdx, 1);
|
|
8567
|
+
}
|
|
8568
|
+
order.push(key);
|
|
8569
|
+
this.store.getState()._set(viewPath, key, data);
|
|
8570
|
+
}
|
|
8571
|
+
delete(viewPath, key) {
|
|
8572
|
+
const order = this.accessOrder.get(viewPath);
|
|
8573
|
+
if (order) {
|
|
8574
|
+
const idx = order.indexOf(key);
|
|
8575
|
+
if (idx !== -1) {
|
|
8576
|
+
order.splice(idx, 1);
|
|
8577
|
+
}
|
|
8578
|
+
}
|
|
8579
|
+
this.store.getState()._delete(viewPath, key);
|
|
8580
|
+
}
|
|
8581
|
+
clear(viewPath) {
|
|
8582
|
+
if (viewPath) {
|
|
8583
|
+
this.accessOrder.delete(viewPath);
|
|
8584
|
+
}
|
|
8585
|
+
else {
|
|
8586
|
+
this.accessOrder.clear();
|
|
8587
|
+
}
|
|
8588
|
+
this.store.getState()._clear(viewPath);
|
|
8589
|
+
}
|
|
8590
|
+
evictOldest(viewPath) {
|
|
8591
|
+
const order = this.accessOrder.get(viewPath);
|
|
8592
|
+
if (!order || order.length === 0)
|
|
8593
|
+
return undefined;
|
|
8594
|
+
const oldest = order.shift();
|
|
8595
|
+
if (oldest !== undefined) {
|
|
8596
|
+
this.store.getState()._delete(viewPath, oldest);
|
|
8597
|
+
}
|
|
8598
|
+
return oldest;
|
|
8599
|
+
}
|
|
8600
|
+
onUpdate(callback) {
|
|
8601
|
+
this.updateCallbacks.add(callback);
|
|
8602
|
+
return () => this.updateCallbacks.delete(callback);
|
|
8603
|
+
}
|
|
8604
|
+
onRichUpdate(callback) {
|
|
8605
|
+
this.richUpdateCallbacks.add(callback);
|
|
8606
|
+
return () => this.richUpdateCallbacks.delete(callback);
|
|
8607
|
+
}
|
|
8608
|
+
notifyUpdate(viewPath, key, update) {
|
|
8609
|
+
for (const callback of this.updateCallbacks) {
|
|
8610
|
+
callback(viewPath, key, update);
|
|
8611
|
+
}
|
|
8612
|
+
}
|
|
8613
|
+
notifyRichUpdate(viewPath, key, update) {
|
|
8614
|
+
for (const callback of this.richUpdateCallbacks) {
|
|
8615
|
+
callback(viewPath, key, update);
|
|
8616
|
+
}
|
|
8617
|
+
}
|
|
8618
|
+
setConnectionState(state, error) {
|
|
8619
|
+
this.store.getState()._setConnectionState(state, error);
|
|
8620
|
+
}
|
|
8621
|
+
setViewConfig(viewPath, config) {
|
|
8622
|
+
const state = this.store.getState();
|
|
8623
|
+
const existingConfig = state.viewConfigs.get(viewPath);
|
|
8624
|
+
if (existingConfig)
|
|
8625
|
+
return;
|
|
8626
|
+
state._setViewConfig(viewPath, config);
|
|
8627
|
+
}
|
|
8628
|
+
getViewConfig(viewPath) {
|
|
8629
|
+
return this.store.getState().viewConfigs.get(viewPath);
|
|
8299
8630
|
}
|
|
8300
|
-
throw new Error('Must provide either network or websocketUrl');
|
|
8301
8631
|
}
|
|
8632
|
+
|
|
8633
|
+
const HyperstackContext = React.createContext(null);
|
|
8302
8634
|
function HyperstackProvider({ children, fallback = null, ...config }) {
|
|
8303
|
-
const networkConfig = resolveNetworkConfig(config.network, config.websocketUrl);
|
|
8304
8635
|
const clientsRef = React.useRef(new Map());
|
|
8305
8636
|
const connectingRef = React.useRef(new Map());
|
|
8306
|
-
const getOrCreateClient = React.useCallback(async (stack) => {
|
|
8307
|
-
const
|
|
8637
|
+
const getOrCreateClient = React.useCallback(async (stack, urlOverride) => {
|
|
8638
|
+
const cacheKey = urlOverride ? `${stack.name}:${urlOverride}` : stack.name;
|
|
8639
|
+
const existing = clientsRef.current.get(cacheKey);
|
|
8308
8640
|
if (existing) {
|
|
8309
8641
|
return existing.client;
|
|
8310
8642
|
}
|
|
8311
|
-
const connecting = connectingRef.current.get(
|
|
8643
|
+
const connecting = connectingRef.current.get(cacheKey);
|
|
8312
8644
|
if (connecting) {
|
|
8313
8645
|
return connecting;
|
|
8314
8646
|
}
|
|
8647
|
+
const adapter = new ZustandAdapter();
|
|
8315
8648
|
const connectionPromise = HyperStack.connect(stack, {
|
|
8316
|
-
url:
|
|
8649
|
+
url: urlOverride,
|
|
8650
|
+
storage: adapter,
|
|
8317
8651
|
autoReconnect: config.autoConnect,
|
|
8318
8652
|
reconnectIntervals: config.reconnectIntervals,
|
|
8319
8653
|
maxReconnectAttempts: config.maxReconnectAttempts,
|
|
8320
8654
|
maxEntriesPerView: config.maxEntriesPerView,
|
|
8655
|
+
flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,
|
|
8321
8656
|
}).then((client) => {
|
|
8322
|
-
|
|
8657
|
+
client.onConnectionStateChange((state, error) => {
|
|
8658
|
+
adapter.setConnectionState(state, error);
|
|
8659
|
+
});
|
|
8660
|
+
adapter.setConnectionState(client.connectionState);
|
|
8661
|
+
clientsRef.current.set(cacheKey, {
|
|
8323
8662
|
client,
|
|
8324
8663
|
disconnect: () => client.disconnect()
|
|
8325
8664
|
});
|
|
8326
|
-
connectingRef.current.delete(
|
|
8665
|
+
connectingRef.current.delete(cacheKey);
|
|
8327
8666
|
return client;
|
|
8328
8667
|
});
|
|
8329
|
-
connectingRef.current.set(
|
|
8668
|
+
connectingRef.current.set(cacheKey, connectionPromise);
|
|
8330
8669
|
return connectionPromise;
|
|
8331
|
-
}, [
|
|
8670
|
+
}, [config.autoConnect, config.reconnectIntervals, config.maxReconnectAttempts, config.maxEntriesPerView]);
|
|
8332
8671
|
const getClient = React.useCallback((stack) => {
|
|
8333
8672
|
if (!stack)
|
|
8334
8673
|
return null;
|
|
@@ -8347,13 +8686,7 @@ function HyperstackProvider({ children, fallback = null, ...config }) {
|
|
|
8347
8686
|
const value = {
|
|
8348
8687
|
getOrCreateClient,
|
|
8349
8688
|
getClient,
|
|
8350
|
-
config
|
|
8351
|
-
websocketUrl: networkConfig.websocketUrl,
|
|
8352
|
-
autoConnect: config.autoConnect,
|
|
8353
|
-
reconnectIntervals: config.reconnectIntervals,
|
|
8354
|
-
maxReconnectAttempts: config.maxReconnectAttempts,
|
|
8355
|
-
maxEntriesPerView: config.maxEntriesPerView,
|
|
8356
|
-
}
|
|
8689
|
+
config,
|
|
8357
8690
|
};
|
|
8358
8691
|
return (React.createElement(HyperstackContext.Provider, { value: value }, children));
|
|
8359
8692
|
}
|
|
@@ -8701,21 +9034,22 @@ function useInstructionMutation(execute) {
|
|
|
8701
9034
|
};
|
|
8702
9035
|
}
|
|
8703
9036
|
|
|
8704
|
-
function useHyperstack(stack) {
|
|
9037
|
+
function useHyperstack(stack, options) {
|
|
8705
9038
|
const { getOrCreateClient, getClient } = useHyperstackContext();
|
|
9039
|
+
const urlOverride = options?.url;
|
|
8706
9040
|
const [client, setClient] = React.useState(getClient(stack));
|
|
8707
9041
|
const [isLoading, setIsLoading] = React.useState(!client);
|
|
8708
9042
|
const [error, setError] = React.useState(null);
|
|
8709
9043
|
React.useEffect(() => {
|
|
8710
9044
|
const existingClient = getClient(stack);
|
|
8711
|
-
if (existingClient) {
|
|
9045
|
+
if (existingClient && !urlOverride) {
|
|
8712
9046
|
setClient(existingClient);
|
|
8713
9047
|
setIsLoading(false);
|
|
8714
9048
|
return;
|
|
8715
9049
|
}
|
|
8716
9050
|
setIsLoading(true);
|
|
8717
9051
|
setError(null);
|
|
8718
|
-
getOrCreateClient(stack)
|
|
9052
|
+
getOrCreateClient(stack, urlOverride)
|
|
8719
9053
|
.then((newClient) => {
|
|
8720
9054
|
setClient(newClient);
|
|
8721
9055
|
setIsLoading(false);
|
|
@@ -8724,7 +9058,7 @@ function useHyperstack(stack) {
|
|
|
8724
9058
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
8725
9059
|
setIsLoading(false);
|
|
8726
9060
|
});
|
|
8727
|
-
}, [stack, getOrCreateClient, getClient]);
|
|
9061
|
+
}, [stack, getOrCreateClient, getClient, urlOverride]);
|
|
8728
9062
|
const views = React.useMemo(() => {
|
|
8729
9063
|
const result = {};
|
|
8730
9064
|
for (const [viewName, viewGroup] of Object.entries(stack.views)) {
|
|
@@ -8759,308 +9093,13 @@ function useHyperstack(stack) {
|
|
|
8759
9093
|
return {
|
|
8760
9094
|
views: views,
|
|
8761
9095
|
instructions: instructions,
|
|
8762
|
-
zustandStore: client?.store,
|
|
9096
|
+
zustandStore: client?.store?.store,
|
|
8763
9097
|
client: client,
|
|
8764
9098
|
isLoading,
|
|
8765
9099
|
error
|
|
8766
9100
|
};
|
|
8767
9101
|
}
|
|
8768
9102
|
|
|
8769
|
-
function getNestedValue(obj, path) {
|
|
8770
|
-
let current = obj;
|
|
8771
|
-
for (const segment of path) {
|
|
8772
|
-
if (current === null || current === undefined)
|
|
8773
|
-
return undefined;
|
|
8774
|
-
if (typeof current !== 'object')
|
|
8775
|
-
return undefined;
|
|
8776
|
-
current = current[segment];
|
|
8777
|
-
}
|
|
8778
|
-
return current;
|
|
8779
|
-
}
|
|
8780
|
-
function compareSortValues(a, b) {
|
|
8781
|
-
if (a === b)
|
|
8782
|
-
return 0;
|
|
8783
|
-
if (a === undefined || a === null)
|
|
8784
|
-
return -1;
|
|
8785
|
-
if (b === undefined || b === null)
|
|
8786
|
-
return 1;
|
|
8787
|
-
if (typeof a === 'number' && typeof b === 'number') {
|
|
8788
|
-
return a - b;
|
|
8789
|
-
}
|
|
8790
|
-
if (typeof a === 'string' && typeof b === 'string') {
|
|
8791
|
-
return a.localeCompare(b);
|
|
8792
|
-
}
|
|
8793
|
-
if (typeof a === 'boolean' && typeof b === 'boolean') {
|
|
8794
|
-
return (a ? 1 : 0) - (b ? 1 : 0);
|
|
8795
|
-
}
|
|
8796
|
-
return String(a).localeCompare(String(b));
|
|
8797
|
-
}
|
|
8798
|
-
function binarySearchInsertPosition(sortedKeys, entities, sortConfig, newKey, newValue) {
|
|
8799
|
-
const newSortValue = getNestedValue(newValue, sortConfig.field);
|
|
8800
|
-
const isDesc = sortConfig.order === 'desc';
|
|
8801
|
-
let low = 0;
|
|
8802
|
-
let high = sortedKeys.length;
|
|
8803
|
-
while (low < high) {
|
|
8804
|
-
const mid = Math.floor((low + high) / 2);
|
|
8805
|
-
const midKey = sortedKeys[mid];
|
|
8806
|
-
const midEntity = entities.get(midKey);
|
|
8807
|
-
const midValue = getNestedValue(midEntity, sortConfig.field);
|
|
8808
|
-
let cmp = compareSortValues(newSortValue, midValue);
|
|
8809
|
-
if (isDesc)
|
|
8810
|
-
cmp = -cmp;
|
|
8811
|
-
if (cmp === 0) {
|
|
8812
|
-
cmp = newKey.localeCompare(midKey);
|
|
8813
|
-
}
|
|
8814
|
-
if (cmp < 0) {
|
|
8815
|
-
high = mid;
|
|
8816
|
-
}
|
|
8817
|
-
else {
|
|
8818
|
-
low = mid + 1;
|
|
8819
|
-
}
|
|
8820
|
-
}
|
|
8821
|
-
return low;
|
|
8822
|
-
}
|
|
8823
|
-
class ZustandAdapter {
|
|
8824
|
-
constructor(_config = {}) {
|
|
8825
|
-
this.updateCallbacks = new Set();
|
|
8826
|
-
this.richUpdateCallbacks = new Set();
|
|
8827
|
-
this.accessOrder = new Map();
|
|
8828
|
-
this.store = zustand.create((set) => ({
|
|
8829
|
-
entities: new Map(),
|
|
8830
|
-
sortedKeys: new Map(),
|
|
8831
|
-
viewConfigs: new Map(),
|
|
8832
|
-
connectionState: 'disconnected',
|
|
8833
|
-
lastError: undefined,
|
|
8834
|
-
_set: (viewPath, key, data) => {
|
|
8835
|
-
set((state) => {
|
|
8836
|
-
const newEntities = new Map(state.entities);
|
|
8837
|
-
const viewMap = new Map(newEntities.get(viewPath) ?? new Map());
|
|
8838
|
-
viewMap.set(key, data);
|
|
8839
|
-
newEntities.set(viewPath, viewMap);
|
|
8840
|
-
return { entities: newEntities };
|
|
8841
|
-
});
|
|
8842
|
-
},
|
|
8843
|
-
_delete: (viewPath, key) => {
|
|
8844
|
-
set((state) => {
|
|
8845
|
-
const newEntities = new Map(state.entities);
|
|
8846
|
-
const viewMap = newEntities.get(viewPath);
|
|
8847
|
-
if (viewMap) {
|
|
8848
|
-
const newViewMap = new Map(viewMap);
|
|
8849
|
-
newViewMap.delete(key);
|
|
8850
|
-
newEntities.set(viewPath, newViewMap);
|
|
8851
|
-
}
|
|
8852
|
-
return { entities: newEntities };
|
|
8853
|
-
});
|
|
8854
|
-
},
|
|
8855
|
-
_clear: (viewPath) => {
|
|
8856
|
-
set((state) => {
|
|
8857
|
-
if (viewPath) {
|
|
8858
|
-
const newEntities = new Map(state.entities);
|
|
8859
|
-
newEntities.delete(viewPath);
|
|
8860
|
-
const newSortedKeys = new Map(state.sortedKeys);
|
|
8861
|
-
newSortedKeys.delete(viewPath);
|
|
8862
|
-
return { entities: newEntities, sortedKeys: newSortedKeys };
|
|
8863
|
-
}
|
|
8864
|
-
return { entities: new Map(), sortedKeys: new Map() };
|
|
8865
|
-
});
|
|
8866
|
-
},
|
|
8867
|
-
_setConnectionState: (connectionState, lastError) => {
|
|
8868
|
-
set({ connectionState, lastError });
|
|
8869
|
-
},
|
|
8870
|
-
_setViewConfig: (viewPath, config) => {
|
|
8871
|
-
set((state) => {
|
|
8872
|
-
const newConfigs = new Map(state.viewConfigs);
|
|
8873
|
-
newConfigs.set(viewPath, config);
|
|
8874
|
-
return { viewConfigs: newConfigs };
|
|
8875
|
-
});
|
|
8876
|
-
},
|
|
8877
|
-
_updateSortedKeys: (viewPath, newSortedKeys) => {
|
|
8878
|
-
set((state) => {
|
|
8879
|
-
const newSortedKeysMap = new Map(state.sortedKeys);
|
|
8880
|
-
newSortedKeysMap.set(viewPath, newSortedKeys);
|
|
8881
|
-
return { sortedKeys: newSortedKeysMap };
|
|
8882
|
-
});
|
|
8883
|
-
},
|
|
8884
|
-
}));
|
|
8885
|
-
}
|
|
8886
|
-
get(viewPath, key) {
|
|
8887
|
-
const entities = this.store.getState().entities;
|
|
8888
|
-
const viewMap = entities.get(viewPath);
|
|
8889
|
-
if (!viewMap)
|
|
8890
|
-
return null;
|
|
8891
|
-
const value = viewMap.get(key);
|
|
8892
|
-
return value !== undefined ? value : null;
|
|
8893
|
-
}
|
|
8894
|
-
getAll(viewPath) {
|
|
8895
|
-
const state = this.store.getState();
|
|
8896
|
-
const viewMap = state.entities.get(viewPath);
|
|
8897
|
-
if (!viewMap)
|
|
8898
|
-
return [];
|
|
8899
|
-
const sortedKeys = state.sortedKeys.get(viewPath);
|
|
8900
|
-
if (sortedKeys && sortedKeys.length > 0) {
|
|
8901
|
-
return sortedKeys.map(k => viewMap.get(k)).filter(v => v !== undefined);
|
|
8902
|
-
}
|
|
8903
|
-
return Array.from(viewMap.values());
|
|
8904
|
-
}
|
|
8905
|
-
getAllSync(viewPath) {
|
|
8906
|
-
const state = this.store.getState();
|
|
8907
|
-
const viewMap = state.entities.get(viewPath);
|
|
8908
|
-
if (!viewMap)
|
|
8909
|
-
return undefined;
|
|
8910
|
-
const sortedKeys = state.sortedKeys.get(viewPath);
|
|
8911
|
-
if (sortedKeys && sortedKeys.length > 0) {
|
|
8912
|
-
return sortedKeys.map(k => viewMap.get(k)).filter(v => v !== undefined);
|
|
8913
|
-
}
|
|
8914
|
-
return Array.from(viewMap.values());
|
|
8915
|
-
}
|
|
8916
|
-
getSync(viewPath, key) {
|
|
8917
|
-
const entities = this.store.getState().entities;
|
|
8918
|
-
const viewMap = entities.get(viewPath);
|
|
8919
|
-
if (!viewMap)
|
|
8920
|
-
return undefined;
|
|
8921
|
-
const value = viewMap.get(key);
|
|
8922
|
-
return value !== undefined ? value : null;
|
|
8923
|
-
}
|
|
8924
|
-
has(viewPath, key) {
|
|
8925
|
-
return this.store.getState().entities.get(viewPath)?.has(key) ?? false;
|
|
8926
|
-
}
|
|
8927
|
-
keys(viewPath) {
|
|
8928
|
-
const viewMap = this.store.getState().entities.get(viewPath);
|
|
8929
|
-
if (!viewMap)
|
|
8930
|
-
return [];
|
|
8931
|
-
return Array.from(viewMap.keys());
|
|
8932
|
-
}
|
|
8933
|
-
size(viewPath) {
|
|
8934
|
-
return this.store.getState().entities.get(viewPath)?.size ?? 0;
|
|
8935
|
-
}
|
|
8936
|
-
set(viewPath, key, data) {
|
|
8937
|
-
const state = this.store.getState();
|
|
8938
|
-
const viewConfig = state.viewConfigs.get(viewPath);
|
|
8939
|
-
if (viewConfig?.sort) {
|
|
8940
|
-
const viewMap = state.entities.get(viewPath) ?? new Map();
|
|
8941
|
-
const currentSortedKeys = [...(state.sortedKeys.get(viewPath) ?? [])];
|
|
8942
|
-
const existingIdx = currentSortedKeys.indexOf(key);
|
|
8943
|
-
if (existingIdx !== -1) {
|
|
8944
|
-
currentSortedKeys.splice(existingIdx, 1);
|
|
8945
|
-
}
|
|
8946
|
-
const tempMap = new Map(viewMap);
|
|
8947
|
-
tempMap.set(key, data);
|
|
8948
|
-
const insertIdx = binarySearchInsertPosition(currentSortedKeys, tempMap, viewConfig.sort, key, data);
|
|
8949
|
-
currentSortedKeys.splice(insertIdx, 0, key);
|
|
8950
|
-
state._set(viewPath, key, data);
|
|
8951
|
-
state._updateSortedKeys(viewPath, currentSortedKeys);
|
|
8952
|
-
}
|
|
8953
|
-
else {
|
|
8954
|
-
let order = this.accessOrder.get(viewPath);
|
|
8955
|
-
if (!order) {
|
|
8956
|
-
order = [];
|
|
8957
|
-
this.accessOrder.set(viewPath, order);
|
|
8958
|
-
}
|
|
8959
|
-
const existingIdx = order.indexOf(key);
|
|
8960
|
-
if (existingIdx !== -1) {
|
|
8961
|
-
order.splice(existingIdx, 1);
|
|
8962
|
-
}
|
|
8963
|
-
order.push(key);
|
|
8964
|
-
state._set(viewPath, key, data);
|
|
8965
|
-
}
|
|
8966
|
-
}
|
|
8967
|
-
delete(viewPath, key) {
|
|
8968
|
-
const state = this.store.getState();
|
|
8969
|
-
const viewConfig = state.viewConfigs.get(viewPath);
|
|
8970
|
-
if (viewConfig?.sort) {
|
|
8971
|
-
const currentSortedKeys = state.sortedKeys.get(viewPath);
|
|
8972
|
-
if (currentSortedKeys) {
|
|
8973
|
-
const newSortedKeys = currentSortedKeys.filter(k => k !== key);
|
|
8974
|
-
state._updateSortedKeys(viewPath, newSortedKeys);
|
|
8975
|
-
}
|
|
8976
|
-
}
|
|
8977
|
-
else {
|
|
8978
|
-
const order = this.accessOrder.get(viewPath);
|
|
8979
|
-
if (order) {
|
|
8980
|
-
const idx = order.indexOf(key);
|
|
8981
|
-
if (idx !== -1) {
|
|
8982
|
-
order.splice(idx, 1);
|
|
8983
|
-
}
|
|
8984
|
-
}
|
|
8985
|
-
}
|
|
8986
|
-
state._delete(viewPath, key);
|
|
8987
|
-
}
|
|
8988
|
-
clear(viewPath) {
|
|
8989
|
-
if (viewPath) {
|
|
8990
|
-
this.accessOrder.delete(viewPath);
|
|
8991
|
-
}
|
|
8992
|
-
else {
|
|
8993
|
-
this.accessOrder.clear();
|
|
8994
|
-
}
|
|
8995
|
-
this.store.getState()._clear(viewPath);
|
|
8996
|
-
}
|
|
8997
|
-
evictOldest(viewPath) {
|
|
8998
|
-
const order = this.accessOrder.get(viewPath);
|
|
8999
|
-
if (!order || order.length === 0)
|
|
9000
|
-
return undefined;
|
|
9001
|
-
const oldest = order.shift();
|
|
9002
|
-
if (oldest !== undefined) {
|
|
9003
|
-
this.store.getState()._delete(viewPath, oldest);
|
|
9004
|
-
}
|
|
9005
|
-
return oldest;
|
|
9006
|
-
}
|
|
9007
|
-
onUpdate(callback) {
|
|
9008
|
-
this.updateCallbacks.add(callback);
|
|
9009
|
-
return () => this.updateCallbacks.delete(callback);
|
|
9010
|
-
}
|
|
9011
|
-
onRichUpdate(callback) {
|
|
9012
|
-
this.richUpdateCallbacks.add(callback);
|
|
9013
|
-
return () => this.richUpdateCallbacks.delete(callback);
|
|
9014
|
-
}
|
|
9015
|
-
notifyUpdate(viewPath, key, update) {
|
|
9016
|
-
for (const callback of this.updateCallbacks) {
|
|
9017
|
-
callback(viewPath, key, update);
|
|
9018
|
-
}
|
|
9019
|
-
}
|
|
9020
|
-
notifyRichUpdate(viewPath, key, update) {
|
|
9021
|
-
for (const callback of this.richUpdateCallbacks) {
|
|
9022
|
-
callback(viewPath, key, update);
|
|
9023
|
-
}
|
|
9024
|
-
}
|
|
9025
|
-
setConnectionState(state, error) {
|
|
9026
|
-
this.store.getState()._setConnectionState(state, error);
|
|
9027
|
-
}
|
|
9028
|
-
setViewConfig(viewPath, config) {
|
|
9029
|
-
const state = this.store.getState();
|
|
9030
|
-
const existingConfig = state.viewConfigs.get(viewPath);
|
|
9031
|
-
if (existingConfig?.sort)
|
|
9032
|
-
return;
|
|
9033
|
-
state._setViewConfig(viewPath, config);
|
|
9034
|
-
if (config.sort) {
|
|
9035
|
-
this.rebuildSortedKeys(viewPath, config.sort);
|
|
9036
|
-
}
|
|
9037
|
-
}
|
|
9038
|
-
getViewConfig(viewPath) {
|
|
9039
|
-
return this.store.getState().viewConfigs.get(viewPath);
|
|
9040
|
-
}
|
|
9041
|
-
rebuildSortedKeys(viewPath, sortConfig) {
|
|
9042
|
-
const state = this.store.getState();
|
|
9043
|
-
const viewMap = state.entities.get(viewPath);
|
|
9044
|
-
if (!viewMap || viewMap.size === 0)
|
|
9045
|
-
return;
|
|
9046
|
-
const isDesc = sortConfig.order === 'desc';
|
|
9047
|
-
const entries = Array.from(viewMap.entries());
|
|
9048
|
-
entries.sort((a, b) => {
|
|
9049
|
-
const aValue = getNestedValue(a[1], sortConfig.field);
|
|
9050
|
-
const bValue = getNestedValue(b[1], sortConfig.field);
|
|
9051
|
-
let cmp = compareSortValues(aValue, bValue);
|
|
9052
|
-
if (isDesc)
|
|
9053
|
-
cmp = -cmp;
|
|
9054
|
-
if (cmp === 0) {
|
|
9055
|
-
cmp = a[0].localeCompare(b[0]);
|
|
9056
|
-
}
|
|
9057
|
-
return cmp;
|
|
9058
|
-
});
|
|
9059
|
-
const sortedKeys = entries.map(([k]) => k);
|
|
9060
|
-
state._updateSortedKeys(viewPath, sortedKeys);
|
|
9061
|
-
}
|
|
9062
|
-
}
|
|
9063
|
-
|
|
9064
9103
|
exports.ConnectionManager = ConnectionManager;
|
|
9065
9104
|
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
9066
9105
|
exports.DEFAULT_MAX_ENTRIES_PER_VIEW = DEFAULT_MAX_ENTRIES_PER_VIEW;
|