holosphere 2.0.0-alpha17 → 2.0.0-alpha19

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-C9IgM3mu.cjs → 2019-CAYA1q1p.cjs} +2 -2
  2. package/dist/{2019-C9IgM3mu.cjs.map → 2019-CAYA1q1p.cjs.map} +1 -1
  3. package/dist/{2019-CyFQLuqg.js → 2019-Cc18leNN.js} +2 -2
  4. package/dist/{2019-CyFQLuqg.js.map → 2019-Cc18leNN.js.map} +1 -1
  5. package/dist/{browser-CZ1l8nhY.cjs → browser-DAFvFs8I.cjs} +2 -2
  6. package/dist/{browser-CZ1l8nhY.cjs.map → browser-DAFvFs8I.cjs.map} +1 -1
  7. package/dist/{browser-BIvmkBi9.js → browser-yWnjyL8K.js} +2 -2
  8. package/dist/{browser-BIvmkBi9.js.map → browser-yWnjyL8K.js.map} +1 -1
  9. package/dist/cjs/holosphere.cjs +1 -1
  10. package/dist/esm/holosphere.js +1 -1
  11. package/dist/{index-HW9kbrzC.js → index-Bgi_csBR.js} +2 -2
  12. package/dist/{index-HW9kbrzC.js.map → index-Bgi_csBR.js.map} +1 -1
  13. package/dist/{index-B7ERi4ng.js → index-CHfVBEUH.js} +132 -101
  14. package/dist/index-CHfVBEUH.js.map +1 -0
  15. package/dist/{index-Cc-_uNpt.cjs → index-CPvYlZtl.cjs} +2 -2
  16. package/dist/{index-Cc-_uNpt.cjs.map → index-CPvYlZtl.cjs.map} +1 -1
  17. package/dist/{index-DusXavIT.js → index-CReYsOty.js} +2 -2
  18. package/dist/{index-DusXavIT.js.map → index-CReYsOty.js.map} +1 -1
  19. package/dist/index-Dgx5DQG_.cjs +29 -0
  20. package/dist/index-Dgx5DQG_.cjs.map +1 -0
  21. package/dist/{index-VrRkpshF.cjs → index-bnw0PiyT.cjs} +2 -2
  22. package/dist/{index-VrRkpshF.cjs.map → index-bnw0PiyT.cjs.map} +1 -1
  23. package/dist/{indexeddb-storage-CVz4Q3lo.cjs → indexeddb-storage-CyOZAiHV.cjs} +2 -2
  24. package/dist/{indexeddb-storage-CVz4Q3lo.cjs.map → indexeddb-storage-CyOZAiHV.cjs.map} +1 -1
  25. package/dist/{indexeddb-storage-yEswGGMG.js → indexeddb-storage-Ko4sjZtM.js} +2 -2
  26. package/dist/{indexeddb-storage-yEswGGMG.js.map → indexeddb-storage-Ko4sjZtM.js.map} +1 -1
  27. package/dist/{memory-storage-CSB5_IfU.js → memory-storage-BTfQQ9Ig.js} +2 -2
  28. package/dist/{memory-storage-CSB5_IfU.js.map → memory-storage-BTfQQ9Ig.js.map} +1 -1
  29. package/dist/{memory-storage-Q8mEQ6Ix.cjs → memory-storage-C6YV3dsT.cjs} +2 -2
  30. package/dist/{memory-storage-Q8mEQ6Ix.cjs.map → memory-storage-C6YV3dsT.cjs.map} +1 -1
  31. package/package.json +1 -1
  32. package/src/federation/registry.js +70 -0
  33. package/src/index.js +80 -90
  34. package/src/storage/nostr-async.js +12 -8
  35. package/src/storage/nostr-client.js +5 -2
  36. package/src/subscriptions/manager.js +13 -4
  37. package/dist/index-B7ERi4ng.js.map +0 -1
  38. package/dist/index-DXFsE8r6.cjs +0 -29
  39. package/dist/index-DXFsE8r6.cjs.map +0 -1
