holosphere 2.0.0-alpha15 → 2.0.0-alpha16

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 (39) hide show
  1. package/dist/{2019-ATLjawsU.cjs → 2019-C9IgM3mu.cjs} +2 -2
  2. package/dist/{2019-ATLjawsU.cjs.map → 2019-C9IgM3mu.cjs.map} +1 -1
  3. package/dist/{2019-BfjzDRje.js → 2019-CyFQLuqg.js} +2 -2
  4. package/dist/{2019-BfjzDRje.js.map → 2019-CyFQLuqg.js.map} +1 -1
  5. package/dist/{browser-CKTczilW.js → browser-BIvmkBi9.js} +2 -2
  6. package/dist/{browser-CKTczilW.js.map → browser-BIvmkBi9.js.map} +1 -1
  7. package/dist/{browser-GOg6KKOV.cjs → browser-CZ1l8nhY.cjs} +2 -2
  8. package/dist/{browser-GOg6KKOV.cjs.map → browser-CZ1l8nhY.cjs.map} +1 -1
  9. package/dist/cjs/holosphere.cjs +1 -1
  10. package/dist/esm/holosphere.js +1 -1
  11. package/dist/{index-C3Cag0SV.js → index-B7ERi4ng.js} +137 -59
  12. package/dist/index-B7ERi4ng.js.map +1 -0
  13. package/dist/{index-D_QecZNu.cjs → index-Cc-_uNpt.cjs} +2 -2
  14. package/dist/{index-D_QecZNu.cjs.map → index-Cc-_uNpt.cjs.map} +1 -1
  15. package/dist/{index-ChpSfdYS.cjs → index-DXFsE8r6.cjs} +7 -7
  16. package/dist/index-DXFsE8r6.cjs.map +1 -0
  17. package/dist/{index-nMC3dWZ5.js → index-DusXavIT.js} +2 -2
  18. package/dist/{index-nMC3dWZ5.js.map → index-DusXavIT.js.map} +1 -1
  19. package/dist/{index-BdnrGafX.js → index-HW9kbrzC.js} +2 -2
  20. package/dist/{index-BdnrGafX.js.map → index-HW9kbrzC.js.map} +1 -1
  21. package/dist/{index-z5HWfWMu.cjs → index-VrRkpshF.cjs} +2 -2
  22. package/dist/{index-z5HWfWMu.cjs.map → index-VrRkpshF.cjs.map} +1 -1
  23. package/dist/{indexeddb-storage-DWSeL-YF.cjs → indexeddb-storage-CVz4Q3lo.cjs} +2 -2
  24. package/dist/{indexeddb-storage-DWSeL-YF.cjs.map → indexeddb-storage-CVz4Q3lo.cjs.map} +1 -1
  25. package/dist/{indexeddb-storage-dx01N0ET.js → indexeddb-storage-yEswGGMG.js} +2 -2
  26. package/dist/{indexeddb-storage-dx01N0ET.js.map → indexeddb-storage-yEswGGMG.js.map} +1 -1
  27. package/dist/{memory-storage-BPIfkpcf.js → memory-storage-CSB5_IfU.js} +2 -2
  28. package/dist/{memory-storage-BPIfkpcf.js.map → memory-storage-CSB5_IfU.js.map} +1 -1
  29. package/dist/{memory-storage-CKUGDq2d.cjs → memory-storage-Q8mEQ6Ix.cjs} +2 -2
  30. package/dist/{memory-storage-CKUGDq2d.cjs.map → memory-storage-Q8mEQ6Ix.cjs.map} +1 -1
  31. package/package.json +1 -1
  32. package/src/crypto/secp256k1.js +3 -3
  33. package/src/federation/hologram.js +2 -2
  34. package/src/index.js +83 -12
  35. package/src/storage/nostr-async.js +47 -31
  36. package/src/storage/nostr-wrapper.js +18 -4
  37. package/src/storage/unified-storage.js +10 -4
  38. package/dist/index-C3Cag0SV.js.map +0 -1
  39. package/dist/index-ChpSfdYS.cjs.map +0 -1
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-ChpSfdYS.cjs");class t extends e.PersistentStorage{constructor(){super(),this.db=null,this.dbName="",this.storeName="events"}async init(e){this.dbName=`holosphere_${e}`;try{await this._openDatabase()}catch(t){if("VersionError"!==t.name)throw t;console.warn(`[IndexedDBStorage] Version conflict detected for ${this.dbName}, recreating database...`),await this._deleteDatabase(),await this._openDatabase()}}async _openDatabase(){return new Promise((e,t)=>{const r=indexedDB.open(this.dbName,1);r.onerror=()=>t(r.error),r.onsuccess=()=>{this.db=r.result,e(void 0)},r.onupgradeneeded=e=>{const t=e.target.result;if(!t.objectStoreNames.contains(this.storeName)){t.createObjectStore(this.storeName,{keyPath:"key"}).createIndex("key","key",{unique:!1})}}})}async _deleteDatabase(){return new Promise((e,t)=>{const r=indexedDB.deleteDatabase(this.dbName);r.onsuccess=()=>e(void 0),r.onerror=()=>t(r.error),r.onblocked=()=>{console.warn(`[IndexedDBStorage] Database deletion blocked for ${this.dbName}`),e(void 0)}})}async put(e,t){return new Promise((r,s)=>{if(!this.db)return void s(new Error("Database not initialized"));const o=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).put({key:e,event:t,timestamp:Date.now()});o.onsuccess=()=>r(void 0),o.onerror=()=>s(o.error)})}async get(e){return new Promise((t,r)=>{if(!this.db)return void r(new Error("Database not initialized"));const s=this.db.transaction([this.storeName],"readonly").objectStore(this.storeName).get(e);s.onsuccess=()=>{const e=s.result;t(e?e.event:null)},s.onerror=()=>r(s.error)})}async getAll(e){return new Promise((t,r)=>{if(!this.db)return void r(new Error("Database not initialized"));const s=this.db.transaction([this.storeName],"readonly").objectStore(this.storeName),o=[];let n;if(e){const t=IDBKeyRange.bound(e,e+"￿",!1,!1);n=s.openCursor(t)}else n=s.openCursor();n.onsuccess=e=>{const r=e.target.result;r?(o.push(r.value.event),r.continue()):t(o)},n.onerror=()=>r(n.error)})}async delete(e){return new Promise((t,r)=>{if(!this.db)return void r(new Error("Database not initialized"));const s=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).delete(e);s.onsuccess=()=>t(void 0),s.onerror=()=>r(s.error)})}async clear(){return new Promise((e,t)=>{if(!this.db)return void t(new Error("Database not initialized"));const r=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).clear();r.onsuccess=()=>e(void 0),r.onerror=()=>t(r.error)})}async close(){this.db&&(this.db.close(),this.db=null)}}exports.IndexedDBStorage=t;
2
- //# sourceMappingURL=indexeddb-storage-DWSeL-YF.cjs.map
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-DXFsE8r6.cjs");class t extends e.PersistentStorage{constructor(){super(),this.db=null,this.dbName="",this.storeName="events"}async init(e){this.dbName=`holosphere_${e}`;try{await this._openDatabase()}catch(t){if("VersionError"!==t.name)throw t;console.warn(`[IndexedDBStorage] Version conflict detected for ${this.dbName}, recreating database...`),await this._deleteDatabase(),await this._openDatabase()}}async _openDatabase(){return new Promise((e,t)=>{const r=indexedDB.open(this.dbName,1);r.onerror=()=>t(r.error),r.onsuccess=()=>{this.db=r.result,e(void 0)},r.onupgradeneeded=e=>{const t=e.target.result;if(!t.objectStoreNames.contains(this.storeName)){t.createObjectStore(this.storeName,{keyPath:"key"}).createIndex("key","key",{unique:!1})}}})}async _deleteDatabase(){return new Promise((e,t)=>{const r=indexedDB.deleteDatabase(this.dbName);r.onsuccess=()=>e(void 0),r.onerror=()=>t(r.error),r.onblocked=()=>{console.warn(`[IndexedDBStorage] Database deletion blocked for ${this.dbName}`),e(void 0)}})}async put(e,t){return new Promise((r,s)=>{if(!this.db)return void s(new Error("Database not initialized"));const o=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).put({key:e,event:t,timestamp:Date.now()});o.onsuccess=()=>r(void 0),o.onerror=()=>s(o.error)})}async get(e){return new Promise((t,r)=>{if(!this.db)return void r(new Error("Database not initialized"));const s=this.db.transaction([this.storeName],"readonly").objectStore(this.storeName).get(e);s.onsuccess=()=>{const e=s.result;t(e?e.event:null)},s.onerror=()=>r(s.error)})}async getAll(e){return new Promise((t,r)=>{if(!this.db)return void r(new Error("Database not initialized"));const s=this.db.transaction([this.storeName],"readonly").objectStore(this.storeName),o=[];let n;if(e){const t=IDBKeyRange.bound(e,e+"￿",!1,!1);n=s.openCursor(t)}else n=s.openCursor();n.onsuccess=e=>{const r=e.target.result;r?(o.push(r.value.event),r.continue()):t(o)},n.onerror=()=>r(n.error)})}async delete(e){return new Promise((t,r)=>{if(!this.db)return void r(new Error("Database not initialized"));const s=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).delete(e);s.onsuccess=()=>t(void 0),s.onerror=()=>r(s.error)})}async clear(){return new Promise((e,t)=>{if(!this.db)return void t(new Error("Database not initialized"));const r=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).clear();r.onsuccess=()=>e(void 0),r.onerror=()=>t(r.error)})}async close(){this.db&&(this.db.close(),this.db=null)}}exports.IndexedDBStorage=t;
2
+ //# sourceMappingURL=indexeddb-storage-CVz4Q3lo.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexeddb-storage-DWSeL-YF.cjs","sources":["../src/storage/indexeddb-storage.js"],"sourcesContent":["/**\n * @fileoverview IndexedDB storage adapter for browsers.\n *\n * Provides persistent storage with good performance using browser IndexedDB.\n * Uses B-tree indexes for efficient prefix queries.\n *\n * @module storage/indexeddb-storage\n */\n\nimport { PersistentStorage } from './persistent-storage.js';\n\n/**\n * IndexedDB storage adapter for browsers.\n *\n * Provides high-performance persistent storage using IndexedDB with efficient prefix queries.\n *\n * @class IndexedDBStorage\n * @extends PersistentStorage\n * @example\n * const storage = new IndexedDBStorage();\n * await storage.init('myapp');\n * await storage.put('key1', { id: 'event1', content: 'test' });\n */\nexport class IndexedDBStorage extends PersistentStorage {\n /**\n * Create a new IndexedDBStorage instance.\n */\n constructor() {\n super();\n /** @type {IDBDatabase|null} */\n this.db = null;\n /** @type {string} */\n this.dbName = '';\n /** @type {string} */\n this.storeName = 'events';\n }\n\n /**\n * Initialize storage with namespace.\n *\n * Creates or opens the IndexedDB database and object store.\n * Handles version conflicts by deleting and recreating the database if needed.\n *\n * @param {string} namespace - Storage namespace\n * @returns {Promise<void>}\n */\n async init(namespace) {\n this.dbName = `holosphere_${namespace}`;\n\n try {\n await this._openDatabase();\n } catch (error) {\n // Handle version conflict by deleting and recreating the database\n if (error.name === 'VersionError') {\n console.warn(`[IndexedDBStorage] Version conflict detected for ${this.dbName}, recreating database...`);\n await this._deleteDatabase();\n await this._openDatabase();\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Open the IndexedDB database.\n *\n * @returns {Promise<void>}\n * @private\n */\n async _openDatabase() {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, 1);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n this.db = request.result;\n resolve(undefined);\n };\n\n request.onupgradeneeded = (event) => {\n const target = /** @type {IDBOpenDBRequest} */ (event.target);\n const db = target.result;\n\n // Create object store if it doesn't exist\n if (!db.objectStoreNames.contains(this.storeName)) {\n const objectStore = db.createObjectStore(this.storeName, { keyPath: 'key' });\n // Create index for prefix queries\n objectStore.createIndex('key', 'key', { unique: false });\n }\n };\n });\n }\n\n /**\n * Delete the IndexedDB database.\n *\n * @returns {Promise<void>}\n * @private\n */\n async _deleteDatabase() {\n return new Promise((resolve, reject) => {\n const request = indexedDB.deleteDatabase(this.dbName);\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n request.onblocked = () => {\n console.warn(`[IndexedDBStorage] Database deletion blocked for ${this.dbName}`);\n // Still resolve - the next open attempt will handle it\n resolve(undefined);\n };\n });\n }\n\n /**\n * Store an event.\n *\n * @param {string} key - Storage key\n * @param {Object} event - Event data\n * @returns {Promise<void>}\n */\n async put(key, event) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.put({ key, event, timestamp: Date.now() });\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Retrieve an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<Object|null>} Event data or null\n */\n async get(key) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readonly');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.get(key);\n\n request.onsuccess = () => {\n const result = request.result;\n resolve(result ? result.event : null);\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Retrieve all events matching a prefix.\n *\n * Uses IDBKeyRange for efficient B-tree index queries.\n *\n * @param {string} prefix - Key prefix to match\n * @returns {Promise<any[]>} Array of matching events\n */\n async getAll(prefix) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readonly');\n const objectStore = transaction.objectStore(this.storeName);\n\n /** @type {any[]} */\n const results = [];\n\n // Use IDBKeyRange for efficient prefix query instead of full table scan\n // This creates a range from \"prefix\" to \"prefix\\uffff\" (highest unicode char)\n // which efficiently uses the B-tree index\n let request;\n if (prefix) {\n const range = IDBKeyRange.bound(prefix, prefix + '\\uffff', false, false);\n request = objectStore.openCursor(range);\n } else {\n // No prefix = get all\n request = objectStore.openCursor();\n }\n\n request.onsuccess = (event) => {\n const target = /** @type {IDBRequest} */ (event.target);\n const cursor = target.result;\n if (cursor) {\n results.push(cursor.value.event);\n cursor.continue();\n } else {\n resolve(results);\n }\n };\n\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Delete an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<void>}\n */\n async delete(key) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.delete(key);\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Clear all stored events.\n *\n * @returns {Promise<void>}\n */\n async clear() {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.clear();\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Close the database connection.\n *\n * @returns {Promise<void>}\n */\n async close() {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n }\n}\n"],"names":["IndexedDBStorage","PersistentStorage","constructor","super","this","db","dbName","storeName","init","namespace","_openDatabase","error","name","console","warn","_deleteDatabase","Promise","resolve","reject","request","indexedDB","open","onerror","onsuccess","result","onupgradeneeded","event","objectStoreNames","contains","createObjectStore","keyPath","createIndex","unique","deleteDatabase","onblocked","put","key","Error","transaction","objectStore","timestamp","Date","now","get","getAll","prefix","results","range","IDBKeyRange","bound","openCursor","cursor","push","value","continue","delete","clear","close"],"mappings":"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"}
1
+ {"version":3,"file":"indexeddb-storage-CVz4Q3lo.cjs","sources":["../src/storage/indexeddb-storage.js"],"sourcesContent":["/**\n * @fileoverview IndexedDB storage adapter for browsers.\n *\n * Provides persistent storage with good performance using browser IndexedDB.\n * Uses B-tree indexes for efficient prefix queries.\n *\n * @module storage/indexeddb-storage\n */\n\nimport { PersistentStorage } from './persistent-storage.js';\n\n/**\n * IndexedDB storage adapter for browsers.\n *\n * Provides high-performance persistent storage using IndexedDB with efficient prefix queries.\n *\n * @class IndexedDBStorage\n * @extends PersistentStorage\n * @example\n * const storage = new IndexedDBStorage();\n * await storage.init('myapp');\n * await storage.put('key1', { id: 'event1', content: 'test' });\n */\nexport class IndexedDBStorage extends PersistentStorage {\n /**\n * Create a new IndexedDBStorage instance.\n */\n constructor() {\n super();\n /** @type {IDBDatabase|null} */\n this.db = null;\n /** @type {string} */\n this.dbName = '';\n /** @type {string} */\n this.storeName = 'events';\n }\n\n /**\n * Initialize storage with namespace.\n *\n * Creates or opens the IndexedDB database and object store.\n * Handles version conflicts by deleting and recreating the database if needed.\n *\n * @param {string} namespace - Storage namespace\n * @returns {Promise<void>}\n */\n async init(namespace) {\n this.dbName = `holosphere_${namespace}`;\n\n try {\n await this._openDatabase();\n } catch (error) {\n // Handle version conflict by deleting and recreating the database\n if (error.name === 'VersionError') {\n console.warn(`[IndexedDBStorage] Version conflict detected for ${this.dbName}, recreating database...`);\n await this._deleteDatabase();\n await this._openDatabase();\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Open the IndexedDB database.\n *\n * @returns {Promise<void>}\n * @private\n */\n async _openDatabase() {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, 1);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n this.db = request.result;\n resolve(undefined);\n };\n\n request.onupgradeneeded = (event) => {\n const target = /** @type {IDBOpenDBRequest} */ (event.target);\n const db = target.result;\n\n // Create object store if it doesn't exist\n if (!db.objectStoreNames.contains(this.storeName)) {\n const objectStore = db.createObjectStore(this.storeName, { keyPath: 'key' });\n // Create index for prefix queries\n objectStore.createIndex('key', 'key', { unique: false });\n }\n };\n });\n }\n\n /**\n * Delete the IndexedDB database.\n *\n * @returns {Promise<void>}\n * @private\n */\n async _deleteDatabase() {\n return new Promise((resolve, reject) => {\n const request = indexedDB.deleteDatabase(this.dbName);\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n request.onblocked = () => {\n console.warn(`[IndexedDBStorage] Database deletion blocked for ${this.dbName}`);\n // Still resolve - the next open attempt will handle it\n resolve(undefined);\n };\n });\n }\n\n /**\n * Store an event.\n *\n * @param {string} key - Storage key\n * @param {Object} event - Event data\n * @returns {Promise<void>}\n */\n async put(key, event) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.put({ key, event, timestamp: Date.now() });\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Retrieve an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<Object|null>} Event data or null\n */\n async get(key) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readonly');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.get(key);\n\n request.onsuccess = () => {\n const result = request.result;\n resolve(result ? result.event : null);\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Retrieve all events matching a prefix.\n *\n * Uses IDBKeyRange for efficient B-tree index queries.\n *\n * @param {string} prefix - Key prefix to match\n * @returns {Promise<any[]>} Array of matching events\n */\n async getAll(prefix) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readonly');\n const objectStore = transaction.objectStore(this.storeName);\n\n /** @type {any[]} */\n const results = [];\n\n // Use IDBKeyRange for efficient prefix query instead of full table scan\n // This creates a range from \"prefix\" to \"prefix\\uffff\" (highest unicode char)\n // which efficiently uses the B-tree index\n let request;\n if (prefix) {\n const range = IDBKeyRange.bound(prefix, prefix + '\\uffff', false, false);\n request = objectStore.openCursor(range);\n } else {\n // No prefix = get all\n request = objectStore.openCursor();\n }\n\n request.onsuccess = (event) => {\n const target = /** @type {IDBRequest} */ (event.target);\n const cursor = target.result;\n if (cursor) {\n results.push(cursor.value.event);\n cursor.continue();\n } else {\n resolve(results);\n }\n };\n\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Delete an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<void>}\n */\n async delete(key) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.delete(key);\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Clear all stored events.\n *\n * @returns {Promise<void>}\n */\n async clear() {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.clear();\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Close the database connection.\n *\n * @returns {Promise<void>}\n */\n async close() {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n }\n}\n"],"names":["IndexedDBStorage","PersistentStorage","constructor","super","this","db","dbName","storeName","init","namespace","_openDatabase","error","name","console","warn","_deleteDatabase","Promise","resolve","reject","request","indexedDB","open","onerror","onsuccess","result","onupgradeneeded","event","objectStoreNames","contains","createObjectStore","keyPath","createIndex","unique","deleteDatabase","onblocked","put","key","Error","transaction","objectStore","timestamp","Date","now","get","getAll","prefix","results","range","IDBKeyRange","bound","openCursor","cursor","push","value","continue","delete","clear","close"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { aV as PersistentStorage } from "./index-C3Cag0SV.js";
1
+ import { aV as PersistentStorage } from "./index-B7ERi4ng.js";
2
2
  class IndexedDBStorage extends PersistentStorage {
3
3
  /**
4
4
  * Create a new IndexedDBStorage instance.
@@ -210,4 +210,4 @@ class IndexedDBStorage extends PersistentStorage {
210
210
  export {
211
211
  IndexedDBStorage
212
212
  };
213
- //# sourceMappingURL=indexeddb-storage-dx01N0ET.js.map
213
+ //# sourceMappingURL=indexeddb-storage-yEswGGMG.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexeddb-storage-dx01N0ET.js","sources":["../src/storage/indexeddb-storage.js"],"sourcesContent":["/**\n * @fileoverview IndexedDB storage adapter for browsers.\n *\n * Provides persistent storage with good performance using browser IndexedDB.\n * Uses B-tree indexes for efficient prefix queries.\n *\n * @module storage/indexeddb-storage\n */\n\nimport { PersistentStorage } from './persistent-storage.js';\n\n/**\n * IndexedDB storage adapter for browsers.\n *\n * Provides high-performance persistent storage using IndexedDB with efficient prefix queries.\n *\n * @class IndexedDBStorage\n * @extends PersistentStorage\n * @example\n * const storage = new IndexedDBStorage();\n * await storage.init('myapp');\n * await storage.put('key1', { id: 'event1', content: 'test' });\n */\nexport class IndexedDBStorage extends PersistentStorage {\n /**\n * Create a new IndexedDBStorage instance.\n */\n constructor() {\n super();\n /** @type {IDBDatabase|null} */\n this.db = null;\n /** @type {string} */\n this.dbName = '';\n /** @type {string} */\n this.storeName = 'events';\n }\n\n /**\n * Initialize storage with namespace.\n *\n * Creates or opens the IndexedDB database and object store.\n * Handles version conflicts by deleting and recreating the database if needed.\n *\n * @param {string} namespace - Storage namespace\n * @returns {Promise<void>}\n */\n async init(namespace) {\n this.dbName = `holosphere_${namespace}`;\n\n try {\n await this._openDatabase();\n } catch (error) {\n // Handle version conflict by deleting and recreating the database\n if (error.name === 'VersionError') {\n console.warn(`[IndexedDBStorage] Version conflict detected for ${this.dbName}, recreating database...`);\n await this._deleteDatabase();\n await this._openDatabase();\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Open the IndexedDB database.\n *\n * @returns {Promise<void>}\n * @private\n */\n async _openDatabase() {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, 1);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n this.db = request.result;\n resolve(undefined);\n };\n\n request.onupgradeneeded = (event) => {\n const target = /** @type {IDBOpenDBRequest} */ (event.target);\n const db = target.result;\n\n // Create object store if it doesn't exist\n if (!db.objectStoreNames.contains(this.storeName)) {\n const objectStore = db.createObjectStore(this.storeName, { keyPath: 'key' });\n // Create index for prefix queries\n objectStore.createIndex('key', 'key', { unique: false });\n }\n };\n });\n }\n\n /**\n * Delete the IndexedDB database.\n *\n * @returns {Promise<void>}\n * @private\n */\n async _deleteDatabase() {\n return new Promise((resolve, reject) => {\n const request = indexedDB.deleteDatabase(this.dbName);\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n request.onblocked = () => {\n console.warn(`[IndexedDBStorage] Database deletion blocked for ${this.dbName}`);\n // Still resolve - the next open attempt will handle it\n resolve(undefined);\n };\n });\n }\n\n /**\n * Store an event.\n *\n * @param {string} key - Storage key\n * @param {Object} event - Event data\n * @returns {Promise<void>}\n */\n async put(key, event) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.put({ key, event, timestamp: Date.now() });\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Retrieve an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<Object|null>} Event data or null\n */\n async get(key) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readonly');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.get(key);\n\n request.onsuccess = () => {\n const result = request.result;\n resolve(result ? result.event : null);\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Retrieve all events matching a prefix.\n *\n * Uses IDBKeyRange for efficient B-tree index queries.\n *\n * @param {string} prefix - Key prefix to match\n * @returns {Promise<any[]>} Array of matching events\n */\n async getAll(prefix) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readonly');\n const objectStore = transaction.objectStore(this.storeName);\n\n /** @type {any[]} */\n const results = [];\n\n // Use IDBKeyRange for efficient prefix query instead of full table scan\n // This creates a range from \"prefix\" to \"prefix\\uffff\" (highest unicode char)\n // which efficiently uses the B-tree index\n let request;\n if (prefix) {\n const range = IDBKeyRange.bound(prefix, prefix + '\\uffff', false, false);\n request = objectStore.openCursor(range);\n } else {\n // No prefix = get all\n request = objectStore.openCursor();\n }\n\n request.onsuccess = (event) => {\n const target = /** @type {IDBRequest} */ (event.target);\n const cursor = target.result;\n if (cursor) {\n results.push(cursor.value.event);\n cursor.continue();\n } else {\n resolve(results);\n }\n };\n\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Delete an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<void>}\n */\n async delete(key) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.delete(key);\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Clear all stored events.\n *\n * @returns {Promise<void>}\n */\n async clear() {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.clear();\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Close the database connection.\n *\n * @returns {Promise<void>}\n */\n async close() {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"indexeddb-storage-yEswGGMG.js","sources":["../src/storage/indexeddb-storage.js"],"sourcesContent":["/**\n * @fileoverview IndexedDB storage adapter for browsers.\n *\n * Provides persistent storage with good performance using browser IndexedDB.\n * Uses B-tree indexes for efficient prefix queries.\n *\n * @module storage/indexeddb-storage\n */\n\nimport { PersistentStorage } from './persistent-storage.js';\n\n/**\n * IndexedDB storage adapter for browsers.\n *\n * Provides high-performance persistent storage using IndexedDB with efficient prefix queries.\n *\n * @class IndexedDBStorage\n * @extends PersistentStorage\n * @example\n * const storage = new IndexedDBStorage();\n * await storage.init('myapp');\n * await storage.put('key1', { id: 'event1', content: 'test' });\n */\nexport class IndexedDBStorage extends PersistentStorage {\n /**\n * Create a new IndexedDBStorage instance.\n */\n constructor() {\n super();\n /** @type {IDBDatabase|null} */\n this.db = null;\n /** @type {string} */\n this.dbName = '';\n /** @type {string} */\n this.storeName = 'events';\n }\n\n /**\n * Initialize storage with namespace.\n *\n * Creates or opens the IndexedDB database and object store.\n * Handles version conflicts by deleting and recreating the database if needed.\n *\n * @param {string} namespace - Storage namespace\n * @returns {Promise<void>}\n */\n async init(namespace) {\n this.dbName = `holosphere_${namespace}`;\n\n try {\n await this._openDatabase();\n } catch (error) {\n // Handle version conflict by deleting and recreating the database\n if (error.name === 'VersionError') {\n console.warn(`[IndexedDBStorage] Version conflict detected for ${this.dbName}, recreating database...`);\n await this._deleteDatabase();\n await this._openDatabase();\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Open the IndexedDB database.\n *\n * @returns {Promise<void>}\n * @private\n */\n async _openDatabase() {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, 1);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n this.db = request.result;\n resolve(undefined);\n };\n\n request.onupgradeneeded = (event) => {\n const target = /** @type {IDBOpenDBRequest} */ (event.target);\n const db = target.result;\n\n // Create object store if it doesn't exist\n if (!db.objectStoreNames.contains(this.storeName)) {\n const objectStore = db.createObjectStore(this.storeName, { keyPath: 'key' });\n // Create index for prefix queries\n objectStore.createIndex('key', 'key', { unique: false });\n }\n };\n });\n }\n\n /**\n * Delete the IndexedDB database.\n *\n * @returns {Promise<void>}\n * @private\n */\n async _deleteDatabase() {\n return new Promise((resolve, reject) => {\n const request = indexedDB.deleteDatabase(this.dbName);\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n request.onblocked = () => {\n console.warn(`[IndexedDBStorage] Database deletion blocked for ${this.dbName}`);\n // Still resolve - the next open attempt will handle it\n resolve(undefined);\n };\n });\n }\n\n /**\n * Store an event.\n *\n * @param {string} key - Storage key\n * @param {Object} event - Event data\n * @returns {Promise<void>}\n */\n async put(key, event) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.put({ key, event, timestamp: Date.now() });\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Retrieve an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<Object|null>} Event data or null\n */\n async get(key) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readonly');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.get(key);\n\n request.onsuccess = () => {\n const result = request.result;\n resolve(result ? result.event : null);\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Retrieve all events matching a prefix.\n *\n * Uses IDBKeyRange for efficient B-tree index queries.\n *\n * @param {string} prefix - Key prefix to match\n * @returns {Promise<any[]>} Array of matching events\n */\n async getAll(prefix) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readonly');\n const objectStore = transaction.objectStore(this.storeName);\n\n /** @type {any[]} */\n const results = [];\n\n // Use IDBKeyRange for efficient prefix query instead of full table scan\n // This creates a range from \"prefix\" to \"prefix\\uffff\" (highest unicode char)\n // which efficiently uses the B-tree index\n let request;\n if (prefix) {\n const range = IDBKeyRange.bound(prefix, prefix + '\\uffff', false, false);\n request = objectStore.openCursor(range);\n } else {\n // No prefix = get all\n request = objectStore.openCursor();\n }\n\n request.onsuccess = (event) => {\n const target = /** @type {IDBRequest} */ (event.target);\n const cursor = target.result;\n if (cursor) {\n results.push(cursor.value.event);\n cursor.continue();\n } else {\n resolve(results);\n }\n };\n\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Delete an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<void>}\n */\n async delete(key) {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.delete(key);\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Clear all stored events.\n *\n * @returns {Promise<void>}\n */\n async clear() {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n const transaction = this.db.transaction([this.storeName], 'readwrite');\n const objectStore = transaction.objectStore(this.storeName);\n\n const request = objectStore.clear();\n\n request.onsuccess = () => resolve(undefined);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Close the database connection.\n *\n * @returns {Promise<void>}\n */\n async close() {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { aV as PersistentStorage } from "./index-C3Cag0SV.js";
1
+ import { aV as PersistentStorage } from "./index-B7ERi4ng.js";
2
2
  class MemoryStorage extends PersistentStorage {
3
3
  /**
4
4
  * Create a new MemoryStorage instance.
@@ -88,4 +88,4 @@ MemoryStorage._globalStore = null;
88
88
  export {
89
89
  MemoryStorage
90
90
  };
91
- //# sourceMappingURL=memory-storage-BPIfkpcf.js.map
91
+ //# sourceMappingURL=memory-storage-CSB5_IfU.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-storage-BPIfkpcf.js","sources":["../src/storage/memory-storage.js"],"sourcesContent":["/**\n * @fileoverview Memory-only storage adapter (fallback, no actual persistence).\n *\n * Provides in-memory storage for testing or when persistent storage is unavailable.\n * Data is shared across instances with the same namespace but lost on process restart.\n *\n * @module storage/memory-storage\n */\n\nimport { PersistentStorage } from './persistent-storage.js';\n\n/**\n * Memory-only storage adapter.\n *\n * Stores data in memory with no persistence across restarts.\n * Uses a global store to share data across instances with same namespace.\n *\n * @class MemoryStorage\n * @extends PersistentStorage\n * @example\n * const storage = new MemoryStorage();\n * await storage.init('myapp');\n * await storage.put('key1', { id: 'event1' });\n */\nexport class MemoryStorage extends PersistentStorage {\n /**\n * Create a new MemoryStorage instance.\n */\n constructor() {\n super();\n this.data = new Map();\n this.namespace = null;\n }\n\n /**\n * Initialize storage with namespace.\n *\n * @param {string} namespace - Storage namespace\n * @returns {Promise<void>}\n */\n async init(namespace) {\n this.namespace = namespace;\n // Check if there's existing data for this namespace\n if (!MemoryStorage._globalStore) {\n MemoryStorage._globalStore = new Map();\n }\n if (!MemoryStorage._globalStore.has(namespace)) {\n MemoryStorage._globalStore.set(namespace, new Map());\n }\n this.data = MemoryStorage._globalStore.get(namespace);\n }\n\n /**\n * Store an event.\n *\n * @param {string} key - Storage key\n * @param {Object} event - Event data\n * @returns {Promise<void>}\n */\n async put(key, event) {\n this.data.set(key, JSON.parse(JSON.stringify(event))); // Deep clone\n }\n\n /**\n * Retrieve an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<Object|null>} Event data or null\n */\n async get(key) {\n const data = this.data.get(key);\n return data ? JSON.parse(JSON.stringify(data)) : null; // Deep clone\n }\n\n /**\n * Retrieve all events matching a prefix.\n *\n * @param {string} prefix - Key prefix to match\n * @returns {Promise<any[]>} Array of matching events\n */\n async getAll(prefix) {\n /** @type {any[]} */\n const results = [];\n for (const [key, value] of this.data.entries()) {\n if (key.startsWith(prefix)) {\n results.push(JSON.parse(JSON.stringify(value)));\n }\n }\n return results;\n }\n\n /**\n * Delete an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<void>}\n */\n async delete(key) {\n this.data.delete(key);\n }\n\n /**\n * Clear all stored events.\n *\n * @returns {Promise<void>}\n */\n async clear() {\n this.data.clear();\n }\n\n /**\n * Close storage (no-op for memory storage).\n *\n * @returns {Promise<void>}\n */\n async close() {\n // Nothing to close for memory storage\n }\n}\n\n/**\n * Global store shared across instances with same namespace.\n * @private\n */\nMemoryStorage._globalStore = null;\n"],"names":[],"mappings":";AAwBO,MAAM,sBAAsB,kBAAkB;AAAA;AAAA;AAAA;AAAA,EAInD,cAAc;AACZ,UAAK;AACL,SAAK,OAAO,oBAAI,IAAG;AACnB,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,KAAK,WAAW;AACpB,SAAK,YAAY;AAEjB,QAAI,CAAC,cAAc,cAAc;AAC/B,oBAAc,eAAe,oBAAI,IAAG;AAAA,IACtC;AACA,QAAI,CAAC,cAAc,aAAa,IAAI,SAAS,GAAG;AAC9C,oBAAc,aAAa,IAAI,WAAW,oBAAI,IAAG,CAAE;AAAA,IACrD;AACA,SAAK,OAAO,cAAc,aAAa,IAAI,SAAS;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,IAAI,KAAK,OAAO;AACpB,SAAK,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,IAAI,KAAK;AACb,UAAM,OAAO,KAAK,KAAK,IAAI,GAAG;AAC9B,WAAO,OAAO,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC,IAAI;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,QAAQ;AAEnB,UAAM,UAAU,CAAA;AAChB,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK,WAAW;AAC9C,UAAI,IAAI,WAAW,MAAM,GAAG;AAC1B,gBAAQ,KAAK,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,MAChD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,KAAK;AAChB,SAAK,KAAK,OAAO,GAAG;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ;AACZ,SAAK,KAAK,MAAK;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ;AAAA,EAEd;AACF;AAMA,cAAc,eAAe;"}
1
+ {"version":3,"file":"memory-storage-CSB5_IfU.js","sources":["../src/storage/memory-storage.js"],"sourcesContent":["/**\n * @fileoverview Memory-only storage adapter (fallback, no actual persistence).\n *\n * Provides in-memory storage for testing or when persistent storage is unavailable.\n * Data is shared across instances with the same namespace but lost on process restart.\n *\n * @module storage/memory-storage\n */\n\nimport { PersistentStorage } from './persistent-storage.js';\n\n/**\n * Memory-only storage adapter.\n *\n * Stores data in memory with no persistence across restarts.\n * Uses a global store to share data across instances with same namespace.\n *\n * @class MemoryStorage\n * @extends PersistentStorage\n * @example\n * const storage = new MemoryStorage();\n * await storage.init('myapp');\n * await storage.put('key1', { id: 'event1' });\n */\nexport class MemoryStorage extends PersistentStorage {\n /**\n * Create a new MemoryStorage instance.\n */\n constructor() {\n super();\n this.data = new Map();\n this.namespace = null;\n }\n\n /**\n * Initialize storage with namespace.\n *\n * @param {string} namespace - Storage namespace\n * @returns {Promise<void>}\n */\n async init(namespace) {\n this.namespace = namespace;\n // Check if there's existing data for this namespace\n if (!MemoryStorage._globalStore) {\n MemoryStorage._globalStore = new Map();\n }\n if (!MemoryStorage._globalStore.has(namespace)) {\n MemoryStorage._globalStore.set(namespace, new Map());\n }\n this.data = MemoryStorage._globalStore.get(namespace);\n }\n\n /**\n * Store an event.\n *\n * @param {string} key - Storage key\n * @param {Object} event - Event data\n * @returns {Promise<void>}\n */\n async put(key, event) {\n this.data.set(key, JSON.parse(JSON.stringify(event))); // Deep clone\n }\n\n /**\n * Retrieve an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<Object|null>} Event data or null\n */\n async get(key) {\n const data = this.data.get(key);\n return data ? JSON.parse(JSON.stringify(data)) : null; // Deep clone\n }\n\n /**\n * Retrieve all events matching a prefix.\n *\n * @param {string} prefix - Key prefix to match\n * @returns {Promise<any[]>} Array of matching events\n */\n async getAll(prefix) {\n /** @type {any[]} */\n const results = [];\n for (const [key, value] of this.data.entries()) {\n if (key.startsWith(prefix)) {\n results.push(JSON.parse(JSON.stringify(value)));\n }\n }\n return results;\n }\n\n /**\n * Delete an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<void>}\n */\n async delete(key) {\n this.data.delete(key);\n }\n\n /**\n * Clear all stored events.\n *\n * @returns {Promise<void>}\n */\n async clear() {\n this.data.clear();\n }\n\n /**\n * Close storage (no-op for memory storage).\n *\n * @returns {Promise<void>}\n */\n async close() {\n // Nothing to close for memory storage\n }\n}\n\n/**\n * Global store shared across instances with same namespace.\n * @private\n */\nMemoryStorage._globalStore = null;\n"],"names":[],"mappings":";AAwBO,MAAM,sBAAsB,kBAAkB;AAAA;AAAA;AAAA;AAAA,EAInD,cAAc;AACZ,UAAK;AACL,SAAK,OAAO,oBAAI,IAAG;AACnB,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,KAAK,WAAW;AACpB,SAAK,YAAY;AAEjB,QAAI,CAAC,cAAc,cAAc;AAC/B,oBAAc,eAAe,oBAAI,IAAG;AAAA,IACtC;AACA,QAAI,CAAC,cAAc,aAAa,IAAI,SAAS,GAAG;AAC9C,oBAAc,aAAa,IAAI,WAAW,oBAAI,IAAG,CAAE;AAAA,IACrD;AACA,SAAK,OAAO,cAAc,aAAa,IAAI,SAAS;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,IAAI,KAAK,OAAO;AACpB,SAAK,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,IAAI,KAAK;AACb,UAAM,OAAO,KAAK,KAAK,IAAI,GAAG;AAC9B,WAAO,OAAO,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC,IAAI;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,QAAQ;AAEnB,UAAM,UAAU,CAAA;AAChB,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK,WAAW;AAC9C,UAAI,IAAI,WAAW,MAAM,GAAG;AAC1B,gBAAQ,KAAK,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,MAChD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,KAAK;AAChB,SAAK,KAAK,OAAO,GAAG;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ;AACZ,SAAK,KAAK,MAAK;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ;AAAA,EAEd;AACF;AAMA,cAAc,eAAe;"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./index-ChpSfdYS.cjs");class e extends t.PersistentStorage{constructor(){super(),this.data=new Map,this.namespace=null}async init(t){this.namespace=t,e._globalStore||(e._globalStore=new Map),e._globalStore.has(t)||e._globalStore.set(t,new Map),this.data=e._globalStore.get(t)}async put(t,e){this.data.set(t,JSON.parse(JSON.stringify(e)))}async get(t){const e=this.data.get(t);return e?JSON.parse(JSON.stringify(e)):null}async getAll(t){const e=[];for(const[s,a]of this.data.entries())s.startsWith(t)&&e.push(JSON.parse(JSON.stringify(a)));return e}async delete(t){this.data.delete(t)}async clear(){this.data.clear()}async close(){}}e._globalStore=null,exports.MemoryStorage=e;
2
- //# sourceMappingURL=memory-storage-CKUGDq2d.cjs.map
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./index-DXFsE8r6.cjs");class e extends t.PersistentStorage{constructor(){super(),this.data=new Map,this.namespace=null}async init(t){this.namespace=t,e._globalStore||(e._globalStore=new Map),e._globalStore.has(t)||e._globalStore.set(t,new Map),this.data=e._globalStore.get(t)}async put(t,e){this.data.set(t,JSON.parse(JSON.stringify(e)))}async get(t){const e=this.data.get(t);return e?JSON.parse(JSON.stringify(e)):null}async getAll(t){const e=[];for(const[s,a]of this.data.entries())s.startsWith(t)&&e.push(JSON.parse(JSON.stringify(a)));return e}async delete(t){this.data.delete(t)}async clear(){this.data.clear()}async close(){}}e._globalStore=null,exports.MemoryStorage=e;
2
+ //# sourceMappingURL=memory-storage-Q8mEQ6Ix.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-storage-CKUGDq2d.cjs","sources":["../src/storage/memory-storage.js"],"sourcesContent":["/**\n * @fileoverview Memory-only storage adapter (fallback, no actual persistence).\n *\n * Provides in-memory storage for testing or when persistent storage is unavailable.\n * Data is shared across instances with the same namespace but lost on process restart.\n *\n * @module storage/memory-storage\n */\n\nimport { PersistentStorage } from './persistent-storage.js';\n\n/**\n * Memory-only storage adapter.\n *\n * Stores data in memory with no persistence across restarts.\n * Uses a global store to share data across instances with same namespace.\n *\n * @class MemoryStorage\n * @extends PersistentStorage\n * @example\n * const storage = new MemoryStorage();\n * await storage.init('myapp');\n * await storage.put('key1', { id: 'event1' });\n */\nexport class MemoryStorage extends PersistentStorage {\n /**\n * Create a new MemoryStorage instance.\n */\n constructor() {\n super();\n this.data = new Map();\n this.namespace = null;\n }\n\n /**\n * Initialize storage with namespace.\n *\n * @param {string} namespace - Storage namespace\n * @returns {Promise<void>}\n */\n async init(namespace) {\n this.namespace = namespace;\n // Check if there's existing data for this namespace\n if (!MemoryStorage._globalStore) {\n MemoryStorage._globalStore = new Map();\n }\n if (!MemoryStorage._globalStore.has(namespace)) {\n MemoryStorage._globalStore.set(namespace, new Map());\n }\n this.data = MemoryStorage._globalStore.get(namespace);\n }\n\n /**\n * Store an event.\n *\n * @param {string} key - Storage key\n * @param {Object} event - Event data\n * @returns {Promise<void>}\n */\n async put(key, event) {\n this.data.set(key, JSON.parse(JSON.stringify(event))); // Deep clone\n }\n\n /**\n * Retrieve an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<Object|null>} Event data or null\n */\n async get(key) {\n const data = this.data.get(key);\n return data ? JSON.parse(JSON.stringify(data)) : null; // Deep clone\n }\n\n /**\n * Retrieve all events matching a prefix.\n *\n * @param {string} prefix - Key prefix to match\n * @returns {Promise<any[]>} Array of matching events\n */\n async getAll(prefix) {\n /** @type {any[]} */\n const results = [];\n for (const [key, value] of this.data.entries()) {\n if (key.startsWith(prefix)) {\n results.push(JSON.parse(JSON.stringify(value)));\n }\n }\n return results;\n }\n\n /**\n * Delete an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<void>}\n */\n async delete(key) {\n this.data.delete(key);\n }\n\n /**\n * Clear all stored events.\n *\n * @returns {Promise<void>}\n */\n async clear() {\n this.data.clear();\n }\n\n /**\n * Close storage (no-op for memory storage).\n *\n * @returns {Promise<void>}\n */\n async close() {\n // Nothing to close for memory storage\n }\n}\n\n/**\n * Global store shared across instances with same namespace.\n * @private\n */\nMemoryStorage._globalStore = null;\n"],"names":["MemoryStorage","PersistentStorage","constructor","super","this","data","Map","namespace","init","_globalStore","has","set","get","put","key","event","JSON","parse","stringify","getAll","prefix","results","value","entries","startsWith","push","delete","clear","close"],"mappings":"wHAwBO,MAAMA,UAAsBC,EAAAA,kBAIjC,WAAAC,GACEC,QACAC,KAAKC,SAAWC,IAChBF,KAAKG,UAAY,IACnB,CAQA,UAAMC,CAAKD,GACTH,KAAKG,UAAYA,EAEZP,EAAcS,eACjBT,EAAcS,iBAAmBH,KAE9BN,EAAcS,aAAaC,IAAIH,IAClCP,EAAcS,aAAaE,IAAIJ,EAAW,IAAID,KAEhDF,KAAKC,KAAOL,EAAcS,aAAaG,IAAIL,EAC7C,CASA,SAAMM,CAAIC,EAAKC,GACbX,KAAKC,KAAKM,IAAIG,EAAKE,KAAKC,MAAMD,KAAKE,UAAUH,IAC/C,CAQA,SAAMH,CAAIE,GACR,MAAMT,EAAOD,KAAKC,KAAKO,IAAIE,GAC3B,OAAOT,EAAOW,KAAKC,MAAMD,KAAKE,UAAUb,IAAS,IACnD,CAQA,YAAMc,CAAOC,GAEX,MAAMC,EAAU,GAChB,IAAA,MAAYP,EAAKQ,KAAUlB,KAAKC,KAAKkB,UAC/BT,EAAIU,WAAWJ,IACjBC,EAAQI,KAAKT,KAAKC,MAAMD,KAAKE,UAAUI,KAG3C,OAAOD,CACT,CAQA,YAAM,CAAOP,GACXV,KAAKC,KAAKqB,OAAOZ,EACnB,CAOA,WAAMa,GACJvB,KAAKC,KAAKsB,OACZ,CAOA,WAAMC,GAEN,EAOF5B,EAAcS,aAAe"}
1
+ {"version":3,"file":"memory-storage-Q8mEQ6Ix.cjs","sources":["../src/storage/memory-storage.js"],"sourcesContent":["/**\n * @fileoverview Memory-only storage adapter (fallback, no actual persistence).\n *\n * Provides in-memory storage for testing or when persistent storage is unavailable.\n * Data is shared across instances with the same namespace but lost on process restart.\n *\n * @module storage/memory-storage\n */\n\nimport { PersistentStorage } from './persistent-storage.js';\n\n/**\n * Memory-only storage adapter.\n *\n * Stores data in memory with no persistence across restarts.\n * Uses a global store to share data across instances with same namespace.\n *\n * @class MemoryStorage\n * @extends PersistentStorage\n * @example\n * const storage = new MemoryStorage();\n * await storage.init('myapp');\n * await storage.put('key1', { id: 'event1' });\n */\nexport class MemoryStorage extends PersistentStorage {\n /**\n * Create a new MemoryStorage instance.\n */\n constructor() {\n super();\n this.data = new Map();\n this.namespace = null;\n }\n\n /**\n * Initialize storage with namespace.\n *\n * @param {string} namespace - Storage namespace\n * @returns {Promise<void>}\n */\n async init(namespace) {\n this.namespace = namespace;\n // Check if there's existing data for this namespace\n if (!MemoryStorage._globalStore) {\n MemoryStorage._globalStore = new Map();\n }\n if (!MemoryStorage._globalStore.has(namespace)) {\n MemoryStorage._globalStore.set(namespace, new Map());\n }\n this.data = MemoryStorage._globalStore.get(namespace);\n }\n\n /**\n * Store an event.\n *\n * @param {string} key - Storage key\n * @param {Object} event - Event data\n * @returns {Promise<void>}\n */\n async put(key, event) {\n this.data.set(key, JSON.parse(JSON.stringify(event))); // Deep clone\n }\n\n /**\n * Retrieve an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<Object|null>} Event data or null\n */\n async get(key) {\n const data = this.data.get(key);\n return data ? JSON.parse(JSON.stringify(data)) : null; // Deep clone\n }\n\n /**\n * Retrieve all events matching a prefix.\n *\n * @param {string} prefix - Key prefix to match\n * @returns {Promise<any[]>} Array of matching events\n */\n async getAll(prefix) {\n /** @type {any[]} */\n const results = [];\n for (const [key, value] of this.data.entries()) {\n if (key.startsWith(prefix)) {\n results.push(JSON.parse(JSON.stringify(value)));\n }\n }\n return results;\n }\n\n /**\n * Delete an event.\n *\n * @param {string} key - Storage key\n * @returns {Promise<void>}\n */\n async delete(key) {\n this.data.delete(key);\n }\n\n /**\n * Clear all stored events.\n *\n * @returns {Promise<void>}\n */\n async clear() {\n this.data.clear();\n }\n\n /**\n * Close storage (no-op for memory storage).\n *\n * @returns {Promise<void>}\n */\n async close() {\n // Nothing to close for memory storage\n }\n}\n\n/**\n * Global store shared across instances with same namespace.\n * @private\n */\nMemoryStorage._globalStore = null;\n"],"names":["MemoryStorage","PersistentStorage","constructor","super","this","data","Map","namespace","init","_globalStore","has","set","get","put","key","event","JSON","parse","stringify","getAll","prefix","results","value","entries","startsWith","push","delete","clear","close"],"mappings":"wHAwBO,MAAMA,UAAsBC,EAAAA,kBAIjC,WAAAC,GACEC,QACAC,KAAKC,SAAWC,IAChBF,KAAKG,UAAY,IACnB,CAQA,UAAMC,CAAKD,GACTH,KAAKG,UAAYA,EAEZP,EAAcS,eACjBT,EAAcS,iBAAmBH,KAE9BN,EAAcS,aAAaC,IAAIH,IAClCP,EAAcS,aAAaE,IAAIJ,EAAW,IAAID,KAEhDF,KAAKC,KAAOL,EAAcS,aAAaG,IAAIL,EAC7C,CASA,SAAMM,CAAIC,EAAKC,GACbX,KAAKC,KAAKM,IAAIG,EAAKE,KAAKC,MAAMD,KAAKE,UAAUH,IAC/C,CAQA,SAAMH,CAAIE,GACR,MAAMT,EAAOD,KAAKC,KAAKO,IAAIE,GAC3B,OAAOT,EAAOW,KAAKC,MAAMD,KAAKE,UAAUb,IAAS,IACnD,CAQA,YAAMc,CAAOC,GAEX,MAAMC,EAAU,GAChB,IAAA,MAAYP,EAAKQ,KAAUlB,KAAKC,KAAKkB,UAC/BT,EAAIU,WAAWJ,IACjBC,EAAQI,KAAKT,KAAKC,MAAMD,KAAKE,UAAUI,KAG3C,OAAOD,CACT,CAQA,YAAM,CAAOP,GACXV,KAAKC,KAAKqB,OAAOZ,EACnB,CAOA,WAAMa,GACJvB,KAAKC,KAAKsB,OACZ,CAOA,WAAMC,GAEN,EAOF5B,EAAcS,aAAe"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "holosphere",
3
- "version": "2.0.0-alpha15",
3
+ "version": "2.0.0-alpha16",
4
4
  "description": "Holonic geospatial communication infrastructure combining H3 hexagonal indexing with distributed P2P storage",
5
5
  "type": "module",
6
6
  "bin": {
@@ -170,7 +170,7 @@ export async function issueCapability(permissions, scope, recipient, options = {
170
170
  isSelfCapability: selfCap, // Mark self-capabilities for clarity
171
171
  nonce: generateNonce(),
172
172
  issued: Date.now(),
173
- expires: Date.now() + expiresIn,
173
+ expires: expiresIn != null ? Date.now() + expiresIn : null,
174
174
  };
175
175
 
176
176
  // Encode token as base64
@@ -303,8 +303,8 @@ export async function verifyCapability(token, requiredPermission, scope) {
303
303
  return false;
304
304
  }
305
305
 
306
- // Check expiration
307
- if (Date.now() > tokenObj.expires) {
306
+ // Check expiration (null/undefined expires = no expiration)
307
+ if (tokenObj.expires != null && Date.now() > tokenObj.expires) {
308
308
  console.log('[verifyCapability] ❌ Token expired:', {
309
309
  expires: tokenObj.expires,
310
310
  now: Date.now(),
@@ -163,7 +163,7 @@ export function createHologram(sourceHolon, targetHolon, lensName, dataId, appna
163
163
  * @param {string} [options.targetAuthorPubKey] - Target author's public key (defaults to client.publicKey)
164
164
  * @param {string} [options.capability] - Pre-issued capability (if not provided, one will be issued)
165
165
  * @param {string[]} [options.permissions=['read']] - Permissions for auto-issued capability
166
- * @param {number} [options.expiresIn] - Capability expiration (null for self-caps = no expiration)
166
+ * @param {number} [options.expiresIn=null] - Capability expiration in ms (null = no expiration)
167
167
  * @returns {Promise<Object>} Hologram object with unified structure
168
168
  */
169
169
  export async function createHologramWithCapability(client, sourceHolon, targetHolon, lensName, dataId, appname, options = {}) {
@@ -188,7 +188,7 @@ export async function createHologramWithCapability(client, sourceHolon, targetHo
188
188
  { holonId: sourceHolon, lensName, dataId },
189
189
  targetAuthorPubKey,
190
190
  {
191
- expiresIn: expiresIn !== null ? expiresIn : (isSelfCapability ? 365 * 24 * 60 * 60 * 1000 : 3600000), // 1 year for self, 1 hour for cross
191
+ expiresIn: expiresIn !== undefined ? expiresIn : null, // Hologram capabilities don't expire — they live as long as the hologram
192
192
  issuer: sourceAuthorPubKey,
193
193
  issuerKey: sourceAuthorPrivKey, // Use passed private key (holon's key when federated via holonsbot)
194
194
  }
package/src/index.js CHANGED
@@ -1419,9 +1419,18 @@ class HoloSphereBase extends HoloSphereCore {
1419
1419
  // Check holon-level authorization BEFORE reading data or caching
1420
1420
  // Helper function to detect if a string is a pubkey (64-char hex)
1421
1421
  const isPubkeyHolon = typeof holonId === 'string' && /^[0-9a-f]{64}$/i.test(holonId);
1422
- // For pubkey-based holons, check if it's our own holon (our pubkey)
1422
+ // Determine effective deleter pubkey - use signingKey's pubkey if provided
1423
+ let effectiveDeleterPubKey = this.client?.publicKey;
1424
+ if (options.signingKey) {
1425
+ try {
1426
+ effectiveDeleterPubKey = crypto.getPublicKey(options.signingKey);
1427
+ } catch (err) {
1428
+ this._log('WARN', '⚠️ Invalid signingKey in delete, using client key', { error: err.message });
1429
+ }
1430
+ }
1431
+ // For pubkey-based holons, check if it's our own holon (matches effective deleter's pubkey)
1423
1432
  // For non-pubkey holons (H3 cells, UUIDs), skip authorization for backwards compatibility
1424
- const isOwnHolon = this.client && (holonId === this.client.publicKey || !isPubkeyHolon);
1433
+ const isOwnHolon = effectiveDeleterPubKey && (holonId === effectiveDeleterPubKey || !isPubkeyHolon);
1425
1434
  const capToken = options.capabilityToken || options.capability;
1426
1435
 
1427
1436
  if (!isOwnHolon && !capToken) {
@@ -1441,7 +1450,12 @@ class HoloSphereBase extends HoloSphereCore {
1441
1450
  let dataSource = cached ? 'write-cache' : null;
1442
1451
 
1443
1452
  if (!existingData) {
1444
- existingData = await storage.read(this.client, path);
1453
+ // When signingKey is provided, read using the signer's pubkey as author
1454
+ const readOptions = {};
1455
+ if (effectiveDeleterPubKey && effectiveDeleterPubKey !== this.client?.publicKey) {
1456
+ readOptions.authors = [effectiveDeleterPubKey];
1457
+ }
1458
+ existingData = await storage.read(this.client, path, readOptions);
1445
1459
  dataSource = existingData ? 'storage' : null;
1446
1460
  }
1447
1461
 
@@ -1455,7 +1469,7 @@ class HoloSphereBase extends HoloSphereCore {
1455
1469
 
1456
1470
  // Check authorization: data owner can delete, others need capability token
1457
1471
  const dataOwner = existingData.owner || existingData._creator;
1458
- const isDataOwner = !dataOwner || dataOwner === this.client.publicKey;
1472
+ const isDataOwner = !dataOwner || dataOwner === this.client.publicKey || dataOwner === effectiveDeleterPubKey;
1459
1473
 
1460
1474
  if (!isDataOwner) {
1461
1475
  // Non-data-owner must provide a valid capability token
@@ -1494,7 +1508,18 @@ class HoloSphereBase extends HoloSphereCore {
1494
1508
  const startTime = Date.now();
1495
1509
  this._log('DEBUG', '🔄 DELETE SYNC START', { path, dataId });
1496
1510
 
1497
- const deletePromise = storage.deleteData(this.client, path).then(() => {
1511
+ // Build storage options - pass signingKey and derive authorPubkey if provided
1512
+ const storageOptions = {};
1513
+ if (options.signingKey) {
1514
+ storageOptions.signingKey = options.signingKey;
1515
+ try {
1516
+ storageOptions.authorPubkey = crypto.getPublicKey(options.signingKey);
1517
+ } catch (err) {
1518
+ this._log('WARN', '⚠️ Invalid signingKey in delete, using client key', { error: err.message });
1519
+ }
1520
+ }
1521
+
1522
+ const deletePromise = storage.deleteData(this.client, path, storageOptions).then(() => {
1498
1523
  const duration = Date.now() - startTime;
1499
1524
  this._metrics.deletes++;
1500
1525
  // Clear from delete cache after successful sync
@@ -1637,15 +1662,30 @@ class HoloSphereBase extends HoloSphereCore {
1637
1662
  *
1638
1663
  * @param {string} holonId - H3 cell ID for the holon
1639
1664
  * @param {string} lensName - Name of the lens (data category)
1665
+ * @param {Object} [options] - Delete options
1666
+ * @param {string} [options.signingKey] - Private key for signing tombstones (hex format).
1667
+ * Required when deleting data written by a different keypair (e.g., per-holon keys).
1640
1668
  * @returns {Promise<boolean>} True if deletion succeeded
1641
1669
  */
1642
- async deleteAll(holonId, lensName) {
1670
+ async deleteAll(holonId, lensName, options = {}) {
1643
1671
  // Auto-convert to strings for convenience
1644
1672
  if (holonId != null) holonId = String(holonId);
1645
1673
  if (lensName != null) lensName = String(lensName);
1646
1674
 
1647
1675
  const path = storage.buildPath(this.config.appName, holonId, lensName);
1648
- return storage.deleteAll(this.client, path);
1676
+
1677
+ // Build storage options - derive authorPubkey from signingKey if provided
1678
+ const storageOptions = {};
1679
+ if (options.signingKey) {
1680
+ storageOptions.signingKey = options.signingKey;
1681
+ try {
1682
+ storageOptions.authorPubkey = crypto.getPublicKey(options.signingKey);
1683
+ } catch (err) {
1684
+ this._log('WARN', '⚠️ Invalid signingKey in deleteAll, using client key', { error: err.message });
1685
+ }
1686
+ }
1687
+
1688
+ return storage.deleteAll(this.client, path, storageOptions);
1649
1689
  }
1650
1690
 
1651
1691
  // === Schema Operations ===
@@ -2276,18 +2316,49 @@ class HoloSphereBase extends HoloSphereCore {
2276
2316
  let innerSubscription = null;
2277
2317
  let unsubscribeCalled = false;
2278
2318
 
2279
- subscriptions.createSubscription(this.client, path, callback, subscriptionOptions)
2280
- .then(subscription => {
2319
+ (async () => {
2320
+ try {
2321
+ // Authorization check — mirrors read() logic
2322
+ const targetPubkey = await this._resolveHolonToPubkey(holonId);
2323
+ const isOtherAuthor = targetPubkey && targetPubkey !== this.client.publicKey;
2324
+
2325
+ if (isOtherAuthor) {
2326
+ const capToken = options.capabilityToken || options.capability;
2327
+ if (capToken) {
2328
+ const authorized = await this.verifyCapability(capToken, 'read', { holonId, lensName });
2329
+ if (!authorized) {
2330
+ this._log('WARN', '❌ Subscribe: invalid capability token - skipping', { holonId, lensName });
2331
+ return;
2332
+ }
2333
+ } else {
2334
+ const capability = await this._getCapabilityForAuthor(targetPubkey, { holonId, lensName });
2335
+ if (!capability) {
2336
+ this._log('WARN', '❌ Subscribe: no capability for federated author - skipping', {
2337
+ holonId, lensName, targetPubkey: targetPubkey?.slice(0, 12) + '...'
2338
+ });
2339
+ return;
2340
+ }
2341
+ this._log('DEBUG', '✅ Subscribe: capability found for federated author', {
2342
+ holonId, lensName, targetPubkey: targetPubkey?.slice(0, 12) + '...'
2343
+ });
2344
+ }
2345
+ }
2346
+
2347
+ if (unsubscribeCalled) return;
2348
+
2349
+ const subscription = await subscriptions.createSubscription(
2350
+ this.client, path, callback, subscriptionOptions
2351
+ );
2281
2352
  innerSubscription = subscription;
2282
2353
  if (unsubscribeCalled) {
2283
2354
  subscription.unsubscribe();
2284
2355
  } else {
2285
2356
  this.subscriptionRegistry.register(subscriptionId, subscription);
2286
2357
  }
2287
- })
2288
- .catch(err => {
2358
+ } catch (err) {
2289
2359
  this._log('ERROR', 'Subscription setup failed', { path, error: err.message });
2290
- });
2360
+ }
2361
+ })();
2291
2362
 
2292
2363
  this._metrics.subscriptions = (this._metrics.subscriptions || 0) + 1;
2293
2364
  return {
@@ -543,9 +543,21 @@ export async function nostrGetAllHybrid(client, pathPrefix, kind = 30000, option
543
543
  * console.log('Item deleted successfully');
544
544
  * }
545
545
  */
546
- export async function nostrDelete(client, path, kind = 30000) {
546
+ export async function nostrDelete(client, path, kindOrOptions = 30000) {
547
+ const options = typeof kindOrOptions === 'number'
548
+ ? { kind: kindOrOptions }
549
+ : kindOrOptions;
550
+ const kind = options.kind || 30000;
551
+ const { signingKey } = options;
552
+
553
+ // When signingKey is provided, query using its pubkey (the actual data author)
554
+ const authorPubkey = options.authorPubkey || client.publicKey;
555
+ const readOptions = authorPubkey !== client.publicKey
556
+ ? { authors: [authorPubkey] }
557
+ : {};
558
+
547
559
  // Read existing data first
548
- const existing = await nostrGet(client, path, kind);
560
+ const existing = await nostrGet(client, path, kind, readOptions);
549
561
 
550
562
  if (!existing) {
551
563
  return { reason: 'not_found', results: [] };
@@ -557,21 +569,15 @@ export async function nostrDelete(client, path, kind = 30000) {
557
569
  _deletedAt: Date.now()
558
570
  };
559
571
 
560
- // Publish tombstone as replacement event
572
+ // Publish tombstone as replacement event, using signingKey if provided
561
573
  // This ensures reads will see _deleted flag (also updates LRU cache via _cacheEvent)
562
- const result = await nostrPut(client, path, tombstone, kind);
563
-
564
- // Also delete from persistent storage to prevent resurrection on restart
565
- if (client.persistentStorage) {
566
- try {
567
- await client.persistentStorage.delete(path);
568
- } catch (e) {
569
- // Ignore storage deletion errors
570
- }
571
- }
574
+ // The tombstone event is persisted locally via _cacheEvent, ensuring it survives page reloads
575
+ const putOptions = { kind };
576
+ if (signingKey) putOptions.signingKey = signingKey;
577
+ const result = await nostrPut(client, path, tombstone, putOptions);
572
578
 
573
579
  // Also publish NIP-09 deletion event for relay compliance
574
- const coordinate = `${kind}:${client.publicKey}:${path}`;
580
+ const coordinate = `${kind}:${authorPubkey}:${path}`;
575
581
  const deletionEvent = {
576
582
  kind: 5, // NIP-09 deletion event
577
583
  created_at: Math.floor(Date.now() / 1000),
@@ -582,8 +588,9 @@ export async function nostrDelete(client, path, kind = 30000) {
582
588
  };
583
589
 
584
590
  // Publish deletion event (best effort, don't block on failure)
591
+ const publishOptions = signingKey ? { signingKey } : {};
585
592
  try {
586
- await client.publish(deletionEvent);
593
+ await client.publish(deletionEvent, publishOptions);
587
594
  // Silent success - deletion queued
588
595
  } catch (error) {
589
596
  // Silent - deletion will be retried
@@ -601,17 +608,29 @@ const DELETE_RATE_LIMIT_MS = 100;
601
608
  *
602
609
  * @param {Object} client - NostrClient instance
603
610
  * @param {string} pathPrefix - Path prefix to delete all items under
604
- * @param {number} [kind=30000] - Original event kind (default: 30000)
611
+ * @param {number|Object} [kindOrOptions=30000] - Event kind (number) or options object
612
+ * @param {number} [kindOrOptions.kind=30000] - Event kind
613
+ * @param {string} [kindOrOptions.signingKey] - Private key for signing tombstones (hex format)
614
+ * @param {string} [kindOrOptions.authorPubkey] - Public key of the data author to query for
605
615
  * @returns {Promise<Object>} Deletion results with success count
606
616
  * @example
607
617
  * const result = await nostrDeleteAll(client, 'myapp/holon1/items/');
608
618
  * console.log(`Deleted ${result.count} items`);
609
619
  */
610
- export async function nostrDeleteAll(client, pathPrefix, kind = 30000) {
611
- // Query events from relay
620
+ export async function nostrDeleteAll(client, pathPrefix, kindOrOptions = 30000) {
621
+ const options = typeof kindOrOptions === 'number'
622
+ ? { kind: kindOrOptions }
623
+ : kindOrOptions;
624
+ const kind = options.kind || 30000;
625
+ const { signingKey } = options;
626
+
627
+ // When signingKey/authorPubkey is provided, query using that pubkey (the actual data author)
628
+ const authorPubkey = options.authorPubkey || client.publicKey;
629
+
630
+ // Query events from relay using the correct author
612
631
  const filter = {
613
632
  kinds: [kind],
614
- authors: [client.publicKey],
633
+ authors: [authorPubkey],
615
634
  limit: 1000,
616
635
  };
617
636
 
@@ -656,6 +675,10 @@ export async function nostrDeleteAll(client, pathPrefix, kind = 30000) {
656
675
  return { success: true, count: 0, results: [] };
657
676
  }
658
677
 
678
+ // Build put options for tombstone publishing - use signingKey if provided
679
+ const putOptions = { kind };
680
+ if (signingKey) putOptions.signingKey = signingKey;
681
+
659
682
  // Publish tombstone events for each matching item (with rate limiting)
660
683
  const tombstoneResults = [];
661
684
  for (let i = 0; i < matching.length; i++) {
@@ -670,18 +693,10 @@ export async function nostrDeleteAll(client, pathPrefix, kind = 30000) {
670
693
  };
671
694
 
672
695
  // Publish tombstone replacement (this also updates LRU cache via _cacheEvent)
673
- const result = await nostrPut(client, dTag, tombstone, kind);
696
+ // The tombstone event is persisted locally, ensuring it survives page reloads
697
+ const result = await nostrPut(client, dTag, tombstone, putOptions);
674
698
  tombstoneResults.push(result);
675
699
 
676
- // Also delete from persistent storage to prevent resurrection on restart
677
- if (client.persistentStorage) {
678
- try {
679
- await client.persistentStorage.delete(dTag);
680
- } catch (e) {
681
- // Ignore storage deletion errors
682
- }
683
- }
684
-
685
700
  // Rate limit: add delay between deletes to avoid overwhelming relays
686
701
  if (i < matching.length - 1 && DELETE_RATE_LIMIT_MS > 0) {
687
702
  await new Promise(resolve => setTimeout(resolve, DELETE_RATE_LIMIT_MS));
@@ -694,11 +709,12 @@ export async function nostrDeleteAll(client, pathPrefix, kind = 30000) {
694
709
  // Collect all coordinates for NIP-09 deletion event
695
710
  const coordinates = matching.map(event => {
696
711
  const dTag = event.tags.find(t => t[0] === 'd')[1];
697
- return `${kind}:${client.publicKey}:${dTag}`;
712
+ return `${kind}:${authorPubkey}:${dTag}`;
698
713
  });
699
714
 
700
715
  // Publish a single kind 5 deletion event with all coordinates
701
716
  // This is for relay compliance (best effort)
717
+ const publishOptions = signingKey ? { signingKey } : {};
702
718
  try {
703
719
  const deletionEvent = {
704
720
  kind: 5, // NIP-09 deletion event
@@ -707,7 +723,7 @@ export async function nostrDeleteAll(client, pathPrefix, kind = 30000) {
707
723
  content: '', // Optional deletion reason/note
708
724
  };
709
725
 
710
- await client.publish(deletionEvent);
726
+ await client.publish(deletionEvent, publishOptions);
711
727
  // Silent success - bulk deletion queued
712
728
  } catch (error) {
713
729
  // Silent - bulk deletion will be retried
@@ -156,11 +156,18 @@ export async function update(client, path, updates) {
156
156
  * Delete data (tombstone)
157
157
  * @param {Object} client - NostrClient instance
158
158
  * @param {string} path - Path
159
+ * @param {Object} [options] - Delete options
160
+ * @param {string} [options.signingKey] - Private key for signing tombstones (hex format)
161
+ * @param {string} [options.authorPubkey] - Public key of the data author
159
162
  * @returns {Promise<boolean>} Success indicator
160
163
  */
161
- export async function deleteData(client, path) {
164
+ export async function deleteData(client, path, options = {}) {
162
165
  try {
163
- const result = await nostrDelete(client, path);
166
+ const deleteOptions = { kind: 30000 };
167
+ if (options.signingKey) deleteOptions.signingKey = options.signingKey;
168
+ if (options.authorPubkey) deleteOptions.authorPubkey = options.authorPubkey;
169
+
170
+ const result = await nostrDelete(client, path, deleteOptions);
164
171
 
165
172
  if (result.reason === 'not_found') {
166
173
  return false; // Item doesn't exist
@@ -179,11 +186,18 @@ export async function deleteData(client, path) {
179
186
  * Delete all data under path prefix (tombstone)
180
187
  * @param {Object} client - NostrClient instance
181
188
  * @param {string} path - Path prefix
189
+ * @param {Object} [options] - Delete options
190
+ * @param {string} [options.signingKey] - Private key for signing tombstones (hex format)
191
+ * @param {string} [options.authorPubkey] - Public key of the data author
182
192
  * @returns {Promise<Object>} Deletion results
183
193
  */
184
- export async function deleteAll(client, path) {
194
+ export async function deleteAll(client, path, options = {}) {
185
195
  try {
186
- const result = await nostrDeleteAll(client, path);
196
+ const deleteOptions = { kind: 30000 };
197
+ if (options.signingKey) deleteOptions.signingKey = options.signingKey;
198
+ if (options.authorPubkey) deleteOptions.authorPubkey = options.authorPubkey;
199
+
200
+ const result = await nostrDeleteAll(client, path, deleteOptions);
187
201
  return result;
188
202
  } catch (error) {
189
203
  console.error('Nostr deleteAll error:', error);