aem-mcp-server 1.5.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -20
- package/dist/aem/aem.connector.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ This project is designed for non-technical persons who want to manage AEM via na
|
|
|
22
22
|
- **CLI agents** — Claude Code, GitHub Copilot CLI, Gemini CLI, Amazon Q CLI
|
|
23
23
|
- **Chat & desktop apps** — Claude Desktop, ChatGPT Desktop, Goose
|
|
24
24
|
- **Supports both AEMaaCS and self-hosted AEM instances**
|
|
25
|
-
- **Two transport modes** — stdio (
|
|
25
|
+
- **Two transport modes** — stdio via `npx` (recommended, zero install) and streamable HTTP
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
@@ -32,19 +32,9 @@ This project is designed for non-technical persons who want to manage AEM via na
|
|
|
32
32
|
- Node.js 20.19.0+ || 22.12.0+ || 23+
|
|
33
33
|
- Access to an AEM instance (local or remote)
|
|
34
34
|
|
|
35
|
-
###
|
|
35
|
+
### Stdio Transport (recommended)
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
npm install aem-mcp-server -g
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### Two Transport Modes
|
|
42
|
-
|
|
43
|
-
AEM MCP Server supports two transport modes. In both cases, the AI agent is configured via the `mcp.json` (or equivalent) settings file in your IDE.
|
|
44
|
-
|
|
45
|
-
#### 1. Stdio Transport (recommended)
|
|
46
|
-
|
|
47
|
-
No manual server start needed — the AI agent spawns the process automatically.
|
|
37
|
+
No installation needed — the AI agent downloads and spawns the process automatically via `npx`.
|
|
48
38
|
|
|
49
39
|
Add to your project's MCP config (`.mcp.json`, `.vscode/mcp.json`, `.cursor/mcp.json`, etc.):
|
|
50
40
|
|
|
@@ -52,8 +42,8 @@ Add to your project's MCP config (`.mcp.json`, `.vscode/mcp.json`, `.cursor/mcp.
|
|
|
52
42
|
{
|
|
53
43
|
"mcpServers": {
|
|
54
44
|
"AEM": {
|
|
55
|
-
"command": "
|
|
56
|
-
"args": ["-t", "stdio", "-H", "http://localhost:4502", "-u", "admin", "-p", "admin"]
|
|
45
|
+
"command": "npx",
|
|
46
|
+
"args": ["-y", "aem-mcp-server", "-t", "stdio", "-H", "http://localhost:4502", "-u", "admin", "-p", "admin"]
|
|
57
47
|
}
|
|
58
48
|
}
|
|
59
49
|
}
|
|
@@ -72,22 +62,23 @@ Add to your project's MCP config (`.mcp.json`, `.vscode/mcp.json`, `.cursor/mcp.
|
|
|
72
62
|
> {
|
|
73
63
|
> "mcpServers": {
|
|
74
64
|
> "AEM": {
|
|
75
|
-
> "command": "
|
|
76
|
-
> "args": ["-t", "stdio", "-H", "${AEM_HOST:-http://localhost:4502}", "-u", "${AEM_USER:-admin}", "-p", "${AEM_PASSWORD:-admin}"]
|
|
65
|
+
> "command": "npx",
|
|
66
|
+
> "args": ["-y", "aem-mcp-server", "-t", "stdio", "-H", "${AEM_HOST:-http://localhost:4502}", "-u", "${AEM_USER:-admin}", "-p", "${AEM_PASSWORD:-admin}"]
|
|
77
67
|
> }
|
|
78
68
|
> }
|
|
79
69
|
> }
|
|
80
70
|
> ```
|
|
81
71
|
|
|
82
|
-
|
|
72
|
+
### Streamable HTTP Transport (alternative)
|
|
83
73
|
|
|
84
|
-
|
|
74
|
+
For scenarios where you need a persistent server (shared team server, multiple clients connecting simultaneously, etc.), install globally and start the server manually:
|
|
85
75
|
|
|
86
76
|
```sh
|
|
77
|
+
npm install aem-mcp-server -g
|
|
87
78
|
aem-mcp -H=http://localhost:4502 -u=admin -p=admin
|
|
88
79
|
```
|
|
89
80
|
|
|
90
|
-
|
|
81
|
+
Then point your AI agent to the URL:
|
|
91
82
|
|
|
92
83
|
```json
|
|
93
84
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"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 B}from"./aem.fetch.js";import{ContentFragmentManager as J}from"./aem.content-fragments.js";import{ExperienceFragmentManager as H}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 B({host:this.config.aem.host,auth:this.config.aem.auth,timeout:this.aemConfig.queries.timeoutMs}),this.contentFragments=new J(this.fetch,this.isAEMaaCS),this.experienceFragments=new H(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 r=[],o=[];return t==="text"&&!n.text&&!n.richText&&r.push("Text component should have text or richText property"),t==="image"&&!n.fileReference&&!n.src&&o.push("Image component requires fileReference or src property"),{valid:o.length===0,errors:o,warnings:r,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(a){throw a.code===P.COMPONENT_NOT_FOUND?a:a.message?.includes("404")||a.response?.status===404?$(P.COMPONENT_NOT_FOUND,`Component not found at path: ${e.componentPath}`,{componentPath:e.componentPath}):M(a,"updateComponent")}const r=n["sling:resourceType"];if(r){p.log(`Validating properties against component definition for: ${r}`);const a=await this.getComponentDefinition(r);if(Object.keys(a.fieldDefinitions).length>0){const l=this.validateComponentProperties(e.properties,a.fieldDefinitions);if(!l.valid){const c=l.errors.join("; ");throw $(P.INVALID_PARAMETERS,`Invalid property values for component ${r}: ${c}`,{resourceType:r,componentPath:a.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 o=new URLSearchParams;r&&o.append("sling:resourceType",r),Object.entries(e.properties).forEach(([a,l])=>{l==null?o.append(`${a}@Delete`,""):Array.isArray(l)?l.forEach(c=>{o.append(`${a}`,c.toString())}):typeof l=="object"?o.append(a,JSON.stringify(l)):o.append(a,l.toString())});const s=await this.fetch.post(e.componentPath,o,{headers:{Accept:"application/json"}}),i=await this.fetch.get(`${e.componentPath}.json`);return y({message:"Component updated successfully",path:e.componentPath,properties:e.properties,updatedProperties:i,response:s,verification:{success:!0,propertiesChanged:Object.keys(e.properties).length,timestamp:new Date().toISOString()}},"updateComponent")},"updateComponent")}async scanPageComponents(e){return C(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),r=[],o=(s,i)=>{!s||typeof s!="object"||(s["sling:resourceType"]&&r.push({path:i,resourceType:s["sling:resourceType"],properties:{...s}}),Object.entries(s).forEach(([a,l])=>{if(typeof l=="object"&&l!==null&&!a.startsWith("rep:")&&!a.startsWith("oak:")){const c=i?`${i}/${a}`:a;o(l,c)}}))};return n["jcr:content"]?o(n["jcr:content"],"jcr:content"):o(n,e),y({pagePath:e,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(([r,o])=>{r.startsWith("jcr:")||r.startsWith("sling:")||o&&typeof o=="object"&&o["jcr:content"]&&n.push({name:r,path:`/content/${r}`,title:o["jcr:content"]["jcr:title"]||r,template:o["jcr:content"]["cq:template"],lastModified:o["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),r=[];let o=null,s="";return Object.entries(n).forEach(([i,a])=>{(i==="master"||i==="language-masters")&&a&&typeof a=="object"&&(o=a,s=`/content/${e}/${i}`)}),o?(Object.entries(o).forEach(([i,a])=>{i.startsWith("jcr:")||i.startsWith("sling:")||a&&typeof a=="object"&&r.push({name:i,path:`${s}/${i}`,title:a["jcr:content"]?.["jcr:title"]||a["jcr:title"]||i,language:a["jcr:content"]?.["jcr:language"]||a["jcr:language"]||i})}),y({site:e,languageMasters:r},"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),r={},o=(s,i,a=[])=>{!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=`${i}/${l}`,A=[...a,l],T=c["jcr:content"],m=T&&typeof T=="object",j=T?.["jcr:language"]||null,S=l.length===2||l.length===3,b=a.length>0&&(a[a.length-1].length===2||a[a.length-1].length===3);if(m&&S&&b){const d=a[a.length-1].toUpperCase(),g=l.toLowerCase(),u=`${g}_${d}`;r[u]={path:h,title:T?.["jcr:title"]||l,language:j||`${g}_${d}`,country:d}}o(c,h,A)}})};return o(n,`/content/${e}`,[]),y({site:e,locales:r,totalCount:Object.keys(r).length},"fetchAvailableLocales")},"fetchAvailableLocales")}async getAllTextContent(e){return C(async()=>{let t;try{const o=`${e}.infinity.json`;t=await this.fetch.get(o)}catch(o){if(o.message?.includes("300")||o.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(o,"getAllTextContent")}}}else throw M(o,"getAllTextContent")}const n=[],r=(o,s)=>{!o||typeof o!="object"||((o.text||o["jcr:title"]||o["jcr:description"])&&n.push({path:s,title:o["jcr:title"],text:o.text,description:o["jcr:description"]}),Object.entries(o).forEach(([i,a])=>{if(typeof a=="object"&&a!==null&&!i.startsWith("rep:")&&!i.startsWith("oak:")){const l=s?`${s}/${i}`:i;r(a,l)}}))};return t["jcr:content"]?r(t["jcr:content"],"jcr:content"):r(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),r=[],o=(s,i)=>{!s||typeof s!="object"||((s.fileReference||s.src)&&r.push({path:i,fileReference:s.fileReference,src:s.src,alt:s.alt||s.altText,title:s["jcr:title"]||s.title}),Object.entries(s).forEach(([a,l])=>{if(typeof l=="object"&&l!==null&&!a.startsWith("rep:")&&!a.startsWith("oak:")){const c=i?`${i}/${a}`:a;o(l,c)}}))};return n["jcr:content"]?o(n["jcr:content"],"jcr:content"):o(n,e),y({pagePath:e,images:r},"getPageImages")},"getPageImages")}async updateImagePath(e,t){return C(async()=>this.updateComponent({componentPath:e,properties:{fileReference:t}}),"updateImagePath")}async getPageContent(e){return C(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t);return y({pagePath:e,content:n},"getPageContent")},"getPageContent")}async listChildren(e,t=1){return C(async()=>{try{const n=await this.fetch.get(`${e}.${t}.json`),r=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([o,s])=>{if(!(o.startsWith("jcr:")||o.startsWith("sling:")||o.startsWith("cq:")||o.startsWith("rep:")||o.startsWith("oak:")||o==="jcr:content")&&s&&typeof s=="object"){const i=`${e}/${o}`;r.push({name:o,path:i,primaryType:s["jcr:primaryType"]||"nt:unstructured",title:s["jcr:content"]?.["jcr:title"]||s["jcr:title"]||o,lastModified:s["jcr:content"]?.["cq:lastModified"]||s["cq:lastModified"],resourceType:s["jcr:content"]?.["sling:resourceType"]||s["sling:resourceType"]})}}),r}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(o=>({name:o.name||o.path?.split("/").pop(),path:o.path,primaryType:o["jcr:primaryType"]||"cq:Page",title:o["jcr:content/jcr:title"]||o.title||o.name,lastModified:o["jcr:content/cq:lastModified"],resourceType:o["jcr:content/sling:resourceType"]}));throw n}},"listChildren")}async listPages(e,t=1,n=20){return C(async()=>{try{const r=await this.fetch.get(`${e}.${t}.json`),o=[],s=(i,a,l)=>{l>t||o.length>=n||Object.entries(i).forEach(([c,h])=>{if(!(o.length>=n)&&!(c.startsWith("jcr:")||c.startsWith("sling:")||c.startsWith("cq:")||c.startsWith("rep:")||c.startsWith("oak:"))&&h&&typeof h=="object"){const A=`${a}/${c}`;h["jcr:primaryType"]==="cq:Page"&&o.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 r&&typeof r=="object"&&s(r,e,0),y({siteRoot:e,pages:o,pageCount:o.length,depth:t,limit:n,totalChildrenScanned:o.length},"listPages")}catch(r){if(p.warn("JSON API failed, falling back to QueryBuilder:",r.message),r.response?.status===404||r.response?.status===403){const o=await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":t.toString(),"p.limit":n.toString(),"p.hits":"full"}),s=(o.hits||[]).map(i=>({name:i.name||i.path?.split("/").pop(),path:i.path,primaryType:"cq:Page",title:i["jcr:content/jcr:title"]||i.title||i.name,template:i["jcr:content/cq:template"],lastModified:i["jcr:content/cq:lastModified"],lastModifiedBy:i["jcr:content/cq:lastModifiedBy"],resourceType:i["jcr:content/sling:resourceType"],type:"page"}));return y({siteRoot:e,pages:s,pageCount:s.length,depth:t,limit:n,totalChildrenScanned:o.total||s.length,fallbackUsed:"QueryBuilder"},"listPages")}throw r}},"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 r=await this.fetch.get("/bin/querybuilder.json",{path:"/content",type:"cq:Page",fulltext:e,"p.limit":t});return{query:e,results:r.hits||[],total:r.total||0,limit:t}},"executeJCRQuery")}async getPageProperties(e){return C(async()=>{const t=`${e}/jcr:content.json`,n=await this.fetch.get(t),r={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:r},"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),r=n["jcr:content"]?.metadata||{};return y({assetPath:e,metadata:r,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(r){if(r?.status===405||r?.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:",r.response?.status,r.response?.data),r}return y({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deletePage")},"deletePage")}async createComponent(e){return C(async()=>{const{pagePath:t,componentPath:n,componentType:r,resourceType:o,properties:s={},name:i}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const a=i||`${r}_${Date.now()}`,l=n||`${t}/jcr:content/${a}`;return await this.fetch.post(l,{"jcr:primaryType":"nt:unstructured","sling:resourceType":o,...s,":operation":"import",":contentType":"json",":replace":"true"}),y({success:!0,componentPath:l,componentType:r,resourceType:o,properties:s,timestamp:new Date().toISOString()},"createComponent")},"createComponent")}async getComponentDefinition(e,t=new Set){const n=[],r={};let o="";if(t.has(e))return p.warn(`Circular reference detected for resourceType: ${e}`),{componentPath:o,requiredProperties:n,fieldDefinitions:r};t.add(e);const s=e.replace(/:/g,"/"),i=[`/apps/${s}`,`/libs/${s}`];let a=!1,l=null;for(const c of i)try{const h=await this.fetch.get(`${c}.json`,{":depth":"2"});if(h){o=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"),r[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),a=!0,p.log(`Found component definition at ${o}, required properties: ${n.join(", ")}, field definitions: ${Object.keys(r).join(", ")}`);break}catch(A){p.warn(`Could not fetch dialog for ${c}: ${A.message}`)}}}catch{continue}if(!a&&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])=>{r[h]||(r[h]=A)}),!o&&c.componentPath&&(o=c.componentPath)}return{componentPath:o,requiredProperties:n,fieldDefinitions:r}}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=[],r=[],o={};for(const[s,i]of Object.entries(t)){const a=e[s];if(a!=null){if(i.type==="select"&&i.options){const l=String(a);if(!i.options.includes(l)){const c=`Invalid value '${a}' for field '${s}'. Allowed values: ${i.options.join(", ")}`;n.push(c),o[s]={provided:a,expected:i.options,message:c}}}if(i.type==="checkbox"&&!(typeof a=="boolean"||a==="true"||a==="false"||a===!0||a===!1)){const c=`Invalid value '${a}' for checkbox field '${s}'. Must be boolean or 'true'/'false'`;n.push(c),o[s]={provided:a,expected:'boolean or "true"/"false"',message:c}}if(i.type==="numberfield"&&isNaN(Number(a))){const l=`Invalid value '${a}' for number field '${s}'. Must be a number`;n.push(l),o[s]={provided:a,expected:"number",message:l}}}}return{valid:n.length===0,errors:n,warnings:r,invalidFields:o}}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 r=Object.keys(n).filter(o=>!o.startsWith("jcr:")&&!o.startsWith("sling:"));return r.length>0&&p.log(`Template child nodes: ${r.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,r]of Object.entries(t))try{const o=`${e}/${n}`,s=new URLSearchParams;r["jcr:primaryType"]?s.append("jcr:primaryType",r["jcr:primaryType"]):s.append("jcr:primaryType","nt:unstructured");for(const[i,a]of Object.entries(r))i!=="jcr:primaryType"&&a!=null&&(Array.isArray(a)?a.forEach(l=>{s.append(i,l.toString())}):typeof a=="object"?s.append(i,JSON.stringify(a)):s.append(i,a.toString()));await this.fetch.post(o,s),p.log(`Created template child node: ${o}`)}catch(o){p.warn(`Failed to create template child node ${n}: ${o.message}`)}}}async addComponent(e){return C(async()=>{const{pagePath:t,resourceType:n,containerPath:r,name:o,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 i=await this.getComponentDefinition(n);if(i.componentPath?p.log(`Component definition found at: ${i.componentPath}`):p.warn(`Component definition not found for ${n}, skipping required property validation`),i.requiredProperties.length>0){const d=[];if(i.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:i.componentPath,requiredProperties:i.requiredProperties,missingProperties:d,providedProperties:Object.keys(s)});p.log(`All required properties validated: ${i.requiredProperties.join(", ")}`)}if(p.log(`Checking field definitions: ${Object.keys(i.fieldDefinitions).length} fields found`),Object.keys(i.fieldDefinitions).length>0){p.log(`Field definitions: ${JSON.stringify(Object.keys(i.fieldDefinitions))}`),p.log(`Validating properties: ${JSON.stringify(Object.keys(s))}`);const d=this.validateComponentProperties(s,i.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:i.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 a;if(r)r.startsWith("/")?a=r:r.includes("jcr:content")?a=`${t}/${r}`:a=`${t}/jcr:content/${r}`;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?(a=`${d}/root/container`,g=!0,p.log(`\u2705 Found root/container at: ${a}`)):(a=`${d}/root/container`,g=!1,p.log(`Found root but no container, will create container at: ${a}`)):(a=`${d}/root/container`,g=!1,p.log(`No root found, will create root/container at: ${a}`))}catch{a=`${d}/root/container`,g=!1,p.warn(`Could not fetch jcr:content structure, will create root/container at: ${a}`)}}try{await this.fetch.get(`${a}.json`),p.log(`\u2705 Container exists at: ${a}`)}catch(d){if(d.message?.includes("404")||d.response?.status===404){p.warn(`Container not found at ${a}, 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}`)}a===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: ${a}`,{targetContainerPath:a,error:w.message})}}else throw M(d,"addComponent")}const l=o||`component_${Date.now()}`,c=`${a}/${l}`;let h=null,A={},T={};if(i.componentPath&&(h=await this.getComponentTemplate(i.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(i.componentPath){const d=await this.fetch.get(`${i.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 O=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}/${O}`,resourceType:D,type:"column",isContainer:R["cq:isContainer"]===!0||R["cq:isContainer"]==="true"}:b[E]={path:`${c}/${O}`,resourceType:D,isContainer:R["cq:isContainer"]===!0||R["cq:isContainer"]==="true"},u(R,O,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}/${O}`,type:"container",children:Object.keys(R).filter(D=>!D.startsWith("jcr:")&&!D.startsWith("sling:")&&!D.startsWith("cq:"))},u(R,O,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:i.componentPath||"Not found",requiredProperties:i.requiredProperties,validationPassed:i.requiredProperties.length===0||i.requiredProperties.every(d=>d in s&&s[d]!==null&&s[d]!==void 0&&s[d]!==""),hasTemplate:h!==null,templatePath:h?`${i.componentPath}/cq:template`:void 0,templateChildNodesCount:Object.keys(T).length,fieldDefinitions:Object.keys(i.fieldDefinitions).length>0?i.fieldDefinitions:void 0},containerPath:a,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 r=new URLSearchParams;r.append(":operation","delete"),await this.fetch.post(t,r),n=!0}catch(r){if(r?.status===405||r?.response?.status===405||r?.status===403||r?.response?.status===403)try{await this.fetch.delete(t),n=!0}catch(o){throw p.error("Both POST and DELETE failed:",r.response?.status,o.response?.status),r}else throw p.error("DELETE failed:",r.response?.status,r.response?.data),r}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 r=[];for(const o of Array.isArray(t)?t:[t])try{const s=new URLSearchParams;s.append("cmd","Deactivate"),s.append("path",o),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const i=await this.fetch.post("/bin/replicate.json",s);r.push({path:o,success:!0,response:i})}catch(s){r.push({path:o,success:!1,error:s.message})}return y({success:r.every(o=>o.success),results:r,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 r=new URLSearchParams;r.append("cmd","Activate"),r.append("path",t);let o;return n?(r.append("ignoredeactivated","false"),r.append("onlymodified","false"),r.append("deep","true"),o=await this.fetch.post("/libs/replication/treeactivation.html",r)):o=await this.fetch.post("/bin/replicate.json",r),y({success:!0,activatedPath:t,activateTree:n,response:o,timestamp:new Date().toISOString()},"activatePage")}catch(r){try{const o=await this.fetch.post("/bin/wcmcommand",{cmd:"activate",path:t,ignoredeactivated:!1,onlymodified:!1});return y({success:!0,activatedPath:t,activateTree:n,response:o,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"activatePage")}catch{throw M(r,"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 r=new URLSearchParams;r.append("cmd","Deactivate"),r.append("path",t),r.append("ignoredeactivated","false"),r.append("onlymodified","false"),n&&r.append("deep","true");const o=await this.fetch.post("/bin/replicate.json",r);return y({success:!0,deactivatedPath:t,deactivateTree:n,response:o,timestamp:new Date().toISOString()},"deactivatePage")}catch(r){try{const o=await this.fetch.post("/bin/wcmcommand",{cmd:"deactivate",path:t,ignoredeactivated:!1,onlymodified:!1});return y({success:!0,deactivatedPath:t,deactivateTree:n,response:o,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"deactivatePage")}catch{throw M(r,"deactivatePage")}}},"deactivatePage")}async updateAsset(e){return C(async()=>{const{assetPath:t,metadata:n,fileContent:r,mimeType:o}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});const s=new URLSearchParams;r&&(s.append("file",r),o&&s.append("jcr:content/jcr:mimeType",o)),n&&typeof n=="object"&&Object.entries(n).forEach(([i,a])=>{s.append(`jcr:content/metadata/${i}`,String(a))});try{const i=await this.fetch.post(t,s),a=await this.fetch.get(`${t}.json`);return y({success:!0,assetPath:t,updatedMetadata:n,updateResponse:i,assetData:a,timestamp:new Date().toISOString()},"updateAsset")}catch(i){throw M(i,"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",r="/settings/wcm/templates";return t=t.replace(/\/+$/,""),t.startsWith("/content/")&&(t=t.replace("/content","")),t.startsWith(n)||(t=`${n}/${t.replace(/^\//,"")}`),t.endsWith(r)||(t+=r),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`),r=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([o,s])=>{o.startsWith("jcr:")||o.startsWith("sling:")||s&&typeof s=="object"&&s["jcr:content"]&&r.push({name:o,path:`${t}/${o}`,title:s["jcr:content"]["jcr:title"]||o,description:s["jcr:content"]["jcr:description"],allowedPaths:s["jcr:content"].allowedPaths,ranking:s["jcr:content"].ranking||0})}),y({sitePath:e,templates:r,totalCount:r.length,source:"site-specific"},"getTemplates")}catch{}try{const t=["/apps/wcm/core/content/sites/templates","/libs/wcm/core/content/sites/templates"],n=[];for(const r of t)try{const o=await this.fetch.get(`${r}.json`,{":depth":"2"});o&&typeof o=="object"&&Object.entries(o).forEach(([s,i])=>{s.startsWith("jcr:")||s.startsWith("sling:")||i&&typeof i=="object"&&n.push({name:s,path:`${r}/${s}`,title:i["jcr:content"]?.["jcr:title"]||s,description:i["jcr:content"]?.["jcr:description"],allowedPaths:i["jcr:content"]?.allowedPaths,ranking:i["jcr:content"]?.ranking||0,source:r.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||[]},r=(o,s="")=>{if(!(!o||typeof o!="object")){if(o.components){const i=Object.keys(o.components);n.allowedComponents.push(...i)}Object.entries(o).forEach(([i,a])=>{typeof a=="object"&&a!==null&&!i.startsWith("jcr:")&&r(a,s?`${s}/${i}`:i)})}};return r(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:r=!1}=e;if(!Array.isArray(t)||t.length===0)throw $(P.INVALID_PARAMETERS,"Updates array is required and cannot be empty");const o=[];if(n)for(const i of t)try{await this.fetch.get(`${i.componentPath}.json`)}catch(a){if(a.response?.status===404&&(o.push({componentPath:i.componentPath,success:!1,error:`Component not found: ${i.componentPath}`,phase:"validation"}),!r))return y({success:!1,message:"Bulk update failed during validation phase",results:o,totalUpdates:t.length,successfulUpdates:0},"bulkUpdateComponents")}let s=0;for(const i of t)try{const a=await this.updateComponent({componentPath:i.componentPath,properties:i.properties});o.push({componentPath:i.componentPath,success:!0,result:a,phase:"update"}),s++}catch(a){if(o.push({componentPath:i.componentPath,success:!1,error:a.message,phase:"update"}),!r)break}return y({success:s===t.length,message:`Bulk update completed: ${s}/${t.length} successful`,results:o,totalUpdates:t.length,successfulUpdates:s,failedUpdates:t.length-s},"bulkUpdateComponents")},"bulkUpdateComponents")}async convertComponents(e){return C(async()=>{const{pagePath:t,sourceResourceType:n,targetResourceType:r,requiredProperties:o={},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(!r||typeof r!="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 a=await this.scanPageComponents(t),c=(a.data?.components||a.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:r,componentsFound:0,componentsConverted:0},"convertComponents");p.log(`Found ${c.length} components with resourceType: ${n}`);const A=(await this.getComponentDefinition(r)).requiredProperties||[],T=[];if(A.length>0&&(A.forEach(b=>{(!(b in o)||o[b]===null||o[b]===void 0||o[b]==="")&&T.push(b)}),T.length>0))return y({message:"Target component requires properties that are not provided",pagePath:t,sourceResourceType:n,targetResourceType:r,componentsFound:c.length,componentsConverted:0,targetComponentRequiredProperties:A,missingRequiredProperties:T,providedProperties:Object.keys(o),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",r),Object.entries(o).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"]!==r)throw new Error("Component creation verification failed: resourceType mismatch");m.push({sourceComponentPath:d,targetComponentPath:d,success:!0,message:`Successfully converted component from ${n} to ${r}`}),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:r,componentsFound:c.length,componentsConverted:j,componentsFailed:S,results:m},"convertComponents")}return y({message:`Converted ${j} of ${c.length} components from ${n} to ${r}`,pagePath:t,sourceResourceType:n,targetResourceType:r,componentsFound:c.length,componentsConverted:j,componentsFailed:S,results:m},"convertComponents")},"convertComponents")}async bulkConvertComponents(e){return C(async()=>{const{pagePaths:t,searchPath:n,depth:r=2,limit:o=50,sourceResourceType:s,targetResourceType:i,requiredProperties:a={},continueOnError:l=!0}=e;if(!s||typeof s!="string")throw $(P.INVALID_PARAMETERS,"Source resource type is required and must be a string");if(!i||typeof i!="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 ${r}`);const d=await this.listPages(n,r,o),g=d.data?.pages||d.pages||[];if(g.length===0)return y({message:`No pages found under ${n}`,searchPath:n,sourceResourceType:s,targetResourceType:i,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:i,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:i,requiredProperties:a,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:i,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:i,pagesProcessed:c.length,pagesSucceeded:A,pagesFailed:T,totalComponentsFound:m,totalComponentsConverted:j,totalComponentsFailed:S,pageResults:h},"bulkConvertComponents")},"bulkConvertComponents")}async getNodeContent(e,t=1){return C(async()=>{const n=`${e}.json`,r=await this.fetch.get(n,{":depth":t.toString()});return{path:e,depth:t,content:r,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 r=`${t}/settings/wcm/templates`;try{const o=await this.fetch.get(`${r}.3.json`),s=[];return o&&typeof o=="object"&&Object.entries(o).forEach(([i,a])=>{if(!(i.startsWith("jcr:")||i.startsWith("sling:"))&&a&&typeof a=="object"&&a["jcr:content"]){const l=`${r}/${i}`,c=a["jcr:content"],h=a?.structure?.["jcr:content"]||{};s.push({name:i,path:l,title:c["jcr:title"]||i,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((i,a)=>i.ranking!==a.ranking?a.ranking-i.ranking:i.name.localeCompare(a.name)),y({parentPath:e,templatesPath:r,templates:s,totalCount:s.length,availableTemplates:s.filter(i=>i.status==="enabled")},"getAvailableTemplates")}catch(o){if(o.response?.status===404){const s="/libs/wcm/foundation/templates",i=await this.fetch.get(`${s}.json`,{":depth":"2"}),a=[];return i&&typeof i=="object"&&Object.entries(i).forEach(([l,c])=>{l.startsWith("jcr:")||l.startsWith("sling:")||c&&typeof c=="object"&&a.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:a,totalCount:a.length,availableTemplates:a,fallbackUsed:!0},"getAvailableTemplates")}throw o}},"getAvailableTemplates")}async createPageWithTemplate(e){return C(async()=>{const{parentPath:t,title:n,template:r,name:o,properties:s={},resourceType:i=""}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});if(!o&&!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:o,title:n}});const a=n||(o?o.replace(/-/g," ").replace(/\b\w/g,f=>f.toUpperCase()):"");let l=r,c=i;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=o||(a?a.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":a,"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,O,D=10)=>{if(!(D<=0)){for(const[x,L]of Object.entries(O))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}`,W=L;try{try{await this.fetch.get(`${q}.json`),W&&typeof W=="object"&&await E(q,W,D-1)}catch{const _=new URLSearchParams;W["jcr:primaryType"]?_.append("jcr:primaryType",W["jcr:primaryType"]):_.append("jcr:primaryType","nt:unstructured"),Object.entries(W).forEach(([F,N])=>{F!=="jcr:primaryType"&&(F.startsWith("jcr:")&&F!=="jcr:primaryType"||typeof N=="object"&&!Array.isArray(N)||N!=null&&(Array.isArray(N)?N.forEach(U=>{_.append(F,String(U))}):_.append(F,String(N))))}),await this.fetch.post(q,_),p.log(`\u2705 Created initial node: ${q}`),await E(q,W,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 r=n["jcr:content"];if(!r||typeof r!="object")throw $(P.INVALID_PARAMETERS,"Invalid template structure: jcr:content not found",{templatePath:e,templateDataKeys:Object.keys(n)});const o=r.allowedPaths,s=Array.isArray(o)?o:o?[o]:[],i=r.allowedParents,a=Array.isArray(i)?i:i?[i]:[];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(a.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=a.includes(h),c||(A=`Template '${e}' cannot be used under parent page with template '${h}'. Allowed parent templates: ${a.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: ${a.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: ${a.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: ${a.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: ${a.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:a,parentTemplate:h,pathAllowed:l,parentAllowed:c})}return y({templatePath:e,targetPath:t,isValid:!0,templateTitle:r["jcr:title"]||"Untitled Template",templateDescription:r["jcr:description"]||"",allowedPaths:s,allowedParents:a,parentTemplate:h,restrictions:{hasPathRestrictions:s.length>0,hasParentRestrictions:a.length>0,allowedPaths:s,allowedParents:a,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 r=n["jcr:content"],o={templatePath:e,title:r["jcr:title"]||"Untitled Template",description:r["jcr:description"]||"",thumbnail:r.thumbnail||"",allowedPaths:r.allowedPaths||[],status:r.status||"enabled",ranking:r.ranking||0,templateType:r.templateType||"page",lastModified:r["cq:lastModified"],createdBy:r["jcr:createdBy"],policies:r.policies||{},structure:r.structure||{},initialContent:r.initial||{}};return t&&(this.templateCache.set(e,o),this.templateCacheExpiry.set(e,Date.now()+this.TEMPLATE_CACHE_TTL)),y(o,"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=[],r=async(o,s=5)=>{if(!(s<=0))try{const i=await this.fetch.get(`${o}.${Math.min(s,3)}.json`);if(i&&typeof i=="object")for(const[a,l]of Object.entries(i)){const c=l;if(!(a.startsWith("jcr:")||a.startsWith("sling:")||a.startsWith("rep:")||a.startsWith("oak:"))&&c&&typeof c=="object"){const h=`${o}/${a}`,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=a,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:a,title:j,description:S,path:h,resourceType:d,componentGroup:b||"General",primaryType:A,hasDialog:T})}else await r(h,s-1)}}}catch(i){i.message?.includes("404")?p.warn(`Path not found: ${o}`):p.warn(`Error fetching components from ${o}: ${i.message}`)}};return await r(t,5),n.sort((o,s)=>o.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"},r=t.map(o=>{const s=o.uri||"",i=s.replace("/var/workflow/models/",""),a=n[i]||"Custom workflow model";return{uri:s,modelId:i,description:a,...o}});return y({models:r,totalCount:r.length,commonWorkflows:Object.entries(n).map(([o,s])=>({modelId:o,uri:`/var/workflow/models/${o}`,description:s}))},"listWorkflowModels")},"listWorkflowModels")}async startWorkflow(e,t,n="JCR_PATH"){return C(async()=>{const r=e.startsWith("/var/workflow/models/")?e:`/var/workflow/models/${e}`,o=new URLSearchParams;o.append("model",r),o.append("payloadType",n),o.append("payload",t);const s=await this.fetch.postWithHeaders("/etc/workflow/instances",o,{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 i=null,a=null;const l=s.headers.get("Location");if(l){i=l.startsWith("http")?new URL(l).pathname:l;const c=i.split("/").filter(h=>h);c.length>0&&(a=c[c.length-1])}else try{const c=await s.text();if(c){const h=JSON.parse(c);if(h&&h.uri){i=h.uri;const A=i.split("/").filter(T=>T);A.length>0&&(a=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:r,payload:t,payloadType:n,instanceId:a,instancePath:i,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),r=Array.isArray(n)?n:n?.instances||[];return y({instances:r,totalCount:r.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}`,r=new URLSearchParams;r.append("state",t);const o=await this.fetch.post(n,r,{headers:{Accept:"application/json"}});return y({instanceId:e,instancePath:n,newState:t,response:o,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 r=t||"";if(!r){const a=(await this.fetch.get(`${e}.routes.json`))?.routes||[];if(a.length===0)throw $(P.INVALID_PARAMETERS,"No routes available for this work item");const l=a[0];if(!l?.rid)throw $(P.INVALID_PARAMETERS,"No valid route ID found in available routes");r=l.rid,p.log(`No route specified, using first available route: ${l.label||"Unknown"} (${r})`)}const o=new URLSearchParams;o.append("item",e),o.append("route",r),n&&o.append("comment",n);const s=await this.fetch.post("/bin/workflow/inbox",o,{headers:{Accept:"application/json"}});return y({workItemPath:e,routeId:r,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 r=await this.fetch.post("/bin/workflow/inbox",n,{headers:{Accept:"application/json"}});return y({workItemPath:e,delegatee:t,response:r,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)}}
|
|
1
|
+
"use strict";import{getAEMConfig as B,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 k}from"./aem.fetch.js";import{ContentFragmentManager as J}from"./aem.content-fragments.js";import{ExperienceFragmentManager as H}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=B({}),this.isAEMaaCS=this.isConfigAEMaaCS(),this.fetch=new k({host:this.config.aem.host,auth:this.config.aem.auth,timeout:this.aemConfig.queries.timeoutMs}),this.contentFragments=new J(this.fetch,this.isAEMaaCS),this.experienceFragments=new H(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 r=[],o=[];return t==="text"&&!n.text&&!n.richText&&r.push("Text component should have text or richText property"),t==="image"&&!n.fileReference&&!n.src&&o.push("Image component requires fileReference or src property"),{valid:o.length===0,errors:o,warnings:r,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 r=n["sling:resourceType"];if(r){p.log(`Validating properties against component definition for: ${r}`);const i=await this.getComponentDefinition(r);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 ${r}: ${c}`,{resourceType:r,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 o=new URLSearchParams;r&&o.append("sling:resourceType",r),Object.entries(e.properties).forEach(([i,l])=>{l==null?o.append(`${i}@Delete`,""):Array.isArray(l)?l.forEach(c=>{o.append(`${i}`,c.toString())}):typeof l=="object"?o.append(i,JSON.stringify(l)):o.append(i,l.toString())});const s=await this.fetch.post(e.componentPath,o,{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){return C(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),r=[],o=(s,a)=>{!s||typeof s!="object"||(s["sling:resourceType"]&&r.push({path:a,resourceType:s["sling:resourceType"],properties:{...s}}),Object.entries(s).forEach(([i,l])=>{if(typeof l=="object"&&l!==null&&!i.startsWith("rep:")&&!i.startsWith("oak:")){const c=a?`${a}/${i}`:i;o(l,c)}}))};return n["jcr:content"]?o(n["jcr:content"],"jcr:content"):o(n,e),y({pagePath:e,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(([r,o])=>{r.startsWith("jcr:")||r.startsWith("sling:")||o&&typeof o=="object"&&o["jcr:content"]&&n.push({name:r,path:`/content/${r}`,title:o["jcr:content"]["jcr:title"]||r,template:o["jcr:content"]["cq:template"],lastModified:o["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),r=[];let o=null,s="";return Object.entries(n).forEach(([a,i])=>{(a==="master"||a==="language-masters")&&i&&typeof i=="object"&&(o=i,s=`/content/${e}/${a}`)}),o?(Object.entries(o).forEach(([a,i])=>{a.startsWith("jcr:")||a.startsWith("sling:")||i&&typeof i=="object"&&r.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:r},"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),r={},o=(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}`;r[u]={path:h,title:T?.["jcr:title"]||l,language:j||`${g}_${d}`,country:d}}o(c,h,A)}})};return o(n,`/content/${e}`,[]),y({site:e,locales:r,totalCount:Object.keys(r).length},"fetchAvailableLocales")},"fetchAvailableLocales")}async getAllTextContent(e){return C(async()=>{let t;try{const o=`${e}.infinity.json`;t=await this.fetch.get(o)}catch(o){if(o.message?.includes("300")||o.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(o,"getAllTextContent")}}}else throw M(o,"getAllTextContent")}const n=[],r=(o,s)=>{!o||typeof o!="object"||((o.text||o["jcr:title"]||o["jcr:description"])&&n.push({path:s,title:o["jcr:title"],text:o.text,description:o["jcr:description"]}),Object.entries(o).forEach(([a,i])=>{if(typeof i=="object"&&i!==null&&!a.startsWith("rep:")&&!a.startsWith("oak:")){const l=s?`${s}/${a}`:a;r(i,l)}}))};return t["jcr:content"]?r(t["jcr:content"],"jcr:content"):r(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),r=[],o=(s,a)=>{!s||typeof s!="object"||((s.fileReference||s.src)&&r.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;o(l,c)}}))};return n["jcr:content"]?o(n["jcr:content"],"jcr:content"):o(n,e),y({pagePath:e,images:r},"getPageImages")},"getPageImages")}async updateImagePath(e,t){return C(async()=>this.updateComponent({componentPath:e,properties:{fileReference:t}}),"updateImagePath")}async getPageContent(e){return C(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t);return y({pagePath:e,content:n},"getPageContent")},"getPageContent")}async listChildren(e,t=1){return C(async()=>{try{const n=await this.fetch.get(`${e}.${t}.json`),r=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([o,s])=>{if(!(o.startsWith("jcr:")||o.startsWith("sling:")||o.startsWith("cq:")||o.startsWith("rep:")||o.startsWith("oak:")||o==="jcr:content")&&s&&typeof s=="object"){const a=`${e}/${o}`;r.push({name:o,path:a,primaryType:s["jcr:primaryType"]||"nt:unstructured",title:s["jcr:content"]?.["jcr:title"]||s["jcr:title"]||o,lastModified:s["jcr:content"]?.["cq:lastModified"]||s["cq:lastModified"],resourceType:s["jcr:content"]?.["sling:resourceType"]||s["sling:resourceType"]})}}),r}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(o=>({name:o.name||o.path?.split("/").pop(),path:o.path,primaryType:o["jcr:primaryType"]||"cq:Page",title:o["jcr:content/jcr:title"]||o.title||o.name,lastModified:o["jcr:content/cq:lastModified"],resourceType:o["jcr:content/sling:resourceType"]}));throw n}},"listChildren")}async listPages(e,t=1,n=20){return C(async()=>{try{const r=await this.fetch.get(`${e}.${t}.json`),o=[],s=(a,i,l)=>{l>t||o.length>=n||Object.entries(a).forEach(([c,h])=>{if(!(o.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"&&o.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 r&&typeof r=="object"&&s(r,e,0),y({siteRoot:e,pages:o,pageCount:o.length,depth:t,limit:n,totalChildrenScanned:o.length},"listPages")}catch(r){if(p.warn("JSON API failed, falling back to QueryBuilder:",r.message),r.response?.status===404||r.response?.status===403){const o=await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":t.toString(),"p.limit":n.toString(),"p.hits":"full"}),s=(o.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:o.total||s.length,fallbackUsed:"QueryBuilder"},"listPages")}throw r}},"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 r=await this.fetch.get("/bin/querybuilder.json",{path:"/content",type:"cq:Page",fulltext:e,"p.limit":t});return{query:e,results:r.hits||[],total:r.total||0,limit:t}},"executeJCRQuery")}async getPageProperties(e){return C(async()=>{const t=`${e}/jcr:content.json`,n=await this.fetch.get(t),r={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:r},"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),r=n["jcr:content"]?.metadata||{};return y({assetPath:e,metadata:r,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(r){if(r?.status===405||r?.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:",r.response?.status,r.response?.data),r}return y({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deletePage")},"deletePage")}async createComponent(e){return C(async()=>{const{pagePath:t,componentPath:n,componentType:r,resourceType:o,properties:s={},name:a}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const i=a||`${r}_${Date.now()}`,l=n||`${t}/jcr:content/${i}`;return await this.fetch.post(l,{"jcr:primaryType":"nt:unstructured","sling:resourceType":o,...s,":operation":"import",":contentType":"json",":replace":"true"}),y({success:!0,componentPath:l,componentType:r,resourceType:o,properties:s,timestamp:new Date().toISOString()},"createComponent")},"createComponent")}async getComponentDefinition(e,t=new Set){const n=[],r={};let o="";if(t.has(e))return p.warn(`Circular reference detected for resourceType: ${e}`),{componentPath:o,requiredProperties:n,fieldDefinitions:r};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){o=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"),r[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 ${o}, required properties: ${n.join(", ")}, field definitions: ${Object.keys(r).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])=>{r[h]||(r[h]=A)}),!o&&c.componentPath&&(o=c.componentPath)}return{componentPath:o,requiredProperties:n,fieldDefinitions:r}}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=[],r=[],o={};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),o[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),o[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),o[s]={provided:i,expected:"number",message:l}}}}return{valid:n.length===0,errors:n,warnings:r,invalidFields:o}}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 r=Object.keys(n).filter(o=>!o.startsWith("jcr:")&&!o.startsWith("sling:"));return r.length>0&&p.log(`Template child nodes: ${r.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,r]of Object.entries(t))try{const o=`${e}/${n}`,s={...r};s["jcr:primaryType"]||(s["jcr:primaryType"]="nt:unstructured"),await this.fetch.post(o,{...s,":operation":"import",":contentType":"json",":replace":"true"}),p.log(`Created template child node: ${o}`)}catch(o){p.warn(`Failed to create template child node ${n}: ${o.message}`)}}}async addComponent(e){return C(async()=>{const{pagePath:t,resourceType:n,containerPath:r,name:o,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(r)r.startsWith("/")?i=r:r.includes("jcr:content")?i=`${t}/${r}`:i=`${t}/jcr:content/${r}`;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=o||`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 W=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}/${W}`,resourceType:D,type:"column",isContainer:R["cq:isContainer"]===!0||R["cq:isContainer"]==="true"}:b[E]={path:`${c}/${W}`,resourceType:D,isContainer:R["cq:isContainer"]===!0||R["cq:isContainer"]==="true"},u(R,W,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}/${W}`,type:"container",children:Object.keys(R).filter(D=>!D.startsWith("jcr:")&&!D.startsWith("sling:")&&!D.startsWith("cq:"))},u(R,W,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 r=new URLSearchParams;r.append(":operation","delete"),await this.fetch.post(t,r),n=!0}catch(r){if(r?.status===405||r?.response?.status===405||r?.status===403||r?.response?.status===403)try{await this.fetch.delete(t),n=!0}catch(o){throw p.error("Both POST and DELETE failed:",r.response?.status,o.response?.status),r}else throw p.error("DELETE failed:",r.response?.status,r.response?.data),r}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 r=[];for(const o of Array.isArray(t)?t:[t])try{const s=new URLSearchParams;s.append("cmd","Deactivate"),s.append("path",o),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);r.push({path:o,success:!0,response:a})}catch(s){r.push({path:o,success:!1,error:s.message})}return y({success:r.every(o=>o.success),results:r,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 r=new URLSearchParams;r.append("cmd","Activate"),r.append("path",t);let o;return n?(r.append("ignoredeactivated","false"),r.append("onlymodified","false"),r.append("deep","true"),o=await this.fetch.post("/libs/replication/treeactivation.html",r)):o=await this.fetch.post("/bin/replicate.json",r),y({success:!0,activatedPath:t,activateTree:n,response:o,timestamp:new Date().toISOString()},"activatePage")}catch(r){try{const o=await this.fetch.post("/bin/wcmcommand",{cmd:"activate",path:t,ignoredeactivated:!1,onlymodified:!1});return y({success:!0,activatedPath:t,activateTree:n,response:o,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"activatePage")}catch{throw M(r,"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 r=new URLSearchParams;r.append("cmd","Deactivate"),r.append("path",t),r.append("ignoredeactivated","false"),r.append("onlymodified","false"),n&&r.append("deep","true");const o=await this.fetch.post("/bin/replicate.json",r);return y({success:!0,deactivatedPath:t,deactivateTree:n,response:o,timestamp:new Date().toISOString()},"deactivatePage")}catch(r){try{const o=await this.fetch.post("/bin/wcmcommand",{cmd:"deactivate",path:t,ignoredeactivated:!1,onlymodified:!1});return y({success:!0,deactivatedPath:t,deactivateTree:n,response:o,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"deactivatePage")}catch{throw M(r,"deactivatePage")}}},"deactivatePage")}async updateAsset(e){return C(async()=>{const{assetPath:t,metadata:n,fileContent:r,mimeType:o}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});const s=new URLSearchParams;r&&(s.append("file",r),o&&s.append("jcr:content/jcr:mimeType",o)),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",r="/settings/wcm/templates";return t=t.replace(/\/+$/,""),t.startsWith("/content/")&&(t=t.replace("/content","")),t.startsWith(n)||(t=`${n}/${t.replace(/^\//,"")}`),t.endsWith(r)||(t+=r),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`),r=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([o,s])=>{o.startsWith("jcr:")||o.startsWith("sling:")||s&&typeof s=="object"&&s["jcr:content"]&&r.push({name:o,path:`${t}/${o}`,title:s["jcr:content"]["jcr:title"]||o,description:s["jcr:content"]["jcr:description"],allowedPaths:s["jcr:content"].allowedPaths,ranking:s["jcr:content"].ranking||0})}),y({sitePath:e,templates:r,totalCount:r.length,source:"site-specific"},"getTemplates")}catch{}try{const t=["/apps/wcm/core/content/sites/templates","/libs/wcm/core/content/sites/templates"],n=[];for(const r of t)try{const o=await this.fetch.get(`${r}.json`,{":depth":"2"});o&&typeof o=="object"&&Object.entries(o).forEach(([s,a])=>{s.startsWith("jcr:")||s.startsWith("sling:")||a&&typeof a=="object"&&n.push({name:s,path:`${r}/${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:r.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||[]},r=(o,s="")=>{if(!(!o||typeof o!="object")){if(o.components){const a=Object.keys(o.components);n.allowedComponents.push(...a)}Object.entries(o).forEach(([a,i])=>{typeof i=="object"&&i!==null&&!a.startsWith("jcr:")&&r(i,s?`${s}/${a}`:a)})}};return r(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:r=!1}=e;if(!Array.isArray(t)||t.length===0)throw $(P.INVALID_PARAMETERS,"Updates array is required and cannot be empty");const o=[];if(n)for(const a of t)try{await this.fetch.get(`${a.componentPath}.json`)}catch(i){if(i.response?.status===404&&(o.push({componentPath:a.componentPath,success:!1,error:`Component not found: ${a.componentPath}`,phase:"validation"}),!r))return y({success:!1,message:"Bulk update failed during validation phase",results:o,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});o.push({componentPath:a.componentPath,success:!0,result:i,phase:"update"}),s++}catch(i){if(o.push({componentPath:a.componentPath,success:!1,error:i.message,phase:"update"}),!r)break}return y({success:s===t.length,message:`Bulk update completed: ${s}/${t.length} successful`,results:o,totalUpdates:t.length,successfulUpdates:s,failedUpdates:t.length-s},"bulkUpdateComponents")},"bulkUpdateComponents")}async convertComponents(e){return C(async()=>{const{pagePath:t,sourceResourceType:n,targetResourceType:r,requiredProperties:o={},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(!r||typeof r!="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:r,componentsFound:0,componentsConverted:0},"convertComponents");p.log(`Found ${c.length} components with resourceType: ${n}`);const A=(await this.getComponentDefinition(r)).requiredProperties||[],T=[];if(A.length>0&&(A.forEach(b=>{(!(b in o)||o[b]===null||o[b]===void 0||o[b]==="")&&T.push(b)}),T.length>0))return y({message:"Target component requires properties that are not provided",pagePath:t,sourceResourceType:n,targetResourceType:r,componentsFound:c.length,componentsConverted:0,targetComponentRequiredProperties:A,missingRequiredProperties:T,providedProperties:Object.keys(o),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",r),Object.entries(o).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"]!==r)throw new Error("Component creation verification failed: resourceType mismatch");m.push({sourceComponentPath:d,targetComponentPath:d,success:!0,message:`Successfully converted component from ${n} to ${r}`}),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:r,componentsFound:c.length,componentsConverted:j,componentsFailed:S,results:m},"convertComponents")}return y({message:`Converted ${j} of ${c.length} components from ${n} to ${r}`,pagePath:t,sourceResourceType:n,targetResourceType:r,componentsFound:c.length,componentsConverted:j,componentsFailed:S,results:m},"convertComponents")},"convertComponents")}async bulkConvertComponents(e){return C(async()=>{const{pagePaths:t,searchPath:n,depth:r=2,limit:o=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 ${r}`);const d=await this.listPages(n,r,o),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){return C(async()=>{const n=`${e}.json`,r=await this.fetch.get(n,{":depth":t.toString()});return{path:e,depth:t,content:r,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 r=`${t}/settings/wcm/templates`;try{const o=await this.fetch.get(`${r}.3.json`),s=[];return o&&typeof o=="object"&&Object.entries(o).forEach(([a,i])=>{if(!(a.startsWith("jcr:")||a.startsWith("sling:"))&&i&&typeof i=="object"&&i["jcr:content"]){const l=`${r}/${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:r,templates:s,totalCount:s.length,availableTemplates:s.filter(a=>a.status==="enabled")},"getAvailableTemplates")}catch(o){if(o.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 o}},"getAvailableTemplates")}async createPageWithTemplate(e){return C(async()=>{const{parentPath:t,title:n,template:r,name:o,properties:s={},resourceType:a=""}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});if(!o&&!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:o,title:n}});const i=n||(o?o.replace(/-/g," ").replace(/\b\w/g,f=>f.toUpperCase()):"");let l=r,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=o||(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,W,D=10)=>{if(!(D<=0)){for(const[x,L]of Object.entries(W))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,N])=>{F!=="jcr:primaryType"&&(F.startsWith("jcr:")&&F!=="jcr:primaryType"||typeof N=="object"&&!Array.isArray(N)||N!=null&&(Array.isArray(N)?N.forEach(U=>{_.append(F,String(U))}):_.append(F,String(N))))}),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 r=n["jcr:content"];if(!r||typeof r!="object")throw $(P.INVALID_PARAMETERS,"Invalid template structure: jcr:content not found",{templatePath:e,templateDataKeys:Object.keys(n)});const o=r.allowedPaths,s=Array.isArray(o)?o:o?[o]:[],a=r.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:r["jcr:title"]||"Untitled Template",templateDescription:r["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 r=n["jcr:content"],o={templatePath:e,title:r["jcr:title"]||"Untitled Template",description:r["jcr:description"]||"",thumbnail:r.thumbnail||"",allowedPaths:r.allowedPaths||[],status:r.status||"enabled",ranking:r.ranking||0,templateType:r.templateType||"page",lastModified:r["cq:lastModified"],createdBy:r["jcr:createdBy"],policies:r.policies||{},structure:r.structure||{},initialContent:r.initial||{}};return t&&(this.templateCache.set(e,o),this.templateCacheExpiry.set(e,Date.now()+this.TEMPLATE_CACHE_TTL)),y(o,"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=[],r=async(o,s=5)=>{if(!(s<=0))try{const a=await this.fetch.get(`${o}.${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=`${o}/${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 r(h,s-1)}}}catch(a){a.message?.includes("404")?p.warn(`Path not found: ${o}`):p.warn(`Error fetching components from ${o}: ${a.message}`)}};return await r(t,5),n.sort((o,s)=>o.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"},r=t.map(o=>{const s=o.uri||"",a=s.replace("/var/workflow/models/",""),i=n[a]||"Custom workflow model";return{uri:s,modelId:a,description:i,...o}});return y({models:r,totalCount:r.length,commonWorkflows:Object.entries(n).map(([o,s])=>({modelId:o,uri:`/var/workflow/models/${o}`,description:s}))},"listWorkflowModels")},"listWorkflowModels")}async startWorkflow(e,t,n="JCR_PATH"){return C(async()=>{const r=e.startsWith("/var/workflow/models/")?e:`/var/workflow/models/${e}`,o=new URLSearchParams;o.append("model",r),o.append("payloadType",n),o.append("payload",t);const s=await this.fetch.postWithHeaders("/etc/workflow/instances",o,{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:r,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),r=Array.isArray(n)?n:n?.instances||[];return y({instances:r,totalCount:r.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}`,r=new URLSearchParams;r.append("state",t);const o=await this.fetch.post(n,r,{headers:{Accept:"application/json"}});return y({instanceId:e,instancePath:n,newState:t,response:o,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 r=t||"";if(!r){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");r=l.rid,p.log(`No route specified, using first available route: ${l.label||"Unknown"} (${r})`)}const o=new URLSearchParams;o.append("item",e),o.append("route",r),n&&o.append("comment",n);const s=await this.fetch.post("/bin/workflow/inbox",o,{headers:{Accept:"application/json"}});return y({workItemPath:e,routeId:r,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 r=await this.fetch.post("/bin/workflow/inbox",n,{headers:{Accept:"application/json"}});return y({workItemPath:e,delegatee:t,response:r,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)}}
|