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.
Files changed (29) hide show
  1. package/dist/cjs/holosphere.cjs +1 -1
  2. package/dist/esm/holosphere.js +1 -1
  3. package/dist/{index-DeZ1xz_s.js → index-Bl6rM1NW.js} +2 -2
  4. package/dist/{index-DeZ1xz_s.js.map → index-Bl6rM1NW.js.map} +1 -1
  5. package/dist/{index-DDGt_V9o.cjs → index-Bwg3OzRM.cjs} +2 -2
  6. package/dist/{index-DDGt_V9o.cjs.map → index-Bwg3OzRM.cjs.map} +1 -1
  7. package/dist/{index-DJXftyvB.js → index-D-jZhliX.js} +531 -98
  8. package/dist/index-D-jZhliX.js.map +1 -0
  9. package/dist/index-Dc6Z8Aob.cjs +18 -0
  10. package/dist/index-Dc6Z8Aob.cjs.map +1 -0
  11. package/dist/{indexeddb-storage-BFt6hMeF.js → indexeddb-storage-5eiUNsHC.js} +2 -2
  12. package/dist/{indexeddb-storage-BFt6hMeF.js.map → indexeddb-storage-5eiUNsHC.js.map} +1 -1
  13. package/dist/{indexeddb-storage-BK5tv4Sh.cjs → indexeddb-storage-FNFUVvTJ.cjs} +2 -2
  14. package/dist/{indexeddb-storage-BK5tv4Sh.cjs.map → indexeddb-storage-FNFUVvTJ.cjs.map} +1 -1
  15. package/dist/{memory-storage-C9HuoL2E.js → memory-storage-CI-gfmuG.js} +2 -2
  16. package/dist/{memory-storage-C9HuoL2E.js.map → memory-storage-CI-gfmuG.js.map} +1 -1
  17. package/dist/{memory-storage-Dao7jfYG.cjs → memory-storage-DMt36uZO.cjs} +2 -2
  18. package/dist/{memory-storage-Dao7jfYG.cjs.map → memory-storage-DMt36uZO.cjs.map} +1 -1
  19. package/dist/{secp256k1-CreY7Pcl.js → secp256k1-CEwJNcfV.js} +2 -2
  20. package/dist/{secp256k1-CreY7Pcl.js.map → secp256k1-CEwJNcfV.js.map} +1 -1
  21. package/dist/{secp256k1-BbKzbLtD.cjs → secp256k1-CiEONUnj.cjs} +2 -2
  22. package/dist/{secp256k1-BbKzbLtD.cjs.map → secp256k1-CiEONUnj.cjs.map} +1 -1
  23. package/package.json +1 -1
  24. package/src/index.js +62 -19
  25. package/dist/cdn/holosphere.min.js +0 -55
  26. package/dist/cdn/holosphere.min.js.map +0 -1
  27. package/dist/index-DJXftyvB.js.map +0 -1
  28. package/dist/index-DMbdcMtK.cjs +0 -18
  29. 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-BFt6hMeF.js");
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-C9HuoL2E.js");
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-alpha8";
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 (const event of matching) {
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 (client.clearCache) {
5072
- client.clearCache(dTag);
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-CreY7Pcl.js");
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-DeZ1xz_s.js");
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-DeZ1xz_s.js");
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-DeZ1xz_s.js");
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
- * @returns {Promise<boolean>} True if write succeeded
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 (!holonId || typeof holonId !== "string") {
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 || typeof lensName !== "string") {
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
- const existingData = await read(this.client, path);
38365
- if (existingData && existingData.hologram === true && existingData.target) {
38366
- const hologramStructureFields = ["hologram", "soul", "target", "id", "_meta"];
38367
- const localOverrideFields = Object.keys(existingData).filter((k) => !hologramStructureFields.includes(k));
38368
- const localData = { ...existingData };
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
- const endTime2 = Date.now();
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 += endTime2 - startTime2;
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
- if (!data._meta) data._meta = {};
38418
- data._meta.createdAt = data._meta.createdAt || Date.now();
38419
- data._meta.updatedAt = Date.now();
38420
- if (options.validate !== false && this.schemas.has(lensName)) {
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(data, schemaObj.schema, lensName, strict);
38583
+ validate(mergedSourceData, schemaObj.schema, lensName, strict);
38425
38584
  }
38426
38585
  }
38427
38586
  const startTime = Date.now();
38428
- await write(this.client, path, data);
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 (!holonId || typeof holonId !== "string") {
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 || typeof lensName !== "string") {
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._log("DEBUG", "read", { holonId, lensName, dataId, path });
38558
- result = await read(this.client, path);
38559
- this._log("DEBUG", "read result", { found: !!result, isHologram: result?.hologram === true });
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
- result = await readAll(this.client, path);
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 existingData = await read(this.client, path);
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
- const startTime = Date.now();
38631
- await write(this.client, path, mergedData);
38632
- const endTime = Date.now();
38633
- this._metrics.writes++;
38634
- if (!this._metrics.totalWriteTime) this._metrics.totalWriteTime = 0;
38635
- this._metrics.totalWriteTime += endTime - startTime;
38636
- if (existingData._meta && existingData._meta.activeHolograms) {
38637
- await refreshActiveHolograms(this.client, this.config.appName, holonId, lensName, dataId);
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 (!holonId || typeof holonId !== "string") {
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 || typeof lensName !== "string") {
38947
+ if (!lensName) {
38658
38948
  throw new ValidationError$1("ValidationError: lensName must be a non-empty string");
38659
38949
  }
38660
- if (!dataId || typeof dataId !== "string") {
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 existingData = await read(this.client, path);
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
- await deleteData(this.client, path);
38689
- this._metrics.deletes++;
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-DJXftyvB.js.map
40274
+ //# sourceMappingURL=index-D-jZhliX.js.map