@verdant-web/store 4.1.0-alpha.3 → 4.1.0-alpha.5

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.
@@ -10,5 +10,5 @@ list should be an Array.`),c.length===0)return new y(0);if(c.length===1)return c
10
10
  - Changed collections: ${t.changedCollections.join(", ")}
11
11
  - New indexes: ${Object.keys(t.addedIndexes).map(a=>t.addedIndexes[a].map(f=>`${a}.${f.name}`)).flatMap(a=>a).join(", ")}
12
12
  - Removed indexes: ${Object.keys(t.removedIndexes).map(a=>t.removedIndexes[a].map(f=>`${a}.${f.name}`)).flatMap(a=>a).join(", ")}
13
- `)}i.pauseRebasing=!1}async function At(i){let n=i;if(i.schema.wip&&(n.namespace=Hs(i.originalNamespace,i.schema),n.log("info","Switched to WIP namespace",n.namespace),!(await n.persistence.getNamespaces()).includes(n.namespace))){let r=await n.persistence.getNamespaceVersion(n.originalNamespace);if(r===0)n.log("debug","No existing data to copy to WIP namespace");else{let s=i.oldSchemas?.find(o=>o.version===r);if(!s)throw new X(X.Code.MigrationPathNotFound,void 0,`Trying to open WIP database for version ${i.schema.version}, but the current local data is version ${r} and a historical schema for that version is not available.`);n.log("info",`Copying data from ${n.originalNamespace} to ${n.namespace}`),await n.persistence.copyNamespace(n.originalNamespace,n.namespace,{...n,schema:s})}}let e=await i.persistence.openNamespace(n.namespace,n);if(n.log("info","Opening persistence metadata"),n.meta=new Mn(await e.openMetadata(i),i),n.log("info","Opening persistence files"),n.files=new kn(await e.openFiles(n),n),n.log("info","Migrating document database"),await Xs({context:n,version:i.schema.version}),n.log("info","Opening persistence documents"),n.documents=new Un(await e.openDocuments(n),n),!i.schema.wip){let t=await i.persistence.getNamespaces();for(let r of t)r.startsWith("@@wip_")&&(i.log("debug","Cleaning up old WIP namespace",r),await i.persistence.deleteNamespace(r,i))}return n}async function Zs(i,n){i.log("info","Importing data from export");let e=i.oldSchemas?.find(a=>a.version===n.data.schemaVersion);if(!e)throw new Error(`Could not find schema for version ${n.data.schemaVersion}`);let t=`@@import_${Date.now()}`,r=await At({...i,schema:e,namespace:t,originalNamespace:t,entityEvents:new N,config:{...i.config,persistence:{...i.config.persistence,disableRebasing:!0}},persistenceShutdownHandler:new Re});await r.meta.resetFrom(n.data);let s=new Set;for(let a of n.data.baselines)s.add(z(a.oid));for(let a of n.data.operations)s.add(z(a.oid));let o=await Promise.all(Array.from(s).map(async a=>{let f=await r.meta.getDocumentSnapshot(a);return{oid:a,getSnapshot:()=>f}}));if(await r.documents.saveEntities(o),await r.files.import(n),i.log("debug","Imported data into temporary namespace",t),await r.persistenceShutdownHandler.shutdown(),e.version!==i.schema.version){let a=i.schema,f=await At({...r,persistenceShutdownHandler:new Re,schema:a});i.log("debug","Upgraded imported data to current schema"),await f.persistenceShutdownHandler.shutdown(),i.log("debug","Shut down upgraded databases")}if(await i.persistenceShutdownHandler.shutdown(),await i.persistence.copyNamespace(t,i.namespace,i),await At(i),n.data.schemaVersion===i.schema.version){let a=await i.meta.stats();if(a.operationsSize.count!==n.data.operations.length)throw i.log("critical","Imported operations count mismatch","expected",n.data.operations.length,"actual",a.operationsSize.count),new X(X.Code.ImportFailed,void 0,"Imported operations count mismatch");if(a.baselinesSize.count!==n.data.baselines.length)throw i.log("critical","Imported documents count mismatch","expected",n.data.baselines.length,"actual",a.baselinesSize.count),new X(X.Code.ImportFailed,void 0,"Imported documents count mismatch")}i.log("debug","Data copied to primary namespace"),await i.persistence.deleteNamespace(t,i),i.log("debug","Deleted temporary namespace"),i.internalEvents.emit("persistenceReset"),i.log("info","Data imported successfully"),i.persistenceShutdownHandler.reset()}var ft=class extends N{constructor(e){super();this.context=e;this.importingPromise=Promise.resolve();this.addData=async e=>{await this.importingPromise;try{let t=e.reset?Ks(e):this.schema.version;return t<this.schema.version?(this.context.log("warn","Incoming reset sync data is from an old schema version",t,`(current ${this.schema.version})`),await this.import({data:{operations:e.operations,baselines:e.baselines??[],localReplica:void 0,schemaVersion:t},fileData:[],files:[]})):await this._entities.addData(e)}catch(t){throw this.context.log("critical","Sync failed",t),this.emit("developerError",new Error("Sync failed, see logs or cause",{cause:t})),t}};this.stats=async()=>{if(this.disposed)return{};let e=await this.context.documents.stats(),t=await this.context.meta.stats(),r=typeof navigator<"u"&&typeof navigator.storage<"u"&&"estimate"in navigator.storage?await navigator.storage.estimate():void 0,s=await this.context.files.stats(),o=Object.values(e).reduce((p,{size:m})=>p+m,0),a=t.baselinesSize.size+t.operationsSize.size,f=a/o;return{collections:e,meta:t,storage:r,totalMetaSize:a,totalCollectionsSize:o,metaToDataRatio:f,files:s,quotaUsage:r?.usage&&r?.quota?r.usage/r.quota:void 0}};this.close=async()=>{this.sync.ignoreIncoming(),await this._entities.flushAllBatches(),this.context.closing=!0,this.context.closeLock&&await this.context.closeLock,this.sync.stop(),this.sync.destroy(),await this._entities.destroy(),this.context.persistenceShutdownHandler.shutdown(),await new Promise(e=>{e()}),this.context.log?.("info","Client closed")};this.__dangerous__resetLocal=async()=>{this.sync.stop(),await bn(this.namespace,indexedDB)};this.export=async({downloadRemoteFiles:e}={downloadRemoteFiles:!0})=>{this.context.log("info","Exporting data...");let t=await this.context.meta.export(),{fileData:r,files:s}=await this.context.files.export(e);return{data:t,fileData:r,files:s}};this.import=async({data:e,fileData:t,files:r})=>{let s=()=>{};this.importingPromise=new Promise(o=>{s=o}),this.context.log("info","Importing data..."),await Zs(this.context,{data:e,files:r,fileData:t}),s()};this.__dangerous__hardReset=async()=>{let e=await this.export();await this.import(e)};this.__cleanupFilesImmediately=()=>this.context.files.cleanupDeletedFiles();this.__manualRebase=()=>(this.context.meta.manualRebase(),new Promise(e=>{let t=this.subscribe("rebase",()=>{t(),e()})}));this.collectionNames=Object.keys(e.schema.collections),this._sync=this.context.config.sync&&!e.schema.wip?new ut(this.context.config.sync,{onData:this.addData,ctx:this.context}):new Bn(this.context),e.schema.wip&&this.context.config.sync&&e.log("warn","\u26A0\uFE0F\u26A0\uFE0F Sync is disabled for WIP schemas. Commit your schema changes to start syncing again. \u26A0\uFE0F\u26A0\uFE0F"),this._fileManager=new yn({sync:this.sync,context:this.context}),this._entities=new mn({ctx:this.context,files:this._fileManager}),this._queryCache=new En({context:e}),this._documentManager=new tn(this.schema,this._entities);let t=kr(()=>{this.emit("futureSeen")},300);this.context.globalEvents.subscribe("futureSeen",t),this.context.globalEvents.subscribe("resetToServer",()=>{this.emit("resetToServer")}),this.context.globalEvents.subscribe("operation",r=>{this.emit("operation",r)}),this.context.globalEvents.subscribe("rebase",()=>{this.emit("rebase")}),this.context.globalEvents.subscribe("fileSaved",r=>{this.emit("fileSaved",r)});for(let[r,s]of Object.entries(e.schema.collections)){let o=r;this[o]=new _n({collection:o,cache:this._queryCache,context:this.context,entities:this.entities,documentManager:this.documentManager})}}get sync(){return this._sync}get entities(){return this._entities}get documentManager(){return this._documentManager}get schema(){return this.context.schema}get namespace(){return this.context.namespace}get undoHistory(){return this.context.undoHistory}get batch(){return this.entities.batch}get __persistence(){return{meta:this.context.meta,queries:this.context.documents,files:this.context.files}}};var Vn=class{constructor(n){this.value=n}deref(){return this.value}};var Nn=class{constructor(n,e){this.base=n;this.version=e;this.withMigrationTime=async(n,e)=>{this.overrideNow=()=>this.base.zero(n),await e(),this.overrideNow=void 0};this.update=this.base.update.bind(this.base);this.nowWithVersion=n=>this.base.now(n);this.zeroWithVersion=n=>this.base.zero(n)}get now(){return this.overrideNow?this.overrideNow():this.base.now(this.version)}get zero(){return this.base.zero(this.version)}};var Ve=class extends fe{constructor(e,{log:t}={}){super();this.db=e;this.globalAbortController=new AbortController;this.createTransaction=(e,t)=>{try{if(this.globalAbortController.signal.aborted)throw new Error("Global abort signal is already aborted");let r=Ms(this.db,e,t?.mode||"readonly",t?.abort,this.log);return this.globalAbortController.signal.addEventListener("abort",r.abort),r.addEventListener("complete",()=>{this.globalAbortController.signal.removeEventListener("abort",r.abort)}),r.addEventListener("error",()=>{this.globalAbortController.signal.removeEventListener("abort",r.abort)}),r}catch(r){throw this.log?.("error","Failed to create abortable transaction for store names",e,r),r}};this.run=async(e,t,r)=>{if(this.disposed||r?.transaction?.error)return Promise.resolve(void 0);let o=(r?.transaction||this.createTransaction([e],r)).objectStore(e),a=t(o);return ct(a)};this.runAll=async(e,t,r)=>{if(this.disposed||r?.transaction?.error)return Promise.resolve([]);let o=(r?.transaction||this.createTransaction([e],r)).objectStore(e),a=t(o);return Promise.all(a.map(ct))};this.iterate=async(e,t,r,s)=>{let a=(s?.transaction||this.createTransaction([e],s)).objectStore(e),f=t(a);return Array.isArray(f)?Promise.all(f.map(p=>new Promise((m,_)=>{p.onsuccess=()=>{let w=p.result;w?r(w.value,a,w)?m():w.continue():m()},p.onerror=()=>{p.error&&Je(p.error)?m():_(p.error)}}))).then(()=>{}):new Promise((p,m)=>{f.onsuccess=()=>{let _=f.result;_?r(_.value,a,_)?p():_.continue():p()},f.onerror=()=>{f.error&&Je(f.error)?p():m(f.error)}})};this.clear=(e,t)=>this.run(e,r=>r.clear(),{mode:"readwrite",transaction:t});this.onVersionChange=e=>{if(this.log?.("warn",`Another tab has requested a version change for ${this.db.name}`),this.db.close(),typeof window<"u")try{window.location.reload()}catch(t){this.log?.("error","Failed to reload the page",t)}};this.log=t,this.addDispose(()=>{if(!this.globalAbortController.signal.aborted)try{this.globalAbortController.abort()}catch(r){if(r instanceof Error&&r.message.includes("invocation"))return;console.error("Error aborting global controller",r)}}),this.db.addEventListener("versionchange",this.onVersionChange),this.addDispose(()=>{this.db.removeEventListener("versionchange",this.onVersionChange)})}};var zn=class extends Ve{constructor(){super(...arguments);this.add=async e=>{let t=e.file?await Ua(e.file):void 0;await this.run("files",r=>r.put({id:e.id,remote:e.remote?"true":"false",deletedAt:null,name:e.name,type:e.type,url:e.url,buffer:t,timestamp:e.timestamp}),{mode:"readwrite"})};this.markUploaded=async e=>{let t=await this.getFileRaw(e);if(!t)throw new Error("File is not in local database");await this.run("files",r=>r.put({...t,remote:"true"}),{mode:"readwrite"})};this.get=async e=>{let t=await this.getFileRaw(e);return t?this.hydrateFileData(t):null};this.delete=e=>this.run("files",t=>t.delete(e),{mode:"readwrite"});this.markPendingDelete=async e=>{let t=await this.getFileRaw(e);if(!t)throw new Error("File is not in local database");await this.run("files",r=>r.put({...t,deletedAt:Date.now()}),{mode:"readwrite"})};this.listUnsynced=async()=>(await this.run("files",t=>t.index("remote").getAll("false"),{mode:"readonly"})).map(this.hydrateFileData);this.resetSyncedStatusSince=async e=>{let t=this.createTransaction(["files"],{mode:"readwrite"}),s=(await this.run("files",o=>o.index("remote").getAll("true"),{transaction:t})).filter(o=>!o.timestamp||!e||o.timestamp>e);await Promise.all(s.map(o=>this.run("files",a=>a.put({...o,remote:"false"}),{transaction:t})))};this.iterateOverPendingDelete=e=>this.iterate("files",t=>t.index("deletedAt").openCursor(IDBKeyRange.lowerBound(0,!0)),(t,r)=>{e(this.hydrateFileData(t),r)},{mode:"readwrite"});this.getAll=async e=>{let[t]=await Vi(this.db,["files"]);return t.map(this.hydrateFileData)};this.stats=async()=>({size:await Ge(this.db,"files")});this.loadFileContents=async(e,t)=>{if(e.file)return e.file;if(e.localPath)throw new Error("Local file paths are not supported in browser");if(e.url){let r=await t.environment.fetch(e.url);if(!r.ok)throw new Error(`Failed to download file: ${r.statusText}`);return r.blob()}throw new Error("File is missing url, file, and localPath")};this.hydrateFileData=e=>{e.remote=e.remote==="true";let t=e.buffer;return delete e.buffer,e.file=t?Ma(t,e.type):void 0,e};this.getFileRaw=async(e,{transaction:t}={})=>{let r=await this.run("files",s=>s.get(e),{mode:"readonly",transaction:t});if(r)return r}}};function Ma(i,n){return new Blob([i],{type:n})}function Ua(i){return"__testReadBuffer"in i?Promise.resolve(i.__testReadBuffer):new Promise((n,e)=>{let t=new FileReader;t.onload=()=>{n(t.result)},t.onerror=e,t.readAsArrayBuffer(i)})}var $n=class extends Ve{constructor(e,t){super(e,t);this.ctx=t;this.transaction=async(e,t)=>{let r=this.createTransaction(e.storeNames,{mode:e.mode,abort:e.abort});return await t(r)};this.getAckInfo=async()=>{let e=await this.run("info",t=>t.get("ack"));return e||{globalAckTimestamp:null}};this.setGlobalAck=async e=>{await this.run("info",t=>t.put({type:"ack",globalAckTimestamp:e}),{mode:"readwrite"})};this.getLocalReplica=async e=>this.run("info",t=>t.get("localReplicaInfo"),e);this.updateLocalReplica=async(e,t)=>{try{await this.run("info",r=>r.put({...e,type:"localReplicaInfo"}),{mode:"readwrite",transaction:t?.transaction})}catch(r){throw this.ctx.log("critical","Error updating local replica",e,r),r}};this.iterateDocumentBaselines=async(e,t,r)=>{await this.iterate("baselines",s=>{let o=z(e),[a,f]=Pr(e),[p,m]=Mr(e);return[s.openCursor(IDBKeyRange.only(o)),s.openCursor(IDBKeyRange.bound(a,f,!1,!1)),s.openCursor(IDBKeyRange.bound(p,m,!1,!1))]},t,r)};this.iterateCollectionBaselines=async(e,t,r)=>{await this.iterate("baselines",s=>[s.openCursor(IDBKeyRange.bound(e,e+"\uFFFF",!1,!1))],t,r)};this.iterateAllBaselines=async(e,t)=>{await this.iterate("baselines",r=>r.index("timestamp").openCursor(),e,t)};this.getBaseline=(e,t)=>this.run("baselines",r=>r.get(e),t);this.setBaselines=async(e,t=Qi)=>{await this.runAll("baselines",r=>e.map(s=>r.put(s)),t)};this.deleteBaseline=async(e,t=Qi)=>{await this.run("baselines",r=>r.delete(e),t)};this.iterateDocumentOperations=(e,t,r)=>this.iterate("operations",s=>{let o=s.index("d_t"),a=Ae(e),f=r?.to?we(e,r.to):Me(e),p=IDBKeyRange.bound(a,f,!1,!1);return o.openCursor(p)},t,r);this.iterateEntityOperations=(e,t,r)=>this.iterate("operations",s=>{let o=Ae(e),a=r?.to?we(e,r.to):Me(e),f=IDBKeyRange.bound(o,a,!1,!1);return s.openCursor(f)},t,r);this.deleteEntityOperations=(e,t)=>this.iterate("operations",r=>{let s=Ae(e),o=t?.to?we(e,t.to):Me(e),a=IDBKeyRange.bound(s,o,!1,!1);return r.openCursor(a)},(r,s)=>{s.delete(r.oid_timestamp)},t);this.iterateCollectionOperations=(e,t,r)=>this.iterate("operations",s=>s.openCursor(IDBKeyRange.bound(e,e+"\uFFFF",!1,!1),"next"),t,r);this.iterateLocalOperations=(e,t)=>this.iterate("operations",r=>{let s=t?.after?we(!0,t.after):Ae(!0),o=Me(!0);return r.index("l_t").openCursor(IDBKeyRange.bound(s,o,!!t?.after,!1),"next")},e);this.iterateAllOperations=(e,t)=>this.iterate("operations",r=>{let s=t?.from?Ae(t.from):void 0,o=t?.before?Me(t.before):Ae(!0),a=s&&o?IDBKeyRange.bound(s,o,!1,!0):s?IDBKeyRange.lowerBound(s,!1):o?IDBKeyRange.upperBound(o,!0):void 0;return r.index("timestamp").openCursor(a,"next")},e,t);this.addOperations=async(e,t=Qi)=>{let r=new Set;return await this.runAll("operations",s=>e.map(o=>(r.add(z(o.oid)),s.put(this.addOperationIndexes(o)))),t),Array.from(r)};this.reset=async({clearReplica:e,transaction:t}={})=>{let r=t||this.createTransaction(["info","operations","baselines"],{mode:"readwrite"});await Promise.all([this.resetLocalReplica(r,e),this.resetBaselines(r),this.resetOperations(r)])};this.stats=async()=>{let e=await Ge(this.db,"operations"),t=await Ge(this.db,"baselines");return{operationsSize:e,baselinesSize:t}};this.resetLocalReplica=async(e,t=!1)=>{if(t)return this.run("info",r=>r.delete("localReplicaInfo"),{mode:"readwrite",transaction:e});{let r=await this.getLocalReplica({transaction:e});r&&(r.ackedLogicalTime=null,r.lastSyncedLogicalTime=null,await this.run("info",s=>s.put({...r,type:"localReplicaInfo"}),{mode:"readwrite",transaction:e}))}};this.resetBaselines=async e=>this.clear("baselines",e);this.resetOperations=async e=>this.clear("operations",e);this.addOperationIndexes=e=>({...e,oid_timestamp:we(e.oid,e.timestamp),l_t:we(e.isLocal,e.timestamp),d_t:we(z(e.oid),e.timestamp)});this.addDispose(()=>(this.ctx.log("info","Closing metadata DB for",this.ctx.namespace),he(e)))}},Qi={mode:"readwrite"};var eo=[Na,za,$a,Ka,Ha,qa],Va=eo.length;function Wi({indexedDB:i=window.indexedDB,namespace:n,log:e}){return new Promise((t,r)=>{let s=i.open(Pt(n),Va),o=!1;s.onupgradeneeded=async a=>{let f=s.result,p=s.transaction,m=eo.slice(a.oldVersion);for(let _ of m)await _(f,p);await new Promise((_,w)=>{p.addEventListener("complete",_),p.addEventListener("error",w)}),a.oldVersion||(o=!0)},s.onerror=()=>{console.error("Error opening database",s.error),r(s.error)},s.onsuccess=()=>{t({db:s.result,wasInitialized:o})}})}async function Na(i,n){let e=i.createObjectStore("baselines",{keyPath:"oid"}),t=i.createObjectStore("operations",{keyPath:"oid_timestamp"}),r=i.createObjectStore("info",{keyPath:"type"});e.createIndex("timestamp","timestamp"),t.createIndex("isLocal_timestamp","isLocal_timestamp"),t.createIndex("documentOid_timestamp","documentOid_timestamp")}async function za(i,n){let e=n.objectStore("operations");await new Promise((t,r)=>{let s=e.openCursor();s.onsuccess=()=>{let o=s.result;if(o){let{isLocal_timestamp:a,documentOid_timestamp:f,...p}=o.value;o.update({...p,l_t:a,d_t:f}),o.continue()}else t()},s.onerror=o=>{r(s.error)}}),e.deleteIndex("isLocal_timestamp"),e.deleteIndex("documentOid_timestamp"),e.createIndex("l_t","l_t",{unique:!1}),e.createIndex("o_t","o_t",{unique:!1}),e.createIndex("d_t","d_t",{unique:!1})}async function $a(i,n){n.objectStore("operations").createIndex("timestamp","timestamp")}async function Ka(i,n){let e=i.createObjectStore("files",{keyPath:"id"});e.createIndex("remote","remote"),e.createIndex("deletedAt","deletedAt")}async function Ha(i,n){let e=n.objectStore("operations");await new Promise((r,s)=>{let o=e.openCursor();o.onsuccess=()=>{let a=o.result;if(a){let f=ci(a.value);f.oid_timestamp!==a.primaryKey?(a.delete(),e.put(f)):a.update(f),a.continue()}else r()},o.onerror=a=>{s(o.error)}});let t=n.objectStore("baselines");await new Promise((r,s)=>{let o=t.openCursor();o.onsuccess=()=>{let a=o.result;if(a){let f=ci(a.value);f.oid!==a.primaryKey?(a.delete(),t.put(f)):a.update(f),a.continue()}else r()},o.onerror=a=>{s(o.error)}})}async function qa(i,n){n.objectStore("files").createIndex("timestamp","timestamp")}var Qa=i=>IDBKeyRange.only(ve(i.equals)),Wa=i=>{},Ja=i=>{let n=i.gte||i.gt,e=i.lte||i.lt;return n===e?IDBKeyRange.only(ve(n)):n?e?IDBKeyRange.bound(ve(n),ve(e),!!i.gt,!!i.lt):IDBKeyRange.lowerBound(ve(n),!!i.gt):IDBKeyRange.upperBound(ve(e),!!i.lt)},Ga=(i,n,e)=>{let t=i.collections[n].compounds[e.where];V(t,`Index ${e.where} does not exist on collection ${n}`);let r=Object.keys(e.match).sort((f,p)=>t.of.indexOf(f)-t.of.indexOf(p));for(let f of r)if(t.of.indexOf(f)!==r.indexOf(f))throw new Error(`Compound index ${e.where} does not have ${f} at the start of its order`);let s=r.map(f=>e.match[f]);if(r.length===t.of.length)return IDBKeyRange.only(we(...s));let o=Ae(...s),a=Me(...s);return IDBKeyRange.bound(o,a)};function Ya(i){let n=i.startsWith,e=i.startsWith+"\uFFFF";return IDBKeyRange.bound(n,e)}function Ji(i,n,e){if(e)return hi(e)?Ja(e):fi(e)?Qa(e):qr(e)?Wa(e):pi(e)?Ya(e):Ga(i,n,e)}var Kn=class extends Ve{constructor(e,t){super(e,{log:t.log});this.close=async()=>{await this.dispose()};this.stats=async()=>{let e=Object.keys(this.ctx.schema.collections),t={};return await Promise.all(e.map(async r=>{let s=await Ge(this.db,r);t[r]=s})),t};this.findOneOid=async e=>{let t=await this.run(e.collection,r=>{let s=e.index?.where?r.index(e.index.where):r,o=e.index?.order==="desc"?"prev":"next",a=Ji(this.ctx.schema,e.collection,e.index);return s.openCursor(a,o)},{mode:"readonly"});return t?ee(e.collection,t.primaryKey.toString()):null};this.findAllOids=async({collection:e,index:t,offset:r,limit:s})=>{let a=this.createTransaction([e],{mode:"readonly"}).objectStore(e),f=t?.where?a.index(t.where):a,p=t?.order==="desc"?"prev":"next",m=Ji(this.ctx.schema,e,t),_=f.openCursor(m,p),w=!1;return{result:await new Promise((y,I)=>{let D=!r,O=0,E=new Set;_.onsuccess=()=>{O++;let B=_.result;if(!B){y(Array.from(E));return}r&&!D?(B.advance(r),D=!0):((!s||E.size<s)&&E.add(ee(e,B.primaryKey.toString())),s&&O>s?(w=!0,y(Array.from(E))):B.continue())},_.onerror=()=>{_.error?.name==="InvalidStateError"?(this.ctx.log("error","find query failed with InvalidStateError",_.error),y([])):_.error&&Je(_.error)?y([]):I(_.error)}}),hasNextPage:w}};this.saveEntities=async(e,t)=>{let r={transaction:this.createTransaction(t.collections,{mode:"readwrite",abort:t.abort})};await Promise.all(e.map(async s=>{let o=s.getSnapshot();try{await this.saveDocument(s.oid,o,r)}catch(a){throw this.ctx.log("error",`Error saving document ${s.oid} (${JSON.stringify(o)})`,a),a instanceof Error?a:new Error("Unknown error saving document")}})),r.transaction.commit()};this.reset=async()=>{let e=Object.keys(this.ctx.schema.collections),t=this.createTransaction(e,{mode:"readwrite"});await Promise.all(e.map(r=>this.run(r,s=>s.clear(),{transaction:t}))),this.ctx.entityEvents.emit("collectionsChanged",e),this.ctx.log("info","\u{1F4A8} Reset queryable storage")};this.saveDocument=async(e,t,{transaction:r})=>{this.ctx.log("debug",`Saving document indexes for querying ${e}`);let{collection:s,id:o}=ae(e);if(!t)await this.run(s,a=>a.delete(o),{mode:"readwrite",transaction:r}),this.ctx.log("debug",`Deleted document indexes for querying ${e}`);else{let a=this.ctx.schema.collections[s],f=Yr(a,t);f["@@@snapshot"]=JSON.stringify(t),await this.run(s,p=>p.put(f),{mode:"readwrite",transaction:r}),this.ctx.log("debug",`Save complete for ${e}`,f)}};this.ctx=t,this.addDispose(()=>(this.ctx.log("info","Closing document database for",this.ctx.namespace),he(this.db)))}};async function to(i,n,e,t,r){r?.("debug","Upgrading database",n,"to version",e);function s(o,a){let f=i.open(lt(n),e),p=!1;f.onupgradeneeded=m=>{let _=f.transaction;t(_,f.result,m),p=!0},f.onsuccess=async m=>{p?(await he(f.result),o(f.result)):a(new Error("Database was not upgraded when a version change was expected"))},f.onerror=m=>{a(f.error||new Error("Unknown error"))},f.onblocked=m=>{r?.("Database upgrade blocked!"),a(new Error("Database upgrade blocked. The app may be open in another tab?"))}}return new Promise(s)}async function Gi({indexedDB:i=Ui,namespace:n,version:e,log:t}){t?.("debug","Opening database",n,"at version",e);let r=await Us(lt(n),e,i);return r.addEventListener("versionchange",s=>{r.close()}),r.addEventListener("close",()=>{t?.("warn","Database closed",n)}),r}var ht=class{constructor(n=window.indexedDB){this.indexedDB=n;this.name="IdbPersistence";this.getNamespaces=async()=>{let n=await this.indexedDB.databases();return Array.from(new Set(n.map(Vs).filter(e=>!!e)))};this.getNamespaceVersion=async n=>{let e=lt(n),r=(await this.indexedDB.databases()).find(s=>s.name===e);return r?r.version??0:0};this.deleteNamespace=async(n,e)=>{await Promise.all([vn(Pt(n),this.indexedDB),vn([n,"collections"].join("_"),this.indexedDB)])};this.openNamespace=async n=>new Yi(this.indexedDB,n);this.copyNamespace=async(n,e,t)=>{let r={...t,namespace:n},s={...t,namespace:e},{db:o}=await Wi({indexedDB:this.indexedDB,log:r.log,namespace:r.namespace}),a=await Gi({indexedDB:this.indexedDB,namespace:r.namespace,version:r.schema.version,log:r.log});r.log("info",`Copying data from ${r.namespace} to ${s.namespace}`),await Ni(o,Pt(s.namespace),s,this.indexedDB),await Ni(a,lt(s.namespace),s,this.indexedDB),await he(o),await he(a)}}},Yi=class{constructor(n,e){this.indexedDB=n;this.namespace=e;this.openMetadata=async n=>{let{db:e}=await Wi({indexedDB:this.indexedDB,log:n.log,namespace:this.namespace});return this.metadataDb=e,n.persistenceShutdownHandler.register(()=>he(e)),new $n(e,n)};this.openDocuments=async n=>{let e=await Gi({version:n.schema.version,indexedDB:this.indexedDB,log:n.log,namespace:this.namespace});return n.persistenceShutdownHandler.register(()=>he(e)),new Kn(e,n)};this.applyMigration=async(n,e)=>{n.log("debug","Applying migration",e.newSchema.version,e),await to(this.indexedDB,this.namespace,e.newSchema.version,(t,r)=>{for(let s of e.addedCollections)r.createObjectStore(s,{keyPath:e.newSchema.collections[s].primaryKey,autoIncrement:!1});for(let s of e.allCollections){let o=t.objectStore(s);for(let a of e.addedIndexes[s]||[])o.createIndex(a.name,a.name,{multiEntry:a.multiEntry});for(let a of e.removedIndexes[s]||[])o.deleteIndex(a.name)}for(let s of e.removedCollections)t.objectStore(s).clear()},n.log)}}openFiles(n){if(!this.metadataDb)throw new Error("Metadata database must be opened first. This is a bug in Verdant.");return Promise.resolve(new zn(this.metadataDb,n))}};var Hn=class{constructor(n){this.init=n;this._initializing=!1;this.initialize=async n=>{if(typeof window>"u"&&!n.environment)throw new Error("A Verdant client was initialized in an environment without a global Window or `environment` configuration. If you are using verdant in a server-rendered framework, you must enforce that all clients are initialized on the client-side, or you must provide some mock interface of the environment to the ClientDescriptor options.");if(this._initializing||this._resolvedValue)return this._readyPromise;this._initializing=!0;try{let e=new Nn(new kt,n.schema.version),t=n.environment||Xa,r={closing:!1,entityEvents:new N,globalEvents:new N,internalEvents:new N,log:n.log||Lr,migrations:n.migrations,namespace:n.namespace,originalNamespace:n.namespace,schema:n.schema,oldSchemas:n.oldSchemas,time:e,undoHistory:n.undoHistory||new rt,weakRef:a=>n.EXPERIMENTAL_weakRefs?new WeakRef(a):new Vn(a),patchCreator:new en(()=>e.now),config:{files:n.files,sync:n.sync,persistence:{disableRebasing:n.disableRebasing,rebaseTimeout:n.rebaseTimeout}},persistence:n.persistence||new ht(t.indexedDB),environment:t,persistenceShutdownHandler:new Re,pauseRebasing:!1};r.log("info","Initializing client",{namespace:r.namespace,version:n.schema.version,persistence:r.persistence.name});let s=await At(r),o=new ft(s);return this.resolveReady(o),this._resolvedValue=o,o}catch(e){throw e instanceof Error?(this.rejectReady(e),e):new Error("Unknown error initializing storage")}finally{this._initializing=!1}};this.open=()=>this.initialize(this.init);this.close=async()=>{this._resolvedValue&&this._resolvedValue.close(),this._initializing&&(await this._readyPromise).close()};this.__dangerous__resetLocal=async()=>{await bn(this.namespace)};this._readyPromise=new Promise((e,t)=>{this.resolveReady=e,this.rejectReady=t}),this._namespace=n.namespace}get namespace(){return this._namespace}get current(){return this._resolvedValue}get readyPromise(){return this._readyPromise}get schema(){return this.init.schema}},Xa={WebSocket:typeof WebSocket<"u"?WebSocket:void 0,fetch:typeof window<"u"?window.fetch.bind(window):fetch,indexedDB:typeof indexedDB<"u"?indexedDB:void 0};var Xi=Fe(Ne(),1);function Za(i){return i?Xi.default.slug():(0,Xi.default)()}var ec={private:qe.onlyMe(),public:void 0};function tc(i,{port:n=3242,initialPresence:e={}}={}){let t=localStorage.getItem("verdant-userId");return t||(t=`user-${Math.random().toString(36).slice(2)}`,localStorage.setItem("verdant-userId",t)),{defaultProfile:{id:t},initialPresence:e,authEndpoint:`http://localhost:${n}/auth/${i}?userId=${t}`}}window.Verdant=Zi;
13
+ `)}i.pauseRebasing=!1}async function At(i){let n=i;if(i.schema.wip&&(n.namespace=Hs(i.originalNamespace,i.schema),n.log("info","Switched to WIP namespace",n.namespace),!(await n.persistence.getNamespaces()).includes(n.namespace))){let r=await n.persistence.getNamespaceVersion(n.originalNamespace);if(r===0)n.log("debug","No existing data to copy to WIP namespace");else{let s=i.oldSchemas?.find(o=>o.version===r);if(!s)throw new X(X.Code.MigrationPathNotFound,void 0,`Trying to open WIP database for version ${i.schema.version}, but the current local data is version ${r} and a historical schema for that version is not available.`);n.log("info",`Copying data from ${n.originalNamespace} to ${n.namespace}`),await n.persistence.copyNamespace(n.originalNamespace,n.namespace,{...n,schema:s})}}let e=await i.persistence.openNamespace(n.namespace,n);if(n.log("info","Opening persistence metadata"),n.meta=new Mn(await e.openMetadata(i),i),n.log("info","Opening persistence files"),n.files=new kn(await e.openFiles(n),n),n.log("info","Migrating document database"),await Xs({context:n,version:i.schema.version}),n.log("info","Opening persistence documents"),n.documents=new Un(await e.openDocuments(n),n),!i.schema.wip){let t=await i.persistence.getNamespaces();for(let r of t)r.startsWith("@@wip_")&&(i.log("debug","Cleaning up old WIP namespace",r),await i.persistence.deleteNamespace(r,i))}return n}async function Zs(i,n){i.log("info","Importing data from export");let e=i.oldSchemas?.find(a=>a.version===n.data.schemaVersion);if(!e)throw new Error(`Could not find schema for version ${n.data.schemaVersion}`);let t=`@@import_${Date.now()}`,r=await At({...i,schema:e,namespace:t,originalNamespace:t,entityEvents:new N,config:{...i.config,persistence:{...i.config.persistence,disableRebasing:!0}},persistenceShutdownHandler:new Re});await r.meta.resetFrom(n.data);let s=new Set;for(let a of n.data.baselines)s.add(z(a.oid));for(let a of n.data.operations)s.add(z(a.oid));let o=await Promise.all(Array.from(s).map(async a=>{let f=await r.meta.getDocumentSnapshot(a);return{oid:a,getSnapshot:()=>f}}));if(await r.documents.saveEntities(o),await r.files.import(n),i.log("debug","Imported data into temporary namespace",t),await r.persistenceShutdownHandler.shutdown(),e.version!==i.schema.version){let a=i.schema,f=await At({...r,persistenceShutdownHandler:new Re,schema:a});i.log("debug","Upgraded imported data to current schema"),await f.persistenceShutdownHandler.shutdown(),i.log("debug","Shut down upgraded databases")}if(await i.persistenceShutdownHandler.shutdown(),await i.persistence.copyNamespace(t,i.namespace,i),await At(i),n.data.schemaVersion===i.schema.version){let a=await i.meta.stats();if(a.operationsSize.count!==n.data.operations.length)throw i.log("critical","Imported operations count mismatch","expected",n.data.operations.length,"actual",a.operationsSize.count),new X(X.Code.ImportFailed,void 0,"Imported operations count mismatch");if(a.baselinesSize.count!==n.data.baselines.length)throw i.log("critical","Imported documents count mismatch","expected",n.data.baselines.length,"actual",a.baselinesSize.count),new X(X.Code.ImportFailed,void 0,"Imported documents count mismatch")}i.log("debug","Data copied to primary namespace"),await i.persistence.deleteNamespace(t,i),i.log("debug","Deleted temporary namespace"),i.internalEvents.emit("persistenceReset"),i.log("info","Data imported successfully"),i.persistenceShutdownHandler.reset()}var ft=class extends N{constructor(e){super();this.context=e;this.importingPromise=Promise.resolve();this.addData=async e=>{await this.importingPromise;try{let t=e.reset?Ks(e):this.schema.version;return t<this.schema.version?(this.context.log("warn","Incoming reset sync data is from an old schema version",t,`(current ${this.schema.version})`),await this.import({data:{operations:e.operations,baselines:e.baselines??[],localReplica:void 0,schemaVersion:t},fileData:[],files:[]})):await this._entities.addData(e)}catch(t){throw this.context.log("critical","Sync failed",t),this.emit("developerError",new Error("Sync failed, see logs or cause",{cause:t})),t}};this.stats=async()=>{if(this.disposed)return{};let e=await this.context.documents.stats(),t=await this.context.meta.stats(),r=typeof navigator<"u"&&typeof navigator.storage<"u"&&"estimate"in navigator.storage?await navigator.storage.estimate():void 0,s=await this.context.files.stats(),o=Object.values(e).reduce((p,{size:m})=>p+m,0),a=t.baselinesSize.size+t.operationsSize.size,f=a/o;return{collections:e,meta:t,storage:r,totalMetaSize:a,totalCollectionsSize:o,metaToDataRatio:f,files:s,quotaUsage:r?.usage&&r?.quota?r.usage/r.quota:void 0}};this.close=async()=>{this.sync.ignoreIncoming(),await this._entities.flushAllBatches(),this.context.closing=!0,this.context.closeLock&&await this.context.closeLock,this.sync.stop(),this.sync.destroy(),await this._entities.destroy(),this.context.persistenceShutdownHandler.shutdown(),await new Promise(e=>{e()}),this.context.log?.("info","Client closed")};this.__dangerous__resetLocal=async()=>{this.sync.stop(),await bn(this.namespace,indexedDB)};this.export=async({downloadRemoteFiles:e}={downloadRemoteFiles:!0})=>{this.context.log("info","Exporting data...");let t=await this.context.meta.export(),{fileData:r,files:s}=await this.context.files.export(e);return{data:t,fileData:r,files:s}};this.import=async({data:e,fileData:t,files:r})=>{let s=()=>{};this.importingPromise=new Promise(o=>{s=o}),this.context.log("info","Importing data..."),await Zs(this.context,{data:e,files:r,fileData:t}),s()};this.__dangerous__hardReset=async()=>{let e=await this.export();await this.import(e)};this.__cleanupFilesImmediately=()=>this.context.files.cleanupDeletedFiles();this.__manualRebase=()=>(this.context.meta.manualRebase(),new Promise(e=>{let t=this.subscribe("rebase",()=>{t(),e()})}));this.collectionNames=Object.keys(e.schema.collections),this._sync=this.context.config.sync&&!e.schema.wip?new ut(this.context.config.sync,{onData:this.addData,ctx:this.context}):new Bn(this.context),e.schema.wip&&this.context.config.sync&&e.log("warn","\u26A0\uFE0F\u26A0\uFE0F Sync is disabled for WIP schemas. Commit your schema changes to start syncing again. \u26A0\uFE0F\u26A0\uFE0F"),this._fileManager=new yn({sync:this.sync,context:this.context}),this._entities=new mn({ctx:this.context,files:this._fileManager}),this._queryCache=new En({context:e}),this._documentManager=new tn(this.schema,this._entities);let t=kr(()=>{this.emit("futureSeen")},300);this.context.globalEvents.subscribe("futureSeen",t),this.context.globalEvents.subscribe("resetToServer",()=>{this.emit("resetToServer")}),this.context.globalEvents.subscribe("operation",r=>{this.emit("operation",r)}),this.context.globalEvents.subscribe("rebase",()=>{this.emit("rebase")}),this.context.globalEvents.subscribe("fileSaved",r=>{this.emit("fileSaved",r)});for(let[r,s]of Object.entries(e.schema.collections)){let o=r;this[o]=new _n({collection:o,cache:this._queryCache,context:this.context,entities:this.entities,documentManager:this.documentManager})}}get sync(){return this._sync}get entities(){return this._entities}get documentManager(){return this._documentManager}get schema(){return this.context.schema}get namespace(){return this.context.namespace}get undoHistory(){return this.context.undoHistory}get batch(){return this.entities.batch}get __persistence(){return{meta:this.context.meta,queries:this.context.documents,files:this.context.files}}};var Vn=class{constructor(n){this.value=n}deref(){return this.value}};var Nn=class{constructor(n,e){this.base=n;this.version=e;this.withMigrationTime=async(n,e)=>{this.overrideNow=()=>this.base.zero(n),await e(),this.overrideNow=void 0};this.update=this.base.update.bind(this.base);this.nowWithVersion=n=>this.base.now(n);this.zeroWithVersion=n=>this.base.zero(n)}get now(){return this.overrideNow?this.overrideNow():this.base.now(this.version)}get zero(){return this.base.zero(this.version)}};var Ve=class extends fe{constructor(e,{log:t}={}){super();this.db=e;this.createTransaction=(e,t)=>{try{if(this.globalAbortController.signal.aborted)throw new Error("Global abort signal is already aborted");let r=Ms(this.db,e,t?.mode||"readonly",t?.abort,this.log);return this.globalAbortController.signal.addEventListener("abort",r.abort),r.addEventListener("complete",()=>{this.globalAbortController.signal.removeEventListener("abort",r.abort)}),r.addEventListener("error",()=>{this.globalAbortController.signal.removeEventListener("abort",r.abort)}),r}catch(r){throw this.log?.("error","Failed to create abortable transaction for store names",e,r),r}};this.run=async(e,t,r)=>{if(this.disposed||r?.transaction?.error)return Promise.resolve(void 0);let o=(r?.transaction||this.createTransaction([e],r)).objectStore(e),a=t(o);return ct(a)};this.runAll=async(e,t,r)=>{if(this.disposed||r?.transaction?.error)return Promise.resolve([]);let o=(r?.transaction||this.createTransaction([e],r)).objectStore(e),a=t(o);return Promise.all(a.map(ct))};this.iterate=async(e,t,r,s)=>{let a=(s?.transaction||this.createTransaction([e],s)).objectStore(e),f=t(a);return Array.isArray(f)?Promise.all(f.map(p=>new Promise((m,_)=>{p.onsuccess=()=>{let w=p.result;w?r(w.value,a,w)?m():w.continue():m()},p.onerror=()=>{p.error&&Je(p.error)?m():_(p.error)}}))).then(()=>{}):new Promise((p,m)=>{f.onsuccess=()=>{let _=f.result;_?r(_.value,a,_)?p():_.continue():p()},f.onerror=()=>{f.error&&Je(f.error)?p():m(f.error)}})};this.clear=(e,t)=>this.run(e,r=>r.clear(),{mode:"readwrite",transaction:t});this.onVersionChange=e=>{if(this.log?.("warn",`Another tab has requested a version change for ${this.db.name}`),this.db.close(),typeof window<"u")try{window.location.reload()}catch(t){this.log?.("error","Failed to reload the page",t)}};this.log=t;let r=new AbortController;this.globalAbortController=r,this.addDispose(function(){if(!r.signal.aborted)try{r.abort.call(r)}catch(s){console.error("Error aborting global controller",s)}}),this.db.addEventListener("versionchange",this.onVersionChange),this.addDispose(()=>{this.db.removeEventListener("versionchange",this.onVersionChange)})}};var zn=class extends Ve{constructor(){super(...arguments);this.add=async e=>{let t=e.file?await Ua(e.file):void 0;await this.run("files",r=>r.put({id:e.id,remote:e.remote?"true":"false",deletedAt:null,name:e.name,type:e.type,url:e.url,buffer:t,timestamp:e.timestamp}),{mode:"readwrite"})};this.markUploaded=async e=>{let t=await this.getFileRaw(e);if(!t)throw new Error("File is not in local database");await this.run("files",r=>r.put({...t,remote:"true"}),{mode:"readwrite"})};this.get=async e=>{let t=await this.getFileRaw(e);return t?this.hydrateFileData(t):null};this.delete=e=>this.run("files",t=>t.delete(e),{mode:"readwrite"});this.markPendingDelete=async e=>{let t=await this.getFileRaw(e);if(!t)throw new Error("File is not in local database");await this.run("files",r=>r.put({...t,deletedAt:Date.now()}),{mode:"readwrite"})};this.listUnsynced=async()=>(await this.run("files",t=>t.index("remote").getAll("false"),{mode:"readonly"})).map(this.hydrateFileData);this.resetSyncedStatusSince=async e=>{let t=this.createTransaction(["files"],{mode:"readwrite"}),s=(await this.run("files",o=>o.index("remote").getAll("true"),{transaction:t})).filter(o=>!o.timestamp||!e||o.timestamp>e);await Promise.all(s.map(o=>this.run("files",a=>a.put({...o,remote:"false"}),{transaction:t})))};this.iterateOverPendingDelete=e=>this.iterate("files",t=>t.index("deletedAt").openCursor(IDBKeyRange.lowerBound(0,!0)),(t,r)=>{e(this.hydrateFileData(t),r)},{mode:"readwrite"});this.getAll=async e=>{let[t]=await Vi(this.db,["files"]);return t.map(this.hydrateFileData)};this.stats=async()=>({size:await Ge(this.db,"files")});this.loadFileContents=async(e,t)=>{if(e.file)return e.file;if(e.localPath)throw new Error("Local file paths are not supported in browser");if(e.url){let r=await t.environment.fetch(e.url);if(!r.ok)throw new Error(`Failed to download file: ${r.statusText}`);return r.blob()}throw new Error("File is missing url, file, and localPath")};this.hydrateFileData=e=>{e.remote=e.remote==="true";let t=e.buffer;return delete e.buffer,e.file=t?Ma(t,e.type):void 0,e};this.getFileRaw=async(e,{transaction:t}={})=>{let r=await this.run("files",s=>s.get(e),{mode:"readonly",transaction:t});if(r)return r}}};function Ma(i,n){return new Blob([i],{type:n})}function Ua(i){return"__testReadBuffer"in i?Promise.resolve(i.__testReadBuffer):new Promise((n,e)=>{let t=new FileReader;t.onload=()=>{n(t.result)},t.onerror=e,t.readAsArrayBuffer(i)})}var $n=class extends Ve{constructor(e,t){super(e,t);this.ctx=t;this.transaction=async(e,t)=>{let r=this.createTransaction(e.storeNames,{mode:e.mode,abort:e.abort});return await t(r)};this.getAckInfo=async()=>{let e=await this.run("info",t=>t.get("ack"));return e||{globalAckTimestamp:null}};this.setGlobalAck=async e=>{await this.run("info",t=>t.put({type:"ack",globalAckTimestamp:e}),{mode:"readwrite"})};this.getLocalReplica=async e=>this.run("info",t=>t.get("localReplicaInfo"),e);this.updateLocalReplica=async(e,t)=>{try{await this.run("info",r=>r.put({...e,type:"localReplicaInfo"}),{mode:"readwrite",transaction:t?.transaction})}catch(r){throw this.ctx.log("critical","Error updating local replica",e,r),r}};this.iterateDocumentBaselines=async(e,t,r)=>{await this.iterate("baselines",s=>{let o=z(e),[a,f]=Pr(e),[p,m]=Mr(e);return[s.openCursor(IDBKeyRange.only(o)),s.openCursor(IDBKeyRange.bound(a,f,!1,!1)),s.openCursor(IDBKeyRange.bound(p,m,!1,!1))]},t,r)};this.iterateCollectionBaselines=async(e,t,r)=>{await this.iterate("baselines",s=>[s.openCursor(IDBKeyRange.bound(e,e+"\uFFFF",!1,!1))],t,r)};this.iterateAllBaselines=async(e,t)=>{await this.iterate("baselines",r=>r.index("timestamp").openCursor(),e,t)};this.getBaseline=(e,t)=>this.run("baselines",r=>r.get(e),t);this.setBaselines=async(e,t=Qi)=>{await this.runAll("baselines",r=>e.map(s=>r.put(s)),t)};this.deleteBaseline=async(e,t=Qi)=>{await this.run("baselines",r=>r.delete(e),t)};this.iterateDocumentOperations=(e,t,r)=>this.iterate("operations",s=>{let o=s.index("d_t"),a=Ae(e),f=r?.to?we(e,r.to):Me(e),p=IDBKeyRange.bound(a,f,!1,!1);return o.openCursor(p)},t,r);this.iterateEntityOperations=(e,t,r)=>this.iterate("operations",s=>{let o=Ae(e),a=r?.to?we(e,r.to):Me(e),f=IDBKeyRange.bound(o,a,!1,!1);return s.openCursor(f)},t,r);this.deleteEntityOperations=(e,t)=>this.iterate("operations",r=>{let s=Ae(e),o=t?.to?we(e,t.to):Me(e),a=IDBKeyRange.bound(s,o,!1,!1);return r.openCursor(a)},(r,s)=>{s.delete(r.oid_timestamp)},t);this.iterateCollectionOperations=(e,t,r)=>this.iterate("operations",s=>s.openCursor(IDBKeyRange.bound(e,e+"\uFFFF",!1,!1),"next"),t,r);this.iterateLocalOperations=(e,t)=>this.iterate("operations",r=>{let s=t?.after?we(!0,t.after):Ae(!0),o=Me(!0);return r.index("l_t").openCursor(IDBKeyRange.bound(s,o,!!t?.after,!1),"next")},e);this.iterateAllOperations=(e,t)=>this.iterate("operations",r=>{let s=t?.from?Ae(t.from):void 0,o=t?.before?Me(t.before):Ae(!0),a=s&&o?IDBKeyRange.bound(s,o,!1,!0):s?IDBKeyRange.lowerBound(s,!1):o?IDBKeyRange.upperBound(o,!0):void 0;return r.index("timestamp").openCursor(a,"next")},e,t);this.addOperations=async(e,t=Qi)=>{let r=new Set;return await this.runAll("operations",s=>e.map(o=>(r.add(z(o.oid)),s.put(this.addOperationIndexes(o)))),t),Array.from(r)};this.reset=async({clearReplica:e,transaction:t}={})=>{let r=t||this.createTransaction(["info","operations","baselines"],{mode:"readwrite"});await Promise.all([this.resetLocalReplica(r,e),this.resetBaselines(r),this.resetOperations(r)])};this.stats=async()=>{let e=await Ge(this.db,"operations"),t=await Ge(this.db,"baselines");return{operationsSize:e,baselinesSize:t}};this.resetLocalReplica=async(e,t=!1)=>{if(t)return this.run("info",r=>r.delete("localReplicaInfo"),{mode:"readwrite",transaction:e});{let r=await this.getLocalReplica({transaction:e});r&&(r.ackedLogicalTime=null,r.lastSyncedLogicalTime=null,await this.run("info",s=>s.put({...r,type:"localReplicaInfo"}),{mode:"readwrite",transaction:e}))}};this.resetBaselines=async e=>this.clear("baselines",e);this.resetOperations=async e=>this.clear("operations",e);this.addOperationIndexes=e=>({...e,oid_timestamp:we(e.oid,e.timestamp),l_t:we(e.isLocal,e.timestamp),d_t:we(z(e.oid),e.timestamp)});this.addDispose(()=>(this.ctx.log("info","Closing metadata DB for",this.ctx.namespace),he(e)))}},Qi={mode:"readwrite"};var eo=[Na,za,$a,Ka,Ha,qa],Va=eo.length;function Wi({indexedDB:i=window.indexedDB,namespace:n,log:e}){return new Promise((t,r)=>{let s=i.open(Pt(n),Va),o=!1;s.onupgradeneeded=async a=>{let f=s.result,p=s.transaction,m=eo.slice(a.oldVersion);for(let _ of m)await _(f,p);await new Promise((_,w)=>{p.addEventListener("complete",_),p.addEventListener("error",w)}),a.oldVersion||(o=!0)},s.onerror=()=>{console.error("Error opening database",s.error),r(s.error)},s.onsuccess=()=>{t({db:s.result,wasInitialized:o})}})}async function Na(i,n){let e=i.createObjectStore("baselines",{keyPath:"oid"}),t=i.createObjectStore("operations",{keyPath:"oid_timestamp"}),r=i.createObjectStore("info",{keyPath:"type"});e.createIndex("timestamp","timestamp"),t.createIndex("isLocal_timestamp","isLocal_timestamp"),t.createIndex("documentOid_timestamp","documentOid_timestamp")}async function za(i,n){let e=n.objectStore("operations");await new Promise((t,r)=>{let s=e.openCursor();s.onsuccess=()=>{let o=s.result;if(o){let{isLocal_timestamp:a,documentOid_timestamp:f,...p}=o.value;o.update({...p,l_t:a,d_t:f}),o.continue()}else t()},s.onerror=o=>{r(s.error)}}),e.deleteIndex("isLocal_timestamp"),e.deleteIndex("documentOid_timestamp"),e.createIndex("l_t","l_t",{unique:!1}),e.createIndex("o_t","o_t",{unique:!1}),e.createIndex("d_t","d_t",{unique:!1})}async function $a(i,n){n.objectStore("operations").createIndex("timestamp","timestamp")}async function Ka(i,n){let e=i.createObjectStore("files",{keyPath:"id"});e.createIndex("remote","remote"),e.createIndex("deletedAt","deletedAt")}async function Ha(i,n){let e=n.objectStore("operations");await new Promise((r,s)=>{let o=e.openCursor();o.onsuccess=()=>{let a=o.result;if(a){let f=ci(a.value);f.oid_timestamp!==a.primaryKey?(a.delete(),e.put(f)):a.update(f),a.continue()}else r()},o.onerror=a=>{s(o.error)}});let t=n.objectStore("baselines");await new Promise((r,s)=>{let o=t.openCursor();o.onsuccess=()=>{let a=o.result;if(a){let f=ci(a.value);f.oid!==a.primaryKey?(a.delete(),t.put(f)):a.update(f),a.continue()}else r()},o.onerror=a=>{s(o.error)}})}async function qa(i,n){n.objectStore("files").createIndex("timestamp","timestamp")}var Qa=i=>IDBKeyRange.only(ve(i.equals)),Wa=i=>{},Ja=i=>{let n=i.gte||i.gt,e=i.lte||i.lt;return n===e?IDBKeyRange.only(ve(n)):n?e?IDBKeyRange.bound(ve(n),ve(e),!!i.gt,!!i.lt):IDBKeyRange.lowerBound(ve(n),!!i.gt):IDBKeyRange.upperBound(ve(e),!!i.lt)},Ga=(i,n,e)=>{let t=i.collections[n].compounds[e.where];V(t,`Index ${e.where} does not exist on collection ${n}`);let r=Object.keys(e.match).sort((f,p)=>t.of.indexOf(f)-t.of.indexOf(p));for(let f of r)if(t.of.indexOf(f)!==r.indexOf(f))throw new Error(`Compound index ${e.where} does not have ${f} at the start of its order`);let s=r.map(f=>e.match[f]);if(r.length===t.of.length)return IDBKeyRange.only(we(...s));let o=Ae(...s),a=Me(...s);return IDBKeyRange.bound(o,a)};function Ya(i){let n=i.startsWith,e=i.startsWith+"\uFFFF";return IDBKeyRange.bound(n,e)}function Ji(i,n,e){if(e)return hi(e)?Ja(e):fi(e)?Qa(e):qr(e)?Wa(e):pi(e)?Ya(e):Ga(i,n,e)}var Kn=class extends Ve{constructor(e,t){super(e,{log:t.log});this.close=async()=>{await this.dispose()};this.stats=async()=>{let e=Object.keys(this.ctx.schema.collections),t={};return await Promise.all(e.map(async r=>{let s=await Ge(this.db,r);t[r]=s})),t};this.findOneOid=async e=>{let t=await this.run(e.collection,r=>{let s=e.index?.where?r.index(e.index.where):r,o=e.index?.order==="desc"?"prev":"next",a=Ji(this.ctx.schema,e.collection,e.index);return s.openCursor(a,o)},{mode:"readonly"});return t?ee(e.collection,t.primaryKey.toString()):null};this.findAllOids=async({collection:e,index:t,offset:r,limit:s})=>{let a=this.createTransaction([e],{mode:"readonly"}).objectStore(e),f=t?.where?a.index(t.where):a,p=t?.order==="desc"?"prev":"next",m=Ji(this.ctx.schema,e,t),_=f.openCursor(m,p),w=!1;return{result:await new Promise((y,I)=>{let D=!r,O=0,E=new Set;_.onsuccess=()=>{O++;let B=_.result;if(!B){y(Array.from(E));return}r&&!D?(B.advance(r),D=!0):((!s||E.size<s)&&E.add(ee(e,B.primaryKey.toString())),s&&O>s?(w=!0,y(Array.from(E))):B.continue())},_.onerror=()=>{_.error?.name==="InvalidStateError"?(this.ctx.log("error","find query failed with InvalidStateError",_.error),y([])):_.error&&Je(_.error)?y([]):I(_.error)}}),hasNextPage:w}};this.saveEntities=async(e,t)=>{let r={transaction:this.createTransaction(t.collections,{mode:"readwrite",abort:t.abort})};await Promise.all(e.map(async s=>{let o=s.getSnapshot();try{await this.saveDocument(s.oid,o,r)}catch(a){throw this.ctx.log("error",`Error saving document ${s.oid} (${JSON.stringify(o)})`,a),a instanceof Error?a:new Error("Unknown error saving document")}})),r.transaction.commit()};this.reset=async()=>{let e=Object.keys(this.ctx.schema.collections),t=this.createTransaction(e,{mode:"readwrite"});await Promise.all(e.map(r=>this.run(r,s=>s.clear(),{transaction:t}))),this.ctx.entityEvents.emit("collectionsChanged",e),this.ctx.log("info","\u{1F4A8} Reset queryable storage")};this.saveDocument=async(e,t,{transaction:r})=>{this.ctx.log("debug",`Saving document indexes for querying ${e}`);let{collection:s,id:o}=ae(e);if(!t)await this.run(s,a=>a.delete(o),{mode:"readwrite",transaction:r}),this.ctx.log("debug",`Deleted document indexes for querying ${e}`);else{let a=this.ctx.schema.collections[s],f=Yr(a,t);f["@@@snapshot"]=JSON.stringify(t),await this.run(s,p=>p.put(f),{mode:"readwrite",transaction:r}),this.ctx.log("debug",`Save complete for ${e}`,f)}};this.ctx=t,this.addDispose(()=>(this.ctx.log("info","Closing document database for",this.ctx.namespace),he(this.db)))}};async function to(i,n,e,t,r){r?.("debug","Upgrading database",n,"to version",e);function s(o,a){let f=i.open(lt(n),e),p=!1;f.onupgradeneeded=m=>{let _=f.transaction;t(_,f.result,m),p=!0},f.onsuccess=async m=>{p?(await he(f.result),o(f.result)):a(new Error("Database was not upgraded when a version change was expected"))},f.onerror=m=>{a(f.error||new Error("Unknown error"))},f.onblocked=m=>{r?.("Database upgrade blocked!"),a(new Error("Database upgrade blocked. The app may be open in another tab?"))}}return new Promise(s)}async function Gi({indexedDB:i=Ui,namespace:n,version:e,log:t}){t?.("debug","Opening database",n,"at version",e);let r=await Us(lt(n),e,i);return r.addEventListener("versionchange",s=>{r.close()}),r.addEventListener("close",()=>{t?.("warn","Database closed",n)}),r}var ht=class{constructor(n=window.indexedDB){this.indexedDB=n;this.name="IdbPersistence";this.getNamespaces=async()=>{let n=await this.indexedDB.databases();return Array.from(new Set(n.map(Vs).filter(e=>!!e)))};this.getNamespaceVersion=async n=>{let e=lt(n),r=(await this.indexedDB.databases()).find(s=>s.name===e);return r?r.version??0:0};this.deleteNamespace=async(n,e)=>{await Promise.all([vn(Pt(n),this.indexedDB),vn([n,"collections"].join("_"),this.indexedDB)])};this.openNamespace=async n=>new Yi(this.indexedDB,n);this.copyNamespace=async(n,e,t)=>{let r={...t,namespace:n},s={...t,namespace:e},{db:o}=await Wi({indexedDB:this.indexedDB,log:r.log,namespace:r.namespace}),a=await Gi({indexedDB:this.indexedDB,namespace:r.namespace,version:r.schema.version,log:r.log});r.log("info",`Copying data from ${r.namespace} to ${s.namespace}`),await Ni(o,Pt(s.namespace),s,this.indexedDB),await Ni(a,lt(s.namespace),s,this.indexedDB),await he(o),await he(a)}}},Yi=class{constructor(n,e){this.indexedDB=n;this.namespace=e;this.openMetadata=async n=>{let{db:e}=await Wi({indexedDB:this.indexedDB,log:n.log,namespace:this.namespace});return this.metadataDb=e,n.persistenceShutdownHandler.register(()=>he(e)),new $n(e,n)};this.openDocuments=async n=>{let e=await Gi({version:n.schema.version,indexedDB:this.indexedDB,log:n.log,namespace:this.namespace});return n.persistenceShutdownHandler.register(()=>he(e)),new Kn(e,n)};this.applyMigration=async(n,e)=>{n.log("debug","Applying migration",e.newSchema.version,e),await to(this.indexedDB,this.namespace,e.newSchema.version,(t,r)=>{for(let s of e.addedCollections)r.createObjectStore(s,{keyPath:e.newSchema.collections[s].primaryKey,autoIncrement:!1});for(let s of e.allCollections){let o=t.objectStore(s);for(let a of e.addedIndexes[s]||[])o.createIndex(a.name,a.name,{multiEntry:a.multiEntry});for(let a of e.removedIndexes[s]||[])o.deleteIndex(a.name)}for(let s of e.removedCollections)t.objectStore(s).clear()},n.log)}}openFiles(n){if(!this.metadataDb)throw new Error("Metadata database must be opened first. This is a bug in Verdant.");return Promise.resolve(new zn(this.metadataDb,n))}};var Hn=class{constructor(n){this.init=n;this._initializing=!1;this.initialize=async n=>{if(typeof window>"u"&&!n.environment)throw new Error("A Verdant client was initialized in an environment without a global Window or `environment` configuration. If you are using verdant in a server-rendered framework, you must enforce that all clients are initialized on the client-side, or you must provide some mock interface of the environment to the ClientDescriptor options.");if(this._initializing||this._resolvedValue)return this._readyPromise;this._initializing=!0;try{let e=new Nn(new kt,n.schema.version),t=n.environment||Xa,r={closing:!1,entityEvents:new N,globalEvents:new N,internalEvents:new N,log:n.log||Lr,migrations:n.migrations,namespace:n.namespace,originalNamespace:n.namespace,schema:n.schema,oldSchemas:n.oldSchemas,time:e,undoHistory:n.undoHistory||new rt,weakRef:a=>n.EXPERIMENTAL_weakRefs?new WeakRef(a):new Vn(a),patchCreator:new en(()=>e.now),config:{files:n.files,sync:n.sync,persistence:{disableRebasing:n.disableRebasing,rebaseTimeout:n.rebaseTimeout}},persistence:n.persistence||new ht(t.indexedDB),environment:t,persistenceShutdownHandler:new Re,pauseRebasing:!1};r.log("info","Initializing client",{namespace:r.namespace,version:n.schema.version,persistence:r.persistence.name});let s=await At(r),o=new ft(s);return this.resolveReady(o),this._resolvedValue=o,o}catch(e){throw e instanceof Error?(this.rejectReady(e),e):new Error("Unknown error initializing storage")}finally{this._initializing=!1}};this.open=()=>this.initialize(this.init);this.close=async()=>{this._resolvedValue&&this._resolvedValue.close(),this._initializing&&(await this._readyPromise).close()};this.__dangerous__resetLocal=async()=>{await bn(this.namespace)};this._readyPromise=new Promise((e,t)=>{this.resolveReady=e,this.rejectReady=t}),this._namespace=n.namespace}get namespace(){return this._namespace}get current(){return this._resolvedValue}get readyPromise(){return this._readyPromise}get schema(){return this.init.schema}},Xa={WebSocket:typeof WebSocket<"u"?WebSocket:void 0,fetch:typeof window<"u"?window.fetch.bind(window):fetch,indexedDB:typeof indexedDB<"u"?indexedDB:void 0};var Xi=Fe(Ne(),1);function Za(i){return i?Xi.default.slug():(0,Xi.default)()}var ec={private:qe.onlyMe(),public:void 0};function tc(i,{port:n=3242,initialPresence:e={}}={}){let t=localStorage.getItem("verdant-userId");return t||(t=`user-${Math.random().toString(36).slice(2)}`,localStorage.setItem("verdant-userId",t)),{defaultProfile:{id:t},initialPresence:e,authEndpoint:`http://localhost:${n}/auth/${i}?userId=${t}`}}window.Verdant=Zi;
14
14
  //# sourceMappingURL=index.js.map