diff --git a/PRODUCT.md b/PRODUCT.md index 3a936d7..a25b3a9 100644 --- a/PRODUCT.md +++ b/PRODUCT.md @@ -40,6 +40,7 @@ The local vault remains useful while offline. Synchronization is explicit, obser - Enroll using a Connect enrollment code. - Store credentials in Obsidian SecretStorage, not plugin data or collection files. - Preview and run mirror synchronization. +- Keep record validation separate from exact-document replication; never silently repair records during sync. - Show progress, last successful checkpoint, failures, and conflicts in plain language. - Refuse unsafe role changes or resource overwrites. diff --git a/README.md b/README.md index 39dcc1e..6c5a279 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,9 @@ disposable vault with the exact installed bundle. The Connect protocol and sync SDKs are pinned to `0.1.0-beta.91`, and mdbase interop is pinned to `0.1.0-rc.2`. Update `package.json`, regenerate `package-lock.json`, and rerun the binary round-trip and mobile gates when -advancing them. +advancing them. The pending lossless malformed-frontmatter upgrade is documented +in [SDK consumer qualification](docs/lossless-sdk-acceptance.md); its strict +candidate check is separate from the released beta.91 test suite. ## Compatibility diff --git a/docs/lossless-sdk-acceptance.md b/docs/lossless-sdk-acceptance.md new file mode 100644 index 0000000..3dec99c --- /dev/null +++ b/docs/lossless-sdk-acceptance.md @@ -0,0 +1,80 @@ +# Lossless SDK consumer qualification + +## Shipping versus candidate behavior + +The tracked dependency pins remain `connect-sync` / `connect-protocol` +`0.1.0-beta.91`. That released mirror engine still blocks malformed frontmatter. +This branch prepares consumer messaging and fixes BOM preservation; it does not +pretend that an unpublished SDK fix is already shipped in the plugin. + +Connect commit `87e28391233c` makes readable malformed YAML a nonblocking +structural diagnostic by default. No option, protocol change, or public +TypeScript shape change is required. Its immutable candidate artifacts were +produced with Connect's guarded `package:consumer` script, not published to npm. +Advance the released pins and lockfile after the SDK release, rerun the strict +candidate test, and include that check in normal qualification at that point. +Do not commit machine-local tarball paths or a nonexistent registry version. + +## Consumer changes + +- Obsidian's binary-to-text adapter preserves UTF-8 BOMs, CRLF and final-newline + choices. Invalid UTF-8 still fails safely. +- The reviewed plan remains the sole apply gate. Diagnostic count and issue code + alone do not decide whether sync is paused. +- Nonblocking diagnostics say `review`, not mandatory `fix`; warning prose no + longer promises that all synchronization is paused. +- Validation remains separate. A regression runs actual plugin schema validation + (missing required title, wrong priority type, invalid enum), transfers the + unchanged record through `MemoryAuthority` into a second mocked Vault, and + verifies that the validation errors are still present afterward. + +## Automated results + +Node 24.19.0: + +| Installed SDK | Unit tests | Build / mobile | +| --- | --- | --- | +| Released beta.91 | 96 passed | 687,949 raw / 195,842 gzip bytes | +| Candidate from `87e28391233c` | 96 passed | 689,242 raw / 196,214 gzip bytes | + +Both build sizes satisfy the unchanged mobile budgets. Lint passes with the +released dependencies. No consumer TypeScript adjustment was needed for the +candidate. Both package manifests and the lockfile keep their released pins. + +`npm run test:sdk-candidate` additionally passes ten exact round trips through +the real `ObsidianMirrorFileSystem` and portable sync engine, including malformed +YAML, duplicate keys, scalar/null/list frontmatter, BOM/CRLF, missing final +newline, BOM-prefixed valid mappings and body-only notes, and a valid sibling. It checks that the preview enables the transfers +and the receive-only mirror settles without attempting repeated repairs. + +The released beta.91 control run fails this stricter command at the expected +assertion: seven blocking issues instead of zero. The candidate passes without +changing the test or adding a runtime option. To qualify an immutable +candidate without changing tracked dependency pins: + +```sh +npm ci --ignore-scripts +npm install --no-save --package-lock=false --ignore-scripts \ + /path/to/immutable-protocol.tgz /path/to/immutable-sync.tgz +npm test +npm run test:sdk-candidate +npm run check:mobile +# Restore released dependencies and the release-pinned generated bundle: +npm ci --ignore-scripts +npm run check:mobile +``` + +## Boundaries not qualified here + +The Vault APIs and authority in these tests are in-memory fixtures. Schema +validation is real plugin code, but the lightweight Obsidian YAML mock uses +JSON-compatible YAML. These results are not live Obsidian or deployed-hosted +acceptance, and do not verify production or staging. + +The prior LAB port-ownership failure and requirement for a clean disposable +Obsidian vault remain outstanding. No guards were bypassed, no existing vault +metadata was removed, and no LAB fixture, installed plugin, production, or +staging service was changed in this work. + +The recovery workspace remains a separate draft PR; this branch does not merge +or replace it. diff --git a/main.js b/main.js index f1f6c1b..f977b66 100644 --- a/main.js +++ b/main.js @@ -168,7 +168,7 @@ ${t.comment}`:t.comment}this.doc.range[2]=t.offset;break}default:this.errors.pus `,t),t>=0&&r.startsWith(i,t)&&t+i.length===r.length}function Lc(r,e){let t=r.match(/^---[ \t]*\r?\n([\s\S]*?)^---[ \t]*(?:\r?\n|$)([\s\S]*)$/m);if(!t)return{frontmatter:{},body:r};let n;try{n=Hn(t[1],{mapAsMap:!0})}catch{return{frontmatter:{},body:r}}if(n===null&&t[1].trim()==="")return{frontmatter:{},body:t[2]??""};let i=yp(n,new Set);return i===Lr||!sO(i)?{frontmatter:{},body:r}:{frontmatter:i,body:t[2]??""}}function yp(r,e){if(r===null||typeof r=="string"||typeof r=="boolean")return r;if(typeof r=="number")return Number.isFinite(r)?r:Lr;if(!r||typeof r!="object"||e.has(r))return Lr;e.add(r);try{if(Array.isArray(r)){let n=[];for(let i of r){let s=yp(i,e);if(s===Lr)return Lr;n.push(s)}return n}if(!(r instanceof Map))return Lr;let t={};for(let[n,i]of r){if(typeof n!="string")return Lr;let s=yp(i,e);if(s===Lr)return Lr;Object.defineProperty(t,n,{value:s,enumerable:!0,configurable:!0,writable:!0})}return t}finally{e.delete(r)}}function sO(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)}function kw(r){return typeof r=="boolean"||r===null||typeof r=="number"&&Number.isSafeInteger(r)?String(r):typeof r!="string"||!/^[A-Za-z][A-Za-z0-9 _.-]*$/u.test(r)||r.length<=5&&rO.has(r.toLowerCase())?null:r}function mt(r,e,t){return r.startsWith(e,t)?t+e.length:-1}function jc(r){let e=Je.create().update(jr.encode(`mdbase-authority-manifest-v2 `));for(let t of[...r].sort((n,i)=>n.kind!==i.kind?n.kinde.type==="file_put"||e.type==="file_remove"))throw new b("file_sync_unsupported","This replica cannot materialize collection file changes yet. Upgrade it before continuing sync.")}function Iw(r){aO([r])}var K=class extends b{status;constructor(e,t,n,i){super(e,t),this.status=n,this.name="AuthorityAdoptionError",i?.cause!==void 0&&(this.cause=i.cause)}},qr=class extends K{sourceMustRemainFenced=!0;constructor(e,t){super("authority_adoption_outcome_unknown",e,void 0,t),this.name="AuthorityAdoptionOutcomeUnknownError"}};var Ji=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function Gn(r,e){if(!Ji.test(r))throw new K("invalid_authority_adoption",`${e} must be a UUID.`);return r.toLowerCase()}var vo=Uint32Array.from([1732584193,4023233417,2562383102,271733878,3285377520]),bn=new Uint32Array(80),bp=class extends Gi{A=vo[0]|0;B=vo[1]|0;C=vo[2]|0;D=vo[3]|0;E=vo[4]|0;constructor(){super(64,20,8,!1)}get(){let{A:e,B:t,C:n,D:i,E:s}=this;return[e,t,n,i,s]}set(e,t,n,i,s){this.A=e|0,this.B=t|0,this.C=n|0,this.D=i|0,this.E=s|0}process(e,t){for(let c=0;c<16;c++,t+=4)bn[c]=e.getUint32(t,!1);for(let c=16;c<80;c++)bn[c]=Tc(bn[c-3]^bn[c-8]^bn[c-14]^bn[c-16],1);let{A:n,B:i,C:s,D:o,E:a}=this;for(let c=0;c<80;c++){let l,u;c<20?(l=Oc(i,s,o),u=1518500249):c<40?(l=i^s^o,u=1859775393):c<60?(l=Nc(i,s,o),u=2400959708):(l=i^s^o,u=3395469782);let d=Tc(n,5)+l+a+u+bn[c]|0;a=o,o=s,s=Tc(i,30),i=n,n=d}n=n+this.A|0,i=i+this.B|0,s=s+this.C|0,o=o+this.D|0,a=a+this.E|0,this.set(n,i,s,o,a)}roundClean(){yn(bn)}destroy(){this.destroyed=!0,this.set(0,0,0,0,0),yn(this.buffer)}},Fc=Rc(()=>new bp);var cO=1e4,lO=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu,dO=new TextEncoder;async function Rw(r,e,t,n,i){let s=fO(n,t.media_type);if(s.size!==t.size||await pO(s,i.signal)!==t.content_digest)throw new K("authority_adoption_file_changed",`File bytes no longer match the fenced snapshot for ${t.path}.`);let o=hO(e.import_id,t),a=await vp(r,`${e.files_url}/uploads`,e.access_token,{protocol_version:1,type:"open_authority_import_file_upload",transfer_id:o,file_id:t.file_id},i.signal);if(yO(a,o,t.size),a.strategy.kind!=="object_put"&&a.strategy.kind!=="object_multipart")throw yt("Connect returned an incompatible authority import file strategy.");let c=a.strategy.kind==="object_put"?Math.max(1,t.size):a.strategy.part_size,l=a.strategy.kind==="object_put"?1:Math.ceil(t.size/c);if(l>cO)throw yt("Authority import returned too many file parts.");let u=new Set(a.received),d=new Map((a.uploaded_parts??[]).map(h=>[h.part_number-1,h]));if(a.received.length===l&&await Tw(r,e,t,o,[...d.values()],i.signal))return;let f=Array(l);for(let[h,y]of d)f[h]=y;let p=[...u].reduce((h,y)=>h+Math.min(c,Math.max(0,t.size-y*c)),0);for(let h=0;hh!==void 0);if(!await Tw(r,e,t,o,m,i.signal))throw new K("authority_adoption_file_upload_incomplete",`Connect could not commit ${t.path}.`)}async function Tw(r,e,t,n,i,s){try{let o=await vp(r,`${e.files_url}/uploads/${encodeURIComponent(n)}/commit`,e.access_token,{protocol_version:1,type:"commit_file_upload",transfer_id:n,...i.length>0?{parts:i}:{}},s);if(o.protocol_version!==1||o.type!=="file_upload_committed"||o.transfer_id!==n||!_O(o.file,t))throw yt("Connect returned an invalid authority import file receipt.");return!0}catch(o){if(i.length===0&&o instanceof K&&o.code==="file_upload_incomplete")return!1;throw o}}async function vp(r,e,t,n,i){let s;try{s=await r({url:e,method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:n,...i?{signal:i}:{}})}catch{throw Uc(i),new K("authority_adoption_unreachable","Connect could not be reached for collection adoption.")}if(s.status<200||s.status>=300)throw wO(s);return s.body}async function uO(r,e,t,n){bO(e.url);let i;try{i=await r({url:e.url,method:"PUT",headers:vO(e.headers),body:t,rawBody:!0,...n?{signal:n}:{}})}catch{throw Uc(n),new K("authority_adoption_unreachable","Connect could not be reached for collection adoption.")}if(i.status<200||i.status>=300)throw new K("authority_adoption_object_upload_failed","Object storage rejected an authority import file part.",i.status);return i}function fO(r,e){if(r instanceof Blob)return r;if(r instanceof ArrayBuffer)return new Blob([r],{type:e});let t=new Uint8Array(r.buffer,r.byteOffset,r.byteLength).slice();return new Blob([t],{type:e})}async function pO(r,e){let t=Je.create(),n=r.stream().getReader();try{for(;;){Uc(e);let i=await n.read();if(i.done)break;t.update(i.value)}}finally{n.releaseLock()}return`sha256:${Oe(t.digest())}`}function hO(r,e){let t=mO(r),n=dO.encode(`mdbase-authority-import-file-v1\0${e.file_id}\0${e.revision}\0${e.content_digest}`),i=Fc(new Uint8Array([...t,...n])).slice(0,16);i[6]=i[6]&15|80,i[8]=i[8]&63|128;let s=Oe(i);return`${s.slice(0,8)}-${s.slice(8,12)}-${s.slice(12,16)}-${s.slice(16,20)}-${s.slice(20)}`}function mO(r){if(!lO.test(r))throw yt("Authority import ID is invalid.");return Uint8Array.from(r.replaceAll("-","").match(/../g).map(e=>Number.parseInt(e,16)))}function yO(r,e,t){let n=r?.strategy;if(r?.protocol_version!==1||r.type!=="file_transfer"||r.transfer_id!==e||r.direction!=="upload"||r.protection!=="transport_tls"||r.total_size!==t||!Array.isArray(r.received)||!n||!["object_put","object_multipart"].includes(n.kind)||n.kind==="object_multipart"&&(!Number.isSafeInteger(n.part_size)||n.part_size<=0))throw yt("Connect returned an invalid authority import file session.");if(n.kind!=="object_put"&&n.kind!=="object_multipart")throw yt("Connect returned an invalid authority import file strategy.");let i=n.kind==="object_put"?Math.max(1,t):n.part_size,s=n.kind==="object_put"?1:Math.ceil(t/i);if(new Set(r.received).size!==r.received.length||r.received.some(a=>!Number.isSafeInteger(a)||a<0||a>=s))throw yt("Connect returned invalid authority import file progress.");let o=r.uploaded_parts??[];if(!Array.isArray(o)||o.some((a,c)=>!Number.isSafeInteger(a?.part_number)||a.part_number<1||a.part_number>s||typeof a.etag!="string"||a.etag.length===0||a.etag.length>255||c>0&&o[c-1].part_number>=a.part_number)||(n.kind==="object_multipart"?o.length!==r.received.length||o.some((a,c)=>a.part_number-1!==r.received[c]):o.length!==0))throw yt("Connect returned invalid authority import part receipts.")}function gO(r,e,t,n,i){if(r?.protocol_version!==1||r.type!=="file_part"||r.transfer_id!==e||r.part_index!==t||r.offset!==n||r.content_length!==i||r.method.toUpperCase()!=="PUT"||!Number.isFinite(Date.parse(r.expires_at))||!_p(r.headers))throw yt("Connect returned an invalid prepared authority import file part.")}function bO(r){let e;try{e=new URL(r)}catch{throw yt("Connect returned an invalid object storage URL.")}if(e.protocol!=="https:"&&!(e.protocol==="http:"&&["localhost","127.0.0.1","[::1]","::1"].includes(e.hostname))||e.username||e.password||e.hash)throw yt("Connect returned an unsafe object storage URL.")}function vO(r){let e={};for(let[t,n]of Object.entries(r)){if(["authorization","cookie","host","proxy-authorization"].includes(t.toLowerCase()))throw yt("Connect returned unsafe object storage headers.");if(/\r|\n/.test(t)||/\r|\n/.test(n))throw yt("Connect returned invalid object storage headers.");e[t]=n}return e}function _O(r,e){return r.file_id===e.file_id&&r.path===e.path&&r.revision===e.revision&&r.content_digest===e.content_digest&&r.size===e.size&&r.media_type===e.media_type&&r.media_class===e.media_class&&r.modified_at===e.modified_at}function wO(r){let e=_p(r.body)&&_p(r.body.error)?r.body.error:{};return new K(typeof e.code=="string"?e.code:"authority_adoption_request_failed",typeof e.message=="string"?e.message:`Collection adoption request failed with status ${r.status}.`,r.status)}function yt(r){return new K("invalid_authority_adoption_response",r)}function Uc(r){if(r?.aborted)throw new K("authority_adoption_cancelled","Collection adoption was cancelled.")}function _p(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)}var wp=1500,SO=250,$O=3e4,qw=1440*60*1e3,Fr=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,Bc=class{request;now;wait;constructor(e={}){this.request=e.request??EO,this.now=e.now??Date.now,this.wait=e.wait??PO}async begin(e,t={}){Jn(t.signal);let n=Ur(e.controlUrl),i=e.mirrorName.trim();if(!i||i.length>200)throw new Ye("invalid_mirror_name","Mirror name must contain between 1 and 200 characters.");if(!["read_only","read_write"].includes(e.mode))throw new Ye("invalid_mirror_mode","Mirror mode must be read-only or read-write.");if(e.collectionId!==void 0&&!Fr.test(e.collectionId))throw new Ye("invalid_collection_id","Collection ID must be a UUID.");let s;try{s=await this.request({url:`${n}/v1/mirror-pairing-requests`,method:"POST",headers:{"content-type":"application/json"},body:{mirror_name:i,mode:e.mode,...e.collectionId?{collection_id:e.collectionId}:{}},...t.signal?{signal:t.signal}:{}})}catch{throw Jn(t.signal),Lw()}let o=Dw(s,201);if(!Yi(o)||!Fr.test(St(o.pairing_id))||!_o(o.pairing_secret)||!CO(o.expires_in)||Number(o.expires_in)*1e3>qw)throw er("Connect returned an invalid mirror approval.");let a=Uw(n,St(o.verification_uri),St(o.pairing_id));return{controlUrl:n,pairingId:St(o.pairing_id),refreshCredential:St(o.pairing_secret),verificationUri:a,expiresAt:new Date(this.now()+Number(o.expires_in)*1e3).toISOString(),requested:{mirrorName:i,mode:e.mode,...e.collectionId?{collectionId:e.collectionId}:{}}}}async enroll(e,t){let n=await this.begin(e,t),{refreshCredential:i,...s}=n;return await t.onVerification(s),this.waitForApproval(n,t)}async waitForApproval(e,t={}){xO(e,this.now());let n=jw(t.pollIntervalMs),i=Date.parse(e.expiresAt),s=0;for(;this.now()=500){let a=Sp(o);if(await this.retry(i,o.retryAfterMs??n,t,s,{code:a.code,message:a.message})===null)break;continue}throw Sp(o)}throw new Ye("mirror_enrollment_expired","Mirror approval expired before it was completed.")}async renew(e,t={}){AO(e),Jn(t.signal);let n;try{n=await this.request({url:Nw(e.controlUrl,e.enrollmentId,"renew"),method:"POST",headers:{authorization:`Bearer ${e.refreshCredential}`},...t.signal?{signal:t.signal}:{}})}catch{throw Jn(t.signal),Lw()}let i={controlUrl:e.controlUrl,pairingId:e.enrollmentId,refreshCredential:e.refreshCredential,verificationUri:`${e.controlUrl}/mirror/${e.enrollmentId}`,expiresAt:new Date(this.now()+6e4).toISOString(),requested:{mirrorName:e.name,mode:e.mode,collectionId:e.collectionId}};return Ow(i,Dw(n,200),{replicaId:e.replicaId})}async retry(e,t,n,i,s){let o=e-this.now();if(o<=0)return null;let a=Math.min(jw(t),o),c=new Date(this.now()+a).toISOString();return n.onStatus?.({state:s?"retrying":"waiting_for_approval",attempt:i,expiresAt:new Date(e).toISOString(),retryAt:c,...s?{error:s}:{}}),await this.wait(a,n.signal),c}},Ye=class extends b{status;constructor(e,t,n){super(e,t),this.status=n,this.name="MirrorEnrollmentError"}};function Ur(r){let e;try{e=new URL(r)}catch{throw new Ye("invalid_connect_url","Connect URL must be an absolute HTTPS origin.")}if(e.pathname!=="/"||e.search||e.hash||e.username||e.password)throw new Ye("invalid_connect_url","Connect URL must be an origin without credentials, path, query, or fragment.");let t=["localhost","127.0.0.1","[::1]","::1"].includes(e.hostname);if(e.protocol!=="https:"&&!(e.protocol==="http:"&&t))throw new Ye("invalid_connect_url","Connect URL must use HTTPS outside loopback development.");return e.origin}async function EO(r){let e=await fetch(r.url,{method:r.method,headers:r.headers,...r.body===void 0?{}:{body:JSON.stringify(r.body)},...r.signal?{signal:r.signal}:{}}),t=await e.json().catch(()=>null),n=e.headers.get("retry-after");return{status:e.status,body:t,...n===null?{}:{retryAfterMs:kO(n)}}}function Ow(r,e,t){if(!Yi(e)||e.status!=="paired"||!Yi(e.replica))throw er("Connect returned an invalid mirror enrollment.");let n=e.replica,i=St(n.id),s=St(n.collection_id),o=St(n.name).trim(),a=n.mode,c=St(e.token),l=St(e.token_expires_at);if(!Fr.test(i)||!Fr.test(s)||!o||!["read_only","read_write"].includes(String(a))||!_o(c)||!Vw(l))throw er("Connect returned invalid mirror credentials.");if(a!==r.requested.mode)throw er("Connect returned a mirror with a different access mode.");if(r.requested.collectionId&&s!==r.requested.collectionId)throw er("Connect returned a different collection.");if(t.mirrorName!==void 0&&o!==t.mirrorName)throw er("Connect returned a mirror with a different name.");if(t.replicaId!==void 0&&i!==t.replicaId)throw er("Connect returned a different mirror replica.");let u;try{u=Fw(St(e.sync_url),s)}catch{throw er("Connect returned an invalid authority sync URL.")}return{controlUrl:Ur(r.controlUrl),syncUrl:u,collectionId:s,replicaId:i,mode:a,name:o,enrollmentId:r.pairingId,accessToken:c,refreshCredential:r.refreshCredential,accessTokenExpiresAt:l}}function xO(r,e){Ur(r.controlUrl);let t=Date.parse(r.expiresAt);if(!Fr.test(r.pairingId)||!_o(r.refreshCredential)||!Number.isFinite(t)||t-e>qw||!r.requested.mirrorName.trim()||r.requested.mirrorName.length>200||!["read_only","read_write"].includes(r.requested.mode)||r.requested.collectionId!==void 0&&!Fr.test(r.requested.collectionId))throw new Ye("invalid_mirror_enrollment_session","Mirror enrollment session is invalid.");Uw(r.controlUrl,r.verificationUri,r.pairingId)}function AO(r){if(Ur(r.controlUrl),Fw(r.syncUrl,r.collectionId),!Fr.test(r.collectionId)||!Fr.test(r.replicaId)||!Fr.test(r.enrollmentId)||!_o(r.accessToken)||!_o(r.refreshCredential)||!Vw(r.accessTokenExpiresAt)||!r.name.trim()||r.name.length>200||!["read_only","read_write"].includes(r.mode))throw new Ye("invalid_mirror_enrollment","Stored mirror enrollment is invalid.")}function Fw(r,e){let t=new URL(r),n=`/v1/authorities/${encodeURIComponent(e)}/sync`;if(!(t.protocol==="https:"||t.protocol==="http:"&&["localhost","127.0.0.1","[::1]","::1"].includes(t.hostname))||t.username||t.password||t.pathname.replace(/\/$/,"")!==n||t.search||t.hash)throw new Error("invalid sync URL");return`${t.origin}${n}`}function Uw(r,e,t){let n;try{n=new URL(e)}catch{throw er("Connect returned an invalid mirror verification URI.")}let i=new URL(`/mirror/${encodeURIComponent(t)}`,r);if(n.origin!==i.origin||n.pathname!==i.pathname||n.search||n.hash||n.username||n.password)throw er("Connect returned an untrusted mirror verification URI.");return n.href}function Nw(r,e,t){return`${Ur(r)}/v1/mirror-pairing-requests/${encodeURIComponent(e)}/${t}`}function Dw(r,e){if(r.status!==e)throw Sp(r);return r.body}function Sp(r){let e=Yi(r.body)&&Yi(r.body.error)?r.body.error:{};return new Ye(St(e.code)||"mirror_enrollment_request_failed",St(e.message)||`Mirror enrollment request failed with status ${r.status}.`,r.status)}function er(r){return new Ye("invalid_mirror_enrollment_response",r)}function Bw(r){return{code:"mirror_enrollment_unreachable",message:"Connect could not be reached for mirror enrollment."}}function Lw(){let r=Bw(void 0);return new Ye(r.code,r.message)}function jw(r=wp){return Number.isFinite(r)?Math.min($O,Math.max(SO,Math.round(r))):wp}function kO(r){let e=Number(r);if(Number.isFinite(e)&&e>=0)return e*1e3;let t=Date.parse(r);return Number.isFinite(t)?Math.max(0,t-Date.now()):wp}function PO(r,e){return Jn(e),new Promise((t,n)=>{let i=setTimeout(o,r),s=()=>{clearTimeout(i),e?.removeEventListener("abort",s),n(new Ye("mirror_enrollment_cancelled","Mirror enrollment was cancelled."))};function o(){e?.removeEventListener("abort",s),t()}e?.addEventListener("abort",s,{once:!0})})}function Jn(r){if(r?.aborted)throw new Ye("mirror_enrollment_cancelled","Mirror enrollment was cancelled.")}function Vw(r){let e=Date.parse(r);return Number.isFinite(e)}function CO(r){return typeof r=="number"&&Number.isSafeInteger(r)&&r>0}function _o(r){return typeof r=="string"&&r.length>=16}function St(r){return typeof r=="string"?r:""}function Yi(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)}var MO=/(?:^|\/)(?:\.{1,2}|)(?:\/|$)/u,IO=/[\p{Cc}:?<>|*"]/u,TO=/[. ](?:\/|$)/u,RO=/(?:^|\/)(?:CON|PRN|AUX|NUL|COM[1-9¹²³]|LPT[1-9¹²³])(?:\.|\/|$)/iu;function tr(r){if(!r||r.startsWith("/")||r.includes("\\")||MO.test(r)||IO.test(r)||TO.test(r)||RO.test(r))throw new b("invalid_path",`Mirror received an unsafe path: ${r}.`)}function $t(r){return tr(r),Xi(r)}function Xi(r){return/^[\x20-\x7e]+$/u.test(r)?/[A-Z]/u.test(r)?r.toLowerCase():r:r.normalize("NFC").toLowerCase().normalize("NFC")}var Hw=new Set(["image","audio","video","pdf","other"]),zw=["image","audio","video","pdf","other"],OO=new Set([".mdbase",".git","node_modules","_contracts","_schemas","_types","_views"]),NO=/^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\.|$)/iu,DO=/^sha256:[0-9a-f]{64}$/u,LO=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu,jO=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/u;function Yn(r){let e={file_classes:[...r?.file_classes??[]],excluded_folders:[...r?.excluded_folders??[]]};if(e.excluded_folders.length>100)throw new b("invalid_file_materialization","Selective sync supports at most 100 excluded folders.");let t=new Set;for(let s of e.file_classes){if(!Hw.has(s)||t.has(s))throw new b("invalid_file_materialization","Selected file media classes must be valid and unique.");t.add(s)}let n=new Set,i=new Set;for(let s of e.excluded_folders){Ww(s,!0);let o=$t(s);if(n.has(s)||i.has(o))throw new b("invalid_file_materialization","Excluded folders must be unique on portable filesystems.");n.add(s),i.add(o)}return e.file_classes.sort((s,o)=>zw.indexOf(s)-zw.indexOf(o)),e.excluded_folders.sort((s,o)=>$t(s).localeCompare($t(o))),e}function Xn(r,e){let t=$t(e);return!r.excluded_folders.some(n=>{let i=$t(n);return t===i||t.startsWith(`${i}/`)})}function zc(r,e){return r.file_classes.includes(e.media_class)&&Xn(r,e.path)}function $p(r){let e=r.includes(".")?r.slice(r.lastIndexOf(".")+1).toLowerCase():"";return["avif","bmp","gif","jpeg","jpg","png","svg","webp"].includes(e)?"image":["flac","m4a","mp3","oga","ogg","opus","wav"].includes(e)?"audio":["3gp","mkv","mov","mp4","webm"].includes(e)?"video":e==="pdf"?"pdf":"other"}function Kw(r,e){return r.file_classes.includes($p(e))&&Xn(r,e)}function gt(r){if(Ww(r.path,!1),typeof r.file_id!="string"||!LO.test(r.file_id)||typeof r.revision!="string"||!r.revision||r.revision.length>255||!DO.test(r.content_digest)||!Number.isSafeInteger(r.size)||r.size<0||!Hw.has(r.media_class)||typeof r.modified_at!="string"||!jO.test(r.modified_at)||!Number.isFinite(Date.parse(r.modified_at))||r.media_type!==void 0&&(typeof r.media_type!="string"||!r.media_type||r.media_type.length>255))throw new b("invalid_snapshot",`Collection file ${r.path} has invalid metadata.`)}function Ww(r,e){if(tr(r),r.length>1024)throw new b("invalid_file_path","Collection file paths cannot exceed 1024 characters.");if(r.split("/").some(i=>i.startsWith(".")||/[<>"|?*]/u.test(i)||NO.test(i)||OO.has(i.toLowerCase()))||!e&&/\.md$/iu.test(r))throw new b("invalid_file_path",`Collection file path ${r} is hidden, reserved, or non-portable.`)}async function Gw(r,e,t){let n,i=new Set;do{let s=await r.fileSnapshot(e.snapshot_id,n);if(s.protocol_version!==1||s.type!=="file_snapshot_page"||s.snapshot_id!==e.snapshot_id||s.scope_epoch!==e.scope_epoch||s.cursor!==e.head)throw new b("invalid_snapshot","Authority file snapshot boundary changed during download.");for(let o of s.files)gt(o);if(await t(s.files),n=s.next_page,n!==void 0&&i.has(n))throw new b("invalid_snapshot","Authority file snapshot repeated a page cursor.");n!==void 0&&i.add(n)}while(n)}function Br(r,e){return r?.size===e.size&&r.content_digest===e.content_digest}async function*Vc(r,e){let t=Je.create(),n=0;for await(let i of r){if(!(i instanceof Uint8Array)||i.byteLength===0){if(i instanceof Uint8Array&&i.byteLength===0)continue;throw new b("file_integrity_failed","A file transport returned invalid bytes.")}if(n+=i.byteLength,!Number.isSafeInteger(n)||n>e.size)throw new b("file_integrity_failed",`Downloaded bytes for ${e.path} are oversized.`);t.update(i),yield i}if(n!==e.size||`sha256:${Oe(t.digest())}`!==e.content_digest)throw new b("file_integrity_failed",`Downloaded bytes for ${e.path} failed integrity verification.`)}async function*Jw(r,e,t){let n=Je.create(),i=0;for await(let s of r){if(!(s instanceof Uint8Array))throw new b("file_integrity_failed",`Local bytes for ${t} are invalid.`);if(s.byteLength!==0){if(i+=s.byteLength,!Number.isSafeInteger(i)||i>e.size)throw new b("pending_local_changed",`Local file ${t} changed while being staged.`);n.update(s),yield s}}if(i!==e.size||`sha256:${Oe(n.digest())}`!==e.content_digest)throw new b("pending_local_changed",`Local file ${t} changed while being staged.`)}async function Hc(r,e,t){if(gt(t),await e.has(t.content_digest))try{for await(let n of Vc(e.read(t.content_digest),t));return}catch{await e.remove(t.content_digest)}try{await e.write(t.content_digest,Vc(r.downloadFile(t),t))}catch(n){throw await e.remove(t.content_digest).catch(()=>{}),n}if(!await e.has(t.content_digest))throw new b("file_integrity_failed","The verified file blob was not persisted.")}var vn=new TextEncoder;function Ap(r){let e=Gn(r.collectionId,"Collection ID"),t=r.sourceHead??0;if(!Number.isSafeInteger(t)||t<0)throw new K("invalid_authority_snapshot","Source head must be a non-negative integer.");let n=new Set,i=r.resources.map(d=>{let f=Kc(d.path);if(n.add(f)||Ep(f),!["configuration","lock","contract","schema","type","view"].includes(d.kind))throw new K("invalid_authority_snapshot",`Unsupported collection resource kind for ${f}.`);return{path:f,kind:d.kind,revision:Yw(d.document),document:d.document}}).sort(FO);if(i[0]?.path!=="mdbase.yaml"||i[0].kind!=="configuration"||i.filter(({kind:d})=>d==="configuration").length!==1)throw new K("invalid_authority_snapshot","A portable snapshot requires one mdbase.yaml configuration resource.");let s=r.records.map(d=>{let f=Kc(d.path);return n.add(f)||Ep(f),{record_id:d.recordId?Gn(d.recordId,`Record ID for ${f}`):Wc(e,f),path:f,document:d.document}}).sort((d,f)=>xp(d.path,f.path)),o=new Set,a=(r.files??[]).map(d=>{let f=Kc(d.path);n.add(f)||Ep(f);let p=Gn(d.file_id,`File ID for ${f}`);if(!o.add(p))throw new K("invalid_authority_snapshot",`Collection snapshot contains file ID more than once: ${p}`);return qO(d,f),{...d,file_id:p,path:f}}).sort((d,f)=>xp(d.path,f.path)),c=Xw(i.flatMap(({path:d,revision:f})=>[d,f])),l=Xw([...i.flatMap(({path:d,revision:f})=>["resource",d,f]),...s.flatMap(d=>["record",d.path,Yw(d.document)]),...a.flatMap(d=>["file",d.path,d.file_id,d.revision,d.content_digest,String(d.size),d.media_type??"",d.media_class])]),u=jc([...i.map(({path:d,document:f})=>({kind:"resource",path:d,identity:"",document_hash:Oe(Je(vn.encode(f)))})),...s.map(d=>({kind:"record",path:d.path,identity:d.record_id,document_hash:Oe(Je(vn.encode(d.document)))})),...a.map(d=>({kind:"file",path:d.path,identity:d.file_id,document_hash:qc(d)}))]);return{protocol_version:1,collection_id:e,source_head:t,source_revision:l,manifest_digest:u,resources:{revision:c,spec_version:r.specVersion,types:r.types??[],contracts:r.contracts??[],documents:i},records:s,files:a}}function qO(r,e){try{gt({...r,path:e})}catch{throw new K("invalid_authority_snapshot",`File descriptor is invalid for ${e}.`)}if(r.media_class!==$p(e))throw new K("invalid_authority_snapshot",`File media class does not match its path for ${e}.`)}function Wc(r,e){let t=UO(Gn(r,"Collection ID")),i=Fc(new Uint8Array([...t,...vn.encode(Kc(e))])).slice(0,16);i[6]=i[6]&15|80,i[8]=i[8]&63|128;let s=Oe(i);return`${s.slice(0,8)}-${s.slice(8,12)}-${s.slice(12,16)}-${s.slice(16,20)}-${s.slice(20)}`}function Yw(r){return`sha256:${Oe(Je(vn.encode(r)))}`}function Xw(r){let e=Je.create();for(let t of r){let n=BigInt(vn.encode(t).length),i=new Uint8Array(8);new DataView(i.buffer).setBigUint64(0,n),e.update(i),e.update(vn.encode(t))}return`sha256:${Oe(e.digest())}`}function FO(r,e){return r.kind==="configuration"&&e.kind!=="configuration"?-1:e.kind==="configuration"&&r.kind!=="configuration"?1:xp(r.path,e.path)}function xp(r,e){let t=vn.encode(r),n=vn.encode(e),i=Math.min(t.length,n.length);for(let s=0;s!t||t==="."||t===".."))throw new K("invalid_authority_snapshot",`Collection path is unsafe: ${r}`);return e}function Ep(r){throw new K("invalid_authority_snapshot",`Collection snapshot contains the path more than once: ${r}`)}function UO(r){return Uint8Array.from(r.replaceAll("-","").match(/../g).map(e=>Number.parseInt(e,16)))}var BO=/^sha256:[a-f0-9]{64}$/,VO=/^[a-f0-9]{64}$/,Pp=1500,Cp=1440*60*1e3,zO=200,Qw=8*1024*1024,HO=new TextEncoder,Gc=class{request;now;wait;constructor(e={}){this.request=e.request??JO,this.now=e.now??Date.now,this.wait=e.wait??GO}async begin(e,t={}){Zn(t.signal);let n=Ur(e.controlUrl),i=Gn(e.collectionId,"Collection ID"),s=kp(e.displayName,"Collection name"),o=kp(e.sourceName,"Source name"),a=e.retainMirror??!0,c=a?kp(e.mirrorName??o,"Mirror name"):void 0,l;try{l=await this.request({url:`${n}/v1/authority-adoptions`,method:"POST",headers:{"content-type":"application/json"},body:{collection_id:i,display_name:s,source_name:o,retain_mirror:a,...c?{mirror_name:c}:{}},...t.signal?{signal:t.signal}:{}})}catch{throw Zn(t.signal),r0()}let u=WO(l,201);if(!Vr(u)||!Ji.test(nt(u.adoption_id))||!Mp(u.adoption_secret)||!ZO(u.expires_in)||Number(u.expires_in)*1e3>Cp)throw it("Connect returned an invalid collection adoption request.");let d=a0(n,nt(u.verification_uri),nt(u.adoption_id));return{controlUrl:n,adoptionId:nt(u.adoption_id),credential:nt(u.adoption_secret),verificationUri:d,expiresAt:new Date(this.now()+Number(u.expires_in)*1e3).toISOString(),requested:{collectionId:i,displayName:s,sourceName:o,retainMirror:a,...c?{mirrorName:c}:{}}}}async start(e,t){let n=await this.begin(e,t),{credential:i,...s}=n;return await t.onVerification(s),{session:n,prepared:await this.waitForApproval(n,t)}}async waitForApproval(e,t={}){Qi(e,this.now());let n=n0(t.pollIntervalMs),i=Date.parse(e.expiresAt),s=0;for(;this.now()=500){let a=Qn(o);if(!await this.retry(i,o.retryAfterMs??n,s,t,{code:a.code,message:a.message}))break;continue}throw Qn(o)}throw new K("authority_adoption_expired","Collection adoption approval expired before upload began.")}async exchange(e,t={}){Qi(e,this.now(),!0);let n=await this.controlRequest(e,"exchange","POST",{},t.signal);if(n.status===202)throw new K("authority_adoption_pending","Collection adoption is still awaiting approval.",202);if(n.status!==200)throw Qn(n);return e0(e,n.body)}async uploadSnapshot(e,t,n,i={}){Qi(e,this.now(),!0),s0(e,t),Zw(e,n);let s={protocol_version:1,collection_id:n.collection_id,source_head:n.source_head,source_revision:n.source_revision,manifest_digest:n.manifest_digest,resources:n.resources,record_count:n.records.length,file_count:n.files.length,files:n.files};await this.importRequest(t.import.manifest_url,"PUT",t.import.access_token,s,i.signal);let o=0,a=[],c=0;for(let l of n.records){let u=HO.encode(JSON.stringify(l)).length;if(u>Qw)throw new K("authority_adoption_record_too_large",`Record ${l.path} is too large to adopt.`);a.length>0&&(a.length===zO||c+u>Qw)&&(await this.uploadPage(t.import,o,a,i.signal),o+=1,a=[],c=0),a.push(l),c+=u}a.length>0&&await this.uploadPage(t.import,o,a,i.signal);for(let l of n.files){if(!i.fileSource)throw new K("authority_adoption_file_source_required",`File bytes are required to adopt ${l.path}.`);let u=await i.fileSource(l);await Rw(this.request,t.import,l,u,i)}await this.importRequest(t.import.finalize_url,"POST",t.import.access_token,void 0,i.signal)}async complete(e,t,n={}){Qi(e,this.now(),!0),Zw(e,t);let i;try{i=await this.controlRequest(e,"complete","POST",{manifest_digest:t.manifest_digest,source_revision:t.source_revision,source_head:t.source_head},n.signal)}catch(a){throw Zn(n.signal),new qr("Connect could not confirm whether hosted authority activated.",{cause:a})}if(i.status>=500)throw new qr("Connect could not confirm whether hosted authority activated.");if(i.status!==200)throw Qn(i);let s=i.body;if(!Vr(s)||s.status!=="completed")throw it("Connect returned an invalid adoption completion.");let o=o0(e,s.adoption);if(o.state!=="completed"||o.manifest_digest!==t.manifest_digest||o.source_revision!==t.source_revision||o.final_head!==t.source_head)throw it("Connect completed a different adoption snapshot.");return{status:"completed",adoption:o}}async cancel(e,t={}){Qi(e,this.now(),!0);let n=await this.controlRequest(e,void 0,"DELETE",void 0,t.signal);if(n.status!==200)throw Qn(n)}mirrorEnrollmentSession(e,t){if(Qi(e,this.now(),!0),t.status!=="completed"||t.adoption.collection_id!==e.requested.collectionId)throw it("Completed adoption does not belong to this session.");return e.requested.retainMirror?{controlUrl:e.controlUrl,pairingId:e.adoptionId,refreshCredential:e.credential,verificationUri:`${e.controlUrl}/mirror/${e.adoptionId}`,expiresAt:new Date(this.now()+Cp).toISOString(),requested:{mirrorName:e.requested.mirrorName??e.requested.sourceName,mode:"read_write",collectionId:e.requested.collectionId}}:null}async uploadPage(e,t,n,i){let s={protocol_version:1,page:t,records:n};await this.importRequest(e.records_url,"PUT",e.access_token,s,i)}async importRequest(e,t,n,i,s){let o;try{o=await this.request({url:e,method:t,headers:{authorization:`Bearer ${n}`,...i===void 0?{}:{"content-type":"application/json"}},...i===void 0?{}:{body:i},...s?{signal:s}:{}})}catch{throw Zn(s),r0()}if(o.status<200||o.status>=300)throw Qn(o)}controlRequest(e,t,n,i,s){return this.request({url:t0(e,t),method:n,headers:{authorization:`Bearer ${e.credential}`,...i===void 0?{}:{"content-type":"application/json"}},...i===void 0?{}:{body:i},...s?{signal:s}:{}})}async retry(e,t,n,i,s){let o=e-this.now();if(o<=0)return!1;let a=Math.min(n0(t),o),c=new Date(this.now()+a).toISOString();return i.onStatus?.({state:s?"retrying":"waiting_for_approval",attempt:n,expiresAt:new Date(e).toISOString(),retryAt:c,...s?{error:s}:{}}),await this.wait(a,i.signal),!0}};function Qi(r,e,t=!1){Ur(r.controlUrl);let n=Date.parse(r.expiresAt);if(!Ji.test(r.adoptionId)||!Mp(r.credential)||!Number.isFinite(n)||n-e>Cp||!t&&n<=e||!Ji.test(r.requested.collectionId)||!r.requested.displayName.trim()||!r.requested.sourceName.trim())throw new K("invalid_authority_adoption_session","Stored collection adoption state is invalid.");a0(r.controlUrl,r.verificationUri,r.adoptionId)}function s0(r,e){if(e.status!=="ready"||e.adoption.id!==r.adoptionId||e.adoption.collection_id!==r.requested.collectionId)throw it("Prepared adoption does not belong to this session.");let t=e.import;if(!Ji.test(t.import_id)||!Mp(t.access_token))throw it("Connect returned an invalid authority import capability.");let n=[[t.manifest_url,"manifest"],[t.records_url,"records"],[t.files_url,"files"],[t.finalize_url,"finalize"]],i;for(let[s,o]of n){let a=KO(s,t.import_id,o);if(!a||i!==void 0&&a.origin!==i)throw it("Connect returned an invalid authority import capability.");i=a.origin}}function Zw(r,e){if(e.protocol_version!==1||e.collection_id!==r.requested.collectionId||!Number.isSafeInteger(e.source_head)||e.source_head<0||!BO.test(e.source_revision)||!VO.test(e.manifest_digest)||e.resources.documents?.length===0||!Array.isArray(e.files))throw new K("invalid_authority_snapshot","Authority snapshot does not belong to this adoption.")}function e0(r,e){if(!Vr(e)||!["ready","activating","completed"].includes(nt(e.status)))throw it("Connect returned an invalid adoption exchange.");let t=o0(r,e.adoption);if(e.status==="completed")return{status:"completed",adoption:t};if(e.status==="activating")return{status:"activating",adoption:t};if(!Vr(e.import)||!Vr(e.staged))throw it("Connect omitted the authority import capability.");let n=e.import,i=e.staged,s={status:"ready",adoption:t,import:{import_id:nt(n.import_id),manifest_url:nt(n.manifest_url),records_url:nt(n.records_url),files_url:nt(n.files_url),finalize_url:nt(n.finalize_url),access_token:nt(n.access_token)},staged:{state:i.state,manifest_digest:i0(i.manifest_digest),source_revision:i0(i.source_revision),source_head:QO(i.source_head)}};if(s0(r,s),!["receiving","uploaded"].includes(s.staged.state))throw it("Connect returned an invalid staged import state.");return s}function o0(r,e){if(!Vr(e))throw it("Connect omitted collection adoption state.");let t=e;if(t.id!==r.adoptionId||t.collection_id!==r.requested.collectionId||!["requested","approved","prepared","activating","completed","cancelled","expired"].includes(t.state)||!Number.isSafeInteger(t.authority_epoch)||t.authority_epoch<2||!XO(t.expires_at))throw it("Connect returned invalid collection adoption state.");return t}function KO(r,e,t){try{let n=new URL(r);return(n.protocol==="https:"||n.protocol==="http:"&&["localhost","127.0.0.1","[::1]","::1"].includes(n.hostname))&&!n.username&&!n.password&&!n.search&&!n.hash&&n.pathname===`/v1/authority-imports/${e}/${t}`?n:null}catch{return null}}function a0(r,e,t){let n;try{n=new URL(e)}catch{throw it("Connect returned an invalid adoption verification URI.")}let i=new URL(`/adopt/${encodeURIComponent(t)}`,r);if(n.origin!==i.origin||n.pathname!==i.pathname||n.search||n.hash||n.username||n.password)throw it("Connect returned an untrusted adoption verification URI.");return n.href}function t0(r,e){let t=e?`/${e}`:"";return`${Ur(r.controlUrl)}/v1/authority-adoptions/${encodeURIComponent(r.adoptionId)}${t}`}function kp(r,e){let t=r.trim();if(!t||t.length>200)throw new K("invalid_authority_adoption",`${e} must contain between 1 and 200 characters.`);return t}function WO(r,e){if(r.status!==e)throw Qn(r);return r.body}function Qn(r){let e=Vr(r.body)&&Vr(r.body.error)?r.body.error:{};return new K(nt(e.code)||"authority_adoption_request_failed",nt(e.message)||`Collection adoption request failed with status ${r.status}.`,r.status)}function it(r){return new K("invalid_authority_adoption_response",r)}function c0(r){return{code:"authority_adoption_unreachable",message:"Connect could not be reached for collection adoption."}}function r0(){let r=c0(void 0);return new K(r.code,r.message)}function n0(r=Pp){return Number.isFinite(r)?Math.min(3e4,Math.max(250,Math.round(r))):Pp}function GO(r,e){return Zn(e),new Promise((t,n)=>{let i=setTimeout(o,r),s=()=>{clearTimeout(i),e?.removeEventListener("abort",s),n(new K("authority_adoption_cancelled","Collection adoption was cancelled."))};function o(){e?.removeEventListener("abort",s),t()}e?.addEventListener("abort",s,{once:!0})})}function Zn(r){if(r?.aborted)throw new K("authority_adoption_cancelled","Collection adoption was cancelled.")}async function JO(r){let e=await fetch(r.url,{method:r.method,headers:r.headers,redirect:"error",...r.body===void 0?{}:{body:r.rawBody?r.body:JSON.stringify(r.body)},...r.signal?{signal:r.signal}:{}}),t=await e.json().catch(()=>({})),n=e.headers.get("retry-after");return{status:e.status,body:t,headers:Object.fromEntries(e.headers.entries()),...n?{retryAfterMs:YO(n)}:{}}}function YO(r){let e=Number(r);if(Number.isFinite(e)&&e>=0)return e*1e3;let t=Date.parse(r);return Number.isFinite(t)?Math.max(0,t-Date.now()):Pp}function XO(r){return Number.isFinite(Date.parse(r))}function i0(r){return r===null?null:nt(r)}function QO(r){return r===null?null:typeof r=="number"&&Number.isSafeInteger(r)&&r>=0?r:Number.NaN}function ZO(r){return typeof r=="number"&&Number.isSafeInteger(r)&&r>0}function Mp(r){return typeof r=="string"&&r.length>=16}function nt(r){return typeof r=="string"?r:""}function Vr(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)}var eN=new TextEncoder,So=Object.freeze({digest:r=>Oe(Je(eN.encode(r))),randomId:()=>{if(typeof globalThis.crypto?.randomUUID!="function")throw new b("mirror_random_unavailable","This runtime must provide crypto.randomUUID() or a MirrorRuntime adapter.");return globalThis.crypto.randomUUID()},now:()=>new Date().toISOString()});var wo=class{held=!1;async runExclusive(e){if(this.held)throw new b("mirror_folder_in_use","Another mdbase mirror process is already using this folder.");this.held=!0;try{return await e()}finally{this.held=!1}}};function l0(r,e,t){if(r.engine_version!==3)throw new b("mirror_state_upgrade_required","Rebuild this prerelease mirror with the plan-only exact-document sync engine.");if(r.protocol_version!==1||r.replica_id!==e)throw new Error;if(r.resources??={},r.generation??=0,r.files??={},r.selective_sync=Yn(r.selective_sync),r.planned_conflicts??={},r.local_bindings??={},r.mode??="read_only",r.mode!==t)throw new b("mirror_mode_mismatch",`Mirror metadata belongs to a ${r.mode.replace("_","-")} replica.`);let n=[];for(let[i,s]of Object.entries(r.records))if(n.push($t(s.path)),s.record&&(s.record.record_id!==i||s.record.path!==s.path))throw new Error;for(let[i,s]of Object.entries(r.resources)){if(tr(i),i!==s.path)throw new Error;n.push($t(s.path))}for(let[i,s]of Object.entries(r.files)){if(gt(s.file),s.file.file_id!==i)throw new Error;n.push($t(s.file.path))}n.sort();for(let i=1;ic.kind==="configuration"&&c.path==="mdbase.yaml");if(n.length!==1)throw new b("invalid_snapshot","Hosted snapshot requires exactly one canonical mdbase.yaml resource.");let i=f0(n[0].document,e),s=m0(n[0].document).settings??{},o=Zi(s.types_folder,"_types"),a=Zi(s.contracts_folder,"_contracts");for(let c of r){let l=c.path.split(".").at(-1),u=c.path.split("/"),d=u.some(p=>p.startsWith(".")),f=!1;if(c.kind==="configuration")f=c.path==="mdbase.yaml";else if(c.kind==="lock")f=c.path==="mdbase.lock.yaml"&&nN(c.document);else if(c.kind==="type")f=Ip(c.path,o)&&l==="md"&&d0(c.document,c.path)==="mdbase.type";else if(c.kind==="contract")f=Ip(c.path,a)&&l==="md"&&d0(c.document,c.path)==="mdbase.contract";else if(c.kind==="schema"){let p=rN(c.document);f=!d&&l==="json"&&u.some(m=>m==="schemas"||m==="_schemas")&&p!==null}else c.kind==="view"&&(f=!d&&l==="base");if(!f)throw new b("invalid_snapshot",`Hosted resource ${c.path} is not valid for kind ${c.kind}.`)}return i}function u0(r){return{reservedFolders:new Set(["_types","_contracts","_types/_migrations",".mdbase"]),resourcePaths:r}}function f0(r,e){let t=m0(r).settings??{},n=Zi(t.types_folder,"_types");return{reservedFolders:new Set([n,Zi(t.contracts_folder,"_contracts"),Zi(t.migrations_folder,`${n}/_migrations`),Zi(t.cache_folder,".mdbase",!0)]),resourcePaths:e}}async function p0(r,e){if(r.length===0)return u0(new Set);let t=await e();if(t===null)throw Re("Mirror collection configuration is missing.");return f0(t,new Set(r))}function _n(r,e){if(tr(r),/(?:^|\/)\./u.test(r)||e.resourcePaths.has(r)||tN(r,e.reservedFolders)||!r.endsWith(".md"))throw new b("invalid_record_path",`Mirror record path ${r} is outside the configured record namespace.`)}function tN(r,e){for(let t of e)if(Ip(r,t))return!0;return!1}function h0(r,e){return r.filter(t=>{try{return _n(t,e),!0}catch{return!1}})}function m0(r){let e;try{e=Hn(r)}catch{throw new b("invalid_snapshot","Hosted mdbase.yaml is not valid YAML.")}if(!e||typeof e!="object"||Array.isArray(e))throw new b("invalid_snapshot","Hosted mdbase.yaml requires an object document.");let t=e;if(typeof t.spec_version!="string")throw new b("invalid_snapshot","Hosted mdbase.yaml requires spec_version.");return t}function Zi(r,e,t=!1){let n=typeof r=="string"?r:e;if(tr(n),!t&&n.split("/").some(i=>i.startsWith(".")))throw new b("invalid_snapshot",`Collection control folder ${n} must not be hidden.`);return n}function Ip(r,e){return r===e||r.startsWith(`${e}/`)}function rN(r){try{let e=JSON.parse(r);return e!==null&&typeof e=="object"&&!Array.isArray(e)?e:null}catch{return null}}function nN(r){try{let e=Hn(r);return e!==null&&typeof e=="object"&&!Array.isArray(e)&&e.kind==="mdbase.type-pack-lock"&&e.lock_version===1&&Array.isArray(e.packs)}catch{return!1}}function d0(r,e){try{return Lc(r,e).frontmatter.kind}catch{return null}}function Et(r){return Xi(r)}function y0(r,e,t,n,i=[]){let s=Et(r);for(let o of t)if(Et(o)===s)throw new b("invalid_record_path",`Mirror record path ${r} aliases authority resource ${o} on a supported filesystem.`);for(let[o,a]of n)if((o!==e||a.path!==r)&&Et(a.path)===s)throw new b("invalid_record_path",`Mirror record paths ${a.path} and ${r} alias on a supported filesystem.`);for(let[,o]of i)if(Et(o.file.path)===s)throw new b("invalid_record_path",`Mirror record path ${r} aliases collection file ${o.file.path} on a supported filesystem.`)}function g0(r,e,t){let n=Et(r);for(let i of Object.values(t.resources??{}))if(Et(i.path)===n)throw new b("invalid_file_path",`Collection file ${r} aliases authority resource ${i.path}.`);for(let i of Object.values(t.records))if(Et(i.path)===n)throw new b("invalid_file_path",`Collection file ${r} aliases record ${i.path}.`);for(let[i,s]of Object.entries(t.files??{}))if((i!==e||s.file.path!==r)&&Et(s.file.path)===n)throw new b("invalid_file_path",`Collection files ${s.file.path} and ${r} alias on a supported filesystem.`)}function $o(r){let e=new Map;for(let t of r){let n=Et(t),i=e.get(n);if(i!==void 0&&i!==t)throw new b("invalid_record_path",`Mirror paths ${i} and ${t} alias on a supported filesystem.`);e.set(n,t)}}var Yc=class{pathPolicy;digest;recordIds=new Set;physicalPaths=new Set;constructor(e,t,n){this.pathPolicy=e,this.digest=n;for(let i of t)this.physicalPaths.add(Xi(i.path))}validate(e){let t=e.document,n=e;if(_n(n.path,this.pathPolicy),this.recordIds.has(n.record_id))throw new b("invalid_snapshot",`Hosted snapshot repeats record identity ${n.record_id}.`);this.recordIds.add(n.record_id);let i=Xi(n.path);if(this.physicalPaths.has(i))throw new b("invalid_snapshot",`Hosted record path ${n.path} aliases another snapshot path on a supported filesystem.`);this.physicalPaths.add(i);let s=this.digest(t);if(n.revision.length!==7+s.length||!n.revision.startsWith("sha256:")||!n.revision.endsWith(s))throw new b("invalid_snapshot",`Hosted record ${n.path} does not match its declared revision.`);if(Mw(t,n)!==!0){let a;try{a=Lc(t,n.path)}catch{throw new b("invalid_snapshot",`Hosted record ${n.path} is not valid Markdown.`)}if(!Tp(a.frontmatter,n.frontmatter)||!iN(a.body,n.body))throw new b("invalid_snapshot",`Hosted record ${n.path} does not match its declared document.`)}return{record:n,document:t,hash:s}}};async function b0(r,e,t){let n,i=new Set;do{let s=await r.snapshot(e.snapshot_id,n);if(s.protocol_version!==1||s.snapshot_id!==e.snapshot_id||s.scope_epoch!==e.scope_epoch||s.cursor!==e.head)throw new b("invalid_snapshot","Authority snapshot boundary changed during download.");if(await t(s.records),n=s.next_page,n!==void 0&&i.has(n))throw new b("invalid_snapshot","Authority snapshot repeated a page cursor.");n!==void 0&&i.add(n)}while(n)}function iN(r,e){return r===e||r.startsWith(` `)&&r.slice(1)===e||e.startsWith(` -`)&&e.slice(1)===r}function Tp(r,e){if(r===e)return!0;if(Array.isArray(r)||Array.isArray(e))return Array.isArray(r)&&Array.isArray(e)&&r.length===e.length&&r.every((i,s)=>Tp(i,e[s]));if(!r||!e||typeof r!="object"||typeof e!="object")return!1;let t=Object.entries(r),n=e;return t.length===Object.keys(n).length&&t.every(([i,s])=>Object.prototype.hasOwnProperty.call(n,i)&&Tp(s,n[i]))}var es=class{fileSystem;runtime;mode;blobStore;constructor(e,t,n,i){this.fileSystem=e,this.runtime=t,this.mode=n,this.blobStore=i}async recordPathPolicy(e){return p0(Object.keys(e.resources??{}),()=>this.fileSystem.read("mdbase.yaml"))}async put(e,t,n={}){let{managedState:i=e,acceptedHash:s,materialized:o,inspectionPreflighted:a}=n;a||_n(t.path,await this.recordPathPolicy(e)),o===void 0&&!a&&y0(t.path,t.record_id,Object.keys(e.resources??{}),Object.entries(e.records),Object.entries(e.files??{}));let c=o?.document??Dc(t),l=a===1?null:await this.fileSystem.read(t.path),u=i?.records[t.record_id],d=l===null?null:this.runtime.digest(l);if(d!==null&&l!==c&&!(u!==void 0&&u.path===t.path&&d===u.hash)&&(!s||d!==s))throw new bt(t.record_id,t.path);u&&u.path!==t.path&&await this.remove(i,t.record_id,u.path);let f=this.runtime.digest(c);s===f&&d===f||await this.fileSystem.write(t.path,c),e.records[t.record_id]={path:t.path,revision:t.revision,hash:o?.hash??f,...this.mode==="read_write"?{record:t}:{}}}async putFile(e,t,n=e,i){if(gt(t),!this.blobStore)throw new b("file_storage_unavailable","Selected collection files require a content-addressed blob store adapter.");g0(t.path,t.file_id,e),e.files??={};let s=n.files?.[t.file_id],o=await this.fileSystem.inspectBinary(t.path),a=Et(t.path),c=Object.values(n.files??{}).find(m=>Et(m.file.path)===a),l=[...Object.values(n.resources??{}),...Object.values(n.records)].find(m=>Et(m.path)===a),u=l?await this.fileSystem.read(t.path):null,d=s?.file.path===t.path&&Br(o,s.file),f=c!==void 0&&Br(o,c.file),p=l!==void 0&&u!==null&&this.runtime.digest(u)===l.hash;if(o!==null&&!Br(o,t)&&!d&&!f&&!p&&!(i!==void 0&&o?.size===i.size&&o.content_digest===i.content_digest))throw new bt(t.file_id,t.path);if(s&&s.file.path!==t.path){let m=await this.fileSystem.inspectBinary(s.file.path);if(m!==null&&!Br(m,s.file))throw new bt(t.file_id,s.file.path)}Br(o,t)||await this.fileSystem.writeBinary(t.path,Vc(this.blobStore.read(t.content_digest),t)),s&&s.file.path!==t.path&&await this.fileSystem.remove(s.file.path),e.files[t.file_id]={file:t}}async removeFile(e,t){let n=e.files?.[t];if(!n)return;let i=await this.fileSystem.inspectBinary(n.file.path);if(i!==null&&!Br(i,n.file))throw new bt(t,n.file.path);i!==null&&await this.fileSystem.remove(n.file.path),delete e.files[t]}async remove(e,t,n,i={}){let s=e.records[t],o=s?.path??n;i.inspectionPreflighted||_n(o,await this.recordPathPolicy(e));let a=await this.fileSystem.read(o);if(a!==null&&s&&this.runtime.digest(a)!==s.hash)throw new bt(t,s.path);a!==null&&await this.fileSystem.remove(o),delete e.records[t]}async putResource(e,t,n){let i=await this.fileSystem.read(t.path),s=n?.resources?.[t.path];if(i!==null&&i!==t.document&&(!s||this.runtime.digest(i)!==s.hash))throw new bt(`resource:${t.path}`,t.path);await this.fileSystem.write(t.path,t.document),e.resources??={},e.resources[t.path]={path:t.path,revision:t.revision,hash:this.runtime.digest(t.document)}}async removeResource(e,t,n){let i=await this.fileSystem.read(t);if(i!==null&&this.runtime.digest(i)!==n.hash)throw new bt(`resource:${t}`,t);i!==null&&await this.fileSystem.remove(t),e.resources&&delete e.resources[t]}};async function v0(r,e,t,n){for(let i in r.records){if(!Object.hasOwn(r.records,i))continue;let s=r.records[i];_n(s.path,e);let o=await t.read(s.path);if(o===null||n(o)!==s.hash)throw new bt(i,s.path)}for(let[i,s]of Object.entries(r.resources??{})){let o=await t.read(s.path);if(o===null||n(o)!==s.hash)throw new bt(`resource:${i}`,s.path)}for(let[i,s]of Object.entries(r.files??{})){let o=await t.inspectBinary(s.file.path);if(!Br(o,s.file))throw new bt(i,s.file.path)}}async function _0(r){let{state:e,selectiveSync:t,fileSystem:n,pathPolicy:i,digest:s}=r;if(t.excluded_folders.length>0||t.file_classes.length!==5)throw new b("promotion_incomplete_file_projection","Moving the source of truth requires every collection file class with no excluded folders.");if(Object.keys(e.planned_conflicts??{}).length>0||e.batch!==void 0)throw new b("promotion_not_converged","Upload or resolve every local change before moving the source of truth.");await v0(e,i,n,s);let o=new Set(Object.keys(e.resources??{})),a=new Set(Object.values(e.records).map(d=>d.path)),c=(await n.listMarkdown(o)).filter(d=>!a.has(d));if(c.length>0)throw new b("promotion_unmanaged_files",`Synchronize unmanaged Markdown before promotion: ${c.join(", ")}.`);if(!n.listBinary)throw new b("promotion_file_scan_unavailable","Moving the source of truth requires binary file enumeration.");let l=new Set(Object.values(e.files??{}).map(d=>d.file.path)),u=(await n.listBinary(new Set([...o,...a]))).filter(d=>!l.has(d));if(u.length>0)throw new b("promotion_unmanaged_files",`Synchronize unmanaged files before moving the source of truth: ${u.join(", ")}.`);return{cursor:e.cursor,digest:jc([...Object.entries(e.resources??{}).map(([d,f])=>({kind:"resource",path:d,identity:"",document_hash:gp(f.hash)})),...Object.entries(e.records).map(([d,f])=>({kind:"record",path:f.path,identity:d,document_hash:gp(f.hash)})),...Object.values(e.files??{}).map(({file:d})=>({kind:"file",path:d.path,identity:d.file_id,document_hash:qc(d)}))])}}function wr(r,e,t,n,i){return{status:r,plan_fingerprint:e.fingerprint,applied:n,pending:t.pending,checkpoint_cursor:t.cursor,conflicts:t.conflicts.length,issues:e.issues,...i?{failure:i}:{}}}function w0(r,e){if(["planned","applying","cancelled","stale","blocked","failed"].includes(r.state))return r;let t=[...r.local_issues];for(let n of e.issues)n.path&&(n.code==="invalid_frontmatter"||n.code==="file_read_failed")&&!t.some(({path:i,code:s,message:o})=>i===n.path&&s===n.code&&o===n.message)&&t.push({path:n.path,code:n.code,message:n.message});return r.conflicts.length>0||t.length>0||e.summary.blocking_issues>0||e.summary.conflicts>0?{...r,state:"attention",local_issues:t}:{...r,state:e.actions.some(n=>n.command!=="advance_checkpoint")?"changes_waiting":"up_to_date"}}function rr(r,e){if(!r)return{state:"not_initialized",mode:e,pending:0,pending_files:0,conflicts:[],local_issues:[],cursor:null,last_synced_at:null};let t=[];for(let[o,a]of Object.entries(r.planned_conflicts??{})){let c=t.findIndex(({entity:u,object_id:d})=>u===a.entity&&d===o);c!==-1&&t.splice(c,1);let l=a.local.state==="exact"?a.local.object.path:a.remote.state==="exact"?a.remote.object.path:null;t.push({entity:a.entity,object_id:o,decision_id:a.decision_id??"",path:l,kind:a.conflict_kind==="rejected"?"rejected":"conflicted",message:a.conflict_kind==="rejected"?"The authority rejected this local change.":"Local and authority changes need a decision."})}let n=[],i=r.batch?r.batch.plan.actions.slice(r.batch.next_action).filter(o=>o.command!=="advance_checkpoint").length:0;return{state:(r.batch?r.batch.phase==="prepared"?"planned":r.batch.phase==="applying"||r.batch.phase==="effects_complete"?"applying":r.batch.phase==="cancelled"?"cancelled":r.batch.failure?.code==="sync_plan_stale"?"stale":"blocked":null)??(t.length?"attention":"up_to_date"),mode:e,pending:i,pending_files:r.batch?r.batch.plan.actions.slice(r.batch.next_action).filter(o=>o.command!=="advance_checkpoint"&&("target"in o&&o.target.entity==="file"||"source"in o&&o.source.entity==="file"||"entity"in o&&o.entity==="file")).length:0,conflicts:t,local_issues:n,cursor:r.batch?.checkpoint_before.cursor??r.cursor,last_synced_at:r.last_synced_at??null,generation:r.generation??0,pending_checkpoint:r.batch?.checkpoint_after.cursor??null,...r.batch?{plan_fingerprint:r.batch.plan.fingerprint}:{},...r.last_completed_plan?{last_completed_plan:r.last_completed_plan}:{},recovery_required:r.batch!==void 0,...r.batch?.failure?{failure:r.batch.failure}:{}}}async function sN(r,e,t){let n=await e.openSession();if(n.protocol_version!==1||n.protocol_profile!=="exact_document_v1"||n.replica_id!==r||n.mode!==t)throw new b("sync_protocol_incompatible",`Filesystem mirror requires exact-document v1 and its own ${t.replace("_","-")} replica.`);return n}async function Xc(r,e,t,n,i){let s=await sN(r,e,t),o=s.resources.documents??[],a=Jc(o),c=new Yc(a,o,i.digest),l=[];await b0(e,s,async f=>{for(let p of f){let m=c.validate(p);Xn(n,m.record.path)&&l.push(m)}});let u=[],d=new Set;return await Gw(e,s,async f=>{for(let p of f)if(zc(n,p)){if(d.has(p.file_id))throw new b("invalid_snapshot",`Hosted snapshot repeats file identity ${p.file_id}.`);d.add(p.file_id),u.push(p)}}),$o([...o.map(f=>f.path),...l.map(({record:f})=>f.path),...u.map(f=>f.path)]),{session:s,resources:o,records:l,files:u}}var Qc="exact_document_plan_only_v1",Zc="three_way_exact_document_v1",el="portable_mirror_projection_v1";function tl(r){return JSON.stringify(Rp(r))}function ei(r,e){return`sha256:${e(tl(r))}`}function Rp(r){if(r===null||typeof r=="string"||typeof r=="boolean")return r;if(typeof r=="number"){if(!Number.isSafeInteger(r))throw new b("invalid_sync_plan","Sync plans contain only safe integer numeric values.");return r}if(Array.isArray(r))return r.map(Rp);if(typeof r=="object"){let e={};for(let t of Object.keys(r).sort()){let n=r[t];if(n===void 0)throw new b("invalid_sync_plan",`Sync plan field ${t} is undefined rather than an explicit state.`);e[t]=Rp(n)}return e}throw new b("invalid_sync_plan","Sync plans contain only canonical I-JSON values.")}function A0(r,e,t){let n=new Map(r.base.map(u=>[u.identity,u])),i=new Map(r.remote.map(u=>[u.identity,u])),s=new Map,o=new Map(r.local.map(u=>[u.object.path,u]));for(let u of r.local)!u.stable_identity||u.object.identity===""||(s.set(u.object.identity,u.object),o.delete(u.object.path));for(let[u,d]of n){let f=o.get(d.path);f&&(s.set(u,{...f.object,identity:u}),o.delete(d.path))}for(let[u,d]of i){if(s.has(u))continue;let f=o.get(d.path);f&&f.object.entity===d.entity&&(s.set(u,{...f.object,identity:u}),o.delete(d.path))}for(let[u,d]of n){if(s.has(u))continue;let f=[...o.values()].filter(m=>m.object.entity===d.entity&&m.object.payload_revision===d.payload_revision);if(f.length!==1)continue;let p=f[0];s.set(u,{...p.object,identity:u,revision:p.object.payload_revision===d.payload_revision?d.revision:p.object.revision}),o.delete(p.object.path)}for(let u of o.values()){let d=u.stable_identity?u.object.identity:mN(e,u.object,t);s.set(d,{...u.object,identity:d})}let a=new Set([...n.keys(),...s.keys(),...i.keys()]),c=new Map([...s.values()].map(u=>[u.path,u])),l=new Map([...i.values()].map(u=>[u.path,u]));return[...a].sort().map(u=>{let d=n.get(u),f=s.get(u),p=i.get(u),m=p?.path??f?.path??d.path,h=f?.path??p?.path??d.path;return{entity:(f??p??d).entity,identity:u,base:Eo(d),local:Eo(f),remote:Eo(p),local_target_owner:Eo(c.get(m)),remote_target_owner:Eo(l.get(h))}})}function k0(r,e){let t=r.issues.some(d=>d.blocking),n=[],i=t&&r.mode==="read_only"&&r.issues.every(d=>!d.blocking||d.code==="invalid_frontmatter");if(!t||i){for(let d of[...r.objects].sort(gN)){let{local:f,remote:p}=d,m=i&&f.state==="exact"&&r.issues.some(h=>h.blocking&&h.path===f.object.path)&&p.state==="exact"&&p.object.path===f.object.path;(!t||m)&&fN(r,d,n,m)}i&&n.length!==r.issues.filter(d=>d.blocking).length&&(n=[]),n=cN(n,r.objects,e),n=oN(n,r.objects)}if(n.length>0||!t&&(r.kind!=="incremental"||r.boundary.checkpoint.cursor!==r.boundary.authority_cursor)){let d=n.map(f=>f.key);n.push({key:"checkpoint",depends_on_keys:d,command:"advance_checkpoint",reason:r.kind==="incremental"?"remote_change":r.kind,expected:r.boundary.checkpoint,next:t?r.boundary.checkpoint:{generation:r.boundary.checkpoint.generation+1,cursor:r.boundary.authority_cursor}})}let o=new Map;for(let d of n){let{key:f,depends_on_keys:p,...m}=d;o.set(f,ei({action_scope:{replica_id:r.boundary.replica_id,scope_epoch:r.boundary.scope_epoch,generation:r.boundary.checkpoint.generation},key:f,...m},e))}let a=n.map(d=>{let{key:f,depends_on_keys:p,...m}=d,h={...m,action_id:o.get(f),depends_on:p.map(y=>o.get(y))};if((h.command==="put_remote"||h.command==="move_remote"||h.command==="delete_remote")&&(h.idempotency_key=h.action_id),h.command==="move_remote"){let y=p.find(g=>g===`${h.source.identity}:put-remote`);y&&(h.revision_from_dependency=o.get(y))}return h}),c=[...r.issues].sort(bN),l={uploads:a.filter(_N).length,downloads:a.filter(wN).length,conflicts:a.filter(d=>d.command==="record_conflict").length,blocking_issues:c.filter(d=>d.blocking).length},u={plan_version:1,engine_profile:Qc,protocol_profile:"exact_document_v1",planner_policy:Zc,projection_policy:el,replica_id:r.boundary.replica_id,mode:r.mode,kind:r.kind,base_cursor:r.boundary.checkpoint.cursor,authority_cursor:r.boundary.authority_cursor,scope_epoch:r.boundary.scope_epoch,checkpoint_generation:r.boundary.checkpoint.generation,selective_sync:r.selective_sync,actions:a,issues:c,summary:l};return{...u,fingerprint:ei(u,e)}}function oN(r,e){if(!r.some(c=>c.command==="move_remote"))return r;let t=[...r],n=new Set;for(let c of aN(t))for(let l of c){let u=t.find(d=>d.key===l);u?.command==="move_remote"&&n.add(u.source.identity)}let i=Dp(t),s=new Map(t.map(c=>[c.key,c])),o=!0;for(;o;){o=!1;for(let c of t){if(c.command!=="move_remote")continue;let l=c.expected_target_owner;if(l.state!=="exact"||l.object.identity===c.source.identity)continue;let u=s.get(i.get(Sr(l.object))??""),d=u&&S0(u);(!u||d&&n.has(d))&&!n.has(c.source.identity)&&(n.add(c.source.identity),o=!0)}}if(n.size>0){t=t.filter(l=>{let u=S0(l);return!u||!n.has(u)||l.command!=="put_remote"&&l.command!=="move_remote"});let c=new Map(e.map(l=>[l.identity,l]));for(let l of[...n].sort()){let u=c.get(l);!u||u.entity==="resource"||t.push({key:`${l}:conflict`,depends_on_keys:[],command:"record_conflict",reason:"local_change",identity:l,entity:u.entity,local:u.local,remote:u.remote,conflict_kind:"path_occupied"})}}let a=Dp(t);for(let c of t){if(c.command!=="move_remote")continue;let l=c.expected_target_owner;if(l.state!=="exact"||l.object.identity===c.source.identity)continue;let u=a.get(Sr(l.object));!u||u===c.key||(c.depends_on_keys.includes(u)||c.depends_on_keys.push(u),c.expected_target_owner={state:"absent"})}return P0(t)}function aN(r){let e=r.filter(s=>s.command==="move_remote"),t=new Map(e.map(s=>[s.key,s])),n=Dp(r),i=new Map;for(let s of e){let o=[],a=new Map,c=s;for(;c;){let l=a.get(c.key);if(l!==void 0){let d=o.slice(l);i.set([...d].sort().join("\0"),d);break}a.set(c.key,o.length),o.push(c.key);let u=c.expected_target_owner;c=u.state==="exact"?t.get(n.get(Sr(u.object))??""):void 0}}return[...i.values()]}function Dp(r){let e=new Map;for(let t of r)t.command==="move_remote"?e.set(Sr(t.source),t.key):t.command==="delete_remote"&&e.set(Sr(t.target),t.key);return e}function S0(r){if(r.command==="move_remote")return r.source.identity;if(r.command==="put_remote"||r.command==="delete_remote")return r.target.identity}function cN(r,e,t){if(!r.some(d=>d.command==="move_local"||d.command==="write_local"))return r;let n=[...r],i=new Set;for(let d of e)d.base.state==="exact"&&i.add(d.base.object.path),d.local.state==="exact"&&i.add(d.local.object.path),d.remote.state==="exact"&&i.add(d.remote.object.path);for(;;){let d=lN(n);if(!d)break;let f=[...d].sort()[0],p=n.findIndex(_=>_.key===f),m=n[p];if(!m||m.command!=="move_local")throw new Error("Planner invariant: local path cycle contains a non-move action.");let h=uN(m.source,m.target_path,i,t);i.add(h);let y={...m.source,path:h},g={key:`${m.source.identity}:stage-local`,depends_on_keys:[...m.depends_on_keys],command:"move_local",reason:m.reason,source:m.source,target_path:h,expected_source_owner:m.expected_source_owner,expected_target_owner:{state:"absent"}};m.source=y,m.expected_source_owner={state:"exact",object:y},m.depends_on_keys=[g.key],n.splice(p,0,g)}let s=Lp(n),o=new Map(e.map(d=>[d.identity,d])),a=new Set,c=new Map(n.map(d=>[d.key,d])),l=!0;for(;l;){l=!1;for(let d of n){let f=$0(d),p=rl(d);if(f?.state!=="exact"||f.object.identity===p)continue;let m=c.get(s.get(Sr(f.object))??""),h=m&&rl(m);(!m||h&&a.has(h))&&!a.has(p)&&(a.add(p),l=!0)}}if(a.size>0){n=n.filter(d=>!a.has(rl(d))||d.command!=="move_local"&&d.command!=="write_local");for(let d of[...a].sort()){let f=o.get(d);!f||f.entity==="resource"||n.push({key:`${d}:conflict`,depends_on_keys:[],command:"record_conflict",reason:"remote_change",identity:d,entity:f.entity,local:f.local,remote:f.remote,conflict_kind:"path_occupied"})}}let u=Lp(n);for(let d of n){let f=$0(d),p=rl(d);if(f?.state!=="exact"||f.object.identity===p)continue;let m=u.get(Sr(f.object));!m||m===d.key||(d.depends_on_keys.includes(m)||d.depends_on_keys.push(m),dN(d,{state:"absent"}))}return P0(n)}function lN(r){let e=r.filter(i=>i.command==="move_local"),t=new Map(e.map(i=>[i.key,i])),n=Lp(r);for(let i of e){let s=[],o=new Map,a=i;for(;a;){let c=o.get(a.key);if(c!==void 0)return s.slice(c);o.set(a.key,s.length),s.push(a.key);let l=a.expected_target_owner;a=l.state==="exact"?t.get(n.get(Sr(l.object))??""):void 0}}}function Lp(r){let e=new Map;for(let t of r)t.command==="move_local"?e.set(Sr(t.source),t.key):t.command==="delete_local"&&e.set(Sr(t.target),t.key);return e}function $0(r){if(r.command==="move_local")return r.expected_target_owner;if(r.command==="write_local")return r.expected_path_owner}function dN(r,e){r.command==="move_local"?r.expected_target_owner=e:r.command==="write_local"&&(r.expected_path_owner=e)}function rl(r){return r.command==="move_local"?r.source.identity:r.command==="write_local"||r.command==="delete_local"?r.target.identity:""}function Sr(r){return`${r.entity}\0${r.identity}\0${r.path}`}function uN(r,e,t,n){let i=r.path.lastIndexOf("/"),s=i<0?"":r.path.slice(0,i+1),o=i<0?r.path:r.path.slice(i+1),a=o.lastIndexOf("."),c=a>0?o.slice(a):"";for(let l=0;;l+=1){let u=n(`${r.entity}\0${r.identity}\0${r.path}\0${e}\0${l}`),d=`${s}.mdbase-sync-stage-${u.slice(0,16)}${c}`;if(!t.has(d))return d}}function P0(r){let e=[...r],t=new Set,n=[];for(;e.length>0;){let i=e.findIndex(o=>o.depends_on_keys.every(a=>t.has(a)));if(i<0)throw new Error("Planner invariant: action dependency graph contains a cycle.");let[s]=e.splice(i,1);n.push(s),t.add(s.key)}return n}function fN(r,e,t,n){if(e.frozen_conflict){t.push(yN(e.frozen_conflict.local,e.frozen_conflict.remote)?{key:`${e.identity}:clear-conflict`,depends_on_keys:[],command:"clear_conflict",reason:"pending",identity:e.identity,entity:e.entity,expected_local:e.frozen_conflict.local,expected_remote:e.frozen_conflict.remote}:{key:`${e.identity}:conflict`,depends_on_keys:[],command:"record_conflict",reason:"pending",identity:e.identity,entity:e.entity,...e.frozen_conflict});return}let i=!Np(e.local,e.base),s=!Np(e.remote,e.base);if(!(!i&&!s)){if(e.entity==="resource"){if(i)return;Op(e,t);return}if(r.mode==="read_only"){(!i||n)&&Op(e,t);return}if(i&&s){if(Np(e.local,e.remote))return;t.push(hN(e));return}i?pN(e,t):Op(e,t)}}function Op(r,e){if(r.remote.state==="absent"){r.local.state==="exact"&&e.push({key:`${r.identity}:delete-local`,depends_on_keys:[],command:"delete_local",reason:"remote_change",target:r.local.object,expected_local:r.local,expected_path_owner:r.local_target_owner});return}let t=r.remote.object;if(r.local.state==="absent"){e.push(E0(r,t,[]));return}let n=r.local.object,i;if(n.path!==t.path){let s=`${r.identity}:move-local`;e.push({key:s,depends_on_keys:[],command:"move_local",reason:"remote_change",source:n,target_path:t.path,expected_source_owner:r.local,expected_target_owner:r.local_target_owner}),i=s}n.revision!==t.revision&&e.push(E0(r,t,i?[i]:[]))}function E0(r,e,t){return{key:`${r.identity}:write-local`,depends_on_keys:t,command:"write_local",reason:"remote_change",target:e,payload_revision:e.payload_revision,expected_local:t.length>0?{state:"exact",object:{...e,revision:zr(r.local).revision,payload_revision:zr(r.local).payload_revision,...zr(r.local).size===void 0?{}:{size:zr(r.local).size}}}:r.local,expected_path_owner:t.length>0?{state:"exact",object:{...e,revision:zr(r.local).revision,payload_revision:zr(r.local).payload_revision,...zr(r.local).size===void 0?{}:{size:zr(r.local).size}}}:r.local_target_owner}}function pN(r,e){if(r.local.state==="absent"){r.remote.state==="exact"&&e.push({key:`${r.identity}:delete-remote`,depends_on_keys:[],command:"delete_remote",reason:"local_change",target:r.remote.object,expected_remote:r.remote,expected_local:r.local,idempotency_key:""});return}let t=r.local.object;if(r.remote.state==="absent"){e.push(x0(r,t,[]));return}let n=r.remote.object,i;if(t.revision!==n.revision){let s=x0(r,{...t,path:n.path},[]);e.push(s),i=s.key}t.path!==n.path&&e.push({key:`${r.identity}:move-remote`,depends_on_keys:i?[i]:[],command:"move_remote",reason:"local_change",source:i?{...n,revision:t.revision}:n,target_path:t.path,expected_source_owner:i?{state:"exact",object:{...n,revision:t.revision}}:r.remote,expected_target_owner:r.remote_target_owner,expected_local:r.local,idempotency_key:""})}function x0(r,e,t){return{key:`${r.identity}:put-remote`,depends_on_keys:t,command:"put_remote",reason:"local_change",target:e,payload_revision:zr(r.local).payload_revision,expected_remote:r.remote,expected_local:r.local,idempotency_key:""}}function hN(r){return{key:`${r.identity}:conflict`,depends_on_keys:[],command:"record_conflict",reason:"remote_change",identity:r.identity,entity:r.entity,local:r.local,remote:r.remote,conflict_kind:r.local.state==="absent"||r.remote.state==="absent"?"delete_vs_change":"both_changed"}}function zr(r){if(r.state!=="exact")throw new Error("Planner invariant: expected exact object state.");return r.object}function Eo(r){return r?{state:"exact",object:r}:{state:"absent"}}function mN(r,e,t){let n=t(`${r}\0${e.entity}\0${e.path}\0${e.revision}`);return`${n.slice(0,8)}-${n.slice(8,12)}-5${n.slice(13,16)}-8${n.slice(17,20)}-${n.slice(20,32)}`}function Np(r,e){return tl(r)===tl(e)}function yN(r,e){return r.state==="absent"||e.state==="absent"?r.state===e.state:r.object.entity===e.object.entity&&r.object.identity===e.object.identity&&r.object.path===e.object.path&&r.object.payload_revision===e.object.payload_revision&&r.object.size===e.object.size}function gN(r,e){return`${r.entity}\0${r.identity}`.localeCompare(`${e.entity}\0${e.identity}`)}function bN(r,e){return vN(`${r.path??""}\0${r.code}\0${r.message}`,`${e.path??""}\0${e.code}\0${e.message}`)}function vN(r,e){let t=new TextEncoder,n=t.encode(r),i=t.encode(e),s=Math.min(n.length,i.length);for(let o=0;o[c.record_id,c])),a=new Map(n.files.map(c=>[c.file_id,c]));return this.finish({kind:e,prior:t,authorityCursor:n.session.head,scopeEpoch:n.session.scope_epoch,local:s,remoteRecords:o,remoteResources:n.resources,remoteFiles:a,snapshot:n})}async inspectIncremental(e){let t=new Map;for(let[l,u]of Object.entries(e.records))u.record&&t.set(l,u.record);let n=new Map(Object.entries(e.files??{}).map(([l,u])=>[l,u.file])),i=new Map(C0(e).map(l=>[EN(l),l])),s=e.cursor,o=e.cursor;for(;;){let l=s,u=await this.transport.changes(l,200);if(u.scope_epoch!==e.scope_epoch||u.cursoru.head||u.has_more&&u.cursor===l)throw new b("invalid_change_page","Authority returned an invalid change boundary.");if(u.reset_required)return this.inspectSnapshot("rebuild",e);for(let d of u.events){if(d.sequence<=o||d.sequence>u.cursor)throw new b("invalid_change_page","Authority change events are not strictly ordered.");o=d.sequence,this.applyRemoteObservation(i,t,n,d)}if(s=u.cursor,!u.has_more)break}let a=Object.values(e.resources??{}).map(l=>({path:l.path,kind:AN(l.path),revision:l.revision,document:""})),c=await this.inspectLocal(e,a,await this.currentRecordPathPolicy(e));return this.mode==="read_only"&&c.issues.some(l=>l.code==="invalid_frontmatter")?this.inspectSnapshot("rebuild",e):this.finish({kind:"incremental",prior:e,authorityCursor:s,scopeEpoch:e.scope_epoch,local:c,remoteRecords:t,remoteResources:[],remoteFiles:n,remoteRefs:[...i.values()]})}applyRemoteObservation(e,t,n,i){if(i.type==="put"){Iw(i);let s=i.record.record_id;Xn(this.selectiveSync,i.record.path)?(e.set(`record:${s}`,M0(i.record)),t.set(s,i.record)):(e.delete(`record:${s}`),t.delete(s));return}if(i.type==="remove"){e.delete(`record:${i.record_id}`),t.delete(i.record_id);return}if(i.type==="file_put"){gt(i.file);let s=i.file.file_id;zc(this.selectiveSync,i.file)?(e.set(`file:${s}`,qp(i.file)),n.set(s,i.file)):(e.delete(`file:${s}`),n.delete(s));return}e.delete(`file:${i.file_id}`),n.delete(i.file_id)}async inspectLocal(e,t,n){let i=[],s=new Map,o=new Map,a=[],c=new Map(Object.entries(e?.records??{}).map(([m,h])=>[h.path,[m,h]])),l=new Map(Object.entries(e?.planned_conflicts??{}).filter(([,m])=>m.entity==="record"&&m.local.state==="exact").map(([m,h])=>[h.local.state==="exact"?h.local.object.path:"",m])),u=new Map(Object.entries(e?.local_bindings??{}).filter(([,m])=>m.entity==="record").map(([m,h])=>[h.path,m])),d=new Set([...Object.keys(e?.resources??{}),...t.map(m=>m.path)]);for(let m of d){let h=e?.resources?.[m],y;try{y=await this.fileSystem.read(m)}catch{a.push(T0(m)),h&&i.push({stable_identity:!0,object:wn("resource",m,m,h.revision)});continue}if(y===null)continue;let g=y,_=_r(g,this.runtime);i.push({stable_identity:!0,object:wn("resource",m,m,_)}),s.set(m,g);let v=t.find(w=>w.path===m);!h&&v&&v.revision!==_?a.push({code:"local_collision",message:`${m} differs locally from the exact authority document.`,path:m,blocking:!0}):h&&h.revision!==_&&a.push({code:"mirror_diverged",message:`Authority-owned resource ${m} changed locally.`,path:m,blocking:!0})}let f=new Set(Object.values(e?.records??{}).map(m=>m.path)),p=h0(await this.fileSystem.listMarkdown(d),n).filter(m=>Xn(this.selectiveSync,m)||f.has(m));for(let m of p){let h=l.get(m),y=u.get(m),g=c.get(m),_=g?.[0],v=h??y??_??"",w;try{w=await this.fileSystem.readText(m)}catch{w=null}if(typeof w!="string"){a.push(w===null?T0(m):{code:"invalid_frontmatter",message:`Invalid frontmatter (${w.code}).`,path:m,blocking:!0});let $=w?.revision??g?.[1].revision;$&&i.push({stable_identity:v!=="",object:wn("record",v,m,$)});continue}let S=w,x=_r(S,this.runtime);i.push({stable_identity:v!=="",object:wn("record",v,m,x)}),s.set(m,S);let C=Pw(S);C.outcome!=="parsed"&&a.push({code:"invalid_frontmatter",message:`Invalid frontmatter (${C.outcome}).`,path:m,blocking:!0})}if(this.selectiveSync.file_classes.length>0){if(!this.fileSystem.listBinary)throw new b("file_storage_unavailable","Selected files require binary enumeration.");let m=new Set(Object.values(e?.files??{}).map(v=>v.file.path)),h=new Map(Object.entries(e?.files??{}).map(([v,w])=>[w.file.path,[v,w]])),y=new Map(Object.entries(e?.planned_conflicts??{}).filter(([,v])=>v.entity==="file"&&v.local.state==="exact").map(([v,w])=>[w.local.state==="exact"?w.local.object.path:"",v])),g=new Map(Object.entries(e?.local_bindings??{}).filter(([,v])=>v.entity==="file").map(([v,w])=>[w.path,v])),_=(await this.fileSystem.listBinary(d)).filter(v=>Kw(this.selectiveSync,v)||m.has(v));for(let v of _){let w=await this.fileSystem.inspectBinary(v);if(!w)continue;let S=y.get(v),x=g.get(v),C=h.get(v),$=S??x??C?.[0]??"";i.push({stable_identity:$!=="",object:{entity:"file",identity:$,path:v,revision:C&&C[1].file.content_digest===w.content_digest?C[1].file.revision:w.content_digest,payload_revision:w.content_digest,size:w.size}}),o.set(v,w)}}try{$o([...d,...p,...o.keys()])}catch(m){let h=Kn(m);a.push({code:Wn(m,"sync_inspection_failed"),message:h.message,blocking:!0})}return{observations:i,documents:s,binary:o,issues:a}}async finish(e){let{prior:t,local:n}=e,i=e.remoteRefs??[...e.remoteResources.map($N),...[...e.remoteRecords.values()].map(M0),...[...e.remoteFiles.values()].map(qp)],s=A0({base:t?C0(t):[],local:n.observations,remote:i},`${this.replicaId}\0${e.scopeEpoch}\0${t?.generation??0}`,this.runtime.digest);for(let d of s){let f=t?.planned_conflicts?.[d.identity];!f||f.entity!==d.entity||(d.frozen_conflict={local:d.local,remote:d.remote,conflict_kind:f.conflict_kind})}let o=[...n.issues];for(let d of s)(e.kind==="initial"||e.kind==="rebuild")&&d.base.state==="absent"&&d.local.state==="exact"&&d.remote.state==="exact"&&!I0(d.local,d.remote)&&o.push({code:"local_collision",message:`${d.remote.object.path} differs locally from the exact authority object.`,path:d.remote.object.path,blocking:this.mode!=="read_write"||d.entity==="resource"}),d.remote.state==="exact"&&d.local_target_owner.state==="exact"&&d.remote.object.identity!==d.local_target_owner.object.identity&&o.push({code:"local_collision",message:`${d.remote.object.path} is owned by different local bytes.`,path:d.remote.object.path,blocking:!0});try{$o([...i.map(d=>d.path),...n.observations.map(({object:d})=>d.path)])}catch(d){let f=Kn(d);o.push({code:Wn(d,"sync_inspection_failed"),message:f.message,blocking:!0})}if(this.mode==="read_only"){for(let d of s)if(d.entity!=="resource"&&!I0(d.local,d.base)){let f=xN(d.local,d.base);if(o.some(p=>p.blocking&&p.path===f))continue;o.push({code:"mirror_diverged",message:`${f} changed in a receive-only mirror.`,path:f,blocking:!0})}}let a=ei(Yn(this.selectiveSync),this.runtime.digest),c={boundary:{engine_profile:Qc,protocol_profile:"exact_document_v1",planner_policy:Zc,projection_policy:el,replica_id:this.replicaId,scope_epoch:e.scopeEpoch,authority_cursor:e.authorityCursor,checkpoint:{generation:t?.generation??0,cursor:t?.cursor??null},selective_sync_fingerprint:a},mode:this.mode,kind:e.kind,selective_sync:Yn(this.selectiveSync),objects:s,issues:SN(o)},l=k0(c,this.runtime.digest),u=await this.bindPayloads(l,n,e.remoteRecords,e.remoteResources,e.remoteFiles);return{summary:c,plan:l,durable_payloads:u,prior:t,snapshot:e.snapshot,remote_records:e.remoteRecords,remote_files:e.remoteFiles}}async bindPayloads(e,t,n,i,s){let o={documents:{},records:{},resources:{},files:{},local_files:{},mutations:{}},a=new Map(i.map(c=>[c.path,c]));for(let c of e.actions)if(c.command==="put_remote"){let l=c.expected_local.state==="exact"?c.expected_local.object.path:c.target.path;if(c.target.entity==="file"){let u=t.binary.get(l);if(!u)throw ti(c.action_id);await this.stageLocalBinary(l,u),o.local_files[c.action_id]={path:l,...u}}else{let u=t.documents.get(l);if(u===void 0||_r(u,this.runtime)!==c.payload_revision)throw ti(c.action_id);o.documents[c.action_id]=u}c.target.entity==="record"&&(o.mutations[c.action_id]={operation:"put",mutation_id:jp(c.action_id),replica_id:this.replicaId,scope_epoch:e.scope_epoch,record_id:c.target.identity,...c.expected_remote.state==="exact"?{base_revision:c.expected_remote.object.revision}:{},path:c.target.path,document:o.documents[c.action_id],created_at:this.runtime.now()})}else if(c.command==="write_local")if(c.target.entity==="record"){let l=n.get(c.target.identity);if(!l||l.revision!==c.target.revision)throw ti(c.action_id);o.records[c.action_id]=l}else if(c.target.entity==="resource"){let l=a.get(c.target.path);if(!l||l.revision!==c.target.revision)throw ti(c.action_id);o.resources[c.action_id]=l}else{let l=s.get(c.target.identity);if(!l||l.revision!==c.target.revision||!this.blobStore)throw ti(c.action_id);await Hc(this.transport,this.blobStore,l),o.files[c.action_id]=l}else if(c.command==="record_conflict"&&c.remote.state==="exact")if(c.entity==="record"){let l=n.get(c.identity);if(!l||l.revision!==c.remote.object.revision)throw ti(c.action_id);o.records[c.action_id]=l}else{let l=s.get(c.identity);if(!l||l.revision!==c.remote.object.revision)throw ti(c.action_id);o.files[c.action_id]=l}else c.command==="move_remote"&&c.source.entity==="record"?o.mutations[c.action_id]={operation:"move",mutation_id:jp(c.action_id),replica_id:this.replicaId,scope_epoch:e.scope_epoch,record_id:c.source.identity,base_revision:c.expected_source_owner.state==="exact"?c.expected_source_owner.object.revision:c.source.revision,path:c.target_path,created_at:this.runtime.now()}:c.command==="delete_remote"&&c.target.entity==="record"&&(o.mutations[c.action_id]={operation:"delete",mutation_id:jp(c.action_id),replica_id:this.replicaId,scope_epoch:e.scope_epoch,record_id:c.target.identity,base_revision:c.expected_remote.state==="exact"?c.expected_remote.object.revision:c.target.revision,created_at:this.runtime.now()});return o}async stageLocalBinary(e,t){if(!this.blobStore||!this.fileSystem.readBinary)throw new b("writable_file_storage_unavailable","Writable files require streaming filesystem and blob-store adapters.");if(await this.blobStore.has(t.content_digest))return;let n=await this.fileSystem.readBinary(e);if(!n)throw new b("sync_plan_stale",`${e} disappeared during inspection.`);await this.blobStore.write(t.content_digest,Jw(n,t,e))}};function SN(r){let e=new Map;for(let t of r)e.set(`${t.code}\0${t.path??""}\0${t.message}\0${t.blocking}`,t);return[...e.values()]}function C0(r){return[...Object.entries(r.resources??{}).map(([e,t])=>wn("resource",e,t.path,t.revision)),...Object.entries(r.records).map(([e,t])=>wn("record",e,t.path,t.revision)),...Object.entries(r.files??{}).map(([e,t])=>qp({...t.file,file_id:e}))]}function M0(r){return wn("record",r.record_id,r.path,r.revision)}function $N(r){return wn("resource",r.path,r.path,r.revision)}function wn(r,e,t,n){return{entity:r,identity:e,path:t,revision:n,payload_revision:n}}function qp(r){return{entity:"file",identity:r.file_id,path:r.path,revision:r.revision,payload_revision:r.content_digest,size:r.size}}function EN(r){return`${r.entity}:${r.identity}`}function I0(r,e){return JSON.stringify(r)===JSON.stringify(e)}function xN(r,e){return r.state==="exact"?r.object.path:e.state==="exact"?e.object.path:"mirror"}function ti(r){return new b("sync_payload_incomplete",`Inspected action ${r} has no revision-bound payload.`)}function T0(r){return{code:"file_read_failed",message:"Could not read local file.",path:r,blocking:!0}}function AN(r){return r==="mdbase.yaml"?"configuration":r.endsWith("lock.yaml")?"lock":r.startsWith("_types/")?"type":r.startsWith("_contracts/")?"contract":r.startsWith("_views/")?"view":"schema"}function jp(r){let e=r.replace(/^sha256:/u,"");return`${e.slice(0,8)}-${e.slice(8,12)}-4${e.slice(13,16)}-8${e.slice(17,20)}-${e.slice(20,32)}`}var xo=class{fileSystem;runtime;constructor(e,t){this.fileSystem=e,this.runtime=t}async validate(e,t){if((t?.generation??0)!==e.checkpoint_generation||(t?.cursor??null)!==e.base_cursor||(t?.scope_epoch??e.scope_epoch)!==e.scope_epoch)throw $r("The durable checkpoint changed after inspection.");for(let n of e.actions)n.depends_on.length===0&&await this.validateAction(n)}async validateAction(e){switch(e.command){case"write_local":await this.validateExpected(e.expected_local),await this.validateExpectedAt(e.target.path,e.expected_path_owner);return;case"delete_local":await this.validateExpected(e.expected_local),await this.validateExpectedAt(e.target.path,e.expected_path_owner);return;case"move_local":await this.validateExpectedAt(e.source.path,e.expected_source_owner),await this.validateExpectedAt(e.target_path,e.expected_target_owner);return;case"put_remote":case"move_remote":await this.validateExpected(e.expected_local);return;case"delete_remote":await this.validateExpectedAt(e.target.path,e.expected_local);return;case"record_conflict":await this.validateExpected(e.local);return;case"clear_conflict":await this.validateExpected(e.expected_local);return;case"advance_checkpoint":return}}async validateExpectedAt(e,t){if(t.state==="absent"){if(await this.fileSystem.exists(e))throw $r(`${e} is no longer vacant.`);return}if(t.object.path!==e||!await il(this.fileSystem,this.runtime,t.object,!0))throw $r(`${e} no longer has the inspected owner and bytes.`)}async validateExpected(e){if(e.state!=="absent"&&!await il(this.fileSystem,this.runtime,e.object,!0))throw $r(`${e.object.path} no longer matches the inspected bytes.`)}};async function il(r,e,t,n){if(t.entity==="file"){let s=await r.inspectBinary(t.path);return!!s&&s.content_digest===t.payload_revision&&(t.size===void 0||s.size===t.size)}let i;try{i=await r.readText(t.path)}catch(s){throw s instanceof b&&s.code==="file_read_failed"&&n?$r(s.message):s}return(typeof i=="string"?_r(i,e):i?.revision)===t.payload_revision}function $r(r){return new b("sync_plan_stale",r)}async function R0(r,e,t,n){if(r?.batch){if(r.batch.plan.fingerprint!==e.fingerprint)throw new b("mirror_recovery_required","A different prepared sync batch must recover before this plan can apply.");return r}let i=r?structuredClone(r):PN(e.replica_id,e.scope_epoch,e.mode,e.selective_sync);i.scope_epoch=e.scope_epoch,i.selective_sync=e.selective_sync;let s=e.actions.at(-1);if(s?.command!=="advance_checkpoint")throw new b("invalid_sync_plan","Prepared plan has no checkpoint action.");return i.batch={phase:"prepared",plan:e,next_action:0,receipts:[],payloads:t,checkpoint_before:{generation:e.checkpoint_generation,cursor:e.base_cursor},checkpoint_after:s.next},await n.write(i),i}async function O0(r,e){let t=st(r);t.phase!=="effects_complete"&&(t.phase="applying",delete t.failure,await ol(r,{type:"phase",plan_fingerprint:t.plan.fingerprint,phase:"applying"},e))}async function N0(r,e,t){let n=st(r),i=n.plan.actions[n.next_action];if(!i||i.action_id!==e.action_id)throw Re("The durable sync receipt does not match the next prepared action.");let s={type:"receipt",plan_fingerprint:n.plan.fingerprint,receipt:structuredClone(e),delta:kN(r,i)};n.receipts.push(structuredClone(e)),n.next_action+=1,await ol(r,s,t)}async function D0(r,e){let t=st(r),n=t.plan.actions[t.next_action];if(!n||n.command!=="advance_checkpoint")throw Re("Sync effects cannot complete before the checkpoint action is next.");t.phase="effects_complete",await ol(r,{type:"effects_complete",plan_fingerprint:t.plan.fingerprint},e)}async function sl(r,e,t,n){let i=st(r);i.phase=e,i.failure=t,await ol(r,{type:"phase",plan_fingerprint:i.plan.fingerprint,phase:e,failure:t},n)}async function ol(r,e,t){t.appendJournal?await t.appendJournal(e):await t.write(r)}function kN(r,e){let t="identity"in e?e.identity:"target"in e?e.target.identity:"source"in e?e.source.identity:null;if(t===null)return{};let n="entity"in e?e.entity:"target"in e?e.target.entity:"source"in e?e.source.entity:null;if(n===null)return{};let i={planned_conflicts:{[t]:Ao(r.planned_conflicts?.[t])},local_bindings:{[t]:Ao(r.local_bindings?.[t])}};return n==="record"?i.records={[t]:Ao(r.records[t])}:n==="resource"?i.resources={[t]:Ao(r.resources?.[t])}:i.files={[t]:Ao(r.files?.[t])},i}function Ao(r){return r===void 0?null:structuredClone(r)}function st(r){if(!r.batch)throw Re("The mirror has no prepared sync batch.");return r.batch}async function L0(r,e){let t=st(r);if(t.phase!=="blocked"||t.failure?.code!=="sync_plan_stale")throw new b("mirror_recovery_required","Only a stale batch at a durable action boundary can be abandoned.");delete r.batch,await e.write(r)}function PN(r,e,t,n){return{protocol_version:1,engine_version:3,generation:0,replica_id:r,scope_epoch:e,cursor:0,records:{},resources:{},files:{},selective_sync:n,mode:t,planned_conflicts:{}}}var cl=class{ports;materializer;ownersByPath=new Map;pathsByOwner=new Map;constructor(e){this.ports=e,this.materializer=new es(e.fileSystem,e.runtime,e.mode,e.blobStore)}async execute(e,t){let n=st(e);this.indexPathOwners(e);let i=new Set(n.receipts.map(s=>s.action_id));for(await O0(e,this.ports.store);n.next_action!i.has(a));if(o){let a={code:"invalid_mirror_state",message:`Action ${s.action_id} is missing dependency ${o}.`,action_id:s.action_id};return await sl(e,"blocked",a,this.ports.store),{status:"blocked",completed:n.next_action,failure:a}}try{let a=await this.dispatch(e,s);await N0(e,a,this.ports.store),i.add(a.action_id),this.ports.onProgress?.(n.next_action,n.plan.actions.length-1)}catch(a){let c=IN(a,s.action_id);return await sl(e,"blocked",c,this.ports.store),{status:c.code==="sync_plan_stale"?"stale":"blocked",completed:n.next_action,failure:c}}}throw Re("Prepared plan has no checkpoint action.")}async dispatch(e,t){switch(t.command){case"write_local":return this.writeLocal(e,t);case"move_local":return this.moveLocal(e,t);case"delete_local":return this.deleteLocal(e,t);case"put_remote":return this.putRemote(e,t);case"move_remote":return this.moveRemote(e,t);case"delete_remote":return this.deleteRemote(e,t);case"record_conflict":return e.planned_conflicts??={},e.planned_conflicts[t.identity]={decision_id:j0(t.entity,t.identity,t.local,t.remote,t.conflict_kind,this.ports.runtime.digest),entity:t.entity,local:t.local,remote:t.remote,conflict_kind:t.conflict_kind},e.local_bindings??={},t.local.state==="exact"?e.local_bindings[t.identity]={entity:t.entity,path:t.local.object.path}:delete e.local_bindings[t.identity],this.rebaseConflict(e,t),{action_id:t.action_id,status:"conflicted"};case"clear_conflict":return delete e.planned_conflicts?.[t.identity],delete e.local_bindings?.[t.identity],{action_id:t.action_id,status:"completed"};case"advance_checkpoint":throw Re("The executor cannot dispatch checkpoint actions.")}}rebaseConflict(e,t){let n=st(e).payloads;if(t.entity==="record"){if(t.remote.state==="absent"){delete e.records[t.identity];return}let s=n.records[t.action_id];if(!s||s.revision!==t.remote.object.revision)throw Er(t);al(s,this.ports.runtime,s.revision),e.records[t.identity]={path:s.path,revision:s.revision,hash:t.local.state==="exact"?t.local.object.payload_revision.replace(/^sha256:/u,""):this.ports.runtime.digest(s.document),...this.ports.mode==="read_write"?{record:s}:{}};return}if(e.files??={},t.remote.state==="absent"){delete e.files[t.identity];return}let i=n.files[t.action_id];if(!i||i.revision!==t.remote.object.revision)throw Er(t);gt(i),e.files[t.identity]={file:i}}async writeLocal(e,t){let n=st(e),i=await this.ports.fileSystem.exists(t.target.path);(!i||!await this.matchesRef(t.target))&&(await this.assertLocal(t.expected_local),await this.assertPathOwner(t.target.path,t.expected_path_owner,i));let s=n.payloads;if(t.target.entity==="record"){let a=s.records[t.action_id];if(!a||a.revision!==t.payload_revision)throw Er(t);return al(a,this.ports.runtime,t.payload_revision),await this.materializer.put(e,a,{inspectionPreflighted:n.plan.summary.blocking_issues?1:!0}),this.installPathOwner(t.target),{action_id:t.action_id,status:"completed"}}if(t.target.entity==="resource"){let a=s.resources[t.action_id];if(!a||a.revision!==t.payload_revision||_r(a.document,this.ports.runtime)!==a.revision)throw Er(t);return await this.materializer.putResource(e,a,e),this.installPathOwner(t.target),{action_id:t.action_id,status:"completed"}}let o=s.files[t.action_id];if(!o||o.content_digest!==t.payload_revision)throw Er(t);return await this.materializer.putFile(e,o,e),this.installPathOwner(t.target),{action_id:t.action_id,status:"completed"}}async moveLocal(e,t){return await this.matchesRef({...t.source,path:t.target_path})||(await this.assertLocal(t.expected_source_owner),await this.assertPathOwner(t.target_path,t.expected_target_owner),await this.ports.fileSystem.move(t.source.path,t.target_path)),CN(e,t.source,t.target_path),this.installPathOwner({...t.source,path:t.target_path}),{action_id:t.action_id,status:"completed"}}async deleteLocal(e,t){if(await this.matchesRef(t.target))if(await this.assertLocal(t.expected_local),t.target.entity==="record")await this.materializer.remove(e,t.target.identity,t.target.path,{inspectionPreflighted:!0});else if(t.target.entity==="resource"){let i=e.resources?.[t.target.identity];i?await this.materializer.removeResource(e,t.target.path,i):await this.ports.fileSystem.remove(t.target.path)}else await this.materializer.removeFile(e,t.target.identity);else MN(e,t.target);return this.removePathOwner(t.target),{action_id:t.action_id,status:"completed"}}async putRemote(e,t){let n=st(e).payloads;if(t.target.entity==="record"){let a=n.mutations[t.action_id],c=n.documents[t.action_id];if(!a||a.operation!=="put"||c===void 0)throw Er(t);if(_r(c,this.ports.runtime)!==t.payload_revision)throw $r("Prepared local document payload no longer matches its revision.");let l=await this.ports.transport.mutate(a);return this.acceptRecordReceipt(e,t,l,c),Fp(t.action_id,l)}if(t.target.entity==="resource")throw new b("invalid_sync_plan","Authority resources are not writable mirror objects.");let i=n.local_files[t.action_id];if(!i||!this.ports.blobStore||!this.ports.transport.uploadFile)throw Er(t);let s={protocol_version:1,type:"open_file_upload",transfer_id:Bp(t.action_id),path:t.target.path,size:i.size,content_digest:i.content_digest,...i.media_type?{media_type:i.media_type}:{},...t.expected_remote.state==="exact"?{if_revision:t.expected_remote.object.revision}:{}},o=await this.ports.transport.uploadFile(s,this.ports.blobStore.read(i.content_digest));if(gt(o.file),o.transfer_id!==s.transfer_id||o.file.path!==t.target.path||o.file.content_digest!==i.content_digest||o.file.size!==i.size)throw Up(t);return e.files??={},e.files[o.file.file_id]={file:o.file},delete e.local_bindings?.[t.target.identity],{action_id:t.action_id,status:"completed",file:o.file}}async moveRemote(e,t){if(t.source.entity==="record"){let s=st(e).payloads.mutations[t.action_id];if(!s||s.operation!=="move")throw Er(t);let o=await this.ports.transport.mutate(s);return this.acceptRecordReceipt(e,t,o),Fp(t.action_id,o)}if(t.source.entity==="resource"||!this.ports.transport.moveFile)throw new b("invalid_sync_plan","This remote move command is unsupported.");let n={protocol_version:1,type:"move_file",mutation_id:Bp(t.action_id),file_id:t.source.identity,if_revision:this.dependencyFileRevision(e,t)??(t.expected_source_owner.state==="exact"?t.expected_source_owner.object.revision:t.source.revision),from_path:t.source.path,path:t.target_path,update_references:!1},i=await this.ports.transport.moveFile(n);if(gt(i.file),i.mutation_id!==n.mutation_id||i.file.file_id!==t.source.identity||i.file.path!==t.target_path)throw Up(t);return e.files??={},e.files[t.source.identity]={file:i.file},delete e.local_bindings?.[t.source.identity],{action_id:t.action_id,status:"completed",file:i.file}}dependencyFileRevision(e,t){if(!t.revision_from_dependency)return;let n=st(e).receipts.find(({action_id:i})=>i===t.revision_from_dependency);if(!n?.file||n.file.file_id!==t.source.identity)throw Re(`Move ${t.action_id} is missing its dependency file receipt.`);return n.file.revision}async deleteRemote(e,t){if(t.target.entity==="record"){let s=st(e).payloads.mutations[t.action_id];if(!s||s.operation!=="delete")throw Er(t);let o=await this.ports.transport.mutate(s);return this.acceptRecordReceipt(e,t,o),Fp(t.action_id,o)}if(t.target.entity==="resource"||!this.ports.transport.deleteFile)throw new b("invalid_sync_plan","This remote delete command is unsupported.");let n={protocol_version:1,type:"delete_file",mutation_id:Bp(t.action_id),file_id:t.target.identity,if_revision:t.expected_remote.state==="exact"?t.expected_remote.object.revision:t.target.revision,path:t.target.path},i=await this.ports.transport.deleteFile(n);if(i.mutation_id!==n.mutation_id||i.file_id!==t.target.identity||i.previous_path!==t.target.path)throw Up(t);return delete e.files?.[t.target.identity],delete e.local_bindings?.[t.target.identity],{action_id:t.action_id,status:"completed"}}acceptRecordReceipt(e,t,n,i){let o=("target"in t?t.target:t.source).identity;if(n.status==="applied"||n.status==="previously_applied"){if(n.record){al(n.record,this.ports.runtime,n.record.revision);let u=e.records[o];e.records[o]={path:n.record.path,revision:n.record.revision,hash:i===void 0?u?.hash??this.ports.runtime.digest(n.record.document):this.ports.runtime.digest(i),...this.ports.mode==="read_write"?{record:n.record}:{}}}else delete e.records[o];delete e.local_bindings?.[o];return}let a=n.status==="conflicted"?n.conflict.current:void 0;a&&al(a,this.ports.runtime,a.revision);let c=a?{state:"exact",object:{entity:"record",identity:o,path:a.path,revision:a.revision,payload_revision:a.revision}}:t.command==="move_remote"?t.expected_source_owner:t.expected_remote;e.planned_conflicts??={};let l=n.status==="rejected"?"rejected":"both_changed";e.planned_conflicts[o]={decision_id:j0("record",o,t.expected_local,c,l,this.ports.runtime.digest),entity:"record",local:t.expected_local,remote:c,conflict_kind:l},e.local_bindings??={},t.expected_local.state==="exact"&&(e.local_bindings[o]={entity:"record",path:t.expected_local.object.path}),a&&(e.records[o]={path:a.path,revision:a.revision,hash:t.expected_local.state==="exact"?t.expected_local.object.payload_revision.replace(/^sha256:/u,""):this.ports.runtime.digest(a.document),...this.ports.mode==="read_write"?{record:a}:{}})}async assertLocal(e){if(e.state!=="absent"&&!await this.matchesRef(e.object))throw $r(`${e.object.path} no longer matches the inspected revision.`)}async assertPathOwner(e,t,n){let i=this.ownersByPath.get(e);if(t.state==="absent"){if(i||(n??await this.ports.fileSystem.exists(e)))throw $r(`${e} is no longer vacant.`);return}if(!i||i.entity!==t.object.entity||i.identity!==t.object.identity)throw $r(`${e} has a different path owner.`)}matchesRef(e){return il(this.ports.fileSystem,this.ports.runtime,e,!1)}indexPathOwners(e){this.ownersByPath.clear(),this.pathsByOwner.clear();for(let[t,n]of Object.entries(e.records))this.installPathOwner({entity:"record",identity:t,path:n.path,revision:n.revision,payload_revision:`sha256:${n.hash}`});for(let[t,n]of Object.entries(e.resources??{}))this.installPathOwner({entity:"resource",identity:t,path:n.path,revision:n.revision,payload_revision:`sha256:${n.hash}`});for(let[t,n]of Object.entries(e.files??{}))this.installPathOwner({entity:"file",identity:t,path:n.file.path,revision:n.file.revision,payload_revision:n.file.content_digest,size:n.file.size})}installPathOwner(e){let t=`${e.entity}:${e.identity}`,n=this.pathsByOwner.get(t);n!==void 0&&this.ownersByPath.delete(n),this.ownersByPath.set(e.path,e),this.pathsByOwner.set(t,e.path)}removePathOwner(e){let t=`${e.entity}:${e.identity}`,n=this.pathsByOwner.get(t)??e.path;this.ownersByPath.delete(n),this.pathsByOwner.delete(t)}};function j0(r,e,t,n,i,s){return ei({entity:r,identity:e,local:t,remote:n,conflict_kind:i},s)}function CN(r,e,t){if(e.entity==="record"){let n=r.records[e.identity];n&&(n.path=t,n.record&&(n.record.path=t))}else if(e.entity==="resource"){let n=r.resources?.[e.identity];n&&(n.path=t)}else{let n=r.files?.[e.identity];n&&(n.file.path=t)}}function MN(r,e){e.entity==="record"?delete r.records[e.identity]:e.entity==="resource"?delete r.resources?.[e.identity]:delete r.files?.[e.identity]}function al(r,e,t){let n=Dc(r);if(r.revision!==t||_r(n,e)!==t)throw new b("invalid_sync_response","Record receipt does not match its exact document revision.")}function Fp(r,e){return e.status==="applied"||e.status==="previously_applied"?{action_id:r,status:"completed",...e.record?{record:e.record}:{}}:{action_id:r,status:e.status}}function Er(r){return new b("sync_payload_incomplete",`Prepared action ${r.action_id} has no exact payload capability.`)}function Up(r){return new b("invalid_sync_response",`Authority receipt does not match prepared action ${r.action_id}.`)}function IN(r,e){let t=Kn(r);return{code:Wn(r,"sync_action_failed"),message:t.message,action_id:e}}function Bp(r){let e=r.replace(/^sha256:/u,"");return`${e.slice(0,8)}-${e.slice(8,12)}-4${e.slice(13,16)}-8${e.slice(17,20)}-${e.slice(20,32)}`}async function q0(r,e,t){let n=st(r);if(n.phase!=="effects_complete")throw Re("A checkpoint cannot advance before every prepared effect is durable.");let i=n.plan.actions[n.next_action];if(!i||i.command!=="advance_checkpoint")throw Re("Prepared checkpoint action is missing.");if(i.expected.generation!==n.checkpoint_before.generation||i.expected.cursor!==n.checkpoint_before.cursor||i.next.generation!==n.checkpoint_after.generation||i.next.cursor!==n.checkpoint_after.cursor)throw Re("Prepared checkpoint boundary is inconsistent.");let s=n.plan.fingerprint;return r.generation=i.next.generation,r.cursor=i.next.cursor??0,r.last_completed_plan=s,r.last_synced_at=e.now(),delete r.batch,await t.write(r),s}async function F0(r,e,t,n){let[i]=e.actions;if(e.actions.length!==1||!i||i.command!=="advance_checkpoint"||i.expected.generation!==(r.generation??0)||i.expected.cursor!==r.cursor||i.next.generation!==(r.generation??0)+1||i.next.cursor!==e.authority_cursor||r.batch!==void 0)throw Re("Empty checkpoint plan is inconsistent.");return r.generation=i.next.generation,r.cursor=i.next.cursor??0,r.last_completed_plan=e.fingerprint,r.last_synced_at=t.now(),await n.write(r),e.fingerprint}var ri=class{replicaId;transport;mode;stateStore;fileSystem;blobStore;selectiveSync;lease;runtime;materializer;onProgress;constructor(e,t,n,i="read_only"){this.replicaId=e,this.transport=t,this.mode=i,this.stateStore=n.stateStore,this.fileSystem=n.fileSystem,this.blobStore=n.blobStore,this.selectiveSync=Yn(n.selectiveSync),this.lease=n.lease??new wo,this.runtime=n.runtime??So,this.materializer=new es(this.fileSystem,this.runtime,this.mode,this.blobStore),this.onProgress=n.onProgress}async sync(e={}){return this.lease.runExclusive(async()=>{let t=await this.readState();if(t?.batch?.phase==="blocked"&&t.batch.failure?.code==="sync_plan_stale"&&(await L0(t,this.journalStore()),t=await this.readState()),t?.batch)return this.executePrepared(t,e.signal);let n=await this.inspectDetailed(t);return this.applyInspection(n,e.signal)})}async inspect(){return this.lease.runExclusive(async()=>{let e=await this.readState();return e?.batch?.plan??(await this.inspectDetailed(e)).plan})}async apply(e,t={}){return this.lease.runExclusive(async()=>{let n=await this.readState();if(n?.batch){if(n.batch.plan.fingerprint!==e.fingerprint)throw new b("mirror_recovery_required","A different prepared plan must recover before this plan can apply.");return this.executePrepared(n,t.signal)}if(n?.last_completed_plan===e.fingerprint){let s=rr(n,this.mode);return wr(s.state==="attention"?"attention":"applied",{...e,issues:[]},s,0)}let i=await this.inspectDetailed(n);return i.plan.fingerprint!==e.fingerprint?wr("stale",e,rr(i.prior,this.mode),0,{code:"sync_plan_stale",message:"The local folder or authority changed. Inspect the sync plan again."}):this.applyInspection(i,t.signal)})}async applyInspection(e,t){let n=e.plan;if(n.issues.some(s=>s.blocking)&&n.actions.length===0)return wr("attention",n,rr(e.prior,this.mode),0);if(t?.aborted)return wr("cancelled",n,rr(e.prior,this.mode),0,{code:"sync_cancelled",message:"Sync cancelled before preparation."});try{await new xo(this.fileSystem,this.runtime).validate(n,e.prior)}catch(s){let o=Kn(s),a=Wn(s,"sync_revalidation_failed");return wr(a==="sync_plan_stale"?"stale":"failed",n,rr(e.prior,this.mode),0,{code:a,message:o.message})}if(n.actions.length===0)return wr("applied",n,rr(e.prior,this.mode),0);if(e.prior&&n.actions.length===1&&n.actions[0]?.command==="advance_checkpoint")return await F0(e.prior,n,this.runtime,this.journalStore()),wr("applied",n,rr(e.prior,this.mode),0);let i=await R0(e.prior??this.initialUnchangedState(e),n,e.durable_payloads,this.journalStore());return this.executePrepared(i,t)}async executePrepared(e,t){let n=e.batch,i=await new cl({transport:this.transport,fileSystem:this.fileSystem,blobStore:this.blobStore,runtime:this.runtime,mode:this.mode,store:this.journalStore(),onProgress:(c,l)=>this.onProgress?.({phase:"applying",completed:c,total:l,done:c===l})}).execute(e,t);if(i.status!=="effects_complete"){let c=rr(e,this.mode);return wr(i.status==="blocked"?"failed":i.status,n.plan,c,i.completed,i.failure)}let s=n.plan;await q0(e,this.runtime,this.journalStore()),await this.pruneFileBlobs();let o=rr(e,this.mode),a=o.conflicts.length>0||o.local_issues.length>0||s.summary.conflicts>0;return wr(a?"attention":"applied",s,o,i.completed)}inspectDetailed(e){return new nl(this.replicaId,this.transport,this.mode,this.fileSystem,this.blobStore,this.selectiveSync,this.runtime,()=>this.readState(),t=>this.currentRecordPathPolicy(t)).inspect(e)}initialUnchangedState(e){if(e.plan.kind!=="initial"||!e.snapshot)return null;let t=new Set(e.summary.objects.filter(o=>o.local.state==="exact"&&o.remote.state==="exact"&&JSON.stringify(o.local)===JSON.stringify(o.remote)).map(o=>`${o.entity}:${o.identity}`)),n={};for(let{record:o,hash:a}of e.snapshot.records)t.has(`record:${o.record_id}`)&&(n[o.record_id]={path:o.path,revision:o.revision,hash:a,...this.mode==="read_write"?{record:o}:{}});let i={};for(let o of e.snapshot.resources)t.has(`resource:${o.path}`)&&(i[o.path]={path:o.path,revision:o.revision,hash:this.runtime.digest(o.document)});let s={};for(let o of e.snapshot.files)t.has(`file:${o.file_id}`)&&(s[o.file_id]={file:o});return{protocol_version:1,engine_version:3,generation:0,replica_id:e.plan.replica_id,scope_epoch:e.plan.scope_epoch,cursor:0,records:n,resources:i,files:s,selective_sync:e.plan.selective_sync,mode:e.plan.mode,planned_conflicts:{}}}async status(){let e=await this.checkpointStatus(),t=await this.inspect();return e.state==="not_initialized"&&!t.issues.some(n=>n.code==="invalid_frontmatter"||n.code==="file_read_failed")?e:w0(e,t)}async checkpointStatus(){return this.lease.runExclusive(()=>this.checkpointStatusUnlocked())}async checkpointStatusUnlocked(){return rr(await this.readState(),this.mode)}async authorityPromotionManifest(){return this.lease.runExclusive(async()=>{if(this.mode!=="read_write")throw new b("promotion_requires_writable_mirror","Only a read-write mirror can prove an authority promotion source.");let e=await this.readState();if(!e)throw new b("mirror_not_initialized","Synchronize this mirror first.");if(e.batch||Object.keys(e.planned_conflicts??{}).length>0)throw new b("promotion_mirror_not_clean","Finish the prepared batch and resolve conflicts before promotion.");let t=await _0({state:e,selectiveSync:this.selectiveSync,fileSystem:this.fileSystem,pathPolicy:await this.currentRecordPathPolicy(e),digest:this.runtime.digest});if((await this.inspectDetailed(e)).plan.actions.some(i=>i.command!=="advance_checkpoint"))throw new b("promotion_mirror_not_clean","Synchronize this mirror immediately before promotion.");return t})}async previewInitialization(){let e=await this.inspect(),t=e.actions.filter(i=>i.command==="put_remote"||i.command==="move_remote"||i.command==="delete_remote"),n=e.actions.filter(i=>i.command==="write_local"||i.command==="move_local"||i.command==="delete_local");return{already_initialized:e.kind==="incremental",download_documents:n.filter(i=>"target"in i&&i.target.entity!=="file"||"source"in i&&i.source.entity!=="file").length,upload_documents:t.filter(i=>"target"in i&&i.target.entity==="record"||"source"in i&&i.source.entity==="record").length,unchanged_documents:0,download_files:n.filter(i=>"target"in i&&i.target.entity==="file"||"source"in i&&i.source.entity==="file").length,upload_files:t.filter(i=>"target"in i&&i.target.entity==="file"||"source"in i&&i.source.entity==="file").length,unchanged_files:0,collisions:e.issues.filter(i=>i.code==="local_collision"&&i.path).map(i=>i.path),local_issues:e.issues.filter(i=>i.code==="invalid_frontmatter"&&i.path!==void 0).map(i=>({code:"invalid_frontmatter",message:i.message,path:i.path}))}}async resolveConflict(e,t,n){await this.lease.runExclusive(()=>this.resolveConflictUnlocked(e,t,n))}async resolveConflictUnlocked(e,t,n){if(this.mode!=="read_write")throw new b("mirror_read_only","Receive-only mirrors have no writable conflicts.");let i=await this.readState();if(!i||i.batch)throw new b("mirror_recovery_required","Finish sync recovery before resolving conflicts.");let s=i.planned_conflicts?.[e];if(!s)throw new b("mirror_conflict_not_found","Writable mirror conflict was not found.");if((s.decision_id??"")!==t)throw U0();let o=new xo(this.fileSystem,this.runtime);s.local.state==="exact"?await o.validateExpected(s.local):s.remote.state==="exact"&&await o.validateExpectedAt(s.remote.object.path,s.local);let a=await Xc(this.replicaId,this.transport,this.mode,this.selectiveSync,this.runtime),c=s.entity==="record"?a.records.find(({record:d})=>d.record_id===e)?.record:void 0,l=s.entity==="file"?a.files.find(d=>d.file_id===e):void 0;if(!(s.entity==="record"?TN(s.remote,c):RN(s.remote,l)))throw U0();n==="remote"&&(s.entity==="record"?await this.installRemoteRecord(i,e,c):await this.installRemoteFile(i,e,l)),delete i.planned_conflicts?.[e],n==="remote"&&delete i.local_bindings?.[e],await this.writeState(i)}async installRemoteRecord(e,t,n){if(n){let o=e.planned_conflicts?.[t],a=await this.fileSystem.read(n.path),c=e.records[t]?.path===n.path||o?.local.state==="exact"&&o.local.object.path===n.path;await this.materializer.put(e,n,{inspectionPreflighted:!1,...c&&a!==null?{acceptedHash:this.runtime.digest(a)}:{}});return}let i=e.planned_conflicts?.[t],s=i?.local.state==="exact"?i.local.object.path:e.records[t]?.path;s&&await this.fileSystem.read(s)!==null&&await this.fileSystem.remove(s),delete e.records[t]}async installRemoteFile(e,t,n){if(n){if(!this.blobStore)throw new b("file_storage_unavailable","File resolution needs a blob store.");await Hc(this.transport,this.blobStore,n);let o=e.planned_conflicts?.[t],a=o?.local.state==="exact"?{content_digest:o.local.object.payload_revision,size:o.local.object.size??0}:void 0,c=e.local_bindings?.[t]?.path;await this.materializer.putFile(e,n,e,a),c&&c!==n.path&&await this.fileSystem.remove(c);return}let i=e.planned_conflicts?.[t],s=i?.local.state==="exact"?i.local.object.path:e.files?.[t]?.file.path;s&&await this.fileSystem.inspectBinary(s)!==null&&await this.fileSystem.remove(s),delete e.files?.[t]}async readState(){let e=await this.stateStore.read();if(e===null)return null;try{return l0(e,this.replicaId,this.mode)}catch(t){throw t instanceof b?t:Re("Mirror metadata is corrupt or belongs to another replica.")}}writeState(e){return this.stateStore.write(e)}journalStore(){let e=this.stateStore;return{write:t=>this.writeState(t),...e.appendJournal?{appendJournal:t=>e.appendJournal(t)}:{}}}async pruneFileBlobs(){if(!this.blobStore)return;let e=await this.readState();if(!e)return;let t=new Set;for(let n of Object.values(e.files??{}))t.add(n.file.content_digest);for(let n of Object.values(e.batch?.payloads.files??{}))t.add(n.content_digest);for(let n of Object.values(e.batch?.payloads.local_files??{}))t.add(n.content_digest);await this.blobStore.prune(t)}currentRecordPathPolicy(e){return this.materializer.recordPathPolicy(e)}};function TN(r,e){return r.state==="absent"?e===void 0:e!==void 0&&r.object.entity==="record"&&r.object.identity===e.record_id&&r.object.path===e.path&&r.object.revision===e.revision&&r.object.payload_revision===e.revision&&r.object.size===void 0}function RN(r,e){return r.state==="absent"?e===void 0:e!==void 0&&r.object.entity==="file"&&r.object.identity===e.file_id&&r.object.path===e.path&&r.object.revision===e.revision&&r.object.payload_revision===e.content_digest&&r.object.size===e.size}function U0(){return new b("mirror_conflict_stale","Local or hosted content changed after this conflict was recorded. Synchronize again before choosing a version.")}var ko=class extends ri{constructor(e,t,n){super(e,t,n,"read_write")}};function B0(r){let e=[...r.actions.flatMap(t=>t.command==="advance_checkpoint"?[]:[ON(t)]),...r.issues.map(t=>({kind:"document",path:t.path??"Sync engine",direction:"attention",action:"fix",detail:t.message}))];return{plan:r,phase:r.kind,entries:e,cursor:r.base_cursor,remoteHead:r.authority_cursor,already_initialized:r.kind==="incremental",download_documents:r.actions.filter(t=>["write_local","move_local","delete_local"].includes(t.command)&&("target"in t?t.target.entity!=="file":"source"in t&&t.source.entity!=="file")).length,upload_documents:r.actions.filter(t=>["put_remote","move_remote","delete_remote"].includes(t.command)&&("target"in t?t.target.entity==="record":"source"in t&&t.source.entity==="record")).length,unchanged_documents:0,download_files:r.actions.filter(t=>["write_local","move_local","delete_local"].includes(t.command)&&("target"in t?t.target.entity==="file":"source"in t&&t.source.entity==="file")).length,upload_files:r.actions.filter(t=>["put_remote","move_remote","delete_remote"].includes(t.command)&&("target"in t?t.target.entity==="file":"source"in t&&t.source.entity==="file")).length,unchanged_files:0,collisions:r.issues.filter(t=>t.blocking&&t.code==="local_collision"&&t.path!==void 0).map(t=>t.path),local_issues:r.issues.filter(t=>["invalid_frontmatter","file_read_failed"].includes(t.code)&&t.path!==void 0).map(t=>({code:t.code,message:t.message,path:t.path}))}}function ON(r){if(r.command==="advance_checkpoint")throw new Error("Checkpoint actions are not preview entries.");if(r.command==="record_conflict"){let c=r.local.state==="exact"?r.local.object:r.remote.state==="exact"?r.remote.object:void 0;return{kind:r.entity==="file"?"file":"document",path:c?.path??r.identity,direction:"attention",action:"fix",detail:`Local and hosted ${r.entity} changes conflict (${r.conflict_kind.replace(/_/g," ")}).`,...c?.entity==="file"&&c.size!==void 0?{estimatedBytes:c.size}:{},...r.entity==="record"?{recordId:r.identity}:{fileId:r.identity}}}if(r.command==="clear_conflict"){let c=r.expected_local.state==="exact"?r.expected_local.object:r.expected_remote.state==="exact"?r.expected_remote.object:void 0;return{kind:r.entity==="file"?"file":"document",path:c?.path??r.identity,direction:"attention",action:"fix",detail:`Local and hosted ${r.entity} content now matches; clear the resolved conflict.`,...c?.entity==="file"&&c.size!==void 0?{estimatedBytes:c.size}:{},...r.entity==="record"?{recordId:r.identity}:{fileId:r.identity}}}let e=r.command.endsWith("_local"),t="target"in r?r.target:r.source,n=r.command==="move_local"||r.command==="move_remote"?r.target_path:t.path,i=r.command==="write_local"&&r.expected_local.state==="absent"||r.command==="put_remote"&&r.expected_remote.state==="absent",s=r.command.startsWith("move_")?"rename":r.command.startsWith("delete_")?"delete":i?"create":"update",o=r.command.split("_")[0],a=r.command.startsWith("move_")?` from ${t.path}`:"";return{kind:t.entity==="file"?"file":"document",path:n,direction:e?"download":"upload",action:s,detail:`${e?"Hosted":"Local"} ${t.entity} will ${o}${a}.`,...t.entity==="file"&&t.size!==void 0?{estimatedBytes:t.size}:{},...t.entity==="record"?{recordId:t.identity}:{},...t.entity==="file"?{fileId:t.identity}:{}}}var ts=".mdbase/connect-role.json",Po=".mdbase/authority-adoption.json",Co=".mdbase/authority-adoption-snapshot.json",NN="mdbase-obsidian-connect",Sn="mirrors",DN="mdbase-obsidian-connect-blobs",$n="manifests",En="chunks",ll=1024*1024,LN=32*1024*1024;function ii(r){if(!Number.isSafeInteger(r)||r<0||r>LN)throw new b("file_too_large","Binary sync supports files up to 32 MiB on this device. Exclude this file's folder to continue.")}var jN="mdbase-connect-access-",qN="mdbase-connect-refresh-",FN="mdbase-connect-adoption-",UN=300*1e3,BN=[".git",".trash",".mdbase"],VN=new Set([".git",".mdbase",".trash","node_modules","_contracts","_schemas","_types","_views"]),Vp=["image","audio","video","pdf","other"],Io=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function ot(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)}function xr(r){let e=r?.file_classes??[];if(e.some(s=>!Vp.includes(s))||new Set(e).size!==e.length)throw new b("invalid_file_materialization","Selected file media classes must be valid and unique.");let t=[...e].sort((s,o)=>Vp.indexOf(s)-Vp.indexOf(o)),n=r?.excluded_folders??[];if(n.some(s=>typeof s!="string"||!s.trim()))throw new b("invalid_file_materialization","Excluded folders cannot be empty.");let i=n.map(s=>Fn(s.trim())).sort((s,o)=>s.toLocaleLowerCase().localeCompare(o.toLocaleLowerCase()));if(i.length>100)throw new b("invalid_file_materialization","File sync supports at most 100 excluded folders.");if(new Set(i.map(s=>s.toLocaleLowerCase())).size!==i.length)throw new b("invalid_file_materialization","Excluded folders must be unique on portable filesystems.");for(let s of i)Mo(s,!0);return{file_classes:t,excluded_folders:i}}function G0(r){let e=r.includes(".")?r.slice(r.lastIndexOf(".")+1).toLowerCase():"";return["avif","bmp","gif","jpeg","jpg","png","svg","webp"].includes(e)?"image":["flac","m4a","mp3","oga","ogg","opus","wav"].includes(e)?"audio":["3gp","mkv","mov","mp4","webm"].includes(e)?"video":e==="pdf"?"pdf":"other"}function zN(r,e,t=G0(e)){if(!r.file_classes.includes(t))return!1;let n=(0,te.normalizePath)(e);return!r.excluded_folders.some(i=>n===i||n.startsWith(`${i}/`))}function Mo(r,e=!1){let t=Fn(r),n=t.split("/");if(t.length>1024||!e&&/\.md$/i.test(t)||n.some(i=>i.startsWith(".")||VN.has(i.toLowerCase())||/[<>"|?*]/u.test(i)||/^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\.|$)/iu.test(i)))throw new b("invalid_file_path",`Collection file path ${t} is hidden, reserved, or non-portable.`);return t}async function Hp(r){let e=new Uint8Array(await crypto.subtle.digest("SHA-256",r));return{size:r.byteLength,content_digest:`sha256:${Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}`}}async function Kp(r){let e=[],t=0;for await(let s of r){if(!(s instanceof Uint8Array))throw new b("file_integrity_failed","A binary stream returned an invalid chunk.");s.byteLength&&(t+=s.byteLength,ii(t),e.push(Uint8Array.from(s)))}let n=new Uint8Array(t),i=0;for(let s of e)n.set(s,i),i+=s.byteLength;return n.buffer}function V0(r){let e=r.includes(".")?r.slice(r.lastIndexOf(".")+1).toLowerCase():"";return{avif:"image/avif",gif:"image/gif",jpeg:"image/jpeg",jpg:"image/jpeg",png:"image/png",svg:"image/svg+xml",webp:"image/webp",flac:"audio/flac",m4a:"audio/mp4",mp3:"audio/mpeg",ogg:"audio/ogg",opus:"audio/opus",wav:"audio/wav",mov:"video/quicktime",mp4:"video/mp4",webm:"video/webm",pdf:"application/pdf"}[e]}async function HN(r,e){if(r!==void 0){if(!e)return JSON.stringify(r);if(r instanceof ArrayBuffer)return r;if(ArrayBuffer.isView(r))return Uint8Array.from(new Uint8Array(r.buffer,r.byteOffset,r.byteLength)).buffer;if(r instanceof Blob)return r.arrayBuffer();throw new b("invalid_file_upload","The adoption upload body was not binary data.")}}function dl(r,e){if(e!=null)return e;if(!r.trim())return{};try{return JSON.parse(r)}catch{return{}}}function J0(r){let e=r["retry-after"]??r["Retry-After"];if(!e)return;let t=Number(e);if(Number.isFinite(t)&&t>=0)return t*1e3;let n=Date.parse(e);if(Number.isFinite(n))return Math.max(0,n-Date.now())}async function fl(r,e=i=>(0,te.requestUrl)(i),t=(i,s)=>window.fetch(i,s),n=KN()){if(n)try{return await z0(r,n)}catch{}try{return await e(r)}catch(i){try{return await z0(r,t)}catch{throw i}}}function KN(){try{if(typeof require!="function")return null;let r=require("electron").remote?.net;return r?.fetch?r.fetch.bind(r):null}catch{return null}}async function z0(r,e){let t=new Headers(r.headers);r.contentType&&!t.has("content-type")&&t.set("content-type",r.contentType);let n=await e(r.url,{method:r.method,headers:t,body:r.body}),i=await n.arrayBuffer(),s=new TextDecoder().decode(i),o=null;if(s.trim())try{o=JSON.parse(s)}catch{o=null}if(r.throw!==!1&&n.status>=400)throw new Error(`Request failed with status ${n.status}`);let a={};return n.headers.forEach((c,l)=>{a[l]=c}),{status:n.status,headers:a,arrayBuffer:i,json:o,text:s}}function WN(){return async r=>{if(r.signal?.aborted)throw new DOMException("Enrollment cancelled.","AbortError");let e=await fl({url:r.url,method:r.method,headers:r.headers,body:r.body===void 0?void 0:JSON.stringify(r.body),contentType:r.body===void 0?void 0:"application/json",throw:!1});if(r.signal?.aborted)throw new DOMException("Enrollment cancelled.","AbortError");return{status:e.status,body:dl(e.text,e.json),retryAfterMs:J0(e.headers)}}}function GN(){return async r=>{if(r.signal?.aborted)throw new DOMException("Collection adoption cancelled.","AbortError");let e=await HN(r.body,r.rawBody),t=await fl({url:r.url,method:r.method,headers:r.headers,body:e,contentType:e===void 0||r.rawBody?void 0:"application/json",throw:!1});if(r.signal?.aborted)throw new DOMException("Collection adoption cancelled.","AbortError");return{status:t.status,body:dl(t.text,t.json),retryAfterMs:J0(t.headers),headers:t.headers}}}var Wp=class{constructor(e,t,n=fl,i){this.accessToken=t;this.send=n;this.onFileProgress=i;let s;try{s=new URL(e)}catch{throw new b("invalid_sync_url","Sync URL must be an absolute authority endpoint.")}if(!(s.protocol==="https:"||s.protocol==="http:"&&["localhost","127.0.0.1","[::1]","::1"].includes(s.hostname))||s.username||s.password||s.search||s.hash||!/^\/v1\/authorities\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/sync\/?$/i.test(s.pathname))throw new b("invalid_sync_url","Sync URL must identify one authority sync endpoint.");this.syncUrl=s.href.replace(/\/$/,""),this.filesUrl=this.syncUrl.replace(/\/sync$/u,"/files")}openSession(){return this.request("POST","sessions")}snapshot(e,t){let n=new URLSearchParams({snapshot_id:e});return t&&n.set("page",t),this.request("GET",`snapshot?${n.toString()}`)}fileSnapshot(e,t){let n=new URLSearchParams({snapshot_id:e});return t&&n.set("page",t),this.request("GET",`files/snapshot?${n.toString()}`)}async*downloadFile(e){ii(e.size);let t=crypto.randomUUID();try{let n=0;this.onFileProgress?.({direction:"download",path:e.path,transferredBytes:n,totalBytes:e.size});let i=await this.fileRequest("POST","downloads",{protocol_version:1,type:"open_file_download",transfer_id:t,file_id:e.file_id,revision:e.revision});if(i.protocol_version!==1||i.type!=="file_transfer"||i.transfer_id!==t||i.direction!=="download"||i.protection!=="transport_tls"||i.total_size!==e.size||i.strategy.kind!=="object_ranges"||!Number.isSafeInteger(i.strategy.part_size)||i.strategy.part_size<=0)throw new b("invalid_sync_response","The authority returned an incompatible file download session.");let s=Math.ceil(e.size/i.strategy.part_size);for(let o=0;o=300)throw this.responseError(c,"file_download_failed");let l=H0(c.headers,"content-length");if(l!==void 0&&Number(l)!==a||c.arrayBuffer.byteLength!==a)throw new b("file_integrity_failed","Hosted authority returned a file part with the wrong length.");n+=a,this.onFileProgress?.({direction:"download",path:e.path,transferredBytes:n,totalBytes:e.size}),a&&(yield new Uint8Array(c.arrayBuffer))}}finally{await this.fileRequest("DELETE",`transfers/${encodeURIComponent(t)}`).catch(()=>{})}}async uploadFile(e,t){ii(e.size);let n=await this.fileRequest("POST","uploads",e);if(n.protocol_version!==1||n.type!=="file_transfer"||n.transfer_id!==e.transfer_id||n.direction!=="upload"||n.protection!=="transport_tls"||n.total_size!==e.size||!["object_put","object_multipart"].includes(n.strategy.kind))throw new b("invalid_sync_response","Authority returned an incompatible file upload session.");let i=n.strategy.kind==="object_multipart"?n.strategy.part_size:Math.max(1,e.size);if(!Number.isSafeInteger(i)||i<=0)throw new b("invalid_sync_response","Authority returned an invalid upload part size.");let s=new Gp(t),o=Math.max(1,Math.ceil(e.size/i));if(n.received.some(d=>!Number.isSafeInteger(d)||d<0||d>=o)||new Set(n.received).size!==n.received.length)throw new b("invalid_sync_response","Authority returned invalid upload progress.");let a=n.uploaded_parts??[];if(a.some((d,f)=>!Number.isSafeInteger(d.part_number)||d.part_number<1||d.part_number>o||!d.etag||d.etag.length>255||f>0&&a[f-1].part_number>=d.part_number)||(n.strategy.kind==="object_multipart"?a.length!==n.received.length||a.some((d,f)=>d.part_number-1!==n.received[f]):a.length!==0))throw new b("invalid_sync_response","Authority returned invalid uploaded part receipts.");let c=n.received.reduce((d,f)=>{let p=f*i;return d+Math.min(i,Math.max(0,e.size-p))},0);if(this.onFileProgress?.({direction:"upload",path:e.path,transferredBytes:c,totalBytes:e.size}),n.received.length===o)return this.commitUpload(e.transfer_id,a);let l=new Set(n.received),u=Array.from({length:o},()=>{});for(let d of a)u[d.part_number-1]=d;for(let d=0;d=300)throw new b("file_upload_failed",`Object storage returned HTTP ${y.status}.`);if(c+=p,this.onFileProgress?.({direction:"upload",path:e.path,transferredBytes:c,totalBytes:e.size}),n.strategy.kind==="object_multipart"){let g=H0(y.headers,"etag");if(!g)throw new b("invalid_sync_response","Object storage omitted a multipart ETag.");u[d]={part_number:d+1,etag:g}}}return await s.expectEnd(),this.commitUpload(e.transfer_id,u.filter(d=>d!==void 0))}async commitUpload(e,t){let n=await this.fileRequest("POST",`uploads/${encodeURIComponent(e)}/commit`,{protocol_version:1,type:"commit_file_upload",transfer_id:e,parts:t});if(n.protocol_version!==1||n.type!=="file_upload_committed"||n.transfer_id!==e)throw new b("invalid_sync_response","Authority returned an invalid file upload receipt.");return n}async moveFile(e){let t=await this.fileRequest("POST",`${encodeURIComponent(e.file_id)}/move`,e);if(t.protocol_version!==1||t.type!=="file_moved"||t.mutation_id!==e.mutation_id)throw new b("invalid_sync_response","Authority returned an invalid file move receipt.");return t}async deleteFile(e){let t=await this.fileRequest("POST",`${encodeURIComponent(e.file_id)}/delete`,e);if(t.protocol_version!==1||t.type!=="file_deleted"||t.mutation_id!==e.mutation_id||t.file_id!==e.file_id)throw new b("invalid_sync_response","Authority returned an invalid file delete receipt.");return t}changes(e,t=200){let n=new URLSearchParams({after:String(e),limit:String(t)});return this.request("GET",`changes?${n.toString()}`)}mutate(e){return this.request("POST","mutations",e)}async request(e,t,n){return this.requestAt(this.syncUrl,e,t,n)}async fileRequest(e,t,n){return this.requestAt(this.filesUrl,e,t,n)}async requestAt(e,t,n,i){let s=await this.send({url:`${e}/${n}`,method:t,headers:{authorization:`Bearer ${this.accessToken}`},body:i===void 0?void 0:JSON.stringify(i),contentType:i===void 0?void 0:"application/json",throw:!1}),o=dl(s.text,s.json);if(s.status<200||s.status>=300)throw this.responseError(s,"sync_failed");return o}responseError(e,t){let n=dl(e.text,e.json),i=ot(n)&&ot(n.error)?n.error:{};return new b(typeof i.code=="string"?i.code:t,typeof i.message=="string"?i.message:`Sync request failed (${e.status}).`)}},Gp=class{constructor(e){this.remainder=new Uint8Array;this.iterator=e[Symbol.asyncIterator]()}async read(e){let t=new Uint8Array(new ArrayBuffer(e)),n=0;for(;n!["authorization","cookie","host","proxy-authorization","content-length"].includes(e.toLowerCase())))}function H0(r,e){return Object.entries(r).find(([n])=>n.toLowerCase()===e.toLowerCase())?.[1]}function Y0(r){return[r.configDir||".obsidian",...BN].map(t=>(0,te.normalizePath)(t).replace(/\/+$/,""))}function ni(r,e){let t=Fn(e);if(Y0(r).some(n=>t===n||t.startsWith(`${n}/`)))throw new b("unsafe_mirror_path",`The collection authority attempted to write a reserved path: ${t}`);return t}function Se(r){if(r?.aborted)throw new DOMException("Synchronization cancelled.","AbortError")}function vt(r,e){return r??new Error(`IndexedDB ${e} failed without an error detail.`)}function QN(r,e){let t=async a=>{Se(e);let c=await a();return Se(e),c},n=async function*(a){for await(let c of a)Se(e),yield c;Se(e)},i=r.uploadFile?.bind(r),s=r.moveFile?.bind(r),o=r.deleteFile?.bind(r);return{openSession:()=>t(()=>r.openSession()),snapshot:(a,c)=>t(()=>r.snapshot(a,c)),fileSnapshot:(a,c)=>t(()=>r.fileSnapshot(a,c)),downloadFile:a=>n(r.downloadFile(a)),...i?{uploadFile:(a,c)=>t(()=>i(a,n(c)))}:{},...s?{moveFile:a=>t(()=>s(a))}:{},...o?{deleteFile:a=>t(()=>o(a))}:{},changes:(a,c)=>t(()=>r.changes(a,c)),mutate:a=>t(()=>r.mutate(a))}}async function rs(r,e){let t=(0,te.normalizePath)(e).replace(/\/+$/,"");if(!t)return;let n="";for(let i of t.split("/")){n=n?`${n}/${i}`:i;let s=r.getAbstractFileByPath(n);if(!(s instanceof te.TFolder)){if(s)throw new b("mirror_path_collision",`A file blocks the mirror folder ${n}.`);await r.adapter.exists(n)||await r.createFolder(n)}}}var Jp=class{constructor(e,t=i=>e.delete(i,!0),n=()=>{}){this.vault=e;this.trashFile=t;this.assertActive=n}async exists(e){let t=ni(this.vault,e);return this.vault.getAbstractFileByPath(t)!==null||await this.vault.adapter.exists(t)}async read(e){let t=await this.readText(e);if(t===null||typeof t=="string")return t;throw new b(t.code,t.reason)}async readText(e){let t=ni(this.vault,e);if(this.vault.getAbstractFileByPath(t)instanceof te.TFolder)throw new b("mirror_path_collision",`Expected a file at ${t}.`);let i;try{i=await this.vault.adapter.readBinary(t)}catch{try{if(!await this.vault.adapter.exists(t))return null}catch{}throw new b("file_read_failed",`Could not read ${t}.`)}try{return new TextDecoder("utf-8",{fatal:!0}).decode(i)}catch{return{kind:"invalid",code:"invalid_utf8",reason:"File is not valid UTF-8.",revision:(await Hp(i)).content_digest}}}async write(e,t){let n=ni(this.vault,e),i=n.lastIndexOf("/");i>=0&&await rs(this.vault,n.slice(0,i));let s=this.vault.getAbstractFileByPath(n);if(s instanceof te.TFolder)throw new b("mirror_path_collision",`A folder blocks the mirror file ${n}.`);this.assertActive(),s instanceof te.TFile?await this.vault.modify(s,t):await this.vault.create(n,t)}async move(e,t){let n=ni(this.vault,e),i=ni(this.vault,t),s=this.vault.getAbstractFileByPath(n);if(!(s instanceof te.TFile))throw new b("mirror_path_collision",`Expected a file at ${n}.`);if(this.vault.getAbstractFileByPath(i)!==null||await this.vault.adapter.exists(i))throw new b("mirror_path_collision",`A file or folder blocks the mirror path ${i}.`);let o=i.lastIndexOf("/");o>=0&&await rs(this.vault,i.slice(0,o)),this.assertActive(),await this.vault.rename(s,i)}async remove(e){let t=ni(this.vault,e),n=this.vault.getAbstractFileByPath(t);if(n!=null){if(!(n instanceof te.TFile))throw new b("mirror_path_collision",`Expected a file at ${t}.`);this.assertActive(),await this.trashFile(n)}}async listMarkdown(e){return this.vault.getMarkdownFiles().map(t=>(0,te.normalizePath)(t.path)).filter(t=>!e.has(t)).filter(t=>!Y0(this.vault).some(n=>t===n||t.startsWith(`${n}/`))).sort()}async inspectBinary(e){let t=Mo(e),n=this.vault.getAbstractFileByPath(t);if(n==null)return null;if(!(n instanceof te.TFile))throw new b("mirror_path_collision",`Expected a file at ${t}.`);ii(n.stat.size);let i=await this.vault.readBinary(n);return ii(i.byteLength),Hp(i)}async writeBinary(e,t){let n=Mo(e),i=await Kp(t),s=n.lastIndexOf("/");s>=0&&await rs(this.vault,n.slice(0,s));let o=this.vault.getAbstractFileByPath(n);if(o instanceof te.TFolder)throw new b("mirror_path_collision",`A folder blocks the mirror file ${n}.`);this.assertActive(),o instanceof te.TFile?await this.vault.modifyBinary(o,i):await this.vault.createBinary(n,i)}async listBinary(e){return X0(this.vault).map(t=>(0,te.normalizePath)(t.path)).filter(t=>!/\.md$/i.test(t)&&!e.has(t)).filter(t=>{try{return Mo(t),!0}catch{return!1}}).sort()}async readBinary(e){let t=Mo(e),n=this.vault.getAbstractFileByPath(t);if(n==null)return null;if(!(n instanceof te.TFile))throw new b("mirror_path_collision",`Expected a file at ${t}.`);ii(n.stat.size);let i=new Uint8Array(await this.vault.readBinary(n));return ii(i.byteLength),(async function*(){for(let s=0;s{}),a}n&&n.stage!==i&&await this.removeStage(n.stage,n.chunks).catch(()=>{})}async remove(e){let t=await this.manifest(e);await this.delete($n,this.manifestKey(e)),t&&await this.removeStage(t.stage,t.chunks)}async prune(e){let t=await this.open(),n=await new Promise((o,a)=>{let c=[],l=t.transaction($n,"readonly").objectStore($n).openCursor();l.onsuccess=()=>{let u=l.result;if(!u)return o(c);c.push([u.key,u.value]),u.continue()},l.onerror=()=>a(vt(l.error,"manifest cursor"))}),i=new Set;for(let[o,a]of n)!Array.isArray(o)||o[0]!==this.namespace||typeof o[1]=="string"&&e.has(o[1])||(await this.delete($n,o),await this.removeStage(a.stage,a.chunks));for(let[o,a]of n)Array.isArray(o)&&o[0]===this.namespace&&typeof o[1]=="string"&&e.has(o[1])&&i.add(a.stage);let s=await new Promise((o,a)=>{let c=[],l=t.transaction(En,"readonly").objectStore(En).openKeyCursor();l.onsuccess=()=>{let u=l.result;if(!u)return o(c);let d=u.key,f=Array.isArray(d)&&typeof d[1]=="string"?d[1]:null;Array.isArray(d)&&d[0]===this.namespace&&(f===null||!i.has(f))&&c.push(d),u.continue()},l.onerror=()=>a(vt(l.error,"chunk cursor"))});for(let o of s)await this.delete(En,o)}manifest(e){return this.get($n,this.manifestKey(e))}manifestKey(e){return[this.namespace,e]}chunkKey(e,t){return[this.namespace,e,t]}async removeStage(e,t){for(let n=0;n{let o=n.transaction(e,"readonly").objectStore(e).get(t);o.onsuccess=()=>i(o.result??null),o.onerror=()=>s(vt(o.error,`read from ${e}`))})}async put(e,t,n){let i=await this.open();await new Promise((s,o)=>{let a=i.transaction(e,"readwrite");a.objectStore(e).put(n,t),a.oncomplete=()=>s(),a.onerror=()=>o(vt(a.error,`write to ${e}`)),a.onabort=()=>o(vt(a.error,`write to ${e}`))})}async delete(e,t){let n=await this.open();await new Promise((i,s)=>{let o=n.transaction(e,"readwrite");o.objectStore(e).delete(t),o.oncomplete=()=>i(),o.onerror=()=>s(vt(o.error,`delete from ${e}`)),o.onabort=()=>s(vt(o.error,`delete from ${e}`))})}close(){this.database?.then(e=>e.close(),()=>{}),this.database=null}open(){if(typeof indexedDB>"u")throw new b("storage_unavailable","IndexedDB is required for binary file sync.");return this.database??=new Promise((e,t)=>{let n=indexedDB.open(DN,1);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains($n)||n.result.createObjectStore($n),n.result.objectStoreNames.contains(En)||n.result.createObjectStore(En)},n.onerror=()=>t(vt(n.error,"binary store open")),n.onsuccess=()=>e(n.result)}),this.database}},Xp=class{constructor(e){this.key=e;this.database=null}async read(){let e=await this.open();return new Promise((t,n)=>{let i=e.transaction(Sn,"readonly").objectStore(Sn).get(this.key);i.onsuccess=()=>t(i.result??null),i.onerror=()=>n(vt(i.error,"mirror state read"))})}async write(e){let t=await this.open();await new Promise((n,i)=>{let s=t.transaction(Sn,"readwrite");s.objectStore(Sn).put(e,this.key),s.oncomplete=()=>n(),s.onerror=()=>i(vt(s.error,"mirror state write")),s.onabort=()=>i(vt(s.error,"mirror state write"))})}async clear(){let e=await this.open();await new Promise((t,n)=>{let i=e.transaction(Sn,"readwrite");i.objectStore(Sn).delete(this.key),i.oncomplete=()=>t(),i.onerror=()=>n(vt(i.error,"mirror state clear")),i.onabort=()=>n(vt(i.error,"mirror state clear"))})}close(){this.database?.then(e=>e.close(),()=>{}),this.database=null}open(){if(typeof indexedDB>"u")throw new b("storage_unavailable","IndexedDB is required for persistent mirror state.");return this.database??=new Promise((e,t)=>{let n=indexedDB.open(NN,1);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains(Sn)||n.result.createObjectStore(Sn)},n.onerror=()=>t(vt(n.error,"mirror state store open")),n.onsuccess=()=>e(n.result)}),this.database}},Qp=class r{constructor(e){this.key=e}static{this.active=new Set}async runExclusive(e){if(r.active.has(this.key))throw new b("mirror_busy","A mirror operation is already running for this vault.");r.active.add(this.key);try{return await e()}finally{r.active.delete(this.key)}}},ul=class{constructor(e,t,n={}){this.app=e;this.settingsHost=t;this.options=n;this.disposed=!1;this.lifetime=new AbortController;this.stateStores=new Map;this.blobStores=new Map;this.progress=null;this.fileProgress=null;this.syncAbort=null;this.statusRequest=null;this.mirrorOperationTail=Promise.resolve();this.adoptionMarker=null;this.fileSystem=n.fileSystem??new Jp(e.vault,i=>e.fileManager.trashFile(i),()=>this.assertActive()),this.enrollmentClient=n.enrollmentClient??new Bc({request:WN()}),this.adoptionClient=n.adoptionClient??new Gc({request:GN()})}dispose(){this.disposed=!0,this.lifetime.abort(),this.cancelSync();for(let e of this.stateStores.values())e.close();for(let e of this.blobStores.values())e.close();this.stateStores.clear(),this.blobStores.clear()}async withLifetime(e,t){this.assertActive();let n=new AbortController,i=()=>n.abort();this.lifetime.signal.addEventListener("abort",i,{once:!0}),e?.addEventListener("abort",i,{once:!0}),e?.aborted&&n.abort();try{return Se(n.signal),await t(n.signal)}finally{this.lifetime.signal.removeEventListener("abort",i),e?.removeEventListener("abort",i)}}assertActive(){if(this.disposed)throw new DOMException("Plugin unloaded.","AbortError")}async initialize(){this.adoptionMarker=await this.readAdoptionMarker();let e=this.settingsHost.getMirrorProfile();if(this.adoptionMarker&&e){if(this.adoptionMarker.phase==="adopted"&&this.adoptionMarker.session.requested.collectionId===e.collectionId){await this.assertMirror(e.collectionId),await this.clearAdoptionCheckpoint(this.adoptionMarker.session.adoptionId);return}throw new b("authority_adoption_state_conflict","This vault contains both an authority-adoption checkpoint and a mirror profile.")}}getProgress(){return this.progress?{...this.progress}:null}getFileProgress(){return this.fileProgress?{...this.fileProgress}:null}getAdoptionMarker(){return this.adoptionMarker?JSON.parse(JSON.stringify(this.adoptionMarker)):null}getSelectiveSync(){return xr(this.settingsHost.getMirrorProfile()?.selectiveSync??this.adoptionMarker?.selective_sync)}async configureSelectiveSync(e){let t=this.requireProfile();await this.settingsHost.saveMirrorProfile({...t,selectiveSync:xr(e)})}assertLocalAuthorityWritable(){if(this.assertActive(),this.adoptionMarker&&["fenced","activating","adopted"].includes(this.adoptionMarker.phase))throw new b("local_authority_fenced",this.adoptionMarker.phase==="adopted"?"Hosted mdbase is now authoritative. Finish reconnecting this vault as its mirror before editing.":"This local authority is frozen while its exact snapshot is adopted by hosted mdbase.")}async adoptLocalCollection(e,t){return this.withLifetime(t.signal,n=>this.adoptLocalCollectionActive(e,{...t,signal:n}))}async adoptLocalCollectionActive(e,t){if(this.settingsHost.getMirrorProfile())throw new b("mirror_already_configured","This vault already mirrors a collection authority.");if(this.adoptionMarker)return this.resumeAdoption(t);let n=await this.ensurePortableCollectionIdentity(),i=await this.adoptionClient.begin({controlUrl:e.controlUrl,collectionId:n.collectionId,displayName:n.displayName,sourceName:e.mirrorName,retainMirror:!0,mirrorName:e.mirrorName},t);return await this.storeAdoptionSecret(i),await this.writeAdoptionMarker({version:1,phase:"waiting_for_approval",session:zp(i),selective_sync:xr(e.selectiveSync),manifest_digest:null,source_revision:null,source_head:null}),await t.onVerification(zp(i)),this.runAdoptionWithRecovery(i,t)}async resumeAdoption(e={}){let t=this.adoptionMarker??await this.readAdoptionMarker();if(!t)throw new b("authority_adoption_not_found","This vault has no collection-adoption checkpoint.");this.adoptionMarker=t;let n=this.app.secretStorage.getSecret(this.adoptionSecretId(t.session.adoptionId));if(!n)throw new b("authority_adoption_credentials_missing","The collection-adoption credential is missing from Obsidian's secret store.");let i={...t.session,credential:n};return t.phase==="waiting_for_approval"&&await e.onVerification?.(zp(i)),this.withLifetime(e.signal,s=>this.runAdoptionWithRecovery(i,{...e,signal:s,onVerification:o=>e.onVerification?.(o)}))}async cancelAdoption(e){return this.withLifetime(e,t=>this.cancelAdoptionActive(t))}async cancelAdoptionActive(e){let t=this.adoptionMarker??await this.readAdoptionMarker();if(!t)return;if(["activating","adopted"].includes(t.phase))throw new b("authority_adoption_activation_started","Hosted activation has started and must be resumed; it can no longer be cancelled.");let n=this.app.secretStorage.getSecret(this.adoptionSecretId(t.session.adoptionId));if(!n)throw new b("authority_adoption_credentials_missing","The collection-adoption credential is missing from Obsidian's secret store.");await this.adoptionClient.cancel({...t.session,credential:n},{signal:e}),await this.clearAdoptionCheckpoint(t.session.adoptionId)}async enroll(e,t){return this.withLifetime(t.signal,n=>this.enrollActive(e,{...t,signal:n}))}async enrollActive(e,t){let n=await this.assertCanBecomeMirror(e.collectionId),i=await this.enrollmentClient.enroll({controlUrl:e.controlUrl,mirrorName:e.mirrorName,mode:e.mode,...n?{collectionId:n}:{}},t),s=await this.markMirror(i.collectionId);try{await this.persistEnrollment(i,e.selectiveSync)}catch(o){if(s)try{await this.app.vault.adapter.remove(ts)}catch{throw new b("enrollment_recovery_required",`Enrollment settings could not be saved and the temporary role marker could not be removed: ${o instanceof Error?o.message:String(o)}`)}throw o}return this.requireProfile()}async preview(){return this.withMirrorOperation(async()=>{let e=this.requireProfile();await this.assertMirror(e.collectionId);let t=await this.createMirror();return B0(await t.inspect())})}async status(){if(this.statusRequest)return this.statusRequest;let e=this.readStatus();this.statusRequest=e;try{return await e}finally{this.statusRequest===e&&(this.statusRequest=null)}}async readStatus(){return this.withMirrorOperation(async()=>{let e=this.settingsHost.getMirrorProfile();return e?(await this.assertMirror(e.collectionId),(await this.createMirror()).status()):null})}async reconnect(){let e=this.requireProfile(),t=this.app.secretStorage.getSecret(this.refreshSecretId(e.collectionId));if(!t)throw new b("mirror_credentials_missing","The mirror refresh credential is missing. Approve this vault again.");let n=await this.enrollmentClient.renew({controlUrl:e.controlUrl,syncUrl:e.syncUrl,collectionId:e.collectionId,replicaId:e.replicaId,mode:e.mode,name:e.name,enrollmentId:e.enrollmentId,accessToken:this.app.secretStorage.getSecret(this.accessSecretId(e.collectionId))??"",refreshCredential:t,accessTokenExpiresAt:e.accessTokenExpiresAt});await this.persistEnrollment(n,e.selectiveSync);let i=await this.status();if(!i)throw new b("mirror_not_configured","The renewed mirror profile could not be loaded.");return i}async reauthorize(e){return this.withLifetime(e.signal,t=>this.reauthorizeActive({...e,signal:t}))}async reauthorizeActive(e){let t=this.requireProfile(),n=this.stateStoreFor(t),i=await n.read();if(i?.batch)throw new b("mirror_recovery_required","Resume the durable synchronization checkpoint before approving this vault again.");let s=await this.enrollmentClient.enroll({controlUrl:t.controlUrl,mirrorName:t.name,mode:t.mode,collectionId:t.collectionId},e);if(s.collectionId!==t.collectionId)throw new b("mirror_identity_conflict","Connect approved a different collection. The existing mirror was not changed.");let o=K0(s,t.selectiveSync);i&&s.replicaId!==t.replicaId&&await this.stateStoreFor(o).write({...i,replica_id:s.replicaId}),await this.persistEnrollment(s,t.selectiveSync),s.replicaId!==t.replicaId&&"clear"in n&&typeof n.clear=="function"&&await n.clear();let a=await this.status();if(!a)throw new b("mirror_not_configured","The reauthorized mirror profile could not be loaded.");return a}async conflictComparison(e){let t=this.requireProfile(),n=await this.transportFor(t),i=await n.openSession(),s={state:"absent"};if(e.entity==="record"){let l;do{let u=await n.snapshot(i.snapshot_id,l),d=u.records.find(f=>f.record_id===e.object_id);if(d){s={state:"exact",path:d.path,revision:d.revision,size:new TextEncoder().encode(d.document).byteLength,document:d.document};break}l=u.next_page}while(l)}else{let l;do{let u=await n.fileSnapshot(i.snapshot_id,l),d=u.files.find(f=>f.file_id===e.object_id);if(d){s={state:"exact",path:d.path,revision:d.content_digest,size:d.size,modifiedAt:d.modified_at};break}l=u.next_page}while(l)}let o=e.path??s.path,a={state:"absent"};if(o){let l=this.app.vault.getAbstractFileByPath(o);if(l instanceof te.TFile)if(e.entity==="record"){let u=await this.app.vault.cachedRead(l);a={state:"exact",path:o,size:new TextEncoder().encode(u).byteLength,modifiedAt:l.stat?.mtime?new Date(l.stat.mtime).toISOString():void 0,document:u}}else{let u=await this.fileSystem.inspectBinary(o);u&&(a={state:"exact",path:o,revision:u.content_digest,size:u.size,modifiedAt:l.stat?.mtime?new Date(l.stat.mtime).toISOString():void 0,resourceUrl:this.app.vault.getResourcePath(l)})}}if(!(await this.status())?.conflicts.some(l=>l.object_id===e.object_id&&l.decision_id===e.decision_id))throw new b("conflict_decision_stale","This file changed again while its versions were loading.");return{entity:e.entity,objectId:e.object_id,decisionId:e.decision_id,local:a,remote:s}}async preserveConflictCopy(e){let t=ni(this.app.vault,e),n=this.app.vault.getAbstractFileByPath(t);if(!(n instanceof te.TFile))throw new b("mirror_conflict_copy_missing",`No local file exists at ${t}.`);let i=n.extension?`.${n.extension}`:"",s=i?t.slice(0,-i.length):t,o=`${s} (local conflict copy)${i}`,a=2;for(;this.app.vault.getAbstractFileByPath(o)||await this.app.vault.adapter.exists(o);)o=`${s} (local conflict copy ${a})${i}`,a+=1;return await this.app.vault.adapter.copy(t,o),o}async disconnect(e){if(this.isSyncing())throw new b("mirror_busy","Stop the current synchronization before disconnecting.");let t=this.requireProfile(),n=this.stateStoreFor(t),i=await n.read();if(i?.batch)throw new b("mirror_recovery_required","Resume the durable synchronization checkpoint before disconnecting.");let s={removed:[],preserved:[]},o=await this.readMarker();o&&await this.app.vault.adapter.remove(ts);try{await this.settingsHost.saveMirrorProfile(null)}catch(a){throw o&&await this.markMirror(t.collectionId),a}return this.app.secretStorage.setSecret(this.accessSecretId(t.collectionId),""),this.app.secretStorage.setSecret(this.refreshSecretId(t.collectionId),""),"clear"in n&&typeof n.clear=="function"&&await n.clear(),await this.blobStoreFor(t).prune(new Set),e&&i&&await this.removeExactMirrorFiles(i,s),s}async sync(e,t,n){if(this.syncAbort)throw new b("mirror_busy","Synchronization is already running for this vault.");let i=new AbortController;this.syncAbort=i;try{return await this.withMirrorOperation(async()=>{let o=await(await this.createMirror(a=>{Se(i.signal),this.progress=a,t?.({...a})},i.signal,a=>{Se(i.signal),this.fileProgress=a,n?.({...a})})).apply(e.plan,{signal:i.signal});return Se(i.signal),o})}finally{this.progress=null,this.fileProgress=null,this.syncAbort=null}}cancelSync(){this.syncAbort?.abort()}isSyncing(){return this.syncAbort!==null}async resolveConflict(e,t,n){return this.withMirrorOperation(async()=>{let i=await this.createMirror();return await i.resolveConflict(e,t,n),i.status()})}async withMirrorOperation(e){let t=this.mirrorOperationTail,n;this.mirrorOperationTail=new Promise(i=>{n=i}),await t;try{return this.assertActive(),await e()}finally{n()}}async runAdoption(e,t){let n=this.requireAdoptionMarker(e.adoptionId),i=null;if(n.phase==="adopted"){let s=await this.adoptionClient.exchange(e,t);if(s.status!=="completed")throw new b("authority_adoption_state_conflict","The local checkpoint says adoption completed, but Connect does not.");i=s}else if(n.phase==="activating"){let s=await this.readAdoptionSnapshot(n),o=await this.adoptionClient.exchange(e,t);i=o.status==="completed"?o:await this.adoptionClient.complete(e,s,t)}else if(n.phase==="fenced"){let s=await this.readAdoptionSnapshot(n),o=await this.adoptionClient.exchange(e,t);o.status==="completed"?i=o:(o.status==="ready"&&await this.adoptionClient.uploadSnapshot(e,o,s,this.adoptionUploadOptions(e,t)),await this.updateAdoptionPhase("activating",s),i=await this.adoptionClient.complete(e,s,t))}else{let s=n.phase==="waiting_for_approval"?await this.adoptionClient.waitForApproval(e,t):await this.requirePreparedAdoption(e,t),o=await this.captureAuthoritySnapshot(e.requested.collectionId,t.signal);await this.updateAdoptionPhase("uploading"),await this.adoptionClient.uploadSnapshot(e,s,o,this.adoptionUploadOptions(e,t));let a=await this.captureAuthoritySnapshot(e.requested.collectionId,t.signal);await this.writeAdoptionSnapshot(a),await this.updateAdoptionPhase("fenced",a);let c=await this.requirePreparedAdoption(e,t);await this.adoptionClient.uploadSnapshot(e,c,a,this.adoptionUploadOptions(e,t)),await this.updateAdoptionPhase("activating",a),i=await this.adoptionClient.complete(e,a,t)}return await this.updateAdoptionPhase("adopted"),this.finishRetainedMirror(e,i,t)}async runAdoptionWithRecovery(e,t){try{return await this.runAdoption(e,t)}catch(n){throw ZN(n)?(await this.adoptionClient.cancel(e,{signal:t.signal}).catch(()=>{}),await this.clearAdoptionCheckpoint(e.adoptionId),new b(n.code,"This adoption ended before hosted activation. The vault remains the writable local authority; start a new adoption to try again.")):n}}async requirePreparedAdoption(e,t){let n=await this.adoptionClient.exchange(e,t);if(n.status==="ready")return n;throw n.status==="activating"?new qr("Hosted authority activation has already started. Resume using the saved fenced snapshot."):new b("authority_adoption_already_completed","Hosted authority has already adopted this collection.")}async finishRetainedMirror(e,t,n){let i,s=this.adoptionClient.mirrorEnrollmentSession(e,t);if(!s)throw new b("authority_adoption_mirror_missing","Hosted authority activated without retaining this vault as a mirror.");try{i=await this.enrollmentClient.waitForApproval(s,{signal:n.signal,onStatus:a=>n.onStatus?.({...a,state:a.state})})}catch(a){if(n.signal?.aborted)throw a;i=await this.enrollmentClient.enroll({controlUrl:e.controlUrl,collectionId:e.requested.collectionId,mirrorName:e.requested.mirrorName??e.requested.sourceName,mode:"read_write"},{signal:n.signal,onVerification:c=>n.onVerification(c)})}let o=await this.markMirror(i.collectionId);try{await this.persistEnrollment(i,this.adoptionMarker?.selective_sync)}catch(a){throw o&&await this.app.vault.adapter.remove(ts),a}return await this.clearAdoptionCheckpoint(e.adoptionId),this.requireProfile()}async captureAuthoritySnapshot(e,t){Se(t);let n=await Ii(this.app.vault);if(Se(t),!n)throw new b("invalid_collection_configuration","A valid mdbase.yaml is required.");let i=await this.app.vault.adapter.read("mdbase.yaml");Se(t);let s=(0,te.parseYaml)(i),o=[{path:"mdbase.yaml",kind:"configuration",document:i}],a=`${(0,te.normalizePath)(n.settings.types_folder)}/`,c=this.app.vault.getMarkdownFiles().filter(p=>(0,te.normalizePath)(p.path).startsWith(a)).sort((p,m)=>p.path.localeCompare(m.path));for(let p of c)Se(t),o.push({path:(0,te.normalizePath)(p.path),kind:"type",document:await this.app.vault.cachedRead(p)}),Se(t);let l=eD(s);if(l.length){let p=l.map(h=>(0,W0.default)(h,{dot:!0})),m=X0(this.app.vault).filter(h=>h.extension==="base").filter(h=>p.some(y=>y((0,te.normalizePath)(h.path)))).sort((h,y)=>h.path.localeCompare(y.path));for(let h of m)Se(t),o.push({path:(0,te.normalizePath)(h.path),kind:"view",document:await this.app.vault.cachedRead(h)}),Se(t)}let u=[];for(let p of this.app.vault.getMarkdownFiles().sort((m,h)=>m.path.localeCompare(h.path))){Se(t);let m=(0,te.normalizePath)(p.path);if(Ti(m,n))continue;let h=await this.app.vault.cachedRead(p);Se(t),u.push({path:m,document:h})}let d=[],f=xr(this.adoptionMarker?.selective_sync);if(f.file_classes.length){let p=this.adoptionBlobStore(e),m=(await this.fileSystem.listBinary?.(new Set(o.map(h=>h.path)))??[]).filter(h=>zN(f,h)).filter(h=>!Ti(h,n));for(let h of m){Se(t);let y=await this.fileSystem.readBinary?.(h);if(Se(t),!y)continue;let g=await Kp(y);Se(t);let _=await Hp(g);Se(t),await p.write(_.content_digest,(async function*(){yield new Uint8Array(g)})()),Se(t);let v=this.app.vault.getAbstractFileByPath(h);d.push({file_id:Wc(e,`file:${h}`),path:h,revision:_.content_digest,..._,...V0(h)?{media_type:V0(h)}:{},media_class:G0(h),modified_at:new Date(v instanceof te.TFile&&v.stat?.mtime?v.stat.mtime:Date.now()).toISOString()})}}return Se(t),Ap({collectionId:e,sourceHead:0,specVersion:n.spec_version,resources:o,records:u,files:d})}adoptionBlobStore(e){return this.options.adoptionBlobStoreFactory?.(e)??this.cachedBlobStore(`adoption:${e}`)}adoptionUploadOptions(e,t){let n=this.adoptionBlobStore(e.requested.collectionId);return{signal:t.signal,fileSource:async i=>Kp(n.read(i.content_digest)),onFileProgress:({file:i,transferredBytes:s,totalBytes:o})=>t.onFileProgress?.(i.path,s,o)}}async ensurePortableCollectionIdentity(){if(!await this.app.vault.adapter.exists("mdbase.yaml"))throw new b("collection_not_initialized","Initialize an mdbase collection before hosting it.");let e=await this.app.vault.adapter.read("mdbase.yaml"),t;try{t=(0,te.parseYaml)(e)}catch{throw new b("invalid_collection_configuration","mdbase.yaml must contain valid YAML.")}if(!ot(t))throw new b("invalid_collection_configuration","mdbase.yaml must contain a YAML mapping.");let n=ot(t["x-mdbase-connect"])?t["x-mdbase-connect"].collection_id:void 0,i;if(n===void 0){i=crypto.randomUUID();let o=ot(t["x-mdbase-connect"])?t["x-mdbase-connect"]:{};t["x-mdbase-connect"]={...o,collection_id:i},this.assertActive(),await this.app.vault.adapter.write("mdbase.yaml",(0,te.stringifyYaml)(t))}else if(typeof n=="string"&&Io.test(n))i=n;else throw new b("invalid_collection_configuration","x-mdbase-connect.collection_id must be a UUID string.");let s=typeof t.name=="string"&&t.name.trim()?t.name.trim():this.app.vault.getName();return{collectionId:i,displayName:s}}stateStoreFor(e){if(this.assertActive(),this.options.stateStoreFactory)return this.options.stateStoreFactory(e);let t=`${e.collectionId}:${e.replicaId}`,n=this.stateStores.get(t);return n||(n=new Xp(t),this.stateStores.set(t,n)),n}cachedBlobStore(e){this.assertActive();let t=this.blobStores.get(e);return t||(t=new Yp(e),this.blobStores.set(e,t)),t}blobStoreFor(e){return this.options.blobStoreFactory?.(e)??this.cachedBlobStore(`${e.collectionId}:${e.replicaId}`)}async transportFor(e,t,n){this.assertActive();let i=await this.freshAccessToken(e);this.assertActive();let s=this.options.transportFactory?.(e,i)??new Wp(e.syncUrl,i,fl,n);return QN(s,t)}async createMirror(e,t,n){let i=this.requireProfile();await this.assertMirror(i.collectionId);let s=await this.transportFor(i,t,n),o={stateStore:this.stateStoreFor(i),fileSystem:this.fileSystem,blobStore:this.blobStoreFor(i),selectiveSync:xr(i.selectiveSync),lease:this.options.leaseFactory?.(i)??new Qp(`${i.collectionId}:${i.replicaId}`),onProgress:e};return i.mode==="read_write"?new ko(i.replicaId,s,o):new ri(i.replicaId,s,o)}requireProfile(){this.assertActive();let e=this.settingsHost.getMirrorProfile();if(!e)throw new b("mirror_not_configured","This vault is not connected to a collection authority.");return e}accessSecretId(e){return`${jN}${e.toLowerCase()}`}refreshSecretId(e){return`${qN}${e.toLowerCase()}`}adoptionSecretId(e){return`${FN}${e.toLowerCase()}`}async storeAdoptionSecret(e){this.app.secretStorage.setSecret(this.adoptionSecretId(e.adoptionId),e.credential)}async persistEnrollment(e,t){this.assertActive(),this.app.secretStorage.setSecret(this.accessSecretId(e.collectionId),e.accessToken),this.app.secretStorage.setSecret(this.refreshSecretId(e.collectionId),e.refreshCredential),await this.settingsHost.saveMirrorProfile(K0(e,t??this.settingsHost.getMirrorProfile()?.selectiveSync))}async removeExactMirrorFiles(e,t){let n=new Map;for(let[i,s]of Object.entries(e.records)){let o=e.local_bindings?.[i]?.path??s.path;n.set(o,{entity:"document",document:s.record?.document,revision:s.revision})}for(let[i,s]of Object.entries(e.resources??{})){let o=e.local_bindings?.[i]?.path??s.path;n.set(o,{entity:"document",document:s.record?.document,revision:s.revision})}for(let[i,s]of Object.entries(e.files??{})){let o=e.local_bindings?.[i]?.path??s.file.path;n.set(o,{entity:"file",digest:s.file.content_digest,size:s.file.size})}for(let[i,s]of[...n.entries()].sort(([o],[a])=>a.localeCompare(o))){let o=!1;if(s.entity==="document"){let a=await this.fileSystem.read(i);o=a!==null&&(s.document!==void 0?a===s.document:await nD(a,s.revision))}else if(s.entity==="file"){let a=await this.fileSystem.inspectBinary(i);o=a!==null&&a.content_digest===s.digest&&a.size===s.size}if(!o){await this.fileSystem.exists(i)&&t.preserved.push(i);continue}try{await this.fileSystem.remove(i),t.removed.push(i)}catch{t.preserved.push(i)}}}async freshAccessToken(e){let t=this.accessSecretId(e.collectionId),n=this.app.secretStorage.getSecret(t),i=Date.parse(e.accessTokenExpiresAt);if(n&&Number.isFinite(i)&&i-Date.now()>UN)return n;let s=this.app.secretStorage.getSecret(this.refreshSecretId(e.collectionId));if(!s)throw new b("mirror_credentials_missing","The mirror refresh credential is missing. Re-enroll this vault.");let o=await this.enrollmentClient.renew({controlUrl:e.controlUrl,syncUrl:e.syncUrl,collectionId:e.collectionId,replicaId:e.replicaId,mode:e.mode,name:e.name,enrollmentId:e.enrollmentId,accessToken:n??"",refreshCredential:s,accessTokenExpiresAt:e.accessTokenExpiresAt});return await this.persistEnrollment(o,e.selectiveSync),o.accessToken}async assertCanBecomeMirror(e){let t=await this.readMarker(),n=await this.readPortableCollectionId();if(n&&!t)throw new b("local_authority_requires_transfer","This vault has a local Connect identity. Transfer authority explicitly before using it as a mirror.");if(n&&t?.collection_id!==n)throw new b("mirror_identity_conflict","The vault identity and mirror role marker identify different collections.");let i=this.settingsHost.getMirrorProfile(),s=i?.collectionId??e;if(t&&s&&t.collection_id!==s)throw new b("mirror_identity_conflict","This vault is already marked as a different mirror.");if(!t&&!i&&await this.app.vault.adapter.exists("mdbase.yaml"))throw new b("existing_collection_requires_transfer","This vault already contains an mdbase collection. Connect an empty vault, or transfer collection authority explicitly.");return s??t?.collection_id}async readPortableCollectionId(){if(!await this.app.vault.adapter.exists("mdbase.yaml"))return null;let e;try{e=(0,te.parseYaml)(await this.app.vault.adapter.read("mdbase.yaml"))}catch{throw new b("invalid_collection_configuration","mdbase.yaml must contain valid YAML.")}if(!ot(e))throw new b("invalid_collection_configuration","mdbase.yaml must contain a YAML mapping.");let t=e["x-mdbase-connect"];if(t===void 0)return null;if(!ot(t))throw new b("invalid_collection_configuration","x-mdbase-connect must be a YAML mapping.");let n=t.collection_id;if(n===void 0)return null;if(typeof n!="string"||!Io.test(n))throw new b("invalid_collection_configuration","x-mdbase-connect.collection_id must be a UUID string.");return n}async markMirror(e){this.assertActive();let t=await this.readMarker();if(t){if(t.collection_id!==e)throw new b("mirror_identity_conflict","This vault already mirrors a different collection authority.");return!1}return await rs(this.app.vault,".mdbase"),this.assertActive(),await this.app.vault.adapter.write(ts,`${JSON.stringify({version:1,role:"mirror",collection_id:e},null,2)} +`)&&e.slice(1)===r}function Tp(r,e){if(r===e)return!0;if(Array.isArray(r)||Array.isArray(e))return Array.isArray(r)&&Array.isArray(e)&&r.length===e.length&&r.every((i,s)=>Tp(i,e[s]));if(!r||!e||typeof r!="object"||typeof e!="object")return!1;let t=Object.entries(r),n=e;return t.length===Object.keys(n).length&&t.every(([i,s])=>Object.prototype.hasOwnProperty.call(n,i)&&Tp(s,n[i]))}var es=class{fileSystem;runtime;mode;blobStore;constructor(e,t,n,i){this.fileSystem=e,this.runtime=t,this.mode=n,this.blobStore=i}async recordPathPolicy(e){return p0(Object.keys(e.resources??{}),()=>this.fileSystem.read("mdbase.yaml"))}async put(e,t,n={}){let{managedState:i=e,acceptedHash:s,materialized:o,inspectionPreflighted:a}=n;a||_n(t.path,await this.recordPathPolicy(e)),o===void 0&&!a&&y0(t.path,t.record_id,Object.keys(e.resources??{}),Object.entries(e.records),Object.entries(e.files??{}));let c=o?.document??Dc(t),l=a===1?null:await this.fileSystem.read(t.path),u=i?.records[t.record_id],d=l===null?null:this.runtime.digest(l);if(d!==null&&l!==c&&!(u!==void 0&&u.path===t.path&&d===u.hash)&&(!s||d!==s))throw new bt(t.record_id,t.path);u&&u.path!==t.path&&await this.remove(i,t.record_id,u.path);let f=this.runtime.digest(c);s===f&&d===f||await this.fileSystem.write(t.path,c),e.records[t.record_id]={path:t.path,revision:t.revision,hash:o?.hash??f,...this.mode==="read_write"?{record:t}:{}}}async putFile(e,t,n=e,i){if(gt(t),!this.blobStore)throw new b("file_storage_unavailable","Selected collection files require a content-addressed blob store adapter.");g0(t.path,t.file_id,e),e.files??={};let s=n.files?.[t.file_id],o=await this.fileSystem.inspectBinary(t.path),a=Et(t.path),c=Object.values(n.files??{}).find(m=>Et(m.file.path)===a),l=[...Object.values(n.resources??{}),...Object.values(n.records)].find(m=>Et(m.path)===a),u=l?await this.fileSystem.read(t.path):null,d=s?.file.path===t.path&&Br(o,s.file),f=c!==void 0&&Br(o,c.file),p=l!==void 0&&u!==null&&this.runtime.digest(u)===l.hash;if(o!==null&&!Br(o,t)&&!d&&!f&&!p&&!(i!==void 0&&o?.size===i.size&&o.content_digest===i.content_digest))throw new bt(t.file_id,t.path);if(s&&s.file.path!==t.path){let m=await this.fileSystem.inspectBinary(s.file.path);if(m!==null&&!Br(m,s.file))throw new bt(t.file_id,s.file.path)}Br(o,t)||await this.fileSystem.writeBinary(t.path,Vc(this.blobStore.read(t.content_digest),t)),s&&s.file.path!==t.path&&await this.fileSystem.remove(s.file.path),e.files[t.file_id]={file:t}}async removeFile(e,t){let n=e.files?.[t];if(!n)return;let i=await this.fileSystem.inspectBinary(n.file.path);if(i!==null&&!Br(i,n.file))throw new bt(t,n.file.path);i!==null&&await this.fileSystem.remove(n.file.path),delete e.files[t]}async remove(e,t,n,i={}){let s=e.records[t],o=s?.path??n;i.inspectionPreflighted||_n(o,await this.recordPathPolicy(e));let a=await this.fileSystem.read(o);if(a!==null&&s&&this.runtime.digest(a)!==s.hash)throw new bt(t,s.path);a!==null&&await this.fileSystem.remove(o),delete e.records[t]}async putResource(e,t,n){let i=await this.fileSystem.read(t.path),s=n?.resources?.[t.path];if(i!==null&&i!==t.document&&(!s||this.runtime.digest(i)!==s.hash))throw new bt(`resource:${t.path}`,t.path);await this.fileSystem.write(t.path,t.document),e.resources??={},e.resources[t.path]={path:t.path,revision:t.revision,hash:this.runtime.digest(t.document)}}async removeResource(e,t,n){let i=await this.fileSystem.read(t);if(i!==null&&this.runtime.digest(i)!==n.hash)throw new bt(`resource:${t}`,t);i!==null&&await this.fileSystem.remove(t),e.resources&&delete e.resources[t]}};async function v0(r,e,t,n){for(let i in r.records){if(!Object.hasOwn(r.records,i))continue;let s=r.records[i];_n(s.path,e);let o=await t.read(s.path);if(o===null||n(o)!==s.hash)throw new bt(i,s.path)}for(let[i,s]of Object.entries(r.resources??{})){let o=await t.read(s.path);if(o===null||n(o)!==s.hash)throw new bt(`resource:${i}`,s.path)}for(let[i,s]of Object.entries(r.files??{})){let o=await t.inspectBinary(s.file.path);if(!Br(o,s.file))throw new bt(i,s.file.path)}}async function _0(r){let{state:e,selectiveSync:t,fileSystem:n,pathPolicy:i,digest:s}=r;if(t.excluded_folders.length>0||t.file_classes.length!==5)throw new b("promotion_incomplete_file_projection","Moving the source of truth requires every collection file class with no excluded folders.");if(Object.keys(e.planned_conflicts??{}).length>0||e.batch!==void 0)throw new b("promotion_not_converged","Upload or resolve every local change before moving the source of truth.");await v0(e,i,n,s);let o=new Set(Object.keys(e.resources??{})),a=new Set(Object.values(e.records).map(d=>d.path)),c=(await n.listMarkdown(o)).filter(d=>!a.has(d));if(c.length>0)throw new b("promotion_unmanaged_files",`Synchronize unmanaged Markdown before promotion: ${c.join(", ")}.`);if(!n.listBinary)throw new b("promotion_file_scan_unavailable","Moving the source of truth requires binary file enumeration.");let l=new Set(Object.values(e.files??{}).map(d=>d.file.path)),u=(await n.listBinary(new Set([...o,...a]))).filter(d=>!l.has(d));if(u.length>0)throw new b("promotion_unmanaged_files",`Synchronize unmanaged files before moving the source of truth: ${u.join(", ")}.`);return{cursor:e.cursor,digest:jc([...Object.entries(e.resources??{}).map(([d,f])=>({kind:"resource",path:d,identity:"",document_hash:gp(f.hash)})),...Object.entries(e.records).map(([d,f])=>({kind:"record",path:f.path,identity:d,document_hash:gp(f.hash)})),...Object.values(e.files??{}).map(({file:d})=>({kind:"file",path:d.path,identity:d.file_id,document_hash:qc(d)}))])}}function wr(r,e,t,n,i){return{status:r,plan_fingerprint:e.fingerprint,applied:n,pending:t.pending,checkpoint_cursor:t.cursor,conflicts:t.conflicts.length,issues:e.issues,...i?{failure:i}:{}}}function w0(r,e){if(["planned","applying","cancelled","stale","blocked","failed"].includes(r.state))return r;let t=[...r.local_issues];for(let n of e.issues)n.path&&(n.code==="invalid_frontmatter"||n.code==="file_read_failed")&&!t.some(({path:i,code:s,message:o})=>i===n.path&&s===n.code&&o===n.message)&&t.push({path:n.path,code:n.code,message:n.message});return r.conflicts.length>0||t.length>0||e.summary.blocking_issues>0||e.summary.conflicts>0?{...r,state:"attention",local_issues:t}:{...r,state:e.actions.some(n=>n.command!=="advance_checkpoint")?"changes_waiting":"up_to_date"}}function rr(r,e){if(!r)return{state:"not_initialized",mode:e,pending:0,pending_files:0,conflicts:[],local_issues:[],cursor:null,last_synced_at:null};let t=[];for(let[o,a]of Object.entries(r.planned_conflicts??{})){let c=t.findIndex(({entity:u,object_id:d})=>u===a.entity&&d===o);c!==-1&&t.splice(c,1);let l=a.local.state==="exact"?a.local.object.path:a.remote.state==="exact"?a.remote.object.path:null;t.push({entity:a.entity,object_id:o,decision_id:a.decision_id??"",path:l,kind:a.conflict_kind==="rejected"?"rejected":"conflicted",message:a.conflict_kind==="rejected"?"The authority rejected this local change.":"Local and authority changes need a decision."})}let n=[],i=r.batch?r.batch.plan.actions.slice(r.batch.next_action).filter(o=>o.command!=="advance_checkpoint").length:0;return{state:(r.batch?r.batch.phase==="prepared"?"planned":r.batch.phase==="applying"||r.batch.phase==="effects_complete"?"applying":r.batch.phase==="cancelled"?"cancelled":r.batch.failure?.code==="sync_plan_stale"?"stale":"blocked":null)??(t.length?"attention":"up_to_date"),mode:e,pending:i,pending_files:r.batch?r.batch.plan.actions.slice(r.batch.next_action).filter(o=>o.command!=="advance_checkpoint"&&("target"in o&&o.target.entity==="file"||"source"in o&&o.source.entity==="file"||"entity"in o&&o.entity==="file")).length:0,conflicts:t,local_issues:n,cursor:r.batch?.checkpoint_before.cursor??r.cursor,last_synced_at:r.last_synced_at??null,generation:r.generation??0,pending_checkpoint:r.batch?.checkpoint_after.cursor??null,...r.batch?{plan_fingerprint:r.batch.plan.fingerprint}:{},...r.last_completed_plan?{last_completed_plan:r.last_completed_plan}:{},recovery_required:r.batch!==void 0,...r.batch?.failure?{failure:r.batch.failure}:{}}}async function sN(r,e,t){let n=await e.openSession();if(n.protocol_version!==1||n.protocol_profile!=="exact_document_v1"||n.replica_id!==r||n.mode!==t)throw new b("sync_protocol_incompatible",`Filesystem mirror requires exact-document v1 and its own ${t.replace("_","-")} replica.`);return n}async function Xc(r,e,t,n,i){let s=await sN(r,e,t),o=s.resources.documents??[],a=Jc(o),c=new Yc(a,o,i.digest),l=[];await b0(e,s,async f=>{for(let p of f){let m=c.validate(p);Xn(n,m.record.path)&&l.push(m)}});let u=[],d=new Set;return await Gw(e,s,async f=>{for(let p of f)if(zc(n,p)){if(d.has(p.file_id))throw new b("invalid_snapshot",`Hosted snapshot repeats file identity ${p.file_id}.`);d.add(p.file_id),u.push(p)}}),$o([...o.map(f=>f.path),...l.map(({record:f})=>f.path),...u.map(f=>f.path)]),{session:s,resources:o,records:l,files:u}}var Qc="exact_document_plan_only_v1",Zc="three_way_exact_document_v1",el="portable_mirror_projection_v1";function tl(r){return JSON.stringify(Rp(r))}function ei(r,e){return`sha256:${e(tl(r))}`}function Rp(r){if(r===null||typeof r=="string"||typeof r=="boolean")return r;if(typeof r=="number"){if(!Number.isSafeInteger(r))throw new b("invalid_sync_plan","Sync plans contain only safe integer numeric values.");return r}if(Array.isArray(r))return r.map(Rp);if(typeof r=="object"){let e={};for(let t of Object.keys(r).sort()){let n=r[t];if(n===void 0)throw new b("invalid_sync_plan",`Sync plan field ${t} is undefined rather than an explicit state.`);e[t]=Rp(n)}return e}throw new b("invalid_sync_plan","Sync plans contain only canonical I-JSON values.")}function A0(r,e,t){let n=new Map(r.base.map(u=>[u.identity,u])),i=new Map(r.remote.map(u=>[u.identity,u])),s=new Map,o=new Map(r.local.map(u=>[u.object.path,u]));for(let u of r.local)!u.stable_identity||u.object.identity===""||(s.set(u.object.identity,u.object),o.delete(u.object.path));for(let[u,d]of n){let f=o.get(d.path);f&&(s.set(u,{...f.object,identity:u}),o.delete(d.path))}for(let[u,d]of i){if(s.has(u))continue;let f=o.get(d.path);f&&f.object.entity===d.entity&&(s.set(u,{...f.object,identity:u}),o.delete(d.path))}for(let[u,d]of n){if(s.has(u))continue;let f=[...o.values()].filter(m=>m.object.entity===d.entity&&m.object.payload_revision===d.payload_revision);if(f.length!==1)continue;let p=f[0];s.set(u,{...p.object,identity:u,revision:p.object.payload_revision===d.payload_revision?d.revision:p.object.revision}),o.delete(p.object.path)}for(let u of o.values()){let d=u.stable_identity?u.object.identity:mN(e,u.object,t);s.set(d,{...u.object,identity:d})}let a=new Set([...n.keys(),...s.keys(),...i.keys()]),c=new Map([...s.values()].map(u=>[u.path,u])),l=new Map([...i.values()].map(u=>[u.path,u]));return[...a].sort().map(u=>{let d=n.get(u),f=s.get(u),p=i.get(u),m=p?.path??f?.path??d.path,h=f?.path??p?.path??d.path;return{entity:(f??p??d).entity,identity:u,base:Eo(d),local:Eo(f),remote:Eo(p),local_target_owner:Eo(c.get(m)),remote_target_owner:Eo(l.get(h))}})}function k0(r,e){let t=r.issues.some(d=>d.blocking),n=[],i=t&&r.mode==="read_only"&&r.issues.every(d=>!d.blocking||d.code==="invalid_frontmatter");if(!t||i){for(let d of[...r.objects].sort(gN)){let{local:f,remote:p}=d,m=i&&f.state==="exact"&&r.issues.some(h=>h.blocking&&h.path===f.object.path)&&p.state==="exact"&&p.object.path===f.object.path;(!t||m)&&fN(r,d,n,m)}i&&n.length!==r.issues.filter(d=>d.blocking).length&&(n=[]),n=cN(n,r.objects,e),n=oN(n,r.objects)}if(n.length>0||!t&&(r.kind!=="incremental"||r.boundary.checkpoint.cursor!==r.boundary.authority_cursor)){let d=n.map(f=>f.key);n.push({key:"checkpoint",depends_on_keys:d,command:"advance_checkpoint",reason:r.kind==="incremental"?"remote_change":r.kind,expected:r.boundary.checkpoint,next:t?r.boundary.checkpoint:{generation:r.boundary.checkpoint.generation+1,cursor:r.boundary.authority_cursor}})}let o=new Map;for(let d of n){let{key:f,depends_on_keys:p,...m}=d;o.set(f,ei({action_scope:{replica_id:r.boundary.replica_id,scope_epoch:r.boundary.scope_epoch,generation:r.boundary.checkpoint.generation},key:f,...m},e))}let a=n.map(d=>{let{key:f,depends_on_keys:p,...m}=d,h={...m,action_id:o.get(f),depends_on:p.map(y=>o.get(y))};if((h.command==="put_remote"||h.command==="move_remote"||h.command==="delete_remote")&&(h.idempotency_key=h.action_id),h.command==="move_remote"){let y=p.find(g=>g===`${h.source.identity}:put-remote`);y&&(h.revision_from_dependency=o.get(y))}return h}),c=[...r.issues].sort(bN),l={uploads:a.filter(_N).length,downloads:a.filter(wN).length,conflicts:a.filter(d=>d.command==="record_conflict").length,blocking_issues:c.filter(d=>d.blocking).length},u={plan_version:1,engine_profile:Qc,protocol_profile:"exact_document_v1",planner_policy:Zc,projection_policy:el,replica_id:r.boundary.replica_id,mode:r.mode,kind:r.kind,base_cursor:r.boundary.checkpoint.cursor,authority_cursor:r.boundary.authority_cursor,scope_epoch:r.boundary.scope_epoch,checkpoint_generation:r.boundary.checkpoint.generation,selective_sync:r.selective_sync,actions:a,issues:c,summary:l};return{...u,fingerprint:ei(u,e)}}function oN(r,e){if(!r.some(c=>c.command==="move_remote"))return r;let t=[...r],n=new Set;for(let c of aN(t))for(let l of c){let u=t.find(d=>d.key===l);u?.command==="move_remote"&&n.add(u.source.identity)}let i=Dp(t),s=new Map(t.map(c=>[c.key,c])),o=!0;for(;o;){o=!1;for(let c of t){if(c.command!=="move_remote")continue;let l=c.expected_target_owner;if(l.state!=="exact"||l.object.identity===c.source.identity)continue;let u=s.get(i.get(Sr(l.object))??""),d=u&&S0(u);(!u||d&&n.has(d))&&!n.has(c.source.identity)&&(n.add(c.source.identity),o=!0)}}if(n.size>0){t=t.filter(l=>{let u=S0(l);return!u||!n.has(u)||l.command!=="put_remote"&&l.command!=="move_remote"});let c=new Map(e.map(l=>[l.identity,l]));for(let l of[...n].sort()){let u=c.get(l);!u||u.entity==="resource"||t.push({key:`${l}:conflict`,depends_on_keys:[],command:"record_conflict",reason:"local_change",identity:l,entity:u.entity,local:u.local,remote:u.remote,conflict_kind:"path_occupied"})}}let a=Dp(t);for(let c of t){if(c.command!=="move_remote")continue;let l=c.expected_target_owner;if(l.state!=="exact"||l.object.identity===c.source.identity)continue;let u=a.get(Sr(l.object));!u||u===c.key||(c.depends_on_keys.includes(u)||c.depends_on_keys.push(u),c.expected_target_owner={state:"absent"})}return P0(t)}function aN(r){let e=r.filter(s=>s.command==="move_remote"),t=new Map(e.map(s=>[s.key,s])),n=Dp(r),i=new Map;for(let s of e){let o=[],a=new Map,c=s;for(;c;){let l=a.get(c.key);if(l!==void 0){let d=o.slice(l);i.set([...d].sort().join("\0"),d);break}a.set(c.key,o.length),o.push(c.key);let u=c.expected_target_owner;c=u.state==="exact"?t.get(n.get(Sr(u.object))??""):void 0}}return[...i.values()]}function Dp(r){let e=new Map;for(let t of r)t.command==="move_remote"?e.set(Sr(t.source),t.key):t.command==="delete_remote"&&e.set(Sr(t.target),t.key);return e}function S0(r){if(r.command==="move_remote")return r.source.identity;if(r.command==="put_remote"||r.command==="delete_remote")return r.target.identity}function cN(r,e,t){if(!r.some(d=>d.command==="move_local"||d.command==="write_local"))return r;let n=[...r],i=new Set;for(let d of e)d.base.state==="exact"&&i.add(d.base.object.path),d.local.state==="exact"&&i.add(d.local.object.path),d.remote.state==="exact"&&i.add(d.remote.object.path);for(;;){let d=lN(n);if(!d)break;let f=[...d].sort()[0],p=n.findIndex(_=>_.key===f),m=n[p];if(!m||m.command!=="move_local")throw new Error("Planner invariant: local path cycle contains a non-move action.");let h=uN(m.source,m.target_path,i,t);i.add(h);let y={...m.source,path:h},g={key:`${m.source.identity}:stage-local`,depends_on_keys:[...m.depends_on_keys],command:"move_local",reason:m.reason,source:m.source,target_path:h,expected_source_owner:m.expected_source_owner,expected_target_owner:{state:"absent"}};m.source=y,m.expected_source_owner={state:"exact",object:y},m.depends_on_keys=[g.key],n.splice(p,0,g)}let s=Lp(n),o=new Map(e.map(d=>[d.identity,d])),a=new Set,c=new Map(n.map(d=>[d.key,d])),l=!0;for(;l;){l=!1;for(let d of n){let f=$0(d),p=rl(d);if(f?.state!=="exact"||f.object.identity===p)continue;let m=c.get(s.get(Sr(f.object))??""),h=m&&rl(m);(!m||h&&a.has(h))&&!a.has(p)&&(a.add(p),l=!0)}}if(a.size>0){n=n.filter(d=>!a.has(rl(d))||d.command!=="move_local"&&d.command!=="write_local");for(let d of[...a].sort()){let f=o.get(d);!f||f.entity==="resource"||n.push({key:`${d}:conflict`,depends_on_keys:[],command:"record_conflict",reason:"remote_change",identity:d,entity:f.entity,local:f.local,remote:f.remote,conflict_kind:"path_occupied"})}}let u=Lp(n);for(let d of n){let f=$0(d),p=rl(d);if(f?.state!=="exact"||f.object.identity===p)continue;let m=u.get(Sr(f.object));!m||m===d.key||(d.depends_on_keys.includes(m)||d.depends_on_keys.push(m),dN(d,{state:"absent"}))}return P0(n)}function lN(r){let e=r.filter(i=>i.command==="move_local"),t=new Map(e.map(i=>[i.key,i])),n=Lp(r);for(let i of e){let s=[],o=new Map,a=i;for(;a;){let c=o.get(a.key);if(c!==void 0)return s.slice(c);o.set(a.key,s.length),s.push(a.key);let l=a.expected_target_owner;a=l.state==="exact"?t.get(n.get(Sr(l.object))??""):void 0}}}function Lp(r){let e=new Map;for(let t of r)t.command==="move_local"?e.set(Sr(t.source),t.key):t.command==="delete_local"&&e.set(Sr(t.target),t.key);return e}function $0(r){if(r.command==="move_local")return r.expected_target_owner;if(r.command==="write_local")return r.expected_path_owner}function dN(r,e){r.command==="move_local"?r.expected_target_owner=e:r.command==="write_local"&&(r.expected_path_owner=e)}function rl(r){return r.command==="move_local"?r.source.identity:r.command==="write_local"||r.command==="delete_local"?r.target.identity:""}function Sr(r){return`${r.entity}\0${r.identity}\0${r.path}`}function uN(r,e,t,n){let i=r.path.lastIndexOf("/"),s=i<0?"":r.path.slice(0,i+1),o=i<0?r.path:r.path.slice(i+1),a=o.lastIndexOf("."),c=a>0?o.slice(a):"";for(let l=0;;l+=1){let u=n(`${r.entity}\0${r.identity}\0${r.path}\0${e}\0${l}`),d=`${s}.mdbase-sync-stage-${u.slice(0,16)}${c}`;if(!t.has(d))return d}}function P0(r){let e=[...r],t=new Set,n=[];for(;e.length>0;){let i=e.findIndex(o=>o.depends_on_keys.every(a=>t.has(a)));if(i<0)throw new Error("Planner invariant: action dependency graph contains a cycle.");let[s]=e.splice(i,1);n.push(s),t.add(s.key)}return n}function fN(r,e,t,n){if(e.frozen_conflict){t.push(yN(e.frozen_conflict.local,e.frozen_conflict.remote)?{key:`${e.identity}:clear-conflict`,depends_on_keys:[],command:"clear_conflict",reason:"pending",identity:e.identity,entity:e.entity,expected_local:e.frozen_conflict.local,expected_remote:e.frozen_conflict.remote}:{key:`${e.identity}:conflict`,depends_on_keys:[],command:"record_conflict",reason:"pending",identity:e.identity,entity:e.entity,...e.frozen_conflict});return}let i=!Np(e.local,e.base),s=!Np(e.remote,e.base);if(!(!i&&!s)){if(e.entity==="resource"){if(i)return;Op(e,t);return}if(r.mode==="read_only"){(!i||n)&&Op(e,t);return}if(i&&s){if(Np(e.local,e.remote))return;t.push(hN(e));return}i?pN(e,t):Op(e,t)}}function Op(r,e){if(r.remote.state==="absent"){r.local.state==="exact"&&e.push({key:`${r.identity}:delete-local`,depends_on_keys:[],command:"delete_local",reason:"remote_change",target:r.local.object,expected_local:r.local,expected_path_owner:r.local_target_owner});return}let t=r.remote.object;if(r.local.state==="absent"){e.push(E0(r,t,[]));return}let n=r.local.object,i;if(n.path!==t.path){let s=`${r.identity}:move-local`;e.push({key:s,depends_on_keys:[],command:"move_local",reason:"remote_change",source:n,target_path:t.path,expected_source_owner:r.local,expected_target_owner:r.local_target_owner}),i=s}n.revision!==t.revision&&e.push(E0(r,t,i?[i]:[]))}function E0(r,e,t){return{key:`${r.identity}:write-local`,depends_on_keys:t,command:"write_local",reason:"remote_change",target:e,payload_revision:e.payload_revision,expected_local:t.length>0?{state:"exact",object:{...e,revision:zr(r.local).revision,payload_revision:zr(r.local).payload_revision,...zr(r.local).size===void 0?{}:{size:zr(r.local).size}}}:r.local,expected_path_owner:t.length>0?{state:"exact",object:{...e,revision:zr(r.local).revision,payload_revision:zr(r.local).payload_revision,...zr(r.local).size===void 0?{}:{size:zr(r.local).size}}}:r.local_target_owner}}function pN(r,e){if(r.local.state==="absent"){r.remote.state==="exact"&&e.push({key:`${r.identity}:delete-remote`,depends_on_keys:[],command:"delete_remote",reason:"local_change",target:r.remote.object,expected_remote:r.remote,expected_local:r.local,idempotency_key:""});return}let t=r.local.object;if(r.remote.state==="absent"){e.push(x0(r,t,[]));return}let n=r.remote.object,i;if(t.revision!==n.revision){let s=x0(r,{...t,path:n.path},[]);e.push(s),i=s.key}t.path!==n.path&&e.push({key:`${r.identity}:move-remote`,depends_on_keys:i?[i]:[],command:"move_remote",reason:"local_change",source:i?{...n,revision:t.revision}:n,target_path:t.path,expected_source_owner:i?{state:"exact",object:{...n,revision:t.revision}}:r.remote,expected_target_owner:r.remote_target_owner,expected_local:r.local,idempotency_key:""})}function x0(r,e,t){return{key:`${r.identity}:put-remote`,depends_on_keys:t,command:"put_remote",reason:"local_change",target:e,payload_revision:zr(r.local).payload_revision,expected_remote:r.remote,expected_local:r.local,idempotency_key:""}}function hN(r){return{key:`${r.identity}:conflict`,depends_on_keys:[],command:"record_conflict",reason:"remote_change",identity:r.identity,entity:r.entity,local:r.local,remote:r.remote,conflict_kind:r.local.state==="absent"||r.remote.state==="absent"?"delete_vs_change":"both_changed"}}function zr(r){if(r.state!=="exact")throw new Error("Planner invariant: expected exact object state.");return r.object}function Eo(r){return r?{state:"exact",object:r}:{state:"absent"}}function mN(r,e,t){let n=t(`${r}\0${e.entity}\0${e.path}\0${e.revision}`);return`${n.slice(0,8)}-${n.slice(8,12)}-5${n.slice(13,16)}-8${n.slice(17,20)}-${n.slice(20,32)}`}function Np(r,e){return tl(r)===tl(e)}function yN(r,e){return r.state==="absent"||e.state==="absent"?r.state===e.state:r.object.entity===e.object.entity&&r.object.identity===e.object.identity&&r.object.path===e.object.path&&r.object.payload_revision===e.object.payload_revision&&r.object.size===e.object.size}function gN(r,e){return`${r.entity}\0${r.identity}`.localeCompare(`${e.entity}\0${e.identity}`)}function bN(r,e){return vN(`${r.path??""}\0${r.code}\0${r.message}`,`${e.path??""}\0${e.code}\0${e.message}`)}function vN(r,e){let t=new TextEncoder,n=t.encode(r),i=t.encode(e),s=Math.min(n.length,i.length);for(let o=0;o[c.record_id,c])),a=new Map(n.files.map(c=>[c.file_id,c]));return this.finish({kind:e,prior:t,authorityCursor:n.session.head,scopeEpoch:n.session.scope_epoch,local:s,remoteRecords:o,remoteResources:n.resources,remoteFiles:a,snapshot:n})}async inspectIncremental(e){let t=new Map;for(let[l,u]of Object.entries(e.records))u.record&&t.set(l,u.record);let n=new Map(Object.entries(e.files??{}).map(([l,u])=>[l,u.file])),i=new Map(C0(e).map(l=>[EN(l),l])),s=e.cursor,o=e.cursor;for(;;){let l=s,u=await this.transport.changes(l,200);if(u.scope_epoch!==e.scope_epoch||u.cursoru.head||u.has_more&&u.cursor===l)throw new b("invalid_change_page","Authority returned an invalid change boundary.");if(u.reset_required)return this.inspectSnapshot("rebuild",e);for(let d of u.events){if(d.sequence<=o||d.sequence>u.cursor)throw new b("invalid_change_page","Authority change events are not strictly ordered.");o=d.sequence,this.applyRemoteObservation(i,t,n,d)}if(s=u.cursor,!u.has_more)break}let a=Object.values(e.resources??{}).map(l=>({path:l.path,kind:AN(l.path),revision:l.revision,document:""})),c=await this.inspectLocal(e,a,await this.currentRecordPathPolicy(e));return this.mode==="read_only"&&c.issues.some(l=>l.code==="invalid_frontmatter")?this.inspectSnapshot("rebuild",e):this.finish({kind:"incremental",prior:e,authorityCursor:s,scopeEpoch:e.scope_epoch,local:c,remoteRecords:t,remoteResources:[],remoteFiles:n,remoteRefs:[...i.values()]})}applyRemoteObservation(e,t,n,i){if(i.type==="put"){Iw(i);let s=i.record.record_id;Xn(this.selectiveSync,i.record.path)?(e.set(`record:${s}`,M0(i.record)),t.set(s,i.record)):(e.delete(`record:${s}`),t.delete(s));return}if(i.type==="remove"){e.delete(`record:${i.record_id}`),t.delete(i.record_id);return}if(i.type==="file_put"){gt(i.file);let s=i.file.file_id;zc(this.selectiveSync,i.file)?(e.set(`file:${s}`,qp(i.file)),n.set(s,i.file)):(e.delete(`file:${s}`),n.delete(s));return}e.delete(`file:${i.file_id}`),n.delete(i.file_id)}async inspectLocal(e,t,n){let i=[],s=new Map,o=new Map,a=[],c=new Map(Object.entries(e?.records??{}).map(([m,h])=>[h.path,[m,h]])),l=new Map(Object.entries(e?.planned_conflicts??{}).filter(([,m])=>m.entity==="record"&&m.local.state==="exact").map(([m,h])=>[h.local.state==="exact"?h.local.object.path:"",m])),u=new Map(Object.entries(e?.local_bindings??{}).filter(([,m])=>m.entity==="record").map(([m,h])=>[h.path,m])),d=new Set([...Object.keys(e?.resources??{}),...t.map(m=>m.path)]);for(let m of d){let h=e?.resources?.[m],y;try{y=await this.fileSystem.read(m)}catch{a.push(T0(m)),h&&i.push({stable_identity:!0,object:wn("resource",m,m,h.revision)});continue}if(y===null)continue;let g=y,_=_r(g,this.runtime);i.push({stable_identity:!0,object:wn("resource",m,m,_)}),s.set(m,g);let v=t.find(w=>w.path===m);!h&&v&&v.revision!==_?a.push({code:"local_collision",message:`${m} differs locally from the exact authority document.`,path:m,blocking:!0}):h&&h.revision!==_&&a.push({code:"mirror_diverged",message:`Authority-owned resource ${m} changed locally.`,path:m,blocking:!0})}let f=new Set(Object.values(e?.records??{}).map(m=>m.path)),p=h0(await this.fileSystem.listMarkdown(d),n).filter(m=>Xn(this.selectiveSync,m)||f.has(m));for(let m of p){let h=l.get(m),y=u.get(m),g=c.get(m),_=g?.[0],v=h??y??_??"",w;try{w=await this.fileSystem.readText(m)}catch{w=null}if(typeof w!="string"){a.push(w===null?T0(m):{code:"invalid_frontmatter",message:`Invalid frontmatter (${w.code}).`,path:m,blocking:!0});let $=w?.revision??g?.[1].revision;$&&i.push({stable_identity:v!=="",object:wn("record",v,m,$)});continue}let S=w,x=_r(S,this.runtime);i.push({stable_identity:v!=="",object:wn("record",v,m,x)}),s.set(m,S);let C=Pw(S);C.outcome!=="parsed"&&a.push({code:"invalid_frontmatter",message:`Invalid frontmatter (${C.outcome}).`,path:m,blocking:!0})}if(this.selectiveSync.file_classes.length>0){if(!this.fileSystem.listBinary)throw new b("file_storage_unavailable","Selected files require binary enumeration.");let m=new Set(Object.values(e?.files??{}).map(v=>v.file.path)),h=new Map(Object.entries(e?.files??{}).map(([v,w])=>[w.file.path,[v,w]])),y=new Map(Object.entries(e?.planned_conflicts??{}).filter(([,v])=>v.entity==="file"&&v.local.state==="exact").map(([v,w])=>[w.local.state==="exact"?w.local.object.path:"",v])),g=new Map(Object.entries(e?.local_bindings??{}).filter(([,v])=>v.entity==="file").map(([v,w])=>[w.path,v])),_=(await this.fileSystem.listBinary(d)).filter(v=>Kw(this.selectiveSync,v)||m.has(v));for(let v of _){let w=await this.fileSystem.inspectBinary(v);if(!w)continue;let S=y.get(v),x=g.get(v),C=h.get(v),$=S??x??C?.[0]??"";i.push({stable_identity:$!=="",object:{entity:"file",identity:$,path:v,revision:C&&C[1].file.content_digest===w.content_digest?C[1].file.revision:w.content_digest,payload_revision:w.content_digest,size:w.size}}),o.set(v,w)}}try{$o([...d,...p,...o.keys()])}catch(m){let h=Kn(m);a.push({code:Wn(m,"sync_inspection_failed"),message:h.message,blocking:!0})}return{observations:i,documents:s,binary:o,issues:a}}async finish(e){let{prior:t,local:n}=e,i=e.remoteRefs??[...e.remoteResources.map($N),...[...e.remoteRecords.values()].map(M0),...[...e.remoteFiles.values()].map(qp)],s=A0({base:t?C0(t):[],local:n.observations,remote:i},`${this.replicaId}\0${e.scopeEpoch}\0${t?.generation??0}`,this.runtime.digest);for(let d of s){let f=t?.planned_conflicts?.[d.identity];!f||f.entity!==d.entity||(d.frozen_conflict={local:d.local,remote:d.remote,conflict_kind:f.conflict_kind})}let o=[...n.issues];for(let d of s)(e.kind==="initial"||e.kind==="rebuild")&&d.base.state==="absent"&&d.local.state==="exact"&&d.remote.state==="exact"&&!I0(d.local,d.remote)&&o.push({code:"local_collision",message:`${d.remote.object.path} differs locally from the exact authority object.`,path:d.remote.object.path,blocking:this.mode!=="read_write"||d.entity==="resource"}),d.remote.state==="exact"&&d.local_target_owner.state==="exact"&&d.remote.object.identity!==d.local_target_owner.object.identity&&o.push({code:"local_collision",message:`${d.remote.object.path} is owned by different local bytes.`,path:d.remote.object.path,blocking:!0});try{$o([...i.map(d=>d.path),...n.observations.map(({object:d})=>d.path)])}catch(d){let f=Kn(d);o.push({code:Wn(d,"sync_inspection_failed"),message:f.message,blocking:!0})}if(this.mode==="read_only"){for(let d of s)if(d.entity!=="resource"&&!I0(d.local,d.base)){let f=xN(d.local,d.base);if(o.some(p=>p.blocking&&p.path===f))continue;o.push({code:"mirror_diverged",message:`${f} changed in a receive-only mirror.`,path:f,blocking:!0})}}let a=ei(Yn(this.selectiveSync),this.runtime.digest),c={boundary:{engine_profile:Qc,protocol_profile:"exact_document_v1",planner_policy:Zc,projection_policy:el,replica_id:this.replicaId,scope_epoch:e.scopeEpoch,authority_cursor:e.authorityCursor,checkpoint:{generation:t?.generation??0,cursor:t?.cursor??null},selective_sync_fingerprint:a},mode:this.mode,kind:e.kind,selective_sync:Yn(this.selectiveSync),objects:s,issues:SN(o)},l=k0(c,this.runtime.digest),u=await this.bindPayloads(l,n,e.remoteRecords,e.remoteResources,e.remoteFiles);return{summary:c,plan:l,durable_payloads:u,prior:t,snapshot:e.snapshot,remote_records:e.remoteRecords,remote_files:e.remoteFiles}}async bindPayloads(e,t,n,i,s){let o={documents:{},records:{},resources:{},files:{},local_files:{},mutations:{}},a=new Map(i.map(c=>[c.path,c]));for(let c of e.actions)if(c.command==="put_remote"){let l=c.expected_local.state==="exact"?c.expected_local.object.path:c.target.path;if(c.target.entity==="file"){let u=t.binary.get(l);if(!u)throw ti(c.action_id);await this.stageLocalBinary(l,u),o.local_files[c.action_id]={path:l,...u}}else{let u=t.documents.get(l);if(u===void 0||_r(u,this.runtime)!==c.payload_revision)throw ti(c.action_id);o.documents[c.action_id]=u}c.target.entity==="record"&&(o.mutations[c.action_id]={operation:"put",mutation_id:jp(c.action_id),replica_id:this.replicaId,scope_epoch:e.scope_epoch,record_id:c.target.identity,...c.expected_remote.state==="exact"?{base_revision:c.expected_remote.object.revision}:{},path:c.target.path,document:o.documents[c.action_id],created_at:this.runtime.now()})}else if(c.command==="write_local")if(c.target.entity==="record"){let l=n.get(c.target.identity);if(!l||l.revision!==c.target.revision)throw ti(c.action_id);o.records[c.action_id]=l}else if(c.target.entity==="resource"){let l=a.get(c.target.path);if(!l||l.revision!==c.target.revision)throw ti(c.action_id);o.resources[c.action_id]=l}else{let l=s.get(c.target.identity);if(!l||l.revision!==c.target.revision||!this.blobStore)throw ti(c.action_id);await Hc(this.transport,this.blobStore,l),o.files[c.action_id]=l}else if(c.command==="record_conflict"&&c.remote.state==="exact")if(c.entity==="record"){let l=n.get(c.identity);if(!l||l.revision!==c.remote.object.revision)throw ti(c.action_id);o.records[c.action_id]=l}else{let l=s.get(c.identity);if(!l||l.revision!==c.remote.object.revision)throw ti(c.action_id);o.files[c.action_id]=l}else c.command==="move_remote"&&c.source.entity==="record"?o.mutations[c.action_id]={operation:"move",mutation_id:jp(c.action_id),replica_id:this.replicaId,scope_epoch:e.scope_epoch,record_id:c.source.identity,base_revision:c.expected_source_owner.state==="exact"?c.expected_source_owner.object.revision:c.source.revision,path:c.target_path,created_at:this.runtime.now()}:c.command==="delete_remote"&&c.target.entity==="record"&&(o.mutations[c.action_id]={operation:"delete",mutation_id:jp(c.action_id),replica_id:this.replicaId,scope_epoch:e.scope_epoch,record_id:c.target.identity,base_revision:c.expected_remote.state==="exact"?c.expected_remote.object.revision:c.target.revision,created_at:this.runtime.now()});return o}async stageLocalBinary(e,t){if(!this.blobStore||!this.fileSystem.readBinary)throw new b("writable_file_storage_unavailable","Writable files require streaming filesystem and blob-store adapters.");if(await this.blobStore.has(t.content_digest))return;let n=await this.fileSystem.readBinary(e);if(!n)throw new b("sync_plan_stale",`${e} disappeared during inspection.`);await this.blobStore.write(t.content_digest,Jw(n,t,e))}};function SN(r){let e=new Map;for(let t of r)e.set(`${t.code}\0${t.path??""}\0${t.message}\0${t.blocking}`,t);return[...e.values()]}function C0(r){return[...Object.entries(r.resources??{}).map(([e,t])=>wn("resource",e,t.path,t.revision)),...Object.entries(r.records).map(([e,t])=>wn("record",e,t.path,t.revision)),...Object.entries(r.files??{}).map(([e,t])=>qp({...t.file,file_id:e}))]}function M0(r){return wn("record",r.record_id,r.path,r.revision)}function $N(r){return wn("resource",r.path,r.path,r.revision)}function wn(r,e,t,n){return{entity:r,identity:e,path:t,revision:n,payload_revision:n}}function qp(r){return{entity:"file",identity:r.file_id,path:r.path,revision:r.revision,payload_revision:r.content_digest,size:r.size}}function EN(r){return`${r.entity}:${r.identity}`}function I0(r,e){return JSON.stringify(r)===JSON.stringify(e)}function xN(r,e){return r.state==="exact"?r.object.path:e.state==="exact"?e.object.path:"mirror"}function ti(r){return new b("sync_payload_incomplete",`Inspected action ${r} has no revision-bound payload.`)}function T0(r){return{code:"file_read_failed",message:"Could not read local file.",path:r,blocking:!0}}function AN(r){return r==="mdbase.yaml"?"configuration":r.endsWith("lock.yaml")?"lock":r.startsWith("_types/")?"type":r.startsWith("_contracts/")?"contract":r.startsWith("_views/")?"view":"schema"}function jp(r){let e=r.replace(/^sha256:/u,"");return`${e.slice(0,8)}-${e.slice(8,12)}-4${e.slice(13,16)}-8${e.slice(17,20)}-${e.slice(20,32)}`}var xo=class{fileSystem;runtime;constructor(e,t){this.fileSystem=e,this.runtime=t}async validate(e,t){if((t?.generation??0)!==e.checkpoint_generation||(t?.cursor??null)!==e.base_cursor||(t?.scope_epoch??e.scope_epoch)!==e.scope_epoch)throw $r("The durable checkpoint changed after inspection.");for(let n of e.actions)n.depends_on.length===0&&await this.validateAction(n)}async validateAction(e){switch(e.command){case"write_local":await this.validateExpected(e.expected_local),await this.validateExpectedAt(e.target.path,e.expected_path_owner);return;case"delete_local":await this.validateExpected(e.expected_local),await this.validateExpectedAt(e.target.path,e.expected_path_owner);return;case"move_local":await this.validateExpectedAt(e.source.path,e.expected_source_owner),await this.validateExpectedAt(e.target_path,e.expected_target_owner);return;case"put_remote":case"move_remote":await this.validateExpected(e.expected_local);return;case"delete_remote":await this.validateExpectedAt(e.target.path,e.expected_local);return;case"record_conflict":await this.validateExpected(e.local);return;case"clear_conflict":await this.validateExpected(e.expected_local);return;case"advance_checkpoint":return}}async validateExpectedAt(e,t){if(t.state==="absent"){if(await this.fileSystem.exists(e))throw $r(`${e} is no longer vacant.`);return}if(t.object.path!==e||!await il(this.fileSystem,this.runtime,t.object,!0))throw $r(`${e} no longer has the inspected owner and bytes.`)}async validateExpected(e){if(e.state!=="absent"&&!await il(this.fileSystem,this.runtime,e.object,!0))throw $r(`${e.object.path} no longer matches the inspected bytes.`)}};async function il(r,e,t,n){if(t.entity==="file"){let s=await r.inspectBinary(t.path);return!!s&&s.content_digest===t.payload_revision&&(t.size===void 0||s.size===t.size)}let i;try{i=await r.readText(t.path)}catch(s){throw s instanceof b&&s.code==="file_read_failed"&&n?$r(s.message):s}return(typeof i=="string"?_r(i,e):i?.revision)===t.payload_revision}function $r(r){return new b("sync_plan_stale",r)}async function R0(r,e,t,n){if(r?.batch){if(r.batch.plan.fingerprint!==e.fingerprint)throw new b("mirror_recovery_required","A different prepared sync batch must recover before this plan can apply.");return r}let i=r?structuredClone(r):PN(e.replica_id,e.scope_epoch,e.mode,e.selective_sync);i.scope_epoch=e.scope_epoch,i.selective_sync=e.selective_sync;let s=e.actions.at(-1);if(s?.command!=="advance_checkpoint")throw new b("invalid_sync_plan","Prepared plan has no checkpoint action.");return i.batch={phase:"prepared",plan:e,next_action:0,receipts:[],payloads:t,checkpoint_before:{generation:e.checkpoint_generation,cursor:e.base_cursor},checkpoint_after:s.next},await n.write(i),i}async function O0(r,e){let t=st(r);t.phase!=="effects_complete"&&(t.phase="applying",delete t.failure,await ol(r,{type:"phase",plan_fingerprint:t.plan.fingerprint,phase:"applying"},e))}async function N0(r,e,t){let n=st(r),i=n.plan.actions[n.next_action];if(!i||i.action_id!==e.action_id)throw Re("The durable sync receipt does not match the next prepared action.");let s={type:"receipt",plan_fingerprint:n.plan.fingerprint,receipt:structuredClone(e),delta:kN(r,i)};n.receipts.push(structuredClone(e)),n.next_action+=1,await ol(r,s,t)}async function D0(r,e){let t=st(r),n=t.plan.actions[t.next_action];if(!n||n.command!=="advance_checkpoint")throw Re("Sync effects cannot complete before the checkpoint action is next.");t.phase="effects_complete",await ol(r,{type:"effects_complete",plan_fingerprint:t.plan.fingerprint},e)}async function sl(r,e,t,n){let i=st(r);i.phase=e,i.failure=t,await ol(r,{type:"phase",plan_fingerprint:i.plan.fingerprint,phase:e,failure:t},n)}async function ol(r,e,t){t.appendJournal?await t.appendJournal(e):await t.write(r)}function kN(r,e){let t="identity"in e?e.identity:"target"in e?e.target.identity:"source"in e?e.source.identity:null;if(t===null)return{};let n="entity"in e?e.entity:"target"in e?e.target.entity:"source"in e?e.source.entity:null;if(n===null)return{};let i={planned_conflicts:{[t]:Ao(r.planned_conflicts?.[t])},local_bindings:{[t]:Ao(r.local_bindings?.[t])}};return n==="record"?i.records={[t]:Ao(r.records[t])}:n==="resource"?i.resources={[t]:Ao(r.resources?.[t])}:i.files={[t]:Ao(r.files?.[t])},i}function Ao(r){return r===void 0?null:structuredClone(r)}function st(r){if(!r.batch)throw Re("The mirror has no prepared sync batch.");return r.batch}async function L0(r,e){let t=st(r);if(t.phase!=="blocked"||t.failure?.code!=="sync_plan_stale")throw new b("mirror_recovery_required","Only a stale batch at a durable action boundary can be abandoned.");delete r.batch,await e.write(r)}function PN(r,e,t,n){return{protocol_version:1,engine_version:3,generation:0,replica_id:r,scope_epoch:e,cursor:0,records:{},resources:{},files:{},selective_sync:n,mode:t,planned_conflicts:{}}}var cl=class{ports;materializer;ownersByPath=new Map;pathsByOwner=new Map;constructor(e){this.ports=e,this.materializer=new es(e.fileSystem,e.runtime,e.mode,e.blobStore)}async execute(e,t){let n=st(e);this.indexPathOwners(e);let i=new Set(n.receipts.map(s=>s.action_id));for(await O0(e,this.ports.store);n.next_action!i.has(a));if(o){let a={code:"invalid_mirror_state",message:`Action ${s.action_id} is missing dependency ${o}.`,action_id:s.action_id};return await sl(e,"blocked",a,this.ports.store),{status:"blocked",completed:n.next_action,failure:a}}try{let a=await this.dispatch(e,s);await N0(e,a,this.ports.store),i.add(a.action_id),this.ports.onProgress?.(n.next_action,n.plan.actions.length-1)}catch(a){let c=IN(a,s.action_id);return await sl(e,"blocked",c,this.ports.store),{status:c.code==="sync_plan_stale"?"stale":"blocked",completed:n.next_action,failure:c}}}throw Re("Prepared plan has no checkpoint action.")}async dispatch(e,t){switch(t.command){case"write_local":return this.writeLocal(e,t);case"move_local":return this.moveLocal(e,t);case"delete_local":return this.deleteLocal(e,t);case"put_remote":return this.putRemote(e,t);case"move_remote":return this.moveRemote(e,t);case"delete_remote":return this.deleteRemote(e,t);case"record_conflict":return e.planned_conflicts??={},e.planned_conflicts[t.identity]={decision_id:j0(t.entity,t.identity,t.local,t.remote,t.conflict_kind,this.ports.runtime.digest),entity:t.entity,local:t.local,remote:t.remote,conflict_kind:t.conflict_kind},e.local_bindings??={},t.local.state==="exact"?e.local_bindings[t.identity]={entity:t.entity,path:t.local.object.path}:delete e.local_bindings[t.identity],this.rebaseConflict(e,t),{action_id:t.action_id,status:"conflicted"};case"clear_conflict":return delete e.planned_conflicts?.[t.identity],delete e.local_bindings?.[t.identity],{action_id:t.action_id,status:"completed"};case"advance_checkpoint":throw Re("The executor cannot dispatch checkpoint actions.")}}rebaseConflict(e,t){let n=st(e).payloads;if(t.entity==="record"){if(t.remote.state==="absent"){delete e.records[t.identity];return}let s=n.records[t.action_id];if(!s||s.revision!==t.remote.object.revision)throw Er(t);al(s,this.ports.runtime,s.revision),e.records[t.identity]={path:s.path,revision:s.revision,hash:t.local.state==="exact"?t.local.object.payload_revision.replace(/^sha256:/u,""):this.ports.runtime.digest(s.document),...this.ports.mode==="read_write"?{record:s}:{}};return}if(e.files??={},t.remote.state==="absent"){delete e.files[t.identity];return}let i=n.files[t.action_id];if(!i||i.revision!==t.remote.object.revision)throw Er(t);gt(i),e.files[t.identity]={file:i}}async writeLocal(e,t){let n=st(e),i=await this.ports.fileSystem.exists(t.target.path);(!i||!await this.matchesRef(t.target))&&(await this.assertLocal(t.expected_local),await this.assertPathOwner(t.target.path,t.expected_path_owner,i));let s=n.payloads;if(t.target.entity==="record"){let a=s.records[t.action_id];if(!a||a.revision!==t.payload_revision)throw Er(t);return al(a,this.ports.runtime,t.payload_revision),await this.materializer.put(e,a,{inspectionPreflighted:n.plan.summary.blocking_issues?1:!0}),this.installPathOwner(t.target),{action_id:t.action_id,status:"completed"}}if(t.target.entity==="resource"){let a=s.resources[t.action_id];if(!a||a.revision!==t.payload_revision||_r(a.document,this.ports.runtime)!==a.revision)throw Er(t);return await this.materializer.putResource(e,a,e),this.installPathOwner(t.target),{action_id:t.action_id,status:"completed"}}let o=s.files[t.action_id];if(!o||o.content_digest!==t.payload_revision)throw Er(t);return await this.materializer.putFile(e,o,e),this.installPathOwner(t.target),{action_id:t.action_id,status:"completed"}}async moveLocal(e,t){return await this.matchesRef({...t.source,path:t.target_path})||(await this.assertLocal(t.expected_source_owner),await this.assertPathOwner(t.target_path,t.expected_target_owner),await this.ports.fileSystem.move(t.source.path,t.target_path)),CN(e,t.source,t.target_path),this.installPathOwner({...t.source,path:t.target_path}),{action_id:t.action_id,status:"completed"}}async deleteLocal(e,t){if(await this.matchesRef(t.target))if(await this.assertLocal(t.expected_local),t.target.entity==="record")await this.materializer.remove(e,t.target.identity,t.target.path,{inspectionPreflighted:!0});else if(t.target.entity==="resource"){let i=e.resources?.[t.target.identity];i?await this.materializer.removeResource(e,t.target.path,i):await this.ports.fileSystem.remove(t.target.path)}else await this.materializer.removeFile(e,t.target.identity);else MN(e,t.target);return this.removePathOwner(t.target),{action_id:t.action_id,status:"completed"}}async putRemote(e,t){let n=st(e).payloads;if(t.target.entity==="record"){let a=n.mutations[t.action_id],c=n.documents[t.action_id];if(!a||a.operation!=="put"||c===void 0)throw Er(t);if(_r(c,this.ports.runtime)!==t.payload_revision)throw $r("Prepared local document payload no longer matches its revision.");let l=await this.ports.transport.mutate(a);return this.acceptRecordReceipt(e,t,l,c),Fp(t.action_id,l)}if(t.target.entity==="resource")throw new b("invalid_sync_plan","Authority resources are not writable mirror objects.");let i=n.local_files[t.action_id];if(!i||!this.ports.blobStore||!this.ports.transport.uploadFile)throw Er(t);let s={protocol_version:1,type:"open_file_upload",transfer_id:Bp(t.action_id),path:t.target.path,size:i.size,content_digest:i.content_digest,...i.media_type?{media_type:i.media_type}:{},...t.expected_remote.state==="exact"?{if_revision:t.expected_remote.object.revision}:{}},o=await this.ports.transport.uploadFile(s,this.ports.blobStore.read(i.content_digest));if(gt(o.file),o.transfer_id!==s.transfer_id||o.file.path!==t.target.path||o.file.content_digest!==i.content_digest||o.file.size!==i.size)throw Up(t);return e.files??={},e.files[o.file.file_id]={file:o.file},delete e.local_bindings?.[t.target.identity],{action_id:t.action_id,status:"completed",file:o.file}}async moveRemote(e,t){if(t.source.entity==="record"){let s=st(e).payloads.mutations[t.action_id];if(!s||s.operation!=="move")throw Er(t);let o=await this.ports.transport.mutate(s);return this.acceptRecordReceipt(e,t,o),Fp(t.action_id,o)}if(t.source.entity==="resource"||!this.ports.transport.moveFile)throw new b("invalid_sync_plan","This remote move command is unsupported.");let n={protocol_version:1,type:"move_file",mutation_id:Bp(t.action_id),file_id:t.source.identity,if_revision:this.dependencyFileRevision(e,t)??(t.expected_source_owner.state==="exact"?t.expected_source_owner.object.revision:t.source.revision),from_path:t.source.path,path:t.target_path,update_references:!1},i=await this.ports.transport.moveFile(n);if(gt(i.file),i.mutation_id!==n.mutation_id||i.file.file_id!==t.source.identity||i.file.path!==t.target_path)throw Up(t);return e.files??={},e.files[t.source.identity]={file:i.file},delete e.local_bindings?.[t.source.identity],{action_id:t.action_id,status:"completed",file:i.file}}dependencyFileRevision(e,t){if(!t.revision_from_dependency)return;let n=st(e).receipts.find(({action_id:i})=>i===t.revision_from_dependency);if(!n?.file||n.file.file_id!==t.source.identity)throw Re(`Move ${t.action_id} is missing its dependency file receipt.`);return n.file.revision}async deleteRemote(e,t){if(t.target.entity==="record"){let s=st(e).payloads.mutations[t.action_id];if(!s||s.operation!=="delete")throw Er(t);let o=await this.ports.transport.mutate(s);return this.acceptRecordReceipt(e,t,o),Fp(t.action_id,o)}if(t.target.entity==="resource"||!this.ports.transport.deleteFile)throw new b("invalid_sync_plan","This remote delete command is unsupported.");let n={protocol_version:1,type:"delete_file",mutation_id:Bp(t.action_id),file_id:t.target.identity,if_revision:t.expected_remote.state==="exact"?t.expected_remote.object.revision:t.target.revision,path:t.target.path},i=await this.ports.transport.deleteFile(n);if(i.mutation_id!==n.mutation_id||i.file_id!==t.target.identity||i.previous_path!==t.target.path)throw Up(t);return delete e.files?.[t.target.identity],delete e.local_bindings?.[t.target.identity],{action_id:t.action_id,status:"completed"}}acceptRecordReceipt(e,t,n,i){let o=("target"in t?t.target:t.source).identity;if(n.status==="applied"||n.status==="previously_applied"){if(n.record){al(n.record,this.ports.runtime,n.record.revision);let u=e.records[o];e.records[o]={path:n.record.path,revision:n.record.revision,hash:i===void 0?u?.hash??this.ports.runtime.digest(n.record.document):this.ports.runtime.digest(i),...this.ports.mode==="read_write"?{record:n.record}:{}}}else delete e.records[o];delete e.local_bindings?.[o];return}let a=n.status==="conflicted"?n.conflict.current:void 0;a&&al(a,this.ports.runtime,a.revision);let c=a?{state:"exact",object:{entity:"record",identity:o,path:a.path,revision:a.revision,payload_revision:a.revision}}:t.command==="move_remote"?t.expected_source_owner:t.expected_remote;e.planned_conflicts??={};let l=n.status==="rejected"?"rejected":"both_changed";e.planned_conflicts[o]={decision_id:j0("record",o,t.expected_local,c,l,this.ports.runtime.digest),entity:"record",local:t.expected_local,remote:c,conflict_kind:l},e.local_bindings??={},t.expected_local.state==="exact"&&(e.local_bindings[o]={entity:"record",path:t.expected_local.object.path}),a&&(e.records[o]={path:a.path,revision:a.revision,hash:t.expected_local.state==="exact"?t.expected_local.object.payload_revision.replace(/^sha256:/u,""):this.ports.runtime.digest(a.document),...this.ports.mode==="read_write"?{record:a}:{}})}async assertLocal(e){if(e.state!=="absent"&&!await this.matchesRef(e.object))throw $r(`${e.object.path} no longer matches the inspected revision.`)}async assertPathOwner(e,t,n){let i=this.ownersByPath.get(e);if(t.state==="absent"){if(i||(n??await this.ports.fileSystem.exists(e)))throw $r(`${e} is no longer vacant.`);return}if(!i||i.entity!==t.object.entity||i.identity!==t.object.identity)throw $r(`${e} has a different path owner.`)}matchesRef(e){return il(this.ports.fileSystem,this.ports.runtime,e,!1)}indexPathOwners(e){this.ownersByPath.clear(),this.pathsByOwner.clear();for(let[t,n]of Object.entries(e.records))this.installPathOwner({entity:"record",identity:t,path:n.path,revision:n.revision,payload_revision:`sha256:${n.hash}`});for(let[t,n]of Object.entries(e.resources??{}))this.installPathOwner({entity:"resource",identity:t,path:n.path,revision:n.revision,payload_revision:`sha256:${n.hash}`});for(let[t,n]of Object.entries(e.files??{}))this.installPathOwner({entity:"file",identity:t,path:n.file.path,revision:n.file.revision,payload_revision:n.file.content_digest,size:n.file.size})}installPathOwner(e){let t=`${e.entity}:${e.identity}`,n=this.pathsByOwner.get(t);n!==void 0&&this.ownersByPath.delete(n),this.ownersByPath.set(e.path,e),this.pathsByOwner.set(t,e.path)}removePathOwner(e){let t=`${e.entity}:${e.identity}`,n=this.pathsByOwner.get(t)??e.path;this.ownersByPath.delete(n),this.pathsByOwner.delete(t)}};function j0(r,e,t,n,i,s){return ei({entity:r,identity:e,local:t,remote:n,conflict_kind:i},s)}function CN(r,e,t){if(e.entity==="record"){let n=r.records[e.identity];n&&(n.path=t,n.record&&(n.record.path=t))}else if(e.entity==="resource"){let n=r.resources?.[e.identity];n&&(n.path=t)}else{let n=r.files?.[e.identity];n&&(n.file.path=t)}}function MN(r,e){e.entity==="record"?delete r.records[e.identity]:e.entity==="resource"?delete r.resources?.[e.identity]:delete r.files?.[e.identity]}function al(r,e,t){let n=Dc(r);if(r.revision!==t||_r(n,e)!==t)throw new b("invalid_sync_response","Record receipt does not match its exact document revision.")}function Fp(r,e){return e.status==="applied"||e.status==="previously_applied"?{action_id:r,status:"completed",...e.record?{record:e.record}:{}}:{action_id:r,status:e.status}}function Er(r){return new b("sync_payload_incomplete",`Prepared action ${r.action_id} has no exact payload capability.`)}function Up(r){return new b("invalid_sync_response",`Authority receipt does not match prepared action ${r.action_id}.`)}function IN(r,e){let t=Kn(r);return{code:Wn(r,"sync_action_failed"),message:t.message,action_id:e}}function Bp(r){let e=r.replace(/^sha256:/u,"");return`${e.slice(0,8)}-${e.slice(8,12)}-4${e.slice(13,16)}-8${e.slice(17,20)}-${e.slice(20,32)}`}async function q0(r,e,t){let n=st(r);if(n.phase!=="effects_complete")throw Re("A checkpoint cannot advance before every prepared effect is durable.");let i=n.plan.actions[n.next_action];if(!i||i.command!=="advance_checkpoint")throw Re("Prepared checkpoint action is missing.");if(i.expected.generation!==n.checkpoint_before.generation||i.expected.cursor!==n.checkpoint_before.cursor||i.next.generation!==n.checkpoint_after.generation||i.next.cursor!==n.checkpoint_after.cursor)throw Re("Prepared checkpoint boundary is inconsistent.");let s=n.plan.fingerprint;return r.generation=i.next.generation,r.cursor=i.next.cursor??0,r.last_completed_plan=s,r.last_synced_at=e.now(),delete r.batch,await t.write(r),s}async function F0(r,e,t,n){let[i]=e.actions;if(e.actions.length!==1||!i||i.command!=="advance_checkpoint"||i.expected.generation!==(r.generation??0)||i.expected.cursor!==r.cursor||i.next.generation!==(r.generation??0)+1||i.next.cursor!==e.authority_cursor||r.batch!==void 0)throw Re("Empty checkpoint plan is inconsistent.");return r.generation=i.next.generation,r.cursor=i.next.cursor??0,r.last_completed_plan=e.fingerprint,r.last_synced_at=t.now(),await n.write(r),e.fingerprint}var ri=class{replicaId;transport;mode;stateStore;fileSystem;blobStore;selectiveSync;lease;runtime;materializer;onProgress;constructor(e,t,n,i="read_only"){this.replicaId=e,this.transport=t,this.mode=i,this.stateStore=n.stateStore,this.fileSystem=n.fileSystem,this.blobStore=n.blobStore,this.selectiveSync=Yn(n.selectiveSync),this.lease=n.lease??new wo,this.runtime=n.runtime??So,this.materializer=new es(this.fileSystem,this.runtime,this.mode,this.blobStore),this.onProgress=n.onProgress}async sync(e={}){return this.lease.runExclusive(async()=>{let t=await this.readState();if(t?.batch?.phase==="blocked"&&t.batch.failure?.code==="sync_plan_stale"&&(await L0(t,this.journalStore()),t=await this.readState()),t?.batch)return this.executePrepared(t,e.signal);let n=await this.inspectDetailed(t);return this.applyInspection(n,e.signal)})}async inspect(){return this.lease.runExclusive(async()=>{let e=await this.readState();return e?.batch?.plan??(await this.inspectDetailed(e)).plan})}async apply(e,t={}){return this.lease.runExclusive(async()=>{let n=await this.readState();if(n?.batch){if(n.batch.plan.fingerprint!==e.fingerprint)throw new b("mirror_recovery_required","A different prepared plan must recover before this plan can apply.");return this.executePrepared(n,t.signal)}if(n?.last_completed_plan===e.fingerprint){let s=rr(n,this.mode);return wr(s.state==="attention"?"attention":"applied",{...e,issues:[]},s,0)}let i=await this.inspectDetailed(n);return i.plan.fingerprint!==e.fingerprint?wr("stale",e,rr(i.prior,this.mode),0,{code:"sync_plan_stale",message:"The local folder or authority changed. Inspect the sync plan again."}):this.applyInspection(i,t.signal)})}async applyInspection(e,t){let n=e.plan;if(n.issues.some(s=>s.blocking)&&n.actions.length===0)return wr("attention",n,rr(e.prior,this.mode),0);if(t?.aborted)return wr("cancelled",n,rr(e.prior,this.mode),0,{code:"sync_cancelled",message:"Sync cancelled before preparation."});try{await new xo(this.fileSystem,this.runtime).validate(n,e.prior)}catch(s){let o=Kn(s),a=Wn(s,"sync_revalidation_failed");return wr(a==="sync_plan_stale"?"stale":"failed",n,rr(e.prior,this.mode),0,{code:a,message:o.message})}if(n.actions.length===0)return wr("applied",n,rr(e.prior,this.mode),0);if(e.prior&&n.actions.length===1&&n.actions[0]?.command==="advance_checkpoint")return await F0(e.prior,n,this.runtime,this.journalStore()),wr("applied",n,rr(e.prior,this.mode),0);let i=await R0(e.prior??this.initialUnchangedState(e),n,e.durable_payloads,this.journalStore());return this.executePrepared(i,t)}async executePrepared(e,t){let n=e.batch,i=await new cl({transport:this.transport,fileSystem:this.fileSystem,blobStore:this.blobStore,runtime:this.runtime,mode:this.mode,store:this.journalStore(),onProgress:(c,l)=>this.onProgress?.({phase:"applying",completed:c,total:l,done:c===l})}).execute(e,t);if(i.status!=="effects_complete"){let c=rr(e,this.mode);return wr(i.status==="blocked"?"failed":i.status,n.plan,c,i.completed,i.failure)}let s=n.plan;await q0(e,this.runtime,this.journalStore()),await this.pruneFileBlobs();let o=rr(e,this.mode),a=o.conflicts.length>0||o.local_issues.length>0||s.summary.conflicts>0;return wr(a?"attention":"applied",s,o,i.completed)}inspectDetailed(e){return new nl(this.replicaId,this.transport,this.mode,this.fileSystem,this.blobStore,this.selectiveSync,this.runtime,()=>this.readState(),t=>this.currentRecordPathPolicy(t)).inspect(e)}initialUnchangedState(e){if(e.plan.kind!=="initial"||!e.snapshot)return null;let t=new Set(e.summary.objects.filter(o=>o.local.state==="exact"&&o.remote.state==="exact"&&JSON.stringify(o.local)===JSON.stringify(o.remote)).map(o=>`${o.entity}:${o.identity}`)),n={};for(let{record:o,hash:a}of e.snapshot.records)t.has(`record:${o.record_id}`)&&(n[o.record_id]={path:o.path,revision:o.revision,hash:a,...this.mode==="read_write"?{record:o}:{}});let i={};for(let o of e.snapshot.resources)t.has(`resource:${o.path}`)&&(i[o.path]={path:o.path,revision:o.revision,hash:this.runtime.digest(o.document)});let s={};for(let o of e.snapshot.files)t.has(`file:${o.file_id}`)&&(s[o.file_id]={file:o});return{protocol_version:1,engine_version:3,generation:0,replica_id:e.plan.replica_id,scope_epoch:e.plan.scope_epoch,cursor:0,records:n,resources:i,files:s,selective_sync:e.plan.selective_sync,mode:e.plan.mode,planned_conflicts:{}}}async status(){let e=await this.checkpointStatus(),t=await this.inspect();return e.state==="not_initialized"&&!t.issues.some(n=>n.code==="invalid_frontmatter"||n.code==="file_read_failed")?e:w0(e,t)}async checkpointStatus(){return this.lease.runExclusive(()=>this.checkpointStatusUnlocked())}async checkpointStatusUnlocked(){return rr(await this.readState(),this.mode)}async authorityPromotionManifest(){return this.lease.runExclusive(async()=>{if(this.mode!=="read_write")throw new b("promotion_requires_writable_mirror","Only a read-write mirror can prove an authority promotion source.");let e=await this.readState();if(!e)throw new b("mirror_not_initialized","Synchronize this mirror first.");if(e.batch||Object.keys(e.planned_conflicts??{}).length>0)throw new b("promotion_mirror_not_clean","Finish the prepared batch and resolve conflicts before promotion.");let t=await _0({state:e,selectiveSync:this.selectiveSync,fileSystem:this.fileSystem,pathPolicy:await this.currentRecordPathPolicy(e),digest:this.runtime.digest});if((await this.inspectDetailed(e)).plan.actions.some(i=>i.command!=="advance_checkpoint"))throw new b("promotion_mirror_not_clean","Synchronize this mirror immediately before promotion.");return t})}async previewInitialization(){let e=await this.inspect(),t=e.actions.filter(i=>i.command==="put_remote"||i.command==="move_remote"||i.command==="delete_remote"),n=e.actions.filter(i=>i.command==="write_local"||i.command==="move_local"||i.command==="delete_local");return{already_initialized:e.kind==="incremental",download_documents:n.filter(i=>"target"in i&&i.target.entity!=="file"||"source"in i&&i.source.entity!=="file").length,upload_documents:t.filter(i=>"target"in i&&i.target.entity==="record"||"source"in i&&i.source.entity==="record").length,unchanged_documents:0,download_files:n.filter(i=>"target"in i&&i.target.entity==="file"||"source"in i&&i.source.entity==="file").length,upload_files:t.filter(i=>"target"in i&&i.target.entity==="file"||"source"in i&&i.source.entity==="file").length,unchanged_files:0,collisions:e.issues.filter(i=>i.code==="local_collision"&&i.path).map(i=>i.path),local_issues:e.issues.filter(i=>i.code==="invalid_frontmatter"&&i.path!==void 0).map(i=>({code:"invalid_frontmatter",message:i.message,path:i.path}))}}async resolveConflict(e,t,n){await this.lease.runExclusive(()=>this.resolveConflictUnlocked(e,t,n))}async resolveConflictUnlocked(e,t,n){if(this.mode!=="read_write")throw new b("mirror_read_only","Receive-only mirrors have no writable conflicts.");let i=await this.readState();if(!i||i.batch)throw new b("mirror_recovery_required","Finish sync recovery before resolving conflicts.");let s=i.planned_conflicts?.[e];if(!s)throw new b("mirror_conflict_not_found","Writable mirror conflict was not found.");if((s.decision_id??"")!==t)throw U0();let o=new xo(this.fileSystem,this.runtime);s.local.state==="exact"?await o.validateExpected(s.local):s.remote.state==="exact"&&await o.validateExpectedAt(s.remote.object.path,s.local);let a=await Xc(this.replicaId,this.transport,this.mode,this.selectiveSync,this.runtime),c=s.entity==="record"?a.records.find(({record:d})=>d.record_id===e)?.record:void 0,l=s.entity==="file"?a.files.find(d=>d.file_id===e):void 0;if(!(s.entity==="record"?TN(s.remote,c):RN(s.remote,l)))throw U0();n==="remote"&&(s.entity==="record"?await this.installRemoteRecord(i,e,c):await this.installRemoteFile(i,e,l)),delete i.planned_conflicts?.[e],n==="remote"&&delete i.local_bindings?.[e],await this.writeState(i)}async installRemoteRecord(e,t,n){if(n){let o=e.planned_conflicts?.[t],a=await this.fileSystem.read(n.path),c=e.records[t]?.path===n.path||o?.local.state==="exact"&&o.local.object.path===n.path;await this.materializer.put(e,n,{inspectionPreflighted:!1,...c&&a!==null?{acceptedHash:this.runtime.digest(a)}:{}});return}let i=e.planned_conflicts?.[t],s=i?.local.state==="exact"?i.local.object.path:e.records[t]?.path;s&&await this.fileSystem.read(s)!==null&&await this.fileSystem.remove(s),delete e.records[t]}async installRemoteFile(e,t,n){if(n){if(!this.blobStore)throw new b("file_storage_unavailable","File resolution needs a blob store.");await Hc(this.transport,this.blobStore,n);let o=e.planned_conflicts?.[t],a=o?.local.state==="exact"?{content_digest:o.local.object.payload_revision,size:o.local.object.size??0}:void 0,c=e.local_bindings?.[t]?.path;await this.materializer.putFile(e,n,e,a),c&&c!==n.path&&await this.fileSystem.remove(c);return}let i=e.planned_conflicts?.[t],s=i?.local.state==="exact"?i.local.object.path:e.files?.[t]?.file.path;s&&await this.fileSystem.inspectBinary(s)!==null&&await this.fileSystem.remove(s),delete e.files?.[t]}async readState(){let e=await this.stateStore.read();if(e===null)return null;try{return l0(e,this.replicaId,this.mode)}catch(t){throw t instanceof b?t:Re("Mirror metadata is corrupt or belongs to another replica.")}}writeState(e){return this.stateStore.write(e)}journalStore(){let e=this.stateStore;return{write:t=>this.writeState(t),...e.appendJournal?{appendJournal:t=>e.appendJournal(t)}:{}}}async pruneFileBlobs(){if(!this.blobStore)return;let e=await this.readState();if(!e)return;let t=new Set;for(let n of Object.values(e.files??{}))t.add(n.file.content_digest);for(let n of Object.values(e.batch?.payloads.files??{}))t.add(n.content_digest);for(let n of Object.values(e.batch?.payloads.local_files??{}))t.add(n.content_digest);await this.blobStore.prune(t)}currentRecordPathPolicy(e){return this.materializer.recordPathPolicy(e)}};function TN(r,e){return r.state==="absent"?e===void 0:e!==void 0&&r.object.entity==="record"&&r.object.identity===e.record_id&&r.object.path===e.path&&r.object.revision===e.revision&&r.object.payload_revision===e.revision&&r.object.size===void 0}function RN(r,e){return r.state==="absent"?e===void 0:e!==void 0&&r.object.entity==="file"&&r.object.identity===e.file_id&&r.object.path===e.path&&r.object.revision===e.revision&&r.object.payload_revision===e.content_digest&&r.object.size===e.size}function U0(){return new b("mirror_conflict_stale","Local or hosted content changed after this conflict was recorded. Synchronize again before choosing a version.")}var ko=class extends ri{constructor(e,t,n){super(e,t,n,"read_write")}};function B0(r){let e=[...r.actions.flatMap(t=>t.command==="advance_checkpoint"?[]:[ON(t)]),...r.issues.map(t=>({kind:"document",path:t.path??"Sync engine",direction:"attention",action:t.blocking?"fix":"review",detail:t.message}))];return{plan:r,phase:r.kind,entries:e,cursor:r.base_cursor,remoteHead:r.authority_cursor,already_initialized:r.kind==="incremental",download_documents:r.actions.filter(t=>["write_local","move_local","delete_local"].includes(t.command)&&("target"in t?t.target.entity!=="file":"source"in t&&t.source.entity!=="file")).length,upload_documents:r.actions.filter(t=>["put_remote","move_remote","delete_remote"].includes(t.command)&&("target"in t?t.target.entity==="record":"source"in t&&t.source.entity==="record")).length,unchanged_documents:0,download_files:r.actions.filter(t=>["write_local","move_local","delete_local"].includes(t.command)&&("target"in t?t.target.entity==="file":"source"in t&&t.source.entity==="file")).length,upload_files:r.actions.filter(t=>["put_remote","move_remote","delete_remote"].includes(t.command)&&("target"in t?t.target.entity==="file":"source"in t&&t.source.entity==="file")).length,unchanged_files:0,collisions:r.issues.filter(t=>t.blocking&&t.code==="local_collision"&&t.path!==void 0).map(t=>t.path),local_issues:r.issues.filter(t=>["invalid_frontmatter","file_read_failed"].includes(t.code)&&t.path!==void 0).map(t=>({code:t.code,message:t.message,path:t.path}))}}function ON(r){if(r.command==="advance_checkpoint")throw new Error("Checkpoint actions are not preview entries.");if(r.command==="record_conflict"){let c=r.local.state==="exact"?r.local.object:r.remote.state==="exact"?r.remote.object:void 0;return{kind:r.entity==="file"?"file":"document",path:c?.path??r.identity,direction:"attention",action:"fix",detail:`Local and hosted ${r.entity} changes conflict (${r.conflict_kind.replace(/_/g," ")}).`,...c?.entity==="file"&&c.size!==void 0?{estimatedBytes:c.size}:{},...r.entity==="record"?{recordId:r.identity}:{fileId:r.identity}}}if(r.command==="clear_conflict"){let c=r.expected_local.state==="exact"?r.expected_local.object:r.expected_remote.state==="exact"?r.expected_remote.object:void 0;return{kind:r.entity==="file"?"file":"document",path:c?.path??r.identity,direction:"attention",action:"fix",detail:`Local and hosted ${r.entity} content now matches; clear the resolved conflict.`,...c?.entity==="file"&&c.size!==void 0?{estimatedBytes:c.size}:{},...r.entity==="record"?{recordId:r.identity}:{fileId:r.identity}}}let e=r.command.endsWith("_local"),t="target"in r?r.target:r.source,n=r.command==="move_local"||r.command==="move_remote"?r.target_path:t.path,i=r.command==="write_local"&&r.expected_local.state==="absent"||r.command==="put_remote"&&r.expected_remote.state==="absent",s=r.command.startsWith("move_")?"rename":r.command.startsWith("delete_")?"delete":i?"create":"update",o=r.command.split("_")[0],a=r.command.startsWith("move_")?` from ${t.path}`:"";return{kind:t.entity==="file"?"file":"document",path:n,direction:e?"download":"upload",action:s,detail:`${e?"Hosted":"Local"} ${t.entity} will ${o}${a}.`,...t.entity==="file"&&t.size!==void 0?{estimatedBytes:t.size}:{},...t.entity==="record"?{recordId:t.identity}:{},...t.entity==="file"?{fileId:t.identity}:{}}}var ts=".mdbase/connect-role.json",Po=".mdbase/authority-adoption.json",Co=".mdbase/authority-adoption-snapshot.json",NN="mdbase-obsidian-connect",Sn="mirrors",DN="mdbase-obsidian-connect-blobs",$n="manifests",En="chunks",ll=1024*1024,LN=32*1024*1024;function ii(r){if(!Number.isSafeInteger(r)||r<0||r>LN)throw new b("file_too_large","Binary sync supports files up to 32 MiB on this device. Exclude this file's folder to continue.")}var jN="mdbase-connect-access-",qN="mdbase-connect-refresh-",FN="mdbase-connect-adoption-",UN=300*1e3,BN=[".git",".trash",".mdbase"],VN=new Set([".git",".mdbase",".trash","node_modules","_contracts","_schemas","_types","_views"]),Vp=["image","audio","video","pdf","other"],Io=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function ot(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)}function xr(r){let e=r?.file_classes??[];if(e.some(s=>!Vp.includes(s))||new Set(e).size!==e.length)throw new b("invalid_file_materialization","Selected file media classes must be valid and unique.");let t=[...e].sort((s,o)=>Vp.indexOf(s)-Vp.indexOf(o)),n=r?.excluded_folders??[];if(n.some(s=>typeof s!="string"||!s.trim()))throw new b("invalid_file_materialization","Excluded folders cannot be empty.");let i=n.map(s=>Fn(s.trim())).sort((s,o)=>s.toLocaleLowerCase().localeCompare(o.toLocaleLowerCase()));if(i.length>100)throw new b("invalid_file_materialization","File sync supports at most 100 excluded folders.");if(new Set(i.map(s=>s.toLocaleLowerCase())).size!==i.length)throw new b("invalid_file_materialization","Excluded folders must be unique on portable filesystems.");for(let s of i)Mo(s,!0);return{file_classes:t,excluded_folders:i}}function G0(r){let e=r.includes(".")?r.slice(r.lastIndexOf(".")+1).toLowerCase():"";return["avif","bmp","gif","jpeg","jpg","png","svg","webp"].includes(e)?"image":["flac","m4a","mp3","oga","ogg","opus","wav"].includes(e)?"audio":["3gp","mkv","mov","mp4","webm"].includes(e)?"video":e==="pdf"?"pdf":"other"}function zN(r,e,t=G0(e)){if(!r.file_classes.includes(t))return!1;let n=(0,te.normalizePath)(e);return!r.excluded_folders.some(i=>n===i||n.startsWith(`${i}/`))}function Mo(r,e=!1){let t=Fn(r),n=t.split("/");if(t.length>1024||!e&&/\.md$/i.test(t)||n.some(i=>i.startsWith(".")||VN.has(i.toLowerCase())||/[<>"|?*]/u.test(i)||/^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\.|$)/iu.test(i)))throw new b("invalid_file_path",`Collection file path ${t} is hidden, reserved, or non-portable.`);return t}async function Hp(r){let e=new Uint8Array(await crypto.subtle.digest("SHA-256",r));return{size:r.byteLength,content_digest:`sha256:${Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}`}}async function Kp(r){let e=[],t=0;for await(let s of r){if(!(s instanceof Uint8Array))throw new b("file_integrity_failed","A binary stream returned an invalid chunk.");s.byteLength&&(t+=s.byteLength,ii(t),e.push(Uint8Array.from(s)))}let n=new Uint8Array(t),i=0;for(let s of e)n.set(s,i),i+=s.byteLength;return n.buffer}function V0(r){let e=r.includes(".")?r.slice(r.lastIndexOf(".")+1).toLowerCase():"";return{avif:"image/avif",gif:"image/gif",jpeg:"image/jpeg",jpg:"image/jpeg",png:"image/png",svg:"image/svg+xml",webp:"image/webp",flac:"audio/flac",m4a:"audio/mp4",mp3:"audio/mpeg",ogg:"audio/ogg",opus:"audio/opus",wav:"audio/wav",mov:"video/quicktime",mp4:"video/mp4",webm:"video/webm",pdf:"application/pdf"}[e]}async function HN(r,e){if(r!==void 0){if(!e)return JSON.stringify(r);if(r instanceof ArrayBuffer)return r;if(ArrayBuffer.isView(r))return Uint8Array.from(new Uint8Array(r.buffer,r.byteOffset,r.byteLength)).buffer;if(r instanceof Blob)return r.arrayBuffer();throw new b("invalid_file_upload","The adoption upload body was not binary data.")}}function dl(r,e){if(e!=null)return e;if(!r.trim())return{};try{return JSON.parse(r)}catch{return{}}}function J0(r){let e=r["retry-after"]??r["Retry-After"];if(!e)return;let t=Number(e);if(Number.isFinite(t)&&t>=0)return t*1e3;let n=Date.parse(e);if(Number.isFinite(n))return Math.max(0,n-Date.now())}async function fl(r,e=i=>(0,te.requestUrl)(i),t=(i,s)=>window.fetch(i,s),n=KN()){if(n)try{return await z0(r,n)}catch{}try{return await e(r)}catch(i){try{return await z0(r,t)}catch{throw i}}}function KN(){try{if(typeof require!="function")return null;let r=require("electron").remote?.net;return r?.fetch?r.fetch.bind(r):null}catch{return null}}async function z0(r,e){let t=new Headers(r.headers);r.contentType&&!t.has("content-type")&&t.set("content-type",r.contentType);let n=await e(r.url,{method:r.method,headers:t,body:r.body}),i=await n.arrayBuffer(),s=new TextDecoder().decode(i),o=null;if(s.trim())try{o=JSON.parse(s)}catch{o=null}if(r.throw!==!1&&n.status>=400)throw new Error(`Request failed with status ${n.status}`);let a={};return n.headers.forEach((c,l)=>{a[l]=c}),{status:n.status,headers:a,arrayBuffer:i,json:o,text:s}}function WN(){return async r=>{if(r.signal?.aborted)throw new DOMException("Enrollment cancelled.","AbortError");let e=await fl({url:r.url,method:r.method,headers:r.headers,body:r.body===void 0?void 0:JSON.stringify(r.body),contentType:r.body===void 0?void 0:"application/json",throw:!1});if(r.signal?.aborted)throw new DOMException("Enrollment cancelled.","AbortError");return{status:e.status,body:dl(e.text,e.json),retryAfterMs:J0(e.headers)}}}function GN(){return async r=>{if(r.signal?.aborted)throw new DOMException("Collection adoption cancelled.","AbortError");let e=await HN(r.body,r.rawBody),t=await fl({url:r.url,method:r.method,headers:r.headers,body:e,contentType:e===void 0||r.rawBody?void 0:"application/json",throw:!1});if(r.signal?.aborted)throw new DOMException("Collection adoption cancelled.","AbortError");return{status:t.status,body:dl(t.text,t.json),retryAfterMs:J0(t.headers),headers:t.headers}}}var Wp=class{constructor(e,t,n=fl,i){this.accessToken=t;this.send=n;this.onFileProgress=i;let s;try{s=new URL(e)}catch{throw new b("invalid_sync_url","Sync URL must be an absolute authority endpoint.")}if(!(s.protocol==="https:"||s.protocol==="http:"&&["localhost","127.0.0.1","[::1]","::1"].includes(s.hostname))||s.username||s.password||s.search||s.hash||!/^\/v1\/authorities\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/sync\/?$/i.test(s.pathname))throw new b("invalid_sync_url","Sync URL must identify one authority sync endpoint.");this.syncUrl=s.href.replace(/\/$/,""),this.filesUrl=this.syncUrl.replace(/\/sync$/u,"/files")}openSession(){return this.request("POST","sessions")}snapshot(e,t){let n=new URLSearchParams({snapshot_id:e});return t&&n.set("page",t),this.request("GET",`snapshot?${n.toString()}`)}fileSnapshot(e,t){let n=new URLSearchParams({snapshot_id:e});return t&&n.set("page",t),this.request("GET",`files/snapshot?${n.toString()}`)}async*downloadFile(e){ii(e.size);let t=crypto.randomUUID();try{let n=0;this.onFileProgress?.({direction:"download",path:e.path,transferredBytes:n,totalBytes:e.size});let i=await this.fileRequest("POST","downloads",{protocol_version:1,type:"open_file_download",transfer_id:t,file_id:e.file_id,revision:e.revision});if(i.protocol_version!==1||i.type!=="file_transfer"||i.transfer_id!==t||i.direction!=="download"||i.protection!=="transport_tls"||i.total_size!==e.size||i.strategy.kind!=="object_ranges"||!Number.isSafeInteger(i.strategy.part_size)||i.strategy.part_size<=0)throw new b("invalid_sync_response","The authority returned an incompatible file download session.");let s=Math.ceil(e.size/i.strategy.part_size);for(let o=0;o=300)throw this.responseError(c,"file_download_failed");let l=H0(c.headers,"content-length");if(l!==void 0&&Number(l)!==a||c.arrayBuffer.byteLength!==a)throw new b("file_integrity_failed","Hosted authority returned a file part with the wrong length.");n+=a,this.onFileProgress?.({direction:"download",path:e.path,transferredBytes:n,totalBytes:e.size}),a&&(yield new Uint8Array(c.arrayBuffer))}}finally{await this.fileRequest("DELETE",`transfers/${encodeURIComponent(t)}`).catch(()=>{})}}async uploadFile(e,t){ii(e.size);let n=await this.fileRequest("POST","uploads",e);if(n.protocol_version!==1||n.type!=="file_transfer"||n.transfer_id!==e.transfer_id||n.direction!=="upload"||n.protection!=="transport_tls"||n.total_size!==e.size||!["object_put","object_multipart"].includes(n.strategy.kind))throw new b("invalid_sync_response","Authority returned an incompatible file upload session.");let i=n.strategy.kind==="object_multipart"?n.strategy.part_size:Math.max(1,e.size);if(!Number.isSafeInteger(i)||i<=0)throw new b("invalid_sync_response","Authority returned an invalid upload part size.");let s=new Gp(t),o=Math.max(1,Math.ceil(e.size/i));if(n.received.some(d=>!Number.isSafeInteger(d)||d<0||d>=o)||new Set(n.received).size!==n.received.length)throw new b("invalid_sync_response","Authority returned invalid upload progress.");let a=n.uploaded_parts??[];if(a.some((d,f)=>!Number.isSafeInteger(d.part_number)||d.part_number<1||d.part_number>o||!d.etag||d.etag.length>255||f>0&&a[f-1].part_number>=d.part_number)||(n.strategy.kind==="object_multipart"?a.length!==n.received.length||a.some((d,f)=>d.part_number-1!==n.received[f]):a.length!==0))throw new b("invalid_sync_response","Authority returned invalid uploaded part receipts.");let c=n.received.reduce((d,f)=>{let p=f*i;return d+Math.min(i,Math.max(0,e.size-p))},0);if(this.onFileProgress?.({direction:"upload",path:e.path,transferredBytes:c,totalBytes:e.size}),n.received.length===o)return this.commitUpload(e.transfer_id,a);let l=new Set(n.received),u=Array.from({length:o},()=>{});for(let d of a)u[d.part_number-1]=d;for(let d=0;d=300)throw new b("file_upload_failed",`Object storage returned HTTP ${y.status}.`);if(c+=p,this.onFileProgress?.({direction:"upload",path:e.path,transferredBytes:c,totalBytes:e.size}),n.strategy.kind==="object_multipart"){let g=H0(y.headers,"etag");if(!g)throw new b("invalid_sync_response","Object storage omitted a multipart ETag.");u[d]={part_number:d+1,etag:g}}}return await s.expectEnd(),this.commitUpload(e.transfer_id,u.filter(d=>d!==void 0))}async commitUpload(e,t){let n=await this.fileRequest("POST",`uploads/${encodeURIComponent(e)}/commit`,{protocol_version:1,type:"commit_file_upload",transfer_id:e,parts:t});if(n.protocol_version!==1||n.type!=="file_upload_committed"||n.transfer_id!==e)throw new b("invalid_sync_response","Authority returned an invalid file upload receipt.");return n}async moveFile(e){let t=await this.fileRequest("POST",`${encodeURIComponent(e.file_id)}/move`,e);if(t.protocol_version!==1||t.type!=="file_moved"||t.mutation_id!==e.mutation_id)throw new b("invalid_sync_response","Authority returned an invalid file move receipt.");return t}async deleteFile(e){let t=await this.fileRequest("POST",`${encodeURIComponent(e.file_id)}/delete`,e);if(t.protocol_version!==1||t.type!=="file_deleted"||t.mutation_id!==e.mutation_id||t.file_id!==e.file_id)throw new b("invalid_sync_response","Authority returned an invalid file delete receipt.");return t}changes(e,t=200){let n=new URLSearchParams({after:String(e),limit:String(t)});return this.request("GET",`changes?${n.toString()}`)}mutate(e){return this.request("POST","mutations",e)}async request(e,t,n){return this.requestAt(this.syncUrl,e,t,n)}async fileRequest(e,t,n){return this.requestAt(this.filesUrl,e,t,n)}async requestAt(e,t,n,i){let s=await this.send({url:`${e}/${n}`,method:t,headers:{authorization:`Bearer ${this.accessToken}`},body:i===void 0?void 0:JSON.stringify(i),contentType:i===void 0?void 0:"application/json",throw:!1}),o=dl(s.text,s.json);if(s.status<200||s.status>=300)throw this.responseError(s,"sync_failed");return o}responseError(e,t){let n=dl(e.text,e.json),i=ot(n)&&ot(n.error)?n.error:{};return new b(typeof i.code=="string"?i.code:t,typeof i.message=="string"?i.message:`Sync request failed (${e.status}).`)}},Gp=class{constructor(e){this.remainder=new Uint8Array;this.iterator=e[Symbol.asyncIterator]()}async read(e){let t=new Uint8Array(new ArrayBuffer(e)),n=0;for(;n!["authorization","cookie","host","proxy-authorization","content-length"].includes(e.toLowerCase())))}function H0(r,e){return Object.entries(r).find(([n])=>n.toLowerCase()===e.toLowerCase())?.[1]}function Y0(r){return[r.configDir||".obsidian",...BN].map(t=>(0,te.normalizePath)(t).replace(/\/+$/,""))}function ni(r,e){let t=Fn(e);if(Y0(r).some(n=>t===n||t.startsWith(`${n}/`)))throw new b("unsafe_mirror_path",`The collection authority attempted to write a reserved path: ${t}`);return t}function Se(r){if(r?.aborted)throw new DOMException("Synchronization cancelled.","AbortError")}function vt(r,e){return r??new Error(`IndexedDB ${e} failed without an error detail.`)}function QN(r,e){let t=async a=>{Se(e);let c=await a();return Se(e),c},n=async function*(a){for await(let c of a)Se(e),yield c;Se(e)},i=r.uploadFile?.bind(r),s=r.moveFile?.bind(r),o=r.deleteFile?.bind(r);return{openSession:()=>t(()=>r.openSession()),snapshot:(a,c)=>t(()=>r.snapshot(a,c)),fileSnapshot:(a,c)=>t(()=>r.fileSnapshot(a,c)),downloadFile:a=>n(r.downloadFile(a)),...i?{uploadFile:(a,c)=>t(()=>i(a,n(c)))}:{},...s?{moveFile:a=>t(()=>s(a))}:{},...o?{deleteFile:a=>t(()=>o(a))}:{},changes:(a,c)=>t(()=>r.changes(a,c)),mutate:a=>t(()=>r.mutate(a))}}async function rs(r,e){let t=(0,te.normalizePath)(e).replace(/\/+$/,"");if(!t)return;let n="";for(let i of t.split("/")){n=n?`${n}/${i}`:i;let s=r.getAbstractFileByPath(n);if(!(s instanceof te.TFolder)){if(s)throw new b("mirror_path_collision",`A file blocks the mirror folder ${n}.`);await r.adapter.exists(n)||await r.createFolder(n)}}}var Jp=class{constructor(e,t=i=>e.delete(i,!0),n=()=>{}){this.vault=e;this.trashFile=t;this.assertActive=n}async exists(e){let t=ni(this.vault,e);return this.vault.getAbstractFileByPath(t)!==null||await this.vault.adapter.exists(t)}async read(e){let t=await this.readText(e);if(t===null||typeof t=="string")return t;throw new b(t.code,t.reason)}async readText(e){let t=ni(this.vault,e);if(this.vault.getAbstractFileByPath(t)instanceof te.TFolder)throw new b("mirror_path_collision",`Expected a file at ${t}.`);let i;try{i=await this.vault.adapter.readBinary(t)}catch{try{if(!await this.vault.adapter.exists(t))return null}catch{}throw new b("file_read_failed",`Could not read ${t}.`)}try{return new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0}).decode(i)}catch{return{kind:"invalid",code:"invalid_utf8",reason:"File is not valid UTF-8.",revision:(await Hp(i)).content_digest}}}async write(e,t){let n=ni(this.vault,e),i=n.lastIndexOf("/");i>=0&&await rs(this.vault,n.slice(0,i));let s=this.vault.getAbstractFileByPath(n);if(s instanceof te.TFolder)throw new b("mirror_path_collision",`A folder blocks the mirror file ${n}.`);this.assertActive(),s instanceof te.TFile?await this.vault.modify(s,t):await this.vault.create(n,t)}async move(e,t){let n=ni(this.vault,e),i=ni(this.vault,t),s=this.vault.getAbstractFileByPath(n);if(!(s instanceof te.TFile))throw new b("mirror_path_collision",`Expected a file at ${n}.`);if(this.vault.getAbstractFileByPath(i)!==null||await this.vault.adapter.exists(i))throw new b("mirror_path_collision",`A file or folder blocks the mirror path ${i}.`);let o=i.lastIndexOf("/");o>=0&&await rs(this.vault,i.slice(0,o)),this.assertActive(),await this.vault.rename(s,i)}async remove(e){let t=ni(this.vault,e),n=this.vault.getAbstractFileByPath(t);if(n!=null){if(!(n instanceof te.TFile))throw new b("mirror_path_collision",`Expected a file at ${t}.`);this.assertActive(),await this.trashFile(n)}}async listMarkdown(e){return this.vault.getMarkdownFiles().map(t=>(0,te.normalizePath)(t.path)).filter(t=>!e.has(t)).filter(t=>!Y0(this.vault).some(n=>t===n||t.startsWith(`${n}/`))).sort()}async inspectBinary(e){let t=Mo(e),n=this.vault.getAbstractFileByPath(t);if(n==null)return null;if(!(n instanceof te.TFile))throw new b("mirror_path_collision",`Expected a file at ${t}.`);ii(n.stat.size);let i=await this.vault.readBinary(n);return ii(i.byteLength),Hp(i)}async writeBinary(e,t){let n=Mo(e),i=await Kp(t),s=n.lastIndexOf("/");s>=0&&await rs(this.vault,n.slice(0,s));let o=this.vault.getAbstractFileByPath(n);if(o instanceof te.TFolder)throw new b("mirror_path_collision",`A folder blocks the mirror file ${n}.`);this.assertActive(),o instanceof te.TFile?await this.vault.modifyBinary(o,i):await this.vault.createBinary(n,i)}async listBinary(e){return X0(this.vault).map(t=>(0,te.normalizePath)(t.path)).filter(t=>!/\.md$/i.test(t)&&!e.has(t)).filter(t=>{try{return Mo(t),!0}catch{return!1}}).sort()}async readBinary(e){let t=Mo(e),n=this.vault.getAbstractFileByPath(t);if(n==null)return null;if(!(n instanceof te.TFile))throw new b("mirror_path_collision",`Expected a file at ${t}.`);ii(n.stat.size);let i=new Uint8Array(await this.vault.readBinary(n));return ii(i.byteLength),(async function*(){for(let s=0;s{}),a}n&&n.stage!==i&&await this.removeStage(n.stage,n.chunks).catch(()=>{})}async remove(e){let t=await this.manifest(e);await this.delete($n,this.manifestKey(e)),t&&await this.removeStage(t.stage,t.chunks)}async prune(e){let t=await this.open(),n=await new Promise((o,a)=>{let c=[],l=t.transaction($n,"readonly").objectStore($n).openCursor();l.onsuccess=()=>{let u=l.result;if(!u)return o(c);c.push([u.key,u.value]),u.continue()},l.onerror=()=>a(vt(l.error,"manifest cursor"))}),i=new Set;for(let[o,a]of n)!Array.isArray(o)||o[0]!==this.namespace||typeof o[1]=="string"&&e.has(o[1])||(await this.delete($n,o),await this.removeStage(a.stage,a.chunks));for(let[o,a]of n)Array.isArray(o)&&o[0]===this.namespace&&typeof o[1]=="string"&&e.has(o[1])&&i.add(a.stage);let s=await new Promise((o,a)=>{let c=[],l=t.transaction(En,"readonly").objectStore(En).openKeyCursor();l.onsuccess=()=>{let u=l.result;if(!u)return o(c);let d=u.key,f=Array.isArray(d)&&typeof d[1]=="string"?d[1]:null;Array.isArray(d)&&d[0]===this.namespace&&(f===null||!i.has(f))&&c.push(d),u.continue()},l.onerror=()=>a(vt(l.error,"chunk cursor"))});for(let o of s)await this.delete(En,o)}manifest(e){return this.get($n,this.manifestKey(e))}manifestKey(e){return[this.namespace,e]}chunkKey(e,t){return[this.namespace,e,t]}async removeStage(e,t){for(let n=0;n{let o=n.transaction(e,"readonly").objectStore(e).get(t);o.onsuccess=()=>i(o.result??null),o.onerror=()=>s(vt(o.error,`read from ${e}`))})}async put(e,t,n){let i=await this.open();await new Promise((s,o)=>{let a=i.transaction(e,"readwrite");a.objectStore(e).put(n,t),a.oncomplete=()=>s(),a.onerror=()=>o(vt(a.error,`write to ${e}`)),a.onabort=()=>o(vt(a.error,`write to ${e}`))})}async delete(e,t){let n=await this.open();await new Promise((i,s)=>{let o=n.transaction(e,"readwrite");o.objectStore(e).delete(t),o.oncomplete=()=>i(),o.onerror=()=>s(vt(o.error,`delete from ${e}`)),o.onabort=()=>s(vt(o.error,`delete from ${e}`))})}close(){this.database?.then(e=>e.close(),()=>{}),this.database=null}open(){if(typeof indexedDB>"u")throw new b("storage_unavailable","IndexedDB is required for binary file sync.");return this.database??=new Promise((e,t)=>{let n=indexedDB.open(DN,1);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains($n)||n.result.createObjectStore($n),n.result.objectStoreNames.contains(En)||n.result.createObjectStore(En)},n.onerror=()=>t(vt(n.error,"binary store open")),n.onsuccess=()=>e(n.result)}),this.database}},Xp=class{constructor(e){this.key=e;this.database=null}async read(){let e=await this.open();return new Promise((t,n)=>{let i=e.transaction(Sn,"readonly").objectStore(Sn).get(this.key);i.onsuccess=()=>t(i.result??null),i.onerror=()=>n(vt(i.error,"mirror state read"))})}async write(e){let t=await this.open();await new Promise((n,i)=>{let s=t.transaction(Sn,"readwrite");s.objectStore(Sn).put(e,this.key),s.oncomplete=()=>n(),s.onerror=()=>i(vt(s.error,"mirror state write")),s.onabort=()=>i(vt(s.error,"mirror state write"))})}async clear(){let e=await this.open();await new Promise((t,n)=>{let i=e.transaction(Sn,"readwrite");i.objectStore(Sn).delete(this.key),i.oncomplete=()=>t(),i.onerror=()=>n(vt(i.error,"mirror state clear")),i.onabort=()=>n(vt(i.error,"mirror state clear"))})}close(){this.database?.then(e=>e.close(),()=>{}),this.database=null}open(){if(typeof indexedDB>"u")throw new b("storage_unavailable","IndexedDB is required for persistent mirror state.");return this.database??=new Promise((e,t)=>{let n=indexedDB.open(NN,1);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains(Sn)||n.result.createObjectStore(Sn)},n.onerror=()=>t(vt(n.error,"mirror state store open")),n.onsuccess=()=>e(n.result)}),this.database}},Qp=class r{constructor(e){this.key=e}static{this.active=new Set}async runExclusive(e){if(r.active.has(this.key))throw new b("mirror_busy","A mirror operation is already running for this vault.");r.active.add(this.key);try{return await e()}finally{r.active.delete(this.key)}}},ul=class{constructor(e,t,n={}){this.app=e;this.settingsHost=t;this.options=n;this.disposed=!1;this.lifetime=new AbortController;this.stateStores=new Map;this.blobStores=new Map;this.progress=null;this.fileProgress=null;this.syncAbort=null;this.statusRequest=null;this.mirrorOperationTail=Promise.resolve();this.adoptionMarker=null;this.fileSystem=n.fileSystem??new Jp(e.vault,i=>e.fileManager.trashFile(i),()=>this.assertActive()),this.enrollmentClient=n.enrollmentClient??new Bc({request:WN()}),this.adoptionClient=n.adoptionClient??new Gc({request:GN()})}dispose(){this.disposed=!0,this.lifetime.abort(),this.cancelSync();for(let e of this.stateStores.values())e.close();for(let e of this.blobStores.values())e.close();this.stateStores.clear(),this.blobStores.clear()}async withLifetime(e,t){this.assertActive();let n=new AbortController,i=()=>n.abort();this.lifetime.signal.addEventListener("abort",i,{once:!0}),e?.addEventListener("abort",i,{once:!0}),e?.aborted&&n.abort();try{return Se(n.signal),await t(n.signal)}finally{this.lifetime.signal.removeEventListener("abort",i),e?.removeEventListener("abort",i)}}assertActive(){if(this.disposed)throw new DOMException("Plugin unloaded.","AbortError")}async initialize(){this.adoptionMarker=await this.readAdoptionMarker();let e=this.settingsHost.getMirrorProfile();if(this.adoptionMarker&&e){if(this.adoptionMarker.phase==="adopted"&&this.adoptionMarker.session.requested.collectionId===e.collectionId){await this.assertMirror(e.collectionId),await this.clearAdoptionCheckpoint(this.adoptionMarker.session.adoptionId);return}throw new b("authority_adoption_state_conflict","This vault contains both an authority-adoption checkpoint and a mirror profile.")}}getProgress(){return this.progress?{...this.progress}:null}getFileProgress(){return this.fileProgress?{...this.fileProgress}:null}getAdoptionMarker(){return this.adoptionMarker?JSON.parse(JSON.stringify(this.adoptionMarker)):null}getSelectiveSync(){return xr(this.settingsHost.getMirrorProfile()?.selectiveSync??this.adoptionMarker?.selective_sync)}async configureSelectiveSync(e){let t=this.requireProfile();await this.settingsHost.saveMirrorProfile({...t,selectiveSync:xr(e)})}assertLocalAuthorityWritable(){if(this.assertActive(),this.adoptionMarker&&["fenced","activating","adopted"].includes(this.adoptionMarker.phase))throw new b("local_authority_fenced",this.adoptionMarker.phase==="adopted"?"Hosted mdbase is now authoritative. Finish reconnecting this vault as its mirror before editing.":"This local authority is frozen while its exact snapshot is adopted by hosted mdbase.")}async adoptLocalCollection(e,t){return this.withLifetime(t.signal,n=>this.adoptLocalCollectionActive(e,{...t,signal:n}))}async adoptLocalCollectionActive(e,t){if(this.settingsHost.getMirrorProfile())throw new b("mirror_already_configured","This vault already mirrors a collection authority.");if(this.adoptionMarker)return this.resumeAdoption(t);let n=await this.ensurePortableCollectionIdentity(),i=await this.adoptionClient.begin({controlUrl:e.controlUrl,collectionId:n.collectionId,displayName:n.displayName,sourceName:e.mirrorName,retainMirror:!0,mirrorName:e.mirrorName},t);return await this.storeAdoptionSecret(i),await this.writeAdoptionMarker({version:1,phase:"waiting_for_approval",session:zp(i),selective_sync:xr(e.selectiveSync),manifest_digest:null,source_revision:null,source_head:null}),await t.onVerification(zp(i)),this.runAdoptionWithRecovery(i,t)}async resumeAdoption(e={}){let t=this.adoptionMarker??await this.readAdoptionMarker();if(!t)throw new b("authority_adoption_not_found","This vault has no collection-adoption checkpoint.");this.adoptionMarker=t;let n=this.app.secretStorage.getSecret(this.adoptionSecretId(t.session.adoptionId));if(!n)throw new b("authority_adoption_credentials_missing","The collection-adoption credential is missing from Obsidian's secret store.");let i={...t.session,credential:n};return t.phase==="waiting_for_approval"&&await e.onVerification?.(zp(i)),this.withLifetime(e.signal,s=>this.runAdoptionWithRecovery(i,{...e,signal:s,onVerification:o=>e.onVerification?.(o)}))}async cancelAdoption(e){return this.withLifetime(e,t=>this.cancelAdoptionActive(t))}async cancelAdoptionActive(e){let t=this.adoptionMarker??await this.readAdoptionMarker();if(!t)return;if(["activating","adopted"].includes(t.phase))throw new b("authority_adoption_activation_started","Hosted activation has started and must be resumed; it can no longer be cancelled.");let n=this.app.secretStorage.getSecret(this.adoptionSecretId(t.session.adoptionId));if(!n)throw new b("authority_adoption_credentials_missing","The collection-adoption credential is missing from Obsidian's secret store.");await this.adoptionClient.cancel({...t.session,credential:n},{signal:e}),await this.clearAdoptionCheckpoint(t.session.adoptionId)}async enroll(e,t){return this.withLifetime(t.signal,n=>this.enrollActive(e,{...t,signal:n}))}async enrollActive(e,t){let n=await this.assertCanBecomeMirror(e.collectionId),i=await this.enrollmentClient.enroll({controlUrl:e.controlUrl,mirrorName:e.mirrorName,mode:e.mode,...n?{collectionId:n}:{}},t),s=await this.markMirror(i.collectionId);try{await this.persistEnrollment(i,e.selectiveSync)}catch(o){if(s)try{await this.app.vault.adapter.remove(ts)}catch{throw new b("enrollment_recovery_required",`Enrollment settings could not be saved and the temporary role marker could not be removed: ${o instanceof Error?o.message:String(o)}`)}throw o}return this.requireProfile()}async preview(){return this.withMirrorOperation(async()=>{let e=this.requireProfile();await this.assertMirror(e.collectionId);let t=await this.createMirror();return B0(await t.inspect())})}async status(){if(this.statusRequest)return this.statusRequest;let e=this.readStatus();this.statusRequest=e;try{return await e}finally{this.statusRequest===e&&(this.statusRequest=null)}}async readStatus(){return this.withMirrorOperation(async()=>{let e=this.settingsHost.getMirrorProfile();return e?(await this.assertMirror(e.collectionId),(await this.createMirror()).status()):null})}async reconnect(){let e=this.requireProfile(),t=this.app.secretStorage.getSecret(this.refreshSecretId(e.collectionId));if(!t)throw new b("mirror_credentials_missing","The mirror refresh credential is missing. Approve this vault again.");let n=await this.enrollmentClient.renew({controlUrl:e.controlUrl,syncUrl:e.syncUrl,collectionId:e.collectionId,replicaId:e.replicaId,mode:e.mode,name:e.name,enrollmentId:e.enrollmentId,accessToken:this.app.secretStorage.getSecret(this.accessSecretId(e.collectionId))??"",refreshCredential:t,accessTokenExpiresAt:e.accessTokenExpiresAt});await this.persistEnrollment(n,e.selectiveSync);let i=await this.status();if(!i)throw new b("mirror_not_configured","The renewed mirror profile could not be loaded.");return i}async reauthorize(e){return this.withLifetime(e.signal,t=>this.reauthorizeActive({...e,signal:t}))}async reauthorizeActive(e){let t=this.requireProfile(),n=this.stateStoreFor(t),i=await n.read();if(i?.batch)throw new b("mirror_recovery_required","Resume the durable synchronization checkpoint before approving this vault again.");let s=await this.enrollmentClient.enroll({controlUrl:t.controlUrl,mirrorName:t.name,mode:t.mode,collectionId:t.collectionId},e);if(s.collectionId!==t.collectionId)throw new b("mirror_identity_conflict","Connect approved a different collection. The existing mirror was not changed.");let o=K0(s,t.selectiveSync);i&&s.replicaId!==t.replicaId&&await this.stateStoreFor(o).write({...i,replica_id:s.replicaId}),await this.persistEnrollment(s,t.selectiveSync),s.replicaId!==t.replicaId&&"clear"in n&&typeof n.clear=="function"&&await n.clear();let a=await this.status();if(!a)throw new b("mirror_not_configured","The reauthorized mirror profile could not be loaded.");return a}async conflictComparison(e){let t=this.requireProfile(),n=await this.transportFor(t),i=await n.openSession(),s={state:"absent"};if(e.entity==="record"){let l;do{let u=await n.snapshot(i.snapshot_id,l),d=u.records.find(f=>f.record_id===e.object_id);if(d){s={state:"exact",path:d.path,revision:d.revision,size:new TextEncoder().encode(d.document).byteLength,document:d.document};break}l=u.next_page}while(l)}else{let l;do{let u=await n.fileSnapshot(i.snapshot_id,l),d=u.files.find(f=>f.file_id===e.object_id);if(d){s={state:"exact",path:d.path,revision:d.content_digest,size:d.size,modifiedAt:d.modified_at};break}l=u.next_page}while(l)}let o=e.path??s.path,a={state:"absent"};if(o){let l=this.app.vault.getAbstractFileByPath(o);if(l instanceof te.TFile)if(e.entity==="record"){let u=await this.app.vault.cachedRead(l);a={state:"exact",path:o,size:new TextEncoder().encode(u).byteLength,modifiedAt:l.stat?.mtime?new Date(l.stat.mtime).toISOString():void 0,document:u}}else{let u=await this.fileSystem.inspectBinary(o);u&&(a={state:"exact",path:o,revision:u.content_digest,size:u.size,modifiedAt:l.stat?.mtime?new Date(l.stat.mtime).toISOString():void 0,resourceUrl:this.app.vault.getResourcePath(l)})}}if(!(await this.status())?.conflicts.some(l=>l.object_id===e.object_id&&l.decision_id===e.decision_id))throw new b("conflict_decision_stale","This file changed again while its versions were loading.");return{entity:e.entity,objectId:e.object_id,decisionId:e.decision_id,local:a,remote:s}}async preserveConflictCopy(e){let t=ni(this.app.vault,e),n=this.app.vault.getAbstractFileByPath(t);if(!(n instanceof te.TFile))throw new b("mirror_conflict_copy_missing",`No local file exists at ${t}.`);let i=n.extension?`.${n.extension}`:"",s=i?t.slice(0,-i.length):t,o=`${s} (local conflict copy)${i}`,a=2;for(;this.app.vault.getAbstractFileByPath(o)||await this.app.vault.adapter.exists(o);)o=`${s} (local conflict copy ${a})${i}`,a+=1;return await this.app.vault.adapter.copy(t,o),o}async disconnect(e){if(this.isSyncing())throw new b("mirror_busy","Stop the current synchronization before disconnecting.");let t=this.requireProfile(),n=this.stateStoreFor(t),i=await n.read();if(i?.batch)throw new b("mirror_recovery_required","Resume the durable synchronization checkpoint before disconnecting.");let s={removed:[],preserved:[]},o=await this.readMarker();o&&await this.app.vault.adapter.remove(ts);try{await this.settingsHost.saveMirrorProfile(null)}catch(a){throw o&&await this.markMirror(t.collectionId),a}return this.app.secretStorage.setSecret(this.accessSecretId(t.collectionId),""),this.app.secretStorage.setSecret(this.refreshSecretId(t.collectionId),""),"clear"in n&&typeof n.clear=="function"&&await n.clear(),await this.blobStoreFor(t).prune(new Set),e&&i&&await this.removeExactMirrorFiles(i,s),s}async sync(e,t,n){if(this.syncAbort)throw new b("mirror_busy","Synchronization is already running for this vault.");let i=new AbortController;this.syncAbort=i;try{return await this.withMirrorOperation(async()=>{let o=await(await this.createMirror(a=>{Se(i.signal),this.progress=a,t?.({...a})},i.signal,a=>{Se(i.signal),this.fileProgress=a,n?.({...a})})).apply(e.plan,{signal:i.signal});return Se(i.signal),o})}finally{this.progress=null,this.fileProgress=null,this.syncAbort=null}}cancelSync(){this.syncAbort?.abort()}isSyncing(){return this.syncAbort!==null}async resolveConflict(e,t,n){return this.withMirrorOperation(async()=>{let i=await this.createMirror();return await i.resolveConflict(e,t,n),i.status()})}async withMirrorOperation(e){let t=this.mirrorOperationTail,n;this.mirrorOperationTail=new Promise(i=>{n=i}),await t;try{return this.assertActive(),await e()}finally{n()}}async runAdoption(e,t){let n=this.requireAdoptionMarker(e.adoptionId),i=null;if(n.phase==="adopted"){let s=await this.adoptionClient.exchange(e,t);if(s.status!=="completed")throw new b("authority_adoption_state_conflict","The local checkpoint says adoption completed, but Connect does not.");i=s}else if(n.phase==="activating"){let s=await this.readAdoptionSnapshot(n),o=await this.adoptionClient.exchange(e,t);i=o.status==="completed"?o:await this.adoptionClient.complete(e,s,t)}else if(n.phase==="fenced"){let s=await this.readAdoptionSnapshot(n),o=await this.adoptionClient.exchange(e,t);o.status==="completed"?i=o:(o.status==="ready"&&await this.adoptionClient.uploadSnapshot(e,o,s,this.adoptionUploadOptions(e,t)),await this.updateAdoptionPhase("activating",s),i=await this.adoptionClient.complete(e,s,t))}else{let s=n.phase==="waiting_for_approval"?await this.adoptionClient.waitForApproval(e,t):await this.requirePreparedAdoption(e,t),o=await this.captureAuthoritySnapshot(e.requested.collectionId,t.signal);await this.updateAdoptionPhase("uploading"),await this.adoptionClient.uploadSnapshot(e,s,o,this.adoptionUploadOptions(e,t));let a=await this.captureAuthoritySnapshot(e.requested.collectionId,t.signal);await this.writeAdoptionSnapshot(a),await this.updateAdoptionPhase("fenced",a);let c=await this.requirePreparedAdoption(e,t);await this.adoptionClient.uploadSnapshot(e,c,a,this.adoptionUploadOptions(e,t)),await this.updateAdoptionPhase("activating",a),i=await this.adoptionClient.complete(e,a,t)}return await this.updateAdoptionPhase("adopted"),this.finishRetainedMirror(e,i,t)}async runAdoptionWithRecovery(e,t){try{return await this.runAdoption(e,t)}catch(n){throw ZN(n)?(await this.adoptionClient.cancel(e,{signal:t.signal}).catch(()=>{}),await this.clearAdoptionCheckpoint(e.adoptionId),new b(n.code,"This adoption ended before hosted activation. The vault remains the writable local authority; start a new adoption to try again.")):n}}async requirePreparedAdoption(e,t){let n=await this.adoptionClient.exchange(e,t);if(n.status==="ready")return n;throw n.status==="activating"?new qr("Hosted authority activation has already started. Resume using the saved fenced snapshot."):new b("authority_adoption_already_completed","Hosted authority has already adopted this collection.")}async finishRetainedMirror(e,t,n){let i,s=this.adoptionClient.mirrorEnrollmentSession(e,t);if(!s)throw new b("authority_adoption_mirror_missing","Hosted authority activated without retaining this vault as a mirror.");try{i=await this.enrollmentClient.waitForApproval(s,{signal:n.signal,onStatus:a=>n.onStatus?.({...a,state:a.state})})}catch(a){if(n.signal?.aborted)throw a;i=await this.enrollmentClient.enroll({controlUrl:e.controlUrl,collectionId:e.requested.collectionId,mirrorName:e.requested.mirrorName??e.requested.sourceName,mode:"read_write"},{signal:n.signal,onVerification:c=>n.onVerification(c)})}let o=await this.markMirror(i.collectionId);try{await this.persistEnrollment(i,this.adoptionMarker?.selective_sync)}catch(a){throw o&&await this.app.vault.adapter.remove(ts),a}return await this.clearAdoptionCheckpoint(e.adoptionId),this.requireProfile()}async captureAuthoritySnapshot(e,t){Se(t);let n=await Ii(this.app.vault);if(Se(t),!n)throw new b("invalid_collection_configuration","A valid mdbase.yaml is required.");let i=await this.app.vault.adapter.read("mdbase.yaml");Se(t);let s=(0,te.parseYaml)(i),o=[{path:"mdbase.yaml",kind:"configuration",document:i}],a=`${(0,te.normalizePath)(n.settings.types_folder)}/`,c=this.app.vault.getMarkdownFiles().filter(p=>(0,te.normalizePath)(p.path).startsWith(a)).sort((p,m)=>p.path.localeCompare(m.path));for(let p of c)Se(t),o.push({path:(0,te.normalizePath)(p.path),kind:"type",document:await this.app.vault.cachedRead(p)}),Se(t);let l=eD(s);if(l.length){let p=l.map(h=>(0,W0.default)(h,{dot:!0})),m=X0(this.app.vault).filter(h=>h.extension==="base").filter(h=>p.some(y=>y((0,te.normalizePath)(h.path)))).sort((h,y)=>h.path.localeCompare(y.path));for(let h of m)Se(t),o.push({path:(0,te.normalizePath)(h.path),kind:"view",document:await this.app.vault.cachedRead(h)}),Se(t)}let u=[];for(let p of this.app.vault.getMarkdownFiles().sort((m,h)=>m.path.localeCompare(h.path))){Se(t);let m=(0,te.normalizePath)(p.path);if(Ti(m,n))continue;let h=await this.app.vault.cachedRead(p);Se(t),u.push({path:m,document:h})}let d=[],f=xr(this.adoptionMarker?.selective_sync);if(f.file_classes.length){let p=this.adoptionBlobStore(e),m=(await this.fileSystem.listBinary?.(new Set(o.map(h=>h.path)))??[]).filter(h=>zN(f,h)).filter(h=>!Ti(h,n));for(let h of m){Se(t);let y=await this.fileSystem.readBinary?.(h);if(Se(t),!y)continue;let g=await Kp(y);Se(t);let _=await Hp(g);Se(t),await p.write(_.content_digest,(async function*(){yield new Uint8Array(g)})()),Se(t);let v=this.app.vault.getAbstractFileByPath(h);d.push({file_id:Wc(e,`file:${h}`),path:h,revision:_.content_digest,..._,...V0(h)?{media_type:V0(h)}:{},media_class:G0(h),modified_at:new Date(v instanceof te.TFile&&v.stat?.mtime?v.stat.mtime:Date.now()).toISOString()})}}return Se(t),Ap({collectionId:e,sourceHead:0,specVersion:n.spec_version,resources:o,records:u,files:d})}adoptionBlobStore(e){return this.options.adoptionBlobStoreFactory?.(e)??this.cachedBlobStore(`adoption:${e}`)}adoptionUploadOptions(e,t){let n=this.adoptionBlobStore(e.requested.collectionId);return{signal:t.signal,fileSource:async i=>Kp(n.read(i.content_digest)),onFileProgress:({file:i,transferredBytes:s,totalBytes:o})=>t.onFileProgress?.(i.path,s,o)}}async ensurePortableCollectionIdentity(){if(!await this.app.vault.adapter.exists("mdbase.yaml"))throw new b("collection_not_initialized","Initialize an mdbase collection before hosting it.");let e=await this.app.vault.adapter.read("mdbase.yaml"),t;try{t=(0,te.parseYaml)(e)}catch{throw new b("invalid_collection_configuration","mdbase.yaml must contain valid YAML.")}if(!ot(t))throw new b("invalid_collection_configuration","mdbase.yaml must contain a YAML mapping.");let n=ot(t["x-mdbase-connect"])?t["x-mdbase-connect"].collection_id:void 0,i;if(n===void 0){i=crypto.randomUUID();let o=ot(t["x-mdbase-connect"])?t["x-mdbase-connect"]:{};t["x-mdbase-connect"]={...o,collection_id:i},this.assertActive(),await this.app.vault.adapter.write("mdbase.yaml",(0,te.stringifyYaml)(t))}else if(typeof n=="string"&&Io.test(n))i=n;else throw new b("invalid_collection_configuration","x-mdbase-connect.collection_id must be a UUID string.");let s=typeof t.name=="string"&&t.name.trim()?t.name.trim():this.app.vault.getName();return{collectionId:i,displayName:s}}stateStoreFor(e){if(this.assertActive(),this.options.stateStoreFactory)return this.options.stateStoreFactory(e);let t=`${e.collectionId}:${e.replicaId}`,n=this.stateStores.get(t);return n||(n=new Xp(t),this.stateStores.set(t,n)),n}cachedBlobStore(e){this.assertActive();let t=this.blobStores.get(e);return t||(t=new Yp(e),this.blobStores.set(e,t)),t}blobStoreFor(e){return this.options.blobStoreFactory?.(e)??this.cachedBlobStore(`${e.collectionId}:${e.replicaId}`)}async transportFor(e,t,n){this.assertActive();let i=await this.freshAccessToken(e);this.assertActive();let s=this.options.transportFactory?.(e,i)??new Wp(e.syncUrl,i,fl,n);return QN(s,t)}async createMirror(e,t,n){let i=this.requireProfile();await this.assertMirror(i.collectionId);let s=await this.transportFor(i,t,n),o={stateStore:this.stateStoreFor(i),fileSystem:this.fileSystem,blobStore:this.blobStoreFor(i),selectiveSync:xr(i.selectiveSync),lease:this.options.leaseFactory?.(i)??new Qp(`${i.collectionId}:${i.replicaId}`),onProgress:e};return i.mode==="read_write"?new ko(i.replicaId,s,o):new ri(i.replicaId,s,o)}requireProfile(){this.assertActive();let e=this.settingsHost.getMirrorProfile();if(!e)throw new b("mirror_not_configured","This vault is not connected to a collection authority.");return e}accessSecretId(e){return`${jN}${e.toLowerCase()}`}refreshSecretId(e){return`${qN}${e.toLowerCase()}`}adoptionSecretId(e){return`${FN}${e.toLowerCase()}`}async storeAdoptionSecret(e){this.app.secretStorage.setSecret(this.adoptionSecretId(e.adoptionId),e.credential)}async persistEnrollment(e,t){this.assertActive(),this.app.secretStorage.setSecret(this.accessSecretId(e.collectionId),e.accessToken),this.app.secretStorage.setSecret(this.refreshSecretId(e.collectionId),e.refreshCredential),await this.settingsHost.saveMirrorProfile(K0(e,t??this.settingsHost.getMirrorProfile()?.selectiveSync))}async removeExactMirrorFiles(e,t){let n=new Map;for(let[i,s]of Object.entries(e.records)){let o=e.local_bindings?.[i]?.path??s.path;n.set(o,{entity:"document",document:s.record?.document,revision:s.revision})}for(let[i,s]of Object.entries(e.resources??{})){let o=e.local_bindings?.[i]?.path??s.path;n.set(o,{entity:"document",document:s.record?.document,revision:s.revision})}for(let[i,s]of Object.entries(e.files??{})){let o=e.local_bindings?.[i]?.path??s.file.path;n.set(o,{entity:"file",digest:s.file.content_digest,size:s.file.size})}for(let[i,s]of[...n.entries()].sort(([o],[a])=>a.localeCompare(o))){let o=!1;if(s.entity==="document"){let a=await this.fileSystem.read(i);o=a!==null&&(s.document!==void 0?a===s.document:await nD(a,s.revision))}else if(s.entity==="file"){let a=await this.fileSystem.inspectBinary(i);o=a!==null&&a.content_digest===s.digest&&a.size===s.size}if(!o){await this.fileSystem.exists(i)&&t.preserved.push(i);continue}try{await this.fileSystem.remove(i),t.removed.push(i)}catch{t.preserved.push(i)}}}async freshAccessToken(e){let t=this.accessSecretId(e.collectionId),n=this.app.secretStorage.getSecret(t),i=Date.parse(e.accessTokenExpiresAt);if(n&&Number.isFinite(i)&&i-Date.now()>UN)return n;let s=this.app.secretStorage.getSecret(this.refreshSecretId(e.collectionId));if(!s)throw new b("mirror_credentials_missing","The mirror refresh credential is missing. Re-enroll this vault.");let o=await this.enrollmentClient.renew({controlUrl:e.controlUrl,syncUrl:e.syncUrl,collectionId:e.collectionId,replicaId:e.replicaId,mode:e.mode,name:e.name,enrollmentId:e.enrollmentId,accessToken:n??"",refreshCredential:s,accessTokenExpiresAt:e.accessTokenExpiresAt});return await this.persistEnrollment(o,e.selectiveSync),o.accessToken}async assertCanBecomeMirror(e){let t=await this.readMarker(),n=await this.readPortableCollectionId();if(n&&!t)throw new b("local_authority_requires_transfer","This vault has a local Connect identity. Transfer authority explicitly before using it as a mirror.");if(n&&t?.collection_id!==n)throw new b("mirror_identity_conflict","The vault identity and mirror role marker identify different collections.");let i=this.settingsHost.getMirrorProfile(),s=i?.collectionId??e;if(t&&s&&t.collection_id!==s)throw new b("mirror_identity_conflict","This vault is already marked as a different mirror.");if(!t&&!i&&await this.app.vault.adapter.exists("mdbase.yaml"))throw new b("existing_collection_requires_transfer","This vault already contains an mdbase collection. Connect an empty vault, or transfer collection authority explicitly.");return s??t?.collection_id}async readPortableCollectionId(){if(!await this.app.vault.adapter.exists("mdbase.yaml"))return null;let e;try{e=(0,te.parseYaml)(await this.app.vault.adapter.read("mdbase.yaml"))}catch{throw new b("invalid_collection_configuration","mdbase.yaml must contain valid YAML.")}if(!ot(e))throw new b("invalid_collection_configuration","mdbase.yaml must contain a YAML mapping.");let t=e["x-mdbase-connect"];if(t===void 0)return null;if(!ot(t))throw new b("invalid_collection_configuration","x-mdbase-connect must be a YAML mapping.");let n=t.collection_id;if(n===void 0)return null;if(typeof n!="string"||!Io.test(n))throw new b("invalid_collection_configuration","x-mdbase-connect.collection_id must be a UUID string.");return n}async markMirror(e){this.assertActive();let t=await this.readMarker();if(t){if(t.collection_id!==e)throw new b("mirror_identity_conflict","This vault already mirrors a different collection authority.");return!1}return await rs(this.app.vault,".mdbase"),this.assertActive(),await this.app.vault.adapter.write(ts,`${JSON.stringify({version:1,role:"mirror",collection_id:e},null,2)} `),!0}async assertMirror(e){let t=await this.readMarker();if(!t||t.collection_id!==e)throw new b("mirror_marker_missing","The vault's mirror role marker is missing or does not match this connection.")}async readMarker(){if(!await this.app.vault.adapter.exists(ts))return null;let e;try{e=JSON.parse(await this.app.vault.adapter.read(ts))}catch{throw new b("invalid_mirror_marker","The mirror role marker is corrupt.")}if(!ot(e)||e.version!==1||e.role!=="mirror"||typeof e.collection_id!="string"||!Io.test(e.collection_id))throw new b("invalid_mirror_marker","The mirror role marker is invalid.");return e}requireAdoptionMarker(e){if(!this.adoptionMarker||this.adoptionMarker.session.adoptionId!==e)throw new b("authority_adoption_state_conflict","The collection-adoption checkpoint does not match this approval.");return this.adoptionMarker}async updateAdoptionPhase(e,t){if(!this.adoptionMarker)throw new b("authority_adoption_not_found","Collection-adoption checkpoint is missing.");await this.writeAdoptionMarker({...this.adoptionMarker,phase:e,...t?{manifest_digest:t.manifest_digest,source_revision:t.source_revision,source_head:t.source_head}:{}})}async writeAdoptionMarker(e){await rs(this.app.vault,".mdbase"),this.assertActive(),await this.app.vault.adapter.write(Po,`${JSON.stringify(e,null,2)} `),this.adoptionMarker=e}async readAdoptionMarker(){if(!await this.app.vault.adapter.exists(Po))return null;let e;try{e=JSON.parse(await this.app.vault.adapter.read(Po))}catch{throw new b("invalid_authority_adoption_checkpoint","The collection-adoption checkpoint is corrupt.")}if(!tD(e))throw new b("invalid_authority_adoption_checkpoint","The collection-adoption checkpoint is invalid.");return e}async writeAdoptionSnapshot(e){this.assertActive(),await rs(this.app.vault,".mdbase"),this.assertActive(),await this.app.vault.adapter.write(Co,JSON.stringify(e))}async readAdoptionSnapshot(e){if(!await this.app.vault.adapter.exists(Co))throw new b("authority_adoption_snapshot_missing","The fenced authority snapshot is missing; hosted activation cannot be resumed safely.");let t;try{t=JSON.parse(await this.app.vault.adapter.read(Co))}catch{throw new b("invalid_authority_adoption_snapshot","The fenced authority snapshot is corrupt.")}if(t.collection_id!==e.session.requested.collectionId||t.manifest_digest!==e.manifest_digest||t.source_revision!==e.source_revision||t.source_head!==e.source_head)throw new b("authority_adoption_snapshot_mismatch","The fenced authority snapshot does not match its durable checkpoint.");return t}async clearAdoptionCheckpoint(e){let t=this.adoptionMarker?.session.requested.collectionId;await this.app.vault.adapter.exists(Co)&&await this.app.vault.adapter.remove(Co),await this.app.vault.adapter.exists(Po)&&await this.app.vault.adapter.remove(Po),this.app.secretStorage.setSecret(this.adoptionSecretId(e),""),this.adoptionMarker=null,t&&await this.adoptionBlobStore(t).prune(new Set).catch(()=>{})}};function zp(r){let{credential:e,...t}=r;return t}function ZN(r){return r instanceof K&&["authority_adoption_expired","authority_adoption_cancelled"].includes(r.code)}function eD(r){if(!ot(r))return[];let e=r["x-obsidian"];return!ot(e)||!ot(e.bases)||!Array.isArray(e.bases.include)?[]:e.bases.include.filter(t=>typeof t=="string")}function X0(r){let e=r.getFiles?.();if(e)return e;let t=[],n=i=>{for(let s of i.children)s instanceof te.TFile?t.push(s):s instanceof te.TFolder&&n(s)};return n(r.getRoot()),t}function tD(r){if(!ot(r)||r.version!==1||!["waiting_for_approval","uploading","fenced","activating","adopted"].includes(String(r.phase))||!ot(r.session))return!1;let e=r.session;return typeof e.controlUrl=="string"&&typeof e.adoptionId=="string"&&Io.test(e.adoptionId)&&typeof e.verificationUri=="string"&&typeof e.expiresAt=="string"&&ot(e.requested)&&typeof e.requested.collectionId=="string"&&Io.test(e.requested.collectionId)&&typeof e.requested.displayName=="string"&&typeof e.requested.sourceName=="string"&&e.requested.retainMirror===!0&&(r.selective_sync===void 0||rD(r.selective_sync))&&(r.manifest_digest===null||typeof r.manifest_digest=="string")&&(r.source_revision===null||typeof r.source_revision=="string")&&(r.source_head===null||Number.isSafeInteger(r.source_head))}function rD(r){if(!ot(r)||!Array.isArray(r.file_classes)||!Array.isArray(r.excluded_folders))return!1;try{let e=xr(r);return e.file_classes.length===r.file_classes.length&&e.excluded_folders.length===r.excluded_folders.length}catch{return!1}}function K0(r,e){return{version:1,syncUrl:r.syncUrl,controlUrl:r.controlUrl,collectionId:r.collectionId,replicaId:r.replicaId,mode:r.mode,name:r.name,enrollmentId:r.enrollmentId,accessTokenExpiresAt:r.accessTokenExpiresAt,selectiveSync:xr(e)}}async function nD(r,e){if(!e?.startsWith("sha256:"))return!1;let t=new Uint8Array(await crypto.subtle.digest("SHA-256",new TextEncoder().encode(r)));return`sha256:${Array.from(t,i=>i.toString(16).padStart(2,"0")).join("")}`===e}var at=require("obsidian");var No="0.3.0",iD=new Set(["name","description","display_name_key","strict","path_pattern","filename_pattern","match","fields","extends"]),sD=new Set(["type","required","default","description","values","items","fields","min","max","min_length","max_length","pattern","unique","deprecated","generated","computed","target","validate_exists","tn_role","tn_completed_values"]);function _e(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)}function Pe(r){return r===void 0?r:JSON.parse(JSON.stringify(r))}function Hr(r){return So.digest(r)}function si(r){return Array.isArray(r)?`[${r.map(si).join(",")}]`:_e(r)?`{${Object.keys(r).sort().map(e=>`${JSON.stringify(e)}:${si(r[e])}`).join(",")}}`:JSON.stringify(r)}function Zp(r){if(Array.isArray(r))return r.map(Zp);if(!_e(r))return r;let e={};for(let[t,n]of Object.entries(r)){let i=Zp(n);i!=null&&(Array.isArray(i)&&i.length===0||_e(i)&&Object.keys(i).length===0||(e[t]=i))}return e}function ns(r){return[...new Set(r)]}function oD(r){return _e(r.fields)?Object.values(r.fields).some(e=>_e(e)&&(typeof e.tn_role=="string"||Array.isArray(e.tn_completed_values))):!1}function eh(r,e,t){let n=_e(e)?e:{},i={},s={},o=[],a;switch(n.type){case"any":a={};break;case"string":case"integer":case"number":case"boolean":a={type:n.type};break;case"date":case"datetime":case"time":a={type:"string",format:n.type==="datetime"?"date-time":n.type};break;case"enum":a={enum:Array.isArray(n.values)?Pe(n.values):[]};break;case"link":a={type:"string"},i[r]={target_type:typeof n.target=="string"?n.target:r.endsWith("Parent")||r.endsWith("uid")?"task":"any",validate_exists:n.validate_exists===!0};break;case"list":{let c=eh(`${r}[]`,n.items,t);a={type:"array",items:c.schema},Object.assign(i,c.links),Object.assign(s,c.legacy),o.push(...c.unsupported);break}case"object":{let c={},l=[];for(let[u,d]of Object.entries(_e(n.fields)?n.fields:{})){let f=eh(`${r}.${u}`,d,t);c[u]=f.schema,Object.assign(i,f.links),Object.assign(s,f.legacy),o.push(...f.unsupported),_e(d)&&d.required===!0&&l.push(u)}t&&r==="blockedBy[]"&&l.push("uid"),a={type:"object",additionalProperties:Object.keys(c).length===0,properties:c,...l.length?{required:ns(l)}:{}};break}default:a={},o.push(`${r}.type`);break}t&&r==="title"&&(a.minLength=1,a.description="Short summary of the task."),typeof n.description=="string"&&(a.description=n.description),typeof n.min=="number"&&(n.type==="string"?a.minLength=n.min:n.type==="list"?a.minItems=n.min:a.minimum=n.min),typeof n.max=="number"&&(n.type==="string"?a.maxLength=n.max:n.type==="list"?a.maxItems=n.max:a.maximum=n.max),typeof n.min_length=="number"&&(n.type==="list"?a.minItems=n.min_length:a.minLength=n.min_length),typeof n.max_length=="number"&&(n.type==="list"?a.maxItems=n.max_length:a.maxLength=n.max_length),typeof n.pattern=="string"&&(a.pattern=n.pattern),n.deprecated===!0&&(a.deprecated=!0),n.default!==void 0&&(a.default=Pe(n.default)),n.computed!==void 0&&o.push(`${r}.computed`);for(let[c,l]of Object.entries(n)){let u=t&&(c==="tn_role"||c==="tn_completed_values");(!sD.has(c)||(c==="tn_role"||c==="tn_completed_values")&&!u)&&(s[`${r}.${c}`]=Pe(l))}return{schema:a,links:i,legacy:s,unsupported:o}}function To(r,e,t,n){let i=_e(r[e])?r[e]:{},s=_e(i.set)?i.set:{};s[t]=n,i.set=s,r[e]=i}function aD(r,e,t){if(t==="now")To(r,"on_create",e,{now:!0});else if(t==="now_on_write")To(r,"on_update",e,{now:!0});else if(t==="uuid")To(r,"on_create",e,{uuid:!0});else if(t==="ulid")To(r,"on_create",e,{ulid:!0});else if(_e(t)&&t.transform==="slugify"&&typeof t.from=="string")To(r,"on_create",e,{slugify:t.from});else return!1;return!0}function cD(r){let e=r.match(/^(.*\/)?\{title\}\.md$/);return e?{runtime:"tasknotes",template:"{{title}}",folder:(e[1]??"").replace(/\/$/,""),generated_by:"tasknotes.filename.create"}:{runtime:"tasknotes",template:r,generated_by:"tasknotes.filename.create"}}function lD(r,e,t){if(t.kind==="mdbase.type"||t.schema!==void 0)throw new Error(`${r} already looks like a v0.3 type.`);if(typeof t.name!="string"||!_e(t.fields))throw new Error(`${r} is not a v0.2 type with a name and fields.`);let n=oD(t),i=t.name.trim().toLowerCase(),s={type:{const:i}},o=[],a={},c={},l=[],u={},d={},f=[],p=[],m={},h={},y={};for(let[x,C]of Object.entries(t.fields)){let $=_e(C)?C:{},M=eh(x,$,n);s[x]=M.schema,Object.assign(c,M.links),Object.assign(d,M.legacy),f.push(...M.unsupported),$.required===!0&&o.push(x),$.default!==void 0&&(a[x]=Pe($.default)),$.unique===!0&&l.push({field:x,scope:"collection"}),$.generated!==void 0&&(aD(u,x,$.generated)?p.push(x):(d[`${x}.generated`]=Pe($.generated),f.push(`${x}.generated`))),typeof $.tn_role=="string"&&(m[$.tn_role]=x),Array.isArray($.tn_completed_values)&&(h.completed_values=Pe($.tn_completed_values))}a.status!==void 0&&(h.default=Pe(a.status)),a.priority!==void 0&&(y.default=Pe(a.priority));let g=typeof t.display_name_key=="string"&&Object.prototype.hasOwnProperty.call(t.fields,t.display_name_key)?t.display_name_key:void 0,_={...g?{display:{name_field:g}}:{},read_defaults:a,links:c,unique:l};typeof t.path_pattern=="string"&&(_.path=n?cD(t.path_pattern):{pattern:t.path_pattern});let v={};for(let[x,C]of Object.entries(t))iD.has(x)||(v[x]=Pe(C));Object.keys(d).length&&(v.fields=d);let w=Zp({kind:"mdbase.type",name:i,version:1,description:typeof t.description=="string"?t.description:void 0,match:_e(t.match)?Pe(t.match):void 0,schema:{dialect:"json-schema-2020-12",value:{$schema:"https://json-schema.org/draft/2020-12/schema",type:"object",additionalProperties:t.strict!==!0,properties:s,...o.length?{required:ns(o)}:{}}},collection:_,lifecycle:u,...n?{"x-tasknotes":{contract:"tasknotes.task",version:1,field_roles:m,status:h,priority:y,archive:{tags_field:m.tags??"tags",archived_tag:"archived"}}}:{},...Object.keys(v).length?{"x-legacy-v0.2":v}:{}}),S=[];t.extends!==void 0&&f.push("extends");for(let x of ns(f).sort())S.push({path:r,code:"migration_lossy",message:`${x} cannot be expressed as canonical v0.3 write behavior and was retained as legacy metadata where possible.`,severity:"lossy"});return n&&S.push({path:r,code:"path_policy_runtime_owned",message:"TaskNotes filename behavior is recorded as TaskNotes runtime metadata.",severity:"warning"}),t.strict!==!0&&S.push({path:r,code:"additional_properties_true",message:"The migrated schema allows additional properties because the source type was not strict.",severity:"warning"}),typeof t.display_name_key=="string"&&!g&&S.push({path:r,code:"display_field_missing",message:`The display field '${t.display_name_key}' is not declared, so collection.display was omitted.`,severity:"warning"}),{target:w,summary:{path:r,name:i,fieldsConverted:Object.keys(t.fields).length,requiredFields:ns(o),defaultsMoved:Object.keys(a),generatedFieldsMoved:ns(p),linksMoved:Object.keys(c),taskNotes:n},diagnostics:S}}function dD(r){let e=Pe(r);e.spec_version=No;let t=_e(e.settings)?e.settings:{};if(e.settings=t,!Array.isArray(t.record_extensions)){let n=Array.isArray(t.extensions)?t.extensions.map(String).map(i=>i.replace(/^\./,"")):[];t.record_extensions=ns(["md",...n])}return Array.isArray(t.explicit_type_keys)||(t.explicit_type_keys=["type","types"]),typeof t.include_subfolders!="boolean"&&(t.include_subfolders=!0),t.validation===void 0&&typeof t.default_validation=="string"&&(t.validation=t.default_validation),t.validation===void 0&&typeof e.default_validation=="string"&&(t.validation=e.default_validation),delete t.default_validation,delete t.extensions,delete e.default_validation,e}function Q0(r,e){let t=_e(r.settings)?r.settings:{};return{spec_version:e,name:typeof r.name=="string"?r.name:void 0,description:typeof r.description=="string"?r.description:void 0,settings:{types_folder:typeof t.types_folder=="string"?t.types_folder:"_types",explicit_type_keys:Array.isArray(t.explicit_type_keys)?t.explicit_type_keys.filter(n=>typeof n=="string"):["type","types"],default_strict:t.default_strict===!0,include_subfolders:t.include_subfolders!==!1,exclude:Array.isArray(t.exclude)?t.exclude.filter(n=>typeof n=="string"):["_types",".obsidian",".git",".mdbase"]}}}function uD(r,e){let t={};for(let[n,i]of Object.entries(_e(e.fields)?e.fields:{}))_e(i)&&(t[n]=Pe(i));return{name:typeof e.name=="string"?e.name:r.split("/").pop()?.replace(/\.md$/,"")??"type",fields:t,match:_e(e.match)?Pe(e.match):void 0,filePath:r,specProfile:"v0.2"}}function fD(r,e){let t=_e(e.schema)?e.schema:{},n=_e(t.value)?t.value:{};return{name:typeof e.name=="string"?e.name:r.split("/").pop()?.replace(/\.md$/,"")??"type",fields:Un(n),match:_e(e.match)?Pe(e.match):void 0,collection:_e(e.collection)?Pe(e.collection):void 0,schema:Pe(n),filePath:r,specProfile:"v0.3"}}function pD(r,e,t){let n=Pe(r);for(let i of e){let s=t.get(i);if(s)for(let[o,a]of Object.entries(s.fields))!(o in n)&&a.default!==void 0&&(n[o]=Pe(a.default))}return n}function hD(r,e,t){let n=Pe(r);for(let i of e){let s=t.get(i);if(s)for(let[o,a]of Object.entries(s.collection?.read_defaults??{}))o in n||(n[o]=Pe(a))}return n}async function mD(r,e){let t=(0,at.normalizePath)(e).replace(/\/+$/,"");if(!t)return;let n="";for(let i of t.split("/")){n=n?`${n}/${i}`:i;let s=r.getAbstractFileByPath(n);if(!(s instanceof at.TFolder)){if(s)throw new Error(`A file blocks folder ${n}.`);await r.adapter.exists(n)||await r.createFolder(n)}}}async function pl(r,e,t){let n=(0,at.normalizePath)(e),i=n.lastIndexOf("/");if(i>=0&&await mD(r,n.slice(0,i)),n.startsWith(".mdbase/")){await r.adapter.write(n,t);return}let s=r.getAbstractFileByPath(n);if(s instanceof at.TFolder)throw new Error(`A folder blocks file ${n}.`);s instanceof at.TFile?await r.modify(s,t):await r.create(n,t)}async function Oo(r,e){let t=(0,at.normalizePath)(e),n=r.getAbstractFileByPath(t);if(n instanceof at.TFile)return r.cachedRead(n);if(await r.adapter.exists(t))return r.adapter.read(t);throw new Error(`File not found: ${e}`)}async function Z0(r){let e=await Oo(r,"mdbase.yaml"),t=(0,at.parseYaml)(e);if(!_e(t))throw new Error("mdbase.yaml must contain a YAML mapping.");let n=typeof t.spec_version=="string"?t.spec_version:"";if(!/^0\.2(?:\.\d+)?$/.test(n))throw new Error(n===No?"This collection is already mdbase v0.3.":`Expected an mdbase v0.2.x collection, found ${JSON.stringify(n)}.`);let i=_e(t.settings)?t.settings:{},s=typeof i.types_folder=="string"?(0,at.normalizePath)(i.types_folder):"_types",o=`${s}/`,a=[],c=[],l=[],u=dD(t),d=`${(0,at.stringifyYaml)(u).trimEnd()} `;a.push({path:"mdbase.yaml",sourceDigest:Hr(e),targetDigest:Hr(d),source:e,target:d});let f=new Map,p=new Map;for(let w of r.getMarkdownFiles().filter(S=>S.path.startsWith(o)).sort((S,x)=>S.path.localeCompare(x.path))){let S=await r.cachedRead(w),x=et(S);if(!x.hasFrontmatter||x.error)throw new Error(`Cannot migrate ${w.path}: ${x.error??"frontmatter is missing"}.`);let C=lD(w.path,n,x.frontmatter),$=uD(w.path,x.frontmatter),M=fD(w.path,C.target);f.set($.name,$),p.set(M.name,M);let A=`${Vt(C.target,x.body)} @@ -192,8 +192,8 @@ Type definition for ${t}.`,extraFrontmatter:m,...o?{readOnlyReason:`This type us `;async function uS(r,e,t,n){let i=await r.resolveConflict(e,t,n),s=await r.preview();return{status:i,preview:s}}function fS(r,e){let t=r.split(` `),n=e.split(` -`),i=t.length>200||n.length>200,s=t.slice(0,200),o=n.slice(0,200),a=Array.from({length:s.length+1},()=>new Uint16Array(o.length+1));for(let d=s.length-1;d>=0;d-=1)for(let f=o.length-1;f>=0;f-=1)a[d][f]=s[d]===o[f]?a[d+1][f+1]+1:Math.max(a[d+1][f],a[d][f+1]);let c=[],l=0,u=0;for(;(l=300);)l=o.length||l=a[l][u+1]?(c.push({kind:"local",value:s[l]}),l+=1):(c.push({kind:"remote",value:o[u]}),u+=1);return{lines:c,truncated:i||l0)return{actionLabel:"Fix local files before syncing",actionDisabled:!0,message:"Synchronization is paused. Fix every listed local file, then refresh the review."};let n=r.actions.filter(s=>s.command!=="advance_checkpoint").length,i=r.actions.some(s=>s.command==="advance_checkpoint");return{actionLabel:n?`Sync ${n} ${n===1?"outcome":"outcomes"}`:i?"Confirm sync checkpoint":"Already up to date",actionDisabled:t||r.actions.length===0,message:e?"Review each transfer below, then sync when ready.":"This vault and the hosted collection are already aligned."}}function At(r){return r<1024?`${r} B`:r<1024*1024?`${(r/1024).toFixed(r<10*1024?1:0)} KB`:r<1024*1024*1024?`${(r/(1024*1024)).toFixed(r<10*1024*1024?1:0)} MB`:`${(r/(1024*1024*1024)).toFixed(1)} GB`}function ch(r){let{connected:e,status:t,progress:n,fileProgress:i,problem:s,validationIssues:o,localChangeObserved:a}=r;if(!e)return o?{state:"attention",label:`mdbase: ${o} ${o===1?"issue":"issues"}`,detail:"Open validation issues",destination:"issues"}:{state:"local",label:"mdbase: Local",detail:"This vault is not connected",destination:"sync"};if(i){let l=i.totalBytes>0?Math.min(100,Math.round(i.transferredBytes/i.totalBytes*100)):100;return{state:"syncing",label:`mdbase: ${i.direction==="upload"?"Uploading":"Downloading"} ${l}%`,detail:`${i.path} \xB7 ${At(i.transferredBytes)} of ${At(i.totalBytes)}`,destination:"sync"}}if(n)return{state:"syncing",label:`mdbase: Syncing${n.total==null?"":` ${n.completed}/${n.total}`}`,detail:"Open synchronization progress",destination:"sync"};if(s)return{state:s.action==="resume"?"paused":s.action==="retry"?"offline":"attention",label:s.action==="resume"?"mdbase: Paused":s.action==="retry"?"mdbase: Offline":"mdbase: Needs attention",detail:s.title,destination:"sync"};if(t?.recovery_required||t?.conflicts.length||t?.local_issues.length||["attention","blocked","failed","stale"].includes(t?.state??""))return{state:"attention",label:"mdbase: Needs attention",detail:"Review synchronization",destination:"sync"};if(t?.state==="cancelled")return{state:"paused",label:"mdbase: Paused",detail:"Review and resume synchronization",destination:"sync"};let c=t?.pending??0;return a||c>0||t?.state==="changes_waiting"||t?.state==="planned"?{state:"waiting",label:c>0?`mdbase: ${c} ${c===1?"change":"changes"}`:"mdbase: Changes waiting",detail:"Review local and hosted changes",destination:"sync"}:t?.state==="up_to_date"?{state:"synced",label:"mdbase: Synced",detail:"Local and hosted collections are aligned",destination:"sync"}:{state:"waiting",label:"mdbase: Ready to sync",detail:"Review the first synchronization",destination:"sync"}}function nr(r){let e=xD(r);return e==="mirror_busy"?{code:e,title:"Synchronization is already running",message:"The active transfer is still using this vault. Its progress is shown below.",action:"resume",actionLabel:"Show progress"}:["mirror_credentials_missing","invalid_mirror_enrollment","mirror_enrollment_expired"].includes(e)?{code:e,title:"Connect approval is required again",message:"Your local files and mirror checkpoint are safe. Approve this vault again to restore access.",action:"reauthorize",actionLabel:"Sign in again"}:["operation_cancelled","cancelled","AbortError"].includes(e)?{code:e,title:"Synchronization paused safely",message:"Completed changes remain checkpointed. Review the current plan before resuming.",action:"resume",actionLabel:"Review and resume"}:["stale","stale_mirror_plan","mirror_plan_stale","conflict_decision_stale"].includes(e)?{code:e,title:"The collection changed again",message:"No stale decision was applied. Review the newest local and hosted versions.",action:"review",actionLabel:"Review newest changes"}:["enrollment_recovery_required","mirror_recovery_required","pending_mirror_recovery"].includes(e)?{code:e,title:"Synchronization needs recovery",message:"Your original files are safe. Resume from the durable checkpoint before disconnecting this vault.",action:"resume",actionLabel:"Resume recovery"}:{code:e,title:"Connect could not be reached",message:AD(r,"Your local files are safe. Check the connection and try again."),action:"retry",actionLabel:"Retry connection"}}function pS(r){return Array.isArray(r)?r.filter(ED).slice(-30):[]}function hS(r,e){return[...r.filter(t=>t.id!==e.id),e].slice(-30)}function mS(r){return{...r,id:crypto.randomUUID(),occurredAt:new Date().toISOString()}}function ED(r){if(!r||typeof r!="object"||Array.isArray(r))return!1;let e=r;return typeof e.id=="string"&&typeof e.occurredAt=="string"&&typeof e.summary=="string"&&(e.detail===void 0||typeof e.detail=="string")&&(e.path===void 0||typeof e.path=="string")&&["success","info","attention","error"].includes(e.tone??"")&&typeof e.requiresAcknowledgement=="boolean"}function xD(r){return r instanceof DOMException?r.name:r&&typeof r=="object"&&"code"in r&&typeof r.code=="string"?r.code:r instanceof Error&&r.name?r.name:"sync_failed"}function AD(r,e){return r instanceof Error&&r.message?r.message:e}var lh="https://connect.mdbase.dev",ai="mdbase-workspace-view",kD=["string","integer","number","boolean","date","datetime","time","enum","link","list","object","any"];function qe(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)}function oi(r){return JSON.parse(JSON.stringify(r))}function vl(r){return typeof r.type=="string"?r.type:"any"}function PD(r){if(!Object.prototype.hasOwnProperty.call(r,"field"))return"field";let e=2;for(;Object.prototype.hasOwnProperty.call(r,`field${e}`);)e+=1;return`field${e}`}function yS(r,e,t){Object.defineProperty(r,e,{configurable:!0,enumerable:!0,writable:!0,value:t})}function Lt(r,e,t,n,i={}){let s=r.createDiv({cls:"mdbase-form-row"}),o=s.createEl("label",{text:e}),a=`mdbase-${Math.random().toString(36).slice(2)}`;o.htmlFor=a,i.description&&s.createDiv({cls:"mdbase-form-description",text:i.description});let c=i.multiline?s.createEl("textarea"):s.createEl("input",{type:"text"});return c.id=a,c.setAttr("data-focus-key",`form-${e.toLowerCase().replace(/[^a-z0-9]+/g,"-")}`),c.value=t,c.placeholder=i.placeholder??"",c.addEventListener("input",()=>n(c.value)),c}function Fe(r,e,t){let n=r.createDiv({cls:"mdbase-status-row"});n.createSpan({cls:"mdbase-status-label",text:e}),n.createSpan({cls:"mdbase-status-value",text:t})}function CD(r){if(r<1e3)return String(r);let e=r<1e4?1:0;return`${(r/1e3).toFixed(e)}k`}function dh(r){if(!r)return"Never synced";let e=Date.parse(r);if(!Number.isFinite(e))return r;let t=Math.round((e-Date.now())/1e3),n=Math.abs(t),i=new Intl.RelativeTimeFormat(void 0,{numeric:"auto"});if(n<60)return i.format(t,"second");let s=Math.round(t/60);if(Math.abs(s)<60)return i.format(s,"minute");let o=Math.round(s/60);return Math.abs(o)<24?i.format(o,"hour"):i.format(Math.round(o/24),"day")}function MD(r){return r?r.state==="up_to_date"?"Up to date":r.state==="changes_waiting"?"Local changes waiting":["attention","blocked","failed","stale"].includes(r.state)||r.recovery_required?"Needs attention":r.state==="cancelled"?"Paused safely":r.state==="applying"?"Synchronizing":r.state==="planned"?"Review ready":"Ready for first sync":"Checking connection"}function uh(r){return r instanceof Error&&r.name==="AbortError"}var ph=class extends ce.Modal{constructor(){super(...arguments);this.resolve=null;this.settled=!1}confirm(t){return new Promise(n=>{this.resolve=n,this.titleEl.setText("Confirm high-impact type changes"),this.contentEl.createEl("p",{text:"These schema changes can change membership or invalidate existing records. The plugin will save only the type definition; it will not rewrite records."});let i=this.contentEl.createEl("ul",{cls:"mdbase-confirm-change-list"});for(let c of t)i.createEl("li",{text:c.summary});let s=this.contentEl.createDiv({cls:"modal-button-container"}),o=s.createEl("button",{text:"Keep reviewing"});o.onclick=()=>this.finish(!1);let a=s.createEl("button",{text:"Save high-impact changes"});a.addClass("mod-warning"),a.onclick=()=>this.finish(!0),this.open()})}onClose(){this.settled||this.finish(!1,!1),this.contentEl.empty()}finish(t,n=!0){this.settled||(this.settled=!0,this.resolve?.(t),this.resolve=null,n&&this.close())}},hh=class extends ce.Modal{constructor(){super(...arguments);this.resolve=null;this.settled=!1}choose(t){return new Promise(n=>{this.resolve=n,this.titleEl.setText("Disconnect this vault?"),this.contentEl.createEl("p",{text:`This stops synchronization with ${t}. It does not delete the hosted collection.`});let i=this.contentEl.createEl("ul");i.createEl("li",{text:"Keep local files leaves the current vault contents in place as an unsynced local copy."}),i.createEl("li",{text:"Remove synced files deletes only files that still exactly match the last checkpoint. Local edits are preserved."});let s=this.contentEl.createDiv({cls:"modal-button-container"}),o=s.createEl("button",{text:"Cancel"});o.onclick=()=>this.finish(null);let a=s.createEl("button",{text:"Disconnect and keep files"});a.onclick=()=>this.finish("keep");let c=s.createEl("button",{text:"Remove unchanged synced files"});c.addClass("mod-warning"),c.onclick=()=>this.finish("remove"),this.open()})}onClose(){this.settled||this.finish(null,!1),this.contentEl.empty()}finish(t,n=!0){this.settled||(this.settled=!0,this.resolve?.(t),this.resolve=null,n&&this.close())}},_l=class extends ce.ItemView{constructor(t,n){super(t);this.host=n;this.destination="types";this.editorMode="design";this.schema=null;this.query="";this.selectedPath=null;this.model=null;this.originalModel=null;this.yamlDraft="";this.dirty=!1;this.busy=!1;this.migrationPlan=null;this.allowLossy=!1;this.mirrorStatus=null;this.mirrorPreview=null;this.mirrorProgress=null;this.fileProgress=null;this.syncProblem=null;this.conflictComparisons=new Map;this.loadingConflictComparisons=new Set;this.pendingSyncFocus=null;this.transientMessage="";this.issueQuery="";this.issueSeverity="all";this.issueLimit=250;this.enrollmentVerification="";this.enrollmentAbort=null;this.enrollmentControlUrl=lh;this.enrollmentMirrorName="Obsidian";this.enrollmentCollectionId="";this.enrollmentMode="read_write";this.filePolicyDraft=null;this.adoptionFileProgress="";this.draftSaveTimer=null;this.fieldQuery="";this.expandedFields=new Set;this.fieldIds=new WeakMap;this.nextFieldId=1;this.refreshVersion=0;this.typeSelectionVersion=0}getViewType(){return ai}getDisplayText(){return"mdbase"}getIcon(){return as}async onOpen(){this.containerEl.addClass("mdbase-workspace"),this.registerDomEvent(this.containerEl,"keydown",t=>{(t.metaKey||t.ctrlKey)&&t.key.toLowerCase()==="s"&&this.dirty&&(t.preventDefault(),this.saveCurrentType())}),await this.refresh(!0)}async onClose(){await this.flushTypeDraft(),this.enrollmentAbort?.abort(),this.enrollmentAbort=null}async refresh(t=!1){let n=++this.refreshVersion;try{let i=await this.host.loadWorkspaceSchema(t);if(n!==this.refreshVersion||(this.schema=i,this.selectedPath&&!this.typeEntries().some(s=>s.filePath===this.selectedPath)&&(this.selectedPath=null,this.model=null,this.originalModel=null),!this.selectedPath&&this.typeEntries().length&&!ce.Platform.isMobile&&(this.selectedPath=this.typeEntries()[0].filePath),this.selectedPath&&(!this.model||t)&&(await this.selectType(this.selectedPath,!1),n!==this.refreshVersion))||(this.destination==="sync"&&await this.refreshMirrorStatus(),n!==this.refreshVersion))return;this.render()}catch(i){if(n!==this.refreshVersion)return;this.transientMessage=i instanceof Error?i.message:String(i),this.render()}}showDestination(t){this.destination=t,t==="sync"?this.refreshMirrorStatus().then(()=>this.render()):this.render()}createNewType(){this.destination="types",this.createType()}async editType(t){this.destination="types",await this.selectType(t)}async reviewSyncChanges(){this.host.getMirrorProfile()&&await this.perform(()=>this.loadMirrorPreview())}async syncNow(){if(this.host.getMirrorProfile()){if(!this.mirrorPreview){await this.reviewSyncChanges(),this.mirrorPreview?.plan.actions.length&&new ce.Notice("The current transfer review is open. Run sync now again or confirm it in the mdbase view.");return}await this.perform(()=>this.applyReviewedSync())}}focusSyncSection(t){this.destination="sync",this.pendingSyncFocus=t,this.render(),window.setTimeout(()=>this.focusPendingSyncSection(),0)}async reconnectCollection(){await this.perform(async()=>{try{this.mirrorStatus=await this.host.connectSync.reconnect(),this.syncProblem=null,this.host.setSyncStatus(this.mirrorStatus),await this.host.recordSyncActivity({summary:"Collection reconnected",detail:"Connect credentials were renewed and the mirror checkpoint was preserved.",tone:"success",requiresAcknowledgement:!1}),this.transientMessage="Connection restored. Your mirror checkpoint was preserved."}catch(t){if(nr(t).action!=="reauthorize")throw t;await this.reauthorizeCollection()}})}typeEntries(){return this.schema?[...this.schema.types.values()].sort((t,n)=>t.name.localeCompare(n.name)):[]}render(){let t=this.containerEl,n=this.captureRenderSnapshot(t);t.empty(),t.addClass("mdbase-workspace");let i=t.createDiv({cls:"mdbase-shell"});this.renderTopbar(i),this.transientMessage&&i.createDiv({cls:"mdbase-inline-message",text:this.transientMessage}).setAttr("role","status");let s=i.createDiv({cls:"mdbase-workspace-content"});s.setAttr("data-scroll-key","workspace"),this.destination==="types"?this.renderTypes(s):this.destination==="sync"?this.renderSync(s):this.renderIssues(s),this.restoreRenderSnapshot(t,n)}captureRenderSnapshot(t){let n=t.ownerDocument,i=t.contains(n.activeElement)?n.activeElement:null,s=i instanceof HTMLInputElement||i instanceof HTMLTextAreaElement?i:null,o=new Map;for(let a of Array.from(t.querySelectorAll("[data-scroll-key]"))){let c=a.getAttr("data-scroll-key");c&&o.set(c,{top:a.scrollTop,left:a.scrollLeft})}return{focusKey:i?.getAttr("data-focus-key")??null,selectionStart:s?.selectionStart??null,selectionEnd:s?.selectionEnd??null,scroll:o}}restoreRenderSnapshot(t,n){for(let[s,o]of n.scroll){let a=t.querySelector(`[data-scroll-key="${s}"]`);a&&(a.scrollTop=o.top,a.scrollLeft=o.left)}if(!n.focusKey)return;let i=t.querySelector(`[data-focus-key="${n.focusKey}"]`);i?.focus({preventScroll:!0}),(i instanceof HTMLInputElement||i instanceof HTMLTextAreaElement)&&n.selectionStart!==null&&n.selectionEnd!==null&&i.setSelectionRange(n.selectionStart,n.selectionEnd)}renderTopbar(t){let n=t.createDiv({cls:"mdbase-topbar"}),i=n.createDiv({cls:"mdbase-identity"}),s=i.createSpan({cls:"mdbase-mark"});s.setAttr("aria-hidden","true"),(0,ce.setIcon)(s,as),i.createSpan({cls:"mdbase-title",text:"mdbase"});let o=n.createDiv({cls:"mdbase-nav"});o.setAttr("role","tablist");for(let[a,c]of[["types","Types"],["sync","Sync"],["issues","Issues"]]){let l=o.createEl("button",{text:c});if(l.addClass("mdbase-nav-button"),l.setAttr("role","tab"),l.setAttr("aria-selected",String(this.destination===a)),this.destination===a&&l.addClass("is-active"),a==="issues"&&this.host.getIssues().length){let u=this.host.getIssues().length;l.createSpan({cls:"mdbase-count",text:CD(u)}).setAttr("title",`${u} issues`)}l.onclick=()=>this.showDestination(a)}}renderTypes(t){if(!this.schema){let i=t.createDiv({cls:"mdbase-empty-state"});i.createEl("h2",{text:"Start an mdbase collection"}),i.createEl("p",{text:"Initialize this vault as a local v0.3 collection, or use Sync to connect an empty vault to a collection authority."});let s=i.createDiv({cls:"mdbase-actions"}),o=s.createEl("button",{text:"Initialize local collection"});o.addClass("mod-cta"),o.disabled=this.busy||this.host.getMirrorProfile()!==null,o.onclick=()=>void this.perform(async()=>{await this.host.initializeCollection(),await this.refresh(!0)});let a=s.createEl("button",{text:"Connect collection authority"});a.onclick=()=>this.showDestination("sync");return}this.schema.config.spec_version.startsWith("0.2.")&&this.renderLegacyBanner(t);let n=t.createDiv({cls:"mdbase-types-layout"});this.model&&n.addClass("has-selection"),this.renderTypeList(n),this.renderTypeEditor(n)}renderLegacyBanner(t){let n=t.createDiv({cls:"mdbase-legacy-banner"}),i=n.createDiv();i.createEl("strong",{text:`mdbase ${this.schema?.config.spec_version} compatibility mode`}),i.createEl("p",{text:"Types are readable and validation remains available, but authoring is disabled until a reviewed v0.3 migration."});let s=n.createEl("button",{text:this.migrationPlan?"Review migration":"Analyze migration"});s.disabled=this.busy||this.host.getMirrorProfile()!==null,s.onclick=()=>void this.perform(async()=>{this.migrationPlan=await this.host.analyzeMigration(),this.render()}),this.host.getMirrorProfile()&&n.createDiv({cls:"mdbase-form-description",text:"Hosted resources must be migrated at the collection authority."}),this.migrationPlan&&this.renderMigrationReview(t,this.migrationPlan)}renderMigrationReview(t,n){let i=t.createDiv({cls:"mdbase-migration-review"}),s=i.createDiv({cls:"mdbase-section-header"});s.createEl("h3",{text:"Migration review"}),s.createSpan({cls:"mdbase-spec-badge",text:`${n.sourceVersion} \u2192 ${n.targetVersion}`});let o=i.createDiv({cls:"mdbase-status-list"});Fe(o,"Files replaced",String(n.operations.length)),Fe(o,"Type definitions",String(n.typeSummaries.length)),Fe(o,"Record reads verified",String(n.recordsVerified)),n.recordsSkipped&&Fe(o,"Records skipped",String(n.recordsSkipped)),Fe(o,"Record files rewritten","0"),Fe(o,"Recovery backup",n.backupLocation);let a=i.createDiv({cls:"mdbase-review-list"});n.diagnostics.length||a.createDiv({cls:"mdbase-review-ok",text:"No migration diagnostics."});for(let d of n.diagnostics.slice(0,250)){let f=a.createDiv({cls:"mdbase-review-item"});f.setAttr("data-severity",d.severity),f.createDiv({cls:"mdbase-review-code",text:`${d.severity} \xB7 ${d.path}`}),f.createDiv({text:d.message})}if(n.diagnostics.length>250&&a.createDiv({cls:"mdbase-form-description",text:`Showing 250 of ${n.diagnostics.length} diagnostics.`}),!n.applicable){let d=i.createEl("label",{cls:"mdbase-consent"}),f=d.createEl("input",{type:"checkbox"});f.checked=this.allowLossy,f.onchange=()=>{this.allowLossy=f.checked,this.render()},d.createSpan({text:"I reviewed the lossy diagnostics and want to apply this migration."})}let c=i.createDiv({cls:"mdbase-actions"}),l=c.createEl("button",{text:"Apply migration"});l.addClass("mod-warning"),l.disabled=this.busy||!n.applicable&&!this.allowLossy,l.onclick=()=>void this.perform(async()=>{await this.host.applyMigration(n,this.allowLossy),this.migrationPlan=null,this.allowLossy=!1,this.model=null,this.originalModel=null,await this.refresh(!0)});let u=c.createEl("button",{text:"Close review"});u.onclick=()=>{this.migrationPlan=null,this.render()}}renderTypeList(t){let n=t.createDiv({cls:"mdbase-type-list-pane"}),i=n.createDiv({cls:"mdbase-pane-header"});i.createEl("h2",{text:"Types"});let s=i.createEl("button");s.setAttr("aria-label","Create type"),(0,ce.setIcon)(s,"plus"),s.disabled=(this.schema?.config.spec_version.startsWith("0.2.")??!0)||this.host.getMirrorProfile()?.mode==="read_only",s.onclick=()=>this.createType();let o=n.createEl("input",{type:"search"});o.addClass("mdbase-type-search"),o.placeholder="Search types",o.setAttr("aria-label","Search types"),o.setAttr("data-focus-key","type-search"),o.value=this.query,o.oninput=()=>{this.query=o.value,this.render();let u=this.containerEl.querySelector(".mdbase-type-search");u?.focus(),u?.setSelectionRange(u.value.length,u.value.length)};let a=n.createDiv({cls:"mdbase-type-list"});a.setAttr("data-scroll-key","type-list");let c=this.query.trim().toLowerCase(),l=this.typeEntries().filter(u=>`${u.name} ${u.description??""} ${u.filePath}`.toLowerCase().includes(c));if(!l.length){a.createDiv({cls:"mdbase-empty-list",text:c?"No matching types.":"No type definitions."});return}for(let u of l){let d=a.createEl("button",{cls:"mdbase-type-row"});u.filePath===this.selectedPath&&d.addClass("is-active"),d.setAttr("aria-current",u.filePath===this.selectedPath?"true":"false"),d.createSpan({cls:"mdbase-type-name",text:u.name}),d.createSpan({cls:"mdbase-type-meta",text:`${Object.keys(u.fields).length} fields \xB7 ${u.specProfile??"v0.2"}`}),d.onclick=()=>void this.selectType(u.filePath)}}renderTypeEditor(t){let n=t.createDiv({cls:"mdbase-type-editor-pane"});if(n.setAttr("data-scroll-key","type-editor"),!this.model){let h=n.createDiv({cls:"mdbase-empty-state"});h.createEl("h2",{text:"Choose a type"}),h.createEl("p",{text:"Select a type definition from the list to inspect or edit it."});return}let i=this.host.getMirrorProfile()?.mode==="read_only",s=this.model.specProfile==="v0.2"||i||!!this.model.readOnlyReason,o=this.model.readOnlyReason??(i?"This mirror has read-only access. Re-enroll it with write access before editing types.":"This v0.2 type is read-only. Review and apply a collection migration before editing."),a=n.createDiv({cls:"mdbase-editor-header"}),c=a.createEl("button",{cls:"mdbase-mobile-back"});c.setAttr("aria-label","Back to type list"),(0,ce.setIcon)(c,"arrow-left"),c.onclick=()=>{if(this.dirty){new ce.Notice("Save or discard the current type changes before going back.");return}this.selectedPath=null,this.model=null,this.originalModel=null,this.render()};let l=a.createDiv(),u=l.createDiv({cls:"mdbase-editor-title-line"});u.createEl("h2",{text:this.model.name||"Untitled type"}),u.createSpan({cls:"mdbase-spec-badge",text:this.model.specProfile??"v0.2"}),this.dirty&&u.createSpan({cls:"mdbase-dirty",text:"Unsaved"}),l.createDiv({cls:"mdbase-editor-path",text:this.selectedPath??"New type"});let d=a.createDiv({cls:"mdbase-editor-actions"});if(this.selectedPath){let h=this.selectedPath,y=d.createEl("button",{text:"Open source"});y.onclick=()=>void this.host.openFileByPath(h)}let f=d.createEl("button",{text:"Save"});if(f.addClass("mod-cta"),f.disabled=s||!this.dirty||this.busy,f.onclick=()=>void this.saveCurrentType(),this.dirty){let h=d.createEl("button",{text:"Discard"});h.onclick=()=>void this.discardCurrentType()}s&&n.createDiv({cls:"mdbase-readonly-note",text:o});let p=n.createDiv({cls:"mdbase-mode-switch"});p.setAttr("role","tablist");for(let[h,y]of[["design","Design"],["yaml","YAML"]]){let g=p.createEl("button",{text:y});g.setAttr("role","tab"),g.setAttr("aria-selected",String(this.editorMode===h)),this.editorMode===h&&g.addClass("is-active"),g.onclick=()=>this.switchEditorMode(h)}let m=n.createDiv({cls:"mdbase-editor-document"});m.setAttr("data-scroll-key","type-document"),this.editorMode==="design"?this.renderDesignEditor(m,this.model,s):this.renderYamlEditor(m,s),this.dirty&&!s&&this.renderDraftBar(n,this.model)}renderDraftBar(t,n){let i=bl(this.originalModel,n),o=gl(n,{knownTypes:this.typeEntries().map(p=>p.name),contracts:this.schema?.contracts.values()}).filter(p=>p.severity==="error").length,a=i.filter(p=>p.risk==="high").length,c=t.createDiv({cls:"mdbase-draft-bar"}),l=c.createDiv({cls:"mdbase-draft-summary"});l.createEl("strong",{text:`${i.length} pending ${i.length===1?"change":"changes"}`}),l.createSpan({text:o?`${o} ${o===1?"error":"errors"} to fix`:a?`${a} high-impact ${a===1?"change":"changes"}`:"Ready to save"});let u=c.createDiv({cls:"mdbase-actions"});if(this.editorMode==="design"){let p=u.createEl("button",{text:"Review"});p.onclick=()=>this.containerEl.querySelector("#mdbase-section-review")?.scrollIntoView({behavior:"smooth",block:"start"})}let d=u.createEl("button",{text:"Discard"});d.onclick=()=>void this.discardCurrentType();let f=u.createEl("button",{text:"Save changes"});f.addClass("mod-cta"),f.disabled=o>0||this.busy,f.onclick=()=>void this.saveCurrentType()}renderDesignEditor(t,n,i){let s=gl(n,{knownTypes:this.typeEntries().map(F=>F.name),contracts:this.schema?.contracts.values()});this.renderSectionNavigation(t,s);let o=t.createEl("section",{cls:"mdbase-editor-section"});o.id="mdbase-section-identity",o.createEl("h3",{text:"Identity"});let a=Lt(o,"Name",n.name,F=>{n.name=F,this.markDirty()},{description:"Stable type name used by collection records."});a.disabled=i;let c=Lt(o,"Description",n.description,F=>{n.description=F,this.markDirty()},{multiline:!0});c.disabled=i;let l=o.createDiv({cls:"mdbase-form-row"}),u=l.createEl("label",{text:"Display field"}),d=l.createEl("select");u.htmlFor=d.id="mdbase-display-field",d.createEl("option",{value:"",text:"Use the file name"});for(let F of n.fields)d.createEl("option",{value:F.name,text:F.name||"Unnamed field"});n.displayNameKey&&!n.fields.some(F=>F.name===n.displayNameKey)&&d.createEl("option",{value:n.displayNameKey,text:`${n.displayNameKey} \xB7 missing`}),d.value=n.displayNameKey,d.onchange=()=>{n.displayNameKey=d.value,this.markDirty()},d.disabled=i;let f=o.createEl("label",{cls:"mdbase-checkbox-row"}),p=f.createEl("input",{type:"checkbox"});p.checked=n.strictMode===!0,p.disabled=i,p.onchange=()=>{n.strictMode=p.checked,this.markDirty()},f.createSpan({text:"Reject undeclared fields"});let m=t.createEl("section",{cls:"mdbase-editor-section"});m.id="mdbase-section-membership",m.createEl("h3",{text:"Membership"});let h=Lt(m,"Path glob",n.matchPathGlob,F=>{n.matchPathGlob=F,this.markDirty()},{placeholder:"Projects/**/*.md"});h.disabled=i;let y=Lt(m,"Fields present",n.matchFieldsPresent,F=>{n.matchFieldsPresent=F,this.markDirty()},{description:"Comma-separated frontmatter keys."});y.disabled=i;let g=Lt(m,"Where",n.matchWhere,F=>{n.matchWhere=F,this.markDirty()},{multiline:!0,description:"YAML predicate, including contains and nested equality conditions.",placeholder:`tags: - contains: task`});g.disabled=i;let _=t.createEl("section",{cls:"mdbase-editor-section"});_.id="mdbase-section-fields";let v=_.createDiv({cls:"mdbase-section-header"});v.createEl("h3",{text:"Fields"});let w=v.createEl("button",{text:"Add field"});w.disabled=i,w.onclick=()=>{let F={type:"string"};n.fields.push({name:"",definition:F}),this.expandedFields.add(this.fieldId(F)),this.markDirty(!0)};let S=_.createDiv({cls:"mdbase-field-toolbar"}),x=n.fields.filter(F=>F.definition.required===!0).length;S.createDiv({cls:"mdbase-field-count",text:`${n.fields.length} ${n.fields.length===1?"field":"fields"} \xB7 ${x} required`});let C=S.createDiv({cls:"mdbase-field-toolbar-actions"}),$=C.createEl("input",{type:"search"});$.placeholder="Filter fields",$.setAttr("aria-label","Filter fields"),$.setAttr("data-focus-key","field-search"),$.value=this.fieldQuery,$.oninput=()=>{this.fieldQuery=$.value,this.render()};let M=C.createEl("button",{text:"Collapse all"});M.disabled=this.expandedFields.size===0,M.onclick=()=>{this.expandedFields.clear(),this.render()};let A=_.createDiv({cls:"mdbase-fields"}),W=this.fieldQuery.trim().toLowerCase(),q=n.fields.filter(F=>!W||this.fieldMatches(F.name,F.definition,W));for(let F of q){let Q=n.fields.indexOf(F);this.renderFieldRow(A,F,Q,i)}q.length||A.createDiv({cls:"mdbase-empty-list",text:n.fields.length?"No fields match this filter.":"No fields declared."});let oe=t.createEl("section",{cls:"mdbase-editor-section"});oe.id="mdbase-section-placement",oe.createEl("h3",{text:"Placement"});let P=Lt(oe,"Path pattern",n.pathPattern,F=>{n.pathPattern=F,this.markDirty()},{placeholder:"Notes/{title}.md"});P.disabled=i,this.renderContractEditor(t,n,i);let k=t.createEl("section",{cls:"mdbase-editor-section mdbase-change-review"});if(k.id="mdbase-section-review",k.createEl("h3",{text:"Change review"}),s.length){let F=k.createDiv({cls:"mdbase-diagnostic-summary"}),Q=s.filter(Xe=>Xe.severity==="error").length,fe=s.length-Q;F.createEl("strong",{text:`${Q} ${Q===1?"error":"errors"} \xB7 ${fe} ${fe===1?"warning":"warnings"}`});for(let Xe of s.slice(0,12)){let Ue=F.createDiv({cls:"mdbase-diagnostic-item"});Ue.setAttr("data-severity",Xe.severity),Ue.createEl("code",{text:Xe.path}),Ue.createSpan({text:Xe.message})}}if(!this.dirty)k.createEl("p",{text:"No pending changes."});else{let F=k.createEl("ul");for(let Q of bl(this.originalModel,n))F.createEl("li",{text:Q.summary}).setAttr("data-risk",Q.risk)}}renderSectionNavigation(t,n){let i=t.createDiv({cls:"mdbase-section-nav"});i.setAttr("aria-label","Type sections"),i.setAttr("data-scroll-key","section-nav");let s=[["identity","Overview"],["membership","Membership"],["fields","Fields"],["applications","Applications"],["review",n.some(o=>o.severity==="error")?"Review \xB7 errors":"Review"]];for(let[o,a]of s){let c=i.createEl("button",{text:a});c.onclick=()=>{this.containerEl.querySelector(`#mdbase-section-${o}`)?.scrollIntoView({behavior:window.matchMedia("(prefers-reduced-motion: reduce)").matches?"auto":"smooth",block:"start"})}}}renderContractEditor(t,n,i){let s=t.createEl("section",{cls:"mdbase-editor-section mdbase-contracts-section"});s.id="mdbase-section-applications";let a=s.createDiv({cls:"mdbase-section-header"}).createDiv();a.createEl("h3",{text:"Works with applications"}),a.createDiv({cls:"mdbase-form-description",text:"Tell compatible applications what this type's fields mean."});let c=[...this.schema?.contracts.values()??[]],l=new Set(n.implementations.map(d=>`${d.contract}@${d.version}`)),u=c.filter(d=>!l.has(ss(d)));c.length||s.createDiv({cls:"mdbase-contract-empty",text:"No record contracts are installed in this collection. Add contract files under the configured contracts folder to connect this type to an application."});for(let d of n.implementations){let f=c.find(p=>p.id===d.contract&&p.version===d.version);this.renderContractImplementation(s,n,d,f,i)}if(u.length){let d=s.createDiv({cls:"mdbase-contract-add"}),f=d.createEl("label",{text:"Installed contract"}),p=d.createEl("select");f.htmlFor=p.id=`mdbase-contract-${Math.random().toString(36).slice(2)}`;for(let h of u)p.createEl("option",{value:ss(h),text:`${h.id} \xB7 ${h.version}`});let m=d.createEl("button",{text:"Connect application contract"});m.disabled=i,m.onclick=()=>{let h=u.find(y=>ss(y)===p.value);if(h)try{nS(n,h),this.markDirty(!0)}catch(y){new ce.Notice(y instanceof Error?y.message:String(y))}}}}renderContractImplementation(t,n,i,s,o){let a=t.createEl("article",{cls:"mdbase-contract-implementation"}),c=a.createDiv({cls:"mdbase-contract-header"}),l=c.createDiv();l.createEl("strong",{text:i.contract}),l.createSpan({cls:"mdbase-contract-version",text:i.version});let u=c.createEl("button",{text:"Remove"});if(u.disabled=o,u.onclick=()=>{iS(n,i.contract,i.version),this.markDirty(!0)},!s){a.createDiv({cls:"mdbase-contract-unavailable",text:"This exact contract is not installed in the collection. Restore it or remove this implementation before saving."});return}let d=Do(s),f=Lo(n),p=d.filter(v=>os(i,v)),m=d.filter(v=>v.required),h=m.filter(v=>os(i,v)).length,y=a.createEl("details");y.open=hW.reference===w),x=jo(v,S),C=_.createDiv({cls:`mdbase-contract-mapping-row ${x.level}`}),$=C.createDiv({cls:"mdbase-contract-field-definition"});$.createEl("code",{text:v.reference}),$.createSpan({cls:v.required?"mdbase-contract-required":"mdbase-contract-optional",text:v.required?"Required":"Optional"}),$.createEl("small",{text:v.description||oS(v.schema)});let M=C.createEl("select");M.setAttr("aria-label",`${i.contract} ${v.reference} source field`),M.setAttr("aria-invalid",x.level==="error"?"true":"false"),M.disabled=o,M.createEl("option",{value:"",text:v.required?"Choose a source field":"Not exposed"});for(let W of f){let q=jo(v,W),oe=M.createEl("option",{value:W.reference,text:`${W.reference} \xB7 ${W.type}${q.level==="warning"?" \xB7 review":""}`});oe.disabled=q.level==="error"}M.value=w,M.onchange=()=>{sS(i,v.reference,M.value||void 0),this.markDirty(!0)};let A=C.createDiv({cls:"mdbase-contract-mapping-status"});A.createEl("strong",{text:x.label}),A.createEl("small",{text:x.message})}if(s.binding_schema){let v=a.createEl("details",{cls:"mdbase-contract-settings"});v.open=!!i.binding;let w=v.createEl("summary");w.createSpan({text:"Contract settings"}),w.createSpan({cls:"mdbase-contract-summary",text:i.binding?"Configured":"Optional"});let S=v.createDiv({cls:"mdbase-contract-settings-body"});if(S.createEl("p",{cls:"mdbase-form-description",text:"Control how compatible applications interpret this type. Values follow the contract's schema."}),i.binding)this.renderContractSchemaValue(S,s.binding_schema,i.binding,x=>{qe(x)&&(rh(i,x),this.markDirty())},"Settings",o);else{let x=S.createEl("button",{text:"Configure settings"});x.disabled=o,x.onclick=()=>{let C=qo(s.binding_schema);qe(C)&&(rh(i,C),this.markDirty(!0))}}}}renderContractSchemaValue(t,n,i,s,o,a){if(xn(n)==="object"){let l=qe(i)?i:{},u=t.createEl("fieldset",{cls:"mdbase-contract-schema-object"});u.createEl("legend",{text:o});let d=qe(n.properties)?n.properties:{},f=new Set(Array.isArray(n.required)?n.required.map(String):[]),p=[...new Set([...f,...Object.keys(l).filter(h=>h in d)])];for(let h of p){let y=d[h];if(!qe(y))continue;let g=u.createDiv({cls:"mdbase-contract-schema-field"}),_=typeof y.description=="string"?y.description:void 0;g.createEl("label",{text:`${h}${f.has(h)?" \xB7 required":""}`}),_&&g.createEl("small",{text:_}),this.renderContractSchemaControl(g,y,l[h],v=>{s({...l,[h]:v})},h,a)}p.length||u.createDiv({cls:"mdbase-empty-list",text:"No settings declared."});let m=Object.keys(d).filter(h=>!p.includes(h));if(m.length){let h=u.createDiv({cls:"mdbase-contract-schema-add"}),y=h.createEl("select");for(let _ of m)y.createEl("option",{value:_,text:_});let g=h.createEl("button",{text:"Add optional setting"});g.disabled=a,g.onclick=()=>{let _=y.value,v=d[_];qe(v)&&s({...l,[_]:qo(v)})}}return}this.renderContractSchemaControl(t,n,i,s,o,a)}renderContractSchemaControl(t,n,i,s,o,a){let c=xn(n);if(Array.isArray(n.enum)){let u=t.createEl("select");u.setAttr("aria-label",o);for(let d of n.enum)u.createEl("option",{value:JSON.stringify(d),text:String(d)});u.value=JSON.stringify(i),u.disabled=a,u.onchange=()=>s(JSON.parse(u.value));return}if(c==="array"){let u=qe(n.items)?n.items:{type:"string"},d=Array.isArray(i)?i:[],f=t.createDiv({cls:"mdbase-contract-schema-array"});for(let[m,h]of d.entries()){let y=f.createDiv({cls:"mdbase-contract-schema-array-item"});y.createSpan({text:`${m+1}.`}),this.renderContractSchemaControl(y,u,h,_=>{s(d.map((v,w)=>w===m?_:v))},`${o} item ${m+1}`,a);let g=y.createEl("button",{text:"Remove"});g.disabled=a||d.length<=(typeof n.minItems=="number"?n.minItems:0),g.onclick=()=>s(d.filter((_,v)=>v!==m))}let p=t.createEl("button",{text:`Add ${o.toLowerCase()} item`});p.disabled=a||typeof n.maxItems=="number"&&d.length>=n.maxItems,p.onclick=()=>s([...d,qo(u)]);return}if(c==="object"){this.renderContractSchemaValue(t,n,i,s,o,a);return}if(c==="boolean"){let u=t.createEl("input",{type:"checkbox"});u.checked=i===!0,u.disabled=a,u.setAttr("aria-label",o),u.onchange=()=>s(u.checked);return}let l=t.createEl("input",{type:c==="number"||c==="integer"?"number":"text"});l.setAttr("aria-label",o),l.value=i==null?"":typeof i=="string"?i:typeof i=="number"||typeof i=="boolean"?String(i):"",l.disabled=a,l.oninput=()=>s(c==="number"||c==="integer"?Number(l.value):l.value)}renderFieldRow(t,n,i,s){this.renderFieldDefinition(t,n.definition,{name:n.name,nameLabel:`Field ${i+1} name`,onNameInput:o=>{n.name=o,this.markDirty()},required:n.definition.required===!0,onRequiredChange:o=>{n.definition.required=o,this.markDirty()},onRemove:()=>{this.model?.fields.splice(i,1),this.markDirty(!0)},readOnly:s,depth:0})}renderFieldDefinition(t,n,i){let s=t.createEl("details",{cls:"mdbase-field-node"});s.setAttr("data-depth",String(i.depth));let o=this.fieldId(n),a=!!this.fieldQuery.trim()&&this.fieldMatches(i.name??i.staticLabel??"",n,this.fieldQuery.trim().toLowerCase());s.open=this.expandedFields.has(o)||a,s.ontoggle=()=>{s.open?this.expandedFields.add(o):this.expandedFields.delete(o)};let c=s.createEl("summary",{cls:"mdbase-field-summary"});c.createSpan({cls:"mdbase-field-summary-name",text:i.name||i.staticLabel||"Unnamed field"}),c.createSpan({cls:"mdbase-field-summary-type",text:vl(n)}),c.createSpan({cls:"mdbase-field-summary-rule",text:i.required?"Required":i.staticLabel?"Item shape":"Optional"});let l=s.createDiv({cls:"mdbase-field-row"});if(i.staticLabel)l.createDiv({cls:"mdbase-field-role",text:i.staticLabel});else{let p=l.createEl("input",{type:"text",cls:"mdbase-field-name-control"});p.setAttr("data-focus-key",`field-${o}-name`),p.setAttr("aria-label",i.nameLabel),p.placeholder="fieldName",p.value=i.name??"",p.disabled=i.readOnly,i.onNameInput&&(p.oninput=()=>i.onNameInput?.(p.value)),i.onNameCommit&&(p.onchange=()=>i.onNameCommit?.(p.value,p))}let u=l.createEl("select",{cls:"mdbase-field-type-control"});u.setAttr("data-focus-key",`field-${o}-type`),u.setAttr("aria-label",`${i.name||i.staticLabel||"Field"} type`);for(let p of kD){let m=p==="any"?"Any value":p[0].toUpperCase()+p.slice(1);u.createEl("option",{value:p,text:m})}u.value=vl(n),u.disabled=i.readOnly,u.onchange=()=>{n.type=u.value,u.value==="list"&&!qe(n.items)&&(n.items={type:"string"}),u.value==="object"&&!qe(n.fields)&&(n.fields={}),u.value==="enum"&&!Array.isArray(n.values)&&(n.values=[]),this.markDirty(!0)};let d=l.createEl("input",{type:"text",cls:"mdbase-field-description-control"});if(d.setAttr("data-focus-key",`field-${o}-description`),d.setAttr("aria-label",`${i.name||i.staticLabel||"Field"} description`),d.placeholder="Description",d.value=typeof n.description=="string"?n.description:"",d.disabled=i.readOnly,d.oninput=()=>{d.value?n.description=d.value:delete n.description,this.markDirty()},i.onRequiredChange){let p=l.createEl("label",{cls:"mdbase-field-required"}),m=p.createEl("input",{type:"checkbox"});m.setAttr("data-focus-key",`field-${o}-required`),m.checked=i.required===!0,m.disabled=i.readOnly,m.onchange=()=>i.onRequiredChange?.(m.checked),p.createSpan({text:"Required"})}if(i.onRemove){let p=l.createEl("button",{cls:"mdbase-field-remove"});p.setAttr("aria-label",`Remove ${i.name||i.staticLabel||"field"}`),(0,ce.setIcon)(p,"trash-2"),p.disabled=i.readOnly,p.onclick=i.onRemove}let f=vl(n);f==="enum"&&this.renderEnumFieldDetails(s,n,i),f==="link"&&this.renderLinkFieldDetails(s,n,i),f==="list"&&this.renderListFieldDetails(s,n,i),f==="object"&&this.renderObjectFieldDetails(s,n,i)}fieldId(t){let n=this.fieldIds.get(t);if(n)return n;let i=`field-${this.nextFieldId}`;return this.nextFieldId+=1,this.fieldIds.set(t,i),i}fieldMatches(t,n,i){return`${t} ${vl(n)} ${typeof n.description=="string"?n.description:""}`.toLowerCase().includes(i)||qe(n.items)&&this.fieldMatches("item",n.items,i)?!0:qe(n.fields)?Object.entries(n.fields).some(([o,a])=>qe(a)&&this.fieldMatches(o,a,i)):!1}renderEnumFieldDetails(t,n,i){let s=t.createDiv({cls:"mdbase-field-details mdbase-field-options"}),o=s.createEl("label",{text:"Allowed values"}),a=s.createEl("input",{type:"text"});a.setAttr("aria-label",`${i.name||i.staticLabel||"Enum"} allowed values`),a.placeholder="draft, published, archived",a.value=Array.isArray(n.values)?n.values.map(String).join(", "):"",a.disabled=i.readOnly,a.oninput=()=>{n.values=a.value.split(",").map(c=>c.trim()).filter(Boolean),this.markDirty()},o.htmlFor=a.id=`mdbase-${Math.random().toString(36).slice(2)}`}renderLinkFieldDetails(t,n,i){let s=t.createDiv({cls:"mdbase-field-details mdbase-field-options"}),o=s.createEl("label",{text:"Target type"}),a=s.createEl("select");a.setAttr("aria-label",`${i.name||i.staticLabel||"Link"} target type`),a.createEl("option",{value:"",text:"Any type"});let c=typeof n.target=="string"?n.target:"";for(let d of this.typeEntries())a.createEl("option",{value:d.name,text:d.name});c&&!this.typeEntries().some(d=>d.name===c)&&a.createEl("option",{value:c,text:`${c} \xB7 missing`}),a.value=c,a.disabled=i.readOnly,a.onchange=()=>{a.value?n.target=a.value:delete n.target,this.markDirty()},o.htmlFor=a.id=`mdbase-${Math.random().toString(36).slice(2)}`;let l=s.createEl("label",{cls:"mdbase-field-required"}),u=l.createEl("input",{type:"checkbox"});u.checked=n.validate_exists===!0,u.disabled=i.readOnly,u.onchange=()=>{n.validate_exists=u.checked,this.markDirty()},l.createSpan({text:"Validate target exists"})}renderListFieldDetails(t,n,i){let s=t.createDiv({cls:"mdbase-field-children"});s.createDiv({cls:"mdbase-field-children-label",text:"List items"});let o=qe(n.items)?n.items:{type:"any"};!qe(n.items)&&!i.readOnly&&(n.items=o),this.renderFieldDefinition(s,o,{staticLabel:"Item",nameLabel:"List item",readOnly:i.readOnly,depth:i.depth+1})}renderObjectFieldDetails(t,n,i){let s=t.createDiv({cls:"mdbase-field-children"}),o=s.createDiv({cls:"mdbase-field-children-header"});o.createDiv({cls:"mdbase-field-children-label",text:"Object fields"});let a=o.createEl("button",{text:"Add nested field"});a.disabled=i.readOnly;let c=qe(n.fields)?n.fields:{};!qe(n.fields)&&!i.readOnly&&(n.fields=c),a.onclick=()=>{let d=PD(c);yS(c,d,{type:"string"}),this.markDirty(!0)};let l=s.createDiv({cls:"mdbase-nested-fields"}),u=Object.entries(c).filter(d=>qe(d[1]));if(!u.length){l.createDiv({cls:"mdbase-empty-list",text:"No nested fields."});return}for(let[d,f]of u){let p=d;this.renderFieldDefinition(l,f,{name:p,nameLabel:`${p} nested field name`,onNameCommit:(m,h)=>{let y=m.trim();if(!y){new ce.Notice("Nested field name is required."),h.value=p;return}if(y!==p&&Object.prototype.hasOwnProperty.call(c,y)){new ce.Notice(`Nested field already exists: ${y}`),h.value=p;return}y!==p&&(delete c[p],yS(c,y,f),p=y,this.markDirty())},required:f.required===!0,onRequiredChange:m=>{f.required=m,this.markDirty()},onRemove:()=>{delete c[p],this.markDirty(!0)},readOnly:i.readOnly,depth:i.depth+1})}}renderYamlEditor(t,n){let i=t.createEl("section",{cls:"mdbase-editor-section mdbase-yaml-section"});i.createEl("h3",{text:"Canonical type document"}),i.createEl("p",{cls:"mdbase-form-description",text:"Unknown v0.3 extensions are preserved. Invalid YAML is never normalized or saved."});let s=i.createEl("textarea",{cls:"mdbase-yaml-editor"});s.setAttr("aria-label","Type definition YAML"),s.setAttr("data-focus-key","yaml-editor"),s.value=this.yamlDraft,s.disabled=n,s.spellcheck=!1,s.oninput=()=>{this.yamlDraft=s.value,this.markDirty(!1)}}renderSync(t){let n=t.createDiv({cls:"mdbase-sync-document"}),i=n.createDiv({cls:"mdbase-document-header"});i.createEl("h2",{text:"Sync"}),i.createEl("p",{text:"A clear handoff between this vault and its hosted collection authority."});let s=this.host.getMirrorProfile();if(!s){this.renderEnrollment(n);return}this.syncProblem=this.host.getCurrentSyncProblem()??this.syncProblem;let o=n.createEl("section",{cls:"mdbase-sync-hero"});o.setAttr("data-state",this.mirrorStatus?.state??"checking");let a=o.createDiv({cls:"mdbase-sync-route"}),c=a.createDiv({cls:"mdbase-sync-endpoint"});(0,ce.setIcon)(c.createSpan({cls:"mdbase-sync-endpoint-icon"}),"vault");let l=c.createDiv();l.createEl("strong",{text:this.app.vault.getName()});let u=s.selectiveSync??{file_classes:[],excluded_folders:[]},d=u.file_classes.length?`Markdown + ${u.file_classes.join(", ")}`:"Markdown only";l.createSpan({text:`${s.mode==="read_write"?"Upload and download":"Downloads only"} \xB7 ${d}`});let f=a.createDiv({cls:"mdbase-sync-connection"});(0,ce.setIcon)(f.createSpan(),s.mode==="read_write"?"arrow-left-right":"arrow-left"),f.createSpan({text:MD(this.mirrorStatus)});let p=a.createDiv({cls:"mdbase-sync-endpoint"});(0,ce.setIcon)(p.createSpan({cls:"mdbase-sync-endpoint-icon"}),"cloud");let m=p.createDiv();m.createEl("strong",{text:s.name}),m.createSpan({text:"Hosted authority"});let h=o.createDiv({cls:"mdbase-sync-meta"}),y=this.mirrorStatus?.pending_files??0;if(h.createSpan({text:`${s.mode==="read_write"?"Read\u2013write mirror":"Read-only mirror"} \xB7 ${dh(this.mirrorStatus?.last_synced_at)}${y?` \xB7 ${y} queued ${y===1?"file":"files"}`:""}`}),h.createEl("code",{text:s.collectionId}).setAttr("title","Collection ID"),this.fileProgress||this.mirrorProgress){let x=o.createDiv({cls:"mdbase-sync-progress",attr:{"aria-live":"polite"}}),C=this.fileProgress?.totalBytes??this.mirrorProgress?.total??null,$=this.fileProgress?.transferredBytes??this.mirrorProgress?.completed??0,M=x.createEl("progress");M.max=C??1,M.value=C==null?0:$,C==null&&M.removeAttribute("value"),x.createDiv({cls:"mdbase-progress-label",text:this.fileProgress?`${this.fileProgress.direction==="upload"?"Uploading":"Downloading"} ${this.fileProgress.path} \xB7 ${At($)} of ${At(C??0)}`:`${this.mirrorProgress?.phase==="uploading"?"Uploading local changes":this.mirrorProgress?.phase==="downloading"?"Downloading collection files":"Applying changes"} \xB7 ${$}${C==null?"":` of ${C}`}`});let A=x.createEl("button",{text:"Stop safely"});A.onclick=()=>{this.host.connectSync.cancelSync(),this.transientMessage="Stopping after the current network request\u2026",this.render()}}(this.syncProblem||this.mirrorStatus?.recovery_required)&&this.renderRecoveryCard(o,this.syncProblem??{code:"mirror_recovery_required",title:"Synchronization needs recovery",message:"Your original files are safe. Resume from the durable checkpoint before disconnecting this vault.",action:"resume",actionLabel:"Resume recovery"});let _=o.createDiv({cls:"mdbase-sync-actions"}),v=_.createEl("button",{text:this.mirrorPreview?"Refresh review":"Review changes"});v.disabled=this.busy,v.onclick=()=>void this.reviewSyncChanges();let w=ah(this.mirrorPreview?.plan??null,this.mirrorPreview?.entries.length??0,this.busy),S=_.createEl("button",{text:w.actionLabel});S.addClass("mod-cta"),S.disabled=w.actionDisabled,S.onclick=()=>void this.perform(()=>this.applyReviewedSync()),this.renderFilePolicyControls(n,{connected:!0}),this.mirrorPreview&&this.renderMirrorPreview(n,this.mirrorPreview),this.mirrorStatus?.conflicts.length&&this.renderConflicts(n,this.mirrorStatus),this.mirrorStatus?.local_issues.length&&this.renderLocalMirrorIssues(n,this.mirrorStatus),this.renderActivity(n),this.renderConnectionDetails(n,s),window.setTimeout(()=>this.focusPendingSyncSection(),0)}async loadMirrorPreview(){this.mirrorPreview=await this.host.connectSync.preview(),this.mirrorStatus=await this.host.connectSync.status(),this.syncProblem=null,this.host.setSyncStatus(this.mirrorStatus),this.transientMessage=ah(this.mirrorPreview.plan,this.mirrorPreview.entries.length).message}async applyReviewedSync(){if(!this.mirrorPreview){await this.loadMirrorPreview();return}let t=this.mirrorPreview;try{let n=await this.host.connectSync.sync(t,i=>{this.mirrorProgress=i,this.host.setSyncProgress(i,this.fileProgress),this.render()},i=>{this.fileProgress=i,this.host.setSyncProgress(this.mirrorProgress,i),this.render()});this.mirrorStatus=await this.host.connectSync.status(),this.mirrorPreview=await this.host.connectSync.preview(),this.syncProblem=n.status==="cancelled"?nr(new DOMException("Synchronization stopped.","AbortError")):n.status==="stale"?nr(Object.assign(new Error("The reviewed plan changed."),{code:"mirror_plan_stale"})):null,this.syncProblem?this.host.setSyncProblem(this.syncProblem):this.host.setSyncStatus(this.mirrorStatus,{clearLocalChanges:n.status==="applied"&&n.pending===0}),this.transientMessage=n.status==="applied"?"Sync completed and the local checkpoint was verified.":n.status==="attention"?"Completed changes are checkpointed. Review the items that still need attention.":n.status==="cancelled"?`Sync paused safely after ${n.applied} actions; ${n.pending} remain.`:n.status==="stale"?"The collection changed again. Review the newest plan; no stale decision was applied.":`Sync stopped at a durable boundary: ${n.failure?.message??n.status}.`,await this.host.recordSyncActivity({summary:n.status==="applied"?`Synchronized ${n.applied} ${n.applied===1?"change":"changes"}`:n.status==="cancelled"?"Synchronization paused safely":"Synchronization needs attention",detail:this.transientMessage,tone:n.status==="applied"?"success":"attention",requiresAcknowledgement:n.status!=="applied"})}catch(n){let i=nr(n);if(this.syncProblem=i,this.host.setSyncProblem(i),this.transientMessage=i.message,await this.host.recordSyncActivity({summary:i.title,detail:i.message,tone:i.action==="resume"?"attention":"error",requiresAcknowledgement:!0}),!uh(n))throw n}finally{this.mirrorProgress=null,this.fileProgress=null,this.host.setSyncProgress(null,null)}}renderRecoveryCard(t,n){let i=t.createDiv({cls:"mdbase-recovery-card"}),s=i.createDiv();s.createEl("strong",{text:n.title}),s.createDiv({text:n.message});let o=i.createEl("button",{text:n.actionLabel});o.disabled=this.busy,o.onclick=()=>{n.action==="retry"?this.reconnectCollection():n.action==="reauthorize"?this.perform(()=>this.reauthorizeCollection()):this.reviewSyncChanges()}}async reauthorizeCollection(){this.enrollmentAbort?.abort();let t=new AbortController;this.enrollmentAbort=t;try{this.mirrorStatus=await this.host.connectSync.reauthorize({signal:t.signal,onVerification:n=>{this.enrollmentVerification=n.verificationUri,this.transientMessage="Approve this vault again in Connect. Its local files and checkpoint remain unchanged.",window.open(n.verificationUri,"_blank","noopener,noreferrer"),this.render()},onStatus:n=>{this.transientMessage=n.state==="waiting_for_approval"?"Waiting for approval in Connect\u2026":`Connect is retrying approval (attempt ${n.attempt}).`,this.render()}}),this.enrollmentVerification="",this.syncProblem=null,this.host.setSyncStatus(this.mirrorStatus),this.transientMessage="Approval restored. The existing mirror checkpoint was preserved.",await this.host.recordSyncActivity({summary:"Connect approval restored",detail:"The existing mirror checkpoint and local files were preserved.",tone:"success",requiresAcknowledgement:!1})}finally{this.enrollmentAbort===t&&(this.enrollmentAbort=null)}}renderActivity(t){let n=t.createEl("section",{cls:"mdbase-editor-section mdbase-activity"});n.id="mdbase-sync-activity";let i=n.createDiv({cls:"mdbase-section-header"});i.createEl("h3",{text:"Recent activity"});let s=this.host.getSyncActivity();if(s.some(a=>!a.requiresAcknowledgement)){let a=i.createEl("button",{text:"Clear completed"});a.disabled=this.busy,a.onclick=()=>void this.host.clearCompletedSyncActivity().then(()=>this.render())}if(this.fileProgress){let a=n.createDiv({cls:"mdbase-activity-row is-current"});(0,ce.setIcon)(a.createSpan(),this.fileProgress.direction==="upload"?"upload":"download");let c=a.createDiv();c.createEl("strong",{text:`${this.fileProgress.direction==="upload"?"Uploading":"Downloading"} ${this.fileProgress.path}`}),c.createDiv({text:`${At(this.fileProgress.transferredBytes)} of ${At(this.fileProgress.totalBytes)}`})}let o=[...s].reverse().filter((a,c)=>a.requiresAcknowledgement||c<8);if(!o.length&&!this.fileProgress){n.createDiv({cls:"mdbase-muted",text:"No recent synchronization activity."});return}for(let a of o){let c=n.createDiv({cls:"mdbase-activity-row"});c.setAttr("data-tone",a.tone),(0,ce.setIcon)(c.createSpan(),a.tone==="success"?"check":a.tone==="info"?"info":"circle-alert");let l=c.createDiv();if(l.createEl("strong",{text:a.summary}),a.detail&&l.createDiv({text:a.detail}),l.createSpan({cls:"mdbase-muted",text:dh(a.occurredAt)}),a.requiresAcknowledgement){let u=c.createEl("button",{text:"Dismiss"});u.disabled=this.busy,u.onclick=()=>void this.host.dismissSyncActivity(a.id).then(()=>this.render())}}}renderConnectionDetails(t,n){let i=t.createEl("section",{cls:"mdbase-editor-section mdbase-connection-details"});i.createEl("h3",{text:"Collection connection"});let s=i.createDiv({cls:"mdbase-status-list"});Fe(s,"Collection",n.name),Fe(s,"Collection ID",n.collectionId),Fe(s,"Vault",this.app.vault.getName()),Fe(s,"Access",n.mode==="read_write"?"Read and write":"Read only"),Fe(s,"Account","Approved through Connect"),Fe(s,"Connect",new URL(n.controlUrl).host),Fe(s,"Last successful sync",dh(this.mirrorStatus?.last_synced_at)),i.createEl("p",{text:"This plugin is the only sync owner for this vault. Connect owns authorization and the mirror engine owns checkpoints and conflict decisions."});let o=i.createDiv({cls:"mdbase-actions"}),a=o.createEl("button",{text:"Reconnect"});a.disabled=this.busy,a.onclick=()=>void this.reconnectCollection();let c=o.createEl("button",{text:"Disconnect\u2026"});c.disabled=this.busy||this.host.connectSync.isSyncing(),c.onclick=()=>void this.disconnectCollection(n)}async disconnectCollection(t){let n=await new hh(this.app).choose(t.name);n&&await this.perform(async()=>{let i=await this.host.connectSync.disconnect(n==="remove");this.mirrorStatus=null,this.mirrorPreview=null,this.syncProblem=null,this.host.setSyncStatus(null,{clearLocalChanges:!0});let s=n==="remove"?`${i.removed.length} unchanged synced ${i.removed.length===1?"file was":"files were"} removed. ${i.preserved.length} locally changed ${i.preserved.length===1?"file was":"files were"} preserved.`:"All local files were retained as an unsynced copy.";this.transientMessage=`Disconnected from ${t.name}. ${s}`,await this.host.recordSyncActivity({summary:`Disconnected from ${t.name}`,detail:s,tone:i.preserved.length?"attention":"info",requiresAcknowledgement:i.preserved.length>0}),await this.refresh(!0)})}focusPendingSyncSection(){if(!this.pendingSyncFocus)return;let t=this.pendingSyncFocus==="activity"?"mdbase-sync-activity":"mdbase-sync-conflicts",n=this.containerEl.querySelector(`#${t}`);n&&(this.pendingSyncFocus=null,n.scrollIntoView({behavior:"smooth",block:"start"}))}filePolicy(){return this.filePolicyDraft??=JSON.parse(JSON.stringify(this.host.connectSync.getSelectiveSync())),this.filePolicyDraft}renderFilePolicyControls(t,n){let i=this.filePolicy(),s=t.createEl("section",{cls:"mdbase-editor-section mdbase-file-policy"});s.createEl("h3",{text:n.connected?"Files on this device":"Collection files"}),s.createEl("p",{text:"Markdown always syncs. Choose which binary file classes this device should materialize; hidden and reserved paths remain excluded."});let o=s.createDiv({cls:"mdbase-file-class-grid"}),a=[["image","Images"],["audio","Audio"],["video","Video"],["pdf","PDFs"],["other","Other files"]];for(let[f,p]of a){let m=o.createEl("label"),h=m.createEl("input",{type:"checkbox"});h.setAttr("data-focus-key",`file-class-${f}`),h.checked=i.file_classes.includes(f),h.onchange=()=>{i.file_classes=h.checked?[...new Set([...i.file_classes,f])]:i.file_classes.filter(y=>y!==f),this.render()},m.createSpan({text:p})}let c=null;if(Lt(s,"Excluded folders",i.excluded_folders.join(", "),f=>{if(i.excluded_folders=f.split(",").map(p=>p.trim()).filter(Boolean),c){let p=JSON.stringify(this.host.connectSync.getSelectiveSync())!==JSON.stringify(i);c.textContent=p?"Apply file policy":"File policy applied",c.disabled=!p||this.busy}},{description:"Comma-separated collection-relative folders. Exclusions apply to Markdown and binary files on this device.",placeholder:"Archive, Private exports"}),i.file_classes.length?i.file_classes.includes("other")&&s.createDiv({cls:"mdbase-inline-message",text:"Other files includes every eligible visible non-Markdown format. Review the transfer ledger carefully before syncing."}):s.createDiv({cls:"mdbase-inline-message",text:"Binary sync is off. This mirror remains Markdown-only."}),!n.connected)return;let l=this.host.connectSync.getSelectiveSync(),u=JSON.stringify(l)!==JSON.stringify(i);c=s.createDiv({cls:"mdbase-actions"}).createEl("button",{text:u?"Apply file policy":"File policy applied"}),c.disabled=!u||this.busy,c.onclick=()=>void this.perform(async()=>{await this.host.connectSync.configureSelectiveSync(i),this.filePolicyDraft=null,this.mirrorPreview=null,this.transientMessage="File policy updated. Review the rebuild before files move.",this.render()})}renderEnrollment(t){if(this.schema||this.host.connectSync.getAdoptionMarker()){this.renderLocalAdoption(t);return}let n=t.createEl("section",{cls:"mdbase-editor-section mdbase-enrollment"});if(n.createEl("h3",{text:"Connect an empty vault"}),n.createEl("p",{text:"Choose access, approve it in Connect, then review the first transfer before any files move."}),this.renderEnrollmentSteps(n,this.enrollmentVerification||this.enrollmentAbort?2:1,["Choose connection","Approve in Connect","Review first sync"]),this.enrollmentVerification){let c=n.createDiv({cls:"mdbase-approval-link"});c.createSpan({text:"Waiting for approval \xB7 "});let l=c.createEl("a",{text:"Open approval page again",href:this.enrollmentVerification});l.setAttr("target","_blank"),l.setAttr("rel","noopener noreferrer")}Lt(n,"Connect URL",this.enrollmentControlUrl,c=>{this.enrollmentControlUrl=c},{placeholder:lh}),Lt(n,"Mirror name",this.enrollmentMirrorName,c=>{this.enrollmentMirrorName=c}),Lt(n,"Collection ID",this.enrollmentCollectionId,c=>{this.enrollmentCollectionId=c},{description:"Optional. Leave blank to choose during approval."});let i=n.createDiv({cls:"mdbase-form-row"});i.createEl("label",{text:"Access"});let s=i.createEl("select");s.createEl("option",{value:"read_write",text:"Read and write"}),s.createEl("option",{value:"read_only",text:"Read only"}),s.value=this.enrollmentMode,s.onchange=()=>{this.enrollmentMode=s.value==="read_only"?"read_only":"read_write"},this.renderFilePolicyControls(n,{connected:!1});let o=n.createDiv({cls:"mdbase-actions"}),a=o.createEl("button",{text:this.enrollmentAbort?"Waiting for approval\u2026":"Continue to approval"});if(a.addClass("mod-cta"),a.disabled=this.busy,a.onclick=()=>void this.perform(async()=>{this.enrollmentAbort?.abort();let c=new AbortController;this.enrollmentAbort=c;try{await this.host.connectSync.enroll({controlUrl:this.enrollmentControlUrl,mirrorName:this.enrollmentMirrorName,mode:this.enrollmentMode,selectiveSync:this.filePolicy(),...this.enrollmentCollectionId.trim()?{collectionId:this.enrollmentCollectionId.trim()}:{}},{signal:c.signal,onVerification:d=>{this.enrollmentVerification=d.verificationUri,this.transientMessage="Approve the mirror in the Connect page. This view will keep waiting securely.",window.open(d.verificationUri,"_blank","noopener,noreferrer"),this.render()},onStatus:d=>{this.transientMessage=d.state==="waiting_for_approval"?"Waiting for approval in Connect\u2026":`Connect is retrying enrollment (attempt ${d.attempt}).`,this.render()}}),this.enrollmentVerification="",await this.loadMirrorPreview();let l=this.mirrorPreview?.entries.reduce((d,f)=>d+(f.estimatedBytes??0),0)??0,u=this.mirrorPreview?.entries.length??0;this.transientMessage=`Mirror enrolled. The first review contains ${u} ${u===1?"item":"items"}${l?` and about ${At(l)} of binary data`:""}. No files have moved yet.`,this.render()}catch(l){if(!uh(l))throw l;this.transientMessage="Approval wait cancelled. No files were synchronized."}finally{this.enrollmentAbort===c&&(this.enrollmentAbort=null)}}),this.enrollmentAbort){let c=o.createEl("button",{text:"Stop waiting"});c.onclick=()=>{this.enrollmentAbort?.abort(),this.transientMessage="Approval wait cancelled. No files were synchronized.",this.render()}}}renderLocalAdoption(t){let n=this.host.connectSync.getAdoptionMarker(),i=t.createEl("section",{cls:"mdbase-editor-section mdbase-enrollment"});i.createEl("h3",{text:"Host this local collection"}),i.createEl("p",{text:n?"This vault has a durable adoption checkpoint. Resume it without creating another hosted collection.":"Hosted mdbase will adopt an exact snapshot and become the collection authority. This vault will then continue as a read-write mirror."});let s=n?["waiting_for_approval","uploading","fenced","activating","adopted"].indexOf(n.phase)+1:1;this.renderEnrollmentSteps(i,Math.min(4,Math.max(1,s)),["Approve move","Stage snapshot","Activate authority","Reconnect mirror"]);let o=this.enrollmentVerification||n?.session.verificationUri;if(o){let l=i.createDiv({cls:"mdbase-approval-link"});l.createSpan({text:"Approval page: "});let u=l.createEl("a",{text:"Open Connect",href:o});u.setAttr("target","_blank"),u.setAttr("rel","noopener noreferrer")}if(n&&(this.enrollmentControlUrl=n.session.controlUrl,this.enrollmentMirrorName=n.session.requested.mirrorName??"Obsidian"),!n)Lt(i,"Connect URL",this.enrollmentControlUrl,l=>{this.enrollmentControlUrl=l},{placeholder:lh}),Lt(i,"Mirror name",this.enrollmentMirrorName,l=>{this.enrollmentMirrorName=l}),this.renderFilePolicyControls(i,{connected:!1});else{let l=i.createDiv({cls:"mdbase-status-list"});Fe(l,"Collection",n.session.requested.collectionId),Fe(l,"Phase",n.phase.replace(/_/g," ")),Fe(l,"Connect",n.session.controlUrl);let u=this.host.connectSync.getSelectiveSync();Fe(l,"Files",u.file_classes.length?u.file_classes.join(", "):"Markdown only")}let a=i.createDiv({cls:"mdbase-inline-message"});a.createEl("strong",{text:"Authority cut-over: "}),a.appendText("once final staging begins, plugin-managed local edits pause until hosted activation is confirmed. The checkpoint survives app restarts and uncertain network responses.");let c=i.createEl("button",{text:n?"Resume adoption":"Approve and host collection"});if(c.addClass("mod-cta"),c.disabled=this.busy,c.onclick=()=>void this.perform(async()=>{this.enrollmentAbort?.abort();let l=new AbortController;this.enrollmentAbort=l;let u=p=>{this.enrollmentVerification=p.verificationUri,this.transientMessage="Approve the authority move in Connect, then return here. This checkpoint is safe to resume.",this.render()},d=p=>{this.transientMessage=p.state==="waiting_for_approval"?"Waiting for authority-move approval in Connect\u2026":`Connect is retrying (attempt ${p.attempt}).`,this.render()},f=(p,m,h)=>{this.adoptionFileProgress=`${p} \xB7 ${At(m)} of ${At(h)}`,this.transientMessage=`Uploading collection file ${this.adoptionFileProgress}`,this.render()};try{n?await this.host.connectSync.resumeAdoption({signal:l.signal,onVerification:u,onStatus:d,onFileProgress:f}):await this.host.connectSync.adoptLocalCollection({controlUrl:this.enrollmentControlUrl,mirrorName:this.enrollmentMirrorName,selectiveSync:this.filePolicy()},{signal:l.signal,onVerification:u,onStatus:d,onFileProgress:f}),this.enrollmentVerification="",this.transientMessage="Hosted mdbase is authoritative and this vault is now its read-write mirror.",await this.refresh(!0)}catch(p){if(!uh(p))throw p;this.transientMessage="Paused safely. Use Resume adoption to continue from the durable checkpoint."}finally{this.enrollmentAbort===l&&(this.enrollmentAbort=null)}}),this.enrollmentAbort){let l=i.createEl("button",{text:"Stop waiting"});l.onclick=()=>{this.enrollmentAbort?.abort(),this.transientMessage="Paused safely. Use Resume adoption to continue from the durable checkpoint.",this.render()}}if(n&&!["activating","adopted"].includes(n.phase)){let l=i.createEl("button",{text:"Cancel adoption"});l.disabled=this.busy,l.onclick=()=>void this.perform(async()=>{await this.host.connectSync.cancelAdoption(),this.enrollmentVerification="",this.transientMessage="Collection adoption cancelled. This vault remains the local authority.",await this.refresh(!0)})}}renderEnrollmentSteps(t,n,i){let s=t.createEl("ol",{cls:"mdbase-enrollment-steps"});i.forEach((o,a)=>{let c=s.createEl("li"),l=a+1;c.toggleClass("is-complete",ll+(u.estimatedBytes??0),0);s.createSpan({cls:"mdbase-transfer-total",text:`${n.entries.length} ${n.entries.length===1?"item":"items"} \xB7 ${n.entries.filter(l=>l.kind==="file").length} files${a?` \xB7 about ${At(a)}`:""}`});let c=[{direction:"download",title:"Download to this vault",empty:"No hosted changes to download."},{direction:"upload",title:"Upload to hosted",empty:"No local changes to upload."},{direction:"attention",title:"Needs attention",empty:"Nothing is blocking or excluded."}];for(let l of c){let u=n.entries.filter(h=>h.direction===l.direction);if(!u.length&&l.direction!=="attention")continue;let d=i.createEl("section",{cls:"mdbase-transfer-group"});d.setAttr("data-direction",l.direction);let f=d.createDiv({cls:"mdbase-transfer-group-heading"}),p=f.createSpan({cls:"mdbase-transfer-group-icon"});if((0,ce.setIcon)(p,l.direction==="download"?"download":l.direction==="upload"?"upload":"circle-alert"),f.createEl("h4",{text:l.title}),f.createSpan({text:String(u.length),cls:"mdbase-transfer-count"}),!u.length){d.createDiv({cls:"mdbase-transfer-empty",text:l.empty});continue}let m=d.createDiv({cls:"mdbase-transfer-ledger"});for(let h of u.slice(0,250)){let y=m.createDiv({cls:"mdbase-transfer-row"});y.createSpan({cls:"mdbase-transfer-action",text:h.action}).setAttr("data-action",h.action);let _=y.createDiv({cls:"mdbase-transfer-body"}),v=_.createDiv({cls:"mdbase-transfer-path"});if(v.createEl("code",{text:h.path}),v.createSpan({cls:"mdbase-transfer-kind",text:h.kind==="file"?"File":"Markdown"}),h.estimatedBytes!==void 0&&v.createSpan({cls:"mdbase-transfer-size",text:At(h.estimatedBytes)}),_.createDiv({text:h.detail}),h.direction==="attention"){let w=y.createEl("button",{text:"Open"});w.onclick=()=>void this.host.openFileByPath(h.path)}}u.length>250&&m.createDiv({cls:"mdbase-transfer-more",text:`${u.length-250} more items are included in this transfer.`})}n.collisions.length?i.createDiv({cls:"mdbase-inline-error",text:"Resolve path collisions before the first sync. Existing local files are never overwritten without review."}):n.local_issues.length&&i.createDiv({cls:"mdbase-inline-message",text:"Synchronization is paused until every invalid or unreadable local file listed here is fixed."})}renderConflicts(t,n){let i=t.createEl("section",{cls:"mdbase-editor-section"});i.id="mdbase-sync-conflicts",i.createEl("h3",{text:"Conflicts"}),i.createEl("p",{text:"Inspect both versions before deciding. Decisions are checked again immediately before they are applied."});for(let s of n.conflicts){let o=i.createDiv({cls:"mdbase-conflict-row"}),a=o.createDiv({cls:"mdbase-conflict-summary"});a.createEl("strong",{text:s.path??s.object_id}),a.createDiv({text:s.entity==="file"?"Binary file conflict":"Note conflict",cls:"mdbase-muted"}),a.createDiv({text:s.message});let c=o.createDiv({cls:"mdbase-actions"}),l=`${s.object_id}:${s.decision_id}`,u=this.conflictComparisons.get(l),d=c.createEl("button",{text:u?"Hide versions":this.loadingConflictComparisons.has(l)?"Loading versions\u2026":"Compare versions"});d.disabled=this.busy||this.loadingConflictComparisons.has(l),d.onclick=()=>{if(u){this.conflictComparisons.delete(l),this.render();return}this.loadConflictComparison(s,l)};for(let f of["local","remote"]){let p=c.createEl("button",{text:f==="local"?"Keep local":"Use hosted"});p.disabled=this.busy,p.onclick=()=>void this.resolveMirrorConflict(s,f,!1)}if(s.path){let f=c.createEl("button",{text:"Keep both"});f.disabled=this.busy,f.onclick=()=>void this.resolveMirrorConflict(s,"remote",!0)}u&&this.renderConflictComparison(o,u)}}async loadConflictComparison(t,n){this.loadingConflictComparisons.add(n),this.render();try{this.conflictComparisons.set(n,await this.host.connectSync.conflictComparison(t))}catch(i){let s=nr(i);this.syncProblem=s,this.host.setSyncProblem(s),this.transientMessage=s.message,s.code==="conflict_decision_stale"&&await this.refreshMirrorStatus()}finally{this.loadingConflictComparisons.delete(n),this.render()}}renderConflictComparison(t,n){let i=t.createDiv({cls:"mdbase-conflict-comparison"});if(n.entity==="record"){let o=fS(n.local.document??"",n.remote.document??""),a=i.createDiv({cls:"mdbase-conflict-legend"});a.createSpan({text:"\u2212 Local only",cls:"is-local"}),a.createSpan({text:"+ Hosted only",cls:"is-remote"});let c=i.createEl("pre",{cls:"mdbase-conflict-diff"});for(let l of o.lines){let u=c.createEl("div",{cls:`is-${l.kind}`});u.createSpan({text:l.kind==="local"?"\u2212":l.kind==="remote"?"+":" "}),u.createSpan({text:l.value||" "})}o.truncated&&i.createDiv({cls:"mdbase-muted",text:"Diff shortened for a responsive review. Open the local note to inspect it in full."});return}let s=i.createDiv({cls:"mdbase-conflict-sides"});this.renderConflictSide(s,"Local",n.local),this.renderConflictSide(s,"Hosted",n.remote)}renderConflictSide(t,n,i){let s=t.createDiv({cls:"mdbase-conflict-side"});if(s.createEl("h4",{text:n}),i.state==="absent"){s.createDiv({cls:"mdbase-muted",text:"File is absent in this version."});return}if(i.path&&s.createEl("code",{text:i.path}),i.size!==void 0&&s.createDiv({text:`Size: ${At(i.size)}`}),i.modifiedAt&&s.createDiv({text:`Modified: ${new Date(i.modifiedAt).toLocaleString()}`}),i.revision&&s.createDiv({cls:"mdbase-conflict-digest",text:`Digest: ${i.revision}`}),i.resourceUrl&&i.path&&/\.(?:avif|gif|jpe?g|png|svg|webp)$/i.test(i.path)){let o=s.createEl("img",{cls:"mdbase-conflict-image-preview"});o.src=i.resourceUrl,o.alt=`${n} preview of ${i.path}`}else n==="Hosted"&&s.createDiv({cls:"mdbase-muted",text:"Hosted binary preview is materialized only after you choose it."})}async resolveMirrorConflict(t,n,i){await this.perform(async()=>{let s=null;try{i&&(s=await this.host.connectSync.preserveConflictCopy(t.path??"")),this.mirrorPreview=null;let o=await uS(this.host.connectSync,t.object_id,t.decision_id,n);this.mirrorStatus=o.status,this.mirrorPreview=o.preview,this.host.setSyncStatus(o.status),this.conflictComparisons.delete(`${t.object_id}:${t.decision_id}`),this.transientMessage=s?`Both versions are safe: the hosted version will use the original path and the local version was copied to ${s}. Review the refreshed plan before syncing.`:"Conflict resolved. Review the refreshed engine plan before syncing.",await this.host.recordSyncActivity({summary:s?"Conflict kept as two files":`Conflict resolved with ${n==="local"?"local":"hosted"} version`,detail:this.transientMessage,path:t.path??void 0,tone:"info",requiresAcknowledgement:!1})}catch(o){let a=nr(o);s&&(a.message=`The local copy at ${s} is safe, but the original changed again. Review the newest versions before deciding.`),this.syncProblem=a,this.host.setSyncProblem(a),this.transientMessage=a.message,a.code==="conflict_decision_stale"&&(this.mirrorPreview=null,await this.refreshMirrorStatus()),await this.host.recordSyncActivity({summary:a.title,detail:a.message,path:t.path??void 0,tone:"attention",requiresAcknowledgement:!0})}})}renderLocalMirrorIssues(t,n){let i=t.createEl("section",{cls:"mdbase-editor-section"});i.createEl("h3",{text:"Local files needing attention"}),i.createEl("p",{text:"Synchronization is paused to keep the mirror checkpoint exact. Fix every malformed or unreadable file below, then preview again."});for(let s of n.local_issues){let o=i.createDiv({cls:"mdbase-conflict-row"}),a=o.createDiv();a.createEl("strong",{text:s.path}),a.createDiv({text:s.message});let l=o.createDiv({cls:"mdbase-actions"}).createEl("button",{text:"Open file"});l.disabled=this.busy,l.onclick=()=>void this.host.openFileByPath(s.path)}}renderIssues(t){let n=t.createDiv({cls:"mdbase-issues-document"}),i=this.host.getIssues(),s=new Set(i.map(g=>g.path)).size,o=n.createDiv({cls:"mdbase-document-header"}),a=o.createDiv();a.createEl("h2",{text:"Issues"}),a.createEl("p",{text:i.length?`${i.length.toLocaleString()} validation issues in ${s.toLocaleString()} files.`:"The collection has no current validation issues."});let c=o.createEl("button",{text:"Validate collection"});c.disabled=this.busy,c.onclick=()=>void this.perform(async()=>{await this.host.validateCollection(),this.render()});let l=n.createDiv({cls:"mdbase-issue-controls"}),u=l.createEl("select");u.setAttr("aria-label","Issue severity"),u.createEl("option",{value:"all",text:"All severities"}),u.createEl("option",{value:"error",text:"Errors"}),u.createEl("option",{value:"warn",text:"Warnings"}),u.value=this.issueSeverity,u.onchange=()=>{this.issueSeverity=u.value==="error"||u.value==="warn"?u.value:"all",this.issueLimit=250,this.render()};let d=l.createEl("input",{type:"search"});d.setAttr("aria-label","Filter issues"),d.setAttr("data-focus-key","issue-search"),d.placeholder="Filter by path, code, field, or message",d.value=this.issueQuery,d.oninput=()=>{this.issueQuery=d.value,this.issueLimit=250,this.render();let g=this.containerEl.querySelector(".mdbase-issue-controls input[type='search']");g?.focus(),g?.setSelectionRange(g.value.length,g.value.length)};let f=this.issueQuery.trim().toLowerCase(),p=i.filter(g=>this.issueSeverity!=="all"&&g.severity!==this.issueSeverity?!1:f?`${g.path} ${g.code} ${g.field??""} ${g.message}`.toLowerCase().includes(f):!0),m=new Set(p.map(g=>g.path)).size;if(n.createDiv({cls:"mdbase-issues-summary",text:p.length===i.length?`Showing ${Math.min(p.length,this.issueLimit).toLocaleString()} of ${p.length.toLocaleString()} issues`:`${p.length.toLocaleString()} matching issues in ${m.toLocaleString()} files`}),!p.length){n.createDiv({cls:"mdbase-empty-state",text:"No validation issues."});return}let h=p.slice(0,this.issueLimit),y=new Map;for(let g of h)y.set(g.path,[...y.get(g.path)??[],g]);for(let[g,_]of y){let v=n.createEl("section",{cls:"mdbase-issue-group"}),w=v.createDiv({cls:"mdbase-issue-group-header"}),S=w.createEl("button",{cls:"mdbase-issue-file-button"});(0,ce.setIcon)(S.createSpan({cls:"mdbase-issue-file-icon"}),"file-text"),S.createSpan({cls:"mdbase-issue-file-path",text:g}),S.onclick=()=>void this.host.openFileByPath(g),w.createSpan({cls:"mdbase-issue-file-count",text:`${_.length} ${_.length===1?"issue":"issues"}`});for(let x of _){let C=v.createDiv({cls:"mdbase-issue-row"});C.setAttr("data-severity",x.severity),C.createSpan({cls:"mdbase-issue-indicator"}).setAttr("aria-hidden","true");let $=C.createDiv({cls:"mdbase-issue-metadata"});$.createEl("code",{text:x.code}),$.createDiv({cls:"mdbase-issue-context",text:`${x.severity==="warn"?"Warning":"Error"}${x.field?` \xB7 ${x.field}`:""}`}),C.createDiv({cls:"mdbase-issue-row-message",text:x.message});let M=C.createDiv({cls:"mdbase-issue-row-actions"}),A=M.createEl("button",{text:x.field?"Open field":"Open file"});A.setAttr("aria-label",`Open ${x.path}${x.field?` at ${x.field}`:""}`),A.onclick=()=>void this.host.openFileByPath(x.path,x.field);let W=this.host.getQuickFixLabel(x);if(W){let q=M.createEl("button",{text:W});q.addClass("mod-cta"),q.onclick=()=>void this.perform(async()=>{await this.host.applyQuickFix(x)})}}}if(p.length>h.length){let g=n.createEl("button",{cls:"mdbase-load-more",text:`Load ${Math.min(250,p.length-h.length)} more`});g.onclick=()=>{this.issueLimit+=250,this.render()}}}async selectType(t,n=!0){if(n&&this.dirty&&t!==this.selectedPath){new ce.Notice("Save or discard the current type changes before switching.");return}let i=++this.typeSelectionVersion,s=await this.host.loadTypeModel(t);if(i!==this.typeSelectionVersion)return;let o=this.host.loadTypeDraft(t),a=o?.version===1&&o.sourceRevision===(s.sourceRevision??null),c=a?oi(o.model):s;this.selectedPath=t,this.model=c,this.originalModel=oi(s),this.yamlDraft=a&&o.yamlDraft?o.yamlDraft:`${Vt(fh(c),c.body)} +`),i=t.length>200||n.length>200,s=t.slice(0,200),o=n.slice(0,200),a=Array.from({length:s.length+1},()=>new Uint16Array(o.length+1));for(let d=s.length-1;d>=0;d-=1)for(let f=o.length-1;f>=0;f-=1)a[d][f]=s[d]===o[f]?a[d+1][f+1]+1:Math.max(a[d+1][f],a[d][f+1]);let c=[],l=0,u=0;for(;(l=300);)l=o.length||l=a[l][u+1]?(c.push({kind:"local",value:s[l]}),l+=1):(c.push({kind:"remote",value:o[u]}),u+=1);return{lines:c,truncated:i||l0)return{actionLabel:"Fix local files before syncing",actionDisabled:!0,message:"Synchronization is paused. Resolve the blocking issues, then refresh the review."};let n=r.actions.filter(o=>o.command!=="advance_checkpoint").length,i=r.actions.some(o=>o.command==="advance_checkpoint"),s=r.issues.some(o=>o.code==="invalid_frontmatter"&&!o.blocking);return{actionLabel:n?`Sync ${n} ${n===1?"outcome":"outcomes"}`:i?"Confirm sync checkpoint":"Already up to date",actionDisabled:t||r.actions.length===0,message:s?"Frontmatter warnings do not block this sync. Document bytes are preserved; synchronization does not repair YAML.":e?"Review each transfer below, then sync when ready.":"This vault and the hosted collection are already aligned."}}function At(r){return r<1024?`${r} B`:r<1024*1024?`${(r/1024).toFixed(r<10*1024?1:0)} KB`:r<1024*1024*1024?`${(r/(1024*1024)).toFixed(r<10*1024*1024?1:0)} MB`:`${(r/(1024*1024*1024)).toFixed(1)} GB`}function ch(r){let{connected:e,status:t,progress:n,fileProgress:i,problem:s,validationIssues:o,localChangeObserved:a}=r;if(!e)return o?{state:"attention",label:`mdbase: ${o} ${o===1?"issue":"issues"}`,detail:"Open validation issues",destination:"issues"}:{state:"local",label:"mdbase: Local",detail:"This vault is not connected",destination:"sync"};if(i){let l=i.totalBytes>0?Math.min(100,Math.round(i.transferredBytes/i.totalBytes*100)):100;return{state:"syncing",label:`mdbase: ${i.direction==="upload"?"Uploading":"Downloading"} ${l}%`,detail:`${i.path} \xB7 ${At(i.transferredBytes)} of ${At(i.totalBytes)}`,destination:"sync"}}if(n)return{state:"syncing",label:`mdbase: Syncing${n.total==null?"":` ${n.completed}/${n.total}`}`,detail:"Open synchronization progress",destination:"sync"};if(s)return{state:s.action==="resume"?"paused":s.action==="retry"?"offline":"attention",label:s.action==="resume"?"mdbase: Paused":s.action==="retry"?"mdbase: Offline":"mdbase: Needs attention",detail:s.title,destination:"sync"};if(t?.recovery_required||t?.conflicts.length||t?.local_issues.length||["attention","blocked","failed","stale"].includes(t?.state??""))return{state:"attention",label:"mdbase: Needs attention",detail:"Review synchronization",destination:"sync"};if(t?.state==="cancelled")return{state:"paused",label:"mdbase: Paused",detail:"Review and resume synchronization",destination:"sync"};let c=t?.pending??0;return a||c>0||t?.state==="changes_waiting"||t?.state==="planned"?{state:"waiting",label:c>0?`mdbase: ${c} ${c===1?"change":"changes"}`:"mdbase: Changes waiting",detail:"Review local and hosted changes",destination:"sync"}:t?.state==="up_to_date"?{state:"synced",label:"mdbase: Synced",detail:"Local and hosted collections are aligned",destination:"sync"}:{state:"waiting",label:"mdbase: Ready to sync",detail:"Review the first synchronization",destination:"sync"}}function nr(r){let e=xD(r);return e==="mirror_busy"?{code:e,title:"Synchronization is already running",message:"The active transfer is still using this vault. Its progress is shown below.",action:"resume",actionLabel:"Show progress"}:["mirror_credentials_missing","invalid_mirror_enrollment","mirror_enrollment_expired"].includes(e)?{code:e,title:"Connect approval is required again",message:"Your local files and mirror checkpoint are safe. Approve this vault again to restore access.",action:"reauthorize",actionLabel:"Sign in again"}:["operation_cancelled","cancelled","AbortError"].includes(e)?{code:e,title:"Synchronization paused safely",message:"Completed changes remain checkpointed. Review the current plan before resuming.",action:"resume",actionLabel:"Review and resume"}:["stale","stale_mirror_plan","mirror_plan_stale","conflict_decision_stale"].includes(e)?{code:e,title:"The collection changed again",message:"No stale decision was applied. Review the newest local and hosted versions.",action:"review",actionLabel:"Review newest changes"}:["enrollment_recovery_required","mirror_recovery_required","pending_mirror_recovery"].includes(e)?{code:e,title:"Synchronization needs recovery",message:"Your original files are safe. Resume from the durable checkpoint before disconnecting this vault.",action:"resume",actionLabel:"Resume recovery"}:{code:e,title:"Connect could not be reached",message:AD(r,"Your local files are safe. Check the connection and try again."),action:"retry",actionLabel:"Retry connection"}}function pS(r){return Array.isArray(r)?r.filter(ED).slice(-30):[]}function hS(r,e){return[...r.filter(t=>t.id!==e.id),e].slice(-30)}function mS(r){return{...r,id:crypto.randomUUID(),occurredAt:new Date().toISOString()}}function ED(r){if(!r||typeof r!="object"||Array.isArray(r))return!1;let e=r;return typeof e.id=="string"&&typeof e.occurredAt=="string"&&typeof e.summary=="string"&&(e.detail===void 0||typeof e.detail=="string")&&(e.path===void 0||typeof e.path=="string")&&["success","info","attention","error"].includes(e.tone??"")&&typeof e.requiresAcknowledgement=="boolean"}function xD(r){return r instanceof DOMException?r.name:r&&typeof r=="object"&&"code"in r&&typeof r.code=="string"?r.code:r instanceof Error&&r.name?r.name:"sync_failed"}function AD(r,e){return r instanceof Error&&r.message?r.message:e}var lh="https://connect.mdbase.dev",ai="mdbase-workspace-view",kD=["string","integer","number","boolean","date","datetime","time","enum","link","list","object","any"];function qe(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)}function oi(r){return JSON.parse(JSON.stringify(r))}function vl(r){return typeof r.type=="string"?r.type:"any"}function PD(r){if(!Object.prototype.hasOwnProperty.call(r,"field"))return"field";let e=2;for(;Object.prototype.hasOwnProperty.call(r,`field${e}`);)e+=1;return`field${e}`}function yS(r,e,t){Object.defineProperty(r,e,{configurable:!0,enumerable:!0,writable:!0,value:t})}function Lt(r,e,t,n,i={}){let s=r.createDiv({cls:"mdbase-form-row"}),o=s.createEl("label",{text:e}),a=`mdbase-${Math.random().toString(36).slice(2)}`;o.htmlFor=a,i.description&&s.createDiv({cls:"mdbase-form-description",text:i.description});let c=i.multiline?s.createEl("textarea"):s.createEl("input",{type:"text"});return c.id=a,c.setAttr("data-focus-key",`form-${e.toLowerCase().replace(/[^a-z0-9]+/g,"-")}`),c.value=t,c.placeholder=i.placeholder??"",c.addEventListener("input",()=>n(c.value)),c}function Fe(r,e,t){let n=r.createDiv({cls:"mdbase-status-row"});n.createSpan({cls:"mdbase-status-label",text:e}),n.createSpan({cls:"mdbase-status-value",text:t})}function CD(r){if(r<1e3)return String(r);let e=r<1e4?1:0;return`${(r/1e3).toFixed(e)}k`}function dh(r){if(!r)return"Never synced";let e=Date.parse(r);if(!Number.isFinite(e))return r;let t=Math.round((e-Date.now())/1e3),n=Math.abs(t),i=new Intl.RelativeTimeFormat(void 0,{numeric:"auto"});if(n<60)return i.format(t,"second");let s=Math.round(t/60);if(Math.abs(s)<60)return i.format(s,"minute");let o=Math.round(s/60);return Math.abs(o)<24?i.format(o,"hour"):i.format(Math.round(o/24),"day")}function MD(r){return r?r.state==="up_to_date"?"Up to date":r.state==="changes_waiting"?"Local changes waiting":["attention","blocked","failed","stale"].includes(r.state)||r.recovery_required?"Needs attention":r.state==="cancelled"?"Paused safely":r.state==="applying"?"Synchronizing":r.state==="planned"?"Review ready":"Ready for first sync":"Checking connection"}function uh(r){return r instanceof Error&&r.name==="AbortError"}var ph=class extends ce.Modal{constructor(){super(...arguments);this.resolve=null;this.settled=!1}confirm(t){return new Promise(n=>{this.resolve=n,this.titleEl.setText("Confirm high-impact type changes"),this.contentEl.createEl("p",{text:"These schema changes can change membership or invalidate existing records. The plugin will save only the type definition; it will not rewrite records."});let i=this.contentEl.createEl("ul",{cls:"mdbase-confirm-change-list"});for(let c of t)i.createEl("li",{text:c.summary});let s=this.contentEl.createDiv({cls:"modal-button-container"}),o=s.createEl("button",{text:"Keep reviewing"});o.onclick=()=>this.finish(!1);let a=s.createEl("button",{text:"Save high-impact changes"});a.addClass("mod-warning"),a.onclick=()=>this.finish(!0),this.open()})}onClose(){this.settled||this.finish(!1,!1),this.contentEl.empty()}finish(t,n=!0){this.settled||(this.settled=!0,this.resolve?.(t),this.resolve=null,n&&this.close())}},hh=class extends ce.Modal{constructor(){super(...arguments);this.resolve=null;this.settled=!1}choose(t){return new Promise(n=>{this.resolve=n,this.titleEl.setText("Disconnect this vault?"),this.contentEl.createEl("p",{text:`This stops synchronization with ${t}. It does not delete the hosted collection.`});let i=this.contentEl.createEl("ul");i.createEl("li",{text:"Keep local files leaves the current vault contents in place as an unsynced local copy."}),i.createEl("li",{text:"Remove synced files deletes only files that still exactly match the last checkpoint. Local edits are preserved."});let s=this.contentEl.createDiv({cls:"modal-button-container"}),o=s.createEl("button",{text:"Cancel"});o.onclick=()=>this.finish(null);let a=s.createEl("button",{text:"Disconnect and keep files"});a.onclick=()=>this.finish("keep");let c=s.createEl("button",{text:"Remove unchanged synced files"});c.addClass("mod-warning"),c.onclick=()=>this.finish("remove"),this.open()})}onClose(){this.settled||this.finish(null,!1),this.contentEl.empty()}finish(t,n=!0){this.settled||(this.settled=!0,this.resolve?.(t),this.resolve=null,n&&this.close())}},_l=class extends ce.ItemView{constructor(t,n){super(t);this.host=n;this.destination="types";this.editorMode="design";this.schema=null;this.query="";this.selectedPath=null;this.model=null;this.originalModel=null;this.yamlDraft="";this.dirty=!1;this.busy=!1;this.migrationPlan=null;this.allowLossy=!1;this.mirrorStatus=null;this.mirrorPreview=null;this.mirrorProgress=null;this.fileProgress=null;this.syncProblem=null;this.conflictComparisons=new Map;this.loadingConflictComparisons=new Set;this.pendingSyncFocus=null;this.transientMessage="";this.issueQuery="";this.issueSeverity="all";this.issueLimit=250;this.enrollmentVerification="";this.enrollmentAbort=null;this.enrollmentControlUrl=lh;this.enrollmentMirrorName="Obsidian";this.enrollmentCollectionId="";this.enrollmentMode="read_write";this.filePolicyDraft=null;this.adoptionFileProgress="";this.draftSaveTimer=null;this.fieldQuery="";this.expandedFields=new Set;this.fieldIds=new WeakMap;this.nextFieldId=1;this.refreshVersion=0;this.typeSelectionVersion=0}getViewType(){return ai}getDisplayText(){return"mdbase"}getIcon(){return as}async onOpen(){this.containerEl.addClass("mdbase-workspace"),this.registerDomEvent(this.containerEl,"keydown",t=>{(t.metaKey||t.ctrlKey)&&t.key.toLowerCase()==="s"&&this.dirty&&(t.preventDefault(),this.saveCurrentType())}),await this.refresh(!0)}async onClose(){await this.flushTypeDraft(),this.enrollmentAbort?.abort(),this.enrollmentAbort=null}async refresh(t=!1){let n=++this.refreshVersion;try{let i=await this.host.loadWorkspaceSchema(t);if(n!==this.refreshVersion||(this.schema=i,this.selectedPath&&!this.typeEntries().some(s=>s.filePath===this.selectedPath)&&(this.selectedPath=null,this.model=null,this.originalModel=null),!this.selectedPath&&this.typeEntries().length&&!ce.Platform.isMobile&&(this.selectedPath=this.typeEntries()[0].filePath),this.selectedPath&&(!this.model||t)&&(await this.selectType(this.selectedPath,!1),n!==this.refreshVersion))||(this.destination==="sync"&&await this.refreshMirrorStatus(),n!==this.refreshVersion))return;this.render()}catch(i){if(n!==this.refreshVersion)return;this.transientMessage=i instanceof Error?i.message:String(i),this.render()}}showDestination(t){this.destination=t,t==="sync"?this.refreshMirrorStatus().then(()=>this.render()):this.render()}createNewType(){this.destination="types",this.createType()}async editType(t){this.destination="types",await this.selectType(t)}async reviewSyncChanges(){this.host.getMirrorProfile()&&await this.perform(()=>this.loadMirrorPreview())}async syncNow(){if(this.host.getMirrorProfile()){if(!this.mirrorPreview){await this.reviewSyncChanges(),this.mirrorPreview?.plan.actions.length&&new ce.Notice("The current transfer review is open. Run sync now again or confirm it in the mdbase view.");return}await this.perform(()=>this.applyReviewedSync())}}focusSyncSection(t){this.destination="sync",this.pendingSyncFocus=t,this.render(),window.setTimeout(()=>this.focusPendingSyncSection(),0)}async reconnectCollection(){await this.perform(async()=>{try{this.mirrorStatus=await this.host.connectSync.reconnect(),this.syncProblem=null,this.host.setSyncStatus(this.mirrorStatus),await this.host.recordSyncActivity({summary:"Collection reconnected",detail:"Connect credentials were renewed and the mirror checkpoint was preserved.",tone:"success",requiresAcknowledgement:!1}),this.transientMessage="Connection restored. Your mirror checkpoint was preserved."}catch(t){if(nr(t).action!=="reauthorize")throw t;await this.reauthorizeCollection()}})}typeEntries(){return this.schema?[...this.schema.types.values()].sort((t,n)=>t.name.localeCompare(n.name)):[]}render(){let t=this.containerEl,n=this.captureRenderSnapshot(t);t.empty(),t.addClass("mdbase-workspace");let i=t.createDiv({cls:"mdbase-shell"});this.renderTopbar(i),this.transientMessage&&i.createDiv({cls:"mdbase-inline-message",text:this.transientMessage}).setAttr("role","status");let s=i.createDiv({cls:"mdbase-workspace-content"});s.setAttr("data-scroll-key","workspace"),this.destination==="types"?this.renderTypes(s):this.destination==="sync"?this.renderSync(s):this.renderIssues(s),this.restoreRenderSnapshot(t,n)}captureRenderSnapshot(t){let n=t.ownerDocument,i=t.contains(n.activeElement)?n.activeElement:null,s=i instanceof HTMLInputElement||i instanceof HTMLTextAreaElement?i:null,o=new Map;for(let a of Array.from(t.querySelectorAll("[data-scroll-key]"))){let c=a.getAttr("data-scroll-key");c&&o.set(c,{top:a.scrollTop,left:a.scrollLeft})}return{focusKey:i?.getAttr("data-focus-key")??null,selectionStart:s?.selectionStart??null,selectionEnd:s?.selectionEnd??null,scroll:o}}restoreRenderSnapshot(t,n){for(let[s,o]of n.scroll){let a=t.querySelector(`[data-scroll-key="${s}"]`);a&&(a.scrollTop=o.top,a.scrollLeft=o.left)}if(!n.focusKey)return;let i=t.querySelector(`[data-focus-key="${n.focusKey}"]`);i?.focus({preventScroll:!0}),(i instanceof HTMLInputElement||i instanceof HTMLTextAreaElement)&&n.selectionStart!==null&&n.selectionEnd!==null&&i.setSelectionRange(n.selectionStart,n.selectionEnd)}renderTopbar(t){let n=t.createDiv({cls:"mdbase-topbar"}),i=n.createDiv({cls:"mdbase-identity"}),s=i.createSpan({cls:"mdbase-mark"});s.setAttr("aria-hidden","true"),(0,ce.setIcon)(s,as),i.createSpan({cls:"mdbase-title",text:"mdbase"});let o=n.createDiv({cls:"mdbase-nav"});o.setAttr("role","tablist");for(let[a,c]of[["types","Types"],["sync","Sync"],["issues","Issues"]]){let l=o.createEl("button",{text:c});if(l.addClass("mdbase-nav-button"),l.setAttr("role","tab"),l.setAttr("aria-selected",String(this.destination===a)),this.destination===a&&l.addClass("is-active"),a==="issues"&&this.host.getIssues().length){let u=this.host.getIssues().length;l.createSpan({cls:"mdbase-count",text:CD(u)}).setAttr("title",`${u} issues`)}l.onclick=()=>this.showDestination(a)}}renderTypes(t){if(!this.schema){let i=t.createDiv({cls:"mdbase-empty-state"});i.createEl("h2",{text:"Start an mdbase collection"}),i.createEl("p",{text:"Initialize this vault as a local v0.3 collection, or use Sync to connect an empty vault to a collection authority."});let s=i.createDiv({cls:"mdbase-actions"}),o=s.createEl("button",{text:"Initialize local collection"});o.addClass("mod-cta"),o.disabled=this.busy||this.host.getMirrorProfile()!==null,o.onclick=()=>void this.perform(async()=>{await this.host.initializeCollection(),await this.refresh(!0)});let a=s.createEl("button",{text:"Connect collection authority"});a.onclick=()=>this.showDestination("sync");return}this.schema.config.spec_version.startsWith("0.2.")&&this.renderLegacyBanner(t);let n=t.createDiv({cls:"mdbase-types-layout"});this.model&&n.addClass("has-selection"),this.renderTypeList(n),this.renderTypeEditor(n)}renderLegacyBanner(t){let n=t.createDiv({cls:"mdbase-legacy-banner"}),i=n.createDiv();i.createEl("strong",{text:`mdbase ${this.schema?.config.spec_version} compatibility mode`}),i.createEl("p",{text:"Types are readable and validation remains available, but authoring is disabled until a reviewed v0.3 migration."});let s=n.createEl("button",{text:this.migrationPlan?"Review migration":"Analyze migration"});s.disabled=this.busy||this.host.getMirrorProfile()!==null,s.onclick=()=>void this.perform(async()=>{this.migrationPlan=await this.host.analyzeMigration(),this.render()}),this.host.getMirrorProfile()&&n.createDiv({cls:"mdbase-form-description",text:"Hosted resources must be migrated at the collection authority."}),this.migrationPlan&&this.renderMigrationReview(t,this.migrationPlan)}renderMigrationReview(t,n){let i=t.createDiv({cls:"mdbase-migration-review"}),s=i.createDiv({cls:"mdbase-section-header"});s.createEl("h3",{text:"Migration review"}),s.createSpan({cls:"mdbase-spec-badge",text:`${n.sourceVersion} \u2192 ${n.targetVersion}`});let o=i.createDiv({cls:"mdbase-status-list"});Fe(o,"Files replaced",String(n.operations.length)),Fe(o,"Type definitions",String(n.typeSummaries.length)),Fe(o,"Record reads verified",String(n.recordsVerified)),n.recordsSkipped&&Fe(o,"Records skipped",String(n.recordsSkipped)),Fe(o,"Record files rewritten","0"),Fe(o,"Recovery backup",n.backupLocation);let a=i.createDiv({cls:"mdbase-review-list"});n.diagnostics.length||a.createDiv({cls:"mdbase-review-ok",text:"No migration diagnostics."});for(let d of n.diagnostics.slice(0,250)){let f=a.createDiv({cls:"mdbase-review-item"});f.setAttr("data-severity",d.severity),f.createDiv({cls:"mdbase-review-code",text:`${d.severity} \xB7 ${d.path}`}),f.createDiv({text:d.message})}if(n.diagnostics.length>250&&a.createDiv({cls:"mdbase-form-description",text:`Showing 250 of ${n.diagnostics.length} diagnostics.`}),!n.applicable){let d=i.createEl("label",{cls:"mdbase-consent"}),f=d.createEl("input",{type:"checkbox"});f.checked=this.allowLossy,f.onchange=()=>{this.allowLossy=f.checked,this.render()},d.createSpan({text:"I reviewed the lossy diagnostics and want to apply this migration."})}let c=i.createDiv({cls:"mdbase-actions"}),l=c.createEl("button",{text:"Apply migration"});l.addClass("mod-warning"),l.disabled=this.busy||!n.applicable&&!this.allowLossy,l.onclick=()=>void this.perform(async()=>{await this.host.applyMigration(n,this.allowLossy),this.migrationPlan=null,this.allowLossy=!1,this.model=null,this.originalModel=null,await this.refresh(!0)});let u=c.createEl("button",{text:"Close review"});u.onclick=()=>{this.migrationPlan=null,this.render()}}renderTypeList(t){let n=t.createDiv({cls:"mdbase-type-list-pane"}),i=n.createDiv({cls:"mdbase-pane-header"});i.createEl("h2",{text:"Types"});let s=i.createEl("button");s.setAttr("aria-label","Create type"),(0,ce.setIcon)(s,"plus"),s.disabled=(this.schema?.config.spec_version.startsWith("0.2.")??!0)||this.host.getMirrorProfile()?.mode==="read_only",s.onclick=()=>this.createType();let o=n.createEl("input",{type:"search"});o.addClass("mdbase-type-search"),o.placeholder="Search types",o.setAttr("aria-label","Search types"),o.setAttr("data-focus-key","type-search"),o.value=this.query,o.oninput=()=>{this.query=o.value,this.render();let u=this.containerEl.querySelector(".mdbase-type-search");u?.focus(),u?.setSelectionRange(u.value.length,u.value.length)};let a=n.createDiv({cls:"mdbase-type-list"});a.setAttr("data-scroll-key","type-list");let c=this.query.trim().toLowerCase(),l=this.typeEntries().filter(u=>`${u.name} ${u.description??""} ${u.filePath}`.toLowerCase().includes(c));if(!l.length){a.createDiv({cls:"mdbase-empty-list",text:c?"No matching types.":"No type definitions."});return}for(let u of l){let d=a.createEl("button",{cls:"mdbase-type-row"});u.filePath===this.selectedPath&&d.addClass("is-active"),d.setAttr("aria-current",u.filePath===this.selectedPath?"true":"false"),d.createSpan({cls:"mdbase-type-name",text:u.name}),d.createSpan({cls:"mdbase-type-meta",text:`${Object.keys(u.fields).length} fields \xB7 ${u.specProfile??"v0.2"}`}),d.onclick=()=>void this.selectType(u.filePath)}}renderTypeEditor(t){let n=t.createDiv({cls:"mdbase-type-editor-pane"});if(n.setAttr("data-scroll-key","type-editor"),!this.model){let h=n.createDiv({cls:"mdbase-empty-state"});h.createEl("h2",{text:"Choose a type"}),h.createEl("p",{text:"Select a type definition from the list to inspect or edit it."});return}let i=this.host.getMirrorProfile()?.mode==="read_only",s=this.model.specProfile==="v0.2"||i||!!this.model.readOnlyReason,o=this.model.readOnlyReason??(i?"This mirror has read-only access. Re-enroll it with write access before editing types.":"This v0.2 type is read-only. Review and apply a collection migration before editing."),a=n.createDiv({cls:"mdbase-editor-header"}),c=a.createEl("button",{cls:"mdbase-mobile-back"});c.setAttr("aria-label","Back to type list"),(0,ce.setIcon)(c,"arrow-left"),c.onclick=()=>{if(this.dirty){new ce.Notice("Save or discard the current type changes before going back.");return}this.selectedPath=null,this.model=null,this.originalModel=null,this.render()};let l=a.createDiv(),u=l.createDiv({cls:"mdbase-editor-title-line"});u.createEl("h2",{text:this.model.name||"Untitled type"}),u.createSpan({cls:"mdbase-spec-badge",text:this.model.specProfile??"v0.2"}),this.dirty&&u.createSpan({cls:"mdbase-dirty",text:"Unsaved"}),l.createDiv({cls:"mdbase-editor-path",text:this.selectedPath??"New type"});let d=a.createDiv({cls:"mdbase-editor-actions"});if(this.selectedPath){let h=this.selectedPath,y=d.createEl("button",{text:"Open source"});y.onclick=()=>void this.host.openFileByPath(h)}let f=d.createEl("button",{text:"Save"});if(f.addClass("mod-cta"),f.disabled=s||!this.dirty||this.busy,f.onclick=()=>void this.saveCurrentType(),this.dirty){let h=d.createEl("button",{text:"Discard"});h.onclick=()=>void this.discardCurrentType()}s&&n.createDiv({cls:"mdbase-readonly-note",text:o});let p=n.createDiv({cls:"mdbase-mode-switch"});p.setAttr("role","tablist");for(let[h,y]of[["design","Design"],["yaml","YAML"]]){let g=p.createEl("button",{text:y});g.setAttr("role","tab"),g.setAttr("aria-selected",String(this.editorMode===h)),this.editorMode===h&&g.addClass("is-active"),g.onclick=()=>this.switchEditorMode(h)}let m=n.createDiv({cls:"mdbase-editor-document"});m.setAttr("data-scroll-key","type-document"),this.editorMode==="design"?this.renderDesignEditor(m,this.model,s):this.renderYamlEditor(m,s),this.dirty&&!s&&this.renderDraftBar(n,this.model)}renderDraftBar(t,n){let i=bl(this.originalModel,n),o=gl(n,{knownTypes:this.typeEntries().map(p=>p.name),contracts:this.schema?.contracts.values()}).filter(p=>p.severity==="error").length,a=i.filter(p=>p.risk==="high").length,c=t.createDiv({cls:"mdbase-draft-bar"}),l=c.createDiv({cls:"mdbase-draft-summary"});l.createEl("strong",{text:`${i.length} pending ${i.length===1?"change":"changes"}`}),l.createSpan({text:o?`${o} ${o===1?"error":"errors"} to fix`:a?`${a} high-impact ${a===1?"change":"changes"}`:"Ready to save"});let u=c.createDiv({cls:"mdbase-actions"});if(this.editorMode==="design"){let p=u.createEl("button",{text:"Review"});p.onclick=()=>this.containerEl.querySelector("#mdbase-section-review")?.scrollIntoView({behavior:"smooth",block:"start"})}let d=u.createEl("button",{text:"Discard"});d.onclick=()=>void this.discardCurrentType();let f=u.createEl("button",{text:"Save changes"});f.addClass("mod-cta"),f.disabled=o>0||this.busy,f.onclick=()=>void this.saveCurrentType()}renderDesignEditor(t,n,i){let s=gl(n,{knownTypes:this.typeEntries().map(F=>F.name),contracts:this.schema?.contracts.values()});this.renderSectionNavigation(t,s);let o=t.createEl("section",{cls:"mdbase-editor-section"});o.id="mdbase-section-identity",o.createEl("h3",{text:"Identity"});let a=Lt(o,"Name",n.name,F=>{n.name=F,this.markDirty()},{description:"Stable type name used by collection records."});a.disabled=i;let c=Lt(o,"Description",n.description,F=>{n.description=F,this.markDirty()},{multiline:!0});c.disabled=i;let l=o.createDiv({cls:"mdbase-form-row"}),u=l.createEl("label",{text:"Display field"}),d=l.createEl("select");u.htmlFor=d.id="mdbase-display-field",d.createEl("option",{value:"",text:"Use the file name"});for(let F of n.fields)d.createEl("option",{value:F.name,text:F.name||"Unnamed field"});n.displayNameKey&&!n.fields.some(F=>F.name===n.displayNameKey)&&d.createEl("option",{value:n.displayNameKey,text:`${n.displayNameKey} \xB7 missing`}),d.value=n.displayNameKey,d.onchange=()=>{n.displayNameKey=d.value,this.markDirty()},d.disabled=i;let f=o.createEl("label",{cls:"mdbase-checkbox-row"}),p=f.createEl("input",{type:"checkbox"});p.checked=n.strictMode===!0,p.disabled=i,p.onchange=()=>{n.strictMode=p.checked,this.markDirty()},f.createSpan({text:"Reject undeclared fields"});let m=t.createEl("section",{cls:"mdbase-editor-section"});m.id="mdbase-section-membership",m.createEl("h3",{text:"Membership"});let h=Lt(m,"Path glob",n.matchPathGlob,F=>{n.matchPathGlob=F,this.markDirty()},{placeholder:"Projects/**/*.md"});h.disabled=i;let y=Lt(m,"Fields present",n.matchFieldsPresent,F=>{n.matchFieldsPresent=F,this.markDirty()},{description:"Comma-separated frontmatter keys."});y.disabled=i;let g=Lt(m,"Where",n.matchWhere,F=>{n.matchWhere=F,this.markDirty()},{multiline:!0,description:"YAML predicate, including contains and nested equality conditions.",placeholder:`tags: + contains: task`});g.disabled=i;let _=t.createEl("section",{cls:"mdbase-editor-section"});_.id="mdbase-section-fields";let v=_.createDiv({cls:"mdbase-section-header"});v.createEl("h3",{text:"Fields"});let w=v.createEl("button",{text:"Add field"});w.disabled=i,w.onclick=()=>{let F={type:"string"};n.fields.push({name:"",definition:F}),this.expandedFields.add(this.fieldId(F)),this.markDirty(!0)};let S=_.createDiv({cls:"mdbase-field-toolbar"}),x=n.fields.filter(F=>F.definition.required===!0).length;S.createDiv({cls:"mdbase-field-count",text:`${n.fields.length} ${n.fields.length===1?"field":"fields"} \xB7 ${x} required`});let C=S.createDiv({cls:"mdbase-field-toolbar-actions"}),$=C.createEl("input",{type:"search"});$.placeholder="Filter fields",$.setAttr("aria-label","Filter fields"),$.setAttr("data-focus-key","field-search"),$.value=this.fieldQuery,$.oninput=()=>{this.fieldQuery=$.value,this.render()};let M=C.createEl("button",{text:"Collapse all"});M.disabled=this.expandedFields.size===0,M.onclick=()=>{this.expandedFields.clear(),this.render()};let A=_.createDiv({cls:"mdbase-fields"}),W=this.fieldQuery.trim().toLowerCase(),q=n.fields.filter(F=>!W||this.fieldMatches(F.name,F.definition,W));for(let F of q){let Q=n.fields.indexOf(F);this.renderFieldRow(A,F,Q,i)}q.length||A.createDiv({cls:"mdbase-empty-list",text:n.fields.length?"No fields match this filter.":"No fields declared."});let oe=t.createEl("section",{cls:"mdbase-editor-section"});oe.id="mdbase-section-placement",oe.createEl("h3",{text:"Placement"});let P=Lt(oe,"Path pattern",n.pathPattern,F=>{n.pathPattern=F,this.markDirty()},{placeholder:"Notes/{title}.md"});P.disabled=i,this.renderContractEditor(t,n,i);let k=t.createEl("section",{cls:"mdbase-editor-section mdbase-change-review"});if(k.id="mdbase-section-review",k.createEl("h3",{text:"Change review"}),s.length){let F=k.createDiv({cls:"mdbase-diagnostic-summary"}),Q=s.filter(Xe=>Xe.severity==="error").length,fe=s.length-Q;F.createEl("strong",{text:`${Q} ${Q===1?"error":"errors"} \xB7 ${fe} ${fe===1?"warning":"warnings"}`});for(let Xe of s.slice(0,12)){let Ue=F.createDiv({cls:"mdbase-diagnostic-item"});Ue.setAttr("data-severity",Xe.severity),Ue.createEl("code",{text:Xe.path}),Ue.createSpan({text:Xe.message})}}if(!this.dirty)k.createEl("p",{text:"No pending changes."});else{let F=k.createEl("ul");for(let Q of bl(this.originalModel,n))F.createEl("li",{text:Q.summary}).setAttr("data-risk",Q.risk)}}renderSectionNavigation(t,n){let i=t.createDiv({cls:"mdbase-section-nav"});i.setAttr("aria-label","Type sections"),i.setAttr("data-scroll-key","section-nav");let s=[["identity","Overview"],["membership","Membership"],["fields","Fields"],["applications","Applications"],["review",n.some(o=>o.severity==="error")?"Review \xB7 errors":"Review"]];for(let[o,a]of s){let c=i.createEl("button",{text:a});c.onclick=()=>{this.containerEl.querySelector(`#mdbase-section-${o}`)?.scrollIntoView({behavior:window.matchMedia("(prefers-reduced-motion: reduce)").matches?"auto":"smooth",block:"start"})}}}renderContractEditor(t,n,i){let s=t.createEl("section",{cls:"mdbase-editor-section mdbase-contracts-section"});s.id="mdbase-section-applications";let a=s.createDiv({cls:"mdbase-section-header"}).createDiv();a.createEl("h3",{text:"Works with applications"}),a.createDiv({cls:"mdbase-form-description",text:"Tell compatible applications what this type's fields mean."});let c=[...this.schema?.contracts.values()??[]],l=new Set(n.implementations.map(d=>`${d.contract}@${d.version}`)),u=c.filter(d=>!l.has(ss(d)));c.length||s.createDiv({cls:"mdbase-contract-empty",text:"No record contracts are installed in this collection. Add contract files under the configured contracts folder to connect this type to an application."});for(let d of n.implementations){let f=c.find(p=>p.id===d.contract&&p.version===d.version);this.renderContractImplementation(s,n,d,f,i)}if(u.length){let d=s.createDiv({cls:"mdbase-contract-add"}),f=d.createEl("label",{text:"Installed contract"}),p=d.createEl("select");f.htmlFor=p.id=`mdbase-contract-${Math.random().toString(36).slice(2)}`;for(let h of u)p.createEl("option",{value:ss(h),text:`${h.id} \xB7 ${h.version}`});let m=d.createEl("button",{text:"Connect application contract"});m.disabled=i,m.onclick=()=>{let h=u.find(y=>ss(y)===p.value);if(h)try{nS(n,h),this.markDirty(!0)}catch(y){new ce.Notice(y instanceof Error?y.message:String(y))}}}}renderContractImplementation(t,n,i,s,o){let a=t.createEl("article",{cls:"mdbase-contract-implementation"}),c=a.createDiv({cls:"mdbase-contract-header"}),l=c.createDiv();l.createEl("strong",{text:i.contract}),l.createSpan({cls:"mdbase-contract-version",text:i.version});let u=c.createEl("button",{text:"Remove"});if(u.disabled=o,u.onclick=()=>{iS(n,i.contract,i.version),this.markDirty(!0)},!s){a.createDiv({cls:"mdbase-contract-unavailable",text:"This exact contract is not installed in the collection. Restore it or remove this implementation before saving."});return}let d=Do(s),f=Lo(n),p=d.filter(v=>os(i,v)),m=d.filter(v=>v.required),h=m.filter(v=>os(i,v)).length,y=a.createEl("details");y.open=hW.reference===w),x=jo(v,S),C=_.createDiv({cls:`mdbase-contract-mapping-row ${x.level}`}),$=C.createDiv({cls:"mdbase-contract-field-definition"});$.createEl("code",{text:v.reference}),$.createSpan({cls:v.required?"mdbase-contract-required":"mdbase-contract-optional",text:v.required?"Required":"Optional"}),$.createEl("small",{text:v.description||oS(v.schema)});let M=C.createEl("select");M.setAttr("aria-label",`${i.contract} ${v.reference} source field`),M.setAttr("aria-invalid",x.level==="error"?"true":"false"),M.disabled=o,M.createEl("option",{value:"",text:v.required?"Choose a source field":"Not exposed"});for(let W of f){let q=jo(v,W),oe=M.createEl("option",{value:W.reference,text:`${W.reference} \xB7 ${W.type}${q.level==="warning"?" \xB7 review":""}`});oe.disabled=q.level==="error"}M.value=w,M.onchange=()=>{sS(i,v.reference,M.value||void 0),this.markDirty(!0)};let A=C.createDiv({cls:"mdbase-contract-mapping-status"});A.createEl("strong",{text:x.label}),A.createEl("small",{text:x.message})}if(s.binding_schema){let v=a.createEl("details",{cls:"mdbase-contract-settings"});v.open=!!i.binding;let w=v.createEl("summary");w.createSpan({text:"Contract settings"}),w.createSpan({cls:"mdbase-contract-summary",text:i.binding?"Configured":"Optional"});let S=v.createDiv({cls:"mdbase-contract-settings-body"});if(S.createEl("p",{cls:"mdbase-form-description",text:"Control how compatible applications interpret this type. Values follow the contract's schema."}),i.binding)this.renderContractSchemaValue(S,s.binding_schema,i.binding,x=>{qe(x)&&(rh(i,x),this.markDirty())},"Settings",o);else{let x=S.createEl("button",{text:"Configure settings"});x.disabled=o,x.onclick=()=>{let C=qo(s.binding_schema);qe(C)&&(rh(i,C),this.markDirty(!0))}}}}renderContractSchemaValue(t,n,i,s,o,a){if(xn(n)==="object"){let l=qe(i)?i:{},u=t.createEl("fieldset",{cls:"mdbase-contract-schema-object"});u.createEl("legend",{text:o});let d=qe(n.properties)?n.properties:{},f=new Set(Array.isArray(n.required)?n.required.map(String):[]),p=[...new Set([...f,...Object.keys(l).filter(h=>h in d)])];for(let h of p){let y=d[h];if(!qe(y))continue;let g=u.createDiv({cls:"mdbase-contract-schema-field"}),_=typeof y.description=="string"?y.description:void 0;g.createEl("label",{text:`${h}${f.has(h)?" \xB7 required":""}`}),_&&g.createEl("small",{text:_}),this.renderContractSchemaControl(g,y,l[h],v=>{s({...l,[h]:v})},h,a)}p.length||u.createDiv({cls:"mdbase-empty-list",text:"No settings declared."});let m=Object.keys(d).filter(h=>!p.includes(h));if(m.length){let h=u.createDiv({cls:"mdbase-contract-schema-add"}),y=h.createEl("select");for(let _ of m)y.createEl("option",{value:_,text:_});let g=h.createEl("button",{text:"Add optional setting"});g.disabled=a,g.onclick=()=>{let _=y.value,v=d[_];qe(v)&&s({...l,[_]:qo(v)})}}return}this.renderContractSchemaControl(t,n,i,s,o,a)}renderContractSchemaControl(t,n,i,s,o,a){let c=xn(n);if(Array.isArray(n.enum)){let u=t.createEl("select");u.setAttr("aria-label",o);for(let d of n.enum)u.createEl("option",{value:JSON.stringify(d),text:String(d)});u.value=JSON.stringify(i),u.disabled=a,u.onchange=()=>s(JSON.parse(u.value));return}if(c==="array"){let u=qe(n.items)?n.items:{type:"string"},d=Array.isArray(i)?i:[],f=t.createDiv({cls:"mdbase-contract-schema-array"});for(let[m,h]of d.entries()){let y=f.createDiv({cls:"mdbase-contract-schema-array-item"});y.createSpan({text:`${m+1}.`}),this.renderContractSchemaControl(y,u,h,_=>{s(d.map((v,w)=>w===m?_:v))},`${o} item ${m+1}`,a);let g=y.createEl("button",{text:"Remove"});g.disabled=a||d.length<=(typeof n.minItems=="number"?n.minItems:0),g.onclick=()=>s(d.filter((_,v)=>v!==m))}let p=t.createEl("button",{text:`Add ${o.toLowerCase()} item`});p.disabled=a||typeof n.maxItems=="number"&&d.length>=n.maxItems,p.onclick=()=>s([...d,qo(u)]);return}if(c==="object"){this.renderContractSchemaValue(t,n,i,s,o,a);return}if(c==="boolean"){let u=t.createEl("input",{type:"checkbox"});u.checked=i===!0,u.disabled=a,u.setAttr("aria-label",o),u.onchange=()=>s(u.checked);return}let l=t.createEl("input",{type:c==="number"||c==="integer"?"number":"text"});l.setAttr("aria-label",o),l.value=i==null?"":typeof i=="string"?i:typeof i=="number"||typeof i=="boolean"?String(i):"",l.disabled=a,l.oninput=()=>s(c==="number"||c==="integer"?Number(l.value):l.value)}renderFieldRow(t,n,i,s){this.renderFieldDefinition(t,n.definition,{name:n.name,nameLabel:`Field ${i+1} name`,onNameInput:o=>{n.name=o,this.markDirty()},required:n.definition.required===!0,onRequiredChange:o=>{n.definition.required=o,this.markDirty()},onRemove:()=>{this.model?.fields.splice(i,1),this.markDirty(!0)},readOnly:s,depth:0})}renderFieldDefinition(t,n,i){let s=t.createEl("details",{cls:"mdbase-field-node"});s.setAttr("data-depth",String(i.depth));let o=this.fieldId(n),a=!!this.fieldQuery.trim()&&this.fieldMatches(i.name??i.staticLabel??"",n,this.fieldQuery.trim().toLowerCase());s.open=this.expandedFields.has(o)||a,s.ontoggle=()=>{s.open?this.expandedFields.add(o):this.expandedFields.delete(o)};let c=s.createEl("summary",{cls:"mdbase-field-summary"});c.createSpan({cls:"mdbase-field-summary-name",text:i.name||i.staticLabel||"Unnamed field"}),c.createSpan({cls:"mdbase-field-summary-type",text:vl(n)}),c.createSpan({cls:"mdbase-field-summary-rule",text:i.required?"Required":i.staticLabel?"Item shape":"Optional"});let l=s.createDiv({cls:"mdbase-field-row"});if(i.staticLabel)l.createDiv({cls:"mdbase-field-role",text:i.staticLabel});else{let p=l.createEl("input",{type:"text",cls:"mdbase-field-name-control"});p.setAttr("data-focus-key",`field-${o}-name`),p.setAttr("aria-label",i.nameLabel),p.placeholder="fieldName",p.value=i.name??"",p.disabled=i.readOnly,i.onNameInput&&(p.oninput=()=>i.onNameInput?.(p.value)),i.onNameCommit&&(p.onchange=()=>i.onNameCommit?.(p.value,p))}let u=l.createEl("select",{cls:"mdbase-field-type-control"});u.setAttr("data-focus-key",`field-${o}-type`),u.setAttr("aria-label",`${i.name||i.staticLabel||"Field"} type`);for(let p of kD){let m=p==="any"?"Any value":p[0].toUpperCase()+p.slice(1);u.createEl("option",{value:p,text:m})}u.value=vl(n),u.disabled=i.readOnly,u.onchange=()=>{n.type=u.value,u.value==="list"&&!qe(n.items)&&(n.items={type:"string"}),u.value==="object"&&!qe(n.fields)&&(n.fields={}),u.value==="enum"&&!Array.isArray(n.values)&&(n.values=[]),this.markDirty(!0)};let d=l.createEl("input",{type:"text",cls:"mdbase-field-description-control"});if(d.setAttr("data-focus-key",`field-${o}-description`),d.setAttr("aria-label",`${i.name||i.staticLabel||"Field"} description`),d.placeholder="Description",d.value=typeof n.description=="string"?n.description:"",d.disabled=i.readOnly,d.oninput=()=>{d.value?n.description=d.value:delete n.description,this.markDirty()},i.onRequiredChange){let p=l.createEl("label",{cls:"mdbase-field-required"}),m=p.createEl("input",{type:"checkbox"});m.setAttr("data-focus-key",`field-${o}-required`),m.checked=i.required===!0,m.disabled=i.readOnly,m.onchange=()=>i.onRequiredChange?.(m.checked),p.createSpan({text:"Required"})}if(i.onRemove){let p=l.createEl("button",{cls:"mdbase-field-remove"});p.setAttr("aria-label",`Remove ${i.name||i.staticLabel||"field"}`),(0,ce.setIcon)(p,"trash-2"),p.disabled=i.readOnly,p.onclick=i.onRemove}let f=vl(n);f==="enum"&&this.renderEnumFieldDetails(s,n,i),f==="link"&&this.renderLinkFieldDetails(s,n,i),f==="list"&&this.renderListFieldDetails(s,n,i),f==="object"&&this.renderObjectFieldDetails(s,n,i)}fieldId(t){let n=this.fieldIds.get(t);if(n)return n;let i=`field-${this.nextFieldId}`;return this.nextFieldId+=1,this.fieldIds.set(t,i),i}fieldMatches(t,n,i){return`${t} ${vl(n)} ${typeof n.description=="string"?n.description:""}`.toLowerCase().includes(i)||qe(n.items)&&this.fieldMatches("item",n.items,i)?!0:qe(n.fields)?Object.entries(n.fields).some(([o,a])=>qe(a)&&this.fieldMatches(o,a,i)):!1}renderEnumFieldDetails(t,n,i){let s=t.createDiv({cls:"mdbase-field-details mdbase-field-options"}),o=s.createEl("label",{text:"Allowed values"}),a=s.createEl("input",{type:"text"});a.setAttr("aria-label",`${i.name||i.staticLabel||"Enum"} allowed values`),a.placeholder="draft, published, archived",a.value=Array.isArray(n.values)?n.values.map(String).join(", "):"",a.disabled=i.readOnly,a.oninput=()=>{n.values=a.value.split(",").map(c=>c.trim()).filter(Boolean),this.markDirty()},o.htmlFor=a.id=`mdbase-${Math.random().toString(36).slice(2)}`}renderLinkFieldDetails(t,n,i){let s=t.createDiv({cls:"mdbase-field-details mdbase-field-options"}),o=s.createEl("label",{text:"Target type"}),a=s.createEl("select");a.setAttr("aria-label",`${i.name||i.staticLabel||"Link"} target type`),a.createEl("option",{value:"",text:"Any type"});let c=typeof n.target=="string"?n.target:"";for(let d of this.typeEntries())a.createEl("option",{value:d.name,text:d.name});c&&!this.typeEntries().some(d=>d.name===c)&&a.createEl("option",{value:c,text:`${c} \xB7 missing`}),a.value=c,a.disabled=i.readOnly,a.onchange=()=>{a.value?n.target=a.value:delete n.target,this.markDirty()},o.htmlFor=a.id=`mdbase-${Math.random().toString(36).slice(2)}`;let l=s.createEl("label",{cls:"mdbase-field-required"}),u=l.createEl("input",{type:"checkbox"});u.checked=n.validate_exists===!0,u.disabled=i.readOnly,u.onchange=()=>{n.validate_exists=u.checked,this.markDirty()},l.createSpan({text:"Validate target exists"})}renderListFieldDetails(t,n,i){let s=t.createDiv({cls:"mdbase-field-children"});s.createDiv({cls:"mdbase-field-children-label",text:"List items"});let o=qe(n.items)?n.items:{type:"any"};!qe(n.items)&&!i.readOnly&&(n.items=o),this.renderFieldDefinition(s,o,{staticLabel:"Item",nameLabel:"List item",readOnly:i.readOnly,depth:i.depth+1})}renderObjectFieldDetails(t,n,i){let s=t.createDiv({cls:"mdbase-field-children"}),o=s.createDiv({cls:"mdbase-field-children-header"});o.createDiv({cls:"mdbase-field-children-label",text:"Object fields"});let a=o.createEl("button",{text:"Add nested field"});a.disabled=i.readOnly;let c=qe(n.fields)?n.fields:{};!qe(n.fields)&&!i.readOnly&&(n.fields=c),a.onclick=()=>{let d=PD(c);yS(c,d,{type:"string"}),this.markDirty(!0)};let l=s.createDiv({cls:"mdbase-nested-fields"}),u=Object.entries(c).filter(d=>qe(d[1]));if(!u.length){l.createDiv({cls:"mdbase-empty-list",text:"No nested fields."});return}for(let[d,f]of u){let p=d;this.renderFieldDefinition(l,f,{name:p,nameLabel:`${p} nested field name`,onNameCommit:(m,h)=>{let y=m.trim();if(!y){new ce.Notice("Nested field name is required."),h.value=p;return}if(y!==p&&Object.prototype.hasOwnProperty.call(c,y)){new ce.Notice(`Nested field already exists: ${y}`),h.value=p;return}y!==p&&(delete c[p],yS(c,y,f),p=y,this.markDirty())},required:f.required===!0,onRequiredChange:m=>{f.required=m,this.markDirty()},onRemove:()=>{delete c[p],this.markDirty(!0)},readOnly:i.readOnly,depth:i.depth+1})}}renderYamlEditor(t,n){let i=t.createEl("section",{cls:"mdbase-editor-section mdbase-yaml-section"});i.createEl("h3",{text:"Canonical type document"}),i.createEl("p",{cls:"mdbase-form-description",text:"Unknown v0.3 extensions are preserved. Invalid YAML is never normalized or saved."});let s=i.createEl("textarea",{cls:"mdbase-yaml-editor"});s.setAttr("aria-label","Type definition YAML"),s.setAttr("data-focus-key","yaml-editor"),s.value=this.yamlDraft,s.disabled=n,s.spellcheck=!1,s.oninput=()=>{this.yamlDraft=s.value,this.markDirty(!1)}}renderSync(t){let n=t.createDiv({cls:"mdbase-sync-document"}),i=n.createDiv({cls:"mdbase-document-header"});i.createEl("h2",{text:"Sync"}),i.createEl("p",{text:"A clear handoff between this vault and its hosted collection authority."});let s=this.host.getMirrorProfile();if(!s){this.renderEnrollment(n);return}this.syncProblem=this.host.getCurrentSyncProblem()??this.syncProblem;let o=n.createEl("section",{cls:"mdbase-sync-hero"});o.setAttr("data-state",this.mirrorStatus?.state??"checking");let a=o.createDiv({cls:"mdbase-sync-route"}),c=a.createDiv({cls:"mdbase-sync-endpoint"});(0,ce.setIcon)(c.createSpan({cls:"mdbase-sync-endpoint-icon"}),"vault");let l=c.createDiv();l.createEl("strong",{text:this.app.vault.getName()});let u=s.selectiveSync??{file_classes:[],excluded_folders:[]},d=u.file_classes.length?`Markdown + ${u.file_classes.join(", ")}`:"Markdown only";l.createSpan({text:`${s.mode==="read_write"?"Upload and download":"Downloads only"} \xB7 ${d}`});let f=a.createDiv({cls:"mdbase-sync-connection"});(0,ce.setIcon)(f.createSpan(),s.mode==="read_write"?"arrow-left-right":"arrow-left"),f.createSpan({text:MD(this.mirrorStatus)});let p=a.createDiv({cls:"mdbase-sync-endpoint"});(0,ce.setIcon)(p.createSpan({cls:"mdbase-sync-endpoint-icon"}),"cloud");let m=p.createDiv();m.createEl("strong",{text:s.name}),m.createSpan({text:"Hosted authority"});let h=o.createDiv({cls:"mdbase-sync-meta"}),y=this.mirrorStatus?.pending_files??0;if(h.createSpan({text:`${s.mode==="read_write"?"Read\u2013write mirror":"Read-only mirror"} \xB7 ${dh(this.mirrorStatus?.last_synced_at)}${y?` \xB7 ${y} queued ${y===1?"file":"files"}`:""}`}),h.createEl("code",{text:s.collectionId}).setAttr("title","Collection ID"),this.fileProgress||this.mirrorProgress){let x=o.createDiv({cls:"mdbase-sync-progress",attr:{"aria-live":"polite"}}),C=this.fileProgress?.totalBytes??this.mirrorProgress?.total??null,$=this.fileProgress?.transferredBytes??this.mirrorProgress?.completed??0,M=x.createEl("progress");M.max=C??1,M.value=C==null?0:$,C==null&&M.removeAttribute("value"),x.createDiv({cls:"mdbase-progress-label",text:this.fileProgress?`${this.fileProgress.direction==="upload"?"Uploading":"Downloading"} ${this.fileProgress.path} \xB7 ${At($)} of ${At(C??0)}`:`${this.mirrorProgress?.phase==="uploading"?"Uploading local changes":this.mirrorProgress?.phase==="downloading"?"Downloading collection files":"Applying changes"} \xB7 ${$}${C==null?"":` of ${C}`}`});let A=x.createEl("button",{text:"Stop safely"});A.onclick=()=>{this.host.connectSync.cancelSync(),this.transientMessage="Stopping after the current network request\u2026",this.render()}}(this.syncProblem||this.mirrorStatus?.recovery_required)&&this.renderRecoveryCard(o,this.syncProblem??{code:"mirror_recovery_required",title:"Synchronization needs recovery",message:"Your original files are safe. Resume from the durable checkpoint before disconnecting this vault.",action:"resume",actionLabel:"Resume recovery"});let _=o.createDiv({cls:"mdbase-sync-actions"}),v=_.createEl("button",{text:this.mirrorPreview?"Refresh review":"Review changes"});v.disabled=this.busy,v.onclick=()=>void this.reviewSyncChanges();let w=ah(this.mirrorPreview?.plan??null,this.mirrorPreview?.entries.length??0,this.busy),S=_.createEl("button",{text:w.actionLabel});S.addClass("mod-cta"),S.disabled=w.actionDisabled,S.onclick=()=>void this.perform(()=>this.applyReviewedSync()),this.renderFilePolicyControls(n,{connected:!0}),this.mirrorPreview&&this.renderMirrorPreview(n,this.mirrorPreview),this.mirrorStatus?.conflicts.length&&this.renderConflicts(n,this.mirrorStatus),this.mirrorStatus?.local_issues.length&&this.renderLocalMirrorIssues(n,this.mirrorStatus),this.renderActivity(n),this.renderConnectionDetails(n,s),window.setTimeout(()=>this.focusPendingSyncSection(),0)}async loadMirrorPreview(){this.mirrorPreview=await this.host.connectSync.preview(),this.mirrorStatus=await this.host.connectSync.status(),this.syncProblem=null,this.host.setSyncStatus(this.mirrorStatus),this.transientMessage=ah(this.mirrorPreview.plan,this.mirrorPreview.entries.length).message}async applyReviewedSync(){if(!this.mirrorPreview){await this.loadMirrorPreview();return}let t=this.mirrorPreview;try{let n=await this.host.connectSync.sync(t,i=>{this.mirrorProgress=i,this.host.setSyncProgress(i,this.fileProgress),this.render()},i=>{this.fileProgress=i,this.host.setSyncProgress(this.mirrorProgress,i),this.render()});this.mirrorStatus=await this.host.connectSync.status(),this.mirrorPreview=await this.host.connectSync.preview(),this.syncProblem=n.status==="cancelled"?nr(new DOMException("Synchronization stopped.","AbortError")):n.status==="stale"?nr(Object.assign(new Error("The reviewed plan changed."),{code:"mirror_plan_stale"})):null,this.syncProblem?this.host.setSyncProblem(this.syncProblem):this.host.setSyncStatus(this.mirrorStatus,{clearLocalChanges:n.status==="applied"&&n.pending===0}),this.transientMessage=n.status==="applied"?"Sync completed and the local checkpoint was verified.":n.status==="attention"?"Completed changes are checkpointed. Review the items that still need attention.":n.status==="cancelled"?`Sync paused safely after ${n.applied} actions; ${n.pending} remain.`:n.status==="stale"?"The collection changed again. Review the newest plan; no stale decision was applied.":`Sync stopped at a durable boundary: ${n.failure?.message??n.status}.`,await this.host.recordSyncActivity({summary:n.status==="applied"?`Synchronized ${n.applied} ${n.applied===1?"change":"changes"}`:n.status==="cancelled"?"Synchronization paused safely":"Synchronization needs attention",detail:this.transientMessage,tone:n.status==="applied"?"success":"attention",requiresAcknowledgement:n.status!=="applied"})}catch(n){let i=nr(n);if(this.syncProblem=i,this.host.setSyncProblem(i),this.transientMessage=i.message,await this.host.recordSyncActivity({summary:i.title,detail:i.message,tone:i.action==="resume"?"attention":"error",requiresAcknowledgement:!0}),!uh(n))throw n}finally{this.mirrorProgress=null,this.fileProgress=null,this.host.setSyncProgress(null,null)}}renderRecoveryCard(t,n){let i=t.createDiv({cls:"mdbase-recovery-card"}),s=i.createDiv();s.createEl("strong",{text:n.title}),s.createDiv({text:n.message});let o=i.createEl("button",{text:n.actionLabel});o.disabled=this.busy,o.onclick=()=>{n.action==="retry"?this.reconnectCollection():n.action==="reauthorize"?this.perform(()=>this.reauthorizeCollection()):this.reviewSyncChanges()}}async reauthorizeCollection(){this.enrollmentAbort?.abort();let t=new AbortController;this.enrollmentAbort=t;try{this.mirrorStatus=await this.host.connectSync.reauthorize({signal:t.signal,onVerification:n=>{this.enrollmentVerification=n.verificationUri,this.transientMessage="Approve this vault again in Connect. Its local files and checkpoint remain unchanged.",window.open(n.verificationUri,"_blank","noopener,noreferrer"),this.render()},onStatus:n=>{this.transientMessage=n.state==="waiting_for_approval"?"Waiting for approval in Connect\u2026":`Connect is retrying approval (attempt ${n.attempt}).`,this.render()}}),this.enrollmentVerification="",this.syncProblem=null,this.host.setSyncStatus(this.mirrorStatus),this.transientMessage="Approval restored. The existing mirror checkpoint was preserved.",await this.host.recordSyncActivity({summary:"Connect approval restored",detail:"The existing mirror checkpoint and local files were preserved.",tone:"success",requiresAcknowledgement:!1})}finally{this.enrollmentAbort===t&&(this.enrollmentAbort=null)}}renderActivity(t){let n=t.createEl("section",{cls:"mdbase-editor-section mdbase-activity"});n.id="mdbase-sync-activity";let i=n.createDiv({cls:"mdbase-section-header"});i.createEl("h3",{text:"Recent activity"});let s=this.host.getSyncActivity();if(s.some(a=>!a.requiresAcknowledgement)){let a=i.createEl("button",{text:"Clear completed"});a.disabled=this.busy,a.onclick=()=>void this.host.clearCompletedSyncActivity().then(()=>this.render())}if(this.fileProgress){let a=n.createDiv({cls:"mdbase-activity-row is-current"});(0,ce.setIcon)(a.createSpan(),this.fileProgress.direction==="upload"?"upload":"download");let c=a.createDiv();c.createEl("strong",{text:`${this.fileProgress.direction==="upload"?"Uploading":"Downloading"} ${this.fileProgress.path}`}),c.createDiv({text:`${At(this.fileProgress.transferredBytes)} of ${At(this.fileProgress.totalBytes)}`})}let o=[...s].reverse().filter((a,c)=>a.requiresAcknowledgement||c<8);if(!o.length&&!this.fileProgress){n.createDiv({cls:"mdbase-muted",text:"No recent synchronization activity."});return}for(let a of o){let c=n.createDiv({cls:"mdbase-activity-row"});c.setAttr("data-tone",a.tone),(0,ce.setIcon)(c.createSpan(),a.tone==="success"?"check":a.tone==="info"?"info":"circle-alert");let l=c.createDiv();if(l.createEl("strong",{text:a.summary}),a.detail&&l.createDiv({text:a.detail}),l.createSpan({cls:"mdbase-muted",text:dh(a.occurredAt)}),a.requiresAcknowledgement){let u=c.createEl("button",{text:"Dismiss"});u.disabled=this.busy,u.onclick=()=>void this.host.dismissSyncActivity(a.id).then(()=>this.render())}}}renderConnectionDetails(t,n){let i=t.createEl("section",{cls:"mdbase-editor-section mdbase-connection-details"});i.createEl("h3",{text:"Collection connection"});let s=i.createDiv({cls:"mdbase-status-list"});Fe(s,"Collection",n.name),Fe(s,"Collection ID",n.collectionId),Fe(s,"Vault",this.app.vault.getName()),Fe(s,"Access",n.mode==="read_write"?"Read and write":"Read only"),Fe(s,"Account","Approved through Connect"),Fe(s,"Connect",new URL(n.controlUrl).host),Fe(s,"Last successful sync",dh(this.mirrorStatus?.last_synced_at)),i.createEl("p",{text:"This plugin is the only sync owner for this vault. Connect owns authorization and the mirror engine owns checkpoints and conflict decisions."});let o=i.createDiv({cls:"mdbase-actions"}),a=o.createEl("button",{text:"Reconnect"});a.disabled=this.busy,a.onclick=()=>void this.reconnectCollection();let c=o.createEl("button",{text:"Disconnect\u2026"});c.disabled=this.busy||this.host.connectSync.isSyncing(),c.onclick=()=>void this.disconnectCollection(n)}async disconnectCollection(t){let n=await new hh(this.app).choose(t.name);n&&await this.perform(async()=>{let i=await this.host.connectSync.disconnect(n==="remove");this.mirrorStatus=null,this.mirrorPreview=null,this.syncProblem=null,this.host.setSyncStatus(null,{clearLocalChanges:!0});let s=n==="remove"?`${i.removed.length} unchanged synced ${i.removed.length===1?"file was":"files were"} removed. ${i.preserved.length} locally changed ${i.preserved.length===1?"file was":"files were"} preserved.`:"All local files were retained as an unsynced copy.";this.transientMessage=`Disconnected from ${t.name}. ${s}`,await this.host.recordSyncActivity({summary:`Disconnected from ${t.name}`,detail:s,tone:i.preserved.length?"attention":"info",requiresAcknowledgement:i.preserved.length>0}),await this.refresh(!0)})}focusPendingSyncSection(){if(!this.pendingSyncFocus)return;let t=this.pendingSyncFocus==="activity"?"mdbase-sync-activity":"mdbase-sync-conflicts",n=this.containerEl.querySelector(`#${t}`);n&&(this.pendingSyncFocus=null,n.scrollIntoView({behavior:"smooth",block:"start"}))}filePolicy(){return this.filePolicyDraft??=JSON.parse(JSON.stringify(this.host.connectSync.getSelectiveSync())),this.filePolicyDraft}renderFilePolicyControls(t,n){let i=this.filePolicy(),s=t.createEl("section",{cls:"mdbase-editor-section mdbase-file-policy"});s.createEl("h3",{text:n.connected?"Files on this device":"Collection files"}),s.createEl("p",{text:"Markdown always syncs. Choose which binary file classes this device should materialize; hidden and reserved paths remain excluded."});let o=s.createDiv({cls:"mdbase-file-class-grid"}),a=[["image","Images"],["audio","Audio"],["video","Video"],["pdf","PDFs"],["other","Other files"]];for(let[f,p]of a){let m=o.createEl("label"),h=m.createEl("input",{type:"checkbox"});h.setAttr("data-focus-key",`file-class-${f}`),h.checked=i.file_classes.includes(f),h.onchange=()=>{i.file_classes=h.checked?[...new Set([...i.file_classes,f])]:i.file_classes.filter(y=>y!==f),this.render()},m.createSpan({text:p})}let c=null;if(Lt(s,"Excluded folders",i.excluded_folders.join(", "),f=>{if(i.excluded_folders=f.split(",").map(p=>p.trim()).filter(Boolean),c){let p=JSON.stringify(this.host.connectSync.getSelectiveSync())!==JSON.stringify(i);c.textContent=p?"Apply file policy":"File policy applied",c.disabled=!p||this.busy}},{description:"Comma-separated collection-relative folders. Exclusions apply to Markdown and binary files on this device.",placeholder:"Archive, Private exports"}),i.file_classes.length?i.file_classes.includes("other")&&s.createDiv({cls:"mdbase-inline-message",text:"Other files includes every eligible visible non-Markdown format. Review the transfer ledger carefully before syncing."}):s.createDiv({cls:"mdbase-inline-message",text:"Binary sync is off. This mirror remains Markdown-only."}),!n.connected)return;let l=this.host.connectSync.getSelectiveSync(),u=JSON.stringify(l)!==JSON.stringify(i);c=s.createDiv({cls:"mdbase-actions"}).createEl("button",{text:u?"Apply file policy":"File policy applied"}),c.disabled=!u||this.busy,c.onclick=()=>void this.perform(async()=>{await this.host.connectSync.configureSelectiveSync(i),this.filePolicyDraft=null,this.mirrorPreview=null,this.transientMessage="File policy updated. Review the rebuild before files move.",this.render()})}renderEnrollment(t){if(this.schema||this.host.connectSync.getAdoptionMarker()){this.renderLocalAdoption(t);return}let n=t.createEl("section",{cls:"mdbase-editor-section mdbase-enrollment"});if(n.createEl("h3",{text:"Connect an empty vault"}),n.createEl("p",{text:"Choose access, approve it in Connect, then review the first transfer before any files move."}),this.renderEnrollmentSteps(n,this.enrollmentVerification||this.enrollmentAbort?2:1,["Choose connection","Approve in Connect","Review first sync"]),this.enrollmentVerification){let c=n.createDiv({cls:"mdbase-approval-link"});c.createSpan({text:"Waiting for approval \xB7 "});let l=c.createEl("a",{text:"Open approval page again",href:this.enrollmentVerification});l.setAttr("target","_blank"),l.setAttr("rel","noopener noreferrer")}Lt(n,"Connect URL",this.enrollmentControlUrl,c=>{this.enrollmentControlUrl=c},{placeholder:lh}),Lt(n,"Mirror name",this.enrollmentMirrorName,c=>{this.enrollmentMirrorName=c}),Lt(n,"Collection ID",this.enrollmentCollectionId,c=>{this.enrollmentCollectionId=c},{description:"Optional. Leave blank to choose during approval."});let i=n.createDiv({cls:"mdbase-form-row"});i.createEl("label",{text:"Access"});let s=i.createEl("select");s.createEl("option",{value:"read_write",text:"Read and write"}),s.createEl("option",{value:"read_only",text:"Read only"}),s.value=this.enrollmentMode,s.onchange=()=>{this.enrollmentMode=s.value==="read_only"?"read_only":"read_write"},this.renderFilePolicyControls(n,{connected:!1});let o=n.createDiv({cls:"mdbase-actions"}),a=o.createEl("button",{text:this.enrollmentAbort?"Waiting for approval\u2026":"Continue to approval"});if(a.addClass("mod-cta"),a.disabled=this.busy,a.onclick=()=>void this.perform(async()=>{this.enrollmentAbort?.abort();let c=new AbortController;this.enrollmentAbort=c;try{await this.host.connectSync.enroll({controlUrl:this.enrollmentControlUrl,mirrorName:this.enrollmentMirrorName,mode:this.enrollmentMode,selectiveSync:this.filePolicy(),...this.enrollmentCollectionId.trim()?{collectionId:this.enrollmentCollectionId.trim()}:{}},{signal:c.signal,onVerification:d=>{this.enrollmentVerification=d.verificationUri,this.transientMessage="Approve the mirror in the Connect page. This view will keep waiting securely.",window.open(d.verificationUri,"_blank","noopener,noreferrer"),this.render()},onStatus:d=>{this.transientMessage=d.state==="waiting_for_approval"?"Waiting for approval in Connect\u2026":`Connect is retrying enrollment (attempt ${d.attempt}).`,this.render()}}),this.enrollmentVerification="",await this.loadMirrorPreview();let l=this.mirrorPreview?.entries.reduce((d,f)=>d+(f.estimatedBytes??0),0)??0,u=this.mirrorPreview?.entries.length??0;this.transientMessage=`Mirror enrolled. The first review contains ${u} ${u===1?"item":"items"}${l?` and about ${At(l)} of binary data`:""}. No files have moved yet.`,this.render()}catch(l){if(!uh(l))throw l;this.transientMessage="Approval wait cancelled. No files were synchronized."}finally{this.enrollmentAbort===c&&(this.enrollmentAbort=null)}}),this.enrollmentAbort){let c=o.createEl("button",{text:"Stop waiting"});c.onclick=()=>{this.enrollmentAbort?.abort(),this.transientMessage="Approval wait cancelled. No files were synchronized.",this.render()}}}renderLocalAdoption(t){let n=this.host.connectSync.getAdoptionMarker(),i=t.createEl("section",{cls:"mdbase-editor-section mdbase-enrollment"});i.createEl("h3",{text:"Host this local collection"}),i.createEl("p",{text:n?"This vault has a durable adoption checkpoint. Resume it without creating another hosted collection.":"Hosted mdbase will adopt an exact snapshot and become the collection authority. This vault will then continue as a read-write mirror."});let s=n?["waiting_for_approval","uploading","fenced","activating","adopted"].indexOf(n.phase)+1:1;this.renderEnrollmentSteps(i,Math.min(4,Math.max(1,s)),["Approve move","Stage snapshot","Activate authority","Reconnect mirror"]);let o=this.enrollmentVerification||n?.session.verificationUri;if(o){let l=i.createDiv({cls:"mdbase-approval-link"});l.createSpan({text:"Approval page: "});let u=l.createEl("a",{text:"Open Connect",href:o});u.setAttr("target","_blank"),u.setAttr("rel","noopener noreferrer")}if(n&&(this.enrollmentControlUrl=n.session.controlUrl,this.enrollmentMirrorName=n.session.requested.mirrorName??"Obsidian"),!n)Lt(i,"Connect URL",this.enrollmentControlUrl,l=>{this.enrollmentControlUrl=l},{placeholder:lh}),Lt(i,"Mirror name",this.enrollmentMirrorName,l=>{this.enrollmentMirrorName=l}),this.renderFilePolicyControls(i,{connected:!1});else{let l=i.createDiv({cls:"mdbase-status-list"});Fe(l,"Collection",n.session.requested.collectionId),Fe(l,"Phase",n.phase.replace(/_/g," ")),Fe(l,"Connect",n.session.controlUrl);let u=this.host.connectSync.getSelectiveSync();Fe(l,"Files",u.file_classes.length?u.file_classes.join(", "):"Markdown only")}let a=i.createDiv({cls:"mdbase-inline-message"});a.createEl("strong",{text:"Authority cut-over: "}),a.appendText("once final staging begins, plugin-managed local edits pause until hosted activation is confirmed. The checkpoint survives app restarts and uncertain network responses.");let c=i.createEl("button",{text:n?"Resume adoption":"Approve and host collection"});if(c.addClass("mod-cta"),c.disabled=this.busy,c.onclick=()=>void this.perform(async()=>{this.enrollmentAbort?.abort();let l=new AbortController;this.enrollmentAbort=l;let u=p=>{this.enrollmentVerification=p.verificationUri,this.transientMessage="Approve the authority move in Connect, then return here. This checkpoint is safe to resume.",this.render()},d=p=>{this.transientMessage=p.state==="waiting_for_approval"?"Waiting for authority-move approval in Connect\u2026":`Connect is retrying (attempt ${p.attempt}).`,this.render()},f=(p,m,h)=>{this.adoptionFileProgress=`${p} \xB7 ${At(m)} of ${At(h)}`,this.transientMessage=`Uploading collection file ${this.adoptionFileProgress}`,this.render()};try{n?await this.host.connectSync.resumeAdoption({signal:l.signal,onVerification:u,onStatus:d,onFileProgress:f}):await this.host.connectSync.adoptLocalCollection({controlUrl:this.enrollmentControlUrl,mirrorName:this.enrollmentMirrorName,selectiveSync:this.filePolicy()},{signal:l.signal,onVerification:u,onStatus:d,onFileProgress:f}),this.enrollmentVerification="",this.transientMessage="Hosted mdbase is authoritative and this vault is now its read-write mirror.",await this.refresh(!0)}catch(p){if(!uh(p))throw p;this.transientMessage="Paused safely. Use Resume adoption to continue from the durable checkpoint."}finally{this.enrollmentAbort===l&&(this.enrollmentAbort=null)}}),this.enrollmentAbort){let l=i.createEl("button",{text:"Stop waiting"});l.onclick=()=>{this.enrollmentAbort?.abort(),this.transientMessage="Paused safely. Use Resume adoption to continue from the durable checkpoint.",this.render()}}if(n&&!["activating","adopted"].includes(n.phase)){let l=i.createEl("button",{text:"Cancel adoption"});l.disabled=this.busy,l.onclick=()=>void this.perform(async()=>{await this.host.connectSync.cancelAdoption(),this.enrollmentVerification="",this.transientMessage="Collection adoption cancelled. This vault remains the local authority.",await this.refresh(!0)})}}renderEnrollmentSteps(t,n,i){let s=t.createEl("ol",{cls:"mdbase-enrollment-steps"});i.forEach((o,a)=>{let c=s.createEl("li"),l=a+1;c.toggleClass("is-complete",ll+(u.estimatedBytes??0),0);s.createSpan({cls:"mdbase-transfer-total",text:`${n.entries.length} ${n.entries.length===1?"item":"items"} \xB7 ${n.entries.filter(l=>l.kind==="file").length} files${a?` \xB7 about ${At(a)}`:""}`});let c=[{direction:"download",title:"Download to this vault",empty:"No hosted changes to download."},{direction:"upload",title:"Upload to hosted",empty:"No local changes to upload."},{direction:"attention",title:"Needs attention",empty:"Nothing is blocking or excluded."}];for(let l of c){let u=n.entries.filter(h=>h.direction===l.direction);if(!u.length&&l.direction!=="attention")continue;let d=i.createEl("section",{cls:"mdbase-transfer-group"});d.setAttr("data-direction",l.direction);let f=d.createDiv({cls:"mdbase-transfer-group-heading"}),p=f.createSpan({cls:"mdbase-transfer-group-icon"});if((0,ce.setIcon)(p,l.direction==="download"?"download":l.direction==="upload"?"upload":"circle-alert"),f.createEl("h4",{text:l.title}),f.createSpan({text:String(u.length),cls:"mdbase-transfer-count"}),!u.length){d.createDiv({cls:"mdbase-transfer-empty",text:l.empty});continue}let m=d.createDiv({cls:"mdbase-transfer-ledger"});for(let h of u.slice(0,250)){let y=m.createDiv({cls:"mdbase-transfer-row"});y.createSpan({cls:"mdbase-transfer-action",text:h.action}).setAttr("data-action",h.action);let _=y.createDiv({cls:"mdbase-transfer-body"}),v=_.createDiv({cls:"mdbase-transfer-path"});if(v.createEl("code",{text:h.path}),v.createSpan({cls:"mdbase-transfer-kind",text:h.kind==="file"?"File":"Markdown"}),h.estimatedBytes!==void 0&&v.createSpan({cls:"mdbase-transfer-size",text:At(h.estimatedBytes)}),_.createDiv({text:h.detail}),h.direction==="attention"){let w=y.createEl("button",{text:"Open"});w.onclick=()=>void this.host.openFileByPath(h.path)}}u.length>250&&m.createDiv({cls:"mdbase-transfer-more",text:`${u.length-250} more items are included in this transfer.`})}n.collisions.length?i.createDiv({cls:"mdbase-inline-error",text:"Resolve path collisions before the first sync. Existing local files are never overwritten without review."}):n.local_issues.length&&i.createDiv({cls:"mdbase-inline-message",text:n.plan.summary.blocking_issues>0?"Synchronization is paused. Resolve the blocking issues, then refresh the review.":"These diagnostics do not block synchronization. Document bytes are preserved unchanged."})}renderConflicts(t,n){let i=t.createEl("section",{cls:"mdbase-editor-section"});i.id="mdbase-sync-conflicts",i.createEl("h3",{text:"Conflicts"}),i.createEl("p",{text:"Inspect both versions before deciding. Decisions are checked again immediately before they are applied."});for(let s of n.conflicts){let o=i.createDiv({cls:"mdbase-conflict-row"}),a=o.createDiv({cls:"mdbase-conflict-summary"});a.createEl("strong",{text:s.path??s.object_id}),a.createDiv({text:s.entity==="file"?"Binary file conflict":"Note conflict",cls:"mdbase-muted"}),a.createDiv({text:s.message});let c=o.createDiv({cls:"mdbase-actions"}),l=`${s.object_id}:${s.decision_id}`,u=this.conflictComparisons.get(l),d=c.createEl("button",{text:u?"Hide versions":this.loadingConflictComparisons.has(l)?"Loading versions\u2026":"Compare versions"});d.disabled=this.busy||this.loadingConflictComparisons.has(l),d.onclick=()=>{if(u){this.conflictComparisons.delete(l),this.render();return}this.loadConflictComparison(s,l)};for(let f of["local","remote"]){let p=c.createEl("button",{text:f==="local"?"Keep local":"Use hosted"});p.disabled=this.busy,p.onclick=()=>void this.resolveMirrorConflict(s,f,!1)}if(s.path){let f=c.createEl("button",{text:"Keep both"});f.disabled=this.busy,f.onclick=()=>void this.resolveMirrorConflict(s,"remote",!0)}u&&this.renderConflictComparison(o,u)}}async loadConflictComparison(t,n){this.loadingConflictComparisons.add(n),this.render();try{this.conflictComparisons.set(n,await this.host.connectSync.conflictComparison(t))}catch(i){let s=nr(i);this.syncProblem=s,this.host.setSyncProblem(s),this.transientMessage=s.message,s.code==="conflict_decision_stale"&&await this.refreshMirrorStatus()}finally{this.loadingConflictComparisons.delete(n),this.render()}}renderConflictComparison(t,n){let i=t.createDiv({cls:"mdbase-conflict-comparison"});if(n.entity==="record"){let o=fS(n.local.document??"",n.remote.document??""),a=i.createDiv({cls:"mdbase-conflict-legend"});a.createSpan({text:"\u2212 Local only",cls:"is-local"}),a.createSpan({text:"+ Hosted only",cls:"is-remote"});let c=i.createEl("pre",{cls:"mdbase-conflict-diff"});for(let l of o.lines){let u=c.createEl("div",{cls:`is-${l.kind}`});u.createSpan({text:l.kind==="local"?"\u2212":l.kind==="remote"?"+":" "}),u.createSpan({text:l.value||" "})}o.truncated&&i.createDiv({cls:"mdbase-muted",text:"Diff shortened for a responsive review. Open the local note to inspect it in full."});return}let s=i.createDiv({cls:"mdbase-conflict-sides"});this.renderConflictSide(s,"Local",n.local),this.renderConflictSide(s,"Hosted",n.remote)}renderConflictSide(t,n,i){let s=t.createDiv({cls:"mdbase-conflict-side"});if(s.createEl("h4",{text:n}),i.state==="absent"){s.createDiv({cls:"mdbase-muted",text:"File is absent in this version."});return}if(i.path&&s.createEl("code",{text:i.path}),i.size!==void 0&&s.createDiv({text:`Size: ${At(i.size)}`}),i.modifiedAt&&s.createDiv({text:`Modified: ${new Date(i.modifiedAt).toLocaleString()}`}),i.revision&&s.createDiv({cls:"mdbase-conflict-digest",text:`Digest: ${i.revision}`}),i.resourceUrl&&i.path&&/\.(?:avif|gif|jpe?g|png|svg|webp)$/i.test(i.path)){let o=s.createEl("img",{cls:"mdbase-conflict-image-preview"});o.src=i.resourceUrl,o.alt=`${n} preview of ${i.path}`}else n==="Hosted"&&s.createDiv({cls:"mdbase-muted",text:"Hosted binary preview is materialized only after you choose it."})}async resolveMirrorConflict(t,n,i){await this.perform(async()=>{let s=null;try{i&&(s=await this.host.connectSync.preserveConflictCopy(t.path??"")),this.mirrorPreview=null;let o=await uS(this.host.connectSync,t.object_id,t.decision_id,n);this.mirrorStatus=o.status,this.mirrorPreview=o.preview,this.host.setSyncStatus(o.status),this.conflictComparisons.delete(`${t.object_id}:${t.decision_id}`),this.transientMessage=s?`Both versions are safe: the hosted version will use the original path and the local version was copied to ${s}. Review the refreshed plan before syncing.`:"Conflict resolved. Review the refreshed engine plan before syncing.",await this.host.recordSyncActivity({summary:s?"Conflict kept as two files":`Conflict resolved with ${n==="local"?"local":"hosted"} version`,detail:this.transientMessage,path:t.path??void 0,tone:"info",requiresAcknowledgement:!1})}catch(o){let a=nr(o);s&&(a.message=`The local copy at ${s} is safe, but the original changed again. Review the newest versions before deciding.`),this.syncProblem=a,this.host.setSyncProblem(a),this.transientMessage=a.message,a.code==="conflict_decision_stale"&&(this.mirrorPreview=null,await this.refreshMirrorStatus()),await this.host.recordSyncActivity({summary:a.title,detail:a.message,path:t.path??void 0,tone:"attention",requiresAcknowledgement:!0})}})}renderLocalMirrorIssues(t,n){let i=t.createEl("section",{cls:"mdbase-editor-section"});i.createEl("h3",{text:"Local files needing attention"}),i.createEl("p",{text:"Review these file diagnostics. The sync preview identifies which issues block synchronization; a diagnostic alone does not mean syncing is paused."});for(let s of n.local_issues){let o=i.createDiv({cls:"mdbase-conflict-row"}),a=o.createDiv();a.createEl("strong",{text:s.path}),a.createDiv({text:s.message});let l=o.createDiv({cls:"mdbase-actions"}).createEl("button",{text:"Open file"});l.disabled=this.busy,l.onclick=()=>void this.host.openFileByPath(s.path)}}renderIssues(t){let n=t.createDiv({cls:"mdbase-issues-document"}),i=this.host.getIssues(),s=new Set(i.map(g=>g.path)).size,o=n.createDiv({cls:"mdbase-document-header"}),a=o.createDiv();a.createEl("h2",{text:"Issues"}),a.createEl("p",{text:i.length?`${i.length.toLocaleString()} validation issues in ${s.toLocaleString()} files.`:"The collection has no current validation issues."});let c=o.createEl("button",{text:"Validate collection"});c.disabled=this.busy,c.onclick=()=>void this.perform(async()=>{await this.host.validateCollection(),this.render()});let l=n.createDiv({cls:"mdbase-issue-controls"}),u=l.createEl("select");u.setAttr("aria-label","Issue severity"),u.createEl("option",{value:"all",text:"All severities"}),u.createEl("option",{value:"error",text:"Errors"}),u.createEl("option",{value:"warn",text:"Warnings"}),u.value=this.issueSeverity,u.onchange=()=>{this.issueSeverity=u.value==="error"||u.value==="warn"?u.value:"all",this.issueLimit=250,this.render()};let d=l.createEl("input",{type:"search"});d.setAttr("aria-label","Filter issues"),d.setAttr("data-focus-key","issue-search"),d.placeholder="Filter by path, code, field, or message",d.value=this.issueQuery,d.oninput=()=>{this.issueQuery=d.value,this.issueLimit=250,this.render();let g=this.containerEl.querySelector(".mdbase-issue-controls input[type='search']");g?.focus(),g?.setSelectionRange(g.value.length,g.value.length)};let f=this.issueQuery.trim().toLowerCase(),p=i.filter(g=>this.issueSeverity!=="all"&&g.severity!==this.issueSeverity?!1:f?`${g.path} ${g.code} ${g.field??""} ${g.message}`.toLowerCase().includes(f):!0),m=new Set(p.map(g=>g.path)).size;if(n.createDiv({cls:"mdbase-issues-summary",text:p.length===i.length?`Showing ${Math.min(p.length,this.issueLimit).toLocaleString()} of ${p.length.toLocaleString()} issues`:`${p.length.toLocaleString()} matching issues in ${m.toLocaleString()} files`}),!p.length){n.createDiv({cls:"mdbase-empty-state",text:"No validation issues."});return}let h=p.slice(0,this.issueLimit),y=new Map;for(let g of h)y.set(g.path,[...y.get(g.path)??[],g]);for(let[g,_]of y){let v=n.createEl("section",{cls:"mdbase-issue-group"}),w=v.createDiv({cls:"mdbase-issue-group-header"}),S=w.createEl("button",{cls:"mdbase-issue-file-button"});(0,ce.setIcon)(S.createSpan({cls:"mdbase-issue-file-icon"}),"file-text"),S.createSpan({cls:"mdbase-issue-file-path",text:g}),S.onclick=()=>void this.host.openFileByPath(g),w.createSpan({cls:"mdbase-issue-file-count",text:`${_.length} ${_.length===1?"issue":"issues"}`});for(let x of _){let C=v.createDiv({cls:"mdbase-issue-row"});C.setAttr("data-severity",x.severity),C.createSpan({cls:"mdbase-issue-indicator"}).setAttr("aria-hidden","true");let $=C.createDiv({cls:"mdbase-issue-metadata"});$.createEl("code",{text:x.code}),$.createDiv({cls:"mdbase-issue-context",text:`${x.severity==="warn"?"Warning":"Error"}${x.field?` \xB7 ${x.field}`:""}`}),C.createDiv({cls:"mdbase-issue-row-message",text:x.message});let M=C.createDiv({cls:"mdbase-issue-row-actions"}),A=M.createEl("button",{text:x.field?"Open field":"Open file"});A.setAttr("aria-label",`Open ${x.path}${x.field?` at ${x.field}`:""}`),A.onclick=()=>void this.host.openFileByPath(x.path,x.field);let W=this.host.getQuickFixLabel(x);if(W){let q=M.createEl("button",{text:W});q.addClass("mod-cta"),q.onclick=()=>void this.perform(async()=>{await this.host.applyQuickFix(x)})}}}if(p.length>h.length){let g=n.createEl("button",{cls:"mdbase-load-more",text:`Load ${Math.min(250,p.length-h.length)} more`});g.onclick=()=>{this.issueLimit+=250,this.render()}}}async selectType(t,n=!0){if(n&&this.dirty&&t!==this.selectedPath){new ce.Notice("Save or discard the current type changes before switching.");return}let i=++this.typeSelectionVersion,s=await this.host.loadTypeModel(t);if(i!==this.typeSelectionVersion)return;let o=this.host.loadTypeDraft(t),a=o?.version===1&&o.sourceRevision===(s.sourceRevision??null),c=a?oi(o.model):s;this.selectedPath=t,this.model=c,this.originalModel=oi(s),this.yamlDraft=a&&o.yamlDraft?o.yamlDraft:`${Vt(fh(c),c.body)} `,this.dirty=!yl(this.originalModel,c),this.editorMode=a&&o.editorMode==="yaml"?"yaml":"design",this.editorMode==="yaml"&&(this.dirty=!0),a&&this.dirty?this.transientMessage=`Recovered unsaved changes for ${t}.`:o&&!a&&(this.transientMessage=`An older draft for ${t} was kept, but the source changed. The current file is shown.`),this.render()}createType(){if(this.dirty){new ce.Notice("Save or discard the current type changes before creating another type.");return}this.typeSelectionVersion+=1;let t=this.host.loadTypeDraft(null),n=t?.version===1?oi(t.model):th();this.selectedPath=null,this.model=n,this.originalModel=null,this.yamlDraft=t?.yamlDraft??"",this.dirty=!0,t&&(this.transientMessage="Recovered an unsaved new type."),this.editorMode=t?.editorMode??"design",this.render()}switchEditorMode(t){if(!(!this.model||t===this.editorMode)){if(t==="yaml")try{this.yamlDraft=`${Vt(fh(this.model),this.model.body)} `}catch(n){new ce.Notice(n instanceof Error?n.message:String(n));return}else if(!this.readYamlDraftIntoModel())return;this.editorMode=t,this.render()}}readYamlDraftIntoModel(){let t=et(this.yamlDraft);if(!t.hasFrontmatter||t.error)return new ce.Notice(`Invalid type YAML: ${t.error??"frontmatter is missing"}`),!1;if(t.frontmatter.kind!=="mdbase.type")return new ce.Notice("Canonical v0.3 type YAML requires kind: mdbase.type."),!1;try{return this.model=ml(t.frontmatter,t.body,this.model?.name||"type"),!0}catch(n){return new ce.Notice(n instanceof Error?n.message:String(n)),!1}}async saveCurrentType(){if(!this.model||this.model.specProfile!=="v0.3"||this.model.readOnlyReason||this.editorMode==="yaml"&&!this.readYamlDraftIntoModel())return;let n=gl(this.model,{knownTypes:this.typeEntries().map(o=>o.name),contracts:this.schema?.contracts.values()}).filter(o=>o.severity==="error");if(n.length){this.transientMessage=`${n.length} ${n.length===1?"error must":"errors must"} be fixed before saving.`,this.render(),new ce.Notice(n[0].message);return}let i=bl(this.originalModel,this.model).filter(o=>o.risk==="high");if(i.length&&!await new ph(this.app).confirm(i))return;let s=this.model;await this.perform(async()=>{let o=this.selectedPath,a=await this.host.saveTypeModel(s,o,this.originalModel?.sourceRevision);await this.host.clearTypeDraft(o),o!==a.path&&await this.host.clearTypeDraft(a.path),this.selectedPath=a.path,this.originalModel=oi(s),this.dirty=!1,this.transientMessage=`Saved ${a.path}.`,await this.refresh(!0)})}markDirty(t=!1){let n=this.dirty;if(this.dirty=this.editorMode==="yaml"?!0:!yl(this.originalModel,this.model),this.scheduleTypeDraftSave(),this.dirty&&!n&&(this.transientMessage=""),t||n!==this.dirty){this.render();return}let i=this.containerEl.querySelector(".mdbase-editor-title-line");i&&!i.querySelector(".mdbase-dirty")&&i.createSpan({cls:"mdbase-dirty",text:"Unsaved"});let s=this.containerEl.querySelector(".mdbase-editor-actions .mod-cta");s&&this.model?.specProfile==="v0.3"&&!this.model.readOnlyReason&&this.host.getMirrorProfile()?.mode!=="read_only"&&(s.disabled=!1);let o=this.containerEl.querySelector(".mdbase-change-review");if(o){let a=o.querySelector("p");a&&a.setText("Pending changes. Review details after leaving the current field.")}}scheduleTypeDraftSave(){this.draftSaveTimer!==null&&window.clearTimeout(this.draftSaveTimer),this.draftSaveTimer=window.setTimeout(()=>{this.draftSaveTimer=null,this.flushTypeDraft()},2e3)}async flushTypeDraft(){if(this.draftSaveTimer!==null&&(window.clearTimeout(this.draftSaveTimer),this.draftSaveTimer=null),!this.model||!this.dirty)return;let t={version:1,path:this.selectedPath,sourceRevision:this.originalModel?.sourceRevision??null,model:oi(this.model),editorMode:this.editorMode,yamlDraft:this.editorMode==="yaml"?this.yamlDraft:void 0,updatedAt:new Date().toISOString()};await this.host.saveTypeDraft(t)}async discardCurrentType(){let t=this.selectedPath;if(await this.host.clearTypeDraft(t),t){let n=await this.host.loadTypeModel(t);this.model=n,this.originalModel=oi(n),this.yamlDraft=`${Vt(fh(n),n.body)} `,this.dirty=!1}else this.model=null,this.originalModel=null,this.yamlDraft="",this.dirty=!1;this.transientMessage="Unsaved changes discarded.",this.render()}async refreshMirrorStatus(){if(!this.host.connectSync.isSyncing())try{this.mirrorStatus=await this.host.connectSync.status(),this.syncProblem=null,this.host.setSyncStatus(this.mirrorStatus)}catch(t){this.mirrorStatus=null,this.syncProblem=nr(t),this.host.setSyncProblem(this.syncProblem),this.transientMessage=this.syncProblem.message}}async perform(t){if(!this.busy){this.busy=!0,this.transientMessage="",this.render();try{await t()}catch(n){let i=n instanceof Error?n.message:String(n);this.transientMessage=i,new ce.Notice(i)}finally{this.busy=!1,this.render()}}}};function fh(r){return r.specProfile==="v0.3"&&!r.readOnlyReason?is(r):oi(r.originalFrontmatter??{name:r.name,fields:Object.fromEntries(r.fields.map(e=>[e.name,e.definition]))})}var Fo=class{constructor(e,t){this.delayMs=e;this.task=t;this.entries=new Map;if(!Number.isFinite(e)||e<0)throw new Error("Debounce delay must be a non-negative finite number.")}has(e){return this.entries.has(e)}schedule(e,t){let n=this.entries.get(e);n||(n={value:t,revision:0,timer:null,ready:!1,running:!1},this.entries.set(e,n)),n.value=t,n.revision+=1,n.ready=!1,n.timer!==null&&clearTimeout(n.timer),n.timer=setTimeout(()=>{this.entries.get(e)===n&&(n.timer=null,n.ready=!0,this.drain(e,n))},this.delayMs)}cancel(e){let t=this.entries.get(e);t&&(t.timer!==null&&clearTimeout(t.timer),this.entries.delete(e))}clear(){for(let e of this.entries.values())e.timer!==null&&clearTimeout(e.timer);this.entries.clear()}async drain(e,t){if(this.entries.get(e)!==t||t.running||!t.ready)return;t.ready=!1,t.running=!0;let n=t.revision,i=t.value,s=()=>this.entries.get(e)===t&&t.revision===n;try{await this.task(i,s)}finally{t.running=!1,this.entries.get(e)===t&&(t.ready?this.drain(e,t):t.timer===null&&t.revision===n&&this.entries.delete(e))}}};var ID={validateOnSave:!0,validateOnOpen:!0,showNoticeOnSave:!1,interopEnabled:!1,mirrorProfile:null,typeDrafts:{},syncActivity:[]};function TD(r){if(!r||typeof r!="object"||Array.isArray(r))return!1;let e=r;return e.version===1&&typeof e.syncUrl=="string"&&typeof e.controlUrl=="string"&&typeof e.collectionId=="string"&&typeof e.replicaId=="string"&&(e.mode==="read_only"||e.mode==="read_write")&&typeof e.name=="string"&&typeof e.enrollmentId=="string"&&typeof e.accessTokenExpiresAt=="string"&&(e.selectiveSync===void 0||Array.isArray(e.selectiveSync.file_classes)&&Array.isArray(e.selectiveSync.excluded_folders))}function RD(r){return r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var Uo=class extends O.Modal{constructor(t,n,i="",s=""){super(t);this.resolvePromise=null;this.settled=!1;this.title=n,this.placeholder=i,this.defaultValue=s}openAndGetValue(){return new Promise(t=>{this.settled=!1,this.resolvePromise=t,this.open()})}onOpen(){let{contentEl:t}=this;t.empty(),t.createEl("h3",{text:this.title});let n=t.createEl("input",{type:"text"});n.placeholder=this.placeholder,n.value=this.defaultValue,n.addClass("prompt-input");let i=t.createDiv({cls:"modal-button-container"}),s=i.createEl("button",{text:"Cancel"}),o=i.createEl("button",{text:"OK"});o.addClass("mod-cta"),s.onclick=()=>{this.finish(null),this.close()},o.onclick=()=>{this.finish(n.value.trim()),this.close()},n.addEventListener("keydown",a=>{a.key==="Enter"&&(a.preventDefault(),this.finish(n.value.trim()),this.close()),a.key==="Escape"&&(a.preventDefault(),this.finish(null),this.close())}),window.setTimeout(()=>n.focus(),0)}onClose(){this.settled||this.finish(null),this.contentEl.empty()}finish(t){this.settled||(this.settled=!0,this.resolvePromise?.(t),this.resolvePromise=null)}},yh=class extends O.SuggestModal{constructor(t,n,i){super(t);this.resultHandled=!1;this.typeDefs=[...n].sort((s,o)=>s.name.localeCompare(o.name)),this.onResult=i,this.setPlaceholder("Type to search..."),this.setInstructions([{command:"\u2191\u2193",purpose:"navigate"},{command:"\u21B5",purpose:"select"},{command:"esc",purpose:"cancel"}]),this.containerEl.addClass("mdbase-type-picker-modal"),this.titleEl.setText("Select type definition")}getSuggestions(t){let n=t.trim().toLowerCase();return n?this.typeDefs.filter(i=>{let s=i.match?.path_glob??"";return`${i.name} ${i.display_name_key??""} ${i.filePath} ${s}`.toLowerCase().includes(n)}).slice(0,100):this.typeDefs.slice(0,100)}renderSuggestion(t,n){let i=n.createDiv({cls:"mdbase-type-picker-suggestion"});i.createDiv({cls:"mdbase-type-picker-name",text:t.name});let s=i.createDiv({cls:"mdbase-type-picker-meta"});s.createSpan({cls:"mdbase-type-picker-path",text:t.filePath}),s.createSpan({cls:"mdbase-type-picker-count",text:`${Object.keys(t.fields??{}).length} fields`}),t.match?.path_glob&&i.createDiv({cls:"mdbase-type-picker-match",text:`match: ${t.match.path_glob}`})}onChooseSuggestion(t){this.resultHandled=!0,this.onResult({type:"selected",typeDef:t})}onClose(){window.setTimeout(()=>{this.resultHandled||this.onResult({type:"cancelled"})},0),super.onClose()}};function mh(r,e){return new Promise(t=>{new yh(r,e,i=>{if(i.type==="selected"){t(i.typeDef);return}t(null)}).open()})}var gh=class extends O.PluginSettingTab{constructor(e,t){super(e,t),this.plugin=t}display(){let{containerEl:e}=this;e.empty(),new O.Setting(e).setName("Validate on save").setDesc("Run mdbase validation when a Markdown file is modified.").addToggle(t=>t.setValue(this.plugin.settings.validateOnSave).onChange(async n=>{this.plugin.settings.validateOnSave=n,await this.plugin.saveSettings()})),new O.Setting(e).setName("Validate on file open").setDesc("Validate the active note when opened.").addToggle(t=>t.setValue(this.plugin.settings.validateOnOpen).onChange(async n=>{this.plugin.settings.validateOnOpen=n,await this.plugin.saveSettings()})),new O.Setting(e).setName("Show notices on save").setDesc("Display a notice when save-time validation finds issues.").addToggle(t=>t.setValue(this.plugin.settings.showNoticeOnSave).onChange(async n=>{this.plugin.settings.showNoticeOnSave=n,await this.plugin.saveSettings()})),new O.Setting(e).setName("Allow local application interoperability").setDesc("Allow installed Obsidian plugins to exchange validated mdbase events and actions in this vault. Contracts establish compatibility; this switch is the separate user grant.").addToggle(t=>t.setValue(this.plugin.settings.interopEnabled).onChange(async n=>{this.plugin.settings.interopEnabled=n,await this.plugin.saveSettings()}))}},wl=class extends O.Plugin{constructor(t,n){super(t,n);this.issueMap=new Map;this.sortedIssuesCache=null;this.mirrorStatus=null;this.mirrorProgress=null;this.fileProgress=null;this.currentSyncProblem=null;this.localChangeObserved=!1;this.schemaCache=null;this.schemaLoadPromise=null;this.saveValidationDebounceMs=2e3;this.schemaRefreshDebounceMs=2e3;this.saveValidations=new Fo(this.saveValidationDebounceMs,async(t,n)=>{try{await this.validateFileAndStore(t,"save",n)}catch(i){console.error("mdbase: background validation failed",i)}});this.schemaRefreshes=new Fo(this.schemaRefreshDebounceMs,async()=>{this.invalidateSchemaCache(),this.refreshWorkspaceViews()});this.connectSync=new ul(t,{getMirrorProfile:()=>this.getMirrorProfile(),saveMirrorProfile:async i=>{this.settings.mirrorProfile=i,await this.saveSettings(),i?this.refreshSyncStatus():(this.mirrorStatus=null,this.currentSyncProblem=null,this.localChangeObserved=!1,this.updateStatusBar())}}),this.interopBridge=new Wa(t,()=>this.settings?.interopEnabled===!0),this.api={apiVersion:1,interop:this.interopBridge,getInteropStatus:()=>({enabled:this.settings?.interopEnabled===!0,profileVersion:"0.1"})}}async onload(){await this.loadSettings(),await this.connectSync.initialize(),(0,O.addIcon)(as,dS),this.statusBarEl=this.addStatusBarItem(),this.statusBarEl.addClass("mdbase-status-bar"),this.statusBarEl.setAttr("role","button"),this.statusBarEl.setAttr("tabindex","0"),this.registerDomEvent(this.statusBarEl,"click",()=>void this.openStatusDestination()),this.registerDomEvent(this.statusBarEl,"keydown",n=>{n.key!=="Enter"&&n.key!==" "||(n.preventDefault(),this.openStatusDestination())}),this.updateStatusBar(),this.registerView(ai,n=>new _l(n,this)),this.addSettingTab(new gh(this.app,this)),this.addRibbonIcon(as,"Open mdbase",()=>void this.openWorkspace()),this.addCommand({id:"mdbase-open",name:"Open workspace",callback:()=>void this.openWorkspace()}),this.addCommand({id:"mdbase-initialize-collection",name:"Initialize collection",callback:()=>void this.initializeCollectionCommand()}),this.addCommand({id:"mdbase-create-type",name:"Create type definition",callback:()=>void this.createTypeDefinitionCommand()}),this.addCommand({id:"mdbase-edit-type",name:"Edit type definition",callback:()=>void this.editTypeDefinitionCommand()}),this.addCommand({id:"mdbase-edit-current-type",name:"Edit current type definition",callback:()=>void this.editCurrentTypeDefinitionCommand()}),this.addCommand({id:"mdbase-create-note-from-type",name:"Create note from type",callback:()=>void this.createNoteFromTypeCommand()}),this.addCommand({id:"mdbase-validate-current-note",name:"Validate current note",callback:()=>void this.validateCurrentNoteCommand()}),this.addCommand({id:"mdbase-validate-collection",name:"Validate collection",callback:()=>void this.runCollectionValidation(!0)}),this.addCommand({id:"mdbase-open-issues-view",name:"Open issues view",callback:()=>void this.openWorkspace("issues")}),this.addCommand({id:"mdbase-sync",name:"Review sync changes",callback:()=>void this.reviewSyncCommand()}),this.addCommand({id:"mdbase-open-sync",name:"Open sync",callback:()=>void this.openWorkspace("sync")}),this.addCommand({id:"mdbase-sync-now",name:"Sync now",callback:()=>void this.syncNowCommand()}),this.addCommand({id:"mdbase-cancel-sync",name:"Cancel current sync",checkCallback:n=>this.connectSync.isSyncing()?(n||(this.connectSync.cancelSync(),this.setSyncProblem(nr(new DOMException("Synchronization stopped.","AbortError")))),!0):!1}),this.addCommand({id:"mdbase-open-activity",name:"Open sync activity",callback:()=>void this.openSyncSection("activity")}),this.addCommand({id:"mdbase-resolve-conflicts",name:"Resolve sync conflicts",callback:()=>void this.openSyncSection("conflicts")}),this.addCommand({id:"mdbase-reconnect",name:"Reconnect collection",callback:()=>void this.reconnectCommand()}),this.registerEvent(this.app.vault.on("modify",n=>{n instanceof O.TFile&&this.onVaultModify(n)})),this.registerEvent(this.app.vault.on("rename",(n,i)=>{n instanceof O.TFile&&this.onVaultRename(n,i)})),this.registerEvent(this.app.vault.on("delete",n=>{n instanceof O.TFile&&this.onVaultDelete(n)})),this.registerEvent(this.app.vault.on("create",n=>{n instanceof O.TFile&&this.onVaultCreate(n)})),this.registerEvent(this.app.workspace.on("file-open",n=>{this.settings.validateOnOpen&&(!(n instanceof O.TFile)||n.extension!=="md"||this.validateFileAndStore(n,"open"))})),this.registerEvent(this.app.workspace.on("editor-change",(n,i)=>{let s=i.file;s instanceof O.TFile&&(this.saveValidations.has(s.path)&&this.scheduleSaveValidation(s),this.schemaRefreshes.has("schema")&&this.isSchemaRelevantPath(s.path)&&this.scheduleSchemaRefresh())}));let t=this.app.workspace.getActiveFile();t&&this.settings.validateOnOpen&&this.validateFileAndStore(t,"open"),this.getMirrorProfile()&&this.refreshSyncStatus(),this.registerInterval(window.setInterval(()=>{this.getMirrorProfile()&&!this.connectSync.isSyncing()&&this.refreshSyncStatus()},6e4))}onunload(){this.connectSync.dispose(),this.interopBridge.dispose().catch(t=>{console.error("mdbase: failed to dispose the interoperability bridge",t)}),this.app.workspace.getLeavesOfType(ai).forEach(t=>t.detach()),this.saveValidations.clear(),this.schemaRefreshes.clear()}async loadSettings(){if(this.settings=Object.assign({},ID,await this.loadData()),!TD(this.settings.mirrorProfile))this.settings.mirrorProfile=null;else try{this.settings.mirrorProfile.selectiveSync=xr(this.settings.mirrorProfile.selectiveSync)}catch{this.settings.mirrorProfile.selectiveSync=xr()}(!this.settings.typeDrafts||typeof this.settings.typeDrafts!="object"||Array.isArray(this.settings.typeDrafts))&&(this.settings.typeDrafts={}),this.settings.syncActivity=pS(this.settings.syncActivity)}async saveSettings(){await this.saveData(this.settings)}getIssues(){return this.sortedIssuesCache??=Array.from(this.issueMap.values()).flat().sort((t,n)=>t.path.localeCompare(n.path)||t.severity.localeCompare(n.severity)||t.code.localeCompare(n.code)),this.sortedIssuesCache}getMirrorProfile(){return this.settings.mirrorProfile?JSON.parse(JSON.stringify(this.settings.mirrorProfile)):null}getSyncActivity(){return this.settings.syncActivity.map(t=>({...t}))}getCurrentSyncProblem(){return this.currentSyncProblem?{...this.currentSyncProblem}:null}setSyncStatus(t,n={}){this.mirrorStatus=t?JSON.parse(JSON.stringify(t)):null,n.clearLocalChanges&&(this.localChangeObserved=!1),this.currentSyncProblem=null,this.updateStatusBar()}setSyncProgress(t,n=null){this.mirrorProgress=t?{...t}:null,this.fileProgress=n?{...n}:null,this.updateStatusBar()}setSyncProblem(t){this.currentSyncProblem=t?{...t}:null,this.updateStatusBar()}async recordSyncActivity(t){this.settings.syncActivity=hS(this.settings.syncActivity,mS(t)),await this.saveSettings(),this.refreshWorkspaceViews()}async dismissSyncActivity(t){this.settings.syncActivity=this.settings.syncActivity.filter(n=>n.id!==t),await this.saveSettings(),this.refreshWorkspaceViews()}async clearCompletedSyncActivity(){this.settings.syncActivity=this.settings.syncActivity.filter(t=>t.requiresAcknowledgement),await this.saveSettings(),this.refreshWorkspaceViews()}async refreshSyncStatus(){if(!this.getMirrorProfile())return this.setSyncStatus(null),null;if(this.connectSync.isSyncing())return this.mirrorStatus;try{let t=await this.connectSync.status();return this.setSyncStatus(t),t}catch(t){return this.setSyncProblem(nr(t)),null}}async loadWorkspaceSchema(t=!1){return this.getConfigAndTypes(t)}async loadTypeModel(t){let n=this.app.vault.getAbstractFileByPath((0,O.normalizePath)(t));if(!(n instanceof O.TFile))throw new Error(`Type file not found: ${t}`);let i=await this.app.vault.cachedRead(n),s=et(i);if(!s.hasFrontmatter||s.error)throw new Error(`Invalid type frontmatter: ${s.error??"frontmatter is missing"}`);let o=ml(s.frontmatter,s.body,n.basename);return o.sourceRevision=oh(i),o}loadTypeDraft(t){let n=this.settings.typeDrafts[t??"__new__"];return n?JSON.parse(JSON.stringify(n)):null}async saveTypeDraft(t){this.settings.typeDrafts[t.path??"__new__"]=JSON.parse(JSON.stringify(t)),await this.saveSettings()}async clearTypeDraft(t){let n=t??"__new__";n in this.settings.typeDrafts&&(delete this.settings.typeDrafts[n],await this.saveSettings())}async saveTypeModel(t,n,i){if(this.connectSync.assertLocalAuthorityWritable(),this.getMirrorProfile()?.mode==="read_only")throw new Error("This mirror has read-only access. Re-enroll it with write access before editing types.");let s=await Ii(this.app.vault);if(!s)throw new Error("No mdbase.yaml found.");if(!s.spec_version.startsWith("0.3."))throw new Error("mdbase v0.2 type definitions are read-only. Migrate the collection first.");let o=n?this.app.vault.getAbstractFileByPath((0,O.normalizePath)(n)):null;if(o!=null&&!(o instanceof O.TFile))throw new Error(`Type file not found: ${n}`);let a=await this.writeTypeDefinition(s,t,o,i);return this.refreshWorkspaceViews(!0),a}async initializeCollection(){this.connectSync.assertLocalAuthorityWritable(),await this.initializeCollectionCommand(),this.refreshWorkspaceViews(!0)}async validateCollection(){await this.runCollectionValidation(!1)}analyzeMigration(){if(this.getMirrorProfile())throw new Error("Collection authority resources must be migrated at the collection authority.");return Z0(this.app.vault)}async applyMigration(t,n){if(this.connectSync.assertLocalAuthorityWritable(),this.getMirrorProfile())throw new Error("Collection authority resources must be migrated at the collection authority.");let i=await eS(this.app.vault,t,{allowLossy:n});if(!i.applied)throw new Error(i.restored?`Migration failed and all writes were rolled back. ${i.error??""}`.trim():`Migration needs manual recovery. See ${i.manifestPath}. ${i.error??""}`.trim());this.invalidateSchemaCache(),new O.Notice(`Migrated to mdbase v0.3. Recovery manifest: ${i.manifestPath}`),this.refreshWorkspaceViews(!0)}async openIssue(t){await this.openFileByPath(t.path,t.field)}getQuickFixLabel(t){return Tb(t)}async applyQuickFix(t){this.connectSync.assertLocalAuthorityWritable();let n=this.app.vault.getAbstractFileByPath(t.path);if(!(n instanceof O.TFile)){new O.Notice(`File not found: ${t.path}`);return}if(this.getMirrorProfile()?.mode==="read_only")throw new Error("This mirror has read-only access.");let i=!1;await this.app.vault.process(n,s=>{this.connectSync.assertLocalAuthorityWritable();let o=Mb(s,t);return i=o.changed,o.content}),new O.Notice(i?`Updated '${t.field??"field"}' in ${n.basename}`:"The field changed or no safe quick fix is available. Revalidate the note."),await this.validateFileAndStore(n,"manual")}async openFileByPath(t,n){let i=this.app.vault.getAbstractFileByPath(t);if(!(i instanceof O.TFile)){new O.Notice(`File not found: ${t}`);return}await this.app.workspace.getLeaf(!0).openFile(i),n&&this.revealFrontmatterField(i,n)}revealFrontmatterField(t,n){let i=this.app.workspace.getMostRecentLeaf();if(!i||!(i.view instanceof O.MarkdownView))return;let s=i.view;if(!(s.file instanceof O.TFile)||s.file.path!==t.path)return;let o=s.editor,a=o.lineCount();if(a<3)return;let c=Pb(n),l=new RegExp(`^\\s*${RD(c)}\\s*:`);if(o.getLine(0).trim()==="---")for(let u=1;u({...s,path:n}))),this.sortedIssuesCache=null,this.refreshIssueViews())}clearPendingSaveValidation(t){this.saveValidations.cancel(t)}scheduleSaveValidation(t){this.saveValidations.schedule(t.path,t)}scheduleSchemaRefresh(){this.schemaRefreshes.schedule("schema",void 0)}refreshSchemaNow(){this.schemaRefreshes.cancel("schema"),this.invalidateSchemaCache(),this.refreshWorkspaceViews()}isSchemaRelevantPath(t){let n=(0,O.normalizePath)(t);if(n==="mdbase.yaml")return!0;let i=new Set(["_types","_contracts"]);this.schemaCache&&(i.add((0,O.normalizePath)(this.schemaCache.config.settings.types_folder)),i.add((0,O.normalizePath)(this.schemaCache.config.settings.contracts_folder??"_contracts")));for(let s of i)if(n===s||n.startsWith(`${s}/`))return!0;return!1}invalidateSchemaCache(){this.schemaCache=null,this.schemaLoadPromise=null}async getConfigAndTypes(t=!1){if(t&&this.invalidateSchemaCache(),this.schemaCache)return this.schemaCache;if(this.schemaLoadPromise)return this.schemaLoadPromise;this.schemaLoadPromise=(async()=>{let n=await Ii(this.app.vault);if(!n)return null;let i=await vb(this.app.vault,n),s=await _b(this.app.vault,n);return{config:n,types:i,contracts:s}})();try{let n=await this.schemaLoadPromise;return n&&(this.schemaCache=n),n}finally{this.schemaLoadPromise=null}}async requireConfigAndTypes(t={}){let n=t.background??!1,i=await this.getConfigAndTypes(t.forceReload??!1);return i?(i.types.size===0&&!n&&new O.Notice(`No types found in ${i.config.settings.types_folder}`),i):(n||new O.Notice("No mdbase.yaml found. Run 'mdbase: Initialize collection' first."),null)}onVaultModify(t){this.observeLocalMirrorChange(t.path),this.isSchemaRelevantPath(t.path)&&this.scheduleSchemaRefresh(),this.settings.validateOnSave&&t.extension==="md"&&this.scheduleSaveValidation(t)}onVaultRename(t,n){this.observeLocalMirrorChange(n),this.observeLocalMirrorChange(t.path),(this.isSchemaRelevantPath(n)||this.isSchemaRelevantPath(t.path))&&this.refreshSchemaNow(),t.extension==="md"&&(this.clearPendingSaveValidation(n),this.moveFileIssues(n,t.path),this.settings.validateOnSave&&this.scheduleSaveValidation(t))}onVaultDelete(t){this.observeLocalMirrorChange(t.path),this.isSchemaRelevantPath(t.path)&&this.refreshSchemaNow(),t.extension==="md"&&(this.clearPendingSaveValidation(t.path),this.clearFileIssues(t.path))}onVaultCreate(t){this.observeLocalMirrorChange(t.path),this.isSchemaRelevantPath(t.path)&&this.refreshSchemaNow()}observeLocalMirrorChange(t){if(!this.getMirrorProfile()||this.connectSync.isSyncing())return;let n=(0,O.normalizePath)(t);[this.app.vault.configDir,".mdbase",".trash",".git"].some(s=>n===s||n.startsWith(`${s}/`))||(this.localChangeObserved=!0,this.updateStatusBar())}async validateFileAndStore(t,n,i=()=>!0){let s=await this.requireConfigAndTypes({background:n!=="manual"});if(!i())return[];if(!s)return n!=="manual"&&this.clearFileIssues(t.path),[];let o=await lf(this.app.vault,t,s.config,s.types);return i()&&(this.setFileIssues(t.path,o),n==="save"&&this.settings.showNoticeOnSave&&o.length>0&&new O.Notice(`mdbase: ${o.length} issue${o.length===1?"":"s"} in ${t.basename}`)),o}async initializeCollectionCommand(){if(this.connectSync.assertLocalAuthorityWritable(),this.getMirrorProfile()){new O.Notice("This vault is configured as a mirror. Sync it instead of initializing a local collection.");return}let{created:t}=await mb(this.app.vault);if(this.invalidateSchemaCache(),t.length===0){new O.Notice("mdbase collection already initialized.");return}new O.Notice(`Initialized mdbase collection: ${t.join(", ")}`)}async reviewSyncCommand(){let t=await this.openWorkspace("sync");this.getMirrorProfile()&&await t.reviewSyncChanges()}async syncNowCommand(){let t=await this.openWorkspace("sync");this.getMirrorProfile()&&await t.syncNow()}async openSyncSection(t){(await this.openWorkspace("sync")).focusSyncSection(t)}async reconnectCommand(){let t=await this.openWorkspace("sync");this.getMirrorProfile()&&await t.reconnectCollection()}async createTypeDefinitionCommand(){(await this.openWorkspace("types")).createNewType()}async editTypeDefinitionCommand(){let t=await this.requireConfigAndTypes();if(!t||t.types.size===0){new O.Notice("No type definitions found.");return}let n=await mh(this.app,[...t.types.values()]);if(!n)return;await(await this.openWorkspace("types")).editType(n.filePath)}async editCurrentTypeDefinitionCommand(){let t=this.app.workspace.getActiveFile();if(!(t instanceof O.TFile)||t.extension!=="md"){new O.Notice("Open a typed Markdown note or type definition first.");return}let n=await this.requireConfigAndTypes();if(!n)return;let s=[...n.types.values()].find(a=>a.filePath===t.path)??null;if(!s){let a=et(await this.app.vault.cachedRead(t));if(a.error){new O.Notice(`Cannot identify this note's type: ${a.error}`);return}let l=sn(t.path,a.frontmatter,n.config,n.types).flatMap(u=>{let d=n.types.get(u);return d?[d]:[]});if(l.length===0){new O.Notice("The current note does not match a known type definition.");return}s=l.length===1?l[0]:await mh(this.app,l)}if(!s){new O.Notice("The current note does not match a known type definition.");return}await(await this.openWorkspace("types")).editType(s.filePath)}async createNoteFromTypeCommand(){this.connectSync.assertLocalAuthorityWritable();let t=await this.requireConfigAndTypes();if(!t)return;if(t.types.size===0){new O.Notice("No type definitions found.");return}let n=await mh(this.app,Array.from(t.types.values()));if(!n)return;let i=$b(n,t.config),s=Eb(n,i);for(let[u,d]of s){let f=`Required field: ${u}`,p=await new Uo(this.app,f,d.type??"string").openAndGetValue();if(p==null)return;if(p.trim().length===0){new O.Notice(`Field '${u}' is required.`);return}try{i[u]=df(p,d)}catch(m){new O.Notice(`Invalid value for ${u}: ${m instanceof Error?m.message:String(m)}`);return}}let o=n.display_name_key??"title";if(i[o]==null){let u=await new Uo(this.app,`Optional ${o} (used for filename)`,"").openAndGetValue();u&&u.trim().length>0&&(i[o]=u.trim())}let a=await Ab(this.app.vault,n,i),c=await new Uo(this.app,"Note path","Relative path in vault",a).openAndGetValue();if(c==null)return;let l=(0,O.normalizePath)(c.trim().length>0?c.trim():a);l.endsWith(".md")||(l=`${l}.md`);try{let u=await kb(this.app.vault,l,i);await this.app.workspace.getLeaf(!0).openFile(u),new O.Notice(`Created note: ${u.path}`),await this.validateFileAndStore(u,"manual")}catch(u){new O.Notice(u instanceof Error?u.message:String(u))}}async validateCurrentNoteCommand(){let t=this.app.workspace.getActiveFile();if(!(t instanceof O.TFile)||t.extension!=="md"){new O.Notice("Open a Markdown note first.");return}let n=await this.validateFileAndStore(t,"manual");n.length===0?new O.Notice("No issues in current note."):(new O.Notice(`Found ${n.length} issue${n.length===1?"":"s"} in current note.`),await this.openIssuesView())}async runCollectionValidation(t){let n=await this.requireConfigAndTypes({background:!1});if(!n)return;let i=await Sb(this.app.vault,n.config,n.types),s=new Map;for(let o of i){let a=s.get(o.path)??[];a.push(o),s.set(o.path,a)}if(this.issueMap=s,this.sortedIssuesCache=null,this.refreshIssueViews(),t)if(i.length===0)new O.Notice("Collection validation passed with no issues.");else{let o=i.filter(c=>c.severity==="error").length,a=i.length-o;new O.Notice(`Collection validation: ${o} error(s), ${a} warning(s)`),await this.openIssuesView()}}async ensureFolderExists(t){let n=(0,O.normalizePath)(t).replace(/\/+$/,"");if(!n)return;let i=n.split("/"),s="";for(let o of i)s=s?`${s}/${o}`:o,await this.app.vault.adapter.exists(s)||await this.app.vault.createFolder(s)}async writeTypeDefinition(t,n,i,s){let o=n.name.trim();if(!o)throw new Error("Type name is required.");if(!t.spec_version.startsWith("0.3.")||n.specProfile!=="v0.3")throw new Error("mdbase v0.2 type definitions are read-only. Migrate the collection first.");let a=is(n),c=n.body.trim()||`# ${o} diff --git a/package.json b/package.json index bd485df..6147c94 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "build:test:staging": "npm run build:staging && node copy-files.mjs", "profile:testvault": "node scripts/profile-testvault.mjs", "test": "npm run test:unit", + "test:sdk-candidate": "tsc -p tsconfig.test.json && node --loader ./test/obsidian-loader.mjs scripts/test-lossless-sync.mjs", "test:unit": "tsc -p tsconfig.test.json && node --experimental-specifier-resolution=node --loader ./test/obsidian-loader.mjs --test .test-dist/test/**/*.test.js", "copy-files": "node copy-files.mjs", "version": "node version-bump.mjs && git add manifest.json versions.json" diff --git a/scripts/test-lossless-sync.mjs b/scripts/test-lossless-sync.mjs new file mode 100644 index 0000000..a3abfe1 --- /dev/null +++ b/scripts/test-lossless-sync.mjs @@ -0,0 +1,80 @@ +// Candidate qualification, not live Obsidian or hosted acceptance. +// Run with the Obsidian test loader after installing immutable candidate SDK +// artifacts without changing package.json/package-lock.json. The released +// beta.91 SDK is expected to fail this stricter contract until its pin advances. +import assert from "node:assert/strict"; +import { TFile } from "obsidian"; +import { MemoryAuthority } from "@mdbase-dev/connect-sync"; +import { DirectoryMirror, MemoryMirrorStateStore, WritableDirectoryMirror } from "@mdbase-dev/connect-sync/mirror"; +import { ObsidianMirrorFileSystem } from "../.test-dist/src/connectSync.js"; +import { previewFromPlan } from "../.test-dist/src/syncPreview.js"; +import { syncReviewPresentation } from "../.test-dist/src/syncUx.js"; + +// Minimal flat-file Vault fixture. All documents and authority state are in +// memory; this harness cannot write into a user's vault or contact a daemon. +function vaultFixture() { + const files = new Map(); + return { + files, + getAbstractFileByPath: (path) => files.get(path)?.file ?? null, + getMarkdownFiles: () => [...files.values()].map(({ file }) => file), + adapter: { + exists: async (path) => files.has(path), + readBinary: async (path) => { + assert.ok(files.has(path), `Missing fixture: ${path}`); + return new TextEncoder().encode(files.get(path).document).buffer; + }, + }, + create: async (path, document) => { + assert.equal(files.has(path), false); + const file = new TFile(path); + files.set(path, { file, document }); + return file; + }, + modify: async (file, document) => { + assert.ok(files.has(file.path)); + files.set(file.path, { file, document }); + }, + }; +} + +const documents = [ + "---\nbroken: [\n---\nBody", + "---\na: 1\na: 2\n---\nBody", + "---\nhello\n---\nBody", + "---\nnull\n---\nBody", + "---\n- one\n- two\n---\nBody", + "\uFEFF---\r\nbroken: [\r\n---\r\nExact — bytes\r\n", + "---\nbroken: [\n---", + "\uFEFF---\r\ntitle: Present\r\n---\r\nExact structured bytes", + "\uFEFF# Body-only bytes\r\n", + "# Valid sibling\n", +]; +const hosted = new MemoryAuthority(); +const writerId = hosted.registerReplica({ name: "Candidate writer", mode: "read_write" }); +const readerId = hosted.registerReplica({ name: "Candidate reader", mode: "read_only" }); +const source = vaultFixture(); +const destination = vaultFixture(); +for (const [index, document] of documents.entries()) await source.create(`${index}.md`, document); +const writer = new WritableDirectoryMirror(writerId, hosted.transport(writerId), { + fileSystem: new ObsidianMirrorFileSystem(source), stateStore: new MemoryMirrorStateStore(), +}); +const reader = new DirectoryMirror(readerId, hosted.transport(readerId), { + fileSystem: new ObsidianMirrorFileSystem(destination), stateStore: new MemoryMirrorStateStore(), +}); +const plan = await writer.inspect(); +assert.equal(plan.summary.blocking_issues, 0, "Candidate SDK must not block readable malformed YAML"); +assert.equal(plan.summary.uploads, documents.length); +const preview = previewFromPlan(plan); +const presentation = syncReviewPresentation(plan, preview.entries.length); +assert.equal(presentation.actionDisabled, false); +assert.match(presentation.message, /warnings do not block/); +assert.equal((await writer.apply(plan)).status, "applied"); +await reader.sync(); +for (const [index, document] of documents.entries()) { + assert.equal(source.files.get(`${index}.md`)?.document, document); + assert.equal(destination.files.get(`${index}.md`)?.document, document); +} +assert.equal((await reader.inspect()).summary.blocking_issues, 0); +assert.deepEqual((await reader.inspect()).actions, []); +console.log(JSON.stringify({ authority: "in_process_reference", vault: "mock", exact_round_trips: documents.length })); diff --git a/src/connectSync.ts b/src/connectSync.ts index f3a26a4..eb38ca2 100644 --- a/src/connectSync.ts +++ b/src/connectSync.ts @@ -906,7 +906,8 @@ export class ObsidianMirrorFileSystem implements MirrorFileSystem { throw new SyncError("file_read_failed", `Could not read ${path}.`); } try { - return new TextDecoder("utf-8", { fatal: true }).decode(bytes); + // Preserve the BOM as a character instead of consuming its bytes. + return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes); } catch { return { kind: "invalid", diff --git a/src/syncPreview.ts b/src/syncPreview.ts index db22a63..af8e557 100644 --- a/src/syncPreview.ts +++ b/src/syncPreview.ts @@ -6,7 +6,7 @@ import type { } from "@mdbase-dev/connect-sync/mirror"; export type SyncPreviewDirection = "download" | "upload" | "attention"; -export type SyncPreviewAction = "create" | "update" | "rename" | "delete" | "replace" | "fix"; +export type SyncPreviewAction = "create" | "update" | "rename" | "delete" | "replace" | "fix" | "review"; export interface SyncPreviewEntry { kind: "document" | "file"; @@ -35,7 +35,7 @@ export function previewFromPlan(plan: MirrorSyncPlan): MdbaseSyncPreview { kind: "document", path: issue.path ?? "Sync engine", direction: "attention", - action: "fix", + action: issue.blocking ? "fix" : "review", detail: issue.message, })), ]; diff --git a/src/syncUx.ts b/src/syncUx.ts index 10cc6f8..7045eb9 100644 --- a/src/syncUx.ts +++ b/src/syncUx.ts @@ -58,11 +58,12 @@ export function syncReviewPresentation( return { actionLabel: "Fix local files before syncing", actionDisabled: true, - message: "Synchronization is paused. Fix every listed local file, then refresh the review.", + message: "Synchronization is paused. Resolve the blocking issues, then refresh the review.", }; } const outcomes = plan.actions.filter((action) => action.command !== "advance_checkpoint").length; const hasCheckpoint = plan.actions.some((action) => action.command === "advance_checkpoint"); + const frontmatterWarning = plan.issues.some((issue) => issue.code === "invalid_frontmatter" && !issue.blocking); return { actionLabel: outcomes ? `Sync ${outcomes} ${outcomes === 1 ? "outcome" : "outcomes"}` @@ -70,9 +71,11 @@ export function syncReviewPresentation( ? "Confirm sync checkpoint" : "Already up to date", actionDisabled: busy || plan.actions.length === 0, - message: entryCount - ? "Review each transfer below, then sync when ready." - : "This vault and the hosted collection are already aligned.", + message: frontmatterWarning + ? "Frontmatter warnings do not block this sync. Document bytes are preserved; synchronization does not repair YAML." + : entryCount + ? "Review each transfer below, then sync when ready." + : "This vault and the hosted collection are already aligned.", }; } diff --git a/src/workspaceView.ts b/src/workspaceView.ts index 3460f1d..9e4c36d 100644 --- a/src/workspaceView.ts +++ b/src/workspaceView.ts @@ -2279,7 +2279,9 @@ export class MdbaseWorkspaceView extends ItemView { } else if (preview.local_issues.length) { section.createDiv({ cls: "mdbase-inline-message", - text: "Synchronization is paused until every invalid or unreadable local file listed here is fixed.", + text: preview.plan.summary.blocking_issues > 0 + ? "Synchronization is paused. Resolve the blocking issues, then refresh the review." + : "These diagnostics do not block synchronization. Document bytes are preserved unchanged.", }); } } @@ -2453,7 +2455,7 @@ export class MdbaseWorkspaceView extends ItemView { const section = container.createEl("section", { cls: "mdbase-editor-section" }); section.createEl("h3", { text: "Local files needing attention" }); section.createEl("p", { - text: "Synchronization is paused to keep the mirror checkpoint exact. Fix every malformed or unreadable file below, then preview again.", + text: "Review these file diagnostics. The sync preview identifies which issues block synchronization; a diagnostic alone does not mean syncing is paused.", }); for (const issue of status.local_issues) { const row = section.createDiv({ cls: "mdbase-conflict-row" }); diff --git a/test/syncPreview.test.ts b/test/syncPreview.test.ts index 5c33eb8..c3ad3d4 100644 --- a/test/syncPreview.test.ts +++ b/test/syncPreview.test.ts @@ -76,6 +76,16 @@ test("sync preview is a direct projection of the engine-owned plan", () => { assert.equal(preview.upload_files, 1); }); +test("nonblocking diagnostics are optional review, not mandatory repair", () => { + const preview = previewFromPlan(plan({ + issues: [{ code: "invalid_frontmatter", path: "opaque.md", message: "Invalid YAML", blocking: false }], + })); + assert.equal(preview.entries[0]?.action, "review"); + assert.equal(preview.local_issues[0]?.path, "opaque.md"); + assert.equal(preview.plan.summary.blocking_issues, 0); + assert.deepEqual(preview.plan.actions, []); +}); + test("an exact idle plan remains an explicit zero-action preview", () => { const preview = previewFromPlan(plan({ base_cursor: 6, diff --git a/test/syncUx.test.ts b/test/syncUx.test.ts index 4225eca..8c1ee32 100644 --- a/test/syncUx.test.ts +++ b/test/syncUx.test.ts @@ -60,10 +60,35 @@ test("blocking sync reviews use fix-first wording and disable apply", () => { assert.equal(presentation.actionLabel, "Fix local files before syncing"); assert.equal(presentation.actionDisabled, true); - assert.match(presentation.message, /paused.*fix every listed local file/i); + assert.match(presentation.message, /paused.*resolve the blocking issues/i); assert.doesNotMatch(`${presentation.actionLabel} ${presentation.message}`, /up to date|sync when ready/i); }); +test("nonblocking frontmatter diagnostics leave exact transfers enabled", () => { + const reviewed = plan({ + actions: [{ + command: "put_remote", action_id: "upload", depends_on: [], + target: { entity: "record", identity: "r", path: "opaque.md", revision: "r1", payload_revision: "r1" }, + payload_revision: "r1", expected_remote: { state: "absent" }, expected_local: { state: "absent" }, + idempotency_key: "upload", reason: "local_change", + }], + issues: [{ code: "invalid_frontmatter", path: "opaque.md", message: "Invalid YAML", blocking: false }], + summary: { uploads: 1, downloads: 0, conflicts: 0, blocking_issues: 0 }, + }); + const presentation = syncReviewPresentation(reviewed, 2); + assert.equal(presentation.actionDisabled, false); + assert.equal(presentation.actionLabel, "Sync 1 outcome"); + assert.match(presentation.message, /warnings do not block.*bytes are preserved/i); + assert.equal(syncReviewPresentation(reviewed, 2, true).actionDisabled, true); + const blocked = syncReviewPresentation({ ...reviewed, actions: [], issues: [ + ...reviewed.issues, + { code: "file_read_failed", path: "unreadable.md", message: "Unreadable", blocking: true }, + ], summary: { uploads: 0, downloads: 0, conflicts: 0, blocking_issues: 1 } }, 2); + assert.equal(blocked.actionDisabled, true); + assert.match(blocked.message, /blocking issues/); + assert.doesNotMatch(blocked.message, /fix every|do not block/i); +}); + test("sync indicator gives transfer, attention, waiting, and synced states stable priority", () => { const base = { connected: true, status: status(), progress: null, fileProgress: null, problem: null, validationIssues: 0, localChangeObserved: false }; assert.equal(syncIndicator(base).state, "synced"); diff --git a/test/v3-foundations.test.ts b/test/v3-foundations.test.ts index ed35ea8..eeb4052 100644 --- a/test/v3-foundations.test.ts +++ b/test/v3-foundations.test.ts @@ -36,6 +36,7 @@ import { type MdbaseConfig, type MdbaseTypeDef, parseFrontmatter, + validateFile, } from "../src/mdbaseCore"; interface StoredFile { @@ -918,21 +919,16 @@ test("device lease rejects concurrent mirror ownership and releases after failur await lease.runExclusive(async () => undefined); }); -test("beta.91 fences invalid or unreadable local Markdown and every valid sibling until repair", async () => { - const invalidCases: Array<[string, string | Uint8Array]> = [ - ["broken.md", "---\nbroken: [\n---\nBody"], - ["duplicate.md", "---\na: 1\na: 2\n---\nBody"], - ["scalar.md", "---\nhello\n---\nBody"], - ["null.md", "---\nnull\n---\nBody"], - ["list.md", "---\n- one\n- two\n---\nBody"], +test("fences invalid UTF-8 or unreadable local Markdown and every valid sibling until repair", async () => { + const invalidCases: Array<[string, Uint8Array]> = [ ["bytes.md", Uint8Array.of(0x62, 0x61, 0x64, 0xff)], + ["truncated.md", Uint8Array.of(0xe2, 0x82)], ]; for (const [path, invalid] of invalidCases) { const hosted = new MemoryAuthority(); const replica = hosted.registerReplica({ name: "Obsidian writer", mode: "read_write" }); const vault = new MemoryVault(); - if (typeof invalid === "string") await vault.create(path, invalid); - else await vault.createBinary(path, Uint8Array.from(invalid).buffer); + await vault.createBinary(path, Uint8Array.from(invalid).buffer); await vault.create("valid.md", "# Valid sibling"); const mirror = new WritableDirectoryMirror(replica, hosted.transport(replica), { fileSystem: new ObsidianMirrorFileSystem(vault as never), @@ -951,11 +947,8 @@ test("beta.91 fences invalid or unreadable local Markdown and every valid siblin const invalidFile = vault.getAbstractFileByPath(path); assert.ok(invalidFile instanceof TFile); - if (typeof invalid === "string") await vault.modify(invalidFile, "# Fixed local note"); - else { - await vault.delete(invalidFile); - await vault.create(path, "# Fixed local note"); - } + await vault.delete(invalidFile); + await vault.create(path, "# Fixed local note"); await mirror.sync(); snapshot = await hosted.transport(replica).snapshot((await hosted.transport(replica).openSession()).snapshot_id); assert.deepEqual(snapshot.records.map((record) => record.path).sort(), [path, "valid.md"].sort()); @@ -989,6 +982,61 @@ test("beta.91 fences invalid or unreadable local Markdown and every valid siblin assert.deepEqual((await mirror.status()).local_issues, []); }); +test("the Obsidian adapter preserves malformed frontmatter, BOMs and line endings as readable UTF-8", async () => { + const vault = new MemoryVault(); + const fileSystem = new ObsidianMirrorFileSystem(vault as never); + for (const document of [ + "---\nbroken: [\n---\nBody", + "---\na: 1\na: 2\n---\nBody", + "---\nhello\n---\nBody", + "---\nnull\n---\nBody", + "---\n- one\n- two\n---\nBody", + "\uFEFF---\r\nbroken: [\r\n---\r\nExact — bytes\r\n", + "\uFEFF# Body-only note without a final newline", + ]) { + await vault.adapter.write("note.md", document); + assert.equal(await fileSystem.readText("note.md"), document); + } +}); + +test("schema errors remain visible while Obsidian adapters round-trip exact records through the reference authority", async () => { + const config: MdbaseConfig = { + spec_version: "0.3.0", + settings: { types_folder: "_types", explicit_type_keys: ["type"], default_strict: false, include_subfolders: true, exclude: ["_types"] }, + }; + const types = new Map([["task", { + name: "task", filePath: "_types/task.md", fields: {}, specProfile: "v0.3", + schema: { + type: "object", required: ["title"], + properties: { title: { type: "string" }, priority: { type: "integer" }, status: { enum: ["open", "done"] } }, + }, + }]]); + const source = new MemoryVault(); + const destination = new MemoryVault(); + // JSON is valid YAML and is also understood by the lightweight Obsidian mock. + const document = '---\r\n{"type": "task", "priority": "not-a-number", "status": "impossible"}\r\n---\r\n\r\nPreserve this invalid record — exactly'; + const file = await source.create("invalid-task.md", document); + const before = await validateFile(source as never, file, config, types); + assert.deepEqual(before.map((issue) => issue.code).sort(), ["schema_enum", "schema_required", "schema_type"]); + const hosted = new MemoryAuthority(); + const writerId = hosted.registerReplica({ name: "Obsidian writer", mode: "read_write" }); + const readerId = hosted.registerReplica({ name: "Second mirror", mode: "read_only" }); + const writer = new WritableDirectoryMirror(writerId, hosted.transport(writerId), { + fileSystem: new ObsidianMirrorFileSystem(source as never), stateStore: new MemoryMirrorStateStore(), + }); + const reader = new DirectoryMirror(readerId, hosted.transport(readerId), { + fileSystem: new ObsidianMirrorFileSystem(destination as never), stateStore: new MemoryMirrorStateStore(), + }); + const plan = await writer.inspect(); + assert.equal(plan.summary.blocking_issues, 0); + assert.equal(plan.summary.uploads, 1); + assert.equal((await writer.apply(plan)).status, "applied"); + await reader.sync(); + assert.equal(source.read("invalid-task.md"), document); + assert.equal(destination.read("invalid-task.md"), document); + assert.deepEqual(await validateFile(source as never, file, config, types), before); +}); + test("portable mirror materializes resources and records through Obsidian Vault APIs", async () => { const configuration = "spec_version: 0.3.0\n"; const noteType = "---\nkind: mdbase.type\n---\n";