holosphere 2.0.0-alpha10 → 2.0.0-alpha11
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/cjs/holosphere.cjs +1 -1
- package/dist/esm/holosphere.js +1 -1
- package/dist/{index-DeZ1xz_s.js → index-Bl6rM1NW.js} +2 -2
- package/dist/{index-DeZ1xz_s.js.map → index-Bl6rM1NW.js.map} +1 -1
- package/dist/{index-DDGt_V9o.cjs → index-Bwg3OzRM.cjs} +2 -2
- package/dist/{index-DDGt_V9o.cjs.map → index-Bwg3OzRM.cjs.map} +1 -1
- package/dist/{index-DJXftyvB.js → index-D-jZhliX.js} +531 -98
- package/dist/index-D-jZhliX.js.map +1 -0
- package/dist/index-Dc6Z8Aob.cjs +18 -0
- package/dist/index-Dc6Z8Aob.cjs.map +1 -0
- package/dist/{indexeddb-storage-BFt6hMeF.js → indexeddb-storage-5eiUNsHC.js} +2 -2
- package/dist/{indexeddb-storage-BFt6hMeF.js.map → indexeddb-storage-5eiUNsHC.js.map} +1 -1
- package/dist/{indexeddb-storage-BK5tv4Sh.cjs → indexeddb-storage-FNFUVvTJ.cjs} +2 -2
- package/dist/{indexeddb-storage-BK5tv4Sh.cjs.map → indexeddb-storage-FNFUVvTJ.cjs.map} +1 -1
- package/dist/{memory-storage-C9HuoL2E.js → memory-storage-CI-gfmuG.js} +2 -2
- package/dist/{memory-storage-C9HuoL2E.js.map → memory-storage-CI-gfmuG.js.map} +1 -1
- package/dist/{memory-storage-Dao7jfYG.cjs → memory-storage-DMt36uZO.cjs} +2 -2
- package/dist/{memory-storage-Dao7jfYG.cjs.map → memory-storage-DMt36uZO.cjs.map} +1 -1
- package/dist/{secp256k1-CreY7Pcl.js → secp256k1-CEwJNcfV.js} +2 -2
- package/dist/{secp256k1-CreY7Pcl.js.map → secp256k1-CEwJNcfV.js.map} +1 -1
- package/dist/{secp256k1-BbKzbLtD.cjs → secp256k1-CiEONUnj.cjs} +2 -2
- package/dist/{secp256k1-BbKzbLtD.cjs.map → secp256k1-CiEONUnj.cjs.map} +1 -1
- package/package.json +1 -1
- package/src/index.js +62 -19
- package/dist/cdn/holosphere.min.js +0 -55
- package/dist/cdn/holosphere.min.js.map +0 -1
- package/dist/index-DJXftyvB.js.map +0 -1
- package/dist/index-DMbdcMtK.cjs +0 -18
- package/dist/index-DMbdcMtK.cjs.map +0 -1
|
@@ -421,12 +421,12 @@ async function createPersistentStorage(namespace, options = {}) {
|
|
|
421
421
|
const isBrowser = typeof window !== "undefined" && typeof window.indexedDB !== "undefined";
|
|
422
422
|
typeof process !== "undefined" && process.versions && void 0;
|
|
423
423
|
if (isBrowser) {
|
|
424
|
-
const { IndexedDBStorage } = await import("./indexeddb-storage-
|
|
424
|
+
const { IndexedDBStorage } = await import("./indexeddb-storage-5eiUNsHC.js");
|
|
425
425
|
const storage = new IndexedDBStorage();
|
|
426
426
|
await storage.init(namespace);
|
|
427
427
|
return storage;
|
|
428
428
|
} else {
|
|
429
|
-
const { MemoryStorage } = await import("./memory-storage-
|
|
429
|
+
const { MemoryStorage } = await import("./memory-storage-CI-gfmuG.js");
|
|
430
430
|
const storage = new MemoryStorage();
|
|
431
431
|
await storage.init(namespace);
|
|
432
432
|
return storage;
|
|
@@ -710,6 +710,22 @@ class NostrClient {
|
|
|
710
710
|
return null;
|
|
711
711
|
}
|
|
712
712
|
}
|
|
713
|
+
/**
|
|
714
|
+
* Get event from LRU cache by d-tag path (for local-first reads).
|
|
715
|
+
* This is always up-to-date because writes update the LRU cache synchronously,
|
|
716
|
+
* unlike persistent storage which uses batched writes.
|
|
717
|
+
* @param {string} path - The d-tag path
|
|
718
|
+
* @param {number} [kind=30000] - Event kind
|
|
719
|
+
* @returns {Object|null} The cached event or null
|
|
720
|
+
*/
|
|
721
|
+
getCachedByPath(path, kind2 = 3e4) {
|
|
722
|
+
const dTagKey = `d:${kind2}:${path}`;
|
|
723
|
+
const cached = this._eventCache.get(dTagKey);
|
|
724
|
+
if (cached && cached.events && cached.events[0]) {
|
|
725
|
+
return cached.events[0];
|
|
726
|
+
}
|
|
727
|
+
return null;
|
|
728
|
+
}
|
|
713
729
|
/**
|
|
714
730
|
* Get all events from persistent storage matching a prefix
|
|
715
731
|
* @param {string} prefix - The path prefix to match
|
|
@@ -3996,7 +4012,7 @@ class GunDBBackend extends StorageBackend {
|
|
|
3996
4012
|
}
|
|
3997
4013
|
}
|
|
3998
4014
|
const name = "holosphere";
|
|
3999
|
-
const version$1 = "2.0.0-
|
|
4015
|
+
const version$1 = "2.0.0-alpha11";
|
|
4000
4016
|
const description = "Holonic geospatial communication infrastructure combining H3 hexagonal indexing with distributed P2P storage";
|
|
4001
4017
|
const type = "module";
|
|
4002
4018
|
const bin = {
|
|
@@ -4779,6 +4795,24 @@ async function nostrPut(client, path, data, kind2 = 3e4) {
|
|
|
4779
4795
|
async function nostrGet(client, path, kind2 = 3e4, options = {}) {
|
|
4780
4796
|
const timeout = options.timeout !== void 0 ? options.timeout : 3e4;
|
|
4781
4797
|
const authors = options.authors || [client.publicKey];
|
|
4798
|
+
if (!options.skipCache && client.getCachedByPath) {
|
|
4799
|
+
const cachedEvent = client.getCachedByPath(path, kind2);
|
|
4800
|
+
if (cachedEvent && cachedEvent.content) {
|
|
4801
|
+
if (authors.includes(cachedEvent.pubkey)) {
|
|
4802
|
+
try {
|
|
4803
|
+
const data = JSON.parse(cachedEvent.content);
|
|
4804
|
+
if (!data || data._deleted) {
|
|
4805
|
+
return null;
|
|
4806
|
+
}
|
|
4807
|
+
if (options.includeAuthor) {
|
|
4808
|
+
data._author = cachedEvent.pubkey;
|
|
4809
|
+
}
|
|
4810
|
+
return data;
|
|
4811
|
+
} catch (error) {
|
|
4812
|
+
}
|
|
4813
|
+
}
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4782
4816
|
if (!options.skipPersistent && client.persistentGet) {
|
|
4783
4817
|
const persistedEvent = await client.persistentGet(path);
|
|
4784
4818
|
if (persistedEvent && persistedEvent.content) {
|
|
@@ -4997,9 +5031,6 @@ async function nostrDelete(client, path, kind2 = 3e4) {
|
|
|
4997
5031
|
} catch (e) {
|
|
4998
5032
|
}
|
|
4999
5033
|
}
|
|
5000
|
-
if (client.clearCache) {
|
|
5001
|
-
client.clearCache(path);
|
|
5002
|
-
}
|
|
5003
5034
|
const coordinate = `${kind2}:${client.publicKey}:${path}`;
|
|
5004
5035
|
const deletionEvent = {
|
|
5005
5036
|
kind: 5,
|
|
@@ -5018,6 +5049,7 @@ async function nostrDelete(client, path, kind2 = 3e4) {
|
|
|
5018
5049
|
}
|
|
5019
5050
|
return result;
|
|
5020
5051
|
}
|
|
5052
|
+
const DELETE_RATE_LIMIT_MS = 100;
|
|
5021
5053
|
async function nostrDeleteAll(client, pathPrefix, kind2 = 3e4) {
|
|
5022
5054
|
const filter = {
|
|
5023
5055
|
kinds: [kind2],
|
|
@@ -5053,7 +5085,8 @@ async function nostrDeleteAll(client, pathPrefix, kind2 = 3e4) {
|
|
|
5053
5085
|
return { success: true, count: 0, results: [] };
|
|
5054
5086
|
}
|
|
5055
5087
|
const tombstoneResults = [];
|
|
5056
|
-
for (
|
|
5088
|
+
for (let i = 0; i < matching.length; i++) {
|
|
5089
|
+
const event = matching[i];
|
|
5057
5090
|
const dTag = event.tags.find((t) => t[0] === "d")[1];
|
|
5058
5091
|
try {
|
|
5059
5092
|
const tombstone = {
|
|
@@ -5068,8 +5101,8 @@ async function nostrDeleteAll(client, pathPrefix, kind2 = 3e4) {
|
|
|
5068
5101
|
} catch (e) {
|
|
5069
5102
|
}
|
|
5070
5103
|
}
|
|
5071
|
-
if (
|
|
5072
|
-
|
|
5104
|
+
if (i < matching.length - 1 && DELETE_RATE_LIMIT_MS > 0) {
|
|
5105
|
+
await new Promise((resolve) => setTimeout(resolve, DELETE_RATE_LIMIT_MS));
|
|
5073
5106
|
}
|
|
5074
5107
|
} catch (error) {
|
|
5075
5108
|
console.error(`Failed to delete item ${dTag}:`, error);
|
|
@@ -5945,7 +5978,7 @@ const sha256 = sha256$1;
|
|
|
5945
5978
|
let secp256k1 = null;
|
|
5946
5979
|
async function loadCrypto() {
|
|
5947
5980
|
if (!secp256k1) {
|
|
5948
|
-
const module2 = await import("./secp256k1-
|
|
5981
|
+
const module2 = await import("./secp256k1-CEwJNcfV.js");
|
|
5949
5982
|
secp256k1 = module2.secp256k1;
|
|
5950
5983
|
}
|
|
5951
5984
|
return secp256k1;
|
|
@@ -6610,7 +6643,9 @@ async function addActiveHologram(client, appname, sourceHolon, lensName, dataId,
|
|
|
6610
6643
|
sourceData._meta.activeHolograms.push({
|
|
6611
6644
|
targetHolon,
|
|
6612
6645
|
created: now,
|
|
6613
|
-
lastUpdated: now
|
|
6646
|
+
lastUpdated: now,
|
|
6647
|
+
platforms: {}
|
|
6648
|
+
// Platform-specific data (e.g., telegram: { messageId: 123 })
|
|
6614
6649
|
});
|
|
6615
6650
|
}
|
|
6616
6651
|
return write(client, sourcePath, sourceData);
|
|
@@ -6651,6 +6686,66 @@ async function removeActiveHologram(client, appname, sourceHolon, lensName, data
|
|
|
6651
6686
|
}
|
|
6652
6687
|
return false;
|
|
6653
6688
|
}
|
|
6689
|
+
async function updateActiveHologramPlatform(client, appname, sourceHolon, lensName, dataId, targetHolon, platform, platformData) {
|
|
6690
|
+
let currentAppname = appname;
|
|
6691
|
+
let currentHolon = sourceHolon;
|
|
6692
|
+
let currentLens = lensName;
|
|
6693
|
+
let currentDataId = dataId;
|
|
6694
|
+
let sourcePath = buildPath(currentAppname, currentHolon, currentLens, currentDataId);
|
|
6695
|
+
let sourceData = await read(client, sourcePath);
|
|
6696
|
+
if (!sourceData) {
|
|
6697
|
+
console.warn(`Source data not found at ${sourcePath}`);
|
|
6698
|
+
return false;
|
|
6699
|
+
}
|
|
6700
|
+
let depth = 0;
|
|
6701
|
+
const maxDepth = 10;
|
|
6702
|
+
while (sourceData.hologram === true && sourceData.target && depth < maxDepth) {
|
|
6703
|
+
depth++;
|
|
6704
|
+
currentAppname = sourceData.target.appname || currentAppname;
|
|
6705
|
+
currentHolon = sourceData.target.holonId;
|
|
6706
|
+
currentLens = sourceData.target.lensName || currentLens;
|
|
6707
|
+
currentDataId = sourceData.target.dataId || currentDataId;
|
|
6708
|
+
sourcePath = buildPath(currentAppname, currentHolon, currentLens, currentDataId);
|
|
6709
|
+
sourceData = await read(client, sourcePath);
|
|
6710
|
+
if (!sourceData) {
|
|
6711
|
+
console.warn(`Real source data not found at ${sourcePath}`);
|
|
6712
|
+
return false;
|
|
6713
|
+
}
|
|
6714
|
+
}
|
|
6715
|
+
if (!sourceData._meta || !Array.isArray(sourceData._meta.activeHolograms)) {
|
|
6716
|
+
console.warn(`No activeHolograms found for ${sourcePath}`);
|
|
6717
|
+
return false;
|
|
6718
|
+
}
|
|
6719
|
+
const hologramEntry = sourceData._meta.activeHolograms.find(
|
|
6720
|
+
(h) => h.targetHolon === targetHolon
|
|
6721
|
+
);
|
|
6722
|
+
if (!hologramEntry) {
|
|
6723
|
+
console.warn(`No hologram entry found for target ${targetHolon}`);
|
|
6724
|
+
return false;
|
|
6725
|
+
}
|
|
6726
|
+
if (!hologramEntry.platforms) {
|
|
6727
|
+
hologramEntry.platforms = {};
|
|
6728
|
+
}
|
|
6729
|
+
hologramEntry.platforms[platform] = {
|
|
6730
|
+
...hologramEntry.platforms[platform],
|
|
6731
|
+
...platformData,
|
|
6732
|
+
lastUpdated: Date.now()
|
|
6733
|
+
};
|
|
6734
|
+
hologramEntry.lastUpdated = Date.now();
|
|
6735
|
+
return write(client, sourcePath, sourceData);
|
|
6736
|
+
}
|
|
6737
|
+
async function getActiveHolograms(client, appname, sourceHolon, lensName, dataId, platform = null) {
|
|
6738
|
+
const sourcePath = buildPath(appname, sourceHolon, lensName, dataId);
|
|
6739
|
+
const sourceData = await read(client, sourcePath);
|
|
6740
|
+
if (!sourceData?._meta?.activeHolograms) {
|
|
6741
|
+
return [];
|
|
6742
|
+
}
|
|
6743
|
+
let holograms = sourceData._meta.activeHolograms;
|
|
6744
|
+
if (platform) {
|
|
6745
|
+
holograms = holograms.filter((h) => h.platforms?.[platform]);
|
|
6746
|
+
}
|
|
6747
|
+
return holograms;
|
|
6748
|
+
}
|
|
6654
6749
|
async function refreshActiveHolograms(client, appname, sourceHolon, lensName, dataId, sourceData = null) {
|
|
6655
6750
|
if (!sourceData) {
|
|
6656
6751
|
const sourcePath = buildPath(appname, sourceHolon, lensName, dataId);
|
|
@@ -6867,6 +6962,7 @@ const hologram = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
6867
6962
|
cleanupCircularHologramsByIds,
|
|
6868
6963
|
createHologram,
|
|
6869
6964
|
deleteHologram,
|
|
6965
|
+
getActiveHolograms,
|
|
6870
6966
|
getHologramSource,
|
|
6871
6967
|
isHologram,
|
|
6872
6968
|
isResolvedHologram,
|
|
@@ -6875,6 +6971,7 @@ const hologram = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
6875
6971
|
removeActiveHologram,
|
|
6876
6972
|
resolveHologram,
|
|
6877
6973
|
setupFederation,
|
|
6974
|
+
updateActiveHologramPlatform,
|
|
6878
6975
|
updateHologramOverrides,
|
|
6879
6976
|
wouldCreateCircularHologram
|
|
6880
6977
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
@@ -17661,7 +17758,7 @@ class ChainManager {
|
|
|
17661
17758
|
*/
|
|
17662
17759
|
async _loadEthers() {
|
|
17663
17760
|
if (!this.ethers) {
|
|
17664
|
-
const ethersModule = await import("./index-
|
|
17761
|
+
const ethersModule = await import("./index-Bl6rM1NW.js");
|
|
17665
17762
|
this.ethers = ethersModule;
|
|
17666
17763
|
}
|
|
17667
17764
|
return this.ethers;
|
|
@@ -26452,7 +26549,7 @@ class ContractDeployer {
|
|
|
26452
26549
|
*/
|
|
26453
26550
|
async _loadEthers() {
|
|
26454
26551
|
if (!this.ethers) {
|
|
26455
|
-
this.ethers = await import("./index-
|
|
26552
|
+
this.ethers = await import("./index-Bl6rM1NW.js");
|
|
26456
26553
|
}
|
|
26457
26554
|
return this.ethers;
|
|
26458
26555
|
}
|
|
@@ -26838,7 +26935,7 @@ class ContractOperations {
|
|
|
26838
26935
|
*/
|
|
26839
26936
|
async _loadEthers() {
|
|
26840
26937
|
if (!this.ethers) {
|
|
26841
|
-
this.ethers = await import("./index-
|
|
26938
|
+
this.ethers = await import("./index-Bl6rM1NW.js");
|
|
26842
26939
|
}
|
|
26843
26940
|
return this.ethers;
|
|
26844
26941
|
}
|
|
@@ -38194,6 +38291,9 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38194
38291
|
this.schemas = /* @__PURE__ */ new Map();
|
|
38195
38292
|
this._cache = /* @__PURE__ */ new Map();
|
|
38196
38293
|
this.subscriptionRegistry = new SubscriptionRegistry();
|
|
38294
|
+
this._writeCache = /* @__PURE__ */ new Map();
|
|
38295
|
+
this._pendingWrites = /* @__PURE__ */ new Map();
|
|
38296
|
+
this._deleteCache = /* @__PURE__ */ new Set();
|
|
38197
38297
|
this._ai = null;
|
|
38198
38298
|
const openaiKey = config.openaiKey || this._getEnv("OPENAI_API_KEY");
|
|
38199
38299
|
if (openaiKey) {
|
|
@@ -38325,6 +38425,8 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38325
38425
|
// === Data Operations ===
|
|
38326
38426
|
/**
|
|
38327
38427
|
* Writes data to a specific holon and lens.
|
|
38428
|
+
* By default, write is optimistic (non-blocking) - returns immediately after caching locally.
|
|
38429
|
+
* Data is synced to relays in the background.
|
|
38328
38430
|
*
|
|
38329
38431
|
* @param {string} holonId - H3 cell ID for the holon
|
|
38330
38432
|
* @param {string} lensName - Name of the lens (data category)
|
|
@@ -38335,15 +38437,18 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38335
38437
|
* @param {boolean} [options.strict] - Override schema strict mode
|
|
38336
38438
|
* @param {boolean} [options.autoPropagate=true] - Whether to propagate to federated holons
|
|
38337
38439
|
* @param {Object} [options.propagationOptions] - Options for propagation
|
|
38338
|
-
* @
|
|
38440
|
+
* @param {boolean} [options.blocking=false] - If true, wait for relay confirmation before returning
|
|
38441
|
+
* @returns {Promise<boolean>} True if write succeeded (or queued for optimistic writes)
|
|
38339
38442
|
* @throws {ValidationError} If holonId, lensName, or data is invalid
|
|
38340
38443
|
* @throws {AuthorizationError} If capability token is invalid
|
|
38341
38444
|
*/
|
|
38342
38445
|
async write(holonId, lensName, data, options = {}) {
|
|
38343
|
-
if (
|
|
38446
|
+
if (holonId != null) holonId = String(holonId);
|
|
38447
|
+
if (lensName != null) lensName = String(lensName);
|
|
38448
|
+
if (!holonId) {
|
|
38344
38449
|
throw new ValidationError$1("ValidationError: holonId must be a non-empty string");
|
|
38345
38450
|
}
|
|
38346
|
-
if (!lensName
|
|
38451
|
+
if (!lensName) {
|
|
38347
38452
|
throw new ValidationError$1("ValidationError: lensName must be a non-empty string");
|
|
38348
38453
|
}
|
|
38349
38454
|
if (!data || typeof data !== "object") {
|
|
@@ -38358,82 +38463,148 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38358
38463
|
}
|
|
38359
38464
|
if (!data.id) {
|
|
38360
38465
|
data.id = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
38466
|
+
} else {
|
|
38467
|
+
data.id = String(data.id);
|
|
38361
38468
|
}
|
|
38469
|
+
if (!data._meta) data._meta = {};
|
|
38470
|
+
data._meta.createdAt = data._meta.createdAt || Date.now();
|
|
38471
|
+
data._meta.updatedAt = Date.now();
|
|
38362
38472
|
const path = buildPath(this.config.appName, holonId, lensName, data.id);
|
|
38363
|
-
this._log("DEBUG", "write", { holonId, lensName, dataId: data.id, path });
|
|
38364
|
-
|
|
38365
|
-
|
|
38366
|
-
const
|
|
38367
|
-
|
|
38368
|
-
|
|
38369
|
-
const sourceUpdates = {};
|
|
38370
|
-
for (const [key, value] of Object.entries(data)) {
|
|
38371
|
-
if (hologramStructureFields.includes(key) || key === "_hologram") continue;
|
|
38372
|
-
if (localOverrideFields.includes(key)) {
|
|
38373
|
-
localData[key] = value;
|
|
38374
|
-
} else {
|
|
38375
|
-
sourceUpdates[key] = value;
|
|
38376
|
-
}
|
|
38377
|
-
}
|
|
38378
|
-
if (Object.keys(sourceUpdates).length > 0 && options.validate !== false && this.schemas.has(lensName)) {
|
|
38379
|
-
const sourcePath = buildPath(
|
|
38380
|
-
existingData.target.appname || this.config.appName,
|
|
38381
|
-
existingData.target.holonId,
|
|
38382
|
-
existingData.target.lensName,
|
|
38383
|
-
existingData.target.dataId
|
|
38384
|
-
);
|
|
38385
|
-
const currentSourceData = await read(this.client, sourcePath);
|
|
38386
|
-
const mergedSourceData = { ...currentSourceData, ...sourceUpdates };
|
|
38387
|
-
const schemaObj = this.schemas.get(lensName);
|
|
38388
|
-
const strict = options.strict !== void 0 ? options.strict : schemaObj.strict;
|
|
38389
|
-
if (schemaObj.schema && typeof schemaObj.schema === "object" && !schemaObj.schema.$ref) {
|
|
38390
|
-
validate(mergedSourceData, schemaObj.schema, lensName, strict);
|
|
38391
|
-
}
|
|
38392
|
-
}
|
|
38393
|
-
const startTime2 = Date.now();
|
|
38394
|
-
await write(this.client, path, localData);
|
|
38395
|
-
if (Object.keys(sourceUpdates).length > 0) {
|
|
38396
|
-
const sourcePath = buildPath(
|
|
38397
|
-
existingData.target.appname || this.config.appName,
|
|
38398
|
-
existingData.target.holonId,
|
|
38399
|
-
existingData.target.lensName,
|
|
38400
|
-
existingData.target.dataId
|
|
38401
|
-
);
|
|
38402
|
-
await update(this.client, sourcePath, sourceUpdates);
|
|
38403
|
-
await refreshActiveHolograms(
|
|
38404
|
-
this.client,
|
|
38405
|
-
existingData.target.appname || this.config.appName,
|
|
38406
|
-
existingData.target.holonId,
|
|
38407
|
-
existingData.target.lensName,
|
|
38408
|
-
existingData.target.dataId
|
|
38409
|
-
);
|
|
38473
|
+
this._log("DEBUG", "write", { holonId, lensName, dataId: data.id, path, blocking: !!options.blocking });
|
|
38474
|
+
if (options.validate !== false && this.schemas.has(lensName)) {
|
|
38475
|
+
const schemaObj = this.schemas.get(lensName);
|
|
38476
|
+
const strict = options.strict !== void 0 ? options.strict : schemaObj.strict;
|
|
38477
|
+
if (schemaObj.schema && typeof schemaObj.schema === "object" && !schemaObj.schema.$ref) {
|
|
38478
|
+
validate(data, schemaObj.schema, lensName, strict);
|
|
38410
38479
|
}
|
|
38411
|
-
|
|
38480
|
+
}
|
|
38481
|
+
this._writeCache.set(path, { data: { ...data }, timestamp: Date.now() });
|
|
38482
|
+
this._deleteCache.delete(path);
|
|
38483
|
+
this._log("DEBUG", "📝 CACHE WRITE", {
|
|
38484
|
+
path,
|
|
38485
|
+
dataId: data.id,
|
|
38486
|
+
cacheSize: this._writeCache.size,
|
|
38487
|
+
mode: options.blocking ? "blocking" : "optimistic"
|
|
38488
|
+
});
|
|
38489
|
+
const syncPromise = this._syncWriteToRelay(holonId, lensName, data, path, options);
|
|
38490
|
+
if (options.blocking) {
|
|
38491
|
+
this._log("DEBUG", "⏳ BLOCKING: Waiting for relay confirmation", { path });
|
|
38492
|
+
return syncPromise;
|
|
38493
|
+
}
|
|
38494
|
+
this._log("DEBUG", "⚡ OPTIMISTIC: Returning immediately, sync in background", { path });
|
|
38495
|
+
return true;
|
|
38496
|
+
}
|
|
38497
|
+
/**
|
|
38498
|
+
* Syncs a write operation to the relay in the background.
|
|
38499
|
+
* Handles hologram writes, propagation, and error logging.
|
|
38500
|
+
*
|
|
38501
|
+
* @private
|
|
38502
|
+
* @param {string} holonId - Holon ID
|
|
38503
|
+
* @param {string} lensName - Lens name
|
|
38504
|
+
* @param {Object} data - Data to write
|
|
38505
|
+
* @param {string} path - Storage path
|
|
38506
|
+
* @param {Object} options - Write options
|
|
38507
|
+
* @returns {Promise<boolean>} True if write succeeded
|
|
38508
|
+
*/
|
|
38509
|
+
async _syncWriteToRelay(holonId, lensName, data, path, options) {
|
|
38510
|
+
const startTime = Date.now();
|
|
38511
|
+
this._log("DEBUG", "🔄 RELAY SYNC START", {
|
|
38512
|
+
path,
|
|
38513
|
+
dataId: data.id,
|
|
38514
|
+
relays: this.client?.relays?.map((r) => r.url) || ["local-only"]
|
|
38515
|
+
});
|
|
38516
|
+
try {
|
|
38517
|
+
const existingData = await read(this.client, path);
|
|
38518
|
+
if (existingData && existingData.hologram === true && existingData.target) {
|
|
38519
|
+
this._log("DEBUG", "🔗 Syncing hologram write", { path, target: existingData.target });
|
|
38520
|
+
return this._syncHologramWrite(existingData, data, path, lensName, options);
|
|
38521
|
+
}
|
|
38522
|
+
await write(this.client, path, data);
|
|
38523
|
+
const endTime = Date.now();
|
|
38524
|
+
const duration = endTime - startTime;
|
|
38412
38525
|
this._metrics.writes++;
|
|
38413
38526
|
if (!this._metrics.totalWriteTime) this._metrics.totalWriteTime = 0;
|
|
38414
|
-
this._metrics.totalWriteTime +=
|
|
38527
|
+
this._metrics.totalWriteTime += duration;
|
|
38528
|
+
this._log("DEBUG", "✅ RELAY SYNC SUCCESS", {
|
|
38529
|
+
path,
|
|
38530
|
+
dataId: data.id,
|
|
38531
|
+
duration: `${duration}ms`,
|
|
38532
|
+
totalWrites: this._metrics.writes
|
|
38533
|
+
});
|
|
38534
|
+
const { autoPropagate = true } = options;
|
|
38535
|
+
if (autoPropagate && !data.hologram) {
|
|
38536
|
+
this._log("DEBUG", "📤 Starting propagation", { path });
|
|
38537
|
+
this.propagate(holonId, lensName, data, options.propagationOptions || {}).catch((err) => this._log("WARN", "⚠️ Propagation failed", { path, error: err.message }));
|
|
38538
|
+
}
|
|
38415
38539
|
return true;
|
|
38540
|
+
} catch (error) {
|
|
38541
|
+
const duration = Date.now() - startTime;
|
|
38542
|
+
this._log("ERROR", "❌ RELAY SYNC FAILED", {
|
|
38543
|
+
path,
|
|
38544
|
+
dataId: data.id,
|
|
38545
|
+
duration: `${duration}ms`,
|
|
38546
|
+
error: error.message,
|
|
38547
|
+
stack: error.stack?.split("\n")[1]?.trim()
|
|
38548
|
+
});
|
|
38549
|
+
this._log("WARN", "💾 Data retained in write cache for retry", { path, cacheSize: this._writeCache.size });
|
|
38550
|
+
return false;
|
|
38416
38551
|
}
|
|
38417
|
-
|
|
38418
|
-
|
|
38419
|
-
|
|
38420
|
-
|
|
38552
|
+
}
|
|
38553
|
+
/**
|
|
38554
|
+
* Handles hologram write synchronization.
|
|
38555
|
+
*
|
|
38556
|
+
* @private
|
|
38557
|
+
*/
|
|
38558
|
+
async _syncHologramWrite(existingData, data, path, lensName, options) {
|
|
38559
|
+
const hologramStructureFields = ["hologram", "soul", "target", "id", "_meta"];
|
|
38560
|
+
const localOverrideFields = Object.keys(existingData).filter((k) => !hologramStructureFields.includes(k));
|
|
38561
|
+
const localData = { ...existingData };
|
|
38562
|
+
const sourceUpdates = {};
|
|
38563
|
+
for (const [key, value] of Object.entries(data)) {
|
|
38564
|
+
if (hologramStructureFields.includes(key) || key === "_hologram") continue;
|
|
38565
|
+
if (localOverrideFields.includes(key)) {
|
|
38566
|
+
localData[key] = value;
|
|
38567
|
+
} else {
|
|
38568
|
+
sourceUpdates[key] = value;
|
|
38569
|
+
}
|
|
38570
|
+
}
|
|
38571
|
+
if (Object.keys(sourceUpdates).length > 0 && options.validate !== false && this.schemas.has(lensName)) {
|
|
38572
|
+
const sourcePath = buildPath(
|
|
38573
|
+
existingData.target.appname || this.config.appName,
|
|
38574
|
+
existingData.target.holonId,
|
|
38575
|
+
existingData.target.lensName,
|
|
38576
|
+
existingData.target.dataId
|
|
38577
|
+
);
|
|
38578
|
+
const currentSourceData = await read(this.client, sourcePath);
|
|
38579
|
+
const mergedSourceData = { ...currentSourceData, ...sourceUpdates };
|
|
38421
38580
|
const schemaObj = this.schemas.get(lensName);
|
|
38422
38581
|
const strict = options.strict !== void 0 ? options.strict : schemaObj.strict;
|
|
38423
38582
|
if (schemaObj.schema && typeof schemaObj.schema === "object" && !schemaObj.schema.$ref) {
|
|
38424
|
-
validate(
|
|
38583
|
+
validate(mergedSourceData, schemaObj.schema, lensName, strict);
|
|
38425
38584
|
}
|
|
38426
38585
|
}
|
|
38427
38586
|
const startTime = Date.now();
|
|
38428
|
-
await write(this.client, path,
|
|
38587
|
+
await write(this.client, path, localData);
|
|
38588
|
+
if (Object.keys(sourceUpdates).length > 0) {
|
|
38589
|
+
const sourcePath = buildPath(
|
|
38590
|
+
existingData.target.appname || this.config.appName,
|
|
38591
|
+
existingData.target.holonId,
|
|
38592
|
+
existingData.target.lensName,
|
|
38593
|
+
existingData.target.dataId
|
|
38594
|
+
);
|
|
38595
|
+
await update(this.client, sourcePath, sourceUpdates);
|
|
38596
|
+
await refreshActiveHolograms(
|
|
38597
|
+
this.client,
|
|
38598
|
+
existingData.target.appname || this.config.appName,
|
|
38599
|
+
existingData.target.holonId,
|
|
38600
|
+
existingData.target.lensName,
|
|
38601
|
+
existingData.target.dataId
|
|
38602
|
+
);
|
|
38603
|
+
}
|
|
38429
38604
|
const endTime = Date.now();
|
|
38430
38605
|
this._metrics.writes++;
|
|
38431
38606
|
if (!this._metrics.totalWriteTime) this._metrics.totalWriteTime = 0;
|
|
38432
38607
|
this._metrics.totalWriteTime += endTime - startTime;
|
|
38433
|
-
const { autoPropagate = true } = options;
|
|
38434
|
-
if (autoPropagate && !data.hologram) {
|
|
38435
|
-
await this.propagate(holonId, lensName, data, options.propagationOptions || {});
|
|
38436
|
-
}
|
|
38437
38608
|
return true;
|
|
38438
38609
|
}
|
|
38439
38610
|
/**
|
|
@@ -38537,10 +38708,13 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38537
38708
|
* @throws {AuthorizationError} If capability token is invalid
|
|
38538
38709
|
*/
|
|
38539
38710
|
async read(holonId, lensName, dataId = null, options = {}) {
|
|
38540
|
-
if (
|
|
38711
|
+
if (holonId != null) holonId = String(holonId);
|
|
38712
|
+
if (lensName != null) lensName = String(lensName);
|
|
38713
|
+
if (dataId != null) dataId = String(dataId);
|
|
38714
|
+
if (!holonId) {
|
|
38541
38715
|
throw new ValidationError$1("ValidationError: holonId must be a non-empty string");
|
|
38542
38716
|
}
|
|
38543
|
-
if (!lensName
|
|
38717
|
+
if (!lensName) {
|
|
38544
38718
|
throw new ValidationError$1("ValidationError: lensName must be a non-empty string");
|
|
38545
38719
|
}
|
|
38546
38720
|
const capToken = options.capabilityToken || options.capability;
|
|
@@ -38554,13 +38728,40 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38554
38728
|
let result;
|
|
38555
38729
|
if (dataId) {
|
|
38556
38730
|
const path = buildPath(this.config.appName, holonId, lensName, dataId);
|
|
38557
|
-
this.
|
|
38558
|
-
|
|
38559
|
-
|
|
38731
|
+
if (this._deleteCache.has(path)) {
|
|
38732
|
+
this._log("DEBUG", "🗑️ CACHE READ: Deleted item", {
|
|
38733
|
+
path,
|
|
38734
|
+
source: "delete-cache",
|
|
38735
|
+
deleteCacheSize: this._deleteCache.size
|
|
38736
|
+
});
|
|
38737
|
+
result = null;
|
|
38738
|
+
} else {
|
|
38739
|
+
const cached = this._writeCache.get(path);
|
|
38740
|
+
if (cached) {
|
|
38741
|
+
const cacheAge = Date.now() - cached.timestamp;
|
|
38742
|
+
this._log("DEBUG", "⚡ CACHE HIT: Write cache", {
|
|
38743
|
+
path,
|
|
38744
|
+
source: "write-cache",
|
|
38745
|
+
cacheAge: `${cacheAge}ms`,
|
|
38746
|
+
writeCacheSize: this._writeCache.size
|
|
38747
|
+
});
|
|
38748
|
+
result = cached.data;
|
|
38749
|
+
} else {
|
|
38750
|
+
this._log("DEBUG", "📖 CACHE MISS: Reading from storage", { path });
|
|
38751
|
+
result = await read(this.client, path);
|
|
38752
|
+
this._log("DEBUG", "💾 STORAGE READ", {
|
|
38753
|
+
path,
|
|
38754
|
+
source: "storage",
|
|
38755
|
+
found: !!result,
|
|
38756
|
+
isHologram: result?.hologram === true
|
|
38757
|
+
});
|
|
38758
|
+
}
|
|
38759
|
+
}
|
|
38560
38760
|
} else {
|
|
38561
38761
|
const path = buildPath(this.config.appName, holonId, lensName);
|
|
38562
38762
|
this._log("DEBUG", "readAll", { holonId, lensName, path });
|
|
38563
|
-
|
|
38763
|
+
const storageResult = await readAll(this.client, path);
|
|
38764
|
+
result = this._mergeWithWriteCache(storageResult, path);
|
|
38564
38765
|
this._log("DEBUG", "readAll result", { count: Array.isArray(result) ? result.length : 0 });
|
|
38565
38766
|
}
|
|
38566
38767
|
const { resolveHolograms = true } = options;
|
|
@@ -38574,8 +38775,39 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38574
38775
|
this._metrics.totalReadTime += endTime - startTime;
|
|
38575
38776
|
return result;
|
|
38576
38777
|
}
|
|
38778
|
+
/**
|
|
38779
|
+
* Merges storage results with write cache for readAll operations.
|
|
38780
|
+
* Cached writes take precedence over storage data.
|
|
38781
|
+
* Deleted items are filtered out.
|
|
38782
|
+
*
|
|
38783
|
+
* @private
|
|
38784
|
+
* @param {Array} storageResult - Results from storage
|
|
38785
|
+
* @param {string} pathPrefix - Path prefix for filtering cache
|
|
38786
|
+
* @returns {Array} Merged results
|
|
38787
|
+
*/
|
|
38788
|
+
_mergeWithWriteCache(storageResult, pathPrefix) {
|
|
38789
|
+
const results = Array.isArray(storageResult) ? [...storageResult] : [];
|
|
38790
|
+
const resultMap = new Map(results.map((item) => [item.id, item]));
|
|
38791
|
+
for (const [cachedPath, cached] of this._writeCache.entries()) {
|
|
38792
|
+
if (cachedPath.startsWith(pathPrefix + "/")) {
|
|
38793
|
+
const cachedData = cached.data;
|
|
38794
|
+
if (cachedData.id) {
|
|
38795
|
+
resultMap.set(cachedData.id, cachedData);
|
|
38796
|
+
}
|
|
38797
|
+
}
|
|
38798
|
+
}
|
|
38799
|
+
for (const deletedPath of this._deleteCache) {
|
|
38800
|
+
if (deletedPath.startsWith(pathPrefix + "/")) {
|
|
38801
|
+
const dataId = deletedPath.split("/").pop();
|
|
38802
|
+
resultMap.delete(dataId);
|
|
38803
|
+
}
|
|
38804
|
+
}
|
|
38805
|
+
return Array.from(resultMap.values());
|
|
38806
|
+
}
|
|
38577
38807
|
/**
|
|
38578
38808
|
* Updates existing data in a specific holon and lens.
|
|
38809
|
+
* By default, update is optimistic (non-blocking) - returns immediately after caching merged data.
|
|
38810
|
+
* Data is synced to relays in the background.
|
|
38579
38811
|
*
|
|
38580
38812
|
* @param {string} holonId - H3 cell ID for the holon
|
|
38581
38813
|
* @param {string} lensName - Name of the lens (data category)
|
|
@@ -38585,6 +38817,7 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38585
38817
|
* @param {string} [options.capabilityToken] - Capability token for authorization
|
|
38586
38818
|
* @param {boolean} [options.validate=true] - Whether to validate against schema
|
|
38587
38819
|
* @param {boolean} [options.strict] - Override schema strict mode
|
|
38820
|
+
* @param {boolean} [options.blocking=false] - If true, wait for relay confirmation before returning
|
|
38588
38821
|
* @returns {Promise<boolean>} True if update succeeded, false if data not found
|
|
38589
38822
|
* @throws {ValidationError} If holonId, lensName, dataId, or updates is invalid
|
|
38590
38823
|
* @throws {AuthorizationError} If capability token is invalid
|
|
@@ -38610,11 +38843,16 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38610
38843
|
}
|
|
38611
38844
|
}
|
|
38612
38845
|
const path = buildPath(this.config.appName, holonId, lensName, dataId);
|
|
38613
|
-
const
|
|
38846
|
+
const cached = this._writeCache.get(path);
|
|
38847
|
+
const existingData = cached ? cached.data : await read(this.client, path);
|
|
38848
|
+
const dataSource = cached ? "write-cache" : "storage";
|
|
38614
38849
|
if (!existingData) {
|
|
38850
|
+
this._log("DEBUG", "✏️ UPDATE: Data not found", { path, dataId });
|
|
38615
38851
|
return false;
|
|
38616
38852
|
}
|
|
38853
|
+
this._log("DEBUG", "✏️ UPDATE: Found data", { path, dataId, source: dataSource });
|
|
38617
38854
|
if (existingData.hologram === true && existingData.target) {
|
|
38855
|
+
this._log("DEBUG", "🔗 Updating hologram", { path });
|
|
38618
38856
|
return this.write(holonId, lensName, { ...existingData, ...updates, id: dataId }, options);
|
|
38619
38857
|
}
|
|
38620
38858
|
const mergedData = { ...existingData, ...updates };
|
|
@@ -38627,44 +38865,104 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38627
38865
|
validate(mergedData, schemaObj.schema, lensName, strict);
|
|
38628
38866
|
}
|
|
38629
38867
|
}
|
|
38630
|
-
|
|
38631
|
-
|
|
38632
|
-
|
|
38633
|
-
|
|
38634
|
-
|
|
38635
|
-
|
|
38636
|
-
|
|
38637
|
-
|
|
38868
|
+
this._writeCache.set(path, { data: { ...mergedData }, timestamp: Date.now() });
|
|
38869
|
+
this._deleteCache.delete(path);
|
|
38870
|
+
this._log("DEBUG", "✏️ CACHE UPDATE", {
|
|
38871
|
+
path,
|
|
38872
|
+
dataId,
|
|
38873
|
+
updatedFields: Object.keys(updates),
|
|
38874
|
+
cacheSize: this._writeCache.size,
|
|
38875
|
+
mode: options.blocking ? "blocking" : "optimistic"
|
|
38876
|
+
});
|
|
38877
|
+
const syncPromise = this._syncUpdateToRelay(holonId, lensName, dataId, mergedData, existingData, path);
|
|
38878
|
+
if (options.blocking) {
|
|
38879
|
+
this._log("DEBUG", "⏳ BLOCKING: Waiting for update confirmation", { path });
|
|
38880
|
+
return syncPromise;
|
|
38638
38881
|
}
|
|
38639
38882
|
return true;
|
|
38640
38883
|
}
|
|
38884
|
+
/**
|
|
38885
|
+
* Syncs an update operation to the relay in the background.
|
|
38886
|
+
*
|
|
38887
|
+
* @private
|
|
38888
|
+
*/
|
|
38889
|
+
async _syncUpdateToRelay(holonId, lensName, dataId, mergedData, existingData, path) {
|
|
38890
|
+
const startTime = Date.now();
|
|
38891
|
+
this._log("DEBUG", "🔄 UPDATE SYNC START", {
|
|
38892
|
+
path,
|
|
38893
|
+
dataId,
|
|
38894
|
+
relays: this.client?.relays?.map((r) => r.url) || ["local-only"]
|
|
38895
|
+
});
|
|
38896
|
+
try {
|
|
38897
|
+
await write(this.client, path, mergedData);
|
|
38898
|
+
const duration = Date.now() - startTime;
|
|
38899
|
+
this._metrics.writes++;
|
|
38900
|
+
if (!this._metrics.totalWriteTime) this._metrics.totalWriteTime = 0;
|
|
38901
|
+
this._metrics.totalWriteTime += duration;
|
|
38902
|
+
this._log("DEBUG", "✅ UPDATE SYNC SUCCESS", {
|
|
38903
|
+
path,
|
|
38904
|
+
dataId,
|
|
38905
|
+
duration: `${duration}ms`,
|
|
38906
|
+
totalWrites: this._metrics.writes
|
|
38907
|
+
});
|
|
38908
|
+
if (existingData._meta && existingData._meta.activeHolograms) {
|
|
38909
|
+
this._log("DEBUG", "🔗 Refreshing active holograms", { path });
|
|
38910
|
+
refreshActiveHolograms(this.client, this.config.appName, holonId, lensName, dataId).catch((err) => this._log("WARN", "⚠️ Hologram refresh failed", { path, error: err.message }));
|
|
38911
|
+
}
|
|
38912
|
+
return true;
|
|
38913
|
+
} catch (error) {
|
|
38914
|
+
const duration = Date.now() - startTime;
|
|
38915
|
+
this._log("ERROR", "❌ UPDATE SYNC FAILED", {
|
|
38916
|
+
path,
|
|
38917
|
+
dataId,
|
|
38918
|
+
duration: `${duration}ms`,
|
|
38919
|
+
error: error.message,
|
|
38920
|
+
stack: error.stack?.split("\n")[1]?.trim()
|
|
38921
|
+
});
|
|
38922
|
+
this._log("WARN", "💾 Data retained in write cache for retry", { path, cacheSize: this._writeCache.size });
|
|
38923
|
+
return false;
|
|
38924
|
+
}
|
|
38925
|
+
}
|
|
38641
38926
|
/**
|
|
38642
38927
|
* Deletes data from a specific holon and lens.
|
|
38928
|
+
* By default, delete is optimistic (non-blocking) - returns immediately after cache invalidation.
|
|
38643
38929
|
*
|
|
38644
38930
|
* @param {string} holonId - H3 cell ID for the holon
|
|
38645
38931
|
* @param {string} lensName - Name of the lens (data category)
|
|
38646
38932
|
* @param {string} dataId - ID of the data to delete
|
|
38647
38933
|
* @param {Object} [options={}] - Delete options
|
|
38648
38934
|
* @param {string} [options.capabilityToken] - Capability token for authorization
|
|
38935
|
+
* @param {boolean} [options.blocking=false] - If true, wait for relay confirmation before returning
|
|
38649
38936
|
* @returns {Promise<boolean>} True if deletion succeeded, false if data not found
|
|
38650
38937
|
* @throws {ValidationError} If holonId, lensName, or dataId is invalid
|
|
38651
38938
|
* @throws {AuthorizationError} If not owner and no valid capability token
|
|
38652
38939
|
*/
|
|
38653
38940
|
async delete(holonId, lensName, dataId, options = {}) {
|
|
38654
|
-
if (
|
|
38941
|
+
if (holonId != null) holonId = String(holonId);
|
|
38942
|
+
if (lensName != null) lensName = String(lensName);
|
|
38943
|
+
if (dataId != null) dataId = String(dataId);
|
|
38944
|
+
if (!holonId) {
|
|
38655
38945
|
throw new ValidationError$1("ValidationError: holonId must be a non-empty string");
|
|
38656
38946
|
}
|
|
38657
|
-
if (!lensName
|
|
38947
|
+
if (!lensName) {
|
|
38658
38948
|
throw new ValidationError$1("ValidationError: lensName must be a non-empty string");
|
|
38659
38949
|
}
|
|
38660
|
-
if (!dataId
|
|
38950
|
+
if (!dataId) {
|
|
38661
38951
|
throw new ValidationError$1("ValidationError: dataId must be a non-empty string");
|
|
38662
38952
|
}
|
|
38663
38953
|
const path = buildPath(this.config.appName, holonId, lensName, dataId);
|
|
38664
|
-
const
|
|
38954
|
+
const cached = this._writeCache.get(path);
|
|
38955
|
+
let existingData = cached ? cached.data : null;
|
|
38956
|
+
let dataSource = cached ? "write-cache" : null;
|
|
38665
38957
|
if (!existingData) {
|
|
38958
|
+
existingData = await read(this.client, path);
|
|
38959
|
+
dataSource = existingData ? "storage" : null;
|
|
38960
|
+
}
|
|
38961
|
+
if (!existingData) {
|
|
38962
|
+
this._log("DEBUG", "🗑️ DELETE: Data not found", { path });
|
|
38666
38963
|
return false;
|
|
38667
38964
|
}
|
|
38965
|
+
this._log("DEBUG", "🗑️ DELETE: Found data", { path, source: dataSource });
|
|
38668
38966
|
const dataOwner = existingData.owner || existingData._creator;
|
|
38669
38967
|
const isOwner = !dataOwner || dataOwner === this.client.publicKey;
|
|
38670
38968
|
const capToken = options.capabilityToken || options.capability;
|
|
@@ -38682,11 +38980,49 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38682
38980
|
throw new AuthorizationError("AuthorizationError: Invalid capability token for delete operation", "delete");
|
|
38683
38981
|
}
|
|
38684
38982
|
}
|
|
38983
|
+
this._writeCache.delete(path);
|
|
38984
|
+
this._deleteCache.add(path);
|
|
38985
|
+
this._log("DEBUG", "🗑️ CACHE DELETE", {
|
|
38986
|
+
path,
|
|
38987
|
+
dataId,
|
|
38988
|
+
deleteCacheSize: this._deleteCache.size,
|
|
38989
|
+
writeCacheSize: this._writeCache.size,
|
|
38990
|
+
mode: options.blocking ? "blocking" : "optimistic"
|
|
38991
|
+
});
|
|
38685
38992
|
if (existingData.hologram === true) {
|
|
38993
|
+
this._log("DEBUG", "🔗 Deleting hologram", { path });
|
|
38686
38994
|
return this.deleteHologram(holonId, lensName, dataId, options);
|
|
38687
38995
|
}
|
|
38688
|
-
|
|
38689
|
-
this.
|
|
38996
|
+
const startTime = Date.now();
|
|
38997
|
+
this._log("DEBUG", "🔄 DELETE SYNC START", { path, dataId });
|
|
38998
|
+
const deletePromise = deleteData(this.client, path).then(() => {
|
|
38999
|
+
const duration = Date.now() - startTime;
|
|
39000
|
+
this._metrics.deletes++;
|
|
39001
|
+
this._deleteCache.delete(path);
|
|
39002
|
+
this._log("DEBUG", "✅ DELETE SYNC SUCCESS", {
|
|
39003
|
+
path,
|
|
39004
|
+
dataId,
|
|
39005
|
+
duration: `${duration}ms`,
|
|
39006
|
+
totalDeletes: this._metrics.deletes,
|
|
39007
|
+
deleteCacheSize: this._deleteCache.size
|
|
39008
|
+
});
|
|
39009
|
+
return true;
|
|
39010
|
+
}).catch((error) => {
|
|
39011
|
+
const duration = Date.now() - startTime;
|
|
39012
|
+
this._log("ERROR", "❌ DELETE SYNC FAILED", {
|
|
39013
|
+
path,
|
|
39014
|
+
dataId,
|
|
39015
|
+
duration: `${duration}ms`,
|
|
39016
|
+
error: error.message
|
|
39017
|
+
});
|
|
39018
|
+
this._log("WARN", "💾 Path retained in delete cache", { path, deleteCacheSize: this._deleteCache.size });
|
|
39019
|
+
return false;
|
|
39020
|
+
});
|
|
39021
|
+
if (options.blocking) {
|
|
39022
|
+
this._log("DEBUG", "⏳ BLOCKING: Waiting for delete confirmation", { path });
|
|
39023
|
+
return deletePromise;
|
|
39024
|
+
}
|
|
39025
|
+
this._log("DEBUG", "⚡ OPTIMISTIC: Returning immediately, delete sync in background", { path });
|
|
38690
39026
|
return true;
|
|
38691
39027
|
}
|
|
38692
39028
|
// === Global Tables ===
|
|
@@ -38698,6 +39034,8 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38698
39034
|
* @returns {Promise<boolean>} True if write succeeded
|
|
38699
39035
|
*/
|
|
38700
39036
|
async writeGlobal(table, data) {
|
|
39037
|
+
if (table != null) table = String(table);
|
|
39038
|
+
if (data?.id != null) data.id = String(data.id);
|
|
38701
39039
|
return writeGlobal(this.client, this.config.appName, table, data);
|
|
38702
39040
|
}
|
|
38703
39041
|
/**
|
|
@@ -38708,6 +39046,8 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38708
39046
|
* @returns {Promise<Object|Array|null>} Data object, array of objects, or null
|
|
38709
39047
|
*/
|
|
38710
39048
|
async readGlobal(table, key = null) {
|
|
39049
|
+
if (table != null) table = String(table);
|
|
39050
|
+
if (key != null) key = String(key);
|
|
38711
39051
|
return readGlobal(this.client, this.config.appName, table, key);
|
|
38712
39052
|
}
|
|
38713
39053
|
/**
|
|
@@ -38719,6 +39059,8 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38719
39059
|
* @returns {Promise<boolean>} True if update succeeded
|
|
38720
39060
|
*/
|
|
38721
39061
|
async updateGlobal(table, key, updates) {
|
|
39062
|
+
if (table != null) table = String(table);
|
|
39063
|
+
if (key != null) key = String(key);
|
|
38722
39064
|
return updateGlobal(this.client, this.config.appName, table, key, updates);
|
|
38723
39065
|
}
|
|
38724
39066
|
/**
|
|
@@ -38729,6 +39071,8 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38729
39071
|
* @returns {Promise<boolean>} True if deletion succeeded
|
|
38730
39072
|
*/
|
|
38731
39073
|
async deleteGlobal(table, key) {
|
|
39074
|
+
if (table != null) table = String(table);
|
|
39075
|
+
if (key != null) key = String(key);
|
|
38732
39076
|
return deleteGlobal(this.client, this.config.appName, table, key);
|
|
38733
39077
|
}
|
|
38734
39078
|
/**
|
|
@@ -38738,6 +39082,7 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38738
39082
|
* @returns {Promise<Array>} Array of all data objects in the table
|
|
38739
39083
|
*/
|
|
38740
39084
|
async getAllGlobal(table) {
|
|
39085
|
+
if (table != null) table = String(table);
|
|
38741
39086
|
return getAllGlobal(this.client, this.config.appName, table);
|
|
38742
39087
|
}
|
|
38743
39088
|
/**
|
|
@@ -38747,6 +39092,7 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38747
39092
|
* @returns {Promise<boolean>} True if deletion succeeded
|
|
38748
39093
|
*/
|
|
38749
39094
|
async deleteAllGlobal(table) {
|
|
39095
|
+
if (table != null) table = String(table);
|
|
38750
39096
|
return deleteAllGlobal(this.client, this.config.appName, table);
|
|
38751
39097
|
}
|
|
38752
39098
|
// === Batch Operations ===
|
|
@@ -38768,6 +39114,8 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
38768
39114
|
* @returns {Promise<boolean>} True if deletion succeeded
|
|
38769
39115
|
*/
|
|
38770
39116
|
async deleteAll(holonId, lensName) {
|
|
39117
|
+
if (holonId != null) holonId = String(holonId);
|
|
39118
|
+
if (lensName != null) lensName = String(lensName);
|
|
38771
39119
|
const path = buildPath(this.config.appName, holonId, lensName);
|
|
38772
39120
|
return deleteAll(this.client, path);
|
|
38773
39121
|
}
|
|
@@ -39075,6 +39423,49 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
39075
39423
|
getHologramSource(data) {
|
|
39076
39424
|
return getHologramSource(data);
|
|
39077
39425
|
}
|
|
39426
|
+
/**
|
|
39427
|
+
* Updates platform-specific data for an active hologram entry.
|
|
39428
|
+
* Used by platform clients (Telegram, Discord, etc.) to store their message IDs.
|
|
39429
|
+
*
|
|
39430
|
+
* @param {string} sourceHolon - Source holon ID where the original data lives
|
|
39431
|
+
* @param {string} lensName - Lens name (e.g., 'quests', 'events')
|
|
39432
|
+
* @param {string} dataId - Data ID
|
|
39433
|
+
* @param {string} targetHolon - Target holon ID where the hologram exists
|
|
39434
|
+
* @param {string} platform - Platform name (e.g., 'telegram', 'discord')
|
|
39435
|
+
* @param {Object} platformData - Platform-specific data (e.g., { messageId: 123 })
|
|
39436
|
+
* @returns {Promise<boolean>} Success indicator
|
|
39437
|
+
*/
|
|
39438
|
+
async updateHologramPlatform(sourceHolon, lensName, dataId, targetHolon, platform, platformData) {
|
|
39439
|
+
return updateActiveHologramPlatform(
|
|
39440
|
+
this.client,
|
|
39441
|
+
this.config.appName,
|
|
39442
|
+
sourceHolon,
|
|
39443
|
+
lensName,
|
|
39444
|
+
dataId,
|
|
39445
|
+
targetHolon,
|
|
39446
|
+
platform,
|
|
39447
|
+
platformData
|
|
39448
|
+
);
|
|
39449
|
+
}
|
|
39450
|
+
/**
|
|
39451
|
+
* Gets active holograms for a data item, optionally filtered by platform.
|
|
39452
|
+
*
|
|
39453
|
+
* @param {string} sourceHolon - Source holon ID
|
|
39454
|
+
* @param {string} lensName - Lens name
|
|
39455
|
+
* @param {string} dataId - Data ID
|
|
39456
|
+
* @param {string} [platform] - Optional platform filter (e.g., 'telegram')
|
|
39457
|
+
* @returns {Promise<Array>} Array of hologram entries with platform data
|
|
39458
|
+
*/
|
|
39459
|
+
async getActiveHolograms(sourceHolon, lensName, dataId, platform = null) {
|
|
39460
|
+
return getActiveHolograms(
|
|
39461
|
+
this.client,
|
|
39462
|
+
this.config.appName,
|
|
39463
|
+
sourceHolon,
|
|
39464
|
+
lensName,
|
|
39465
|
+
dataId,
|
|
39466
|
+
platform
|
|
39467
|
+
);
|
|
39468
|
+
}
|
|
39078
39469
|
/**
|
|
39079
39470
|
* Cleans up circular hologram references in a holon.
|
|
39080
39471
|
*
|
|
@@ -39518,6 +39909,48 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
39518
39909
|
avgWriteTime: writes > 0 ? totalWriteTime / writes : 0
|
|
39519
39910
|
};
|
|
39520
39911
|
}
|
|
39912
|
+
/**
|
|
39913
|
+
* Returns the current status of optimistic caches for debugging.
|
|
39914
|
+
* Useful for understanding what data is pending sync or marked as deleted.
|
|
39915
|
+
*
|
|
39916
|
+
* @returns {Object} Cache status object
|
|
39917
|
+
* @returns {number} return.writeCacheSize - Number of items in write cache (pending sync)
|
|
39918
|
+
* @returns {number} return.deleteCacheSize - Number of items in delete cache (pending delete sync)
|
|
39919
|
+
* @returns {Array<Object>} return.writeCacheEntries - Array of write cache entries with path and age
|
|
39920
|
+
* @returns {Array<string>} return.deleteCachePaths - Array of paths marked as deleted
|
|
39921
|
+
*/
|
|
39922
|
+
getCacheStatus() {
|
|
39923
|
+
const now = Date.now();
|
|
39924
|
+
const writeCacheEntries = [];
|
|
39925
|
+
for (const [path, entry] of this._writeCache.entries()) {
|
|
39926
|
+
writeCacheEntries.push({
|
|
39927
|
+
path,
|
|
39928
|
+
dataId: entry.data?.id,
|
|
39929
|
+
age: `${now - entry.timestamp}ms`,
|
|
39930
|
+
timestamp: new Date(entry.timestamp).toISOString()
|
|
39931
|
+
});
|
|
39932
|
+
}
|
|
39933
|
+
return {
|
|
39934
|
+
writeCacheSize: this._writeCache.size,
|
|
39935
|
+
deleteCacheSize: this._deleteCache.size,
|
|
39936
|
+
writeCacheEntries,
|
|
39937
|
+
deleteCachePaths: Array.from(this._deleteCache)
|
|
39938
|
+
};
|
|
39939
|
+
}
|
|
39940
|
+
/**
|
|
39941
|
+
* Clears the optimistic caches. Useful for testing or resetting state.
|
|
39942
|
+
* Warning: This may cause inconsistencies if there are pending syncs.
|
|
39943
|
+
*/
|
|
39944
|
+
clearCaches() {
|
|
39945
|
+
const writeSize = this._writeCache.size;
|
|
39946
|
+
const deleteSize = this._deleteCache.size;
|
|
39947
|
+
this._writeCache.clear();
|
|
39948
|
+
this._deleteCache.clear();
|
|
39949
|
+
this._log("WARN", "🧹 CACHES CLEARED", {
|
|
39950
|
+
writeCacheCleared: writeSize,
|
|
39951
|
+
deleteCacheCleared: deleteSize
|
|
39952
|
+
});
|
|
39953
|
+
}
|
|
39521
39954
|
// === Aliases ===
|
|
39522
39955
|
/**
|
|
39523
39956
|
* Alias for {@link HoloSphereBase#write}.
|
|
@@ -39838,4 +40271,4 @@ export {
|
|
|
39838
40271
|
exists as y,
|
|
39839
40272
|
bytes as z
|
|
39840
40273
|
};
|
|
39841
|
-
//# sourceMappingURL=index-
|
|
40274
|
+
//# sourceMappingURL=index-D-jZhliX.js.map
|