From 812c92b8b8c92d51169d8070ac9b50244275f0d1 Mon Sep 17 00:00:00 2001 From: callumalpass Date: Wed, 16 Sep 2026 19:48:01 +1000 Subject: [PATCH 1/2] fix(sync): preserve BOMs and distinguish diagnostics from blockers --- PRODUCT.md | 1 + README.md | 4 +- docs/lossless-sdk-acceptance.md | 80 +++++++++++++++++++++++++++++++++ main.js | 6 +-- package.json | 1 + scripts/test-lossless-sync.mjs | 78 ++++++++++++++++++++++++++++++++ src/connectSync.ts | 3 +- src/syncPreview.ts | 4 +- src/syncUx.ts | 11 +++-- src/workspaceView.ts | 6 ++- test/syncPreview.test.ts | 10 +++++ test/syncUx.test.ts | 27 ++++++++++- test/v3-foundations.test.ts | 76 +++++++++++++++++++++++++------ 13 files changed, 279 insertions(+), 28 deletions(-) create mode 100644 docs/lossless-sdk-acceptance.md create mode 100644 scripts/test-lossless-sync.mjs 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..2ce1bc9 --- /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 `478353d28bb0` 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 `478353d28bb0` | 96 passed | 689,210 raw / 196,238 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 eight 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, 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..e6e26a9 --- /dev/null +++ b/scripts/test-lossless-sync.mjs @@ -0,0 +1,78 @@ +// 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---", + "# 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"; From 41c984a601c51b7ef57940983618341ac649fbbf Mon Sep 17 00:00:00 2001 From: callumalpass Date: Wed, 16 Sep 2026 19:55:26 +1000 Subject: [PATCH 2/2] test(sync): qualify valid BOM documents with final SDK candidate --- docs/lossless-sdk-acceptance.md | 8 ++++---- scripts/test-lossless-sync.mjs | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/lossless-sdk-acceptance.md b/docs/lossless-sdk-acceptance.md index 2ce1bc9..3dec99c 100644 --- a/docs/lossless-sdk-acceptance.md +++ b/docs/lossless-sdk-acceptance.md @@ -7,7 +7,7 @@ The tracked dependency pins remain `connect-sync` / `connect-protocol` 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 `478353d28bb0` makes readable malformed YAML a nonblocking +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. @@ -35,16 +35,16 @@ Node 24.19.0: | Installed SDK | Unit tests | Build / mobile | | --- | --- | --- | | Released beta.91 | 96 passed | 687,949 raw / 195,842 gzip bytes | -| Candidate from `478353d28bb0` | 96 passed | 689,210 raw / 196,238 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 eight exact round trips through +`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, and a valid sibling. It checks that the preview enables the transfers +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 diff --git a/scripts/test-lossless-sync.mjs b/scripts/test-lossless-sync.mjs index e6e26a9..a3abfe1 100644 --- a/scripts/test-lossless-sync.mjs +++ b/scripts/test-lossless-sync.mjs @@ -46,6 +46,8 @@ const documents = [ "---\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();