workspace-sync-C-pBiGKB.js
1 var C=Object.defineProperty;var $=(a,e,t)=>e in a?C(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var g=(a,e,t)=>$(a,typeof e!="symbol"?e+"":e,t);import{l as f}from"./global-imVxZN8Y.js";const v={available:!1,graphName:null,graphPath:null,lastChecked:new Date().toISOString()},W={enabled:!1,apiUrl:"http://127.0.0.1:12315/api",autoSync:!0,syncInterval:3e4},H=3e3,x=1e4;class z{constructor(){g(this,"settings",{...W});g(this,"connectionState",{...v})}setSettings(e){this.settings={...this.settings,...e}}getSettings(){return{...this.settings}}getConnectionState(){return{...this.connectionState}}async checkConnection(){if(!this.settings.enabled)return this.connectionState={available:!1,graphName:null,graphPath:null,lastChecked:new Date().toISOString(),error:"Logseq integration is disabled"},this.connectionState;try{const e=new AbortController,t=setTimeout(()=>e.abort(),H),s=await this.call("logseq.App.getCurrentGraph",[],e.signal);clearTimeout(t),this.connectionState={available:!0,graphName:(s==null?void 0:s.name)||null,graphPath:(s==null?void 0:s.path)||null,lastChecked:new Date().toISOString()},console.log("[Logseq] Connected to graph:",s==null?void 0:s.name)}catch(e){this.connectionState={available:!1,graphName:null,graphPath:null,lastChecked:new Date().toISOString(),error:e instanceof Error?e.message:"Connection failed"},console.warn("[Logseq] Connection check failed:",this.connectionState.error)}return this.connectionState}async getOrCreatePage(e){const t=await this.call("logseq.Editor.getPage",[e]);return t||(await this.call("logseq.Editor.createPage",[e,{},{redirect:!1}]),await this.call("logseq.Editor.getPage",[e]))}async getPageBlocksTree(e){return await this.call("logseq.Editor.getPageBlocksTree",[e])||[]}async getBlock(e,t=!0){return await this.call("logseq.Editor.getBlock",[e,{includeChildren:t}])}async insertBlock(e,t,s={}){return await this.call("logseq.Editor.insertBlock",[e,t,{before:s.before??!1,sibling:s.sibling??!1,properties:s.properties??{}}])}async updateBlock(e,t){await this.call("logseq.Editor.updateBlock",[e,t])}async deleteBlock(e){await this.call("logseq.Editor.removeBlock",[e])}async setBlockProperties(e,t){for(const[s,n]of Object.entries(t))await this.call("logseq.Editor.upsertBlockProperty",[e,s,n])}async getPageFirstBlock(e){return(await this.getPageBlocksTree(e))[0]||null}async appendBlockToPage(e,t){return await this.call("logseq.Editor.appendBlockInPage",[e,t])}async search(e){return await this.call("logseq.App.search",[e])||[]}async searchPages(e){if(!e||e.trim().length===0)return[];try{const t=await this.call("logseq.App.search",[e]);if(!t||!Array.isArray(t))return[];const s=new Set,n=[];for(const r of t){const o=r["page/original-name"]||r["page/name"];o&&!s.has(o.toLowerCase())&&(s.add(o.toLowerCase()),n.push({name:o,uuid:r["block/uuid"]}))}return n.slice(0,50)}catch(t){return console.warn("[Logseq] Page search failed:",t),[]}}async query(e){return await this.call("logseq.DB.q",[e])||[]}async getAllPageNames(){try{const e=await this.call("logseq.Editor.getAllPages",[]);if(console.log("[Logseq] getAllPages raw results:",e==null?void 0:e.length,"pages"),!e||!Array.isArray(e))return console.warn("[Logseq] getAllPages returned invalid data:",e),[];const t=e.filter(s=>!(s.journal===!0||s["journal?"]===!0)&&s.name).map(s=>({name:s.name.toLowerCase(),displayName:s.originalName||s["original-name"]||s.name})).sort((s,n)=>s.displayName.localeCompare(n.displayName));return console.log("[Logseq] getAllPageNames processed:",t.length,"non-journal pages"),t.length>0&&console.log("[Logseq] Sample pages:",t.slice(0,5).map(s=>s.displayName)),t}catch(e){return console.error("[Logseq] getAllPageNames failed:",e),[]}}async getCurrentGraph(){return await this.call("logseq.App.getCurrentGraph",[])}async call(e,t=[],s){const n={"Content-Type":"application/json"};this.settings.authToken&&(n.Authorization=`Bearer ${this.settings.authToken}`);const r=s?void 0:new AbortController,o=r?setTimeout(()=>r.abort(),x):void 0;try{const i=await fetch(this.settings.apiUrl,{method:"POST",headers:n,body:JSON.stringify({method:e,args:t}),signal:s||(r==null?void 0:r.signal)});if(o&&clearTimeout(o),!i.ok){const p=await i.text();throw new Error(`Logseq API error (${i.status}): ${p}`)}const c=await i.json();if(c!=null&&c.error)throw new Error(`Logseq API error: ${c.error}`);return c}catch(i){throw o&&clearTimeout(o),i instanceof Error&&i.name==="AbortError"?new Error("Logseq API request timed out"):i}}}let N=null;function re(){return N||(N=new z),N}const B={hasAccess:!1,directoryPath:null,graphName:null},q="mnemonic-filesystem",y="handles",O="logseq-graph",K=1;function G(){return typeof window<"u"&&"showDirectoryPicker"in window&&typeof window.showDirectoryPicker=="function"}function J(a){return a.replace(/\.\./g,"").replace(/[/\\]/g,"").replace(/[<>:"|?*]/g,"")}function Z(a,e){const t=J(e),s=Date.now(),n=t.lastIndexOf(".");let r,o;n>0?(r=t.slice(0,n),o=t.slice(n+1)):(r=t,o="");const i=o?`${r}_${s}_0.${o}`:`${r}_${s}_0`;return`assets/mnemonic/${a}/${i}`}class V{constructor(){g(this,"directoryHandle",null);g(this,"state",{...B});g(this,"listeners",new Set);g(this,"activeBlobUrls",new Set)}getState(){return{...this.state}}isSupported(){return G()}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){const e=this.getState();for(const t of this.listeners)t(e)}updateState(e){this.state={...this.state,...e},this.notify()}async openDB(){return new Promise((e,t)=>{const s=indexedDB.open(q,K);s.onerror=()=>{t(new Error("Failed to open IndexedDB"))},s.onsuccess=()=>{e(s.result)},s.onupgradeneeded=n=>{const r=n.target.result;r.objectStoreNames.contains(y)||r.createObjectStore(y)}})}async persistHandle(){if(!this.directoryHandle)return;const e=await this.openDB();return new Promise((t,s)=>{const n=e.transaction(y,"readwrite"),o=n.objectStore(y).put(this.directoryHandle,O);o.onsuccess=()=>{e.close(),t()},o.onerror=()=>{e.close(),s(new Error("Failed to persist directory handle"))},n.onerror=()=>e.close(),n.onabort=()=>e.close()})}async requestAccess(){if(!this.isSupported())return this.updateState({hasAccess:!1,error:"File System Access API is not supported in this browser"}),!1;try{return this.directoryHandle=await window.showDirectoryPicker({mode:"readwrite",startIn:"documents"}),await this.persistHandle(),this.updateState({hasAccess:!0,directoryPath:this.directoryHandle.name,graphName:this.directoryHandle.name,error:void 0}),!0}catch(e){return this.updateState({hasAccess:!1,error:e instanceof Error?e.message:"Failed to get directory access"}),!1}}async restoreAccess(){try{const e=await this.openDB();return new Promise(t=>{const s=e.transaction(y,"readonly"),r=s.objectStore(y).get(O);r.onsuccess=async()=>{e.close();const o=r.result;if(!o){this.updateState({hasAccess:!1}),t(!1);return}try{await o.queryPermission({mode:"readwrite"})==="granted"?(this.directoryHandle=o,this.updateState({hasAccess:!0,directoryPath:o.name,graphName:o.name}),t(!0)):await o.requestPermission({mode:"readwrite"})==="granted"?(this.directoryHandle=o,this.updateState({hasAccess:!0,directoryPath:o.name,graphName:o.name}),t(!0)):(this.updateState({hasAccess:!1}),t(!1))}catch{this.updateState({hasAccess:!1}),t(!1)}},r.onerror=()=>{e.close(),this.updateState({hasAccess:!1}),t(!1)},s.onerror=()=>e.close(),s.onabort=()=>e.close()})}catch{return this.updateState({hasAccess:!1}),!1}}async revokeAccess(){this.revokeAllBlobUrls(),this.directoryHandle=null,this.updateState({hasAccess:!1,directoryPath:null,graphName:null,error:void 0});try{const e=await this.openDB();return new Promise(t=>{const s=e.transaction(y,"readwrite"),r=s.objectStore(y).delete(O);r.onsuccess=()=>{e.close(),t()},r.onerror=()=>{e.close(),t()},s.onerror=()=>e.close(),s.onabort=()=>e.close()})}catch{}}async writeAsset(e,t,s){if(!this.directoryHandle)throw new Error("No directory access. Call requestAccess() first.");const n=Z(e,t),r=n.split("/"),o=r.pop();let i=this.directoryHandle;for(const m of r)i=await i.getDirectoryHandle(m,{create:!0});const p=await(await i.getFileHandle(o,{create:!0})).createWritable();return await p.write(s),await p.close(),n}async fileExists(e){if(!this.directoryHandle)return!1;try{const t=e.split("/"),s=t.pop();let n=this.directoryHandle;for(const r of t)n=await n.getDirectoryHandle(r);return await n.getFileHandle(s),!0}catch{return!1}}async getFileUrl(e){if(!this.directoryHandle)return null;try{const t=e.split("/"),s=t.pop();let n=this.directoryHandle;for(const c of t)n=await n.getDirectoryHandle(c);const o=await(await n.getFileHandle(s)).getFile(),i=URL.createObjectURL(o);return this.activeBlobUrls.add(i),i}catch{return null}}revokeBlobUrl(e){this.activeBlobUrls.has(e)&&(URL.revokeObjectURL(e),this.activeBlobUrls.delete(e))}revokeAllBlobUrls(){for(const e of this.activeBlobUrls)URL.revokeObjectURL(e);this.activeBlobUrls.clear()}async readFile(e){if(!this.directoryHandle)return null;try{const t=e.split("/"),s=t.pop();let n=this.directoryHandle;for(const o of t)n=await n.getDirectoryHandle(o);return await(await n.getFileHandle(s)).getFile()}catch{return null}}async deleteFile(e){if(!this.directoryHandle)return!1;try{const t=e.split("/"),s=t.pop();let n=this.directoryHandle;for(const r of t)n=await n.getDirectoryHandle(r);return await n.removeEntry(s),!0}catch{return!1}}}let R=null;function ie(){return R||(R=new V),R}const b={enabled:!1},ce={4:{label:"P1",color:"#d1453b",dots:4},3:{label:"P2",color:"#eb8909",dots:3},2:{label:"P3",color:"#246fe0",dots:2},1:{label:"P4",color:"#666666",dots:1}},j="todoistSettings",w="todoistOAuthState",I={authorizationEndpoint:"https://todoist.com/oauth/authorize",tokenEndpoint:"https://todoist.com/oauth/access_token",scope:"data:read_write"};function L(a){const e=new Uint8Array(a);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}function _(){return L(64)}async function Y(a){const t=new TextEncoder().encode(a),s=await crypto.subtle.digest("SHA-256",t);return btoa(String.fromCharCode(...new Uint8Array(s))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}class Q{constructor(){g(this,"clientId",null);g(this,"clientSecret",null)}setOAuthCredentials(e,t){this.clientId=e,this.clientSecret=t||null}isOAuthConfigured(){return this.clientId!==null}async loadSettings(){try{const t=(await f.storage.local.get(j))[j];return t?{...b,...t}:b}catch(e){return console.error("[Mnemonic/Todoist] Failed to load settings:",e),b}}async saveSettings(e){try{const s={...await this.loadSettings(),...e};await f.storage.local.set({[j]:s})}catch(t){throw console.error("[Mnemonic/Todoist] Failed to save settings:",t),t}}async clearSettings(){try{await f.storage.local.remove([j,w])}catch(e){throw console.error("[Mnemonic/Todoist] Failed to clear settings:",e),e}}async getAccessToken(){return(await this.loadSettings()).accessToken||null}async isAuthenticated(){return await this.getAccessToken()!==null}async initiateOAuth(){if(!this.clientId)throw new Error("OAuth not configured. Call setOAuthCredentials() first or use API token authentication.");const e=L(32),t=_();await Y(t);const s={state:e,codeVerifier:t,createdAt:Date.now()};await f.storage.local.set({[w]:s});const n=new URLSearchParams({client_id:this.clientId,scope:I.scope,state:e});return`${I.authorizationEndpoint}?${n.toString()}`}async launchOAuthFlow(){if(!this.clientId)return{success:!1,error:"OAuth not configured"};try{const e=L(32),t=_(),s={state:e,codeVerifier:t,createdAt:Date.now()};await f.storage.local.set({[w]:s});const n=f.identity.getRedirectURL(),r=new URL(I.authorizationEndpoint);r.searchParams.set("client_id",this.clientId),r.searchParams.set("scope",I.scope),r.searchParams.set("state",e),r.searchParams.set("redirect_uri",n);const o=await f.identity.launchWebAuthFlow({url:r.toString(),interactive:!0}),i=new URL(o),c=i.searchParams.get("code"),p=i.searchParams.get("state"),m=i.searchParams.get("error");if(m)return{success:!1,error:`OAuth error: ${m}`};if(!c)return{success:!1,error:"No authorization code received"};const P=await this.getStoredOAuthState();if(!P||P.state!==p)return{success:!1,error:"State mismatch - possible CSRF attack"};const S=await this.exchangeCodeForToken(c,n);return S.success?(await this.saveSettings({enabled:!0,authMethod:"oauth",accessToken:S.accessToken}),await f.storage.local.remove(w),{success:!0}):{success:!1,error:S.error}}catch(e){return{success:!1,error:e instanceof Error?e.message:"OAuth flow failed"}}}async exchangeCodeForToken(e,t){if(!this.clientId||!this.clientSecret)return{success:!1,error:"OAuth credentials not configured"};try{const s=await fetch(I.tokenEndpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:this.clientId,client_secret:this.clientSecret,code:e,redirect_uri:t})});return s.ok?{success:!0,accessToken:(await s.json()).access_token}:{success:!1,error:`Token exchange failed: ${await s.text()}`}}catch(s){return{success:!1,error:s instanceof Error?s.message:"Token exchange failed"}}}async getStoredOAuthState(){try{const t=(await f.storage.local.get(w))[w];return t?Date.now()-t.createdAt>5*60*1e3?(await f.storage.local.remove(w),null):t:null}catch{return null}}async authenticateWithApiToken(e){if(!e||e.trim().length===0)return{success:!1,error:"API token is required"};try{const t=await fetch("https://api.todoist.com/api/v1/projects",{headers:{Authorization:`Bearer ${e}`}});return t.ok?(await this.saveSettings({enabled:!0,authMethod:"api_token",accessToken:e}),{success:!0}):t.status===401||t.status===403?{success:!1,error:"Invalid API token"}:{success:!1,error:`API error: ${t.statusText}`}}catch(t){return{success:!1,error:t instanceof Error?t.message:"Connection failed"}}}async disconnect(){await this.clearSettings()}async getAuthMethod(){return(await this.loadSettings()).authMethod||null}}const T=new Q;var A=(a=>(a.UNAUTHORIZED="UNAUTHORIZED",a.RATE_LIMITED="RATE_LIMITED",a.NOT_FOUND="NOT_FOUND",a.BAD_REQUEST="BAD_REQUEST",a.NETWORK_ERROR="NETWORK_ERROR",a.OFFLINE="OFFLINE",a.TIMEOUT="TIMEOUT",a.SERVER_ERROR="SERVER_ERROR",a.UNKNOWN="UNKNOWN",a))(A||{});class d extends Error{constructor(e,t,s=!1,n){super(e),this.code=t,this.retryable=s,this.retryAfter=n,this.name="TodoistError"}getUserMessage(){switch(this.code){case"UNAUTHORIZED":return"Authentication failed. Please reconnect your Todoist account.";case"RATE_LIMITED":return this.retryAfter?`Rate limited. Please try again in ${this.retryAfter} seconds.`:"Rate limited. Please try again in a moment.";case"NOT_FOUND":return"The requested resource was not found.";case"BAD_REQUEST":return"Invalid request. Please check your input.";case"NETWORK_ERROR":return"Network error. Please check your internet connection.";case"OFFLINE":return"You appear to be offline. Please check your connection.";case"TIMEOUT":return"Request timed out. Please try again.";case"SERVER_ERROR":return"Todoist server error. Please try again later.";default:return"An unexpected error occurred."}}}function D(a){if(typeof navigator<"u"&&!navigator.onLine)return new d("Device is offline","OFFLINE",!0);if(a instanceof TypeError&&(a.message.includes("fetch")||a.message.includes("network")))return new d("Network request failed","NETWORK_ERROR",!0);if(a instanceof DOMException&&a.name==="AbortError")return new d("Request timed out","TIMEOUT",!0);if(a instanceof Response)return F(a);if(X(a)){const e={status:a.status,statusText:a.statusText||"",headers:new Headers};return F(e)}return a instanceof Error?a.message.includes("401")||a.message.includes("unauthorized")?new d(a.message,"UNAUTHORIZED",!1):a.message.includes("429")||a.message.includes("rate limit")?new d(a.message,"RATE_LIMITED",!0):new d(a.message,"UNKNOWN",!1):new d("An unexpected error occurred","UNKNOWN",!1)}function F(a){const e=a.status;switch(e){case 400:return new d("Bad request","BAD_REQUEST",!1);case 401:case 403:return new d("Authentication failed","UNAUTHORIZED",!1);case 404:return new d("Resource not found","NOT_FOUND",!1);case 429:{const t=a.headers.get("Retry-After"),s=t?parseInt(t,10):void 0;return new d("Rate limited","RATE_LIMITED",!0,s)}case 500:case 502:case 503:case 504:return new d("Server error","SERVER_ERROR",!0);default:return new d(`HTTP error ${e}`,"UNKNOWN",e>=500)}}function X(a){return typeof a=="object"&&a!==null&&"status"in a&&typeof a.status=="number"}async function l(a,e={}){const{maxRetries:t=3,baseDelay:s=1e3,maxDelay:n=1e4,shouldRetry:r=i=>i.retryable}=e;let o=null;for(let i=0;i<=t;i++)try{return await a()}catch(c){if(o=c instanceof d?c:D(c),i<t&&r(o)){let p=Math.min(s*Math.pow(2,i),n);o.retryAfter&&(p=o.retryAfter*1e3),console.log(`[Mnemonic/Todoist] Retrying in ${p}ms (attempt ${i+1}/${t})`),await ee(p)}else throw o}throw o||new d("Retry failed","UNKNOWN",!1)}function ee(a){return new Promise(e=>setTimeout(e,a))}const U="https://api.todoist.com/api/v1",M="Mnemonic";function te(a){return a.replace(/_([a-z])/g,(e,t)=>t.toUpperCase())}function E(a){if(a==null)return a;if(Array.isArray(a))return a.map(e=>E(e));if(typeof a=="object"){const e={};for(const[t,s]of Object.entries(a)){const n=te(t);e[n]=E(s)}return e}return a}async function u(a,e={},t){const s=await fetch(`${U}${a}`,{...e,headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json",...e.headers}});if(!s.ok)throw s;if(s.status===204)return!0;const n=await s.json();return E(n)}async function k(a,e){const t=[];let s=null;do{const n=a.includes("?")?"&":"?",r=s?`${U}${a}${n}cursor=${encodeURIComponent(s)}`:`${U}${a}`,o=await fetch(r,{headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"}});if(!o.ok)throw o;const i=await o.json();if(i&&typeof i=="object"&&"results"in i){const c=E(i.results);t.push(...c),s=i.next_cursor||null}else Array.isArray(i)&&t.push(...E(i)),s=null}while(s);return t}class se{constructor(){g(this,"masterProjectId",null);g(this,"accessToken",null)}async initializeFromStorage(){const e=await T.loadSettings();return!e.enabled||!e.accessToken?{connected:!1,masterProjectName:M,error:"Not connected to Todoist"}:this.initialize(e.accessToken)}async initialize(e){try{this.accessToken=e;let s=(await l(()=>this.getProjects())).find(n=>n.name===M&&!n.parentId);return s||(s=await l(()=>this.addProject({name:M}))),this.masterProjectId=s.id,await T.saveSettings({masterProjectId:s.id,lastSync:new Date().toISOString()}),{connected:!0,authMethod:(await T.loadSettings()).authMethod,accessToken:e,masterProjectId:s.id,masterProjectName:s.name,lastSync:new Date().toISOString()}}catch(t){const s=t instanceof d?t:D(t);return s.code===A.UNAUTHORIZED&&(await T.clearSettings(),this.masterProjectId=null,this.accessToken=null),{connected:!1,masterProjectName:M,error:s.getUserMessage()}}}async disconnect(){await T.disconnect(),this.masterProjectId=null,this.accessToken=null}isConnected(){return this.accessToken!==null&&this.masterProjectId!==null}getMasterProjectId(){return this.masterProjectId}ensureInitialized(){if(!this.accessToken||!this.masterProjectId)throw new d("Todoist client not initialized",A.UNAUTHORIZED,!1);return this.accessToken}async getProjects(){const e=this.accessToken;if(!e)throw new d("Not authenticated",A.UNAUTHORIZED,!1);return k("/projects",e)}async getWorkspaceProjects(){const e=this.ensureInitialized();console.log("[Mnemonic/Todoist] getWorkspaceProjects: Fetching all projects from Todoist API..."),console.log("[Mnemonic/Todoist] getWorkspaceProjects: masterProjectId =",this.masterProjectId);const t=await l(()=>k("/projects",e));console.log("[Mnemonic/Todoist] getWorkspaceProjects: Total projects from API:",t.length),console.log("[Mnemonic/Todoist] getWorkspaceProjects: All projects:",t.map(o=>({id:o.id,name:o.name,parentId:o.parentId})));const s=t.filter(o=>o.parentId===this.masterProjectId);console.log("[Mnemonic/Todoist] getWorkspaceProjects: Mnemonic children (direct):",s.map(o=>({id:o.id,name:o.name})));const n=t.filter(o=>s.some(i=>i.id===o.parentId));console.log("[Mnemonic/Todoist] getWorkspaceProjects: Mnemonic grandchildren:",n.map(o=>({id:o.id,name:o.name})));const r=[...s,...n];return console.log("[Mnemonic/Todoist] getWorkspaceProjects: Returning",r.length,"workspace projects"),r}async addProject(e){const t=this.accessToken;if(!t)throw new d("Not authenticated",A.UNAUTHORIZED,!1);const s={name:e.name};return e.parentId&&(s.parent_id=e.parentId),u("/projects",{method:"POST",body:JSON.stringify(s)},t)}async createWorkspaceProject(e,t,s){const n=this.ensureInitialized(),r=t==="child"&&s?s:this.masterProjectId;return await l(()=>u("/projects",{method:"POST",body:JSON.stringify({name:e,parent_id:r})},n))}async updateProject(e,t){const s=this.ensureInitialized();return await l(()=>u(`/projects/${e}`,{method:"POST",body:JSON.stringify({name:t})},s))}async deleteProject(e){const t=this.ensureInitialized();return await l(()=>u(`/projects/${e}`,{method:"DELETE"},t))}async getProject(e){const t=this.ensureInitialized();return await l(()=>u(`/projects/${e}`,{},t))}async getProjectTasks(e){const t=this.ensureInitialized();return await l(()=>k(`/tasks?project_id=${e}`,t))}async getAllWorkspaceTasks(){const e=this.ensureInitialized(),t=await this.getWorkspaceProjects(),n=[this.masterProjectId,...t.map(o=>o.id)].map(o=>l(()=>k(`/tasks?project_id=${o}`,e)));return(await Promise.all(n)).flat()}async getTask(e){const t=this.ensureInitialized();return await l(()=>u(`/tasks/${e}`,{},t))}async createTask(e,t){const s=this.ensureInitialized();return await l(()=>u("/tasks",{method:"POST",body:JSON.stringify({content:t.content,description:t.description,project_id:e,due_string:t.dueString,due_date:t.dueDate,due_datetime:t.dueDatetime,priority:t.priority,labels:t.labels,section_id:t.sectionId})},s))}async updateTask(e){const t=this.ensureInitialized();return await l(()=>u(`/tasks/${e.taskId}`,{method:"POST",body:JSON.stringify({content:e.content,description:e.description,due_string:e.dueString,due_date:e.dueDate,due_datetime:e.dueDatetime,priority:e.priority,labels:e.labels})},t))}async completeTask(e){const t=this.ensureInitialized();return await l(()=>u(`/tasks/${e}/close`,{method:"POST"},t))}async reopenTask(e){const t=this.ensureInitialized();return await l(()=>u(`/tasks/${e}/reopen`,{method:"POST"},t))}async deleteTask(e){const t=this.ensureInitialized();return await l(()=>u(`/tasks/${e}`,{method:"DELETE"},t))}async getProjectSections(e){const t=this.ensureInitialized();return await l(()=>k(`/sections?project_id=${e}`,t))}async createSection(e,t){const s=this.ensureInitialized();return await l(()=>u("/sections",{method:"POST",body:JSON.stringify({name:t,project_id:e})},s))}async updateSection(e,t){const s=this.ensureInitialized();return await l(()=>u(`/sections/${e}`,{method:"POST",body:JSON.stringify({name:t})},s))}async deleteSection(e){const t=this.ensureInitialized();return await l(()=>u(`/sections/${e}`,{method:"DELETE"},t))}async getLabels(){const e=this.ensureInitialized();return await l(()=>k("/labels",e))}async createLabel(e,t){const s=this.ensureInitialized();return await l(()=>u("/labels",{method:"POST",body:JSON.stringify({name:e,color:t})},s))}async updateLabel(e,t,s){const n=this.ensureInitialized();return await l(()=>u(`/labels/${e}`,{method:"POST",body:JSON.stringify({name:t,color:s})},n))}async deleteLabel(e){const t=this.ensureInitialized();return await l(()=>u(`/labels/${e}`,{method:"DELETE"},t))}}const h=new se;class ne{constructor(){g(this,"mappings",new Map);g(this,"initialized",!1)}async loadMappings(){var e;console.log("[Mnemonic/Todoist] loadMappings: Loading mappings from storage...");try{const t=await T.loadSettings();console.log("[Mnemonic/Todoist] loadMappings: Settings loaded, projectMappings count:",((e=t.projectMappings)==null?void 0:e.length)||0),t.projectMappings&&(this.mappings=new Map(t.projectMappings.map(s=>[s.workspaceId,s])),console.log("[Mnemonic/Todoist] loadMappings: Loaded mappings:",Array.from(this.mappings.values()).map(s=>({workspaceId:s.workspaceId,workspaceName:s.workspaceName,todoistProjectId:s.todoistProjectId})))),this.initialized=!0,console.log("[Mnemonic/Todoist] loadMappings: Initialization complete, total mappings:",this.mappings.size)}catch(t){console.error("[Mnemonic/Todoist] Failed to load mappings:",t),this.mappings=new Map}}async saveMappings(){const e=Array.from(this.mappings.values());await T.saveSettings({projectMappings:e})}async ensureLoaded(){this.initialized||await this.loadMappings()}async getProjectId(e){var t;return await this.ensureLoaded(),(t=this.mappings.get(e))==null?void 0:t.todoistProjectId}async getWorkspaceId(e){await this.ensureLoaded();for(const t of this.mappings.values())if(t.todoistProjectId===e)return t.workspaceId}async getMapping(e){return await this.ensureLoaded(),this.mappings.get(e)}async syncWorkspace(e,t){if(console.log("[Mnemonic/Todoist] syncWorkspace: Starting sync for workspace:",{id:e.id,name:e.name,type:e.type}),await this.ensureLoaded(),!h.isConnected())throw console.log("[Mnemonic/Todoist] syncWorkspace: Client not connected, throwing error"),new d("Todoist client not connected","UNAUTHORIZED",!1);const s=this.mappings.get(e.id);if(console.log("[Mnemonic/Todoist] syncWorkspace: Existing mapping for workspace:",s?{todoistProjectId:s.todoistProjectId,workspaceName:s.workspaceName}:"none"),s){if(s.workspaceName!==e.name){console.log("[Mnemonic/Todoist] syncWorkspace: Name changed, updating project");try{await h.updateProject(s.todoistProjectId,e.name),s.workspaceName=e.name,s.todoistProjectName=e.name,s.lastSynced=new Date().toISOString(),await this.saveMappings()}catch(o){if((o instanceof d?o:D(o)).code==="NOT_FOUND")return console.log("[Mnemonic/Todoist] syncWorkspace: Project not found in Todoist, removing mapping and re-syncing"),this.mappings.delete(e.id),this.syncWorkspace(e,t);throw o}}return console.log("[Mnemonic/Todoist] syncWorkspace: Returning existing mapping"),s}console.log("[Mnemonic/Todoist] syncWorkspace: No existing mapping, checking for existing Todoist projects by name...");let n;try{const o=await h.getWorkspaceProjects();console.log("[Mnemonic/Todoist] syncWorkspace: Found",o.length,"existing workspace projects"),console.log("[Mnemonic/Todoist] syncWorkspace: Looking for project matching workspace name:",e.name.toLowerCase());const i=o.find(c=>c.name.toLowerCase()===e.name.toLowerCase());i?(console.log(`[Mnemonic/Todoist] syncWorkspace: Found existing project "${i.name}" (id: ${i.id}) for workspace "${e.name}"`),n=i):(console.log("[Mnemonic/Todoist] syncWorkspace: No matching project found, creating new project..."),n=await h.createWorkspaceProject(e.name,e.type,t),console.log("[Mnemonic/Todoist] syncWorkspace: Created new project:",{id:n.id,name:n.name}))}catch(o){console.error("[Mnemonic/Todoist] syncWorkspace: Failed to check for existing projects:",o),console.log("[Mnemonic/Todoist] syncWorkspace: Fallback - creating new project..."),n=await h.createWorkspaceProject(e.name,e.type,t)}const r={workspaceId:e.id,workspaceName:e.name,workspaceType:e.type,todoistProjectId:n.id,todoistProjectName:n.name,parentProjectId:t,lastSynced:new Date().toISOString()};return this.mappings.set(e.id,r),await this.saveMappings(),r}async syncAllWorkspaces(e){await this.ensureLoaded();const t={success:!0,tasksUpdated:0,tasksFailed:0,projectsCreated:0,errors:[]};if(!h.isConnected())return t.success=!1,t.errors.push("Todoist client not connected"),t;const s=e.filter(i=>i.type==="parent"),n=e.filter(i=>i.type==="standalone"),r=e.filter(i=>i.type==="child"),o=new Set(this.mappings.keys());for(const i of s)try{const c=await this.syncWorkspace(i);o.has(i.id)||t.projectsCreated++;const p=r.filter(m=>m.parentId===i.id);for(const m of p)try{await this.syncWorkspace(m,c.todoistProjectId),o.has(m.id)||t.projectsCreated++}catch(P){const S=P instanceof Error?P.message:"Unknown error";t.errors.push(`Failed to sync child workspace ${m.name}: ${S}`),t.success=!1}}catch(c){const p=c instanceof Error?c.message:"Unknown error";t.errors.push(`Failed to sync parent workspace ${i.name}: ${p}`),t.success=!1}for(const i of n)try{await this.syncWorkspace(i),o.has(i.id)||t.projectsCreated++}catch(c){const p=c instanceof Error?c.message:"Unknown error";t.errors.push(`Failed to sync standalone workspace ${i.name}: ${p}`),t.success=!1}return t}async removeMapping(e,t=!1){await this.ensureLoaded();const s=this.mappings.get(e);if(s){if(t&&h.isConnected())try{await h.deleteProject(s.todoistProjectId)}catch(n){console.error("[Mnemonic/Todoist] Failed to delete Todoist project:",n)}this.mappings.delete(e),await this.saveMappings()}}async getAllMappings(){return await this.ensureLoaded(),Array.from(this.mappings.values())}async reconcile(){if(await this.ensureLoaded(),!h.isConnected())return 0;try{const e=await h.getWorkspaceProjects(),t=new Set(e.map(r=>r.id)),s=h.getMasterProjectId();s&&t.add(s);let n=0;for(const[r,o]of this.mappings)t.has(o.todoistProjectId)||(this.mappings.delete(r),n++);return n>0&&await this.saveMappings(),n}catch(e){return console.error("[Mnemonic/Todoist] Failed to reconcile mappings:",e),0}}async findPotentialMatches(e){if(console.log("[Mnemonic/Todoist] findPotentialMatches: Starting with",e.length,"workspaces"),await this.ensureLoaded(),!h.isConnected())return console.log("[Mnemonic/Todoist] findPotentialMatches: Client not connected, returning empty"),new Map;const t=new Map;try{const s=await h.getWorkspaceProjects();console.log("[Mnemonic/Todoist] findPotentialMatches: Retrieved",s.length,"Todoist projects"),console.log("[Mnemonic/Todoist] findPotentialMatches: Project names:",s.map(r=>r.name));const n=e.filter(r=>!this.mappings.has(r.id));console.log("[Mnemonic/Todoist] findPotentialMatches: Unmapped workspaces:",n.length),console.log("[Mnemonic/Todoist] findPotentialMatches: Unmapped workspace names:",n.map(r=>r.name));for(const r of n){const o=s.find(i=>i.name.toLowerCase()===r.name.toLowerCase());o?(console.log(`[Mnemonic/Todoist] findPotentialMatches: MATCH - workspace "${r.name}" -> project "${o.name}" (${o.id})`),t.set(r.id,o)):console.log(`[Mnemonic/Todoist] findPotentialMatches: NO MATCH for workspace "${r.name}"`)}}catch(s){console.error("[Mnemonic/Todoist] findPotentialMatches: Failed to find potential matches:",s)}return console.log("[Mnemonic/Todoist] findPotentialMatches: Returning",t.size,"matches"),t}async linkToExistingProject(e,t){if(await this.ensureLoaded(),!h.isConnected())throw new d("Todoist client not connected","UNAUTHORIZED",!1);const s=await h.getProject(t),n={workspaceId:e.id,workspaceName:e.name,workspaceType:e.type,todoistProjectId:s.id,todoistProjectName:s.name,parentProjectId:s.parentId||void 0,lastSynced:new Date().toISOString()};return this.mappings.set(e.id,n),await this.saveMappings(),n}async clearAllMappings(){this.mappings.clear(),await this.saveMappings(),this.initialized=!1}async getAllMappingsMap(){return await this.ensureLoaded(),new Map(this.mappings)}async restoreMappingsFromWorkspaces(e){console.log("[Mnemonic/Todoist] restoreMappingsFromWorkspaces: Starting with",e.length,"workspaces"),await this.ensureLoaded();let t=0;for(const s of e){if(!s.todoistProjectId){console.log(`[Mnemonic/Todoist] restoreMappingsFromWorkspaces: Skipping "${s.name}" - no todoistProjectId`);continue}if(this.mappings.has(s.id)){console.log(`[Mnemonic/Todoist] restoreMappingsFromWorkspaces: Skipping "${s.name}" - already has mapping`);continue}console.log(`[Mnemonic/Todoist] restoreMappingsFromWorkspaces: Restoring mapping for "${s.name}" -> ${s.todoistProjectId}`);const n={workspaceId:s.id,workspaceName:s.name,workspaceType:s.type,todoistProjectId:s.todoistProjectId,todoistProjectName:s.name,lastSynced:new Date().toISOString()};this.mappings.set(s.id,n),t++}return t>0?(await this.saveMappings(),console.log(`[Mnemonic/Todoist] restoreMappingsFromWorkspaces: Restored ${t} mappings from synced workspaces`)):console.log("[Mnemonic/Todoist] restoreMappingsFromWorkspaces: No mappings to restore"),t}async detectAndLinkExistingProjects(e){if(console.log("[Mnemonic/Todoist] detectAndLinkExistingProjects: Starting with",e.length,"workspaces"),await this.ensureLoaded(),!h.isConnected())return console.log("[Mnemonic/Todoist] detectAndLinkExistingProjects: Client not connected, returning empty"),new Map;const t=new Map;try{console.log("[Mnemonic/Todoist] detectAndLinkExistingProjects: Calling findPotentialMatches...");const s=await this.findPotentialMatches(e);console.log("[Mnemonic/Todoist] detectAndLinkExistingProjects: findPotentialMatches returned",s.size,"matches");for(const[n,r]of s){const o=e.find(c=>c.id===n);if(!o){console.log("[Mnemonic/Todoist] detectAndLinkExistingProjects: Could not find workspace for id:",n);continue}console.log(`[Mnemonic/Todoist] detectAndLinkExistingProjects: Creating mapping for "${o.name}" -> "${r.name}" (${r.id})`);const i={workspaceId:o.id,workspaceName:o.name,workspaceType:o.type,todoistProjectId:r.id,todoistProjectName:r.name,parentProjectId:r.parentId||void 0,lastSynced:new Date().toISOString()};this.mappings.set(n,i),t.set(n,i)}t.size>0?(await this.saveMappings(),console.log(`[Mnemonic/Todoist] detectAndLinkExistingProjects: Auto-linked ${t.size} workspaces to existing projects`)):console.log("[Mnemonic/Todoist] detectAndLinkExistingProjects: No new mappings created")}catch(s){console.error("[Mnemonic/Todoist] detectAndLinkExistingProjects: Failed to detect and link existing projects:",s)}return t}}const le=new ne;export{W as D,ce as P,d as T,ie as a,h as b,re as g,D as h,G as i,T as t,le as w};