holosphere 2.0.0-alpha7 → 2.0.0-alpha8

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 (40) hide show
  1. package/dist/cjs/holosphere.cjs +1 -1
  2. package/dist/esm/holosphere.js +1 -1
  3. package/dist/{index-d6f4RJBM.js → index-4XHHKe6S.js} +356 -58
  4. package/dist/index-4XHHKe6S.js.map +1 -0
  5. package/dist/{index-jmTHEbR2.js → index-BjP1TXGz.js} +2 -2
  6. package/dist/{index-jmTHEbR2.js.map → index-BjP1TXGz.js.map} +1 -1
  7. package/dist/{index-C-IlLYlk.cjs → index-CKffQDmQ.cjs} +2 -2
  8. package/dist/{index-C-IlLYlk.cjs.map → index-CKffQDmQ.cjs.map} +1 -1
  9. package/dist/index-Dz5kOZMI.cjs +5 -0
  10. package/dist/index-Dz5kOZMI.cjs.map +1 -0
  11. package/dist/{indexeddb-storage-a8GipaDr.cjs → indexeddb-storage-DD7EFBVc.cjs} +2 -2
  12. package/dist/{indexeddb-storage-a8GipaDr.cjs.map → indexeddb-storage-DD7EFBVc.cjs.map} +1 -1
  13. package/dist/{indexeddb-storage-D8kOl0oK.js → indexeddb-storage-lExjjFlV.js} +2 -2
  14. package/dist/{indexeddb-storage-D8kOl0oK.js.map → indexeddb-storage-lExjjFlV.js.map} +1 -1
  15. package/dist/{memory-storage-DBQK622V.js → memory-storage-C68adso2.js} +2 -2
  16. package/dist/{memory-storage-DBQK622V.js.map → memory-storage-C68adso2.js.map} +1 -1
  17. package/dist/{memory-storage-gfRovk2O.cjs → memory-storage-DD_6yyXT.cjs} +2 -2
  18. package/dist/{memory-storage-gfRovk2O.cjs.map → memory-storage-DD_6yyXT.cjs.map} +1 -1
  19. package/dist/{secp256k1-BCAPF45D.cjs → secp256k1-DYELiqgx.cjs} +2 -2
  20. package/dist/{secp256k1-BCAPF45D.cjs.map → secp256k1-DYELiqgx.cjs.map} +1 -1
  21. package/dist/{secp256k1-DYm_CMqW.js → secp256k1-OM8siPyy.js} +2 -2
  22. package/dist/{secp256k1-DYm_CMqW.js.map → secp256k1-OM8siPyy.js.map} +1 -1
  23. package/examples/holosphere-widget.js +1242 -0
  24. package/examples/widget-demo.html +274 -0
  25. package/examples/widget.html +703 -0
  26. package/package.json +3 -1
  27. package/src/cdn-entry.js +22 -0
  28. package/src/contracts/queries.js +16 -1
  29. package/src/core/holosphere.js +2 -2
  30. package/src/crypto/nostr-utils.js +36 -2
  31. package/src/federation/handshake.js +16 -4
  32. package/src/index.js +16 -2
  33. package/src/storage/backends/gundb-backend.js +293 -9
  34. package/src/storage/gun-wrapper.js +64 -16
  35. package/src/storage/nostr-async.js +40 -25
  36. package/src/storage/unified-storage.js +31 -1
  37. package/vite.config.cdn.js +60 -0
  38. package/dist/index-Bvwyvd0T.cjs +0 -5
  39. package/dist/index-Bvwyvd0T.cjs.map +0 -1
  40. package/dist/index-d6f4RJBM.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { getPublicKey as getPublicKey$2, finalizeEvent, SimplePool, nip19, nip04, verifyEvent as verifyEvent$1 } from "nostr-tools";
1
+ import { getPublicKey as getPublicKey$2, finalizeEvent, SimplePool, nip19, nip04, nip44, verifyEvent as verifyEvent$1 } from "nostr-tools";
2
2
  import * as h3 from "h3-js";
3
3
  import { latLngToCell, getResolution, cellToParent, cellToChildren, isValidCell } from "h3-js";
4
4
  import Ajv from "ajv";
