aem-mcp-server
Version:
AEM MCP server. Chat with your AEM instance for content, component, and asset operations.
2 lines • 61.5 kB
JavaScript
"use strict";import{getAEMConfig as k,isValidContentPath as v}from"./aem.config.js";import{AEM_ERROR_CODES as P,createAEMError as $,createSuccessResponse as y,handleAEMHttpError as M,safeExecute as C}from"./aem.errors.js";import{AEMFetch as J}from"./aem.fetch.js";import{filterNodeTree as U,filterProperties as H}from"./aem.filter.js";import{ContentFragmentManager as G}from"./aem.content-fragments.js";import{ExperienceFragmentManager as Q}from"./aem.experience-fragments.js";import{LOGGER as p}from"../utils/logger.js";export class AEMConnector{isInitialized;isAEMaaCS;config;aemConfig;fetch;contentFragments;experienceFragments;constructor(e){this.isInitialized=!1,this.config=this.loadConfig(e),this.aemConfig=k({}),this.isAEMaaCS=this.isConfigAEMaaCS(),this.fetch=new J({host:this.config.aem.host,auth:this.config.aem.auth,timeout:this.aemConfig.queries.timeoutMs}),this.contentFragments=new G(this.fetch,this.isAEMaaCS),this.experienceFragments=new Q(this.fetch,this.config.aem.host)}async init(){await this.fetch.init(),this.isInitialized=!0}isConfigAEMaaCS(){return!!(this.config.aem.auth.clientId&&this.config.aem.auth.clientSecret)}loadConfig(e={}){let t;return e.id&&e.secret?t={clientId:e.id,clientSecret:e.secret}:t={username:e.user||"admin",password:e.pass||"admin"},{aem:{host:e.host||"http://localhost:4502",author:e.host||"http://localhost:4502",publish:"http://localhost:4503",auth:t,endpoints:{content:"/content",dam:"/content/dam",query:"/bin/querybuilder.json",crxde:"/crx/de",jcr:""}},mcp:{name:"NAEM MCP Server",version:"1.0.0"}}}getPreviewUrl(e){return`${this.config.aem.host}${e}.html?wcmmode=disabled`}async testConnection(){const e=await this.testAEMConnection(),t=e?await this.testAuthConnection():!1;return{aem:e,auth:t}}async testAEMConnection(){try{this.isInitialized||await this.init();const e="/libs/granite/core/content/login.html";p.log("Testing AEM connection to:",e);const t=await this.fetch.get(e,void 0,void 0,5e3,!0);return p.log("\u2705 AEM connection successful!"),!0}catch(e){return p.error("\u274C AEM connection failed:",e.message),!1}}async testAuthConnection(){try{this.isInitialized||await this.init();const e="/libs/granite/security/currentuser.json";p.log("Testing AEM authentication connection to:",e);const t=await this.fetch.get(e,void 0,void 0,5e3);return p.log("\u2705 AEM authentication connection successful!"),!0}catch(e){return p.error("\u274C AEM authentication connection failed:",e.message),!1}}validateComponentProps(e,t,n){const o=[],r=[];return t==="text"&&!n.text&&!n.richText&&o.push("Text component should have text or richText property"),t==="image"&&!n.fileReference&&!n.src&&r.push("Image component requires fileReference or src property"),{valid:r.length===0,errors:r,warnings:o,componentType:t,propsValidated:Object.keys(n).length}}async updateComponent(e){return C(async()=>{if(!e.componentPath||typeof e.componentPath!="string")throw $(P.INVALID_PARAMETERS,"Component path is required and must be a string");if(!e.properties||typeof e.properties!="object")throw $(P.INVALID_PARAMETERS,"Properties are required and must be an object");if(!v(e.componentPath,this.aemConfig))throw $(P.INVALID_PATH,`Component path '${e.componentPath}' is not within allowed content roots`,{path:e.componentPath,allowedRoots:Object.values(this.aemConfig.contentPaths)});const t=`${e.componentPath}.json`;let n;try{if(n=await this.fetch.get(t),!n||typeof n=="object"&&Object.keys(n).length===0)throw $(P.COMPONENT_NOT_FOUND,`Component not found at path: ${e.componentPath}`,{componentPath:e.componentPath})}catch(i){throw i.code===P.COMPONENT_NOT_FOUND?i:i.message?.includes("404")||i.response?.status===404?$(P.COMPONENT_NOT_FOUND,`Component not found at path: ${e.componentPath}`,{componentPath:e.componentPath}):M(i,"updateComponent")}const o=n["sling:resourceType"];if(o){p.log(`Validating properties against component definition for: ${o}`);const i=await this.getComponentDefinition(o);if(Object.keys(i.fieldDefinitions).length>0){const l=this.validateComponentProperties(e.properties,i.fieldDefinitions);if(!l.valid){const c=l.errors.join("; ");throw $(P.INVALID_PARAMETERS,`Invalid property values for component ${o}: ${c}`,{resourceType:o,componentPath:i.componentPath,validationErrors:l.errors,invalidFields:l.invalidFields,providedProperties:e.properties})}l.warnings.length>0&&p.warn(`Property validation warnings: ${l.warnings.join("; ")}`),p.log("Property values validated successfully against component dialog definitions")}}const r=new URLSearchParams;o&&r.append("sling:resourceType",o),Object.entries(e.properties).forEach(([i,l])=>{l==null?r.append(`${i}@Delete`,""):Array.isArray(l)?l.forEach(c=>{r.append(`${i}`,c.toString())}):typeof l=="object"?r.append(i,JSON.stringify(l)):r.append(i,l.toString())});const s=await this.fetch.post(e.componentPath,r,{headers:{Accept:"application/json"}}),a=await this.fetch.get(`${e.componentPath}.json`);return y({message:"Component updated successfully",path:e.componentPath,properties:e.properties,updatedProperties:a,response:s,verification:{success:!0,propertiesChanged:Object.keys(e.properties).length,timestamp:new Date().toISOString()}},"updateComponent")},"updateComponent")}async scanPageComponents(e,t="standard"){return C(async()=>{const n=`${e}.infinity.json`,o=await this.fetch.get(n),r=[],s=(a,i)=>{!a||typeof a!="object"||(a["sling:resourceType"]&&r.push({path:i,resourceType:a["sling:resourceType"],properties:H({...a},t)}),Object.entries(a).forEach(([l,c])=>{if(typeof c=="object"&&c!==null&&!l.startsWith("rep:")&&!l.startsWith("oak:")){const h=i?`${i}/${l}`:l;s(c,h)}}))};return o["jcr:content"]?s(o["jcr:content"],"jcr:content"):s(o,e),y({pagePath:e,verbosity:t,components:r,totalComponents:r.length},"scanPageComponents")},"scanPageComponents")}async fetchSites(){return C(async()=>{const t=await this.fetch.get("/content.2.json",{":depth":"2"}),n=[];return Object.entries(t).forEach(([o,r])=>{o.startsWith("jcr:")||o.startsWith("sling:")||r&&typeof r=="object"&&r["jcr:content"]&&n.push({name:o,path:`/content/${o}`,title:r["jcr:content"]["jcr:title"]||o,template:r["jcr:content"]["cq:template"],lastModified:r["jcr:content"]["cq:lastModified"]})}),y({sites:n,totalCount:n.length},"fetchSites")},"fetchSites")}async fetchLanguageMasters(e){return C(async()=>{const t=`/content/${e}.2.json`,n=await this.fetch.get(t),o=[];let r=null,s="";return Object.entries(n).forEach(([a,i])=>{(a==="master"||a==="language-masters")&&i&&typeof i=="object"&&(r=i,s=`/content/${e}/${a}`)}),r?(Object.entries(r).forEach(([a,i])=>{a.startsWith("jcr:")||a.startsWith("sling:")||i&&typeof i=="object"&&o.push({name:a,path:`${s}/${a}`,title:i["jcr:content"]?.["jcr:title"]||i["jcr:title"]||a,language:i["jcr:content"]?.["jcr:language"]||i["jcr:language"]||a})}),y({site:e,languageMasters:o},"fetchLanguageMasters")):y({site:e,languageMasters:[],message:"No master or language-masters node found"},"fetchLanguageMasters")},"fetchLanguageMasters")}async fetchAvailableLocales(e){return C(async()=>{const t=`/content/${e}.4.json`,n=await this.fetch.get(t),o={},r=(s,a,i=[])=>{!s||typeof s!="object"||Object.entries(s).forEach(([l,c])=>{if(!(l.startsWith("jcr:")||l.startsWith("sling:")||l.startsWith("cq:")||l.startsWith("rep:")||l.startsWith("oak:")||l==="jcr:content")&&c&&typeof c=="object"){const h=`${a}/${l}`,A=[...i,l],T=c["jcr:content"],m=T&&typeof T=="object",j=T?.["jcr:language"]||null,S=l.length===2||l.length===3,b=i.length>0&&(i[i.length-1].length===2||i[i.length-1].length===3);if(m&&S&&b){const d=i[i.length-1].toUpperCase(),g=l.toLowerCase(),u=`${g}_${d}`;o[u]={path:h,title:T?.["jcr:title"]||l,language:j||`${g}_${d}`,country:d}}r(c,h,A)}})};return r(n,`/content/${e}`,[]),y({site:e,locales:o,totalCount:Object.keys(o).length},"fetchAvailableLocales")},"fetchAvailableLocales")}async getAllTextContent(e){return C(async()=>{let t;try{const r=`${e}.infinity.json`;t=await this.fetch.get(r)}catch(r){if(r.message?.includes("300")||r.message?.includes("redirect")){p.warn(`infinity.json failed for ${e}, trying depth-based approach`);try{t=await this.fetch.get(`${e}.5.json`)}catch{try{t=await this.fetch.get(`${e}/jcr:content.5.json`)}catch{throw M(r,"getAllTextContent")}}}else throw M(r,"getAllTextContent")}const n=[],o=(r,s)=>{!r||typeof r!="object"||((r.text||r["jcr:title"]||r["jcr:description"])&&n.push({path:s,title:r["jcr:title"],text:r.text,description:r["jcr:description"]}),Object.entries(r).forEach(([a,i])=>{if(typeof i=="object"&&i!==null&&!a.startsWith("rep:")&&!a.startsWith("oak:")){const l=s?`${s}/${a}`:a;o(i,l)}}))};return t["jcr:content"]?o(t["jcr:content"],"jcr:content"):o(t,e),y({pagePath:e,textContent:n},"getAllTextContent")},"getAllTextContent")}async getPageTextContent(e){return C(async()=>this.getAllTextContent(e),"getPageTextContent")}async getPageImages(e){return C(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),o=[],r=(s,a)=>{!s||typeof s!="object"||((s.fileReference||s.src)&&o.push({path:a,fileReference:s.fileReference,src:s.src,alt:s.alt||s.altText,title:s["jcr:title"]||s.title}),Object.entries(s).forEach(([i,l])=>{if(typeof l=="object"&&l!==null&&!i.startsWith("rep:")&&!i.startsWith("oak:")){const c=a?`${a}/${i}`:i;r(l,c)}}))};return n["jcr:content"]?r(n["jcr:content"],"jcr:content"):r(n,e),y({pagePath:e,images:o},"getPageImages")},"getPageImages")}async updateImagePath(e,t){return C(async()=>this.updateComponent({componentPath:e,properties:{fileReference:t}}),"updateImagePath")}async getPageContent(e,t="standard"){return C(async()=>{const n=`${e}.infinity.json`,o=await this.fetch.get(n),r=U(o,t);return y({pagePath:e,verbosity:t,content:r},"getPageContent")},"getPageContent")}async listChildren(e,t=1){return C(async()=>{try{const n=await this.fetch.get(`${e}.${t}.json`),o=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([r,s])=>{if(!(r.startsWith("jcr:")||r.startsWith("sling:")||r.startsWith("cq:")||r.startsWith("rep:")||r.startsWith("oak:")||r==="jcr:content")&&s&&typeof s=="object"){const a=`${e}/${r}`;o.push({name:r,path:a,primaryType:s["jcr:primaryType"]||"nt:unstructured",title:s["jcr:content"]?.["jcr:title"]||s["jcr:title"]||r,lastModified:s["jcr:content"]?.["cq:lastModified"]||s["cq:lastModified"],resourceType:s["jcr:content"]?.["sling:resourceType"]||s["sling:resourceType"]})}}),o}catch(n){if(n.response?.status===404||n.response?.status===403)return((await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":"1","p.limit":"1000","p.hits":"full"})).hits||[]).map(r=>({name:r.name||r.path?.split("/").pop(),path:r.path,primaryType:r["jcr:primaryType"]||"cq:Page",title:r["jcr:content/jcr:title"]||r.title||r.name,lastModified:r["jcr:content/cq:lastModified"],resourceType:r["jcr:content/sling:resourceType"]}));throw n}},"listChildren")}async listPages(e,t=1,n=20){return C(async()=>{try{const o=await this.fetch.get(`${e}.${t}.json`),r=[],s=(a,i,l)=>{l>t||r.length>=n||Object.entries(a).forEach(([c,h])=>{if(!(r.length>=n)&&!(c.startsWith("jcr:")||c.startsWith("sling:")||c.startsWith("cq:")||c.startsWith("rep:")||c.startsWith("oak:"))&&h&&typeof h=="object"){const A=`${i}/${c}`;h["jcr:primaryType"]==="cq:Page"&&r.push({name:c,path:A,primaryType:"cq:Page",title:h["jcr:content"]?.["jcr:title"]||c,template:h["jcr:content"]?.["cq:template"],lastModified:h["jcr:content"]?.["cq:lastModified"],lastModifiedBy:h["jcr:content"]?.["cq:lastModifiedBy"],resourceType:h["jcr:content"]?.["sling:resourceType"],type:"page"}),l<t&&s(h,A,l+1)}})};return o&&typeof o=="object"&&s(o,e,0),y({siteRoot:e,pages:r,pageCount:r.length,depth:t,limit:n,totalChildrenScanned:r.length},"listPages")}catch(o){if(p.warn("JSON API failed, falling back to QueryBuilder:",o.message),o.response?.status===404||o.response?.status===403){const r=await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":t.toString(),"p.limit":n.toString(),"p.hits":"full"}),s=(r.hits||[]).map(a=>({name:a.name||a.path?.split("/").pop(),path:a.path,primaryType:"cq:Page",title:a["jcr:content/jcr:title"]||a.title||a.name,template:a["jcr:content/cq:template"],lastModified:a["jcr:content/cq:lastModified"],lastModifiedBy:a["jcr:content/cq:lastModifiedBy"],resourceType:a["jcr:content/sling:resourceType"],type:"page"}));return y({siteRoot:e,pages:s,pageCount:s.length,depth:t,limit:n,totalChildrenScanned:r.total||s.length,fallbackUsed:"QueryBuilder"},"listPages")}throw o}},"listPages")}async executeJCRQuery(e,t=20){return C(async()=>{if(!e||e.trim().length===0)throw new Error("Query is required and must be a non-empty string. Note: Only QueryBuilder fulltext is supported, not JCR SQL2.");const n=e.toLowerCase();if(/drop|delete|update|insert|exec|script|\.|<script/i.test(n)||e.length>1e3)throw new Error("Query contains potentially unsafe patterns or is too long");const o=await this.fetch.get("/bin/querybuilder.json",{path:"/content",type:"cq:Page",fulltext:e,"p.limit":t});return{query:e,results:o.hits||[],total:o.total||0,limit:t}},"executeJCRQuery")}async getPageProperties(e){return C(async()=>{const t=`${e}/jcr:content.json`,n=await this.fetch.get(t),o={title:n["jcr:title"],description:n["jcr:description"],template:n["cq:template"],lastModified:n["cq:lastModified"],lastModifiedBy:n["jcr:createdBy"],created:n["jcr:created"],createdBy:n["jcr:createdBy"],primaryType:n["jcr:primaryType"],resourceType:n["sling:resourceType"],tags:n["cq:tags"]||[],properties:n};return y({pagePath:e,properties:o},"getPageProperties")},"getPageProperties")}async searchContent(e){return C(async()=>{const t=await this.fetch.get(this.config.aem.endpoints.query,e);return y({params:e,results:t.hits||[],total:t.total||0,rawResponse:t},"searchContent")},"searchContent")}async getAssetMetadata(e){return C(async()=>{const t=`${e}.json`,n=await this.fetch.get(t),o=n["jcr:content"]?.metadata||{};return y({assetPath:e,metadata:o,fullData:n},"getAssetMetadata")},"getAssetMetadata")}async createPage(e){return this.createPageWithTemplate(e)}async deletePage(e){return C(async()=>{const{pagePath:t}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});let n=!1;try{await this.fetch.delete(t),n=!0}catch(o){if(o?.status===405||o?.response?.status===405)try{await this.fetch.post("/bin/wcmcommand",{cmd:"deletePage",path:t,force:e.force?"true":"false"}),n=!0}catch{try{await this.fetch.post(t,{":operation":"delete"}),n=!0}catch(s){throw s}}else throw p.error("DELETE failed:",o.response?.status,o.response?.data),o}return y({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deletePage")},"deletePage")}async createComponent(e){return C(async()=>{const{pagePath:t,componentPath:n,componentType:o,resourceType:r,properties:s={},name:a}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const i=a||`${o}_${Date.now()}`,l=n||`${t}/jcr:content/${i}`,c=new URLSearchParams;return c.append(":operation","import"),c.append(":contentType","json"),c.append(":replace","true"),c.append(":content",JSON.stringify({"jcr:primaryType":"nt:unstructured","sling:resourceType":r,...s})),await this.fetch.post(l,c),y({success:!0,componentPath:l,componentType:o,resourceType:r,properties:s,timestamp:new Date().toISOString()},"createComponent")},"createComponent")}async getComponentDefinition(e,t=new Set){const n=[],o={};let r="";if(t.has(e))return p.warn(`Circular reference detected for resourceType: ${e}`),{componentPath:r,requiredProperties:n,fieldDefinitions:o};t.add(e);const s=e.replace(/:/g,"/"),a=[`/apps/${s}`,`/libs/${s}`];let i=!1,l=null;for(const c of a)try{const h=await this.fetch.get(`${c}.json`,{":depth":"2"});if(h){r=c,h["sling:resourceSuperType"]&&(l=h["sling:resourceSuperType"],p.log(`Found sling:resourceSuperType: ${l} for component at ${c}`));try{const A=`${c}/_cq_dialog`,T=await this.fetch.get(`${A}.infinity.json`),m=(j,S="")=>{if(!j||typeof j!="object")return;const b=j["sling:resourceType"]||"",d=j.name||j.fieldName,g=d?d.replace(/^\.\//,""):null;if(g&&(b.includes("form/select")||b.includes("form/checkbox")||b.includes("form/textfield")||b.includes("form/textarea")||b.includes("form/numberfield")||b.includes("form/pathfield")||b.includes("form/datepicker")||b.includes("form/colorfield"))){const u=j.required===!0||j.required==="true",f={name:g,type:this.getFieldType(b),required:u,description:j.fieldDescription||j.fieldLabel||"",defaultValue:j.value!==void 0?j.value:j.checked!==void 0?j.checked:void 0};if(b.includes("form/select")&&j.items){const w=[],I=j.items;typeof I=="object"&&Object.values(I).forEach(E=>{E&&typeof E=="object"&&E.value!==void 0&&w.push(String(E.value))}),w.length>0&&(f.options=w)}b.includes("form/checkbox"),o[g]=f,p.log(`Found field definition: ${g} (type: ${f.type}, options: ${f.options?f.options.join(", "):"N/A"})`),u&&!n.includes(g)&&n.push(g)}j.items&&(Array.isArray(j.items)?j.items.forEach((u,f)=>{m(u,S?`${S}/items[${f}]`:`items[${f}]`)}):typeof j.items=="object"&&Object.entries(j.items).forEach(([u,f])=>{m(f,S?`${S}/items/${u}`:`items/${u}`)})),Object.entries(j).forEach(([u,f])=>{typeof f=="object"&&f!==null&&!u.startsWith("jcr:")&&!u.startsWith("sling:")&&u!=="items"&&m(f,S?`${S}/${u}`:u)})};m(T),i=!0,p.log(`Found component definition at ${r}, required properties: ${n.join(", ")}, field definitions: ${Object.keys(o).join(", ")}`);break}catch(A){p.warn(`Could not fetch dialog for ${c}: ${A.message}`)}}}catch{continue}if(!i&&l){p.log(`No dialog found for ${e}, checking super type: ${l}`);const c=await this.getComponentDefinition(l,t);c.requiredProperties.forEach(h=>{n.includes(h)||n.push(h)}),Object.entries(c.fieldDefinitions).forEach(([h,A])=>{o[h]||(o[h]=A)}),!r&&c.componentPath&&(r=c.componentPath)}return{componentPath:r,requiredProperties:n,fieldDefinitions:o}}getFieldType(e){return e.includes("form/select")?"select":e.includes("form/checkbox")?"checkbox":e.includes("form/textfield")?"textfield":e.includes("form/textarea")?"textarea":e.includes("form/numberfield")?"numberfield":e.includes("form/pathfield")?"pathfield":e.includes("form/datepicker")?"datepicker":e.includes("form/colorfield")?"colorfield":"unknown"}validateComponentProperties(e,t){const n=[],o=[],r={};for(const[s,a]of Object.entries(t)){const i=e[s];if(i!=null){if(a.type==="select"&&a.options){const l=String(i);if(!a.options.includes(l)){const c=`Invalid value '${i}' for field '${s}'. Allowed values: ${a.options.join(", ")}`;n.push(c),r[s]={provided:i,expected:a.options,message:c}}}if(a.type==="checkbox"&&!(typeof i=="boolean"||i==="true"||i==="false"||i===!0||i===!1)){const c=`Invalid value '${i}' for checkbox field '${s}'. Must be boolean or 'true'/'false'`;n.push(c),r[s]={provided:i,expected:'boolean or "true"/"false"',message:c}}if(a.type==="numberfield"&&isNaN(Number(i))){const l=`Invalid value '${i}' for number field '${s}'. Must be a number`;n.push(l),r[s]={provided:i,expected:"number",message:l}}}}return{valid:n.length===0,errors:n,warnings:o,invalidFields:r}}async getComponentTemplate(e){if(!e)return null;try{const t=`${e}/cq:template.infinity.json`;p.log(`Checking for cq:template at: ${t}`);const n=await this.fetch.get(t);if(n&&typeof n=="object"&&Object.keys(n).length>0){p.log(`\u2705 Found cq:template with ${Object.keys(n).length} top-level keys`);const o=Object.keys(n).filter(r=>!r.startsWith("jcr:")&&!r.startsWith("sling:"));return o.length>0&&p.log(`Template child nodes: ${o.join(", ")}`),n}return null}catch(t){const n=t.response?.status||t.statusCode||"unknown";return n===404?p.log(`No cq:template found at ${e}/cq:template`):p.warn(`Error checking for cq:template: ${t.message} (status: ${n})`),null}}async applyTemplateChildNodes(e,t){if(!(!t||Object.keys(t).length===0)){p.log(`Applying ${Object.keys(t).length} child nodes from cq:template to: ${e}`);for(const[n,o]of Object.entries(t))try{const r=`${e}/${n}`,s={...o};s["jcr:primaryType"]||(s["jcr:primaryType"]="nt:unstructured");const a=new URLSearchParams;a.append(":operation","import"),a.append(":contentType","json"),a.append(":replace","true"),a.append(":content",JSON.stringify(s)),await this.fetch.post(r,a),p.log(`Created template child node: ${r}`)}catch(r){p.warn(`Failed to create template child node ${n}: ${r.message}`)}}}async addComponent(e){return C(async()=>{const{pagePath:t,resourceType:n,containerPath:o,name:r,properties:s={}}=e;if(!t||typeof t!="string")throw $(P.INVALID_PARAMETERS,"Page path is required and must be a string",{pagePath:t});if(!n||typeof n!="string")throw $(P.INVALID_PARAMETERS,"Resource type is required and must be a string",{resourceType:n});if(!v(t,this.aemConfig))throw $(P.INVALID_PATH,`Invalid page path: ${String(t)}`,{pagePath:t});p.log(`Fetching component definition for resourceType: ${n}`);const a=await this.getComponentDefinition(n);if(a.componentPath?p.log(`Component definition found at: ${a.componentPath}`):p.warn(`Component definition not found for ${n}, skipping required property validation`),a.requiredProperties.length>0){const d=[];if(a.requiredProperties.forEach(g=>{(!(g in s)||s[g]===null||s[g]===void 0||s[g]==="")&&d.push(g)}),d.length>0)throw $(P.INVALID_PARAMETERS,`Missing required properties for component ${n}: ${d.join(", ")}`,{resourceType:n,componentPath:a.componentPath,requiredProperties:a.requiredProperties,missingProperties:d,providedProperties:Object.keys(s)});p.log(`All required properties validated: ${a.requiredProperties.join(", ")}`)}if(p.log(`Checking field definitions: ${Object.keys(a.fieldDefinitions).length} fields found`),Object.keys(a.fieldDefinitions).length>0){p.log(`Field definitions: ${JSON.stringify(Object.keys(a.fieldDefinitions))}`),p.log(`Validating properties: ${JSON.stringify(Object.keys(s))}`);const d=this.validateComponentProperties(s,a.fieldDefinitions);if(p.log(`Validation result: valid=${d.valid}, errors=${d.errors.length}`),!d.valid){const g=d.errors.join("; ");throw p.error(`Property validation failed: ${g}`),$(P.INVALID_PARAMETERS,`Invalid property values for component ${n}: ${g}`,{resourceType:n,componentPath:a.componentPath,validationErrors:d.errors,invalidFields:d.invalidFields,providedProperties:s})}d.warnings.length>0&&p.warn(`Property validation warnings: ${d.warnings.join("; ")}`),p.log("Property values validated successfully against component dialog definitions")}else p.log(`No field definitions found for component ${n}, skipping property value validation`);try{const d=await this.fetch.get(`${t}.json`);if(!d||typeof d=="object"&&Object.keys(d).length===0)throw $(P.INVALID_PATH,`Page not found or invalid at: ${t}`,{pagePath:t});p.log(`Page verified at: ${t}`)}catch(d){throw d.message?.includes("404")||d.message?.includes("Page not found")||d.response?.status===404?$(P.INVALID_PATH,`Page not found at: ${t}`,{pagePath:t}):M(d,"addComponent")}let i;if(o)o.startsWith("/")?i=o:o.includes("jcr:content")?i=`${t}/${o}`:i=`${t}/jcr:content/${o}`;else{const d=`${t}/jcr:content`;let g=!1;try{const u=await this.fetch.get(`${d}.5.json`);u&&u.root?u.root.container?(i=`${d}/root/container`,g=!0,p.log(`\u2705 Found root/container at: ${i}`)):(i=`${d}/root/container`,g=!1,p.log(`Found root but no container, will create container at: ${i}`)):(i=`${d}/root/container`,g=!1,p.log(`No root found, will create root/container at: ${i}`))}catch{i=`${d}/root/container`,g=!1,p.warn(`Could not fetch jcr:content structure, will create root/container at: ${i}`)}}try{await this.fetch.get(`${i}.json`),p.log(`\u2705 Container exists at: ${i}`)}catch(d){if(d.message?.includes("404")||d.response?.status===404){p.warn(`Container not found at ${i}, attempting to create root/container structure`);const u=`${`${t}/jcr:content`}/root`,f=`${u}/container`;try{try{await this.fetch.get(`${u}.json`),p.log(`\u2705 Root already exists at: ${u}`)}catch{p.log(`Creating root at: ${u}`),await this.fetch.post(u,{"jcr:primaryType":"nt:unstructured","sling:resourceType":"aemmcp/base/components/aemmcp-container/v1/aemmcp-container"}),p.log(`\u2705 Created root at: ${u}`)}i===f&&(p.log(`Creating container at: ${f}`),await this.fetch.post(f,{"jcr:primaryType":"nt:unstructured","sling:resourceType":"aemmcp/base/components/aemmcp-container/v1/aemmcp-container"}),p.log(`\u2705 Created container at: ${f}`))}catch(w){throw $(P.INVALID_PATH,`Could not create root/container structure at: ${i}`,{targetContainerPath:i,error:w.message})}}else throw M(d,"addComponent")}const l=r||`component_${Date.now()}`,c=`${i}/${l}`;let h=null,A={},T={};if(a.componentPath&&(h=await this.getComponentTemplate(a.componentPath),h)){p.log("Component has cq:template, processing structure...");const d=["jcr:created","jcr:createdBy","jcr:lastModified","jcr:lastModifiedBy","jcr:mixinTypes"],g=["jcr:primaryType","sling:resourceType","layout","showSeparator","columns","separator"];for(const[u,f]of Object.entries(h))d.includes(u)||(g.includes(u)?A[u]=f:typeof f=="object"&&f!==null&&!Array.isArray(f)&&(f["jcr:primaryType"]!==void 0||f["sling:resourceType"]!==void 0)?T[u]=f:(typeof f!="object"||Array.isArray(f))&&(A[u]=f));p.log(`Template: ${Object.keys(A).length} properties, ${Object.keys(T).length} child nodes`)}const m=new URLSearchParams;if(Object.entries(A).forEach(([d,g])=>{g!=null&&(Array.isArray(g)?g.forEach(u=>m.append(d,u.toString())):typeof g=="object"?m.append(d,JSON.stringify(g)):m.append(d,g.toString()))}),Object.entries(s).forEach(([d,g])=>{g!=null&&(Array.isArray(g)?g.forEach(u=>m.append(d,u.toString())):typeof g=="object"?m.append(d,JSON.stringify(g)):m.append(d,g.toString()))}),m.has("jcr:primaryType")||m.append("jcr:primaryType","nt:unstructured"),m.has("sling:resourceType")||m.append("sling:resourceType",n),await this.fetch.post(c,m),Object.keys(T).length>0)try{await this.applyTemplateChildNodes(c,T),p.log(`\u2705 Applied ${Object.keys(T).length} child nodes from cq:template`)}catch(d){p.warn(`Failed to apply cq:template child nodes: ${d.message}`)}let j=await this.fetch.get(`${c}.json`),S=!1,b=null;try{if(a.componentPath){const d=await this.fetch.get(`${a.componentPath}.json`,{":depth":"2"});if(d&&(d["cq:isContainer"]===!0||d["cq:isContainer"]==="true")){S=!0,p.log(`Component ${n} is a container component (cq:isContainer=true)`),await new Promise(f=>setTimeout(f,1e3)),j=await this.fetch.get(`${c}.json`,{":depth":"5"}),Object.keys(j).some(f=>!f.startsWith("jcr:")&&!f.startsWith("sling:")&&!f.startsWith("cq:")&&f!=="layout"&&f!=="showSeparator"&&f!=="columns"&&f!=="separator")||(p.log("No default structure detected immediately, waiting longer..."),await new Promise(f=>setTimeout(f,1500)),j=await this.fetch.get(`${c}.json`,{":depth":"5"})),b={};const u=(f,w,I=0)=>{I>3||Object.keys(f).forEach(E=>{if(!E.startsWith("jcr:")&&!E.startsWith("sling:")&&!E.startsWith("cq:")&&E!=="layout"&&E!=="showSeparator"&&E!=="columns"&&E!=="separator"){const R=f[E];if(R&&typeof R=="object"&&!Array.isArray(R)){const N=w?`${w}/${E}`:E;if(R["sling:resourceType"]){const D=R["sling:resourceType"];D.includes("aemmcp-cc-container")||D.includes("columncontrol")||E.startsWith("col_")||E.match(/^col_\d+$/)?b[E]={path:`${c}/${N}`,resourceType:D,type:"column",isContainer:R["cq:isContainer"]===!0||R["cq:isContainer"]==="true"}:b[E]={path:`${c}/${N}`,resourceType:D,isContainer:R["cq:isContainer"]===!0||R["cq:isContainer"]==="true"},u(R,N,I+1)}else(E.startsWith("col_")||E.match(/^col_\d+$/)||E.startsWith("column")||E.match(/^column\d+$/)||E.includes("container")||E.includes("parsys")||E.includes("Column")||E.includes("Container"))&&(b[E]={path:`${c}/${N}`,type:"container",children:Object.keys(R).filter(D=>!D.startsWith("jcr:")&&!D.startsWith("sling:")&&!D.startsWith("cq:"))},u(R,N,I+1))}}})};u(j,""),Object.keys(b).length>0?p.log(`Container structure detected: ${Object.keys(b).join(", ")}`):p.log("Container component created but no default structure detected yet")}}}catch(d){p.warn(`Could not check container status: ${d.message}`)}return y({success:!0,pagePath:t,previewUrl:this.getPreviewUrl(t),componentPath:c,resourceType:n,isContainer:S,containerStructure:b||void 0,componentDefinition:{path:a.componentPath||"Not found",requiredProperties:a.requiredProperties,validationPassed:a.requiredProperties.length===0||a.requiredProperties.every(d=>d in s&&s[d]!==null&&s[d]!==void 0&&s[d]!==""),hasTemplate:h!==null,templatePath:h?`${a.componentPath}/cq:template`:void 0,templateChildNodesCount:Object.keys(T).length,fieldDefinitions:Object.keys(a.fieldDefinitions).length>0?a.fieldDefinitions:void 0},containerPath:i,componentName:l,properties:s,verification:j,timestamp:new Date().toISOString()},"addComponent")},"addComponent")}async deleteComponent(e){return C(async()=>{const{componentPath:t}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid component path: ${String(t)}`,{componentPath:t});let n=!1;try{const o=new URLSearchParams;o.append(":operation","delete"),await this.fetch.post(t,o),n=!0}catch(o){if(o?.status===405||o?.response?.status===405||o?.status===403||o?.response?.status===403)try{await this.fetch.delete(t),n=!0}catch(r){throw p.error("Both POST and DELETE failed:",o.response?.status,r.response?.status),o}else throw p.error("DELETE failed:",o.response?.status,o.response?.data),o}return y({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deleteComponent")},"deleteComponent")}async unpublishContent(e){return C(async()=>{const{contentPaths:t,unpublishTree:n=!1}=e;if(!t||Array.isArray(t)&&t.length===0)throw $(P.INVALID_PARAMETERS,"Content paths array is required and cannot be empty",{contentPaths:t});const o=[];for(const r of Array.isArray(t)?t:[t])try{const s=new URLSearchParams;s.append("cmd","Deactivate"),s.append("path",r),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);o.push({path:r,success:!0,response:a})}catch(s){o.push({path:r,success:!1,error:s.message})}return y({success:o.every(r=>r.success),results:o,unpublishedPaths:t,unpublishTree:n,timestamp:new Date().toISOString()},"unpublishContent")},"unpublishContent")}async activatePage(e){return C(async()=>{const{pagePath:t,activateTree:n=!1}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const o=new URLSearchParams;o.append("cmd","Activate"),o.append("path",t);let r;return n?(o.append("ignoredeactivated","false"),o.append("onlymodified","false"),o.append("deep","true"),r=await this.fetch.post("/libs/replication/treeactivation.html",o)):r=await this.fetch.post("/bin/replicate.json",o),y({success:!0,activatedPath:t,activateTree:n,response:r,timestamp:new Date().toISOString()},"activatePage")}catch(o){try{const r=await this.fetch.post("/bin/wcmcommand",{cmd:"activate",path:t,ignoredeactivated:!1,onlymodified:!1});return y({success:!0,activatedPath:t,activateTree:n,response:r,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"activatePage")}catch{throw M(o,"activatePage")}}},"activatePage")}async deactivatePage(e){return C(async()=>{const{pagePath:t,deactivateTree:n=!1}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const o=new URLSearchParams;o.append("cmd","Deactivate"),o.append("path",t),o.append("ignoredeactivated","false"),o.append("onlymodified","false"),n&&o.append("deep","true");const r=await this.fetch.post("/bin/replicate.json",o);return y({success:!0,deactivatedPath:t,deactivateTree:n,response:r,timestamp:new Date().toISOString()},"deactivatePage")}catch(o){try{const r=await this.fetch.post("/bin/wcmcommand",{cmd:"deactivate",path:t,ignoredeactivated:!1,onlymodified:!1});return y({success:!0,deactivatedPath:t,deactivateTree:n,response:r,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"deactivatePage")}catch{throw M(o,"deactivatePage")}}},"deactivatePage")}async updateAsset(e){return C(async()=>{const{assetPath:t,metadata:n,fileContent:o,mimeType:r}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});const s=new URLSearchParams;o&&(s.append("file",o),r&&s.append("jcr:content/jcr:mimeType",r)),n&&typeof n=="object"&&Object.entries(n).forEach(([a,i])=>{s.append(`jcr:content/metadata/${a}`,String(i))});try{const a=await this.fetch.post(t,s),i=await this.fetch.get(`${t}.json`);return y({success:!0,assetPath:t,updatedMetadata:n,updateResponse:a,assetData:i,timestamp:new Date().toISOString()},"updateAsset")}catch(a){throw M(a,"updateAsset")}},"updateAsset")}async deleteAsset(e){return C(async()=>{const{assetPath:t,force:n=!1}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});return await this.fetch.delete(t),y({success:!0,deletedPath:t,force:n,timestamp:new Date().toISOString()},"deleteAsset")},"deleteAsset")}getTemplatesPath(e){if(!e||e.trim().length===0)return"";let t=e.trim(),n="/conf",o="/settings/wcm/templates";return t=t.replace(/\/+$/,""),t.startsWith("/content/")&&(t=t.replace("/content","")),t.startsWith(n)||(t=`${n}/${t.replace(/^\//,"")}`),t.endsWith(o)||(t+=o),t}async getTemplates(e){return C(async()=>{if(e)try{const t=this.getTemplatesPath(e);if(!t)throw $(P.INVALID_PARAMETERS,`Cannot determine configuration path for site: ${e}`,{sitePath:e});p.log("Looking for site-specific templates at:",t);const n=await this.fetch.get(`${t}.2.json`),o=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([r,s])=>{r.startsWith("jcr:")||r.startsWith("sling:")||s&&typeof s=="object"&&s["jcr:content"]&&o.push({name:r,path:`${t}/${r}`,title:s["jcr:content"]["jcr:title"]||r,description:s["jcr:content"]["jcr:description"],allowedPaths:s["jcr:content"].allowedPaths,ranking:s["jcr:content"].ranking||0})}),y({sitePath:e,templates:o,totalCount:o.length,source:"site-specific"},"getTemplates")}catch{}try{const t=["/apps/wcm/core/content/sites/templates","/libs/wcm/core/content/sites/templates"],n=[];for(const o of t)try{const r=await this.fetch.get(`${o}.json`,{":depth":"2"});r&&typeof r=="object"&&Object.entries(r).forEach(([s,a])=>{s.startsWith("jcr:")||s.startsWith("sling:")||a&&typeof a=="object"&&n.push({name:s,path:`${o}/${s}`,title:a["jcr:content"]?.["jcr:title"]||s,description:a["jcr:content"]?.["jcr:description"],allowedPaths:a["jcr:content"]?.allowedPaths,ranking:a["jcr:content"]?.ranking||0,source:o.includes("/apps/")?"apps":"libs"})})}catch{}return y({sitePath:e||"global",templates:n,totalCount:n.length,source:"global"},"getTemplates")}catch(t){throw M(t,"getTemplates")}},"getTemplates")}async getTemplateStructure(e){return C(async()=>{try{const t=await this.fetch.get(`${e}.infinity.json`),n={path:e,properties:t["jcr:content"]||{},policies:t["jcr:content"]?.policies||{},structure:t["jcr:content"]?.structure||{},initialContent:t["jcr:content"]?.initial||{},allowedComponents:[],allowedPaths:t["jcr:content"]?.allowedPaths||[]},o=(r,s="")=>{if(!(!r||typeof r!="object")){if(r.components){const a=Object.keys(r.components);n.allowedComponents.push(...a)}Object.entries(r).forEach(([a,i])=>{typeof i=="object"&&i!==null&&!a.startsWith("jcr:")&&o(i,s?`${s}/${a}`:a)})}};return o(n.policies),n.allowedComponents=[...new Set(n.allowedComponents)],y({templatePath:e,structure:n,fullData:t},"getTemplateStructure")}catch(t){throw M(t,"getTemplateStructure")}},"getTemplateStructure")}async bulkUpdateComponents(e){return C(async()=>{const{updates:t,validateFirst:n=!0,continueOnError:o=!1}=e;if(!Array.isArray(t)||t.length===0)throw $(P.INVALID_PARAMETERS,"Updates array is required and cannot be empty");const r=[];if(n)for(const a of t)try{await this.fetch.get(`${a.componentPath}.json`)}catch(i){if(i.response?.status===404&&(r.push({componentPath:a.componentPath,success:!1,error:`Component not found: ${a.componentPath}`,phase:"validation"}),!o))return y({success:!1,message:"Bulk update failed during validation phase",results:r,totalUpdates:t.length,successfulUpdates:0},"bulkUpdateComponents")}let s=0;for(const a of t)try{const i=await this.updateComponent({componentPath:a.componentPath,properties:a.properties});r.push({componentPath:a.componentPath,success:!0,result:i,phase:"update"}),s++}catch(i){if(r.push({componentPath:a.componentPath,success:!1,error:i.message,phase:"update"}),!o)break}return y({success:s===t.length,message:`Bulk update completed: ${s}/${t.length} successful`,results:r,totalUpdates:t.length,successfulUpdates:s,failedUpdates:t.length-s},"bulkUpdateComponents")},"bulkUpdateComponents")}async convertComponents(e){return C(async()=>{const{pagePath:t,sourceResourceType:n,targetResourceType:o,requiredProperties:r={},continueOnError:s=!0}=e;if(!t||typeof t!="string")throw $(P.INVALID_PARAMETERS,"Page path is required and must be a string");if(!n||typeof n!="string")throw $(P.INVALID_PARAMETERS,"Source resource type is required and must be a string");if(!o||typeof o!="string")throw $(P.INVALID_PARAMETERS,"Target resource type is required and must be a string");if(!v(t,this.aemConfig))throw $(P.INVALID_PATH,`Page path '${t}' is not within allowed content roots`);p.log(`Scanning page ${t} for components with resourceType: ${n}`);const i=await this.scanPageComponents(t),c=(i.data?.components||i.components||[]).filter(b=>b.resourceType===n);if(c.length===0)return y({message:`No components found with resourceType: ${n} on page ${t}`,pagePath:t,sourceResourceType:n,targetResourceType:o,componentsFound:0,componentsConverted:0},"convertComponents");p.log(`Found ${c.length} components with resourceType: ${n}`);const A=(await this.getComponentDefinition(o)).requiredProperties||[],T=[];if(A.length>0&&(A.forEach(b=>{(!(b in r)||r[b]===null||r[b]===void 0||r[b]==="")&&T.push(b)}),T.length>0))return y({message:"Target component requires properties that are not provided",pagePath:t,sourceResourceType:n,targetResourceType:o,componentsFound:c.length,componentsConverted:0,targetComponentRequiredProperties:A,missingRequiredProperties:T,providedProperties:Object.keys(r),note:"Please provide the missing required properties in the requiredProperties parameter and retry"},"convertComponents");const m=[];let j=0,S=0;for(const b of c)try{let d;b.path.startsWith("/")?d=b.path:b.path.startsWith("jcr:content")?d=`${t}/${b.path}`:d=`${t}/${b.path}`,p.log(`Processing component at: ${d}`),p.log(`Deleting source component at: ${d}`),await this.deleteComponent({componentPath:d}),p.log(`Creating target component at: ${d}`);const g=new URLSearchParams;g.append("jcr:primaryType","nt:unstructured"),g.append("sling:resourceType",o),Object.entries(r).forEach(([f,w])=>{if(w!=null){const I=f.startsWith("./")?f.substring(2):f;Array.isArray(w)?w.forEach(E=>{g.append(I,E.toString())}):typeof w=="object"?g.append(I,JSON.stringify(w)):g.append(I,w.toString())}}),await this.fetch.post(d,g,{headers:{Accept:"application/json"}});const u=await this.fetch.get(`${d}.json`);if(!u||!u["sling:resourceType"]||u["sling:resourceType"]!==o)throw new Error("Component creation verification failed: resourceType mismatch");m.push({sourceComponentPath:d,targetComponentPath:d,success:!0,message:`Successfully converted component from ${n} to ${o}`}),j++,p.log(`\u2705 Successfully converted component at: ${d}`)}catch(d){const g=d.message||String(d);if(p.error(`Failed to convert component at ${b.path}: ${g}`),m.push({sourceComponentPath:b.path,success:!1,error:g}),S++,!s)return y({message:"Conversion stopped due to error (continueOnError=false)",pagePath:t,sourceResourceType:n,targetResourceType:o,componentsFound:c.length,componentsConverted:j,componentsFailed:S,results:m},"convertComponents")}return y({message:`Converted ${j} of ${c.length} components from ${n} to ${o}`,pagePath:t,sourceResourceType:n,targetResourceType:o,componentsFound:c.length,componentsConverted:j,componentsFailed:S,results:m},"convertComponents")},"convertComponents")}async bulkConvertComponents(e){return C(async()=>{const{pagePaths:t,searchPath:n,depth:o=2,limit:r=50,sourceResourceType:s,targetResourceType:a,requiredProperties:i={},continueOnError:l=!0}=e;if(!s||typeof s!="string")throw $(P.INVALID_PARAMETERS,"Source resource type is required and must be a string");if(!a||typeof a!="string")throw $(P.INVALID_PARAMETERS,"Target resource type is required and must be a string");let c=[];if(t&&Array.isArray(t)&&t.length>0)c=t,p.log(`Processing ${c.length} specified pages`);else if(n){if(!v(n,this.aemConfig))throw $(P.INVALID_PATH,`Search path '${n}' is not within allowed content roots`);p.log(`Searching for pages under ${n} with depth ${o}`);const d=await this.listPages(n,o,r),g=d.data?.pages||d.pages||[];if(g.length===0)return y({message:`No pages found under ${n}`,searchPath:n,sourceResourceType:s,targetResourceType:a,pagesProcessed:0,pagesSucceeded:0,pagesFailed:0,totalComponentsConverted:0},"bulkConvertComponents");c=g.map(u=>u.path||u["@path"]).filter(u=>u),p.log(`Found ${c.length} pages to process`)}else throw $(P.INVALID_PARAMETERS,"Either pagePaths array or searchPath must be provided");if(c.length===0)return y({message:"No pages to process",sourceResourceType:s,targetResourceType:a,pagesProcessed:0,pagesSucceeded:0,pagesFailed:0,totalComponentsConverted:0},"bulkConvertComponents");const h=[];let A=0,T=0,m=0,j=0,S=0;for(const b of c)try{p.log(`Processing page: ${b}`);const g=await this.convertComponents({pagePath:b,sourceResourceType:s,targetResourceType:a,requiredProperties:i,continueOnError:l}),u=g.data||g;if(g.success!==!1){const f=u.componentsFound||0,w=u.componentsConverted||0,I=u.componentsFailed||0;m+=f,j+=w,S+=I,f>0&&A++,h.push({pagePath:b,success:!0,componentsFound:f,componentsConverted:w,componentsFailed:I,message:u.message||"Processed successfully"})}else T++,h.push({pagePath:b,success:!1,error:u.message||"Conversion failed",componentsFound:0,componentsConverted:0,componentsFailed:0})}catch(d){const g=d.message||String(d);if(p.error(`Failed to process page ${b}: ${g}`),T++,h.push({pagePath:b,success:!1,error:g,componentsFound:0,componentsConverted:0,componentsFailed:0}),!l)return y({message:"Bulk conversion stopped due to error (continueOnError=false)",sourceResourceType:s,targetResourceType:a,pagesProcessed:c.length,pagesSucceeded:A,pagesFailed:T,totalComponentsFound:m,totalComponentsConverted:j,totalComponentsFailed:S,pageResults:h},"bulkConvertComponents")}return y({message:`Bulk conversion completed: ${A} pages succeeded, ${T} pages failed. Total components converted: ${j}`,sourceResourceType:s,targetResourceType:a,pagesProcessed:c.length,pagesSucceeded:A,pagesFailed:T,totalComponentsFound:m,totalComponentsConverted:j,totalComponentsFailed:S,pageResults:h},"bulkConvertComponents")},"bulkConvertComponents")}async getNodeContent(e,t=1,n="standard"){return C(async()=>{const o=`${e}.json`,r=await this.fetch.get(o,{":depth":t.toString()}),s=U(r,n);return{path:e,depth:t,verbosity:n,content:s,timestamp:new Date().toISOString()}},"getNodeContent")}async getAvailableTemplates(e){return C(async()=>{console.log("getAvailableTemplates for parentPath:",e);let t="/conf";const n=e.split("/");n.length>=3&&n[1]==="content"&&(t=`/conf/${n[2]}`);const o=`${t}/settings/wcm/templates`;try{const r=await this.fetch.get(`${o}.3.json`),s=[];return r&&typeof r=="object"&&Object.entries(r).forEach(([a,i])=>{if(!(a.startsWith("jcr:")||a.startsWith("sling:"))&&i&&typeof i=="object"&&i["jcr:content"]){const l=`${o}/${a}`,c=i["jcr:content"],h=i?.structure?.["jcr:content"]||{};s.push({name:a,path:l,title:c["jcr:title"]||a,description:c["jcr:description"]||"",thumbnail:c.thumbnail||"",allowedPaths:c.allowedPaths||[],status:c.status||"enabled",ranking:c.ranking||0,templateType:c.templateType||"page",resourceType:h["sling:resourceType"]||"",lastModified:c["cq:lastModified"],createdBy:c["jcr:createdBy"]})}}),s.sort((a,i)=>a.ranking!==i.ranking?i.ranking-a.ranking:a.name.localeCompare(i.name)),y({parentPath:e,templatesPath:o,templates:s,totalCount:s.length,availableTemplates:s.filter(a=>a.status==="enabled")},"getAvailableTemplates")}catch(r){if(r.response?.status===404){const s="/libs/wcm/foundation/templates",a=await this.fetch.get(`${s}.json`,{":depth":"2"}),i=[];return a&&typeof a=="object"&&Object.entries(a).forEach(([l,c])=>{l.startsWith("jcr:")||l.startsWith("sling:")||c&&typeof c=="object"&&i.push({name:l,path:`${s}/${l}`,title:c["jcr:title"]||l,description:c["jcr:description"]||"Global template",status:"enabled",ranking:0,templateType:"page",isGlobal:!0})}),y({parentPath:e,templatesPath:s,templates:i,totalCount:i.length,availableTemplates:i,fallbackUsed:!0},"getAvailableTemplates")}throw r}},"getAvailableTemplates")}async createPageWithTemplate(e){return C(async()=>{const{parentPath:t,title:n,template:o,name:r,properties:s={},resourceType:a=""}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});if(!r&&!n)throw $(P.INVALID_PARAMETERS,'Either "name" or "title" must be provided to create a page. Please provide at least one of these parameters.',{parentPath:t,providedParams:{name:r,title:n}});const i=n||(r?r.replace(/-/g," ").replace(/\b\w/g,f=>f.toUpperCase()):"");let l=o,c=a;if(!l){const w=(await this.getAvailableTemplates(t)).data.availableTemplates;if(w.length===0)throw $(P.INVALID_PARAMETERS,"No templates available for this path",{parentPath:t});const I=w[0];l=I.path,!c&&I.resourceType&&(c=I.resourceType),p.log(`\u{1F3AF} Auto-selected template: ${l} (${w[0].title})`,c)}let h=null;try{const f=await this.fetch.get(`${l}.json`);p.log(`\u2705 Template verified: ${l}`,f);try{const w=await this.fetch.get(`${l}.infinity.json`);if(!c&&w){const I=w.structure?.["jcr:content"];if(I&&I["sling:resourceType"])c=I["sling:resourceType"],p.log(`\u{1F4CB} Extracted resourceType from template structure: ${c}`);else{const E=w["jcr:content"];E&&E["sling:resourceType"]&&(c=E["sling:resourceType"],p.log(`\u{1F4CB} Extracted resourceType from template jcr:content: ${c}`))}}w&&w["jcr:content"]&&w.initial?(h=w.initial,p.log(`\u{1F4CB} Found initial content node at template/initial: ${l}`)):p.warn(`\u26A0\uFE0F No initial content found in template at /initial: ${l}`)}catch(w){p.warn(`Could not fetch template structure: ${w.message}`)}}catch(f){throw p.error("Template verification failed:",f.message,f),f?.response?.status===404?$(P.INVALID_PARAMETERS,`Template not found: ${l}`,{template:l}):M(f,"createPageWithTemplate")}try{await this.validateTemplate(l,t),p.log(`\u2705 Template validation passed for ${l} at ${t}`)}catch(f){throw f}const A=r||(i?i.replace(/\s+/g,"-").toLowerCase():""),T=`${t}/${A}`;try{const f=await this.fetch.get(`${T}.json`);if(f&&(f["jcr:primaryType"]==="cq:Page"||f["jcr:content"]))throw $(P.INVALID_PARAMETERS,`Page already exists at path: ${T}. Cannot overwrite existing page. Please use a different name or delete the existing page first.`,{pagePath:T,parentPath:t,pageName:A,existingPage:!0})}catch(f){if(f.code===P.INVALID_PARAMETERS&&f.message?.includes("already exists"))throw f;f.response?.status!==404&&!f.message?.includes("404")&&p.warn(`Could not check if page exists at ${T}: ${f.message}`)}const m={"jcr:primaryType":"cq:Page","jcr:content":{"jcr:primaryType":"cq:PageContent","jcr:title":i,"cq:template":l,"sling:resourceType":c||"foundation/components/page","cq:lastModified":new Date().toISOString(),"jcr:createdBy":"admin","jcr:created":new Date().toISOString(),"cq:lastModifiedBy":"admin",...s}};if(h&&h["jcr:content"]){const f=h["jcr:content"];Object.entries(f).forEach(([w,I])=>{w==="jcr:created"||w==="jcr:createdBy"||w==="jcr:title"||w==="cq:template"||w.startsWith("jcr:")&&w!=="jcr:primaryType"||typeof I=="object"&&!Array.isArray(I)&&!w.startsWith("jcr:")&&!w.startsWith("sling:")&&!w.startsWith("cq:")||m["jcr:content"][w]||(m["jcr:content"][w]=I)}),p.log("\u{1F4E6} Merged initial content properties from template")}const j=new URLSearchParams;j.append("jcr:primaryType","cq:Page"),await this.fetch.post(T,j);const S=new URLSearchParams;if(Object.entries(m["jcr:content"]).forEach(([f,w])=>{if(!(f==="jcr:created"||f==="jcr:createdBy"))if(typeof w=="object"&&w!==null&&!Array.isArray(w)){if(!f.startsWith("jcr:")&&!f.startsWith("sling:")&&!f.startsWith("cq:"))return;S.append(f,JSON.stringify(w))}else S.append(f,String(w))}),await this.fetch.post(`${T}/jcr:content`,S),h&&h["jcr:content"]){const f=`${T}/jcr:content`,I=h["jcr:content"],E=async(R,N,D=10)=>{if(!(D<=0)){for(const[x,L]of Object.entries(N))if(!(x.startsWith("jcr:")||x.startsWith("sling:")||x.startsWith("cq:")||x.startsWith("rep:")||x.startsWith("oak:"))&&L&&typeof L=="object"&&!Array.isArray(L)){const q=`${R}/${x}`,O=L;try{try{await this.fetch.get(`${q}.json`),O&&typeof O=="object"&&await E(q,O,D-1)}catch{const _=new URLSearchParams;O["jcr:primaryType"]?_.append("jcr:primaryType",O["jcr:primaryType"]):_.append("jcr:primaryType","nt:unstructured"),Object.entries(O).forEach(([F,W])=>{F!=="jcr:primaryType"&&(F.startsWith("jcr:")&&F!=="jcr:primaryType"||typeof W=="object"&&!Array.isArray(W)||W!=null&&(Array.isArray(W)?W.forEach(B=>{_.append(F,String(B))}):_.append(F,String(W))))}),await this.fetch.post(q,_),p.log(`\u2705 Created initial node: ${q}`),await E(q,O,D-1)}}catch(V){p.warn(`Could not create initial node ${q}: ${V.message}`)}}}};await E(f,I),p.log("\u2705 Created initial content structure from template under jcr:content")}const b=await this.fetch.get(`${T}.json`),d=b["jcr:content"]!==void 0;let g=!1;try{g=(await this.fetch.get(`${T}.html`)).status===200}catch{g=!1}const u={hasErrors:!1,errors:[]};return y({success:!0,pagePath:T,previewUrl:this.getPreviewUrl(T),title:n,templateUsed:l,jcrContentCreated:d,pageAccessible:g,errorLogCheck:u,creationDetails:{timestamp:new Date().toISOString(),steps:["Template validation completed","Template initial content fetched","Page node created","jcr:content node created",h?"Initial content structure created from template":"No initial content in template","Page structure verified","Accessibility check completed"],initialContentCreated:h!==null},pageStructure:b.data},"createPageWithTemplate")},"createPageWithTemplate")}async validateTemplate(e,t){return C(async()=>{try{let n;try{n=await this.fetch.get(`${e}.2.json`)}catch(m){try{n=await this.fetch.get(`${e}.infinity.json`)}catch(j){throw $(P.INVALID_PARAMETERS,`Template not found or inaccessible: ${e}`,{templatePath:e,fetchError:m.message,infinityError:j.message})}}if(!n||typeof n!="object")throw $(P.INVALID_PARAMETERS,"Invalid template structure: template data is not an object",{templatePath:e});const o=n["jcr:content"];if(!o||typeof o!="object")throw $(P.INVALID_PARAMETERS,"Invalid template structure: jcr:content not found",{templatePath:e,templateDataKeys:Object.keys(n)});const r=o.allowedPaths,s=Array.isArray(r)?r:r?[r]:[],a=o.allowedParents,i=Array.isArray(a)?a:a?[a]:[];let l=s.length===0;s.length>0&&(l=s.some(m=>{if(m.includes("(")||m.includes("*")||m.includes("?"))try{return new RegExp(m).test(t)}catch{return t.startsWith(m.replace(/[()*?]/g,""))}return t.startsWith(m)}));let c=!0,h=null,A=null;if(i.length>0)try{const m=await this.fetch.get(`${t}.2.json`);m&&m["jcr:content"]?(h=m["jcr:content"]["cq:template"]||null,h?(c=i.includes(h),c||(A=`Template '${e}' cannot be used under parent page with template '${h}'. Allowed parent templates: ${i.join(", ")}`)):(c=!1,A=`Template '${e}' requires a parent page with one of the allowed templates, but parent page at '${t}' has no template. Allowed parent templates: ${i.join(", ")}`)):(c=!1,A=`Template '${e}' requires a parent page with one of the allowed templates, but '${t}' is not a page. Allowed parent templates: ${i.join(", ")}`)}catch(m){m.response?.status===404?(c=!1,A=`Template '${e}' requires a parent page with one of the allowed templates, but parent path '${t}' does not exist. Allowed parent templates: ${i.join(", ")}`):(p.warn(`Could not validate parent path ${t}: ${m.message}`),c=!1,A=`Could not validate parent path '${t}': ${m.message}. Template '${e}' requires a parent page with one of the allowed templates: ${i.join(", ")}`)}if(!(l&&c)){const m=[];throw l||m.push(`Path '${t}' is not allowed. Allowed paths: ${s.join(", ")}`),!c&&A&&m.push(A),$(P.INVALID_PARAMETERS,`Template '${e}' cannot be used at '${t}'. ${m.join(" ")}`,{templatePath:e,targetPath:t,allowedPaths:s,allowedParents:i,parentTemplate:h,pathAllowed:l,parentAllowed:c})}return y({templatePath:e,targetPath:t,isValid:!0,templateTitle:o["jcr:title"]||"Untitled Template",templateDescription:o["jcr:description"]||"",allowedPaths:s,allowedParents:i,parentTemplate:h,restrictions:{hasPathRestrictions:s.length>0,hasParentRestrictions:i.length>0,allowedPaths:s,allowedParents:i,pathAllowed:l,parentAllowed:c}},"validateTemplate")}catch(n){throw n.code===P.INVALID_PARAMETERS?n:n.response?.status===404?$(P.INVALID_PARAMETERS,`Template not found: ${e}`,{templatePath:e}):M(n,"validateTemplate")}},"validateTemplate")}templateCache=new Map;templateCacheExpiry=new Map;TEMPLATE_CACHE_TTL=300*1e3;async getTemplateMetadata(e,t=!0){return C(async()=>{if(t&&this.templateCache.has(e)){const s=this.templateCacheExpiry.get(e)||0;if(Date.now()<s)return y({...this.templateCache.get(e),fromCache:!0},"getTemplateMetadata")}const n=await this.fetch.get(`${e}.json`);if(!n||!n["jcr:content"])throw $(P.INVALID_PARAMETERS,"Invalid template structure",{templatePath:e});const o=n["jcr:content"],r={templatePath:e,title:o["jcr:title"]||"Untitled Template",description:o["jcr:description"]||"",thumbnail:o.thumbnail||"",allowedPaths:o.allowedPaths||[],status:o.status||"enabled",ranking:o.ranking||0,templateType:o.templateType||"page",lastModified:o["cq:lastModified"],createdBy:o["jcr:createdBy"],policies:o.policies||{},structure:o.structure||{},initialContent:o.initial||{}};return t&&(this.templateCache.set(e,r),this.templateCacheExpiry.set(e,Date.now()+this.TEMPLATE_CACHE_TTL)),y(r,"getTemplateMetadata")},"getTemplateMetadata")}clearTemplateCache(){this.templateCache.clear(),this.templateCacheExpiry.clear(),console.log("\u{1F5D1}\uFE0F Template cache cleared")}async getComponents(e){return C(async()=>{const t=e||this.aemConfig.components.componentPaths?.projectRoot1||"/apps/aemmcp/base/components";p.log(`Fetching components from root path: ${t}`);const n=[],o=async(r,s=5)=>{if(!(s<=0))try{const a=await this.fetch.get(`${r}.${Math.min(s,3)}.json`);if(a&&typeof a=="object")for(const[i,l]of Object.entries(a)){const c=l;if(!(i.startsWith("jcr:")||i.startsWith("sling:")||i.startsWith("rep:")||i.startsWith("oak:"))&&c&&typeof c=="object"){const h=`${r}/${i}`,A=c["jcr:primaryType"],T=c._cq_dialog!==void 0,m=c[".content.xml"]!==void 0||c["component.html"]!==void 0||c["component.js"]!==void 0||c["component.css"]!==void 0;if(T||m||A==="cq:Component"){let j=i,S="",b="",d="";if(c["jcr:title"]?j=c["jcr:title"]:c["jcr:content"]?.["jcr:title"]&&(j=c["jcr:content"]["jcr:title"]),c["jcr:description"]?S=c["jcr:description"]:c["jcr:content"]?.["jcr:description"]&&(S=c["jcr:content"]["jcr:description"]),c.componentGroup&&(b=c.componentGroup),d=h.replace("/apps/","").replace("/libs/",""),c._cq_dialog)try{const g=await this.fetch.get(`${h}/_cq_dialog.json`,{":depth":"2"});g&&g["jcr:title"]&&(j=g["jcr:title"]||j),g&&g["jcr:description"]&&(S=g["jcr:description"]||S)}catch{}n.push({name:i,title:j,description:S,path:h,resourceType:d,componentGroup:b||"General",primaryType:A,hasDialog:T})}else await o(h,s-1)}}}catch(a){a.message?.includes("404")?p.warn(`Path not found: ${r}`):p.warn(`Error fetching components from ${r}: ${a.message}`)}};return await o(t,5),n.sort((r,s)=>r.name.localeCompare(s.name)),p.log(`Found ${n.length} components from ${t}`),y({rootPath:t,components:n,totalCount:n.length},"getComponents")},"getComponents")}async listWorkflowModels(){return C(async()=>{const e=await this.fetch.get("/etc/workflow/models.json"),t=Array.isArray(e)?e:[],n={request_for_activation:"Publish/activate pages",request_for_deactivation:"Unpublish/deactivate pages",request_for_deletion:"Delete pages",request_for_deletion_without_deactivation:"Delete pages without unpublishing first","dam/update_asset":"Update DAM assets","dam/dam-update-language-copy":"Update language copies of assets","dam/dam-create-language-copy":"Create language copies of assets","wcm-translation/translate-language-copy":"Translate language copies","wcm-translation/create_language_copy":"Create language copies","wcm-translation/prepare_translation_project":"Prepare translation project","wcm-translation/sync_translation_job":"Sync translation job","wcm-translation/update_language_copy":"Update language copy",activationmodel:"Activation workflow model",scheduled_activation:"Scheduled activation",scheduled_deactivation:"Scheduled deactivation"},o=t.map(r=>{const s=r.uri||"",a=s.replace("/var/workflow/models/",""),i=n[a]||"Custom workflow model";return{uri:s,modelId:a,description:i,...r}});return y({models:o,totalCount:o.length,commonWorkflows:Object.entries(n).map(([r,s])=>({modelId:r,uri:`/var/workflow/models/${r}`,description:s}))},"listWorkflowModels")},"listWorkflowModels")}async startWorkflow(e,t,n="JCR_PATH"){return C(async()=>{const o=e.startsWith("/var/workflow/models/")?e:`/var/workflow/models/${e}`,r=new URLSearchParams;r.append("model",o),r.append("payloadType",n),r.append("payload",t);const s=await this.fetch.postWithHeaders("/etc/workflow/instances",r,{headers:{Accept:"application/json"}});if(!s.ok&&s.status!==201){const c=await s.text();throw $(P.INVALID_PARAMETERS,`Failed to start workflow: ${s.status} - ${c}`)}let a=null,i=null;const l=s.headers.get("Location");if(l){a=l.startsWith("http")?new URL(l).pathname:l;const c=a.split("/").filter(h=>h);c.length>0&&(i=c[c.length-1])}else try{const c=await s.text();if(c){const h=JSON.parse(c);if(h&&h.uri){a=h.uri;const A=a.split("/").filter(T=>T);A.length>0&&(i=A[A.length-1])}}}catch{p.log("Workflow created (201), but response is not JSON. Location header:",l||"not found")}return y({modelId:e,modelUri:o,payload:t,payloadType:n,instanceId:i,instancePath:a,locationHeader:l,status:s.status,message:"Workflow instance started successfully"},"startWorkflow")},"startWorkflow")}async listWorkflowInstances(e){return C(async()=>{const t=e?`/etc/workflow/instances.${e}.json`:"/etc/workflow/instances.json",n=await this.fetch.get(t),o=Array.isArray(n)?n:n?.instances||[];return y({instances:o,totalCount:o.length,state:e||"all",timestamp:new Date().toISOString()},"listWorkflowInstances")},"listWorkflowInstances")}async getWorkflowInstance(e){return C(async()=>{const t=e.startsWith("/var/workflow/instances/")?e:`/var/workflow/instances/${e}`,n=await this.fetch.get(`${t}.json`);return y({instanceId:e,instancePath:t,instance:n,timestamp:new Date().toISOString()},"getWorkflowInstance")},"getWorkflowInstance")}async updateWorkflowInstanceState(e,t){return C(async()=>{if(!["RUNNING","SUSPENDED","ABORTED"].includes(t))throw $(P.INVALID_PARAMETERS,`Invalid state: ${t}. Must be RUNNING, SUSPENDED, or ABORTED`);const n=e.startsWith("/var/workflow/instances/")?e:`/var/workflow/instances/${e}`,o=new URLSearchParams;o.append("state",t);const r=await this.fetch.post(n,o,{headers:{Accept:"application/json"}});return y({instanceId:e,instancePath:n,newState:t,response:r,message:`Workflow instance state updated to ${t}`},"updateWorkflowInstanceState")},"updateWorkflowInstanceState")}async getInboxItems(){return C(async()=>{const e=await this.fetch.get("/bin/workflow/inbox.json"),t=e?.items||e||[];return y({items:t,totalCount:Array.isArray(t)?t.length:0,timestamp:new Date().toISOString()},"getInboxItems")},"getInboxItems")}async completeWorkItem(e,t,n){return C(async()=>{let o=t||"";if(!o){const i=(await this.fetch.get(`${e}.routes.json`))?.routes||[];if(i.length===0)throw $(P.INVALID_PARAMETERS,"No routes available for this work item");const l=i[0];if(!l?.rid)throw $(P.INVALID_PARAMETERS,"No valid route ID found in available routes");o=l.rid,p.log(`No route specified, using first available route: ${l.label||"Unknown"} (${o})`)}const r=new URLSearchParams;r.append("item",e),r.append("route",o),n&&r.append("comment",n);const s=await this.fetch.post("/bin/workflow/inbox",r,{headers:{Accept:"application/json"}});return y({workItemPath:e,routeId:o,comment:n,response:s,message:"Work item completed successfully"},"completeWorkItem")},"completeWorkItem")}async delegateWorkItem(e,t){return C(async()=>{const n=new URLSearchParams;n.append("item",e),n.append("delegatee",t);const o=await this.fetch.post("/bin/workflow/inbox",n,{headers:{Accept:"application/json"}});return y({workItemPath:e,delegatee:t,response:o,message:`Work item delegated to ${t}`},"delegateWorkItem")},"delegateWorkItem")}async getWorkItemRoutes(e){return C(async()=>{const n=(await this.fetch.get(`${e}.routes.json`))?.routes||[];return y({workItemPath:e,routes:n,totalCount:n.length,timestamp:new Date().toISOString()},"getWorkItemRoutes")},"getWorkItemRoutes")}async getContentFragment(e){return this.contentFragments.getContentFragment(e)}async listContentFragments(e){return this.contentFragments.listContentFragments(e)}async manageContentFragment(e){return this.contentFragments.manageContentFragment(e)}async manageContentFragmentVariation(e){return this.contentFragments.manageContentFragmentVariation(e)}async getExperienceFragment(e){return this.experienceFragments.getExperienceFragment(e)}async listExperienceFragments(e){return this.experienceFragments.listExperienceFragments(e)}async manageExperienceFragment(e){return this.experienceFragments.manageExperienceFragment(e)}async manageExperienceFragmentVariation(e){return this.experienceFragments.manageExperienceFragmentVariation(e)}}