@verdant-web/store 5.0.0 → 5.0.1
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.
package/dist/bundle/index.js
CHANGED
|
@@ -12,7 +12,7 @@ Patch that was not applied: ${JSON.stringify(n)}`),!1)}function Me(i,n,e){if(i==
|
|
|
12
12
|
- New indexes: ${Object.keys(r.addedIndexes).map(l=>r.addedIndexes[l].map(p=>`${l}.${p.name}`)).flatMap(l=>l).join(", ")}
|
|
13
13
|
- Removed indexes: ${Object.keys(r.removedIndexes).map(l=>r.removedIndexes[l].map(p=>`${l}.${p.name}`)).flatMap(l=>l).join(", ")}
|
|
14
14
|
`)}i.pauseRebasing=!1}var mi=class{constructor(n,e){this.db=n;this.context=e;this.onServerReset=n=>this.db.resetSyncedStatusSince(n);this.add=async n=>{if(n.url&&!(n.localPath||n.file)){this.context.log("debug","Remote file added to an entity. This usually means an entity was cloned. Downloading remote file...",n.id);let e=await this.loadFileContents(n,0,3);n.file=new File([e],n.name,{type:n.type}),delete n.url,this.context.log("debug","Downloaded remote file",n.id,n.name,". Cleared its remote URL.")}else!n.url&&!n.file&&!n.localPath&&this.context.log("warn","File added without a file or URL. This file will not be available for use.",n.id);return n.remote=!1,this.context.log("debug","Adding file to persistence",n),await this.db.add(n),this.context.internalEvents.emit("fileAdded",n),this.context.globalEvents.emit("fileSaved",n),this.context.log("debug","File added",n.id,n.name,n.type,n.file?"with binary file":n.url?"with url":n.localPath?"with local path":"with no data"),n};this.onUploaded=this.db.markUploaded.bind(this.db);this.get=this.db.get.bind(this.db);this.getAll=this.db.getAll.bind(this.db);this.listUnsynced=this.db.listUnsynced.bind(this.db);this.iterateOverPendingDelete=this.db.iterateOverPendingDelete.bind(this.db);this.stats=this.db.stats.bind(this.db);this.getFileExportName=(n,e)=>`${e}___${n}`;this.export=async(n=!0)=>{let e=await this.getAll();if(n)for(let s of e)if(!s.file&&(s.url||s.localPath))try{let o=await this.loadFileContents(s);s.file=o}catch(o){this.context.log("error","Failed to download file to cache it locally. The file will still be available using its URL. Check the file server's CORS configuration.",s,o)}else s.file||this.context.log("warn",`File ${s.id} has no file or URL. It will be missing in the export.`,s);let t=[],r=[];for(let s of e){let o=s.file;if(delete s.file,t.push(s),o){let a=new File([o],this.getFileExportName(s.name,s.id),{type:s.type});r.push(a)}else this.context.log("warn",`File ${s.id} was could not be loaded locally or from the server. It will be missing in the export.`)}return{fileData:t,files:r}};this.import=async({fileData:n,files:e})=>{let t=new Map(e.map(s=>{let{id:o}=this.parseFileExportname(s.name);return[o,s]})),r=n.map(s=>{let o=t.get(s.id);return o?{...s,file:o}:(this.context.log("warn",`File ${s.id} was not found in import`),s)});await Promise.all(r.map(s=>this.add(s)))};this.parseFileExportname=n=>{let[e,t]=n.split("___");return{id:e,originalFileName:t}};this.loadFileContents=async(n,e=0,t=0)=>{try{return await this.db.loadFileContents(n,this.context)}catch(r){if(e<t)return new Promise((s,o)=>{setTimeout(()=>{this.loadFileContents(n,e+1,t).then(s,o)},1e3)});throw this.context.log("error",`Failed to download file after ${t} retries`,r),new Error(`Failed to download file after ${t} retries`,{cause:r})}};this.cleanupDeletedFiles=async()=>{let n=0,e=0,t=[];await this.iterateOverPendingDelete(r=>{this.config.canCleanupDeletedFile(r)?(n++,t.push(r.id)):e++});for(let r of t)await this.db.delete(r);this.context.log("info",`Cleaned up ${n} files, skipped ${e} files`)};this.onFileRefsDeleted=async n=>{await Promise.all(n.map(async e=>{try{await this.db.markPendingDelete(e.id)}catch(t){this.context.log("error","Failed to mark file for deletion",t)}})),this.context.log("info",`Marked ${n.length} files as pending delete`)};e.internalEvents.subscribe("filesDeleted",this.onFileRefsDeleted),this.cleanupDeletedFiles()}get config(){return{canCleanupDeletedFile(n){return n.deletedAt!==null&&n.deletedAt<Date.now()-1e3*60*24*3},...this.context.config.files}}};var Ds=Be(Qe(),1);var gi=class{constructor(n,e,t){this.db=n;this.meta=e;this.ctx=t;this.createOperation=async n=>{let e=await this.meta.getLocalReplica();return this.ctx.log("debug","Creating operation message",n.operations.length),{type:"op",timestamp:this.ctx.time.now,replicaId:e.id,operations:n.operations.map($t)}};this.createSyncStep1=async n=>{let e=await this.meta.getLocalReplica(),t=n===null?null:e.lastSyncedLogicalTime,r=[],s=new Set;return this.db.transaction({mode:"readwrite",storeNames:["operations","baselines"]},async o=>{t?(this.ctx.log("debug","Syncing local operations since",t),await this.db.iterateLocalOperations(l=>{r.push($t(l)),s.add(K(l.oid))},{after:t,transaction:o})):(this.ctx.log("debug","Syncing all operations"),await this.db.iterateAllOperations(l=>{r.push($t(l)),s.add(K(l.oid))},{transaction:o}));let a=[];return t||await this.db.iterateAllBaselines(l=>{a.push(l)},{transaction:o}),r.length>0&&this.ctx.log("debug",`Syncing ${r.length} operations since ${t}`),{type:"sync",schemaVersion:this.ctx.schema.version,timestamp:this.ctx.time.now,replicaId:e.id,resyncAll:!e.lastSyncedLogicalTime,operations:r,baselines:a,since:t}})};this.createPresenceUpdate=async n=>{let e=await this.meta.getLocalReplica();return{type:"presence-update",presence:n.presence,replicaId:e.id,internal:n.internal}};this.createHeartbeat=async()=>({type:"heartbeat"});this.createAck=async n=>{let e=await this.meta.getLocalReplica();return{type:"ack",timestamp:this.ctx.time.now,replicaId:e.id,nonce:n}}}};var yi=class{constructor(n,e,t){this.db=n;this.meta=e;this.ctx=t;this.tryAutonomousRebase=async()=>{(await this.meta.getLocalReplica()).lastSyncedLogicalTime||this.ctx.closing||this.ctx.persistenceShutdownHandler.isShuttingDown||(this.ctx.log("debug","Running autonomous library rebase"),await this.runRebase(this.ctx.time.now))};this.runRebase=async n=>{this.ctx.closing||this.ctx.persistenceShutdownHandler.isShuttingDown||(await this.db.transaction({storeNames:["baselines","operations"],mode:"readwrite"},async e=>{let t=new Set,r,s=0;if(await this.db.iterateAllOperations(a=>{t.add(a.oid),r=a.timestamp,s++},{before:n,transaction:e}),!t.size||this.ctx.closing||this.ctx.persistenceShutdownHandler.isShuttingDown)return;let o=[];for(let a of t)o.push(await this.rebase(a,r||n,e))}),this.ctx.globalEvents.emit("rebase"))};this.scheduleRebase=async n=>{this.rebaseTimeout&&clearTimeout(this.rebaseTimeout),this.rebaseTimeout=setTimeout(this.runRebase,this.ctx.config.persistence?.rebaseTimeout??1e4,n),this.ctx.log("debug","Scheduled rebase up to global ack",n)};this.rebaseTimeout=null;this.rebase=async(n,e,t)=>{if(this.ctx.closing||this.ctx.persistenceShutdownHandler.isShuttingDown)return;let r=await this.db.getBaseline(n,{transaction:t}),s=r?.snapshot||void 0,o=0,a=r?.authz,l=[];if(this.ctx.closing||this.ctx.persistenceShutdownHandler.isShuttingDown)return;await this.db.iterateEntityOperations(n,f=>{(!r||f.timestamp>r.timestamp)&&(s=Me(s,f.data,l),f.data.op==="initialize"&&(a=f.authz)),o++},{to:e,transaction:t}),s&&z(s,n);let p={oid:n,snapshot:s,timestamp:e,authz:a};if(!(this.ctx.closing||this.ctx.persistenceShutdownHandler.isShuttingDown))return this.ctx.closeLock=(async()=>{if(p.snapshot?await this.db.setBaselines([p],{transaction:t}):await this.db.deleteBaseline(n,{transaction:t}),await this.db.deleteEntityOperations(n,{to:e,transaction:t}),this.ctx.log("debug","rebased",n,"up to",e,":",s,"and deleted",o,"operations"),l.length){let f=l.filter(fe);f.length&&this.ctx.internalEvents.emit("filesDeleted",f)}})(),p}}};var bi=class{constructor(n,e){this.db=n;this.ctx=e;this.insertOperations=async(n,e)=>{this.ctx.log("debug",`Inserting ${n.length} operations`,n);let t=await this.db.addOperations(n,e);for(let r of n)this.ctx.globalEvents.emit("operation",r);return!this.ctx.config.persistence?.disableRebasing&&!this.ctx.pauseRebasing&&this.rebaser.tryAutonomousRebase(),t};this.insertLocalOperations=async(n,e)=>{if(n.length===0)return;for(let r of n)r.isLocal=!0;await this.insertOperations(n,e),this.ctx.log("debug",`Inserted ${n.length} local operations; sending sync message`);let t=await this.messageCreator.createOperation({operations:n});this.ctx.internalEvents.emit("outgoingSyncMessage",t)};this.insertRemoteOperations=async(n,e)=>{if(n.length===0)return[];for(let t of n)t.isLocal=!1;await this.insertOperations(n,e),this.ack(n[n.length-1].timestamp)};this.insertRemoteBaselines=async(n,e)=>{if(n.length===0)return[];this.ctx.log("debug",`Inserting ${n.length} remote baselines`),await this.db.setBaselines(n,e);let t=new Set;return n.forEach(r=>{t.add(K(r.oid))}),Array.from(t)};this.deleteDocument=async n=>{let e=new Set,t=K(n);return L(t===n,"Must be root document OID"),e.add(t),this.db.transaction({storeNames:["baselines","operations"]},async r=>{await Promise.all([this.db.iterateDocumentBaselines(t,a=>{e.add(a.oid)},{transaction:r}),this.db.iterateDocumentOperations(t,a=>{e.add(a.oid)},{transaction:r})]);let s=await this.getDocumentAuthz(t),o=new Array;for(let a of e)o.push({oid:a,timestamp:this.ctx.time.now,data:{op:"delete"},authz:s});return this.insertLocalOperations(o,{transaction:r})})};this.deleteCollection=async n=>{let e=new Set;return this.db.transaction({storeNames:["baselines","operations"],mode:"readwrite"},async t=>{await Promise.all([this.db.iterateCollectionBaselines(n,s=>{e.add(s.oid)},{transaction:t}),this.db.iterateCollectionOperations(n,s=>{e.add(s.oid)},{transaction:t})]);let r=new Array;for(let s of e)r.push({oid:s,timestamp:this.ctx.time.now,data:{op:"delete"},authz:void 0});return this.insertLocalOperations(r,{transaction:t})})};this.getDocumentSnapshot=async(n,e={})=>{let t=K(n);return L(t===n,"Must be root document OID"),this.db.transaction({storeNames:["baselines","operations"],mode:"readwrite"},async r=>{let s=[];await this.db.iterateDocumentBaselines(t,l=>{s.push(l)},{transaction:r});let o=new Map;for(let l of s)l.snapshot&&z(l.snapshot,l.oid),o.set(l.oid,l.snapshot);await this.db.iterateDocumentOperations(t,l=>{let p=o.get(l.oid)||void 0,f=Me(p,l.data);f&&z(f,l.oid),o.set(l.oid,f)},{transaction:r,to:e.to||this.ctx.time.now});let a=o.get(t);return a&&Nt(a,o),a})};this.getDocumentData=async(n,e)=>this.db.transaction({storeNames:["baselines","operations"],abort:e?.abort},async t=>{let r=[],s={};return await Promise.all([this.db.iterateDocumentBaselines(n,o=>{r.push(o)},{transaction:t}),this.db.iterateDocumentOperations(n,o=>{s[o.oid]??=[],s[o.oid].push(o)},{transaction:t})]),{baselines:r,operations:s}});this.getDocumentAuthz=async n=>{let e;return await this.db.iterateEntityOperations(n,t=>{if(t.data.op==="initialize")return e=t.authz,!0}),e};this.insertData=async(n,e)=>this.db.transaction({storeNames:["baselines","operations"],abort:e?.abort,mode:"readwrite"},async t=>{if(this.ctx.log("debug","Begin insert data transaction"),n.baselines&&await this.insertRemoteBaselines(n.baselines,{transaction:t}),this.ctx.log("debug","Inserted baselines (if any)"),e?.abort?.aborted)throw new Error("Aborted");n.operations&&(n.isLocal?(this.ctx.log("debug","Inserting local operations"),await this.insertLocalOperations(n.operations,{transaction:t})):(this.ctx.log("debug","Inserting remote operations"),await this.insertRemoteOperations(n.operations,{transaction:t}))),this.ctx.log("debug","End insert data transaction")});this.updateLastSynced=async n=>{if(!this.ctx.closing)return this.updateLocalReplica({lastSyncedLogicalTime:n})};this.setGlobalAck=async n=>{this.ctx.closing||(await this.db.setGlobalAck(n),this.ctx.config.persistence?.disableRebasing||await this.rebaser.scheduleRebase(n))};this._cachedLocalReplica=null;this._creatingLocalReplica=void 0;this.getLocalReplica=async n=>{if(this._cachedLocalReplica)return this._cachedLocalReplica;let e=await this.db.getLocalReplica(n);return e?(this.ctx.log("debug","Read local replica",e),this._cachedLocalReplica=e,e):this._creatingLocalReplica?this._creatingLocalReplica:(this._creatingLocalReplica=(async()=>{let r={id:(0,Ds.default)(),userId:null,ackedLogicalTime:null,lastSyncedLogicalTime:null};return await this.db.updateLocalReplica(r),this._cachedLocalReplica=r,r})(),this._creatingLocalReplica)};this.updateLocalReplica=async(n,e)=>{let t=await this.getLocalReplica(e);L(!!t,"Local replica must exist"),Object.assign(t,n),this._cachedLocalReplica=t,await this.db.updateLocalReplica(t,e)};this.iterateLocalOperations=this.db.iterateLocalOperations;this.iterateAllOperations=this.db.iterateAllOperations;this.iterateAllBaselines=this.db.iterateAllBaselines;this.reset=async()=>{this.ctx.closing||await this.db.reset()};this.stats=this.db.stats;this.export=async()=>{let n=new Array,e=new Array;return this.db.transaction({storeNames:["baselines","operations"],mode:"readwrite"},async t=>{await this.iterateAllOperations(s=>{e.push(s)},{transaction:t}),await this.iterateAllBaselines(s=>{n.push(s)},{transaction:t});let r=await this.getLocalReplica();return{operations:e,baselines:n,localReplica:r,schemaVersion:this.ctx.schema.version}})};this.resetFrom=async n=>{this._cachedLocalReplica=null,await this.db.reset({clearReplica:!0}),n.localReplica&&await this.updateLocalReplica({ackedLogicalTime:n.localReplica.ackedLogicalTime,lastSyncedLogicalTime:n.localReplica.lastSyncedLogicalTime}),this.ctx.log("debug","Resetting metadata from export",n),await this.insertData({operations:n.operations,baselines:n.baselines,isLocal:!0})};this.manualRebase=async()=>{if(this.ctx.closing||this.ctx.config.persistence?.disableRebasing)return;let n=await this.db.getAckInfo();n.globalAckTimestamp&&await this.rebaser.scheduleRebase(n.globalAckTimestamp)};this.ack=async n=>{let e=await this.getLocalReplica();n>this.ctx.time.now||(this.ctx.internalEvents.emit("outgoingSyncMessage",{type:"ack",replicaId:e.id,timestamp:n}),!this.ctx.closing&&(!e.ackedLogicalTime||n>e.ackedLogicalTime)&&this.updateLocalReplica({ackedLogicalTime:n}))};this.rebaser=new yi(n,this,e),this.messageCreator=new gi(n,this,e)}};var wi=class{constructor(n,e){this.db=n;this.ctx=e;this.reset=this.db.reset.bind(this.db);this.close=this.db.close.bind(this.db);this.saveEntities=async(n,e)=>{if(n.length===0)return;let t=new Set(Object.keys(this.ctx.schema.collections)),r=[],s=n.filter(o=>{let{collection:a}=ue(o.oid);return t.has(a)?(r.includes(a)||r.push(a),!0):(this.ctx.log("warn",`Entity ${o.oid} is in a collection that no longer exists in the schema. It will not be saved.`),!1)});if(r.length!==0){this.ctx.log("debug","Saving",s.length,"entities"),await this.db.saveEntities(s,{abort:e?.abort,collections:r}),this.ctx.log("debug","Saved",s.length,"entities"),this.ctx.entityEvents.emit("collectionsChanged",r);for(let o of n)this.ctx.entityEvents.emit("documentChanged",o.oid)}};this.findOneOid=this.db.findOneOid.bind(this.db);this.findAllOids=this.db.findAllOids.bind(this.db);this.stats=this.db.stats.bind(this.db)}};async function Ln(i){let n=i.schema;if(i.schema.wip&&(i.namespace=Ss(i.originalNamespace,i.schema),i.log("info","\u{1F528}","Switched to WIP namespace",i.namespace),!(await i.persistence.getNamespaces()).includes(i.namespace))){let a=await i.persistence.getNamespaceVersion(i.originalNamespace);if(a===0)i.log("debug","No existing data to copy to WIP namespace");else{let l=i.oldSchemas?.find(p=>p.version===a);if(!l)throw new N(N.Code.MigrationPathNotFound,void 0,`Trying to open WIP database for version ${i.schema.version}, but the current local data is version ${a} and a historical schema for that version is not available.`);i.log("info",`Copying data from ${i.originalNamespace} to ${i.namespace}`),await i.persistence.copyNamespace(i.originalNamespace,i.namespace,i.cloneWithOptions({schema:l}))}}let e=await i.persistence.openNamespace(i.namespace,i);i.log("info","Opening persistence metadata",i.namespace);let t=new bi(await e.openMetadata(i),i);i.log("info","Opening persistence files",i.namespace);let r=new mi(await e.openFiles(i),i);if(i.log("info","Migrating document database"),await Cs({context:i,version:i.schema.version,meta:t}),i.log("info","Opening persistence documents"),i.schema.version<=0)throw i.schema!==n?(i.log("critical","Schema at initialization does not match original schema. This is likely a bug in Verdant!"),new N(N.Code.ConfigurationError,void 0,"Schema at initialization does not match original schema. This is likely a bug in Verdant!")):new N(N.Code.ConfigurationError,void 0,`Schema version must be greater than 0. Found version ${i.schema.version} with collections [${Object.keys(i.schema.collections).join(", ")}]
|
|
15
|
-
${JSON.stringify(i.schema)}`);let s=new wi(await e.openDocuments(i),i);if(!i.schema.wip){let o=await i.persistence.getNamespaces();for(let a of o)a.startsWith("@@wip_")&&(i.log("debug","Cleaning up old WIP namespace",a),await i.persistence.deleteNamespace(a,i))}return{meta:t,files:r,documents:s}}async function As(i,n){i.log("info","Importing data from export");let e=i.oldSchemas?.find(l=>l.version===n.data.schemaVersion);if(!e)throw new Error(`Could not find schema for version ${n.data.schemaVersion}`);let t=`@@import_${Date.now()}`,r=i.cloneWithOptions({schema:e,namespace:t,disableRebasing:!0,persistenceShutdownHandler:new ke(i.log)});await r.reinitialize();let s=await r.meta;await s.resetFrom(n.data);let o=new Set;for(let l of n.data.baselines)o.add(K(l.oid));for(let l of n.data.operations)o.add(K(l.oid));let a=await Promise.all(Array.from(o).map(async l=>{let p=await s.getDocumentSnapshot(l);return{oid:l,getSnapshot:()=>p}}));if(await(await r.documents).saveEntities(a),await(await r.files).import(n),i.log("debug","Imported data into temporary namespace",t),await r.persistenceShutdownHandler.shutdown(),e.version!==i.schema.version){let l=r.cloneWithOptions({persistenceShutdownHandler:new ke(i.log),schema:i.schema,oldSchemas:i.oldSchemas});await l.reinitialize(),i.log("debug","Upgraded imported data to current schema"),await l.persistenceShutdownHandler.shutdown(),i.log("debug","Shut down upgraded databases")}if(await i.persistenceShutdownHandler.shutdown(),await i.persistence.copyNamespace(t,i.namespace,i),i.log("debug","Copied imported data to primary namespace"),await i.reinitialize(),i.log("debug","Reinitialized primary persistence layer"),n.data.schemaVersion===i.schema.version){let l=await(await i.meta).stats();if(l.operationsSize.count!==n.data.operations.length)throw i.log("critical","Imported operations count mismatch","expected",n.data.operations.length,"actual",l.operationsSize.count),new N(N.Code.ImportFailed,void 0,"Imported operations count mismatch");if(l.baselinesSize.count!==n.data.baselines.length)throw i.log("critical","Imported documents count mismatch","expected",n.data.baselines.length,"actual",l.baselinesSize.count),new N(N.Code.ImportFailed,void 0,"Imported documents count mismatch")}else i.log("debug","Skipping integrity check due to schema version mismatch (not an error)",{exportedVersion:n.data.schemaVersion,currentVersion:i.schema.version});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 vi=class{constructor(n,e){this.base=n;this.version=e;this.withMigrationTime=async(n,e)=>{this.overrideNow=()=>this.base.zero(n),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 $a={WebSocket:typeof WebSocket<"u"?WebSocket:void 0,fetch:typeof window<"u"?window.fetch.bind(window):fetch,indexedDB:typeof indexedDB<"u"?indexedDB:void 0,location:typeof window<"u"?window.location:void 0,history:typeof window<"u"?window.history:void 0},xi=class i{constructor(n,e){this.init=n;this.weakRef=n=>this.init.EXPERIMENTAL_weakRefs?new WeakRef(n):new li(n);this.closing=!1;this.pauseRebasing=!1;this.getClient=()=>{throw new N(N.Code.Unexpected,void 0,"Client not yet initialized. This is a Verdant bug, please report it.")};this.reinitialize=async()=>{this.initializedPromise=Ln(this),await this.initializedPromise};if(typeof window>"u"&&!this.init.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 Client options.");this.namespace=this.init.namespace,this.originalNamespace=this.init.namespace,this.time=new vi(new ni,this.init.schema.version),this.log=this.init.log===!1?di:this.init.log||Cn("\u{1F33F}"),this.migrations=n.migrations,this.undoHistory=n.undoHistory||new ct,this.entityEvents=new W,this.internalEvents=new W,this.globalEvents=new W,this.schema=n.schema,this.oldSchemas=n.oldSchemas,this.patchCreator=new Ve(()=>this.time.now),this.persistenceShutdownHandler=n.persistenceShutdownHandler||new ke(this.log),this.config={files:n.files,sync:n.sync,persistence:{disableRebasing:n.disableRebasing,rebaseTimeout:n.rebaseTimeout},queries:n.queries},this.environment={...$a,...n.environment},this.persistence=n.persistence||new ht(this.environment.indexedDB),this.initializedPromise=e||Ln(this),this.initializedPromise.then(()=>{this.log("info","Persistence initialized")})}get meta(){return this.initializedPromise.then(n=>n.meta)}get documents(){return this.initializedPromise.then(n=>n.documents)}get files(){return this.initializedPromise.then(n=>n.files)}get waitForInitialization(){return this.initializedPromise.then(()=>{})}cloneWithOptions(n){return new i({...this.init,...n},this.initializedPromise)}};var Si=class{constructor(n,e){this.schema=n;this.entities=e;this.getOid=(n,e)=>{let t=this.schema.collections[n].primaryKey,r=e[t];return L(r,`Document must have a primary key: ${t.toString()} (got: ${JSON.stringify(e)})`),ee(n,r)};this.addDefaults=(n,e)=>{let t=this.schema.collections[n];return qe(t,e)};this.validate=(n,e)=>{let t=this.schema.collections[n];return os(t.fields,e)};this.create=async(n,e,t={})=>{let r=t,s=this.addDefaults(n,e),o=this.validate(n,s),a=this.getOid(n,o);if(t.access){let l=this.schema.collections[n];t.access!=="shared"&&e[l.primaryKey]&&!t.silenceAccessControlWithPrimaryKeyWarning&&console.warn("Using a custom primary key with access control is not supported. This may result in corrupted documents. Read more about why: https://verdant.dev/docs/sync/access#a-warning-about-custom-primaryKey"),r.access=t.access}return this.entities.create(o,a,r)};this.delete=async(n,e,t={})=>{let r=ee(n,e);return this.entities.delete(r,t)};this.deleteAll=async(n,e={})=>this.entities.deleteAll(n.map(([t,r])=>ee(t,r)),e);this.deleteAllFromCollection=async(n,e,t={})=>this.entities.deleteAll(e.map(r=>ee(n,r)),t);this.clone=async(n,e,t={})=>{if(!qt(e.uid))throw new Error("Cannot clone non-root documents");let r=e.getSnapshot(),s=this.schema.collections[n];if(delete r[s.primaryKey],!s.fields[s.primaryKey].default){if(!t.primaryKey)throw new Error(`Error cloning document from collection ${n}: collection does not have a default on primary key. You must supply a value to options.primaryKey for the clone.`);r[s.primaryKey]=t.primaryKey}return this.create(n,r,t)}}};var Bi=Be(uo(),1);var Yn=Be(Qe(),1);function sc(i){return{id:(0,Yn.default)(),file:i,url:void 0,remote:!1,name:i.name,type:i.type}}function pt(i,n){if(typeof window<"u"&&nt(i)){let e=sc(i);return n(e),xt(e.id)}if(Ut(i)){let e={...i,id:(0,Yn.default)()};return n(e),xt(e.id)}if(Array.isArray(i)){for(let e=0;e<i.length;e++)i[e]=pt(i[e],n);return i}if(typeof i=="object"){for(let e in i)i[e]=pt(i[e],n);return i}return i}var mt=Symbol("entity-file-update"),Ri=Symbol("entity-file-mark-failed"),Xn=Symbol("child-file-changed"),oc,ac,ve=class extends W{constructor(e,{downloadRemote:t=!1,ctx:r,parent:s}){super();this.id=e;this._objectUrl=null;this._fileData=null;this._loading=!0;this._failed=!1;this._downloadRemote=!1;this._uploaded=!1;this.unsubscribes=[];this[oc]=e=>{this.ctx.log("debug","EntityFile updated",this.id,e),this._loading=!1,this._failed=!1,this._fileData=e,e.file&&(this._objectUrl&&"revokeObjectURL"in URL&&URL.revokeObjectURL(this._objectUrl),this.ctx.log("debug","Creating object URL for file",this.id),this._objectUrl=URL.createObjectURL(e.file)),this.emitChange()};this[ac]=e=>{this._failed=!0,this._failedReason=e,this._loading=!1,this.emitChange()};this.onUploaded=e=>{this._fileData??=e,this._uploaded=!0,this.ctx.log("debug","File marked uploaded",this.id,this._fileData),this.emitChange()};this.destroy=()=>{this._objectUrl&&URL.revokeObjectURL(this._objectUrl),this.dispose()};this.ctx=r,this.parent=s,this._downloadRemote=t,this.unsubscribes.push(this.ctx.internalEvents.subscribe(`fileUploaded:${e}`,this.onUploaded))}static{oc=mt,ac=Ri}get downloadRemote(){return this._downloadRemote}get isFile(){return!0}get isUploaded(){return this._uploaded||this._fileData?.remote||!1}get error(){return this._failedReason||null}emitChange(){this.parent[Xn](this),this.emit("change")}get url(){return this.loading?null:this._objectUrl?this._objectUrl:this._fileData?.url??null}get name(){return this._fileData?.name??null}get type(){return this._fileData?.type??null}get loading(){return this._loading}get failed(){return this._failed}getSnapshot(){return{id:this.id,url:this._objectUrl??this._fileData?.url??void 0,name:this.name??"unknown-file",remote:this._fileData?.remote??!1,type:this.type??"",file:this._fileData?.file}}};var Fi=class{constructor({initial:n,ctx:e}){this.cache=new Map;this.get=n=>{let e=this.getCached(n.oid);if(e)return e;let t=new me(n);return this.cache.set(n.oid,this.ctx.weakRef(t)),t};this.has=n=>this.cache.has(n);this.getCached=n=>{if(this.cache.has(n)){let t=this.cache.get(n)?.deref();if(t)return t;this.cache.delete(n)}return null};if(this.ctx=e,n)for(let t of n)this.cache.set(t.oid,e.weakRef(t))}};function ho(i,n,e){let t={previousValue:i.get(n),isLocal:!1};function r(s){if(i.deleted)return;let o=i.get(n);o!==this.previousValue&&(this.isLocal=s.isLocal,e(o,this),this.previousValue=o)}return i.subscribe("change",r.bind(t))}var fo=Symbol("private entity context key"),lc,me=class i extends W{constructor({oid:e,schema:t,entityFamily:r,parent:s,ctx:o,metadataFamily:a,readonlyKeys:l,files:p,storeEvents:f,deleteSelf:S,fieldPath:w}){super();this.fieldPath=[];this._viewData=void 0;this.validationError=void 0;this.cachedDeepUpdatedAt=null;this.wasDeletedLastChange=!1;this.cachedView=void 0;this.onAdd=(e,t)=>{t.oid===this.oid&&this.addConfirmedData(t)};this.onReplace=(e,t)=>{t.oid===this.oid&&this.replaceAllData(t)};this.onResetAll=()=>{this.resetAllData()};this.childIsNull=e=>{if(e instanceof i){let t=e.view;return t==null}return e==null};this.getFieldSchema=e=>{let t=Ne(this.schema,e);return L(t,`No schema for key ${e}`),t};this.validate=Nr(()=>(this.validationError=Te({field:this.schema,value:this.rawView,fieldPath:this.fieldPath,expectRefs:!0})??void 0,this.validationError),()=>[this.viewData]);this.viewWithMappedChildren=e=>{let t=this.view;if(!t)return null;if(Array.isArray(t)){let r=t.map(s=>s instanceof i||s instanceof ve?e(s):s);return z(r,this.oid),r}else{let r=Object.entries(t).reduce((s,[o,a])=>(a instanceof i||a instanceof ve?s[o]=e(a):s[o]=a,s),{});return z(r,this.oid),r}};this.rawViewWithMappedChildren=e=>{let t=this.rawView;if(!t)return null;if(Array.isArray(t)){let r=t.map((s,o)=>$(s)?e(this.getChild(o,s.id)):s);return z(r,this.oid),r}else{let r=Object.entries(t).reduce((s,[o,a])=>($(a)?s[o]=e(this.getChild(o,a.id)):s[o]=a,s),{});return z(r,this.oid),r}};this.getSnapshot=()=>this.viewWithMappedChildren(e=>e.getSnapshot());this.getUnprunedSnapshot=()=>this.rawViewWithMappedChildren(e=>e instanceof ve?e.getSnapshot():e.getUnprunedSnapshot());this.addPendingOperations=e=>{this.ctx.log("debug","Entity: adding pending operations",this.oid,e),this.deepInvalid&&(this.ctx.log("warn","Changes are being applied to a pruned entity. This means that the pruned version is being treated as the new baseline and any pruned invalid data is lost.",this.oid),this.canonizePrunedVersion()),this.applyPendingOperations(e)};this.applyPendingOperations=e=>{if(this.access)for(let r of e)r.authz=this.access;let t=this.metadataFamily.addPendingData(e);for(let r of t)this.change(r)};this.getPruneDiff=()=>{let e=this.getSnapshot(),t=this.getUnprunedSnapshot();return this.patchCreator.createDiff(t,e,{authz:this.access,merge:!1})};this.canonizePrunedVersion=()=>{this.applyPendingOperations(this.getPruneDiff())};this.addConfirmedData=e=>{this.ctx.log("debug","Entity: adding confirmed data",this.oid);let t=this.metadataFamily.addConfirmedData(e);for(let r of t)this.change(r)};this.replaceAllData=e=>{this.ctx.log("debug","Entity: replacing all data",this.oid);let t=this.metadataFamily.replaceAllData(e);for(let r of t)this.change(r)};this.resetAllData=()=>{this.ctx.log("debug","Entity: resetting all data",this.oid),this.cachedDeepUpdatedAt=null,this.cachedView=void 0,this._viewData=void 0;let e=this.metadataFamily.replaceAllData({});for(let t of e)this.change(t)};this.invalidateCachedView=()=>{this._viewData=void 0,this.cachedView=void 0};this.invalidate=e=>{if(e.oid===this.oid)this.invalidateCachedView();else{let t=this.entityFamily.getCached(e.oid);t&&t instanceof i&&t.invalidate(e)}};this.change=e=>{if(e.oid===this.oid)this.invalidateCachedView(),this.parent?this.changeNested(e):this.changeRoot(e);else{let t=this.entityFamily.getCached(e.oid);t&&t instanceof i&&t.change(e)}};this.changeRoot=e=>{this.deleted?this.wasDeletedLastChange?this.ctx.log("debug","Entity already deleted, not emitting delete or change events",this.oid):(this.ctx.log("debug","Entity deleted",this.oid),this.emit("delete",{isLocal:e.isLocal}),this.wasDeletedLastChange=!0):(this.wasDeletedLastChange&&(this.ctx.log("debug","Entity restored",this.oid),this.emit("restore",{isLocal:e.isLocal}),this.wasDeletedLastChange=!1),this.deepChange(this,e),this.emit("change",{isLocal:e.isLocal}))};this.changeNested=e=>{if(this.deleted){this.ctx.log("debug","Entity deleted, not emitting change",this.oid);return}this.deepChange(this,e),this.emit("change",{isLocal:e.isLocal})};this.deepChange=(e,t)=>{if(this.deleted){this.ctx.log("debug","Entity deleted, not emitting deep change",this.oid);return}this.cachedDeepUpdatedAt=null,this.cachedView=void 0,this.emit("changeDeep",e,t),this.parent?.deepChange(e,t)};this[lc]=e=>{this.deepChange(this,{isLocal:!1,oid:this.oid})};this.getChild=(e,t)=>{let r=Ne(this.schema,e);if(!r)throw new Error(`No schema for key ${String(e)} in ${JSON.stringify(this.schema)}`);return this.entityFamily.get({oid:t,schema:r,entityFamily:this.entityFamily,metadataFamily:this.metadataFamily,parent:this,ctx:this.ctx,files:this.files,fieldPath:[...this.fieldPath,e],storeEvents:this.storeEvents,deleteSelf:this.delete.bind(this,e)})};this.subscribeToField=(e,t,r)=>ho(this,e,r);this.get=e=>{Zn(e);let t=this.rawView;if(!t)throw new Error(`Cannot access data at key ${e} on deleted entity ${this.oid}`);let r=t[e],s=Ne(this.schema,e);if(!s)throw new Error(`No schema for key ${String(e)} in ${JSON.stringify(this.schema)}`);if($(r))if(fe(r)){if(s.type!=="file"){if(pe(s))return null;if(Fe(s))return Pe(s);throw new Error(`Expected file schema for key ${String(e)}, got ${s.type}`)}return this.files.get(r.id,{downloadRemote:!!s.downloadRemote,ctx:this.ctx,parent:this})}else{if(s.type==="file"){if(console.error(`Expected file ref for key ${String(e)}, got ${r}`),pe(s))return null;if(Fe(s))return Pe(s);throw new Error(`No valid value for key ${String(e)} in ${JSON.stringify(this.schema)}`)}let o=this.getChild(e,r.id);if(o.deepInvalid){if(s.type==="array"||s.type==="map")return o;if(pe(s))return null;if(Fe(s)){let a=o.getUnprunedSnapshot(),l=this.patchCreator.createDiff(a,Pe(s),{merge:!1,mergeUnknownObjects:!0,authz:this.access});return this.processPrunedChild(e,o,l)}}return o}else{if(this.schema.type==="map"&&r===void 0)return;if(Te({field:s,value:r,fieldPath:[...this.fieldPath,e],depth:1,requireDefaults:!0}))if(Fe(s)){if(ss(s))return Pe(s);let o=Pe(s),a=Oe(this.oid),l=this.patchCreator.createInitialize(o,a,this.access);l.push(...this.patchCreator.createSet(this.oid,e,ie(a),this.access));let p=this.getChild(e,a);return this.processPrunedChild(e,p,l)}else return;return r}};this.processPrunedChild=(e,t,r)=>{this.ctx.log("warn","Replacing invalid child object field with ephemeral, valid data",this.oid,e);let s=this.metadataFamily.addEphemeralData(r);for(let o of s)this.invalidate(o);return t};this.getOrSet=(e,t)=>{Zn(e);let r=this.get(e);return r||(this.set(e,t),this.get(e))};this.processInputValue=(e,t)=>{if(this.readonlyKeys.includes(t))throw new Error(`Cannot set readonly key ${t.toString()}`);nt(e)||(e=de(e,!1));let r=Ne(this.schema,t);if(r){Je(e,r);let s=Te({field:r,value:e,fieldPath:[...this.fieldPath,t]});if(s)throw new Error(`Validation error: ${s.message}`,{cause:s})}return pt(e,s=>this.files.add(s,this))};this.getDeleteMode=e=>{if(this.readonlyKeys.includes(e))return!1;if(this.schema.type==="any"||this.schema.type==="map")return"delete";if(this.schema.type==="object"){let t=this.schema.properties[e];if(!t||t.type==="any")return"delete";if(t.type==="map")return!1;if(t.nullable)return"null"}return!1};this.getItemRefValue=e=>{if(e instanceof i)return ie(e.oid);if(e instanceof ve)return xt(e.id);if(typeof e=="object"){let t=G(e);if(!t||!this.entityFamily.has(t))throw new Error(`Cannot move object ${JSON.stringify(e)} which does not exist in this list`);return ie(t)}else return e};this.set=(e,t,r)=>{Zn(e),!(!r?.force&&this.get(e)===t)&&(this.isList?this.addPendingOperations(this.patchCreator.createListSet(this.oid,e,this.processInputValue(t,e))):this.addPendingOperations(this.patchCreator.createSet(this.oid,e,this.processInputValue(t,e))))};this.getAll=()=>this.view;this.delete=e=>{if(this.isList)cc(e),this.addPendingOperations(this.patchCreator.createListDelete(this.oid,e));else{let t=this.getDeleteMode(e);if(!t)throw new Error(`Cannot delete key ${e.toString()} - the property is not marked as optional in the schema.`);t==="delete"?this.addPendingOperations(this.patchCreator.createRemove(this.oid,e)):this.addPendingOperations(this.patchCreator.createSet(this.oid,e,null))}};this.keys=()=>this.view?Object.keys(this.view):[];this.entries=()=>this.view?Object.entries(this.view):[];this.values=()=>this.view?Object.values(this.view):[];this.update=(e,{merge:t=!0,replaceSubObjects:r=!1,preserveUndefined:s=!1,dangerouslyDisableMerge:o=!1}={})=>{if(!t&&!o&&this.schema.type!=="any"&&this.schema.type!=="map")throw new Error('Cannot use .update without merge if the field has a strict schema type. merge: false is only available on "any" or "map" types.');let a={};z(a,this.oid);for(let[l,p]of Object.entries(e)){if(this.readonlyKeys.includes(l))throw new Error(`Cannot set readonly key ${l.toString()}`);if(p===void 0&&!s)continue;let f=Ne(this.schema,l);f&&Je(p,f),a[l]=this.processInputValue(p,l)}this.addPendingOperations(this.patchCreator.createDiff(this.getSnapshot(),a,{mergeUnknownObjects:!r,merge:t}))};this.push=e=>{this.addPendingOperations(this.patchCreator.createListPush(this.oid,this.processInputValue(e,this.view.length)))};this.insert=(e,t)=>{this.addPendingOperations(this.patchCreator.createListInsert(this.oid,e,this.processInputValue(t,e)))};this.move=(e,t)=>{this.addPendingOperations(this.patchCreator.createListMoveByIndex(this.oid,e,t))};this.moveItem=(e,t)=>{let r=this.getItemRefValue(e);if($(r))this.addPendingOperations(this.patchCreator.createListMoveByRef(this.oid,r,t));else{let s=this.view.indexOf(e);if(s===-1)throw new Error(`Cannot move item ${JSON.stringify(e)} which does not exist in this list`);this.move(s,t)}};this.add=e=>{this.addPendingOperations(this.patchCreator.createListAdd(this.oid,this.processInputValue(e,this.view.length)))};this.removeAll=e=>{this.addPendingOperations(this.patchCreator.createListRemove(this.oid,this.getItemRefValue(e)))};this.removeFirst=e=>{this.addPendingOperations(this.patchCreator.createListRemove(this.oid,this.getItemRefValue(e),"first"))};this.removeLast=e=>{this.addPendingOperations(this.patchCreator.createListRemove(this.oid,this.getItemRefValue(e),"last"))};this.map=e=>this.view.map(e);this.filter=e=>this.view.filter(e);this.has=e=>{if(!this.isList)throw new Error("has() is only available on list entities");let t=this.getItemRefValue(e);return $(t)?this.view.some(r=>{if($(r))return De(r,t)}):this.view.includes(e)};this.forEach=e=>{this.view.forEach(e)};this.reduce=(e,t)=>this.view.reduce(e,t);this.some=e=>this.view.some(e);this.every=e=>this.view.every(e);this.find=e=>this.view.find(e);this.includes=this.has;this.deleteSelf=()=>this._deleteSelf();this.__getViewData__=(e,t)=>this.metadataFamily.get(e).computeView(t==="confirmed");this.__getFamilyOids__=()=>this.metadataFamily.getAllOids();this.__discardPendingOperation__=e=>{this.metadataFamily.discardPendingOperation(e),this.invalidateCachedView()};L(!!e,"oid is required"),this.oid=e,this.readonlyKeys=l||[],this.ctx=o,this.files=p,this.schema=t,this.fieldPath=w||[],this.entityFamily=r||new Fi({initial:[this],ctx:o}),this.metadataFamily=a,this.storeEvents=f,this.parent=s,this._deleteSelf=S,this.parent||(f.add.attach(this.onAdd),f.replace.attach(this.onReplace),f.resetAll.attach(this.onResetAll))}get[fo](){return this.ctx}get metadata(){return this.metadataFamily.get(this.oid)}get patchCreator(){return this.ctx.patchCreator}get viewData(){return this._viewData===void 0&&(this._viewData=this.metadata.computeView(),this.validate()),this._viewData}get rawView(){return this.viewData.view}get view(){if(this.cachedView!==void 0)return this.cachedView;if(this.viewData.deleted)return null;let e=this.rawView;if(!e&&!pe(this.schema)||this.schema.type==="array"&&!Array.isArray(e)||(this.schema.type==="object"||this.schema.type==="map")&&!V(e))return Fe(this.schema)?Pe(this.schema):null;let r=this.isList?[]:{};if(z(r,this.oid),Array.isArray(e)){let s=Ne(this.schema,0);if(!s)this.ctx.log("error","No child field schema for list entity.",this.oid);else for(let o=0;o<e.length;o++){let a=this.get(o);this.childIsNull(a)&&!pe(s)?this.ctx.log("error","Child missing in non-nullable field",this.oid,"index:",o):r.push(a)}}else if(V(e)){let s=this.schema.type==="object"?Object.keys(this.schema.properties):Object.keys(e);for(let o of s){let a=Ne(this.schema,o);if(!a){this.ctx.log("error","No child field schema for object entity at key",o),this.schema.type==="map"?r={}:r=null;break}let l=this.get(o);if(this.childIsNull(l)&&!pe(a)){if(this.ctx.log("error","Child entity is missing for non-nullable field",this.oid,"key:",o),this.schema.type!=="map"){r=null;break}}else pe(a)&&l===void 0?r[o]=null:r[o]=l}}return this.cachedView=r,r}get uid(){return this.oid}get deleted(){return this.viewData.deleted||this.view===null}get quickDeleted(){return this._viewData?.deleted||this.cachedView===null}get invalid(){return!!this.validate()}get deepInvalid(){if(this.invalid)return!0;if(Array.isArray(this.rawView)){for(let e=0;e<this.rawView.length;e++)if(re(this.rawView[e])&&this.getChild(e,this.rawView[e].id).deepInvalid)return!0}else if(V(this.rawView)){for(let e in this.rawView)if(re(this.rawView[e])&&this.getChild(e,this.rawView[e].id).deepInvalid)return!0}return!1}get isList(){return this.schema.type==="array"||Array.isArray(this.viewData.view)}get updatedAt(){return this.viewData.updatedAt}get deepUpdatedAt(){if(this.cachedDeepUpdatedAt)return this.cachedDeepUpdatedAt;let e=this.updatedAt;return this.isList?this.forEach(t=>{if(t instanceof i){let r=t.deepUpdatedAt;r&&(!e||r>e)&&(e=r)}}):this.values().forEach(t=>{if(t instanceof i){let r=t.deepUpdatedAt;r&&(!e||r>e)&&(e=r)}}),this.cachedDeepUpdatedAt=e,e}get isOutdatedVersion(){return this.parent?this.parent.isOutdatedVersion:this.viewData.fromOlderVersion}get namespace(){return this.ctx.namespace}get access(){return this.viewData.authz}get isAuthorized(){return!!this.access}get size(){return this.isList?this.length:this.keys().length}get length(){return this.view.length}[(lc=Xn,Symbol.iterator)](){let e=0,t=this.view?.length;return{next:()=>e<t?{value:this.get(e++),done:!1}:{value:void 0,done:!0}}}};function Zn(i){if(typeof i=="symbol")throw new Error("Symbol keys aren't supported")}function cc(i){if(typeof i!="number")throw new Error("Only number keys are supported in list entities")}function po(i){return i[fo].getClient()}var er=class{constructor({oid:n,ctx:e,confirmedOperations:t,pendingOperations:r,baseline:s}){this.baseline=null;this.confirmedOperations=[];this.ephemeralOperations=[];this.pendingOperations=[];this.computeView=(n=!1)=>{let e=de(this.baseline?.snapshot??void 0),t=this.baseline?.timestamp??null,r=this.baseline?.authz,s=this.applyOperations(e,!e,this.confirmedOperations,t,t);s.futureSeen&&this.ctx.globalEvents.emit("futureSeen",s.futureSeen),s.authz&&(r=s.authz);let o=!this.ephemeralOperations||n?s:this.applyOperations(s.view,s.deleted,this.ephemeralOperations,s.latestTimestamp,null),a=n?s:this.applyOperations(o.view,o.deleted,this.pendingOperations,o.latestTimestamp,null);a.authz&&(r=a.authz),a.view&&z(a.view,this.oid);let l=!!s.latestTimestamp&&Pn(s.latestTimestamp,this.ctx.time.now)<0,p=!this.baseline&&!this.pendingOperations.length&&!this.confirmedOperations.length;p&&this.ctx.log("warn",`Tried to load Entity ${this.oid} with no data`);let f=a.latestTimestamp??s.latestTimestamp??t,S=f?_n(f):0;return!a.view&&!a.deleted&&!p&&this.ctx.log("warn",`Entity ${this.oid} has no view, no deleted flag, and not empty`),{view:a.view??void 0,deleted:a.deleted,empty:p,fromOlderVersion:l,updatedAt:S,latestTimestamp:f,authz:r}};this.addBaseline=n=>{if(!(this.baseline&&this.baseline.timestamp>=n.timestamp))for(this.baseline=n;this.confirmedOperations[0]?.timestamp<n.timestamp;)this.confirmedOperations.shift()};this.addConfirmedOperations=n=>{let e=0;for(let t of n){let r=this.confirmedOperations.findIndex(o=>o.timestamp>=t.timestamp);r!==-1?this.confirmedOperations[r].timestamp!==t.timestamp&&(this.confirmedOperations.splice(r,0,t),e++):(this.confirmedOperations.push(t),e++);let s=this.pendingOperations.length;this.discardPendingOperation(t),e-=s-this.pendingOperations.length}return e};this.addPendingOperation=n=>{this.pendingOperations.push(n)};this.addEphemeralOperation=n=>{this.ephemeralOperations||(this.ephemeralOperations=[]),this.ephemeralOperations.push(n)};this.clearEphemeralOperations=()=>{let n=this.ephemeralOperations;return this.ephemeralOperations=[],n};this.discardPendingOperation=n=>{this.pendingOperations=this.pendingOperations.filter(e=>e.timestamp!==n.timestamp)};this.applyOperations=(n,e,t,r,s)=>{let o,a,l=this.ctx.time.now;for(let p of t)if(!(s&&p.timestamp<=s)){if(Pn(p.timestamp,l)>0){o=p.timestamp;continue}if(p.data.op==="delete")e=!0;else try{n=Me(n,p.data),p.data.op==="initialize"&&(e=!1,p.authz&&(a=p.authz))}catch(f){throw this.ctx.log("critical",`Failed to apply operation to entity ${this.oid}: ${JSON.stringify(p)}`,f),f}(!r||p.timestamp>r)&&(r=p.timestamp)}return{view:n,latestTimestamp:r??null,deleted:e,futureSeen:o,authz:a}};L(n,"oid is required"),this.ctx=e,this.oid=n,t&&(this.confirmedOperations=t),r&&(this.pendingOperations=r),s&&(this.baseline=s)}},Ti=class{constructor({ctx:n,onPendingOperations:e,rootOid:t}){this.entities=new Map;this.get=n=>(L(n,"oid is required"),this.entities.has(n)||this.entities.set(n,new er({oid:n,ctx:this.ctx})),this.entities.get(n));this.getAllOids=()=>Array.from(this.entities.keys());this.addConfirmedData=({baselines:n=[],operations:e={},isLocal:t=!1})=>{let r={};for(let s of n){if(!je(this.rootOid,s.oid))throw new Error(`Invalid baseline for entity ${this.rootOid}: `+JSON.stringify(s));this.get(s.oid).addBaseline(s),r[s.oid]??={oid:s.oid,isLocal:t}}for(let[s,o]of Object.entries(e)){if(!je(this.rootOid,s))throw new Error(`Invalid operations for entity ${this.rootOid}: `+JSON.stringify(o));this.get(s).addConfirmedOperations(o)!==0&&(r[s]??={oid:s,isLocal:t})}return Object.values(r)};this.addPendingData=n=>{this.flushAllEphemeral();let e={};for(let t of n)this.get(t.oid).addPendingOperation(t),e[t.oid]??={oid:t.oid,isLocal:!0};return this.onPendingOperations(n),Object.values(e)};this.ephemeralMemo=new Array;this.flushAllEphemeral=()=>{for(let n of this.entities.values()){let e=n.clearEphemeralOperations();e&&this.ephemeralMemo.push(...e)}if(this.ephemeralMemo.length){let n=this.ephemeralMemo.slice();this.ephemeralMemo.length=0,this.addPendingData(n)}};this.addEphemeralData=n=>{let e={};for(let t of n)this.get(t.oid).addEphemeralOperation(t),e[t.oid]??={oid:t.oid,isLocal:!0};return Object.values(e)};this.replaceAllData=({operations:n={},baselines:e=[]})=>{let t=Array.from(this.entities.keys());this.entities.clear();let r={};for(let s of t)r[s]={oid:s,isLocal:!1};for(let s of e){if(!je(this.rootOid,s.oid))throw new Error(`Invalid baseline for entity ${this.rootOid}: `+JSON.stringify(s));this.get(s.oid).addBaseline(s),r[s.oid]??={oid:s.oid,isLocal:!1}}for(let[s,o]of Object.entries(n)){if(!je(this.rootOid,s))throw new Error(`Invalid operations for entity ${this.rootOid}: `+JSON.stringify(o));this.get(s).addConfirmedOperations(o),r[s]??={oid:s,isLocal:!1}}return Object.values(r)};this.discardPendingOperation=n=>{this.entities.get(n.oid)?.discardPendingOperation(n)};this.ctx=n,this.rootOid=t,this.onPendingOperations=e}};var kt="@@default",ki=class{constructor({batchTimeout:n=200,ctx:e,entities:t}){this.currentBatchKey=kt;this.flushOperations=async(n,e,t)=>{if(!n.length)return;this.ctx.log("debug","Flushing",n.length,"operations from batch",e,"to storage / sync");let r=[],s={};for(let o=n.length-1;o>=0;o--){let a=n[o],l=s[a.oid];if(l&&Fr(a,l)){this.entities.discardPendingOperation(a);continue}let p=Rr(a);p!==!1&&(s[a.oid]||(s[a.oid]=new Set),s[a.oid].add(p)),r.unshift(a)}for(let o of r)o.timestamp=this.ctx.time.now;await this.commitOperations(r,t)};this.commitOperations=async(n,e)=>{if(n.length){if(e.undoable){let t=await this.createUndo({ops:n,source:e.source});t&&this.ctx.undoHistory.addUndo(t)}await this.entities.addData({operations:n,baselines:[],isLocal:!0})}};this.addOperations=n=>{n.length&&(this.batcher.add({key:this.currentBatchKey,items:n}),this.ctx.log("debug","added",n.length,"ops to batch",this.currentBatchKey,", size = ",this.batcher.getSize(this.currentBatchKey)))};this.batch=({undoable:n=!0,batchName:e=vt(),max:t=null,timeout:r=this.defaultBatchTimeout}={})=>{let s=this.batcher.add({key:e,max:t,timeout:r,items:[],userData:{undoable:n}}),o={run:a=>(this.currentBatchKey=e,a(),this.currentBatchKey=kt,o),commit:async()=>(await this.batcher.flush(kt),s.flush()),flush:()=>o.commit(),discard:()=>{this.batcher.discard(e)}};return o};this.flushAll=()=>(this.ctx.log("debug","Flushing all operations"),Promise.all(this.batcher.flushAll()));this.createUndo=async n=>{let e=await this.getInverseOperations(n);return e.length?async()=>{let t=await this.createUndo({ops:e,source:n.source,isRedo:!0});for(let r of e)r.timestamp=this.ctx.time.now;return this.ctx.log("debug",n.isRedo?"Redo":"Undo",e,`
|
|
15
|
+
${JSON.stringify(i.schema)}`);let s=new wi(await e.openDocuments(i),i);if(!i.schema.wip){let o=await i.persistence.getNamespaces();for(let a of o)a.startsWith("@@wip_")&&(i.log("debug","Cleaning up old WIP namespace",a),await i.persistence.deleteNamespace(a,i))}return{meta:t,files:r,documents:s}}async function As(i,n){i.log("info","Importing data from export");let e=i.oldSchemas?.find(l=>l.version===n.data.schemaVersion);if(!e)throw new Error(`Could not find schema for version ${n.data.schemaVersion}`);let t=`@@import_${Date.now()}`,r=i.cloneWithOptions({schema:e,namespace:t,disableRebasing:!0,persistenceShutdownHandler:new ke(i.log)});await r.reinitialize();let s=await r.meta;await s.resetFrom(n.data);let o=new Set;for(let l of n.data.baselines)o.add(K(l.oid));for(let l of n.data.operations)o.add(K(l.oid));let a=await Promise.all(Array.from(o).map(async l=>{let p=await s.getDocumentSnapshot(l);return{oid:l,getSnapshot:()=>p}}));if(await(await r.documents).saveEntities(a),await(await r.files).import(n),i.log("debug","Imported data into temporary namespace",t),await r.persistenceShutdownHandler.shutdown(),e.version!==i.schema.version){let l=r.cloneWithOptions({persistenceShutdownHandler:new ke(i.log),schema:i.schema,oldSchemas:i.oldSchemas});await l.reinitialize(),i.log("debug","Upgraded imported data to current schema"),await l.persistenceShutdownHandler.shutdown(),i.log("debug","Shut down upgraded databases")}if(await i.persistenceShutdownHandler.shutdown(),await i.persistence.copyNamespace(t,i.namespace,i),i.log("debug","Copied imported data to primary namespace"),await i.reinitialize(),i.log("debug","Reinitialized primary persistence layer"),n.data.schemaVersion===i.schema.version){let l=await(await i.meta).stats();if(l.operationsSize.count!==n.data.operations.length)throw i.log("critical","Imported operations count mismatch","expected",n.data.operations.length,"actual",l.operationsSize.count),new N(N.Code.ImportFailed,void 0,"Imported operations count mismatch");if(l.baselinesSize.count!==n.data.baselines.length)throw i.log("critical","Imported documents count mismatch","expected",n.data.baselines.length,"actual",l.baselinesSize.count),new N(N.Code.ImportFailed,void 0,"Imported documents count mismatch")}else i.log("debug","Skipping integrity check due to schema version mismatch (not an error)",{exportedVersion:n.data.schemaVersion,currentVersion:i.schema.version});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 vi=class{constructor(n,e){this.base=n;this.version=e;this.withMigrationTime=async(n,e)=>{this.overrideNow=()=>this.base.zero(n),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 $a={WebSocket:typeof WebSocket<"u"?WebSocket:void 0,fetch:typeof window<"u"?window.fetch.bind(window):fetch,indexedDB:typeof indexedDB<"u"?indexedDB:void 0,location:typeof window<"u"?window.location:void 0,history:typeof window<"u"?window.history:void 0},xi=class i{constructor(n,e){this.init=n;this.weakRef=n=>this.init.EXPERIMENTAL_weakRefs?new WeakRef(n):new li(n);this.closing=!1;this.pauseRebasing=!1;this.getClient=()=>{throw new N(N.Code.Unexpected,void 0,"Client not yet initialized. This is a Verdant bug, please report it.")};this.reinitialize=async()=>{this.initializedPromise=Ln(this),await this.initializedPromise};if(typeof window>"u"&&!this.init.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 Client options.");this.namespace=this.init.namespace,this.originalNamespace=this.init.namespace,this.time=new vi(new ni,this.init.schema.version),this.log=this.init.log===!1?di:this.init.log||Cn("\u{1F33F}"),this.migrations=n.migrations,this.undoHistory=n.undoHistory||new ct,this.entityEvents=new W,this.internalEvents=new W,this.globalEvents=new W,this.schema=n.schema,this.oldSchemas=n.oldSchemas,this.patchCreator=new Ve(()=>this.time.now),this.persistenceShutdownHandler=n.persistenceShutdownHandler||new ke(this.log),this.config={files:n.files,sync:n.sync,persistence:{disableRebasing:n.disableRebasing,rebaseTimeout:n.rebaseTimeout},queries:n.queries},this.environment={...$a,...n.environment},this.persistence=n.persistence||new ht(this.environment.indexedDB),this.initializedPromise=e||Ln(this),this.initializedPromise.then(()=>{this.log("info","Persistence initialized")})}get meta(){return this.initializedPromise.then(n=>n.meta)}get documents(){return this.initializedPromise.then(n=>n.documents)}get files(){return this.initializedPromise.then(n=>n.files)}get waitForInitialization(){return this.initializedPromise.then(()=>{})}cloneWithOptions(n){return new i({...this.init,...n},this.initializedPromise)}};var Si=class{constructor(n,e){this.schema=n;this.entities=e;this.getOid=(n,e)=>{let t=this.schema.collections[n].primaryKey,r=e[t];return L(r,`Document must have a primary key: ${t.toString()} (got: ${JSON.stringify(e)})`),ee(n,r)};this.addDefaults=(n,e)=>{let t=this.schema.collections[n];return qe(t,e)};this.validate=(n,e)=>{let t=this.schema.collections[n];return os(t.fields,e)};this.create=async(n,e,t={})=>{let r=t,s=this.addDefaults(n,e),o=this.validate(n,s),a=this.getOid(n,o);if(t.access){let l=this.schema.collections[n];t.access!=="shared"&&e[l.primaryKey]&&!t.silenceAccessControlWithPrimaryKeyWarning&&console.warn("Using a custom primary key with access control is not supported. This may result in corrupted documents. Read more about why: https://verdant.dev/docs/sync/access#a-warning-about-custom-primaryKey"),r.access=t.access}return this.entities.create(o,a,r)};this.delete=async(n,e,t={})=>{let r=ee(n,e);return this.entities.delete(r,t)};this.deleteAll=async(n,e={})=>this.entities.deleteAll(n.map(([t,r])=>ee(t,r)),e);this.deleteAllFromCollection=async(n,e,t={})=>this.entities.deleteAll(e.map(r=>ee(n,r)),t);this.clone=async(n,e,t={})=>{if(!qt(e.uid))throw new Error("Cannot clone non-root documents");let r=e.getSnapshot(),s=this.schema.collections[n];if(delete r[s.primaryKey],!s.fields[s.primaryKey].default){if(!t.primaryKey)throw new Error(`Error cloning document from collection ${n}: collection does not have a default on primary key. You must supply a value to options.primaryKey for the clone.`);r[s.primaryKey]=t.primaryKey}return this.create(n,r,t)}}};var Bi=Be(uo(),1);var Yn=Be(Qe(),1);function sc(i){return{id:(0,Yn.default)(),file:i,url:void 0,remote:!1,name:i.name,type:i.type}}function pt(i,n){if(typeof window<"u"&&nt(i)){let e=sc(i);return n(e),xt(e.id)}if(Ut(i)){let e={...i,id:(0,Yn.default)()};return n(e),xt(e.id)}if(Array.isArray(i)){for(let e=0;e<i.length;e++)i[e]=pt(i[e],n);return i}if(typeof i=="object"){for(let e in i)i[e]=pt(i[e],n);return i}return i}var mt=Symbol("entity-file-update"),Ri=Symbol("entity-file-mark-failed"),Xn=Symbol("child-file-changed"),oc,ac,ve=class extends W{constructor(e,{downloadRemote:t=!1,ctx:r,parent:s}){super();this.id=e;this._objectUrl=null;this._fileData=null;this._loading=!0;this._failed=!1;this._downloadRemote=!1;this._uploaded=!1;this.unsubscribes=[];this[oc]=e=>{this.ctx.log("debug","EntityFile updated",this.id,e),this._loading=!1,this._failed=!1,this._fileData=e,e.file&&(this._objectUrl&&"revokeObjectURL"in URL&&URL.revokeObjectURL(this._objectUrl),this.ctx.log("debug","Creating object URL for file",this.id),this._objectUrl=URL.createObjectURL(e.file)),this.emitChange()};this[ac]=e=>{this._failed=!0,this._failedReason=e,this._loading=!1,this.emitChange()};this.onUploaded=e=>{this._fileData??=e,this._uploaded=!0,this.ctx.log("debug","File marked uploaded",this.id,this._fileData),this.emitChange()};this.destroy=()=>{this._objectUrl&&URL.revokeObjectURL(this._objectUrl),this.dispose()};this.ctx=r,this.parent=s,this._downloadRemote=t,this.unsubscribes.push(this.ctx.internalEvents.subscribe(`fileUploaded:${e}`,this.onUploaded))}static{oc=mt,ac=Ri}get downloadRemote(){return this._downloadRemote}get isFile(){return!0}get isUploaded(){return this._uploaded||this._fileData?.remote||!1}get error(){return this._failedReason||null}emitChange(){this.parent[Xn](this),this.emit("change")}get url(){return this.loading?null:this._objectUrl?this._objectUrl:this._fileData?.url??null}get name(){return this._fileData?.name??null}get type(){return this._fileData?.type??null}get loading(){return this._loading}get failed(){return this._failed}getSnapshot(){return{id:this.id,url:this._fileData?.url??this._objectUrl??void 0,name:this.name??"unknown-file",remote:this._fileData?.remote??!1,type:this.type??"",file:this._fileData?.file}}};var Fi=class{constructor({initial:n,ctx:e}){this.cache=new Map;this.get=n=>{let e=this.getCached(n.oid);if(e)return e;let t=new me(n);return this.cache.set(n.oid,this.ctx.weakRef(t)),t};this.has=n=>this.cache.has(n);this.getCached=n=>{if(this.cache.has(n)){let t=this.cache.get(n)?.deref();if(t)return t;this.cache.delete(n)}return null};if(this.ctx=e,n)for(let t of n)this.cache.set(t.oid,e.weakRef(t))}};function ho(i,n,e){let t={previousValue:i.get(n),isLocal:!1};function r(s){if(i.deleted)return;let o=i.get(n);o!==this.previousValue&&(this.isLocal=s.isLocal,e(o,this),this.previousValue=o)}return i.subscribe("change",r.bind(t))}var fo=Symbol("private entity context key"),lc,me=class i extends W{constructor({oid:e,schema:t,entityFamily:r,parent:s,ctx:o,metadataFamily:a,readonlyKeys:l,files:p,storeEvents:f,deleteSelf:S,fieldPath:w}){super();this.fieldPath=[];this._viewData=void 0;this.validationError=void 0;this.cachedDeepUpdatedAt=null;this.wasDeletedLastChange=!1;this.cachedView=void 0;this.onAdd=(e,t)=>{t.oid===this.oid&&this.addConfirmedData(t)};this.onReplace=(e,t)=>{t.oid===this.oid&&this.replaceAllData(t)};this.onResetAll=()=>{this.resetAllData()};this.childIsNull=e=>{if(e instanceof i){let t=e.view;return t==null}return e==null};this.getFieldSchema=e=>{let t=Ne(this.schema,e);return L(t,`No schema for key ${e}`),t};this.validate=Nr(()=>(this.validationError=Te({field:this.schema,value:this.rawView,fieldPath:this.fieldPath,expectRefs:!0})??void 0,this.validationError),()=>[this.viewData]);this.viewWithMappedChildren=e=>{let t=this.view;if(!t)return null;if(Array.isArray(t)){let r=t.map(s=>s instanceof i||s instanceof ve?e(s):s);return z(r,this.oid),r}else{let r=Object.entries(t).reduce((s,[o,a])=>(a instanceof i||a instanceof ve?s[o]=e(a):s[o]=a,s),{});return z(r,this.oid),r}};this.rawViewWithMappedChildren=e=>{let t=this.rawView;if(!t)return null;if(Array.isArray(t)){let r=t.map((s,o)=>$(s)?e(this.getChild(o,s.id)):s);return z(r,this.oid),r}else{let r=Object.entries(t).reduce((s,[o,a])=>($(a)?s[o]=e(this.getChild(o,a.id)):s[o]=a,s),{});return z(r,this.oid),r}};this.getSnapshot=()=>this.viewWithMappedChildren(e=>e.getSnapshot());this.getUnprunedSnapshot=()=>this.rawViewWithMappedChildren(e=>e instanceof ve?e.getSnapshot():e.getUnprunedSnapshot());this.addPendingOperations=e=>{this.ctx.log("debug","Entity: adding pending operations",this.oid,e),this.deepInvalid&&(this.ctx.log("warn","Changes are being applied to a pruned entity. This means that the pruned version is being treated as the new baseline and any pruned invalid data is lost.",this.oid),this.canonizePrunedVersion()),this.applyPendingOperations(e)};this.applyPendingOperations=e=>{if(this.access)for(let r of e)r.authz=this.access;let t=this.metadataFamily.addPendingData(e);for(let r of t)this.change(r)};this.getPruneDiff=()=>{let e=this.getSnapshot(),t=this.getUnprunedSnapshot();return this.patchCreator.createDiff(t,e,{authz:this.access,merge:!1})};this.canonizePrunedVersion=()=>{this.applyPendingOperations(this.getPruneDiff())};this.addConfirmedData=e=>{this.ctx.log("debug","Entity: adding confirmed data",this.oid);let t=this.metadataFamily.addConfirmedData(e);for(let r of t)this.change(r)};this.replaceAllData=e=>{this.ctx.log("debug","Entity: replacing all data",this.oid);let t=this.metadataFamily.replaceAllData(e);for(let r of t)this.change(r)};this.resetAllData=()=>{this.ctx.log("debug","Entity: resetting all data",this.oid),this.cachedDeepUpdatedAt=null,this.cachedView=void 0,this._viewData=void 0;let e=this.metadataFamily.replaceAllData({});for(let t of e)this.change(t)};this.invalidateCachedView=()=>{this._viewData=void 0,this.cachedView=void 0};this.invalidate=e=>{if(e.oid===this.oid)this.invalidateCachedView();else{let t=this.entityFamily.getCached(e.oid);t&&t instanceof i&&t.invalidate(e)}};this.change=e=>{if(e.oid===this.oid)this.invalidateCachedView(),this.parent?this.changeNested(e):this.changeRoot(e);else{let t=this.entityFamily.getCached(e.oid);t&&t instanceof i&&t.change(e)}};this.changeRoot=e=>{this.deleted?this.wasDeletedLastChange?this.ctx.log("debug","Entity already deleted, not emitting delete or change events",this.oid):(this.ctx.log("debug","Entity deleted",this.oid),this.emit("delete",{isLocal:e.isLocal}),this.wasDeletedLastChange=!0):(this.wasDeletedLastChange&&(this.ctx.log("debug","Entity restored",this.oid),this.emit("restore",{isLocal:e.isLocal}),this.wasDeletedLastChange=!1),this.deepChange(this,e),this.emit("change",{isLocal:e.isLocal}))};this.changeNested=e=>{if(this.deleted){this.ctx.log("debug","Entity deleted, not emitting change",this.oid);return}this.deepChange(this,e),this.emit("change",{isLocal:e.isLocal})};this.deepChange=(e,t)=>{if(this.deleted){this.ctx.log("debug","Entity deleted, not emitting deep change",this.oid);return}this.cachedDeepUpdatedAt=null,this.cachedView=void 0,this.emit("changeDeep",e,t),this.parent?.deepChange(e,t)};this[lc]=e=>{this.deepChange(this,{isLocal:!1,oid:this.oid})};this.getChild=(e,t)=>{let r=Ne(this.schema,e);if(!r)throw new Error(`No schema for key ${String(e)} in ${JSON.stringify(this.schema)}`);return this.entityFamily.get({oid:t,schema:r,entityFamily:this.entityFamily,metadataFamily:this.metadataFamily,parent:this,ctx:this.ctx,files:this.files,fieldPath:[...this.fieldPath,e],storeEvents:this.storeEvents,deleteSelf:this.delete.bind(this,e)})};this.subscribeToField=(e,t,r)=>ho(this,e,r);this.get=e=>{Zn(e);let t=this.rawView;if(!t)throw new Error(`Cannot access data at key ${e} on deleted entity ${this.oid}`);let r=t[e],s=Ne(this.schema,e);if(!s)throw new Error(`No schema for key ${String(e)} in ${JSON.stringify(this.schema)}`);if($(r))if(fe(r)){if(s.type!=="file"){if(pe(s))return null;if(Fe(s))return Pe(s);throw new Error(`Expected file schema for key ${String(e)}, got ${s.type}`)}return this.files.get(r.id,{downloadRemote:!!s.downloadRemote,ctx:this.ctx,parent:this})}else{if(s.type==="file"){if(console.error(`Expected file ref for key ${String(e)}, got ${r}`),pe(s))return null;if(Fe(s))return Pe(s);throw new Error(`No valid value for key ${String(e)} in ${JSON.stringify(this.schema)}`)}let o=this.getChild(e,r.id);if(o.deepInvalid){if(s.type==="array"||s.type==="map")return o;if(pe(s))return null;if(Fe(s)){let a=o.getUnprunedSnapshot(),l=this.patchCreator.createDiff(a,Pe(s),{merge:!1,mergeUnknownObjects:!0,authz:this.access});return this.processPrunedChild(e,o,l)}}return o}else{if(this.schema.type==="map"&&r===void 0)return;if(Te({field:s,value:r,fieldPath:[...this.fieldPath,e],depth:1,requireDefaults:!0}))if(Fe(s)){if(ss(s))return Pe(s);let o=Pe(s),a=Oe(this.oid),l=this.patchCreator.createInitialize(o,a,this.access);l.push(...this.patchCreator.createSet(this.oid,e,ie(a),this.access));let p=this.getChild(e,a);return this.processPrunedChild(e,p,l)}else return;return r}};this.processPrunedChild=(e,t,r)=>{this.ctx.log("warn","Replacing invalid child object field with ephemeral, valid data",this.oid,e);let s=this.metadataFamily.addEphemeralData(r);for(let o of s)this.invalidate(o);return t};this.getOrSet=(e,t)=>{Zn(e);let r=this.get(e);return r||(this.set(e,t),this.get(e))};this.processInputValue=(e,t)=>{if(this.readonlyKeys.includes(t))throw new Error(`Cannot set readonly key ${t.toString()}`);nt(e)||(e=de(e,!1));let r=Ne(this.schema,t);if(r){Je(e,r);let s=Te({field:r,value:e,fieldPath:[...this.fieldPath,t]});if(s)throw new Error(`Validation error: ${s.message}`,{cause:s})}return pt(e,s=>this.files.add(s,this))};this.getDeleteMode=e=>{if(this.readonlyKeys.includes(e))return!1;if(this.schema.type==="any"||this.schema.type==="map")return"delete";if(this.schema.type==="object"){let t=this.schema.properties[e];if(!t||t.type==="any")return"delete";if(t.type==="map")return!1;if(t.nullable)return"null"}return!1};this.getItemRefValue=e=>{if(e instanceof i)return ie(e.oid);if(e instanceof ve)return xt(e.id);if(typeof e=="object"){let t=G(e);if(!t||!this.entityFamily.has(t))throw new Error(`Cannot move object ${JSON.stringify(e)} which does not exist in this list`);return ie(t)}else return e};this.set=(e,t,r)=>{Zn(e),!(!r?.force&&this.get(e)===t)&&(this.isList?this.addPendingOperations(this.patchCreator.createListSet(this.oid,e,this.processInputValue(t,e))):this.addPendingOperations(this.patchCreator.createSet(this.oid,e,this.processInputValue(t,e))))};this.getAll=()=>this.view;this.delete=e=>{if(this.isList)cc(e),this.addPendingOperations(this.patchCreator.createListDelete(this.oid,e));else{let t=this.getDeleteMode(e);if(!t)throw new Error(`Cannot delete key ${e.toString()} - the property is not marked as optional in the schema.`);t==="delete"?this.addPendingOperations(this.patchCreator.createRemove(this.oid,e)):this.addPendingOperations(this.patchCreator.createSet(this.oid,e,null))}};this.keys=()=>this.view?Object.keys(this.view):[];this.entries=()=>this.view?Object.entries(this.view):[];this.values=()=>this.view?Object.values(this.view):[];this.update=(e,{merge:t=!0,replaceSubObjects:r=!1,preserveUndefined:s=!1,dangerouslyDisableMerge:o=!1}={})=>{if(!t&&!o&&this.schema.type!=="any"&&this.schema.type!=="map")throw new Error('Cannot use .update without merge if the field has a strict schema type. merge: false is only available on "any" or "map" types.');let a={};z(a,this.oid);for(let[l,p]of Object.entries(e)){if(this.readonlyKeys.includes(l))throw new Error(`Cannot set readonly key ${l.toString()}`);if(p===void 0&&!s)continue;let f=Ne(this.schema,l);f&&Je(p,f),a[l]=this.processInputValue(p,l)}this.addPendingOperations(this.patchCreator.createDiff(this.getSnapshot(),a,{mergeUnknownObjects:!r,merge:t}))};this.push=e=>{this.addPendingOperations(this.patchCreator.createListPush(this.oid,this.processInputValue(e,this.view.length)))};this.insert=(e,t)=>{this.addPendingOperations(this.patchCreator.createListInsert(this.oid,e,this.processInputValue(t,e)))};this.move=(e,t)=>{this.addPendingOperations(this.patchCreator.createListMoveByIndex(this.oid,e,t))};this.moveItem=(e,t)=>{let r=this.getItemRefValue(e);if($(r))this.addPendingOperations(this.patchCreator.createListMoveByRef(this.oid,r,t));else{let s=this.view.indexOf(e);if(s===-1)throw new Error(`Cannot move item ${JSON.stringify(e)} which does not exist in this list`);this.move(s,t)}};this.add=e=>{this.addPendingOperations(this.patchCreator.createListAdd(this.oid,this.processInputValue(e,this.view.length)))};this.removeAll=e=>{this.addPendingOperations(this.patchCreator.createListRemove(this.oid,this.getItemRefValue(e)))};this.removeFirst=e=>{this.addPendingOperations(this.patchCreator.createListRemove(this.oid,this.getItemRefValue(e),"first"))};this.removeLast=e=>{this.addPendingOperations(this.patchCreator.createListRemove(this.oid,this.getItemRefValue(e),"last"))};this.map=e=>this.view.map(e);this.filter=e=>this.view.filter(e);this.has=e=>{if(!this.isList)throw new Error("has() is only available on list entities");let t=this.getItemRefValue(e);return $(t)?this.view.some(r=>{if($(r))return De(r,t)}):this.view.includes(e)};this.forEach=e=>{this.view.forEach(e)};this.reduce=(e,t)=>this.view.reduce(e,t);this.some=e=>this.view.some(e);this.every=e=>this.view.every(e);this.find=e=>this.view.find(e);this.includes=this.has;this.deleteSelf=()=>this._deleteSelf();this.__getViewData__=(e,t)=>this.metadataFamily.get(e).computeView(t==="confirmed");this.__getFamilyOids__=()=>this.metadataFamily.getAllOids();this.__discardPendingOperation__=e=>{this.metadataFamily.discardPendingOperation(e),this.invalidateCachedView()};L(!!e,"oid is required"),this.oid=e,this.readonlyKeys=l||[],this.ctx=o,this.files=p,this.schema=t,this.fieldPath=w||[],this.entityFamily=r||new Fi({initial:[this],ctx:o}),this.metadataFamily=a,this.storeEvents=f,this.parent=s,this._deleteSelf=S,this.parent||(f.add.attach(this.onAdd),f.replace.attach(this.onReplace),f.resetAll.attach(this.onResetAll))}get[fo](){return this.ctx}get metadata(){return this.metadataFamily.get(this.oid)}get patchCreator(){return this.ctx.patchCreator}get viewData(){return this._viewData===void 0&&(this._viewData=this.metadata.computeView(),this.validate()),this._viewData}get rawView(){return this.viewData.view}get view(){if(this.cachedView!==void 0)return this.cachedView;if(this.viewData.deleted)return null;let e=this.rawView;if(!e&&!pe(this.schema)||this.schema.type==="array"&&!Array.isArray(e)||(this.schema.type==="object"||this.schema.type==="map")&&!V(e))return Fe(this.schema)?Pe(this.schema):null;let r=this.isList?[]:{};if(z(r,this.oid),Array.isArray(e)){let s=Ne(this.schema,0);if(!s)this.ctx.log("error","No child field schema for list entity.",this.oid);else for(let o=0;o<e.length;o++){let a=this.get(o);this.childIsNull(a)&&!pe(s)?this.ctx.log("error","Child missing in non-nullable field",this.oid,"index:",o):r.push(a)}}else if(V(e)){let s=this.schema.type==="object"?Object.keys(this.schema.properties):Object.keys(e);for(let o of s){let a=Ne(this.schema,o);if(!a){this.ctx.log("error","No child field schema for object entity at key",o),this.schema.type==="map"?r={}:r=null;break}let l=this.get(o);if(this.childIsNull(l)&&!pe(a)){if(this.ctx.log("error","Child entity is missing for non-nullable field",this.oid,"key:",o),this.schema.type!=="map"){r=null;break}}else pe(a)&&l===void 0?r[o]=null:r[o]=l}}return this.cachedView=r,r}get uid(){return this.oid}get deleted(){return this.viewData.deleted||this.view===null}get quickDeleted(){return this._viewData?.deleted||this.cachedView===null}get invalid(){return!!this.validate()}get deepInvalid(){if(this.invalid)return!0;if(Array.isArray(this.rawView)){for(let e=0;e<this.rawView.length;e++)if(re(this.rawView[e])&&this.getChild(e,this.rawView[e].id).deepInvalid)return!0}else if(V(this.rawView)){for(let e in this.rawView)if(re(this.rawView[e])&&this.getChild(e,this.rawView[e].id).deepInvalid)return!0}return!1}get isList(){return this.schema.type==="array"||Array.isArray(this.viewData.view)}get updatedAt(){return this.viewData.updatedAt}get deepUpdatedAt(){if(this.cachedDeepUpdatedAt)return this.cachedDeepUpdatedAt;let e=this.updatedAt;return this.isList?this.forEach(t=>{if(t instanceof i){let r=t.deepUpdatedAt;r&&(!e||r>e)&&(e=r)}}):this.values().forEach(t=>{if(t instanceof i){let r=t.deepUpdatedAt;r&&(!e||r>e)&&(e=r)}}),this.cachedDeepUpdatedAt=e,e}get isOutdatedVersion(){return this.parent?this.parent.isOutdatedVersion:this.viewData.fromOlderVersion}get namespace(){return this.ctx.namespace}get access(){return this.viewData.authz}get isAuthorized(){return!!this.access}get size(){return this.isList?this.length:this.keys().length}get length(){return this.view.length}[(lc=Xn,Symbol.iterator)](){let e=0,t=this.view?.length;return{next:()=>e<t?{value:this.get(e++),done:!1}:{value:void 0,done:!0}}}};function Zn(i){if(typeof i=="symbol")throw new Error("Symbol keys aren't supported")}function cc(i){if(typeof i!="number")throw new Error("Only number keys are supported in list entities")}function po(i){return i[fo].getClient()}var er=class{constructor({oid:n,ctx:e,confirmedOperations:t,pendingOperations:r,baseline:s}){this.baseline=null;this.confirmedOperations=[];this.ephemeralOperations=[];this.pendingOperations=[];this.computeView=(n=!1)=>{let e=de(this.baseline?.snapshot??void 0),t=this.baseline?.timestamp??null,r=this.baseline?.authz,s=this.applyOperations(e,!e,this.confirmedOperations,t,t);s.futureSeen&&this.ctx.globalEvents.emit("futureSeen",s.futureSeen),s.authz&&(r=s.authz);let o=!this.ephemeralOperations||n?s:this.applyOperations(s.view,s.deleted,this.ephemeralOperations,s.latestTimestamp,null),a=n?s:this.applyOperations(o.view,o.deleted,this.pendingOperations,o.latestTimestamp,null);a.authz&&(r=a.authz),a.view&&z(a.view,this.oid);let l=!!s.latestTimestamp&&Pn(s.latestTimestamp,this.ctx.time.now)<0,p=!this.baseline&&!this.pendingOperations.length&&!this.confirmedOperations.length;p&&this.ctx.log("warn",`Tried to load Entity ${this.oid} with no data`);let f=a.latestTimestamp??s.latestTimestamp??t,S=f?_n(f):0;return!a.view&&!a.deleted&&!p&&this.ctx.log("warn",`Entity ${this.oid} has no view, no deleted flag, and not empty`),{view:a.view??void 0,deleted:a.deleted,empty:p,fromOlderVersion:l,updatedAt:S,latestTimestamp:f,authz:r}};this.addBaseline=n=>{if(!(this.baseline&&this.baseline.timestamp>=n.timestamp))for(this.baseline=n;this.confirmedOperations[0]?.timestamp<n.timestamp;)this.confirmedOperations.shift()};this.addConfirmedOperations=n=>{let e=0;for(let t of n){let r=this.confirmedOperations.findIndex(o=>o.timestamp>=t.timestamp);r!==-1?this.confirmedOperations[r].timestamp!==t.timestamp&&(this.confirmedOperations.splice(r,0,t),e++):(this.confirmedOperations.push(t),e++);let s=this.pendingOperations.length;this.discardPendingOperation(t),e-=s-this.pendingOperations.length}return e};this.addPendingOperation=n=>{this.pendingOperations.push(n)};this.addEphemeralOperation=n=>{this.ephemeralOperations||(this.ephemeralOperations=[]),this.ephemeralOperations.push(n)};this.clearEphemeralOperations=()=>{let n=this.ephemeralOperations;return this.ephemeralOperations=[],n};this.discardPendingOperation=n=>{this.pendingOperations=this.pendingOperations.filter(e=>e.timestamp!==n.timestamp)};this.applyOperations=(n,e,t,r,s)=>{let o,a,l=this.ctx.time.now;for(let p of t)if(!(s&&p.timestamp<=s)){if(Pn(p.timestamp,l)>0){o=p.timestamp;continue}if(p.data.op==="delete")e=!0;else try{n=Me(n,p.data),p.data.op==="initialize"&&(e=!1,p.authz&&(a=p.authz))}catch(f){throw this.ctx.log("critical",`Failed to apply operation to entity ${this.oid}: ${JSON.stringify(p)}`,f),f}(!r||p.timestamp>r)&&(r=p.timestamp)}return{view:n,latestTimestamp:r??null,deleted:e,futureSeen:o,authz:a}};L(n,"oid is required"),this.ctx=e,this.oid=n,t&&(this.confirmedOperations=t),r&&(this.pendingOperations=r),s&&(this.baseline=s)}},Ti=class{constructor({ctx:n,onPendingOperations:e,rootOid:t}){this.entities=new Map;this.get=n=>(L(n,"oid is required"),this.entities.has(n)||this.entities.set(n,new er({oid:n,ctx:this.ctx})),this.entities.get(n));this.getAllOids=()=>Array.from(this.entities.keys());this.addConfirmedData=({baselines:n=[],operations:e={},isLocal:t=!1})=>{let r={};for(let s of n){if(!je(this.rootOid,s.oid))throw new Error(`Invalid baseline for entity ${this.rootOid}: `+JSON.stringify(s));this.get(s.oid).addBaseline(s),r[s.oid]??={oid:s.oid,isLocal:t}}for(let[s,o]of Object.entries(e)){if(!je(this.rootOid,s))throw new Error(`Invalid operations for entity ${this.rootOid}: `+JSON.stringify(o));this.get(s).addConfirmedOperations(o)!==0&&(r[s]??={oid:s,isLocal:t})}return Object.values(r)};this.addPendingData=n=>{this.flushAllEphemeral();let e={};for(let t of n)this.get(t.oid).addPendingOperation(t),e[t.oid]??={oid:t.oid,isLocal:!0};return this.onPendingOperations(n),Object.values(e)};this.ephemeralMemo=new Array;this.flushAllEphemeral=()=>{for(let n of this.entities.values()){let e=n.clearEphemeralOperations();e&&this.ephemeralMemo.push(...e)}if(this.ephemeralMemo.length){let n=this.ephemeralMemo.slice();this.ephemeralMemo.length=0,this.addPendingData(n)}};this.addEphemeralData=n=>{let e={};for(let t of n)this.get(t.oid).addEphemeralOperation(t),e[t.oid]??={oid:t.oid,isLocal:!0};return Object.values(e)};this.replaceAllData=({operations:n={},baselines:e=[]})=>{let t=Array.from(this.entities.keys());this.entities.clear();let r={};for(let s of t)r[s]={oid:s,isLocal:!1};for(let s of e){if(!je(this.rootOid,s.oid))throw new Error(`Invalid baseline for entity ${this.rootOid}: `+JSON.stringify(s));this.get(s.oid).addBaseline(s),r[s.oid]??={oid:s.oid,isLocal:!1}}for(let[s,o]of Object.entries(n)){if(!je(this.rootOid,s))throw new Error(`Invalid operations for entity ${this.rootOid}: `+JSON.stringify(o));this.get(s).addConfirmedOperations(o),r[s]??={oid:s,isLocal:!1}}return Object.values(r)};this.discardPendingOperation=n=>{this.entities.get(n.oid)?.discardPendingOperation(n)};this.ctx=n,this.rootOid=t,this.onPendingOperations=e}};var kt="@@default",ki=class{constructor({batchTimeout:n=200,ctx:e,entities:t}){this.currentBatchKey=kt;this.flushOperations=async(n,e,t)=>{if(!n.length)return;this.ctx.log("debug","Flushing",n.length,"operations from batch",e,"to storage / sync");let r=[],s={};for(let o=n.length-1;o>=0;o--){let a=n[o],l=s[a.oid];if(l&&Fr(a,l)){this.entities.discardPendingOperation(a);continue}let p=Rr(a);p!==!1&&(s[a.oid]||(s[a.oid]=new Set),s[a.oid].add(p)),r.unshift(a)}for(let o of r)o.timestamp=this.ctx.time.now;await this.commitOperations(r,t)};this.commitOperations=async(n,e)=>{if(n.length){if(e.undoable){let t=await this.createUndo({ops:n,source:e.source});t&&this.ctx.undoHistory.addUndo(t)}await this.entities.addData({operations:n,baselines:[],isLocal:!0})}};this.addOperations=n=>{n.length&&(this.batcher.add({key:this.currentBatchKey,items:n}),this.ctx.log("debug","added",n.length,"ops to batch",this.currentBatchKey,", size = ",this.batcher.getSize(this.currentBatchKey)))};this.batch=({undoable:n=!0,batchName:e=vt(),max:t=null,timeout:r=this.defaultBatchTimeout}={})=>{let s=this.batcher.add({key:e,max:t,timeout:r,items:[],userData:{undoable:n}}),o={run:a=>(this.currentBatchKey=e,a(),this.currentBatchKey=kt,o),commit:async()=>(await this.batcher.flush(kt),s.flush()),flush:()=>o.commit(),discard:()=>{this.batcher.discard(e)}};return o};this.flushAll=()=>(this.ctx.log("debug","Flushing all operations"),Promise.all(this.batcher.flushAll()));this.createUndo=async n=>{let e=await this.getInverseOperations(n);return e.length?async()=>{let t=await this.createUndo({ops:e,source:n.source,isRedo:!0});for(let r of e)r.timestamp=this.ctx.time.now;return this.ctx.log("debug",n.isRedo?"Redo":"Undo",e,`
|
|
16
16
|
was
|
|
17
17
|
`,n.ops),await this.commitOperations(e,{undoable:!1}),t}:null};this.getInverseOperations=async({ops:n,source:e})=>{let t=zt(n),r=[],s=()=>this.ctx.time.now;return await Promise.all(Object.entries(t).map(async([o,a])=>{let p=(e??await this.entities.hydrate(K(o)))?.__getViewData__(o,"confirmed");if(!p){this.ctx.log("warn","could not find entity",o,"for undo operation",n);return}let f=fs(o,p.view,a,s);r.unshift(...f)})),r};this.ctx=e,this.entities=t,this.defaultBatchTimeout=n,this.batcher=new it(this.flushOperations),this.batcher.add({key:kt,items:[],max:100,timeout:n,userData:{undoable:!0}})}get isDefaultBatch(){return this.currentBatchKey===kt}};var Li=class extends oe{constructor({ctx:e,files:t}){super();this.events={add:new Bi.WeakEvent,replace:new Bi.WeakEvent,resetAll:new Bi.WeakEvent};this.cache=new Map;this.pendingEntityPromises=new Map;this.abortDataQueueController=new AbortController;this.ongoingResetPromise=null;this.entityFinalizationRegistry=new FinalizationRegistry(e=>{this.ctx.log("debug","Entity GC",e)});this.addData=async e=>{if(this.disposed){this.ctx.log("warn","EntityStore is disposed, not adding incoming data");return}e.reset&&(this.ctx.log("info","Resetting local store to replicate remote synced data - dropping any current transactions"),this.abortDataQueueController.abort(0),this.abortDataQueueController=new AbortController,this.ongoingResetPromise=this.resetData().finally(()=>{this.ongoingResetPromise=null,this.ctx.globalEvents.emit("resetToServer")})),this.ongoingResetPromise&&(this.ctx.log("debug","Waiting for ongoing reset to complete"),await this.ongoingResetPromise,this.ctx.log("debug","Ongoing reset complete")),await this.processData(e)};this.empty=async()=>{await(await this.ctx.documents).reset(),this.events.resetAll.invoke(this),this.cache.clear()};this.resetData=async()=>{if(this.disposed){this.ctx.log("warn","EntityStore is disposed, not resetting local data");return}await(await this.ctx.meta).reset(),await(await this.ctx.documents).reset(),this.events.resetAll.invoke(this)};this.processData=async e=>{if(this.disposed){this.ctx.log("warn","EntityStore is disposed, not processing incoming data");return}let t=e?.baselines??[],r=e?.operations??[];if(t.length===0&&r.length===0){this.ctx.log("debug","No data to process");return}this.ctx.log("debug","Processing incoming data",{operations:r.length,baselines:t.length,reset:!!e.reset});let s=Array.from(new Set(t.map(f=>K(f.oid)).concat(r.map(f=>K(f.oid))))),o=Ar(t),a=Dr(r);this.ctx.log("debug","Applying data to live entities");for(let f of s){let S=o[f],w=a[f]??[],A=zt(w),y=e.reset?this.events.replace:this.events.add,I=this.pendingEntityPromises.get(f);I?(this.ctx.log("debug","Waiting for ongoing entity hydration",f),I.then(()=>{y.invoke(this,{oid:f,baselines:S,operations:A,isLocal:!1})})):(this.ctx.log("debug","Applying data to entity",f),y.invoke(this,{oid:f,baselines:S,operations:A,isLocal:!1}))}let l={abort:this.abortDataQueueController.signal};await(await this.ctx.meta).insertData(e,l),this.ctx.log("debug","Data processing complete, all data saved to metadata db.");let p=await Promise.all(s.map(async f=>await this.hydrate(f,l)??{oid:f,getSnapshot(){return null}}));try{this.ctx.log("debug","Saving entities to queryable storage"),await(await this.ctx.documents).saveEntities(p,l)}catch(f){this.disposed?this.ctx.log("warn","Error saving entities to queryable storage - EntityStore is disposed",f):this.ctx.log("error","Error saving entities to queryable storage",f)}};this.hydrate=async(e,t)=>{if(!qt(e))throw new Error("Cannot hydrate non-root entity");if(this.cache.has(e)){let s=this.cache.get(e);if(s){let o=s.deref();if(o)return o.deleted?(this.ctx.log("debug","Cached entity is deleted",e),null):(this.ctx.log("debug","Using cached hydrated entity",e),o);this.ctx.log("debug","Removing GC'd entity from cache",e),this.cache.delete(e)}}let r=this.pendingEntityPromises.get(e);if(r)return this.ctx.log("debug","Waiting for ongoing entity hydration",e),r;{this.ctx.log("debug","Hydrating entity from storage",e);let s=this.constructEntity(e);if(!s)return this.ctx.log("warn","Entity schema not found, cannot construct",e),null;let o=this.loadEntity(s,t);return o.finally(()=>{this.pendingEntityPromises.delete(e),this.ctx.log("debug","Hydration complete",e)}),this.pendingEntityPromises.set(e,o),o}};this.destroy=async()=>{this.ctx.log("warn","Disposing EntityStore"),this.dispose(),await this.batcher.flushAll()};this.create=async(e,t,{undoable:r=!0,access:s}={})=>{this.ctx.log("debug","Creating new entity",t);let{collection:o}=ue(t);Ht(e);let a=[],l=pt(e,a.push.bind(a));z(l,t);let{schema:p}=this.getCollectionSchema(o);if(!p)throw new Error(`Collection schema not found for ${o}`);let f=this.ctx.patchCreator.createInitialize(l,t,s);await this.batcher.commitOperations(f,{undoable:!!r});let S=await this.hydrate(t);if(!S)throw this.ctx.log("error","Failed to create entity; hydrated entity is null. Hopefully an error is logged above.",t),new N(N.Code.Unexpected,void 0,`Failed to create document ${t}`);return this.ctx.log("debug","Associating",a.length,"files to new entity",t),a.forEach(w=>this.files.add(w,S)),S};this.deleteAll=async(e,t)=>{this.ctx.log("info","Deleting documents",e),L(e.every(o=>o===K(o)),"Only root documents may be deleted via client methods");let r=await Promise.all(e.map(o=>this.hydrate(o))),s=[];for(let o of r)if(o){let a=o.__getFamilyOids__(),l=this.ctx.patchCreator.createDeleteAll(a);for(let p of l)p.authz=o.access;s.push(...l)}await this.batcher.commitOperations(s,{undoable:t?.undoable===void 0?!0:t.undoable}),e.forEach(o=>{this.cache.delete(o),this.ctx.log("debug","Deleted document from cache",o)})};this.delete=async(e,t)=>this.deleteAll([e],t);this.getCollectionSchema=e=>{let t=this.ctx.schema.collections[e];return t?{schema:{type:"object",nullable:!1,properties:t.fields},readonlyKeys:[t.primaryKey]}:(this.ctx.log("warn",`Missing schema for collection: ${e}`),{schema:null,readonlyKeys:[]})};this.constructEntity=e=>{L(!!e,"Cannot construct entity without OID");let{collection:t}=ue(e),{schema:r,readonlyKeys:s}=this.getCollectionSchema(t);if(!r)return null;if(this.disposed)return this.ctx.log("warn","Cannot hydrate entity after store has been disposed"),null;let o=new Ti({ctx:this.ctx,onPendingOperations:this.onPendingOperations,rootOid:e});return new me({ctx:this.ctx,oid:e,schema:r,readonlyKeys:s,files:this.files,metadataFamily:o,storeEvents:this.events,deleteSelf:this.delete.bind(this,e)})};this.onPendingOperations=e=>{this.batcher.addOperations(e)};this.discardPendingOperation=e=>{let t=K(e.oid);this.cache.get(t)?.deref()?.__discardPendingOperation__(e)};this.loadEntity=async(e,t)=>(await this.loadEntityData(e,t),this.cache.set(e.oid,this.ctx.weakRef(e)),this.entityFinalizationRegistry.register(e,e.oid),e);this.loadEntityData=async(e,t)=>{let{operations:r,baselines:s}=await(await this.ctx.meta).getDocumentData(e.oid,t);return!s.length&&!Object.keys(r).length?(this.ctx.log("debug","No data found for entity",e.oid),null):(this.ctx.log("debug","Loaded entity from storage",e.oid),this.events.replace.invoke(this,{oid:e.oid,baselines:s,operations:r,isLocal:!1}),e)};this.clearCache=()=>{this.ctx.log("debug","Emptying entity cache"),this.cache.clear()};this.ctx=e,this.files=t,this.batcher=new ki({ctx:e,entities:this}),this.addDispose(this.ctx.internalEvents.subscribe("persistenceReset",this.clearCache))}get batch(){return this.batcher.batch}get flushAllBatches(){return this.batcher.flushAll}};var Mi=class extends oe{constructor({sync:e,context:t}){super();this.cache=new Map;this.add=async(e,t)=>{let r=this.cache.get(e.id);r||(r=new ve(e.id,{ctx:this.context,parent:t}),this.cache.set(e.id,r)),e.remote||r[mt](e);let s=await(await this.context.files).add(e);r[mt](s)};this.get=(e,t)=>{if(this.cache.has(e))return this.cache.get(e);let r=new ve(e,t);return this.cache.set(e,r),this.load(r),r};this.load=async e=>{let t=await(await this.context.files).get(e.id);if(t)e[mt](t);else try{if(this.sync.status!=="active"){this.context.log("info","Sync is not active, waiting for online to load file",e.id,e.name);let s=this.sync.subscribe("onlineChange",o=>{o&&(s(),this.load(e))});return}let r=await this.sync.getFile(e.id);r.success?(await(await this.context.files).add(r.data),e[mt](r.data)):(this.context.log("error","Failed to load file",r),e[Ri](r.error?.toString()))}catch(r){this.context.log("error","Failed to load file",r),e[Ri](r instanceof Error?r.message:String(r))}};this.onFileUploaded=async e=>{this.context.log("debug","Marking file as uploaded",e.id),(await this.context.files).onUploaded(e.id)};this.sync=e,this.context=t,this.addDispose(this.context.internalEvents.subscribe("fileUploaded",this.onFileUploaded))}};function dc(i){return i!==null}function tr(i){return Array.isArray(i)?i.map(tr).filter(dc):i instanceof me&&i.deleted?null:i}function $e(i,n){return!i&&!n||i&&n&&We(i)===We(n)}var ir=Symbol("ON_ALL_UNSUBSCRIBED"),ge=Symbol("UPDATE"),uc,xe=class extends oe{constructor({initial:e,context:t,collection:r,key:s,shouldUpdate:o}){super();this._internalUnsubscribes=[];this._status="initial";this._executionPromise=null;this.setValue=e=>{this._rawValue=e,this.subscribeToDeleteAndRestore(this._rawValue);let t=tr(e),r=!0;this.status==="initializing"||this.status==="initial"?r=!0:this.isListQuery?this._value.length===t.length&&this._value.every((s,o)=>s===t[o])&&(r=!1):this._value===t&&(r=!1),this._value=t,r&&(this.context.log("debug","Query value changed",this.key),this._events.emit("change",this._value)),this.status="ready"};this.refreshValue=()=>{this.setValue(this._rawValue)};this.subscribeToDeleteAndRestore=e=>{for(;this._internalUnsubscribes.length;)this._internalUnsubscribes.pop()?.();Array.isArray(e)?e.forEach(t=>{t instanceof me&&(this._internalUnsubscribes.push(t.subscribe("delete",this.refreshValue)),this._internalUnsubscribes.push(t.subscribe("restore",this.refreshValue)))}):e instanceof me&&(this._internalUnsubscribes.push(e.subscribe("delete",this.refreshValue)),this._internalUnsubscribes.push(e.subscribe("restore",()=>{this.refreshValue()})))};this.execute=()=>{let e=new Date;return this.context.log("debug",`[${e.toLocaleTimeString()}]`,"Executing query",this.key),this.status==="initial"?this.status="initializing":this.status==="ready"&&(this.status="revalidating"),this._executionPromise=this.run().then(()=>this._value).catch(t=>{if(t instanceof Error){if(t.name==="InvalidStateError"||t.name==="InvalidAccessError")return this._value;throw t}else throw new Error("Unknown error executing query")}).finally(()=>{let t=new Date,r=t.getTime()-e.getTime();this.context.log("debug",`[${t.toLocaleTimeString()}]`,"Query executed",this.key,`Duration: ${r}ms`)}),this._executionPromise};this[uc]=e=>{this._allUnsubscribedHandler=e};this._rawValue=e,this._value=e,this.isListQuery=Array.isArray(e),this._events=new W(l=>{l==="change"&&this._allUnsubscribedHandler?.(this)}),this.context=t,this.key=s,this.collection=r;let a=o||(l=>l.includes(r));this.addDispose(this.context.entityEvents.subscribe("collectionsChanged",l=>{a(l)&&(this.context.log("info","Updating query",this.key),this.execute())}))}static{uc=ir}get current(){return this._value}get resolved(){return this.status==="ready"?Promise.resolve(this._value):this._executionPromise??this.execute()}get subscribed(){return this._events.totalSubscriberCount()>0}get status(){return this._status}set status(e){this._status!==e&&(this._status=e,this._events.emit("statusChange",this._status))}get hasDeleted(){return this.isListQuery?this._rawValue.length!==this._value.length:!!this._rawValue&&!this._value}subscribe(e,t){if(t===void 0&&typeof e=="function")return this.resolved,this._events.subscribe("change",e);if(e==="change"&&t!==void 0)return this.resolved,this._events.subscribe("change",t);if(e==="statusChange"&&typeof t=="function")return this._events.subscribe(e,t);throw new Error("Invalid invocation of Query.subscribe")}get __rawValue(){return this._rawValue}};var ji=class extends xe{constructor({id:e,hydrate:t,...r}){super({initial:null,...r});this.run=async()=>{let e=await this.hydrate(this.oid);this.setValue(e)};this.oid=ee(r.collection,e),this.hydrate=t}};var hc,Vi=class extends xe{constructor({index:e,hydrate:t,...r}){super({initial:null,...r});this.run=async()=>{let e=await(await this.context.documents).findOneOid({collection:this.collection,index:this.index});this.setValue(e?await this.hydrate(e):null)};this[hc]=e=>{$e(this.index,e)||(this.index=e,this.execute())};this.index=e,this.hydrate=t}static{hc=ge}};var fc,Ui=class extends xe{constructor({index:e,hydrate:t,pageSize:r,page:s,...o}){super({initial:[],...o});this._hasNextPage=!1;this.run=async()=>{let{result:e,hasNextPage:t}=await(await this.context.documents).findAllOids({collection:this.collection,index:this.index,limit:this._pageSize,offset:this._page*this._pageSize});this._hasNextPage=t,this.setValue(await Promise.all(e.map(this.hydrate)))};this.nextPage=async()=>{this.hasNextPage&&(this._page++,await this.run())};this.previousPage=async()=>{this._page!==0&&(this._page--,await this.run())};this.setPage=async e=>{this._page=e,await this.run()};this[fc]=e=>{$e(this.index,e)||(this.index=e,this.execute())};this.index=e,this.hydrate=t,this._pageSize=r,this._page=s}static{fc=ge}get pageSize(){return this._pageSize}get page(){return this._page}get hasNextPage(){return this._hasNextPage}get hasPreviousPage(){return this._page>0}};var pc,Ni=class extends xe{constructor({hydrate:e,pageSize:t,index:r,...s}){super({initial:[],...s});this._upToPage=1;this._hasNextPage=!1;this.run=async()=>{let{result:e,hasNextPage:t}=await(await this.context.documents).findAllOids({collection:this.collection,limit:this._pageSize*this._upToPage,offset:0,index:this.index});this._hasNextPage=t,this.setValue(await Promise.all(e.map(this.hydrate)))};this.loadMore=async()=>{let{result:e,hasNextPage:t}=await(await this.context.documents).findAllOids({collection:this.collection,limit:this._pageSize,offset:this._pageSize*this._upToPage,index:this.index});this._hasNextPage=t,this._upToPage++,this.setValue([...this.current,...await Promise.all(e.map(this.hydrate))])};this[pc]=e=>{$e(this.index,e)||(this.index=e,this.execute())};this.index=r,this.hydrate=e,this._pageSize=t}static{pc=ge}get pageSize(){return this._pageSize}get hasMore(){return this._hasNextPage}};var mc,zi=class extends xe{constructor({index:e,hydrate:t,...r}){super({initial:[],...r});this.run=async()=>{let{result:e}=await(await this.context.documents).findAllOids({collection:this.collection,index:this.index});this.context.log("debug",`FindAllQuery: ${e.length} oids found: ${e}`),this.setValue(await Promise.all(e.map(this.hydrate)))};this[mc]=e=>{$e(this.index,e)||(this.index=e,this.execute())};this.index=e,this.hydrate=t}static{mc=ge}};var $i=class{constructor({collection:n,cache:e,entities:t,context:r,documentManager:s}){this.serializeIndex=n=>n?We(n):"";this.get=n=>{let e=`get:${this.collection}:${n}`;return this.cache.getOrSet(e,()=>new ji({id:n,collection:this.collection,hydrate:this.hydrate,context:this.context,key:e}))};this.findOne=({index:n,key:e}={})=>{let t=e||`findOne:${this.collection}:${this.serializeIndex(n)}`;return this.cache.getOrSet(t,()=>new Vi({index:n,collection:this.collection,hydrate:this.hydrate,context:this.context,key:t}),r=>{r[ge](n)})};this.findAll=({index:n,key:e}={})=>{let t=e||`findAll:${this.collection}:${this.serializeIndex(n)}`;return this.cache.getOrSet(t,()=>new zi({index:n,collection:this.collection,hydrate:this.hydrate,context:this.context,key:t}),r=>{r[ge](n)})};this.findPage=({index:n,pageSize:e,page:t,key:r})=>{let s=r||`findPage:${this.collection}:${this.serializeIndex(n)}:${e}`;return this.cache.getOrSet(s,()=>new Ui({index:n,collection:this.collection,hydrate:this.hydrate,context:this.context,key:s,pageSize:e,page:t}),o=>{o[ge](n)})};this.findAllInfinite=({index:n,pageSize:e,key:t})=>{let r=t||`findAllInfinite:${this.collection}:${this.serializeIndex(n)}:${e}`;return this.cache.getOrSet(r,()=>new Ni({index:n,collection:this.collection,hydrate:this.hydrate,context:this.context,key:r,pageSize:e}),s=>{s[ge](n)})};this.cache=e,this.collection=n,this.hydrate=t.hydrate,this.context=r,this.documentManager=s,this.put=this.documentManager.create.bind(this.documentManager,this.collection),this.delete=this.documentManager.delete.bind(this.documentManager,this.collection),this.deleteAll=this.documentManager.deleteAllFromCollection.bind(this.documentManager,this.collection),this.clone=this.documentManager.clone.bind(this.documentManager,this.collection)}};var Ki=class extends oe{constructor({evictionTime:e=5*1e3,context:t}){super();this._cache=new Map;this._holds=new Set;this.enqueueQueryEviction=e=>{setTimeout(()=>{if(!e.subscribed){if(this._holds.has(e.key)){this.context.log("debug","QueryCache: keepAlive hold on query preserves after unsubscribe",e.key);return}this._cache.get(e.key)===e&&(this._cache.delete(e.key),this.context.log("debug","QueryCache: evicted query",e.key))}},this._evictionTime)};this.dropAll=()=>{this.context.log("debug","QueryCache: drop all",this._cache.size,"queries"),this._cache.forEach(e=>e.dispose()),this._cache.clear()};this.forceRefreshAll=()=>{this.context.log("debug","QueryCache: force refresh all",this._cache.size,"queries"),this._cache.forEach(e=>e.execute())};this._evictionTime=e,this.context=t,this.addDispose(this.context.internalEvents.subscribe("persistenceReset",this.forceRefreshAll))}get activeKeys(){return Array.from(this._cache.keys())}get(e){return this._cache.get(e)||null}set(e){return this._cache.set(e.key,e),e[ir](this.enqueueQueryEviction),this.enqueueQueryEviction(e),e}getOrSet(e,t,r){let s=this.get(e);return s?(r?.(s),s):(this.context.log("debug","QueryCache: creating new query",e),this.set(t()))}keepAlive(e){this._holds.add(e),this.context.log("debug","QueryCache: keepAlive",e)}dropKeepAlive(e){this._holds.delete(e);let t=this.get(e);t&&(t.subscribed||(this.context.log("debug","QueryCache: dropKeepAlive on unsubscribed query; queuing eviction",e),this.enqueueQueryEviction(t)))}get keepAlives(){return this._holds}};async function mo(){try{let i=await navigator.permissions.query({name:"periodic-background-sync"});if(i.state==="granted"){let n=await navigator.serviceWorker.ready;if("periodicSync"in n)try{await n.periodicSync.register("verdant-sync",{minInterval:24*60*60*1e3})}catch(e){console.warn("Failed to register background sync:",e)}}else console.debug("Background sync permission is not granted:",i)}catch(i){console.error("Failed to initiate background sync:",i)}}var Wi=class extends oe{constructor({endpointProvider:e,ctx:t}){super();this.onFileAdded=async e=>{if(!e.remote){this.ctx.log("debug","Uploading file",e.id,e.name);try{await this.uploadFile(e)}catch(t){this.ctx.log("error","File upload failed",t)}}};this.uploadFile=async(e,t={current:0,max:3})=>{let r=e.file;if(!r)throw new Error("Cannot upload a non-local file");let{files:s,token:o}=await this.endpointProvider.getEndpoints(),a=new FormData;a.append("file",r);try{let l=await this.ctx.environment.fetch(s+`/${e.id}`,{method:"POST",body:a,credentials:"include",headers:{Authorization:`Bearer ${o}`}});if(l.ok)return this.ctx.internalEvents.emit(`fileUploaded:${e.id}`,e),this.ctx.internalEvents.emit("fileUploaded",e),this.ctx.log("info","File upload successful"),{success:!0};{let p=await l.text();return this.ctx.log("error","File upload failed",l.status,p),l.status<500||t.current>=t.max?{success:!1,error:`Failed to upload file: ${l.status} ${p}`}:(await new Promise(f=>setTimeout(f,1e3)),this.uploadFile(e,{max:t.max,current:t.current+1}))}}catch(l){return this.ctx.log("error","File upload failed",l),t.current>=t.max?{success:!1,error:l.message}:(await new Promise(p=>setTimeout(p,1e3)),this.uploadFile(e,{max:t.max,current:t.current+1}))}};this.getFile=async(e,t={current:0,max:3})=>{let{files:r,token:s}=await this.endpointProvider.getEndpoints();try{let o=await this.ctx.environment.fetch(r+`/${e}`,{method:"GET",credentials:"include",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`}});return o.ok?{success:!0,data:await o.json()}:(this.ctx.log("error","File information fetch failed",r+`/${e}`,o.status,await o.text()),o.status<500&&o.status!==404||t.current>=t.max?{success:!1,error:`Failed to fetch file: ${o.status}`}:(await new Promise(a=>setTimeout(a,1e3)),this.getFile(e,{current:t.current+1,max:t.max})))}catch(o){return this.ctx.log("error","File information fetch failed",`${r}/${e}`,o),t.current>=t.max?{success:!1,error:o.message}:(await new Promise(a=>setTimeout(a,1e3)),this.getFile(e,{current:t.current+1,max:t.max}))}};this.endpointProvider=e,this.ctx=t,this.addDispose(t.internalEvents.subscribe("fileAdded",this.onFileAdded))}};var nr=Symbol("handleMessage"),gc,Bt=class extends W{constructor({initialPresence:e,updateBatchTimeout:t=200,defaultProfile:r,ctx:s}){super();this._peers={};this._self={profile:{}};this._selfReplicaIds=new Set;this._peerIds=new Array;this.isSelf=(e,t)=>e.id===t.replicaId||this._selfReplicaIds.has(t.replicaId)||this._self.id===t.id;this[gc]=async(e,t)=>{let r=!1,s=!1,o=new Set(this.peerIds);if(t.type==="presence-changed")this.isSelf(e,t.userInfo)?(this._self=t.userInfo,this._selfReplicaIds.add(t.userInfo.replicaId),s=!0,this.emit("selfChanged",t.userInfo)):(o.add(t.userInfo.id),this._peers[t.userInfo.id]=t.userInfo,r=!0,this.emit("peerChanged",t.userInfo.id,t.userInfo));else if(t.type==="sync-resp"){this._peers={},o.clear();for(let[a,l]of Object.entries(t.peerPresence))this.isSelf(e,l)?(this._self=l,this._selfReplicaIds.add(l.replicaId),s=!0,this.emit("selfChanged",l)):(r=!0,o.add(a),this._peers[a]=l,this.emit("peerChanged",a,l))}else if(t.type==="presence-offline"){o.delete(t.userId),this._selfReplicaIds.delete(t.replicaId);let a=this._peers[t.userId];delete this._peers[t.userId],r=!0,this.emit("peerLeft",t.userId,a)}r&&(this._peerIds=Array.from(o).sort(),this.emit("peersChanged",this._peers)),(r||s)&&this.emit("change")};this.update=async e=>{this._updateBatch.update({items:[{presence:e}]}),this.self.presence={...this.self.presence,...e},this.emit("selfChanged",this.self),this.emit("change")};this.flushPresenceUpdates=e=>{let t={presence:this.self.presence,internal:this.self.internal};for(let r of e)r.presence&&Object.assign(t.presence,r.presence),r.internal&&Object.assign(t.internal,r.internal);this.emit("update",t)};this.setViewId=e=>{this._updateBatch.update({items:[{internal:{viewId:e}}]}),this.self.internal.viewId=e,this.emit("selfChanged",this.self),this.emit("change")};this.setFieldId=(e,t=Date.now())=>{this._updateBatch.update({items:[{internal:{lastFieldId:e,lastFieldTimestamp:t}}]}),this.self.internal.lastFieldId=e,this.emit("selfChanged",this.self),this.emit("change")};this.getViewPeers=()=>this._peerIds.map(e=>this._peers[e]).filter(e=>this.self.internal.viewId===void 0||e.internal.viewId===this.self.internal.viewId);this.getFieldPeers=(e,t=60*1e3)=>this._peerIds.map(r=>this._peers[r]).filter(r=>r.internal.lastFieldId===e&&Date.now()-r.internal.lastFieldTimestamp<t);this.self.presence=e,this.self.profile=r,this.self.internal=gn,this.self.id="",this.self.replicaId="",s.waitForInitialization.then(()=>s.meta).then(o=>o.getLocalReplica()).then(o=>{this.self.replicaId=o.id}),this._updateBatcher=new it(this.flushPresenceUpdates),this._updateBatch=this._updateBatcher.add({max:25,timeout:t,items:[],key:"default"})}static{gc=nr}get self(){return this._self}get peers(){return this._peers}get peerIds(){return this._peerIds}get everyone(){let e={...this._peers};return e[this.self.id]=this.self,e}get selfReplicaIds(){return this._selfReplicaIds}};var gt=class extends W{constructor({interval:e=15*1e3,deadlineLength:t=3*1e3,restartOnTabFocus:r=!0}={}){super();this.nextBeat=null;this.deadline=null;this.keepAlive=()=>{this.deadline&&(clearTimeout(this.deadline),this.deadline=null,this.start())};this.start=(e=!1)=>{this.stop(),e?this.beat():this.nextBeat=setTimeout(this.beat,this._interval)};this.stop=()=>{this.nextBeat&&(clearTimeout(this.nextBeat),this.nextBeat=null),this.deadline&&(clearTimeout(this.deadline),this.deadline=null)};this.beat=async()=>{this.emit("beat"),this.deadline=setTimeout(this.onDeadline,this.deadlineLength)};this.onDeadline=()=>{this.deadline=null,this.emit("missed")};this.setInterval=e=>{this._interval=e};this._interval=e,this.deadlineLength=t,typeof window<"u"&&r&&(window.addEventListener("pageshow",()=>this.start(!0)),document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&this.start(!0)}))}get interval(){return this._interval}};var Hi=class extends W{constructor({endpointProvider:e,presence:t,interval:r=15*1e3,ctx:s}){super();this.mode="pull";this._isConnected=!1;this._status="paused";this._hasSynced=!1;this.setInterval=e=>{this.heartbeat.setInterval(e)};this.sendRequest=async e=>{this.ctx.log("debug","Sending sync request",e);try{let{http:t,token:r}=await this.endpointProvider.getEndpoints(),s=await this.fetch(t,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`},body:JSON.stringify({messages:e}),credentials:"include"});if(s.ok){this.heartbeat.keepAlive();let o=await s.json(),a=Promise.all(o.messages.map(this.handleServerMessage));this._isConnected||(this._isConnected=!0,this.emit("onlineChange",!0)),await a}else{this.ctx.log("error","Sync request failed",t,s.status),this._isConnected&&(this._isConnected=!1,this.emit("onlineChange",!1));let o=await s.json();pr(o)&&(o.code===bt.TokenExpired?(this.endpointProvider.clearCache(),this.heartbeat.keepAlive()):this.ctx.log("error","Server error",o)),s.status>=500&&this.heartbeat.keepAlive()}}catch(t){this._isConnected&&(this._isConnected=!1,this.emit("onlineChange",!1)),this.ctx.log("error",t),this.heartbeat.keepAlive()}};this.handleServerMessage=async e=>{e.type==="sync-resp"&&(this._hasSynced=!0,e.ackThisNonce&&(this.ctx.log("debug","Sending sync ack",e.ackThisNonce),await this.sendRequest([await(await this.ctx.meta).messageCreator.createAck(e.ackThisNonce)]))),this.emit("message",e)};this.throttledPresenceUpdate=Er(e=>{this.sendRequest([e])},3e3);this.send=e=>{if(this.status!=="active"){this.ctx.log("warn","Attempted to send message while sync is not active",e);return}switch(e.type){case"presence-update":return this.throttledPresenceUpdate(e);case"sync":case"heartbeat":return this.sendRequest([e]);case"op":if(this._hasSynced)return this.sendRequest([e]);break}};this.start=async()=>{this.status!=="active"&&(this.ctx.log("debug","Starting push-pull sync"),await this.endpointProvider.getEndpoints(),this.heartbeat.start(!0),this._status="active")};this.destroy=()=>{this.dispose(),this.stop()};this.onHeartbeat=async()=>{this.sendRequest([await(await this.ctx.meta).messageCreator.createPresenceUpdate(this.presence.self),await(await this.ctx.meta).messageCreator.createSyncStep1()])};this.onHeartbeatMissed=async()=>{this.emit("onlineChange",!1),this.ctx.log("warn","Missed heartbeat"),this._isConnected=!1};this.syncOnce=async()=>{await this.sendRequest([await(await this.ctx.meta).messageCreator.createSyncStep1()])};this.ctx=s,this.presence=t,this.endpointProvider=e,this.heartbeat=new gt({interval:r}),this.heartbeat.subscribe("beat",this.onHeartbeat),this.heartbeat.subscribe("missed",this.onHeartbeatMissed)}get fetch(){return this.ctx.environment.fetch}get interval(){return this.heartbeat.interval}get hasSynced(){return this._hasSynced}stop(){this.ctx.log("debug","Stopping push-pull sync"),this.heartbeat.stop(),this._status="paused"}reconnect(){this.heartbeat.start(!0)}ignoreIncoming(){this.stop()}get isConnected(){return this._isConnected}get status(){return this._status}};function rr(i){this.message=i}rr.prototype=new Error,rr.prototype.name="InvalidCharacterError";var go=typeof window<"u"&&window.atob&&window.atob.bind(window)||function(i){var n=String(i).replace(/=+$/,"");if(n.length%4==1)throw new rr("'atob' failed: The string to be decoded is not correctly encoded.");for(var e,t,r=0,s=0,o="";t=n.charAt(s++);~t&&(e=r%4?64*e+t:t,r++%4)?o+=String.fromCharCode(255&e>>(-2*r&6)):0)t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(t);return o};function yc(i){var n=i.replace(/-/g,"+").replace(/_/g,"/");switch(n.length%4){case 0:break;case 2:n+="==";break;case 3:n+="=";break;default:throw"Illegal base64url string!"}try{return function(e){return decodeURIComponent(go(e).replace(/(.)/g,function(t,r){var s=r.charCodeAt(0).toString(16).toUpperCase();return s.length<2&&(s="0"+s),"%"+s}))}(n)}catch{return go(n)}}function qi(i){this.message=i}function bc(i,n){if(typeof i!="string")throw new qi("Invalid token specified");var e=(n=n||{}).header===!0?0:1;try{return JSON.parse(yc(i.split(".")[e]))}catch(t){throw new qi("Invalid token specified: "+t.message)}}qi.prototype=new Error,qi.prototype.name="InvalidTokenError";var yo=bc;var Qi=class{constructor(n,e){this.config=n;this.ctx=e;this.cached=null;this.tokenInfo=null;this.getEndpoints=async()=>{if(this.cached)return this.cached;let n;if(this.config.fetchAuth)n=await this.config.fetchAuth();else{let a=this.ctx.environment.fetch;n=await a(this.config.authEndpoint,{credentials:"include"}).then(l=>{if(l.ok)return l.json();throw new Error(`Auth endpoint returned non-200 response: ${l.status}`)})}L(n.accessToken,"No access token provided from auth endpoint");let e=yo(n.accessToken);L(e.url,"No sync endpoint provided from auth endpoint"),L(e.type!==void 0,"No replica type provided from auth endpoint"),this.tokenInfo={userId:e.sub,libraryId:e.lib,url:e.url,fileUrl:e.file,role:e.role,type:parseInt(e.type+"")};let t=new URL(e.url);t.protocol=t.protocol.replace("ws","http");let r=t.toString();t.protocol=t.protocol.replace("http","ws");let s=t.toString(),o=e.file;if(!o){let a=new URL(r);a.pathname=a.pathname+"/files",o=a.toString()}return this.cached={http:r,websocket:s,files:o,token:n.accessToken},this.cached};this.clearCache=()=>{this.cached=null};if(!n.authEndpoint&&!n.fetchAuth)throw new Error("Either authEndpoint or fetchAuth must be provided to ServerSyncEndpointProvider")}get type(){return this.tokenInfo?.type??Ue.Realtime}};var Ji=class extends W{constructor(e){super();this.timer=null;this.isScheduled=!1;this.next=()=>{this.isScheduled||(this.isScheduled=!0,this.timer=setTimeout(()=>{this.emit("trigger"),this.isScheduled=!1,this.backoff.next()},this.backoff.current))};this.reset=()=>{this.backoff.reset(),this.timer&&(clearTimeout(this.timer),this.timer=null)};this.backoff=e}},Gi=class{constructor(n,e,t){this.current=0;this.next=()=>{this.current=Math.min(this.max,Math.max(1,this.current)*this.factor)};this.reset=()=>{this.current=0};this.current=n,this.max=e,this.factor=t}};var Xi=class extends W{constructor({endpointProvider:e,ctx:t,presence:r}){super();this.socket=null;this.connectQueue=[];this.syncQueue=[];this.incomingQueue=[];this._status="paused";this.synced=!1;this.hasStartedSync=!1;this._ignoreIncoming=!1;this.mode="realtime";this.heartbeat=new gt;this.reconnectScheduler=new Ji(new Gi(2e3,6e4,1.5));this.onOpen=()=>{if(!this.socket)throw new Error("Invalid sync state: online but socket is null");if(this.synced=!1,this.connectQueue.length){for(let e of this.connectQueue)this.ctx.log("debug","Sending queued message",JSON.stringify(e,null,2)),this.socket.send(JSON.stringify(e));this.connectQueue=[]}this.ctx.log("debug","Sync connected"),this.onOnlineChange(!0)};this.onOnlineChange=async e=>{if(this.ctx.log("info","Socket online change",e),!this.disposed&&!this.ctx.closing){if(!e)this.hasStartedSync=!1,this.synced=!1,this.heartbeat.stop();else{this.ctx.log("debug","Starting sync"),this.hasStartedSync=!0,this.synced=!1;let t=await this.ctx.meta;this.ctx.log("debug","HERE"),this.send(await t.messageCreator.createPresenceUpdate(this.presence.self)),this.send(await t.messageCreator.createSyncStep1()),this.heartbeat.start()}this.emit("onlineChange",e)}};this.onMessage=async e=>{if(this.reconnectScheduler.reset(),this._ignoreIncoming){this.ctx.log("warn","Ignoring incoming message (ignore incoming flag set)",e.data);return}let t=JSON.parse(e.data);switch(this.ctx.log("debug","Received",t.type,"message"),t.type){case"sync-resp":if(t.ackThisNonce&&this.send(await(await this.ctx.meta).messageCreator.createAck(t.ackThisNonce)),this.hasStartedSync=!0,this.synced=!0,this.syncQueue.length)if(t.overwriteLocalData)this.ctx.log("warn","Overwriting local data - dropping outgoing message queue"),this.syncQueue=[];else{for(let r of this.syncQueue)this.send(r);this.syncQueue=[]}if(this.emit("message",t),this.incomingQueue.length){for(let r of this.incomingQueue)this.emit("message",r);this.incomingQueue=[]}break;case"need-since":case"presence-changed":case"presence-offline":this.emit("message",t);break;case"op-re":if(!this.synced){this.ctx.log("debug","Enqueueing op-re message because sync hasn't finished yet",t),this.incomingQueue.push(t);break}this.emit("message",t);break;case"heartbeat-response":this.heartbeat.keepAlive(),this.emit("message",t);break;default:this.synced&&this.emit("message",t);break}};this.onError=e=>{this.ctx.log("error","Sync socket error",e,e.target),!this.disposed&&(this.reconnectScheduler.next(),this.ctx.log("info","Attempting reconnect to websocket sync"))};this.onClose=e=>{this.ctx.log("info","Sync socket disconnected",e.code),this.onOnlineChange(!1),!this.disposed&&(this.reconnectScheduler.next(),this.ctx.log("info","Attempting reconnect to websocket sync"))};this.initializeSocket=async()=>{let e=await this.endpointProvider.getEndpoints();return this.socket=new this.ctx.environment.WebSocket(e.websocket,["Bearer",e.token]),this.socket.addEventListener("message",this.onMessage),this.socket.addEventListener("open",this.onOpen),this.socket.addEventListener("error",this.onError),this.socket.addEventListener("close",this.onClose),this.socket};this.sendHeartbeat=async()=>{this.send(await(await this.ctx.meta).messageCreator.createHeartbeat())};this.reconnect=()=>{this.stop(),this.start()};this.canSkipSyncWait=e=>e.type==="sync"||e.type==="presence-update"||e.type==="sync-ack"||e.type==="heartbeat";this.send=e=>{if(this.status!=="active"){this.ctx.log("debug","Ignoring outgoing message",e.type,"sync is not active");return}if(!this.hasStartedSync&&!this.canSkipSyncWait(e)){this.ctx.log("debug","Ignoring outgoing message",e.type,"still waiting to begin initial sync");return}this.canSkipSyncWait(e)?this.socket?.readyState===Yi?(this.ctx.log("debug","Sending message",JSON.stringify(e,null,2)),this.socket.send(JSON.stringify(e))):(this.ctx.log("debug","Enqueueing message until socket is open",JSON.stringify(e,null,2)),this.connectQueue.push(e)):this.synced?this.socket?.readyState===Yi&&(this.ctx.log("debug","Sending message",JSON.stringify(e,null,2)),this.socket.send(JSON.stringify(e))):this.hasStartedSync&&(this.ctx.log("debug","Enqueueing message until synced",JSON.stringify(e,null,2)),this.syncQueue.push(e))};this.destroy=()=>{this.dispose(),this.stop()};this.start=async()=>{this.socket||(await this.initializeSocket(),this._status="active")};this.stop=()=>{this.socket?.removeEventListener("message",this.onMessage),this.socket?.removeEventListener("close",this.onClose),this.socket?.readyState===Yi&&this.socket.close(),this.socket=null,this._status="paused"};this.ctx=t,this.endpointProvider=e,this.presence=r,this.reconnectScheduler.subscribe("trigger",this.initializeSocket),this.heartbeat.subscribe("beat",this.sendHeartbeat)}get hasSynced(){return this.synced}ignoreIncoming(){this.incomingQueue=[],this._ignoreIncoming=!0}get isConnected(){return this.socket?.readyState===Yi}get status(){return this._status}},Yi=1;var Zi=class extends W{constructor(e){super();this.mode="pull";this.hasSynced=!1;this.destroy=()=>{};this.isConnected=!1;this.status="paused";this.pullInterval=0;this.uploadFile=async()=>({success:!1,retry:!1});this.getFile=async()=>({success:!1,error:"Sync is not active"});this.syncOnce=async()=>{};this.presence=new Bt({initialPresence:null,defaultProfile:null,ctx:e})}send(){}async start(){}stop(){}ignoreIncoming(){}reconnect(){}setMode(){}setPullInterval(){}},yt=class extends W{constructor({authEndpoint:e,fetchAuth:t,initialPresence:r,automaticTransportSelection:s=!0,autoStart:o,initialTransport:a,pullInterval:l,presenceUpdateBatchTimeout:p,defaultProfile:f,useBroadcastChannel:S,onOutgoingMessage:w,EXPERIMENTAL_backgroundSync:A},{ctx:y,onData:I}){super();this.broadcastChannel=null;this._activelySyncing=!1;this._hasSynced=!1;this.handleBroadcastChannelMessage=e=>{e.data.type==="sync"&&this.handleMessage(e.data.message,{source:"broadcastChannel"})};this.handleMessage=async(e,{source:t}={source:"network"})=>{if(!this.ctx.closing){if(e.type==="op-re"||e.type==="sync-resp")for(let r of e.operations)this.ctx.time.update(r.timestamp);switch(this.ctx.log("debug","sync message",JSON.stringify(e,null,2)),e.type){case"op-re":await this.onData({operations:e.operations,baselines:e.baselines}),e.globalAckTimestamp&&await(await this.ctx.meta).setGlobalAck(e.globalAckTimestamp);break;case"global-ack":await(await this.ctx.meta).setGlobalAck(e.timestamp);break;case"sync-resp":this._activelySyncing=!0,this.emit("syncingChange",!0),await this.onData({operations:e.operations,baselines:e.baselines,reset:e.overwriteLocalData}),e.globalAckTimestamp&&await(await this.ctx.meta).setGlobalAck(e.globalAckTimestamp),await(await this.ctx.meta).updateLastSynced(e.ackedTimestamp),this._activelySyncing=!1,this.emit("syncingChange",!1),this._hasSynced=!0,this.emit("synced");break;case"need-since":this.emit("serverReset",e.since),(await this.ctx.files).onServerReset(e.since),this.activeSync.send(await(await this.ctx.meta).messageCreator.createSyncStep1(e.since));break;case"server-ack":await(await this.ctx.meta).updateLastSynced(e.timestamp)}t==="network"&&this.broadcastChannel?.postMessage({type:"sync",message:e}),this.presence[nr](await(await this.ctx.meta).getLocalReplica(),e)}};this.handleOnlineChange=async e=>{if(this.emit("onlineChange",e),e){let t=await(await this.ctx.files).listUnsynced(),r=await Promise.allSettled(t.map(s=>this.fileSync.uploadFile(s)));r.some(s=>s.status==="rejected")&&this.ctx.log("error","Failed to upload unsynced files",r.filter(s=>s.status==="rejected").map(s=>s.reason))}};this.handlePresenceUpdate=async e=>{this.send(await(await this.ctx.meta).messageCreator.createPresenceUpdate(e))};this.setMode=e=>{if(e==="realtime"&&!this.canDoRealtime)throw new Error("Cannot switch to realtime mode, because the current auth token does not allow it");let t;e==="realtime"?t=this.webSocketSync:t=this.pushPullSync,t!==this.activeSync&&(this.ctx.log("debug","switching to",e,"mode"),this.activeSync.status==="active"&&t.start(),this.activeSync.stop(),this.activeSync=t)};this.setPullInterval=e=>{this.pushPullSync.setInterval(e)};this.send=async e=>{if(this.activeSync.status==="active"){let t=this.endpointProvider.tokenInfo?.userId;if(!t)throw new N(N.Code.Unexpected,void 0,"Active sync has invalid token info");(e.type==="sync"||e.type==="op")&&fr(e,t),this.activeSync.send(e),this.onOutgoingMessage?.(e)}};this.uploadFile=async e=>(this.ctx.log("info","Uploading file",{name:e.name,type:e.type,id:e.id,size:e.file?.size}),this.activeSync.status==="active"?this.fileSync.uploadFile(e):{success:!1,retry:!1,error:"Sync is not active"});this.getFile=async e=>{if(this.activeSync.status==="active")return this.fileSync.getFile(e);if(await this.getSyncStartPromise(),this.activeSync.status==="paused")throw new N(N.Code.Offline,void 0,"Sync is not active");return this.fileSync.getFile(e)};this.getSyncStartPromise=(e=5e3)=>new Promise((t,r)=>{let s=setTimeout(()=>{r(new Error("Sync did not start in time")),o()},e),o=this.subscribe("onlineChange",a=>{a&&(clearTimeout(s),o(),t())})});this.start=()=>(this.ctx.log("info","Starting sync"),this.activeSync.start());this.stop=()=>(this.ctx.log("info","Stopping sync"),this.activeSync.stop());this.destroy=()=>{this.dispose(),this.webSocketSync.destroy(),this.pushPullSync.destroy()};this.reconnect=()=>this.activeSync.reconnect();this.syncOnce=()=>this.pushPullSync.syncOnce();if(this.onData=I,this.ctx=y,this.onOutgoingMessage=w,this.presence=new Bt({initialPresence:r,defaultProfile:f,updateBatchTimeout:p,ctx:y}),this.endpointProvider=new Qi({authEndpoint:e,fetchAuth:t},y),this.webSocketSync=new Xi({endpointProvider:this.endpointProvider,presence:this.presence,ctx:y}),this.pushPullSync=new Hi({endpointProvider:this.endpointProvider,presence:this.presence,interval:l,ctx:y}),this.fileSync=new Wi({endpointProvider:this.endpointProvider,ctx:y}),S&&"BroadcastChannel"in window&&(this.broadcastChannel=new BroadcastChannel(`verdant-${y.namespace}`),this.broadcastChannel.addEventListener("message",this.handleBroadcastChannelMessage)),y.log("info","Sync initialized with transport:",a??"pull"),a==="realtime"?this.activeSync=this.webSocketSync:this.activeSync=this.pushPullSync,this.presence.subscribe("update",this.handlePresenceUpdate),y.internalEvents.subscribe("outgoingSyncMessage",this.send),this.webSocketSync.subscribe("message",this.handleMessage),this.webSocketSync.subscribe("onlineChange",this.handleOnlineChange),this.pushPullSync.subscribe("message",this.handleMessage),this.pushPullSync.subscribe("onlineChange",this.handleOnlineChange),s&&this.canDoRealtime){let E=()=>{P&&clearTimeout(P);let T=this.presence.getViewPeers().length>0||s!=="peers-only"&&this.presence.selfReplicaIds.size>1;T&&this.mode==="pull"?this.setMode("realtime"):!T&&this.mode==="realtime"&&(P=setTimeout(()=>{this.presence.getViewPeers().length===0&&this.setMode("pull")},1e3))},P;this.presence.subscribe("peersChanged",E),s!=="peers-only"&&this.presence.subscribe("selfChanged",E)}o&&this.start(),A&&mo()}get canDoRealtime(){return this.endpointProvider.type===Ue.Realtime||this.endpointProvider.type===Ue.PassiveRealtime||this.endpointProvider.type===Ue.ReadOnlyRealtime}get syncing(){return this._activelySyncing}get hasSynced(){return this._hasSynced}get pullInterval(){return this.pushPullSync.interval}ignoreIncoming(){this.activeSync.ignoreIncoming()}get isConnected(){return this.activeSync.isConnected}get status(){return this.activeSync.status}get mode(){return this.activeSync.mode}};function bo(i){return i.operations.map(t=>t.timestamp).concat(i.baselines?.map(t=>t.timestamp)??[]).reduce((t,r)=>{let s=ai(r);return s>t?s:t},1)}var en=class extends W{constructor(e){super();this.contextInit=e;this.importingPromise=Promise.resolve();this.addData=async e=>{if(this.context.closing){this.context.log("warn","Client is closing; ignoring incoming sync data");return}await this.importingPromise;try{let t=e.reset?bo(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. To avoid data corruption, the client will now shut down.",t),this.emit("developerError",new N(N.Code.Unexpected,t,"Sync failed. To avoid data corruption, the client will now shut down.")),await this.close(),t}};this.stats=async()=>{if(this.disposed)return{};let e=await(await this.context.documents).stats(),t=await(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(await this.context.files).stats(),o=Object.values(e).reduce((p,{size:f})=>p+f,0),a=t.baselinesSize.size+t.operationsSize.size,l=a/o;return{collections:e,meta:t,storage:r,totalMetaSize:a,totalCollectionsSize:o,metaToDataRatio:l,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(),this.context.internalEvents.disable(),this.context.entityEvents.disable(),await new Promise(e=>{e()}),this.context.log?.("info","Client closed")};this.__dangerous__resetLocal=async()=>{this.sync.stop(),await gs(this.namespace,this.context.environment)};this.export=async({downloadRemoteFiles:e}={downloadRemoteFiles:!0})=>{this.context.log("info","Exporting data...");let t=await(await this.context.meta).export(),{fileData:r,files:s}=await(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 As(this.context,{data:e,files:r,fileData:t}),s()};this.__dangerous__hardReset=async()=>{let e=await this.export();await this.import(e)};this.__cleanupFilesImmediately=async()=>(await this.context.files).cleanupDeletedFiles();this.__manualRebase=async()=>((await this.context.meta).manualRebase(),new Promise(e=>{let t=this.subscribe("rebase",()=>{t(),e()})}));this.context=new xi(this.contextInit),this.context.getClient=()=>this,this.collectionNames=Object.keys(this.context.schema.collections),this._sync=this.context.config.sync&&!this.context.schema.wip?new yt(this.context.config.sync,{onData:this.addData,ctx:this.context}):new Zi(this.context),this.context.schema.wip&&this.context.config.sync&&this.context.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 Mi({sync:this.sync,context:this.context}),this._entities=new Li({ctx:this.context,files:this._fileManager}),this._queryCache=new Ki({context:this.context,evictionTime:this.context.config.queries?.evictionTime}),this._documentManager=new Si(this.schema,this._entities);let t=Ir(()=>{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(this.context.schema.collections)){let o=r;this[o]=new $i({collection:o,cache:this._queryCache,context:this.context,entities:this.entities,documentManager:this.documentManager})}}get schema(){return this.context.schema}get namespace(){return this.context.namespace}get undoHistory(){return this.context.undoHistory}get queries(){return this._queryCache}get sync(){return this._sync}get entities(){return this._entities}get documentManager(){return this._documentManager}async getReplicaId(){return(await(await this.context.meta).getLocalReplica()).id}get batch(){return this.entities.batch}get __persistence(){return{meta:this.context.meta,queries:this.context.documents,files:this.context.files}}get __persistenceReady(){return this.context.waitForInitialization}};var wc={private:Ke.onlyMe(),public:void 0};function vc(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}`}}var sr=Be(Qe(),1);function xc(i){return i?sr.default.slug():(0,sr.default)()}window.Verdant=or;
|
|
18
18
|
//# sourceMappingURL=index.js.map
|