@@ -1,2 +1,2 @@
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
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-Dgx5DQG_.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-CyOZAiHV.cjs.map
@@ -1 +1 @@
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
+ {"version":3,"file":"indexeddb-storage-CyOZAiHV.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-B7ERi4ng.js";
1
+ import { aV as PersistentStorage } from "./index-CHfVBEUH.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-yEswGGMG.js.map
213
+ //# sourceMappingURL=indexeddb-storage-Ko4sjZtM.js.map
@@ -1 +1 @@
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":";AAuBO,MAAM,yBAAyB,kBAAkB;AAAA;AAAA;AAAA;AAAA,EAItD,cAAc;AACZ,UAAK;AAEL,SAAK,KAAK;AAEV,SAAK,SAAS;AAEd,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,KAAK,WAAW;AACpB,SAAK,SAAS,cAAc,SAAS;AAErC,QAAI;AACF,YAAM,KAAK,cAAa;AAAA,IAC1B,SAAS,OAAO;AAEd,UAAI,MAAM,SAAS,gBAAgB;AACjC,gBAAQ,KAAK,oDAAoD,KAAK,MAAM,0BAA0B;AACtG,cAAM,KAAK,gBAAe;AAC1B,cAAM,KAAK,cAAa;AAAA,MAC1B,OAAO;AACL,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,gBAAgB;AACpB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,UAAU,UAAU,KAAK,KAAK,QAAQ,CAAC;AAE7C,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAC5C,cAAQ,YAAY,MAAM;AACxB,aAAK,KAAK,QAAQ;AAClB,gBAAQ,MAAS;AAAA,MACnB;AAEA,cAAQ,kBAAkB,CAAC,UAAU;AACnC,cAAM;AAAA;AAAA,UAA0C,MAAM;AAAA;AACtD,cAAM,KAAK,OAAO;AAGlB,YAAI,CAAC,GAAG,iBAAiB,SAAS,KAAK,SAAS,GAAG;AACjD,gBAAM,cAAc,GAAG,kBAAkB,KAAK,WAAW,EAAE,SAAS,OAAO;AAE3E,sBAAY,YAAY,OAAO,OAAO,EAAE,QAAQ,OAAO;AAAA,QACzD;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,kBAAkB;AACtB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,UAAU,UAAU,eAAe,KAAK,MAAM;AACpD,cAAQ,YAAY,MAAM,QAAQ,MAAS;AAC3C,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAC5C,cAAQ,YAAY,MAAM;AACxB,gBAAQ,KAAK,oDAAoD,KAAK,MAAM,EAAE;AAE9E,gBAAQ,MAAS;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,IAAI,KAAK,OAAO;AACpB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAI;AACZ,eAAO,IAAI,MAAM,0BAA0B,CAAC;AAC5C;AAAA,MACF;AACA,YAAM,cAAc,KAAK,GAAG,YAAY,CAAC,KAAK,SAAS,GAAG,WAAW;AACrE,YAAM,cAAc,YAAY,YAAY,KAAK,SAAS;AAE1D,YAAM,UAAU,YAAY,IAAI,EAAE,KAAK,OAAO,WAAW,KAAK,IAAG,GAAI;AAErE,cAAQ,YAAY,MAAM,QAAQ,MAAS;AAC3C,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,IAAI,KAAK;AACb,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAI;AACZ,eAAO,IAAI,MAAM,0BAA0B,CAAC;AAC5C;AAAA,MACF;AACA,YAAM,cAAc,KAAK,GAAG,YAAY,CAAC,KAAK,SAAS,GAAG,UAAU;AACpE,YAAM,cAAc,YAAY,YAAY,KAAK,SAAS;AAE1D,YAAM,UAAU,YAAY,IAAI,GAAG;AAEnC,cAAQ,YAAY,MAAM;AACxB,cAAM,SAAS,QAAQ;AACvB,gBAAQ,SAAS,OAAO,QAAQ,IAAI;AAAA,MACtC;AACA,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,OAAO,QAAQ;AACnB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAI;AACZ,eAAO,IAAI,MAAM,0BAA0B,CAAC;AAC5C;AAAA,MACF;AACA,YAAM,cAAc,KAAK,GAAG,YAAY,CAAC,KAAK,SAAS,GAAG,UAAU;AACpE,YAAM,cAAc,YAAY,YAAY,KAAK,SAAS;AAG1D,YAAM,UAAU,CAAA;AAKhB,UAAI;AACJ,UAAI,QAAQ;AACV,cAAM,QAAQ,YAAY,MAAM,QAAQ,SAAS,KAAU,OAAO,KAAK;AACvE,kBAAU,YAAY,WAAW,KAAK;AAAA,MACxC,OAAO;AAEL,kBAAU,YAAY,WAAU;AAAA,MAClC;AAEA,cAAQ,YAAY,CAAC,UAAU;AAC7B,cAAM;AAAA;AAAA,UAAoC,MAAM;AAAA;AAChD,cAAM,SAAS,OAAO;AACtB,YAAI,QAAQ;AACV,kBAAQ,KAAK,OAAO,MAAM,KAAK;AAC/B,iBAAO,SAAQ;AAAA,QACjB,OAAO;AACL,kBAAQ,OAAO;AAAA,QACjB;AAAA,MACF;AAEA,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,KAAK;AAChB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAI;AACZ,eAAO,IAAI,MAAM,0BAA0B,CAAC;AAC5C;AAAA,MACF;AACA,YAAM,cAAc,KAAK,GAAG,YAAY,CAAC,KAAK,SAAS,GAAG,WAAW;AACrE,YAAM,cAAc,YAAY,YAAY,KAAK,SAAS;AAE1D,YAAM,UAAU,YAAY,OAAO,GAAG;AAEtC,cAAQ,YAAY,MAAM,QAAQ,MAAS;AAC3C,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ;AACZ,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAI;AACZ,eAAO,IAAI,MAAM,0BAA0B,CAAC;AAC5C;AAAA,MACF;AACA,YAAM,cAAc,KAAK,GAAG,YAAY,CAAC,KAAK,SAAS,GAAG,WAAW;AACrE,YAAM,cAAc,YAAY,YAAY,KAAK,SAAS;AAE1D,YAAM,UAAU,YAAY,MAAK;AAEjC,cAAQ,YAAY,MAAM,QAAQ,MAAS;AAC3C,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ;AACZ,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAK;AACb,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AACF;"}
1
+ {"version":3,"file":"indexeddb-storage-Ko4sjZtM.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-B7ERi4ng.js";
1
+ import { aV as PersistentStorage } from "./index-CHfVBEUH.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-CSB5_IfU.js.map
91
+ //# sourceMappingURL=memory-storage-BTfQQ9Ig.js.map
@@ -1 +1 @@
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
+ {"version":3,"file":"memory-storage-BTfQQ9Ig.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-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
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./index-Dgx5DQG_.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-C6YV3dsT.cjs.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"memory-storage-C6YV3dsT.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-alpha17",
3
+ "version": "2.0.0-alpha19",
4
4
  "description": "Holonic geospatial communication infrastructure combining H3 hexagonal indexing with distributed P2P storage",
5
5
  "type": "module",
6
6
  "bin": {
@@ -91,12 +91,18 @@ export async function getFederationRegistry(client, appname, options = {}) {
91
91
  const registry = await readGlobal(client, appname, FEDERATION_TABLE, client.publicKey) || {
92
92
  id: client.publicKey,
93
93
  federatedWith: [],
94
+ capabilityWriters: [],
94
95
  discoveryEnabled: false,
95
96
  autoAccept: false,
96
97
  defaultScope: { holonId: '*', lensName: '*' },
97
98
  defaultPermissions: ['read'],
98
99
  };
99
100
 
101
+ // Ensure capabilityWriters array exists (for registries created before this field)
102
+ if (!registry.capabilityWriters) {
103
+ registry.capabilityWriters = [];
104
+ }
105
+
100
106
  // Build partner index and cache
101
107
  const partnerIndex = buildPartnerIndex(registry.federatedWith || []);
102
108
  registryCache.set(cacheKey, {
@@ -230,6 +236,70 @@ export async function getFederatedAuthors(client, appname) {
230
236
  return registry.federatedWith.map(p => p.pubKey);
231
237
  }
232
238
 
239
+ // ============================================================================
240
+ // Capability Writers (External writers registered via app-level authorization)
241
+ // ============================================================================
242
+
243
+ /**
244
+ * Add a capability writer — an external pubkey authorized to write to this holon
245
+ * via app-level capability tokens (e.g., QR codes). Their pubkey is recorded so
246
+ * their data is included in future read queries.
247
+ *
248
+ * @param {Object} client - NostrClient instance
249
+ * @param {string} appname - Application namespace
250
+ * @param {string} writerPubKey - Writer's public key to register
251
+ * @returns {Promise<boolean>} Success indicator
252
+ */
253
+ export async function addCapabilityWriter(client, appname, writerPubKey) {
254
+ const registry = await getFederationRegistry(client, appname);
255
+
256
+ // Deduplicate
257
+ const exists = registry.capabilityWriters.some(w => w.pubKey === writerPubKey);
258
+ if (exists) {
259
+ return true; // Already registered
260
+ }
261
+
262
+ registry.capabilityWriters.push({
263
+ pubKey: writerPubKey,
264
+ addedAt: Date.now(),
265
+ });
266
+
267
+ return saveFederationRegistry(client, appname, registry);
268
+ }
269
+
270
+ /**
271
+ * Get all capability writer public keys.
272
+ *
273
+ * @param {Object} client - NostrClient instance
274
+ * @param {string} appname - Application namespace
275
+ * @returns {Promise<string[]>} Array of capability writer pubkeys
276
+ */
277
+ export async function getCapabilityWriters(client, appname) {
278
+ const registry = await getFederationRegistry(client, appname);
279
+ return (registry.capabilityWriters || []).map(w => w.pubKey);
280
+ }
281
+
282
+ /**
283
+ * Remove a capability writer.
284
+ *
285
+ * @param {Object} client - NostrClient instance
286
+ * @param {string} appname - Application namespace
287
+ * @param {string} writerPubKey - Writer's public key to remove
288
+ * @returns {Promise<boolean>} Success indicator
289
+ */
290
+ export async function removeCapabilityWriter(client, appname, writerPubKey) {
291
+ const registry = await getFederationRegistry(client, appname);
292
+
293
+ const initialLength = registry.capabilityWriters.length;
294
+ registry.capabilityWriters = registry.capabilityWriters.filter(w => w.pubKey !== writerPubKey);
295
+
296
+ if (registry.capabilityWriters.length === initialLength) {
297
+ return false; // Writer not found
298
+ }
299
+
300
+ return saveFederationRegistry(client, appname, registry);
301
+ }
302
+
233
303
  /**
234
304
  * Get a specific federated partner (O(1) lookup via index)
235
305
  * @param {Object} client - NostrClient instance
package/src/index.js CHANGED
@@ -382,7 +382,24 @@ class HoloSphereBase extends HoloSphereCore {
382
382
  const capToken = options.capabilityToken || options.capability;
383
383
  const actingAsHolon = options.actingAs || options.actingAsHolon;
384
384
 
385
- if (!isOwnHolon) {
385
+ if (options.externalWriter) {
386
+ // App-level authorization already validated (e.g., QR capability token).
387
+ // Skip holosphere-level auth checks, but register this writer's pubkey
388
+ // so their data is included in future read queries.
389
+ const writerPubKey = effectiveWriterPubKey;
390
+ if (writerPubKey && writerPubKey !== holonId) {
391
+ try {
392
+ await registry.addCapabilityWriter(this.client, this.config.appName, writerPubKey);
393
+ this._log('DEBUG', '✅ Registered capability writer', {
394
+ writerPubKey: writerPubKey.slice(0, 12) + '...',
395
+ holonId: holonId.slice(0, 12) + '...'
396
+ });
397
+ } catch (err) {
398
+ this._log('WARN', '⚠️ Failed to register capability writer', { error: err.message });
399
+ }
400
+ }
401
+ // Fall through to actual write below (skip all auth checks)
402
+ } else if (!isOwnHolon) {
386
403
  // Writing to non-own holon - check authorization
387
404
  // Priority: 1. Explicit capability token, 2. Membership-based access
388
405
 
@@ -875,21 +892,22 @@ class HoloSphereBase extends HoloSphereCore {
875
892
  }
876
893
 
877
894
  /**
878
- * Recursively resolves holograms (references) to their source data.
879
- * Handles circular reference detection and local overrides.
895
+ * Resolves holograms (references) to their source data.
896
+ * Iterates arrays and filters out invalid entries; delegates individual
897
+ * hologram resolution (including capability verification and recursive
898
+ * chain resolution) to federation.resolveHologram().
880
899
  *
881
900
  * @private
882
901
  * @param {Object|Array|null} data - Data that may contain holograms
883
- * @param {Set<string>} [visited=new Set()] - Set of visited paths for circular detection
884
902
  * @returns {Promise<Object|Array|null>} Resolved data with holograms replaced by source data
885
903
  */
886
- async _resolveHolograms(data, visited = new Set()) {
904
+ async _resolveHolograms(data) {
887
905
  if (!data) return data;
888
906
 
889
907
  if (Array.isArray(data)) {
890
908
  const resolved = [];
891
909
  for (const item of data) {
892
- const resolvedItem = await this._resolveHolograms(item, new Set());
910
+ const resolvedItem = await this._resolveHolograms(item);
893
911
  if (resolvedItem !== null) {
894
912
  resolved.push(resolvedItem);
895
913
  }
@@ -897,81 +915,13 @@ class HoloSphereBase extends HoloSphereCore {
897
915
  return resolved;
898
916
  }
899
917
 
900
- if (data && typeof data === 'object') {
901
- if (data.hologram === true && data.target) {
902
- const sourcePath = storage.buildPath(
903
- data.target.appname || this.config.appName,
904
- data.target.holonId,
905
- data.target.lensName,
906
- data.target.dataId
907
- );
908
- this._log('DEBUG', 'resolving hologram', {
909
- hologramId: data.id,
910
- sourcePath,
911
- targetHolon: data.target.holonId,
912
- targetLens: data.target.lensName,
913
- targetDataId: data.target.dataId
914
- });
915
-
916
- // Circular reference detection
917
- if (visited.has(sourcePath)) {
918
- this._log('WARN', 'Circular hologram reference detected', { sourcePath });
919
- return null;
920
- }
921
- visited.add(sourcePath);
922
-
923
- let resolveOptions = {};
924
- if (data.target.authorPubKey) {
925
- resolveOptions.authors = [data.target.authorPubKey];
926
- resolveOptions.includeAuthor = true;
927
- }
928
-
929
- const sourceData = await storage.read(this.client, sourcePath, resolveOptions);
930
- this._log('DEBUG', 'hologram source fetched', { found: !!sourceData, sourcePath });
931
- if (sourceData) {
932
- // If source is also a hologram, recursively resolve it
933
- let resolvedSource = sourceData;
934
- if (sourceData.hologram === true && sourceData.target) {
935
- resolvedSource = await this._resolveHolograms(sourceData, visited);
936
- if (resolvedSource === null) {
937
- return null; // Circular reference or unresolvable
938
- }
939
- }
940
-
941
- // Get local override fields from the hologram (excluding hologram structure fields)
942
- const hologramStructureFields = ['hologram', 'soul', 'target', '_meta', 'id', 'capability', 'crossHolosphere'];
943
- const localOverrides = {};
944
- for (const [k, v] of Object.entries(data)) {
945
- if (!hologramStructureFields.includes(k)) {
946
- localOverrides[k] = v;
947
- }
948
- }
949
-
950
- const merged = {
951
- ...resolvedSource,
952
- ...localOverrides,
953
- _hologram: {
954
- isHologram: true,
955
- soul: data.soul,
956
- sourceHolon: data.target.holonId,
957
- source: data.target,
958
- localOverrides: Object.keys(localOverrides),
959
- crossHolosphere: data.crossHolosphere || false,
960
- }
961
- };
962
-
963
- // Preserve source _meta but add hologram source info
964
- if (resolvedSource._meta) {
965
- merged._meta = { ...resolvedSource._meta, source: data.target.holonId };
966
- } else {
967
- merged._meta = { source: data.target.holonId };
968
- }
969
-
970
- return merged;
971
- }
972
- return null; // Source not found
973
- }
918
+ if (data?.hologram === true && data.target) {
919
+ return federation.resolveHologram(this.client, data, new Set(), [], {
920
+ appname: this.config.appName,
921
+ deleteCircular: false,
922
+ });
974
923
  }
924
+
975
925
  return data;
976
926
  }
977
927
 
@@ -1098,7 +1048,7 @@ class HoloSphereBase extends HoloSphereCore {
1098
1048
  this.config.appName
1099
1049
  );
1100
1050
  const fedAuthorsTimeout = new Promise((resolve) =>
1101
- setTimeout(() => resolve([]), 5000) // 5 second timeout, resolve with empty array
1051
+ setTimeout(() => resolve([]), 1000) // 1 second timeout, resolve with empty array
1102
1052
  );
1103
1053
  const federatedAuthors = await Promise.race([fedAuthorsPromise, fedAuthorsTimeout]);
1104
1054
  this._log('DEBUG', '✅ Got federated authors', { count: federatedAuthors?.length || 0 });
@@ -1110,6 +1060,22 @@ class HoloSphereBase extends HoloSphereCore {
1110
1060
  } catch (err) {
1111
1061
  this._log('WARN', 'Failed to get federated authors', { error: err.message });
1112
1062
  }
1063
+
1064
+ // Also include capability writers — external users who wrote via app-level authorization
1065
+ try {
1066
+ const capWritersPromise = registry.getCapabilityWriters(this.client, this.config.appName);
1067
+ const capWritersTimeout = new Promise((resolve) =>
1068
+ setTimeout(() => resolve([]), 1000)
1069
+ );
1070
+ const capabilityWriters = await Promise.race([capWritersPromise, capWritersTimeout]);
1071
+ if (capabilityWriters.length > 0) {
1072
+ const existing = readOptions.authors || [this.client.publicKey];
1073
+ readOptions.authors = [...new Set([...existing, ...capabilityWriters])];
1074
+ this._log('DEBUG', 'Including capability writers', { count: capabilityWriters.length });
1075
+ }
1076
+ } catch (err) {
1077
+ this._log('WARN', 'Failed to get capability writers', { error: err.message });
1078
+ }
1113
1079
  }
1114
1080
 
1115
1081
  const startTime = Date.now();
@@ -2311,17 +2277,18 @@ class HoloSphereBase extends HoloSphereCore {
2311
2277
  throw new TypeError('callback must be a function');
2312
2278
  }
2313
2279
  const path = storage.buildPath(this.config.appName, holonId, lensName);
2314
- const subscriptionOptions = { realtimeOnly: true, ...options };
2280
+ const subscriptionOptions = { realtimeOnly: true, appname: this.config.appName, ...options };
2315
2281
  const subscriptionId = `${holonId}-${lensName}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2316
2282
  let innerSubscription = null;
2317
2283
  let unsubscribeCalled = false;
2318
2284
 
2319
2285
  (async () => {
2320
2286
  try {
2321
- // Authorization check — mirrors read() logic
2322
- const targetPubkey = await this._resolveHolonToPubkey(holonId);
2323
- const isOtherAuthor = targetPubkey && targetPubkey !== this.client.publicKey;
2287
+ // Quick sync check: 64-char hex pubkeys different from ours are clearly other-author
2288
+ const isPubkeyHolon = typeof holonId === 'string' && /^[0-9a-f]{64}$/i.test(holonId);
2289
+ const isOtherAuthor = isPubkeyHolon && holonId !== this.client.publicKey;
2324
2290
 
2291
+ // For other-author subscriptions, do authorization check before creating subscription
2325
2292
  if (isOtherAuthor) {
2326
2293
  const capToken = options.capabilityToken || options.capability;
2327
2294
  if (capToken) {
@@ -2331,29 +2298,52 @@ class HoloSphereBase extends HoloSphereCore {
2331
2298
  return;
2332
2299
  }
2333
2300
  } else {
2334
- const capability = await this._getCapabilityForAuthor(targetPubkey, { holonId, lensName });
2301
+ const capability = await this._getCapabilityForAuthor(holonId, { holonId, lensName });
2335
2302
  if (!capability) {
2336
2303
  this._log('WARN', '❌ Subscribe: no capability for federated author - skipping', {
2337
- holonId, lensName, targetPubkey: targetPubkey?.slice(0, 12) + '...'
2304
+ holonId, lensName, targetPubkey: holonId?.slice(0, 12) + '...'
2338
2305
  });
2339
2306
  return;
2340
2307
  }
2341
2308
  this._log('DEBUG', '✅ Subscribe: capability found for federated author', {
2342
- holonId, lensName, targetPubkey: targetPubkey?.slice(0, 12) + '...'
2309
+ holonId, lensName, targetPubkey: holonId?.slice(0, 12) + '...'
2343
2310
  });
2344
2311
  }
2345
2312
  }
2346
2313
 
2347
2314
  if (unsubscribeCalled) return;
2348
2315
 
2316
+ // Create subscription immediately with own public key as author filter
2317
+ subscriptionOptions.authors = [this.client.publicKey];
2318
+
2349
2319
  const subscription = await subscriptions.createSubscription(
2350
2320
  this.client, path, callback, subscriptionOptions
2351
2321
  );
2352
2322
  innerSubscription = subscription;
2353
2323
  if (unsubscribeCalled) {
2354
2324
  subscription.unsubscribe();
2355
- } else {
2356
- this.subscriptionRegistry.register(subscriptionId, subscription);
2325
+ return;
2326
+ }
2327
+ this.subscriptionRegistry.register(subscriptionId, subscription);
2328
+
2329
+ // Post-setup enhancement: include federated authors
2330
+ // Own-data subscription is already active; this adds partner visibility
2331
+ if (!isOtherAuthor) {
2332
+ try {
2333
+ const fedAuthorsPromise = registry.getFederatedAuthors(
2334
+ this.client,
2335
+ this.config.appName
2336
+ );
2337
+ const fedAuthorsTimeout = new Promise((resolve) =>
2338
+ setTimeout(() => resolve([]), 1000)
2339
+ );
2340
+ const federatedAuthors = await Promise.race([fedAuthorsPromise, fedAuthorsTimeout]);
2341
+ if (federatedAuthors.length > 0) {
2342
+ this._log('DEBUG', 'Subscribe: federated authors found post-setup', { count: federatedAuthors.length });
2343
+ }
2344
+ } catch (err) {
2345
+ this._log('WARN', 'Failed to get federated authors for subscription', { error: err.message });
2346
+ }
2357
2347
  }
2358
2348
  } catch (err) {
2359
2349
  this._log('ERROR', 'Subscription setup failed', { path, error: err.message });
@@ -878,8 +878,11 @@ export async function nostrSubscribeMany(client, pathPrefix, callback, options =
878
878
  const pathParts = pathPrefix.split('/');
879
879
  const targetAuthor = pathParts[1] || client.publicKey;
880
880
 
881
- // Create unique key for this subscription (use target author, not client pubkey)
882
- const subscriptionKey = `${targetAuthor}:${kind}:${pathPrefix}`;
881
+ // Use provided authors list (includes federated partners) or fall back to target author only
882
+ const allAuthors = options.authors || [targetAuthor];
883
+
884
+ // Create unique key for this subscription (include all authors to avoid conflicts)
885
+ const subscriptionKey = `${allAuthors.join(',')}:${kind}:${pathPrefix}`;
883
886
 
884
887
  // Check if we already have an active subscription for this path
885
888
  const existingSub = globalSubscriptions.get(subscriptionKey);
@@ -921,8 +924,8 @@ export async function nostrSubscribeMany(client, pathPrefix, callback, options =
921
924
  }
922
925
  seenEventIds.add(event.id);
923
926
 
924
- // Check if event is from a different author (relay not respecting filter)
925
- if (event.pubkey !== targetAuthor) {
927
+ // Check if event is from an allowed author (relay not respecting filter)
928
+ if (!allAuthors.includes(event.pubkey)) {
926
929
  rejectedCount++;
927
930
 
928
931
  // Only log periodically to avoid spam
@@ -931,7 +934,7 @@ export async function nostrSubscribeMany(client, pathPrefix, callback, options =
931
934
  console.warn('[nostrSubscribeMany] ⚠️ Relay not respecting authors filter!', {
932
935
  rejectedCount,
933
936
  acceptedCount,
934
- expected: targetAuthor,
937
+ expectedAuthors: allAuthors.length,
935
938
  message: 'Consider using a different relay or implementing private relay'
936
939
  });
937
940
  lastLogTime = now;
@@ -966,13 +969,14 @@ export async function nostrSubscribeMany(client, pathPrefix, callback, options =
966
969
 
967
970
  // Subscribe to data events (kind 30000) for initial load
968
971
  // Use 'since' to only get events from subscription time onward for real-time updates
969
- const subscriptionStartTime = Math.floor(Date.now() / 1000);
972
+ // Subtract 2 seconds to capture events written during async subscription setup
973
+ // (the subscribe() IIFE in index.js does auth/federation lookups before reaching here)
974
+ const subscriptionStartTime = Math.floor(Date.now() / 1000) - 2;
970
975
  const dataFilter = {
971
976
  kinds: [kind],
972
- authors: [targetAuthor], // Use target author (holon ID), not client pubkey
977
+ authors: allAuthors, // Include target author + federated partners
973
978
  since: subscriptionStartTime // Only get new events after subscription
974
979
  };
975
- console.log('[nostrSubscribeMany] Subscribing to:', { pathPrefix, targetAuthor: targetAuthor.slice(0, 12), clientPubKey: client.publicKey.slice(0, 12) });
976
980
  const dataSubscription = await client.subscribe(dataFilter, handleDataEvent);
977
981
 
978
982
  // Use Page Visibility API to refresh when tab becomes visible