@@ -361,12 +361,12 @@ async function createPersistentStorage(namespace, options = {}) {
361
361
  const isBrowser = typeof window !== "undefined" && typeof window.indexedDB !== "undefined";
362
362
  typeof process !== "undefined" && process.versions && void 0;
363
363
  if (isBrowser) {
364
- const { IndexedDBStorage } = await import("./indexeddb-storage-D8kOl0oK.js");
364
+ const { IndexedDBStorage } = await import("./indexeddb-storage-lExjjFlV.js");
365
365
  const storage = new IndexedDBStorage();
366
366
  await storage.init(namespace);
367
367
  return storage;
368
368
  } else {
369
- const { MemoryStorage } = await import("./memory-storage-DBQK622V.js");
369
+ const { MemoryStorage } = await import("./memory-storage-C68adso2.js");
370
370
  const storage = new MemoryStorage();
371
371
  await storage.init(namespace);
372
372
  return storage;
@@ -1628,8 +1628,16 @@ function buildGlobalPath(appname, tableName, key = null) {
1628
1628
  function encodePathComponent$1(component) {
1629
1629
  return encodeURIComponent(component).replace(/%2F/g, "/");
1630
1630
  }
1631
+ function getGunPath(gun, path) {
1632
+ const parts = path.split("/").filter((p) => p.length > 0);
1633
+ let ref = gun;
1634
+ for (const part of parts) {
1635
+ ref = ref.get(part);
1636
+ }
1637
+ return ref;
1638
+ }
1631
1639
  function serializeForGun(data) {
1632
- return { _json: JSON.stringify(data) };
1640
+ return JSON.stringify(data);
1633
1641
  }
1634
1642
  function deserializeFromGun(data) {
1635
1643
  if (!data) {
@@ -1681,15 +1689,29 @@ function deserializeFromGun(data) {
1681
1689
  async function write$2(gun, path, data) {
1682
1690
  try {
1683
1691
  const serialized = serializeForGun(data);
1684
- await gunPut(gun.get(path), serialized, 2e3);
1685
- await new Promise((resolve) => setTimeout(resolve, 50));
1686
- return true;
1692
+ const parts = path.split("/").filter((p) => p.length > 0);
1693
+ console.log("[gun-wrapper] write:", { path, parts, dataId: data?.id });
1694
+ const ref = getGunPath(gun, path);
1695
+ console.log("[gun-wrapper] write ref soul:", ref?._.get);
1696
+ const ack = await gunPut(ref, serialized, 5e3);
1697
+ console.log("[gun-wrapper] write ack:", { ok: ack.ok, timeout: ack.timeout });
1698
+ if (ack.timeout) {
1699
+ console.warn("[gun-wrapper] write timed out (data may not be persisted):", path);
1700
+ }
1701
+ console.log("[gun-wrapper] write complete:", path);
1702
+ return { ok: true, timeout: ack.timeout || false };
1687
1703
  } catch (error) {
1704
+ console.error("[gun-wrapper] write error:", error);
1688
1705
  throw error;
1689
1706
  }
1690
1707
  }
1691
1708
  async function read$2(gun, path) {
1692
- const rawData = await gunPromise(gun.get(path), 2e3);
1709
+ const parts = path.split("/").filter((p) => p.length > 0);
1710
+ console.log("[gun-wrapper] read:", { path, parts });
1711
+ const ref = getGunPath(gun, path);
1712
+ console.log("[gun-wrapper] read ref soul:", ref?._.get);
1713
+ const rawData = await gunPromise(ref, 2e3);
1714
+ console.log("[gun-wrapper] read rawData:", rawData ? typeof rawData === "string" ? rawData.substring(0, 100) : "object" : "null");
1693
1715
  if (!rawData) {
1694
1716
  return null;
1695
1717
  }
@@ -1700,16 +1722,20 @@ async function read$2(gun, path) {
1700
1722
  return data;
1701
1723
  }
1702
1724
  async function readAll$2(gun, path, timeout = 5e3) {
1725
+ const parts = path.split("/").filter((p) => p.length > 0);
1726
+ console.log("[gun-wrapper] readAll:", { path, parts });
1703
1727
  return new Promise((resolve) => {
1704
1728
  const output2 = /* @__PURE__ */ new Map();
1705
1729
  let settled = false;
1706
1730
  let expectedCount = 0;
1707
1731
  let receivedCount = 0;
1708
- const ref = gun.get(path);
1732
+ const ref = getGunPath(gun, path);
1733
+ console.log("[gun-wrapper] readAll ref soul:", ref?._.get);
1709
1734
  const tryResolve = () => {
1710
1735
  if (settled) return;
1711
1736
  if (expectedCount > 0 && receivedCount >= expectedCount) {
1712
1737
  settled = true;
1738
+ console.log("[gun-wrapper] readAll resolved with", output2.size, "items");
1713
1739
  resolve(Array.from(output2.values()));
1714
1740
  }
1715
1741
  };
@@ -1718,14 +1744,20 @@ async function readAll$2(gun, path, timeout = 5e3) {
1718
1744
  };
1719
1745
  ref.once((parentData) => {
1720
1746
  if (settled) return;
1747
+ console.log("[gun-wrapper] readAll parentData:", parentData);
1748
+ console.log("[gun-wrapper] readAll parentData keys:", parentData ? Object.keys(parentData).filter((k) => k !== "_") : "null");
1749
+ console.log("[gun-wrapper] readAll parentData type:", typeof parentData);
1721
1750
  if (!parentData) {
1722
1751
  settled = true;
1752
+ console.log("[gun-wrapper] readAll: no parent data, returning empty");
1723
1753
  resolve([]);
1724
1754
  return;
1725
1755
  }
1726
1756
  const keys = Object.keys(parentData).filter((k) => k !== "_");
1757
+ console.log("[gun-wrapper] readAll keys:", keys);
1727
1758
  if (keys.length === 0) {
1728
1759
  settled = true;
1760
+ console.log("[gun-wrapper] readAll: no keys, returning empty");
1729
1761
  resolve([]);
1730
1762
  return;
1731
1763
  }
@@ -1767,7 +1799,7 @@ async function readAll$2(gun, path, timeout = 5e3) {
1767
1799
  });
1768
1800
  }
1769
1801
  async function update$2(gun, path, updates) {
1770
- const rawData = await gunPromise(gun.get(path));
1802
+ const rawData = await gunPromise(getGunPath(gun, path));
1771
1803
  if (!rawData) {
1772
1804
  return false;
1773
1805
  }
@@ -1778,7 +1810,8 @@ async function update$2(gun, path, updates) {
1778
1810
  const merged = { ...existing, ...updates };
1779
1811
  try {
1780
1812
  const serialized = serializeForGun(merged);
1781
- await gunPut(gun.get(path), serialized);
1813
+ await gunPut(getGunPath(gun, path), serialized, 2e3);
1814
+ await new Promise((resolve) => setTimeout(resolve, 200));
1782
1815
  return true;
1783
1816
  } catch (error) {
1784
1817
  throw error;
@@ -1786,7 +1819,7 @@ async function update$2(gun, path, updates) {
1786
1819
  }
1787
1820
  async function deleteData$2(gun, path) {
1788
1821
  try {
1789
- const rawData = await gunPromise(gun.get(path));
1822
+ const rawData = await gunPromise(getGunPath(gun, path));
1790
1823
  if (!rawData) {
1791
1824
  return true;
1792
1825
  }
@@ -1796,7 +1829,8 @@ async function deleteData$2(gun, path) {
1796
1829
  _deleted: true,
1797
1830
  _deletedAt: Date.now()
1798
1831
  };
1799
- await gunPut(gun.get(path), serializeForGun(tombstone));
1832
+ await gunPut(getGunPath(gun, path), serializeForGun(tombstone), 2e3);
1833
+ await new Promise((resolve) => setTimeout(resolve, 200));
1800
1834
  return true;
1801
1835
  } catch (error) {
1802
1836
  throw error;
@@ -1818,7 +1852,7 @@ function subscribe$2(gun, path, callback, options = {}) {
1818
1852
  const pathParts = path.split("/");
1819
1853
  const isPrefix = options.prefix !== void 0 ? options.prefix : pathParts.length <= 3;
1820
1854
  if (isPrefix) {
1821
- const ref = gun.get(path);
1855
+ const ref = getGunPath(gun, path);
1822
1856
  ref.map().on((data, key) => {
1823
1857
  if (data && !key.startsWith("_")) {
1824
1858
  const deserialized = deserializeFromGun(data);
@@ -1836,7 +1870,7 @@ function subscribe$2(gun, path, callback, options = {}) {
1836
1870
  }
1837
1871
  };
1838
1872
  } else {
1839
- const listener = gun.get(path).on((data, key) => {
1873
+ const listener = getGunPath(gun, path).on((data, key) => {
1840
1874
  if (data) {
1841
1875
  const deserialized = deserializeFromGun(data);
1842
1876
  if (deserialized && !deserialized._deleted) {
@@ -3193,24 +3227,44 @@ class GunDBBackend extends StorageBackend {
3193
3227
  this.schemaValidator = null;
3194
3228
  this.subscriptions = /* @__PURE__ */ new Map();
3195
3229
  this.subscriptionCounter = 0;
3230
+ this.writeCache = /* @__PURE__ */ new Map();
3231
+ this.writeCacheTTL = 3e5;
3232
+ this.pendingWrites = /* @__PURE__ */ new Map();
3233
+ this.maxWriteRetries = 5;
3234
+ this.writeRetryInterval = 1e4;
3196
3235
  }
3197
3236
  async init() {
3198
3237
  let Gun;
3199
3238
  try {
3239
+ console.log("[gundb-backend] Importing Gun...");
3200
3240
  const gunModule = await import("./browser-BSniCNqO.js").then((n2) => n2.b);
3201
3241
  Gun = gunModule.default || gunModule;
3242
+ console.log("[gundb-backend] Gun imported:", typeof Gun);
3202
3243
  } catch (error) {
3203
3244
  throw new Error(
3204
3245
  'GunDB backend requires the "gun" package. Install it with: npm install gun'
3205
3246
  );
3206
3247
  }
3248
+ const dataDir = this.config.dataDir || "radata";
3207
3249
  const gunConfig = {
3208
3250
  peers: this.config.peers || [],
3209
3251
  radisk: this.config.radisk !== false,
3210
3252
  localStorage: this.config.localStorage !== false,
3211
- file: this.config.dataDir
3253
+ file: dataDir
3212
3254
  };
3255
+ console.log("[gundb-backend] Gun config:", JSON.stringify(gunConfig));
3213
3256
  this.gun = Gun(gunConfig);
3257
+ console.log("[gundb-backend] Gun instance created:", !!this.gun);
3258
+ try {
3259
+ const testKey = `_test_${Date.now()}`;
3260
+ this.gun.get(testKey).put({ test: true }, (ack) => {
3261
+ console.log("[gundb-backend] Gun test write ack:", ack);
3262
+ });
3263
+ console.log("[gundb-backend] Gun test write initiated");
3264
+ } catch (e) {
3265
+ console.error("[gundb-backend] Gun test write failed:", e.message);
3266
+ }
3267
+ await new Promise((resolve) => setTimeout(resolve, 500));
3214
3268
  try {
3215
3269
  const SEA = Gun.SEA;
3216
3270
  if (SEA) {
@@ -3236,6 +3290,88 @@ class GunDBBackend extends StorageBackend {
3236
3290
  await this.schemaValidator.init();
3237
3291
  }
3238
3292
  // ============================================================================
3293
+ // WRITE CACHE (for immediate consistency)
3294
+ // ============================================================================
3295
+ /**
3296
+ * Add item to the local write cache for immediate consistency
3297
+ * @private
3298
+ */
3299
+ _addToWriteCache(path, data) {
3300
+ if (!data || !data.id) return;
3301
+ const parentPath = path.substring(0, path.lastIndexOf("/"));
3302
+ if (!parentPath) return;
3303
+ if (!this.writeCache.has(parentPath)) {
3304
+ this.writeCache.set(parentPath, /* @__PURE__ */ new Map());
3305
+ }
3306
+ const pathCache = this.writeCache.get(parentPath);
3307
+ pathCache.set(data.id.toString(), {
3308
+ data,
3309
+ timestamp: Date.now()
3310
+ });
3311
+ }
3312
+ /**
3313
+ * Remove item from write cache (e.g., on delete)
3314
+ * @private
3315
+ */
3316
+ _removeFromWriteCache(path, id2) {
3317
+ const parentPath = path.substring(0, path.lastIndexOf("/"));
3318
+ const pathCache = this.writeCache.get(parentPath);
3319
+ if (pathCache) {
3320
+ pathCache.delete(id2.toString());
3321
+ }
3322
+ }
3323
+ /**
3324
+ * Get cached writes for a path, filtering out expired entries
3325
+ * @private
3326
+ */
3327
+ _getWriteCacheEntries(path) {
3328
+ const pathCache = this.writeCache.get(path);
3329
+ if (!pathCache) return [];
3330
+ const now = Date.now();
3331
+ const validEntries = [];
3332
+ for (const [id2, entry] of pathCache) {
3333
+ if (now - entry.timestamp < this.writeCacheTTL) {
3334
+ validEntries.push(entry.data);
3335
+ } else {
3336
+ pathCache.delete(id2);
3337
+ }
3338
+ }
3339
+ return validEntries;
3340
+ }
3341
+ /**
3342
+ * Merge cached writes with results from Gun
3343
+ * @private
3344
+ */
3345
+ _mergeWithWriteCache(path, gunResults) {
3346
+ const cachedWrites = this._getWriteCacheEntries(path);
3347
+ if (cachedWrites.length === 0) return gunResults;
3348
+ const resultMap = /* @__PURE__ */ new Map();
3349
+ for (const item of gunResults) {
3350
+ if (item && item.id) {
3351
+ resultMap.set(item.id.toString(), item);
3352
+ }
3353
+ }
3354
+ for (const item of cachedWrites) {
3355
+ if (item && item.id && !item._deleted) {
3356
+ resultMap.set(item.id.toString(), item);
3357
+ } else if (item && item.id && item._deleted) {
3358
+ resultMap.delete(item.id.toString());
3359
+ }
3360
+ }
3361
+ return Array.from(resultMap.values());
3362
+ }
3363
+ /**
3364
+ * Clear write cache for a specific path or all paths
3365
+ * @param {string} [path] - Optional path to clear, clears all if not provided
3366
+ */
3367
+ clearWriteCache(path = null) {
3368
+ if (path) {
3369
+ this.writeCache.delete(path);
3370
+ } else {
3371
+ this.writeCache.clear();
3372
+ }
3373
+ }
3374
+ // ============================================================================
3239
3375
  // PATH BUILDING
3240
3376
  // ============================================================================
3241
3377
  buildPath(appName, holonId, lensName, key = null) {
@@ -3253,19 +3389,56 @@ class GunDBBackend extends StorageBackend {
3253
3389
  if (pathParts.length >= 3) {
3254
3390
  const lens = pathParts[2];
3255
3391
  if (!this.isReference(data)) {
3256
- const result = await this.schemaValidator.validateData(
3392
+ const result2 = await this.schemaValidator.validateData(
3257
3393
  this.gun,
3258
3394
  this.appName,
3259
3395
  lens,
3260
3396
  data
3261
3397
  );
3262
- if (!result.valid) {
3263
- throw new Error(`Schema validation failed: ${JSON.stringify(result.errors)}`);
3398
+ if (!result2.valid) {
3399
+ throw new Error(`Schema validation failed: ${JSON.stringify(result2.errors)}`);
3264
3400
  }
3265
3401
  }
3266
3402
  }
3267
3403
  }
3268
- return write$2(this.gun, path, data);
3404
+ this._addToWriteCache(path, data);
3405
+ const result = await this._writeWithRetry(path, data);
3406
+ return result;
3407
+ }
3408
+ /**
3409
+ * Write to Gun with automatic retry on timeout
3410
+ * @private
3411
+ */
3412
+ async _writeWithRetry(path, data, attempt = 0) {
3413
+ const result = await write$2(this.gun, path, data);
3414
+ if (result.timeout && attempt < this.maxWriteRetries) {
3415
+ this.pendingWrites.set(path, {
3416
+ data,
3417
+ retries: attempt + 1,
3418
+ lastAttempt: Date.now()
3419
+ });
3420
+ this._scheduleRetry(path);
3421
+ } else if (!result.timeout) {
3422
+ this.pendingWrites.delete(path);
3423
+ }
3424
+ return result;
3425
+ }
3426
+ /**
3427
+ * Schedule a background retry for a pending write
3428
+ * @private
3429
+ */
3430
+ _scheduleRetry(path) {
3431
+ setTimeout(async () => {
3432
+ const pending = this.pendingWrites.get(path);
3433
+ if (!pending) return;
3434
+ if (pending.retries >= this.maxWriteRetries) {
3435
+ console.warn(`[gundb-backend] Max retries reached for: ${path}`);
3436
+ this.pendingWrites.delete(path);
3437
+ return;
3438
+ }
3439
+ console.log(`[gundb-backend] Retrying write (attempt ${pending.retries + 1}): ${path}`);
3440
+ await this._writeWithRetry(path, pending.data, pending.retries);
3441
+ }, this.writeRetryInterval);
3269
3442
  }
3270
3443
  async read(path, options = {}) {
3271
3444
  const data = await read$2(this.gun, path);
@@ -3275,7 +3448,8 @@ class GunDBBackend extends StorageBackend {
3275
3448
  return data;
3276
3449
  }
3277
3450
  async readAll(path, options = {}) {
3278
- const items = await readAll$2(this.gun, path);
3451
+ const gunItems = await readAll$2(this.gun, path);
3452
+ const items = this._mergeWithWriteCache(path, gunItems);
3279
3453
  if (options.resolveReferences) {
3280
3454
  const resolved = [];
3281
3455
  for (const item of items) {
@@ -3296,7 +3470,20 @@ class GunDBBackend extends StorageBackend {
3296
3470
  return update$2(this.gun, path, updates);
3297
3471
  }
3298
3472
  async delete(path) {
3299
- return deleteData$2(this.gun, path);
3473
+ const result = await deleteData$2(this.gun, path);
3474
+ const pathParts = path.split("/");
3475
+ const id2 = pathParts[pathParts.length - 1];
3476
+ if (id2) {
3477
+ const parentPath = path.substring(0, path.lastIndexOf("/"));
3478
+ if (!this.writeCache.has(parentPath)) {
3479
+ this.writeCache.set(parentPath, /* @__PURE__ */ new Map());
3480
+ }
3481
+ this.writeCache.get(parentPath).set(id2.toString(), {
3482
+ data: { id: id2, _deleted: true },
3483
+ timestamp: Date.now()
3484
+ });
3485
+ }
3486
+ return result;
3300
3487
  }
3301
3488
  async deleteAll(path) {
3302
3489
  const items = await this.readAll(path);
@@ -3314,16 +3501,67 @@ class GunDBBackend extends StorageBackend {
3314
3501
  // GLOBAL TABLE OPERATIONS
3315
3502
  // ============================================================================
3316
3503
  async writeGlobal(tableName, data) {
3317
- return writeGlobal$1(this.gun, this.appName, tableName, data);
3504
+ const path = this.buildGlobalPath(tableName, data.id);
3505
+ this._addToWriteCache(path, data);
3506
+ const result = await this._writeGlobalWithRetry(tableName, data);
3507
+ return result;
3508
+ }
3509
+ /**
3510
+ * Write to global table with automatic retry on timeout
3511
+ * @private
3512
+ */
3513
+ async _writeGlobalWithRetry(tableName, data, attempt = 0) {
3514
+ const path = this.buildGlobalPath(tableName, data.id);
3515
+ const result = await writeGlobal$1(this.gun, this.appName, tableName, data);
3516
+ if (result.timeout && attempt < this.maxWriteRetries) {
3517
+ this.pendingWrites.set(path, {
3518
+ data: { tableName, data },
3519
+ isGlobal: true,
3520
+ retries: attempt + 1,
3521
+ lastAttempt: Date.now()
3522
+ });
3523
+ this._scheduleGlobalRetry(path, tableName);
3524
+ } else if (!result.timeout) {
3525
+ this.pendingWrites.delete(path);
3526
+ }
3527
+ return result;
3528
+ }
3529
+ /**
3530
+ * Schedule a background retry for a pending global write
3531
+ * @private
3532
+ */
3533
+ _scheduleGlobalRetry(path, tableName) {
3534
+ setTimeout(async () => {
3535
+ const pending = this.pendingWrites.get(path);
3536
+ if (!pending || !pending.isGlobal) return;
3537
+ if (pending.retries >= this.maxWriteRetries) {
3538
+ console.warn(`[gundb-backend] Max retries reached for global: ${path}`);
3539
+ this.pendingWrites.delete(path);
3540
+ return;
3541
+ }
3542
+ console.log(`[gundb-backend] Retrying global write (attempt ${pending.retries + 1}): ${path}`);
3543
+ await this._writeGlobalWithRetry(tableName, pending.data.data, pending.retries);
3544
+ }, this.writeRetryInterval);
3318
3545
  }
3319
3546
  async readGlobal(tableName, key) {
3320
3547
  return readGlobal$1(this.gun, this.appName, tableName, key);
3321
3548
  }
3322
3549
  async readAllGlobal(tableName, timeout = 5e3) {
3323
- return readAllGlobal(this.gun, this.appName, tableName, timeout);
3550
+ const gunItems = await readAllGlobal(this.gun, this.appName, tableName, timeout);
3551
+ const path = this.buildGlobalPath(tableName);
3552
+ return this._mergeWithWriteCache(path, gunItems);
3324
3553
  }
3325
3554
  async deleteGlobal(tableName, key) {
3326
- return deleteGlobal$1(this.gun, this.appName, tableName, key);
3555
+ const result = await deleteGlobal$1(this.gun, this.appName, tableName, key);
3556
+ const path = this.buildGlobalPath(tableName);
3557
+ if (!this.writeCache.has(path)) {
3558
+ this.writeCache.set(path, /* @__PURE__ */ new Map());
3559
+ }
3560
+ this.writeCache.get(path).set(key.toString(), {
3561
+ data: { id: key, _deleted: true },
3562
+ timestamp: Date.now()
3563
+ });
3564
+ return result;
3327
3565
  }
3328
3566
  async deleteAllGlobal(tableName) {
3329
3567
  return deleteAllGlobal$1(this.gun, this.appName, tableName);
@@ -3594,6 +3832,7 @@ class GunDBBackend extends StorageBackend {
3594
3832
  if (this.schemaValidator) {
3595
3833
  this.schemaValidator.clearCache();
3596
3834
  }
3835
+ this.writeCache.clear();
3597
3836
  if (this.auth) {
3598
3837
  this.auth.logout();
3599
3838
  }
@@ -3629,7 +3868,7 @@ class GunDBBackend extends StorageBackend {
3629
3868
  }
3630
3869
  }
3631
3870
  const name = "holosphere";
3632
- const version$1 = "2.0.0-alpha7";
3871
+ const version$1 = "2.0.0-alpha8";
3633
3872
  const description = "Holonic geospatial communication infrastructure combining H3 hexagonal indexing with distributed P2P storage";
3634
3873
  const type = "module";
3635
3874
  const bin = {
@@ -3648,6 +3887,8 @@ const exports = {
3648
3887
  const scripts = {
3649
3888
  dev: "vite",
3650
3889
  build: "vite build",
3890
+ "build:cdn": "vite build --config vite.config.cdn.js",
3891
+ "build:all": "npm run build && npm run build:cdn",
3651
3892
  test: "vitest run",
3652
3893
  "test:watch": "vitest",
3653
3894
  "test:coverage": "vitest run --coverage",
@@ -3735,7 +3976,7 @@ let HoloSphere$1 = class HoloSphere {
3735
3976
  * @param {string} config.backend - Storage backend: 'nostr' | 'gundb' | 'activitypub' (default: 'nostr')
3736
3977
  * @param {string[]} config.relays - Nostr relay URLs (default from HOLOSPHERE_RELAYS env or ['wss://relay.holons.io', 'wss://relay.nostr.band'])
3737
3978
  * @param {string} config.privateKey - Private key for signing (hex format, optional)
3738
- * @param {string} config.logLevel - Log verbosity: ERROR|WARN|INFO|DEBUG (default: 'WARN')
3979
+ * @param {string} config.logLevel - Log verbosity: ERROR|WARN|INFO|DEBUG (default: 'INFO')
3739
3980
  * @param {boolean} config.hybridMode - Enable hybrid mode (local + relay queries) (default: true)
3740
3981
  * @param {Object} config.gundb - GunDB-specific configuration
3741
3982
  * @param {Object} config.activitypub - ActivityPub-specific configuration
@@ -3764,7 +4005,7 @@ let HoloSphere$1 = class HoloSphere {
3764
4005
  backend: config.backend || "nostr",
3765
4006
  relays: config.relays || getDefaultRelays(),
3766
4007
  privateKey: config.privateKey || getEnv("HOLOSPHERE_PRIVATE_KEY"),
3767
- logLevel: config.logLevel || getEnv("HOLOSPHERE_LOG_LEVEL") || "WARN",
4008
+ logLevel: config.logLevel || getEnv("HOLOSPHERE_LOG_LEVEL") || "INFO",
3768
4009
  hybridMode: config.hybridMode !== false
3769
4010
  // Enable by default
3770
4011
  };
@@ -4396,20 +4637,23 @@ async function nostrGet(client, path, kind2 = 3e4, options = {}) {
4396
4637
  if (!options.skipPersistent && client.persistentGet) {
4397
4638
  const persistedEvent = await client.persistentGet(path);
4398
4639
  if (persistedEvent && persistedEvent.content) {
4399
- try {
4400
- const data = JSON.parse(persistedEvent.content);
4401
- if (data._deleted) {
4402
- return null;
4403
- }
4404
- if (options.includeAuthor) {
4405
- data._author = persistedEvent.pubkey;
4406
- }
4407
- if (client.refreshPathInBackground) {
4408
- client.refreshPathInBackground(path, kind2, { authors, timeout });
4640
+ if (!authors.includes(persistedEvent.pubkey)) ;
4641
+ else {
4642
+ try {
4643
+ const data = JSON.parse(persistedEvent.content);
4644
+ if (data._deleted) {
4645
+ return null;
4646
+ }
4647
+ if (options.includeAuthor) {
4648
+ data._author = persistedEvent.pubkey;
4649
+ }
4650
+ if (client.refreshPathInBackground) {
4651
+ client.refreshPathInBackground(path, kind2, { authors, timeout });
4652
+ }
4653
+ return data;
4654
+ } catch (error) {
4655
+ console.warn("[nostrGet] Failed to parse persisted event:", error);
4409
4656
  }
4410
- return data;
4411
- } catch (error) {
4412
- console.warn("[nostrGet] Failed to parse persisted event:", error);
4413
4657
  }
4414
4658
  }
4415
4659
  }
@@ -4443,10 +4687,11 @@ async function _executeNostrGet(client, path, kind2, authors, timeout, options)
4443
4687
  // Increase limit to get events from all authors
4444
4688
  };
4445
4689
  const events = await client.query(filter, { timeout });
4446
- if (events.length === 0) {
4690
+ const authoredEvents = events.filter((event2) => authors.includes(event2.pubkey));
4691
+ if (authoredEvents.length === 0) {
4447
4692
  return null;
4448
4693
  }
4449
- const event = events.sort((a, b2) => b2.created_at - a.created_at)[0];
4694
+ const event = authoredEvents.sort((a, b2) => b2.created_at - a.created_at)[0];
4450
4695
  try {
4451
4696
  const data = JSON.parse(event.content);
4452
4697
  if (data._deleted) {
@@ -4470,6 +4715,7 @@ async function nostrGetAll(client, pathPrefix, kind2 = 3e4, options = {}) {
4470
4715
  const byPath = /* @__PURE__ */ new Map();
4471
4716
  for (const event of persistedEvents) {
4472
4717
  if (!event || !event.tags) continue;
4718
+ if (!authors.includes(event.pubkey)) continue;
4473
4719
  const dTag = event.tags.find((t) => t[0] === "d");
4474
4720
  if (!dTag || !dTag[1] || !dTag[1].startsWith(pathPrefix)) continue;
4475
4721
  const path = dTag[1];
@@ -4524,7 +4770,9 @@ async function _executeNostrGetAll(client, pathPrefix, kind2, authors, timeout,
4524
4770
  const events = await client.query(filter, { timeout });
4525
4771
  const matching = events.filter((event) => {
4526
4772
  const dTag = event.tags.find((t) => t[0] === "d");
4527
- return dTag && dTag[1] && dTag[1].startsWith(pathPrefix);
4773
+ const pathMatches = dTag && dTag[1] && dTag[1].startsWith(pathPrefix);
4774
+ const authorAllowed = authors.includes(event.pubkey);
4775
+ return pathMatches && authorAllowed;
4528
4776
  });
4529
4777
  const byPath = /* @__PURE__ */ new Map();
4530
4778
  for (const event of matching) {
@@ -4560,7 +4808,9 @@ async function nostrGetAllHybrid(client, pathPrefix, kind2 = 3e4, options = {})
4560
4808
  const events = await queryMethod.call(client, filter, { timeout });
4561
4809
  const matching = events.filter((event) => {
4562
4810
  const dTag = event.tags.find((t) => t[0] === "d");
4563
- return dTag && dTag[1] && dTag[1].startsWith(pathPrefix);
4811
+ const pathMatches = dTag && dTag[1] && dTag[1].startsWith(pathPrefix);
4812
+ const authorAllowed = authors.includes(event.pubkey);
4813
+ return pathMatches && authorAllowed;
4564
4814
  });
4565
4815
  const byPath = /* @__PURE__ */ new Map();
4566
4816
  for (const event of matching) {
@@ -4986,30 +5236,45 @@ function buildPath(appName, holonId, lensName, key = null) {
4986
5236
  return buildPath$1(appName, holonId, lensName, key);
4987
5237
  }
4988
5238
  async function write(client, path, data) {
5239
+ if (client.write && client.gun) {
5240
+ return client.write(path, data);
5241
+ }
4989
5242
  if (client.gun) {
4990
5243
  return write$2(client.gun, path, data);
4991
5244
  }
4992
5245
  return write$1(client, path, data);
4993
5246
  }
4994
5247
  async function read(client, path, options = {}) {
5248
+ if (client.read && client.gun) {
5249
+ return client.read(path, options);
5250
+ }
4995
5251
  if (client.gun) {
4996
5252
  return read$2(client.gun, path);
4997
5253
  }
4998
5254
  return read$1(client, path, options);
4999
5255
  }
5000
5256
  async function readAll(client, path, options = {}) {
5257
+ if (client.readAll && client.gun) {
5258
+ return client.readAll(path, options);
5259
+ }
5001
5260
  if (client.gun) {
5002
5261
  return readAll$2(client.gun, path);
5003
5262
  }
5004
5263
  return readAll$1(client, path, options);
5005
5264
  }
5006
5265
  async function update(client, path, updates) {
5266
+ if (client.update && client.gun) {
5267
+ return client.update(path, updates);
5268
+ }
5007
5269
  if (client.gun) {
5008
5270
  return update$2(client.gun, path, updates);
5009
5271
  }
5010
5272
  return update$1(client, path, updates);
5011
5273
  }
5012
5274
  async function deleteData(client, path) {
5275
+ if (client.delete && client.gun) {
5276
+ return client.delete(path);
5277
+ }
5013
5278
  if (client.gun) {
5014
5279
  return deleteData$2(client.gun, path);
5015
5280
  }
@@ -5022,6 +5287,9 @@ async function deleteAll(client, path) {
5022
5287
  return deleteAll$1(client, path);
5023
5288
  }
5024
5289
  function subscribe(client, path, callback, options = {}) {
5290
+ if (client.subscribe && client.gun) {
5291
+ return client.subscribe(path, callback, options);
5292
+ }
5025
5293
  if (client.gun) {
5026
5294
  return subscribe$2(client.gun, path, callback, options);
5027
5295
  }
@@ -5526,7 +5794,7 @@ const sha256 = sha256$1;
5526
5794
  let secp256k1 = null;
5527
5795
  async function loadCrypto() {
5528
5796
  if (!secp256k1) {
5529
- const module2 = await import("./secp256k1-DYm_CMqW.js");
5797
+ const module2 = await import("./secp256k1-OM8siPyy.js");
5530
5798
  secp256k1 = module2.secp256k1;
5531
5799
  }
5532
5800
  return secp256k1;
@@ -6530,6 +6798,16 @@ async function encryptNIP04(privateKey, recipientPubKey, content) {
6530
6798
  async function decryptNIP04(privateKey, senderPubKey, encryptedContent) {
6531
6799
  return await nip04.decrypt(privateKey, senderPubKey, encryptedContent);
6532
6800
  }
6801
+ function encryptNIP44(privateKey, recipientPubKey, content) {
6802
+ const privKeyBytes = hexToBytes(privateKey);
6803
+ const conversationKey = nip44.v2.utils.getConversationKey(privKeyBytes, recipientPubKey);
6804
+ return nip44.v2.encrypt(content, conversationKey);
6805
+ }
6806
+ function decryptNIP44(privateKey, senderPubKey, encryptedContent) {
6807
+ const privKeyBytes = hexToBytes(privateKey);
6808
+ const conversationKey = nip44.v2.utils.getConversationKey(privKeyBytes, senderPubKey);
6809
+ return nip44.v2.decrypt(encryptedContent, conversationKey);
6810
+ }
6533
6811
  function createSignedEvent(kind2, content, tags, privateKey) {
6534
6812
  const event = {
6535
6813
  kind: kind2,
@@ -6583,7 +6861,9 @@ const nostrUtils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePr
6583
6861
  createDMEvent,
6584
6862
  createSignedEvent,
6585
6863
  decryptNIP04,
6864
+ decryptNIP44,
6586
6865
  encryptNIP04,
6866
+ encryptNIP44,
6587
6867
  generateNonce,
6588
6868
  getPublicKey,
6589
6869
  hexToBytes,
@@ -6645,7 +6925,7 @@ function createFederationResponse({
6645
6925
  async function sendFederationRequest$1(client, privateKey, recipientPubKey, request) {
6646
6926
  try {
6647
6927
  const content = JSON.stringify(request);
6648
- const encrypted = await encryptNIP04(privateKey, recipientPubKey, content);
6928
+ const encrypted = encryptNIP44(privateKey, recipientPubKey, content);
6649
6929
  const event = createDMEvent(recipientPubKey, encrypted, privateKey);
6650
6930
  if (client?.publish) {
6651
6931
  await client.publish(event);
@@ -6663,7 +6943,7 @@ async function sendFederationRequest$1(client, privateKey, recipientPubKey, requ
6663
6943
  async function sendFederationResponse(client, privateKey, recipientPubKey, response) {
6664
6944
  try {
6665
6945
  const content = JSON.stringify(response);
6666
- const encrypted = await encryptNIP04(privateKey, recipientPubKey, content);
6946
+ const encrypted = encryptNIP44(privateKey, recipientPubKey, content);
6667
6947
  const event = createDMEvent(recipientPubKey, encrypted, privateKey);
6668
6948
  if (client?.publish) {
6669
6949
  await client.publish(event);
@@ -6690,7 +6970,12 @@ function subscribeToFederationDMs(client, privateKey, publicKey, handlers) {
6690
6970
  const pTag = event.tags?.find((t) => t[0] === "p");
6691
6971
  if (!pTag || pTag[1] !== publicKey) return;
6692
6972
  try {
6693
- const decrypted = await decryptNIP04(privateKey, event.pubkey, event.content);
6973
+ let decrypted;
6974
+ try {
6975
+ decrypted = decryptNIP44(privateKey, event.pubkey, event.content);
6976
+ } catch (nip44Error) {
6977
+ decrypted = await decryptNIP04(privateKey, event.pubkey, event.content);
6978
+ }
6694
6979
  const payload = JSON.parse(decrypted);
6695
6980
  if (payload.type === "federation_request" && payload.version === "1.0") {
6696
6981
  console.log("[Handshake] Received federation request from:", event.pubkey.substring(0, 8) + "...");
@@ -7010,7 +7295,7 @@ async function createSubscription(client, path, callback, options = {}) {
7010
7295
  lastInvoke = now;
7011
7296
  callback(resolvedData, key);
7012
7297
  };
7013
- const subscription = await subscribe(client, path, wrappedCallback, {});
7298
+ const subscription = await subscribe(client, path, wrappedCallback, { realtimeOnly });
7014
7299
  return {
7015
7300
  path,
7016
7301
  unsubscribe: () => {
@@ -17160,7 +17445,7 @@ class ChainManager {
17160
17445
  */
17161
17446
  async _loadEthers() {
17162
17447
  if (!this.ethers) {
17163
- const ethersModule = await import("./index-jmTHEbR2.js");
17448
+ const ethersModule = await import("./index-BjP1TXGz.js");
17164
17449
  this.ethers = ethersModule;
17165
17450
  }
17166
17451
  return this.ethers;
@@ -25929,7 +26214,7 @@ class ContractDeployer {
25929
26214
  */
25930
26215
  async _loadEthers() {
25931
26216
  if (!this.ethers) {
25932
- this.ethers = await import("./index-jmTHEbR2.js");
26217
+ this.ethers = await import("./index-BjP1TXGz.js");
25933
26218
  }
25934
26219
  return this.ethers;
25935
26220
  }
@@ -26291,7 +26576,7 @@ class ContractOperations {
26291
26576
  */
26292
26577
  async _loadEthers() {
26293
26578
  if (!this.ethers) {
26294
- this.ethers = await import("./index-jmTHEbR2.js");
26579
+ this.ethers = await import("./index-BjP1TXGz.js");
26295
26580
  }
26296
26581
  return this.ethers;
26297
26582
  }
@@ -35542,8 +35827,7 @@ const ContractABIs = {
35542
35827
  Zoned: ZonedABI,
35543
35828
  Appreciative: AppreciativeABI,
35544
35829
  Bundle: BundleABI,
35545
- Holons: HolonsABI,
35546
- TestToken: TestTokenABI
35830
+ Holons: HolonsABI
35547
35831
  };
35548
35832
  function formatEthNumber(wei) {
35549
35833
  return Number(formatEther(wei));
@@ -37040,6 +37324,7 @@ class HoloSphereBase extends HoloSphere$1 {
37040
37324
  data.id = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
37041
37325
  }
37042
37326
  const path = buildPath(this.config.appName, holonId, lensName, data.id);
37327
+ this._log("DEBUG", "write", { holonId, lensName, dataId: data.id, path });
37043
37328
  const existingData = await read(this.client, path);
37044
37329
  if (existingData && existingData.hologram === true && existingData.target) {
37045
37330
  const hologramStructureFields = ["hologram", "soul", "target", "id", "_meta"];
@@ -37135,8 +37420,15 @@ class HoloSphereBase extends HoloSphere$1 {
37135
37420
  data.target.lensName,
37136
37421
  data.target.dataId
37137
37422
  );
37423
+ this._log("DEBUG", "resolving hologram", {
37424
+ hologramId: data.id,
37425
+ sourcePath,
37426
+ targetHolon: data.target.holonId,
37427
+ targetLens: data.target.lensName,
37428
+ targetDataId: data.target.dataId
37429
+ });
37138
37430
  if (visited.has(sourcePath)) {
37139
- console.warn(`Circular hologram reference detected: ${sourcePath}`);
37431
+ this._log("WARN", "Circular hologram reference detected", { sourcePath });
37140
37432
  return null;
37141
37433
  }
37142
37434
  visited.add(sourcePath);
@@ -37146,6 +37438,7 @@ class HoloSphereBase extends HoloSphere$1 {
37146
37438
  resolveOptions.includeAuthor = true;
37147
37439
  }
37148
37440
  const sourceData = await read(this.client, sourcePath, resolveOptions);
37441
+ this._log("DEBUG", "hologram source fetched", { found: !!sourceData, sourcePath });
37149
37442
  if (sourceData) {
37150
37443
  let resolvedSource = sourceData;
37151
37444
  if (sourceData.hologram === true && sourceData.target) {
@@ -37203,13 +37496,18 @@ class HoloSphereBase extends HoloSphere$1 {
37203
37496
  let result;
37204
37497
  if (dataId) {
37205
37498
  const path = buildPath(this.config.appName, holonId, lensName, dataId);
37499
+ this._log("DEBUG", "read", { holonId, lensName, dataId, path });
37206
37500
  result = await read(this.client, path);
37501
+ this._log("DEBUG", "read result", { found: !!result, isHologram: result?.hologram === true });
37207
37502
  } else {
37208
37503
  const path = buildPath(this.config.appName, holonId, lensName);
37504
+ this._log("DEBUG", "readAll", { holonId, lensName, path });
37209
37505
  result = await readAll(this.client, path);
37506
+ this._log("DEBUG", "readAll result", { count: Array.isArray(result) ? result.length : 0 });
37210
37507
  }
37211
37508
  const { resolveHolograms = true } = options;
37212
- if (resolveHolograms) {
37509
+ if (resolveHolograms && result) {
37510
+ this._log("DEBUG", "resolving holograms", { itemCount: Array.isArray(result) ? result.length : 1 });
37213
37511
  result = await this._resolveHolograms(result);
37214
37512
  }
37215
37513
  const endTime = Date.now();
@@ -37972,4 +38270,4 @@ export {
37972
38270
  exists as y,
37973
38271
  bytes as z
37974
38272
  };
37975
- //# sourceMappingURL=index-d6f4RJBM.js.map
38273
+ //# sourceMappingURL=index-4XHHKe6S.js.map