@oneclick.dev/cms-core-modules 0.0.123 → 0.0.124

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.
Files changed (98) hide show
  1. package/dist/{ContentEditor-C-Wm48ai.js → ContentEditor-BcIUumah.js} +39 -39
  2. package/dist/{ContentEditor-MDGolnIP.mjs → ContentEditor-DX1bTQKs.mjs} +4910 -4793
  3. package/dist/{Detail-Dk8E6_uF.mjs → Detail-B7pe808q.mjs} +15 -14
  4. package/dist/Detail-BESGmY7z.js +1 -0
  5. package/dist/{Detail-Cn9Bk0u0.mjs → Detail-B_Q7Qkuz.mjs} +26 -25
  6. package/dist/Detail-CNiLIEh9.js +1 -0
  7. package/dist/{Detail-B_scnidb.mjs → Detail-DNxAzeCs.mjs} +35 -34
  8. package/dist/Detail-t3tOcy3X.js +1 -0
  9. package/dist/Editor-D4evcdhv.mjs +768 -0
  10. package/dist/Editor-gLLtuJbY.js +1 -0
  11. package/dist/FormStatusBadge.vue_vue_type_script_setup_true_lang-BjKpsqT9.js +1 -0
  12. package/dist/FormStatusBadge.vue_vue_type_script_setup_true_lang-D5v5AhLe.mjs +76 -0
  13. package/dist/Knowledge-B66vxz--.js +1 -0
  14. package/dist/Knowledge-BnMTTuJ7.mjs +742 -0
  15. package/dist/Overview-BE4uYPx0.mjs +745 -0
  16. package/dist/Overview-CQRpFhbJ.mjs +286 -0
  17. package/dist/Overview-CTs7sX4d.js +1 -0
  18. package/dist/Overview-Cj89XXKN.js +1 -0
  19. package/dist/Overview-D_c18mbR.mjs +248 -0
  20. package/dist/Overview-g4Rcj245.js +1 -0
  21. package/dist/TableView-6mdrL3zx.js +4 -0
  22. package/dist/{TableView-C4Zyp8d7.mjs → TableView-Du5nOE6R.mjs} +2 -2
  23. package/dist/Test-CzmOmi53.mjs +447 -0
  24. package/dist/Test-USDtNFY1.js +1 -0
  25. package/dist/UserDetail-DpUL3mhk.mjs +1483 -0
  26. package/dist/UserDetail-KRIfxaYd.js +1 -0
  27. package/dist/cms-core-modules.css +1 -1
  28. package/dist/index-CQuN5rKx.js +1 -0
  29. package/dist/index-CnDGeInm.js +1 -0
  30. package/dist/index-CqDTc5Ig.mjs +20 -0
  31. package/dist/index-CyvvAdt4.js +1 -0
  32. package/dist/index-DIRD7t5_.mjs +20 -0
  33. package/dist/index-OJuBYR60.mjs +57 -0
  34. package/dist/index.cjs.js +1 -1
  35. package/dist/index.mjs +327 -84
  36. package/dist/server-handlers.cjs.js +1 -1
  37. package/dist/server-handlers.mjs +1764 -605
  38. package/dist/src/contentManager/components/content-editor/tiptap-extensions/Container.d.ts +1 -1
  39. package/dist/src/contentManager/components/content-editor/tiptap-extensions/Row.d.ts +7 -0
  40. package/dist/src/contentManager/components/content-editor/tiptap-extensions/Stack.d.ts +7 -0
  41. package/dist/src/contentManager/components/content-editor/tiptap-extensions/helpers/resizeHistory.d.ts +9 -0
  42. package/dist/src/contentManager/components/content-editor/tiptap-menus/ElementEditorMenu.vue.d.ts +3 -1
  43. package/dist/src/firebaseAuthManager/components/DeleteUserDialog.vue.d.ts +25 -0
  44. package/dist/src/firebaseAuthManager/components/ReasonActionDialog.vue.d.ts +23 -0
  45. package/dist/src/firebaseAuthManager/components/UserStatusBadge.vue.d.ts +5 -0
  46. package/dist/src/firebaseAuthManager/config.d.ts +151 -0
  47. package/dist/src/firebaseAuthManager/index.d.ts +163 -0
  48. package/dist/src/firebaseAuthManager/index.vue.d.ts +14 -0
  49. package/dist/src/firebaseAuthManager/metadata.d.ts +2 -0
  50. package/dist/src/firebaseAuthManager/pages/Overview.vue.d.ts +2 -0
  51. package/dist/src/firebaseAuthManager/pages/UserDetail.vue.d.ts +2 -0
  52. package/dist/src/firebaseAuthManager/permissions.d.ts +5 -0
  53. package/dist/src/firebaseAuthManager/routes.d.ts +5 -0
  54. package/dist/src/firebaseAuthManager/server.d.ts +162 -0
  55. package/dist/src/firebaseAuthManager/types.d.ts +84 -0
  56. package/dist/src/formBuilder/components/FormPreviewDialog.vue.d.ts +12 -0
  57. package/dist/src/formBuilder/components/FormStatusBadge.vue.d.ts +9 -0
  58. package/dist/src/formBuilder/config.d.ts +31 -0
  59. package/dist/src/formBuilder/index.d.ts +44 -0
  60. package/dist/src/formBuilder/index.vue.d.ts +14 -0
  61. package/dist/src/formBuilder/lib/forms.d.ts +11 -0
  62. package/dist/src/formBuilder/metadata.d.ts +2 -0
  63. package/dist/src/formBuilder/pages/Editor.vue.d.ts +2 -0
  64. package/dist/src/formBuilder/pages/Overview.vue.d.ts +2 -0
  65. package/dist/src/formBuilder/permissions.d.ts +5 -0
  66. package/dist/src/formBuilder/routes.d.ts +5 -0
  67. package/dist/src/formBuilder/server.d.ts +87 -0
  68. package/dist/src/formBuilder/tools.d.ts +1 -0
  69. package/dist/src/formBuilder/types.d.ts +46 -0
  70. package/dist/src/helpbot/composables/useHelpbotApi.d.ts +18 -0
  71. package/dist/src/helpbot/config.d.ts +17 -0
  72. package/dist/src/helpbot/index.d.ts +29 -0
  73. package/dist/src/helpbot/index.vue.d.ts +2 -0
  74. package/dist/src/helpbot/metadata.d.ts +2 -0
  75. package/dist/src/helpbot/pages/Knowledge.vue.d.ts +4 -0
  76. package/dist/src/helpbot/pages/Overview.vue.d.ts +2 -0
  77. package/dist/src/helpbot/pages/Test.vue.d.ts +4 -0
  78. package/dist/src/helpbot/permissions.d.ts +5 -0
  79. package/dist/src/helpbot/routes.d.ts +5 -0
  80. package/dist/src/helpbot/types.d.ts +42 -0
  81. package/dist/src/index.d.ts +3 -0
  82. package/dist/src/server-handlers.d.ts +2 -0
  83. package/dist/types-9N-DKxNj.mjs +250 -0
  84. package/dist/types-BMraG1kN.js +1 -0
  85. package/dist/types-Cem0vPo_.js +1 -0
  86. package/dist/types-ibC3-e1N.mjs +2630 -0
  87. package/dist/useHelpbotApi-BIIungOh.js +1 -0
  88. package/dist/useHelpbotApi-HC9JSiSn.mjs +229 -0
  89. package/dist/vee-validate-zod-BQElPD0m.js +1 -0
  90. package/dist/vee-validate-zod-CN16K2x9.mjs +1780 -0
  91. package/package.json +2 -2
  92. package/src/formBuilder/tools.ts +1 -0
  93. package/dist/Detail-BCUUNWvp.js +0 -1
  94. package/dist/Detail-DGg_xXZc.js +0 -1
  95. package/dist/Detail-Sc2qZdxj.js +0 -1
  96. package/dist/TableView-BkwoKREo.js +0 -4
  97. package/dist/vee-validate-zod-SjDT8X35.mjs +0 -4404
  98. package/dist/vee-validate-zod-VCmLnrtQ.js +0 -1
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("h3");require("vue");function O(C){const{initFirebase:R,normalizeTimestamps:A}=C,p=e.createRouter();async function l(g){const{supabase:y,instanceId:h}=g.context.module,{data:s,error:d}=await y.from("project_modules").select("config").eq("id",h).single();if(d||!s?.config)throw e.createError({statusCode:500,statusMessage:"Failed to load module config."});const c=s.config,a=c.project,n=c.productCollection||"products";if(!a)throw e.createError({statusCode:400,statusMessage:"Products module has no Firebase integration configured."});return{firebase:await R(g,a),collection:n}}return p.get("/products",e.defineEventHandler(async g=>{try{const{firebase:y,collection:h}=await l(g);return(await y.firestore().collection(h).get()).docs.map(d=>A({id:d.id,...d.data()}))}catch(y){throw console.error("Products handler — list error:",y),e.createError({statusCode:y.statusCode||500,statusMessage:y.statusMessage||"Failed to list products"})}})),p.get("/products/:productId",e.defineEventHandler(async g=>{const y=e.getRouterParam(g,"productId");if(!y)throw e.createError({statusCode:400,statusMessage:"Product ID is required."});try{const{firebase:h,collection:s}=await l(g),d=await h.firestore().collection(s).doc(y).get();if(!d.exists)throw e.createError({statusCode:404,statusMessage:"Product not found."});return A({id:d.id,...d.data()})}catch(h){throw console.error("Products handler — get error:",h),e.createError({statusCode:h.statusCode||500,statusMessage:h.statusMessage||"Failed to get product"})}})),p.get("/empty-stock",e.defineEventHandler(async g=>{const h=e.getQuery(g).category;try{const{firebase:s,collection:d}=await l(g);let c=s.firestore().collection(d).where("stock","==",0);h&&(c=c.where("collections","array-contains",h));const n=(await c.get()).docs.map(i=>A({id:i.id,...i.data()}));return{count:n.length,products:n.map(i=>({id:i.id,title:i.title,slug:i.slug,stock:i.stock,price:i.price,currency:i.currency,status:i.status}))}}catch(s){throw console.error("Products handler — empty-stock error:",s),e.createError({statusCode:s.statusCode||500,statusMessage:s.statusMessage||"Failed to query empty stock"})}})),p.handler}function V(C){const{initFirebase:R,normalizeTimestamps:A,deleteField:p}=C,l=e.createRouter(),g="privateSettings",y="refundFlow",h="metadataSchema";async function s(t){const{supabase:r,instanceId:o}=t.context.module,{data:u,error:f}=await r.from("project_modules").select("config").eq("id",o).single();if(f||!u?.config)throw e.createError({statusCode:500,statusMessage:"Failed to load module config."});const m=u.config,w=m.project,D=m.reservationsCollection||"bookings_orders",E=m.agendaCollection||"agendas";if(!w)throw e.createError({statusCode:400,statusMessage:"Appointments module has no Firebase integration configured."});return{firebase:await R(t,w),reservationsCollection:D,agendaCollection:E}}function d(t){return(t.reservations||[]).map(o=>({orderId:t.id,reservationId:o.id,customerInfo:t.customerInfo,date:o.date,startTime:o.timeslot?.startTime,endTime:o.timeslot?.endTime,spots:o.spots,status:t.status,reservationStatus:o.status,resourceId:o.resourceId,reservationPrice:o.totalPrice,reservationBasePrice:o.basePrice,reservationAddOnsPrice:o.addOnsPrice,pricingOption:o.pricingOption,amountDue:t.amountDue,amountPaid:t.amountPaid,createdAt:t.createdAt,metadata:t.metadata||{},reservationMetadata:o.metadata||{}}))}function c(t){return{showAmount:t?.showAmount!==!1,fields:Array.isArray(t?.fields)?t.fields:[]}}function a(){return p?{refundFlow:p(),refundFlowEnabled:p(),refundDialog:p()}:{refundFlow:null,refundFlowEnabled:null,refundDialog:null}}function n(t){return Array.isArray(t)?t:[]}function i(){return p?{metadataSchema:p()}:{metadataSchema:null}}return l.get("/agendas",e.defineEventHandler(async t=>{try{const{firebase:r,agendaCollection:o}=await s(t),f=(await r.firestore().collection(o).get()).docs.map(m=>{const w=m.data();return{id:m.id,serviceName:w.serviceName||"",type:w.type||"regular"}});return{count:f.length,agendas:f}}catch(r){throw console.error("Appointments handler — list agendas error:",r),e.createError({statusCode:r.statusCode||500,statusMessage:r.statusMessage||"Failed to list agendas"})}})),l.get("/agendas/:agendaId/opening-hours",e.defineEventHandler(async t=>{const r=e.getRouterParam(t,"agendaId"),u=e.getQuery(t).date;if(!r)throw e.createError({statusCode:400,statusMessage:"Agenda ID is required."});if(!u)throw e.createError({statusCode:400,statusMessage:"Date is required (YYYY-MM-DD)."});try{const{firebase:f,agendaCollection:m}=await s(t),w=await f.firestore().collection(m).doc(r).get();if(!w.exists)throw e.createError({statusCode:404,statusMessage:"Agenda not found."});const D=w.data(),E=(D.resources||[]).filter(P=>P.isActive),I=D.exceptions||[],M=new Date(u+"T00:00:00").getDay(),k=E.map(P=>{const T=P.openingHours?.[M]||[],q=I.find(b=>{const U=u>=b.startDate&&u<=b.endDate,Q=!b.resourceIds||b.resourceIds.length===0||b.resourceIds.includes(P.id);return U&&Q});return q?{resourceId:P.id,resourceName:P.name,date:u,dayOfWeek:M,isClosed:q.isClosed,hours:q.isClosed?[]:(q.timeslots||[]).map(b=>({start:b.startTime,end:b.endTime})),isException:!0}:{resourceId:P.id,resourceName:P.name,date:u,dayOfWeek:M,isClosed:T.length===0,hours:T,isException:!1}});return{agendaId:r,serviceName:D.serviceName||"",date:u,resources:k}}catch(f){throw console.error("Appointments handler — opening hours error:",f),e.createError({statusCode:f.statusCode||500,statusMessage:f.statusMessage||"Failed to get opening hours"})}})),l.get("/agendas/:agendaId/refund-flow",e.defineEventHandler(async t=>{const r=e.getRouterParam(t,"agendaId");if(!r)throw e.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const{firebase:o,agendaCollection:u}=await s(t),m=o.firestore().collection(u).doc(r),w=m.collection(g).doc(y),[D,E]=await Promise.all([m.get(),w.get()]);if(!D.exists)throw e.createError({statusCode:404,statusMessage:"Agenda not found."});const I=E.exists?E.data()||{}:{};return{refundFlow:I.flow||{},refundFlowEnabled:I.enabled!==!1,refundDialog:c(I.dialog)}}catch(o){throw console.error("Appointments handler — refund flow load error:",o),e.createError({statusCode:o.statusCode||500,statusMessage:o.statusMessage||"Failed to load refund flow"})}})),l.patch("/agendas/:agendaId/refund-flow",e.defineEventHandler(async t=>{const r=e.getRouterParam(t,"agendaId");if(!r)throw e.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const o=await e.readBody(t),{firebase:u,agendaCollection:f}=await s(t),m=u.firestore(),w=m.collection(f).doc(r),D=w.collection(g).doc(y),E=c(o?.refundDialog);if(!(await w.get()).exists)throw e.createError({statusCode:404,statusMessage:"Agenda not found."});const S=m.batch();return S.set(D,{flow:o?.refundFlow||{},enabled:o?.refundFlowEnabled!==!1,dialog:E,updatedAt:new Date().toISOString()},{merge:!0}),S.set(w,a(),{merge:!0}),await S.commit(),{success:!0}}catch(o){throw console.error("Appointments handler — refund flow save error:",o),e.createError({statusCode:o.statusCode||500,statusMessage:o.statusMessage||"Failed to save refund flow"})}})),l.get("/agendas/:agendaId/metadata-schema",e.defineEventHandler(async t=>{const r=e.getRouterParam(t,"agendaId");if(!r)throw e.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const{firebase:o,agendaCollection:u}=await s(t),m=o.firestore().collection(u).doc(r),w=m.collection(g).doc(h),[D,E]=await Promise.all([m.get(),w.get()]);if(!D.exists)throw e.createError({statusCode:404,statusMessage:"Agenda not found."});const I=E.exists?E.data()||{}:{};return{metadataSchema:n(I.schema)}}catch(o){throw console.error("Appointments handler — metadata schema load error:",o),e.createError({statusCode:o.statusCode||500,statusMessage:o.statusMessage||"Failed to load metadata schema"})}})),l.patch("/agendas/:agendaId/metadata-schema",e.defineEventHandler(async t=>{const r=e.getRouterParam(t,"agendaId");if(!r)throw e.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const o=await e.readBody(t),{firebase:u,agendaCollection:f}=await s(t),m=u.firestore(),w=m.collection(f).doc(r),D=w.collection(g).doc(h);if(!(await w.get()).exists)throw e.createError({statusCode:404,statusMessage:"Agenda not found."});const I=m.batch();return I.set(D,{schema:n(o?.metadataSchema),updatedAt:new Date().toISOString()},{merge:!0}),I.set(w,i(),{merge:!0}),await I.commit(),{success:!0}}catch(o){throw console.error("Appointments handler — metadata schema save error:",o),e.createError({statusCode:o.statusCode||500,statusMessage:o.statusMessage||"Failed to save metadata schema"})}})),l.get("/appointments",e.defineEventHandler(async t=>{const r=e.getQuery(t),o=Math.min(Number(r.quantity)||20,100),u=r.status||"confirmed";try{const{firebase:f,reservationsCollection:m}=await s(t),D=(await f.firestore().collection(m).orderBy("createdAt","desc").where("status","==",u).limit(o).get()).docs.flatMap(E=>d(A({id:E.id,...E.data()})));return{count:D.length,appointments:D}}catch(f){throw console.error("Appointments handler — list error:",f),e.createError({statusCode:f.statusCode||500,statusMessage:f.statusMessage||"Failed to list appointments"})}})),l.get("/appointments/find",e.defineEventHandler(async t=>{const r=e.getQuery(t),o=(r.name||"").toLowerCase().trim(),u=(r.email||"").toLowerCase().trim(),f=r.date;try{const{firebase:m,reservationsCollection:w}=await s(t);let D=m.firestore().collection(w);u&&(D=D.where("customerInfo.email","==",u));let I=(await D.orderBy("createdAt","desc").limit(200).get()).docs.flatMap(S=>d(A({id:S.id,...S.data()})));return f&&(I=I.filter(S=>S.date===f)),o&&(I=I.filter(S=>{const M=(S.customerInfo?.firstName||"").toLowerCase(),k=(S.customerInfo?.lastName||"").toLowerCase();return M.includes(o)||k.includes(o)||`${M} ${k}`.includes(o)})),{count:I.length,appointments:I}}catch(m){throw console.error("Appointments handler — find error:",m),e.createError({statusCode:m.statusCode||500,statusMessage:m.statusMessage||"Failed to find appointments"})}})),l.get("/appointments/:appointmentId",e.defineEventHandler(async t=>{const r=e.getRouterParam(t,"appointmentId");if(!r)throw e.createError({statusCode:400,statusMessage:"Appointment ID is required."});try{const{firebase:o,reservationsCollection:u}=await s(t),f=await o.firestore().collection(u).doc(r).get();if(!f.exists)throw e.createError({statusCode:404,statusMessage:"Appointment not found."});const m=A({id:f.id,...f.data()});return{...m,reservations:d(m)}}catch(o){throw console.error("Appointments handler — get error:",o),e.createError({statusCode:o.statusCode||500,statusMessage:o.statusMessage||"Failed to get appointment"})}})),l.handler}const F="https://analyticsdata.googleapis.com/v1beta",x=["https://www.googleapis.com/auth/analytics.readonly","https://www.googleapis.com/auth/webmasters.readonly"],H="https://searchconsole.googleapis.com/webmasters/v3";function B(C){const{decrypt:R,getGoogleAccessToken:A}=C,p=e.createRouter();async function l(s){const{supabase:d,instanceId:c}=s.context.module,{data:a,error:n}=await d.from("project_modules").select("config").eq("id",c).single();if(n||!a?.config)throw e.createError({statusCode:500,statusMessage:"Failed to load module config."});const i=a.config,t=i.propertyId,r=i.serviceAccount,o=i.siteUrl||"";if(!r)throw e.createError({statusCode:400,statusMessage:"No Google Service Account configured for this module."});if(!t)throw e.createError({statusCode:400,statusMessage:"No GA4 Property ID configured for this module."});const{data:u,error:f}=await d.from("integrations").select("config").eq("id",r).single();let m=u?.config;if(f||!m){const{data:S,error:M}=await d.from("agency_integrations").select("config").eq("id",r).single();if(M||!S?.config)throw e.createError({statusCode:500,statusMessage:"Failed to load Google Service Account credentials."});m=S.config}const w=R(m.clientEmail),D=R(m.privateKey),E=R(m.projectId);return{accessToken:await A({clientEmail:w,privateKey:D,projectId:E},x),propertyId:t,siteUrl:o}}async function g(s,d,c){const a=await fetch(`${F}/properties/${d}:runReport`,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify(c)});if(!a.ok){const n=await a.json().catch(()=>({}));throw console.error("GA4 runReport failed:",n),e.createError({statusCode:a.status,statusMessage:n?.error?.message||"GA4 API request failed"})}return a.json()}p.get("/report",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=n.match(/^(\d+)daysAgo$/),r=t?parseInt(t[1],10):30,o=`${r*2}daysAgo`,u=`${r+1}daysAgo`,f=[{name:"sessions"},{name:"totalUsers"},{name:"screenPageViews"},{name:"bounceRate"},{name:"averageSessionDuration"},{name:"newUsers"},{name:"engagementRate"},{name:"sessionsPerUser"},{name:"screenPageViewsPerSession"}];let m,w=!0;try{m=await g(d,c,{dateRanges:[{startDate:n,endDate:i,name:"current"},{startDate:o,endDate:u,name:"previous"}],dimensions:[{name:"date"}],metrics:f,metricAggregations:["TOTAL"],orderBys:[{dimension:{dimensionName:"date"}}]})}catch{w=!1,m=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"date"}],metrics:f,metricAggregations:["TOTAL"],orderBys:[{dimension:{dimensionName:"date"}}]})}return $(m,w)})),p.get("/realtime",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=await fetch(`${F}/properties/${c}:runRealtimeReport`,{method:"POST",headers:{Authorization:`Bearer ${d}`,"Content-Type":"application/json"},body:JSON.stringify({dimensions:[{name:"unifiedScreenName"}],metrics:[{name:"activeUsers"}],orderBys:[{metric:{metricName:"activeUsers"},desc:!0}],limit:5})});if(!a.ok){const r=await a.json().catch(()=>({}));throw e.createError({statusCode:a.status,statusMessage:r?.error?.message||"Realtime API failed"})}const n=await a.json(),i=(n?.rows||[]).reduce((r,o)=>r+parseInt(o.metricValues?.[0]?.value||"0",10),0),t=(n?.rows||[]).map(r=>({page:r.dimensionValues?.[0]?.value||"(not set)",activeUsers:parseInt(r.metricValues?.[0]?.value||"0",10)}));return{activeUsers:i,activePages:t}})),p.get("/top-pages",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=parseInt(a.limit||"10",10),r=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"pagePath"}],metrics:[{name:"screenPageViews"},{name:"totalUsers"},{name:"averageSessionDuration"}],orderBys:[{metric:{metricName:"screenPageViews"},desc:!0}],limit:t*2}),o=v(r,"pagePath"),u=new Map;for(const m of o.rows){const w=m.pagePath.toLowerCase().replace(/\/+$/,""),D=u.get(w);if(D){D.screenPageViews+=m.screenPageViews||0,D.totalUsers+=m.totalUsers||0;const E=D.screenPageViews;E>0&&(D.averageSessionDuration=(D.averageSessionDuration*(E-(m.screenPageViews||0))+(m.averageSessionDuration||0)*(m.screenPageViews||0))/E)}else u.set(w,{...m})}return{rows:[...u.values()].sort((m,w)=>(w.screenPageViews||0)-(m.screenPageViews||0)).slice(0,t),rowCount:o.rowCount}})),p.get("/top-sources",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"sessionSource"}],metrics:[{name:"sessions"},{name:"totalUsers"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:10});return v(t,"sessionSource")})),p.get("/devices",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"deviceCategory"}],metrics:[{name:"sessions"},{name:"totalUsers"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}]});return v(t,"deviceCategory")})),p.get("/countries",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"country"}],metrics:[{name:"sessions"},{name:"totalUsers"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:10});return v(t,"country")})),p.get("/acquisition/channels",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"sessionDefaultChannelGroup"}],metrics:[{name:"sessions"},{name:"totalUsers"},{name:"newUsers"},{name:"engagementRate"},{name:"averageSessionDuration"},{name:"screenPageViewsPerSession"},{name:"conversions"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:15});return v(t,"sessionDefaultChannelGroup")})),p.get("/acquisition/source-medium",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"sessionSourceMedium"}],metrics:[{name:"sessions"},{name:"totalUsers"},{name:"newUsers"},{name:"bounceRate"},{name:"averageSessionDuration"},{name:"conversions"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:20});return v(t,"sessionSourceMedium")})),p.get("/acquisition/referrals",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"sessionSource"}],dimensionFilter:{filter:{fieldName:"sessionMedium",stringFilter:{value:"referral"}}},metrics:[{name:"sessions"},{name:"totalUsers"},{name:"engagementRate"},{name:"averageSessionDuration"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:20});return v(t,"sessionSource")})),p.get("/acquisition/campaigns",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"sessionCampaignName"}],dimensionFilter:{notExpression:{filter:{fieldName:"sessionCampaignName",stringFilter:{value:"(not set)"}}}},metrics:[{name:"sessions"},{name:"totalUsers"},{name:"conversions"},{name:"engagementRate"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:20});return v(t,"sessionCampaignName")})),p.get("/content/all-pages",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=parseInt(a.limit||"50",10),r=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"pagePath"}],metrics:[{name:"screenPageViews"},{name:"totalUsers"},{name:"averageSessionDuration"},{name:"bounceRate"},{name:"engagementRate"},{name:"sessions"},{name:"userEngagementDuration"}],orderBys:[{metric:{metricName:"screenPageViews"},desc:!0}],limit:t});return v(r,"pagePath")})),p.get("/content/landing-pages",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"landingPagePlusQueryString"}],metrics:[{name:"sessions"},{name:"totalUsers"},{name:"bounceRate"},{name:"averageSessionDuration"},{name:"screenPageViews"},{name:"engagementRate"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:30});return v(t,"landingPagePlusQueryString")})),p.get("/content/exit-pages",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"pagePath"}],metrics:[{name:"sessions"},{name:"screenPageViews"},{name:"totalUsers"},{name:"bounceRate"}],orderBys:[{metric:{metricName:"screenPageViews"},desc:!0}],limit:20}),r=v(t,"pagePath");return r.rows=r.rows.map(o=>({...o,exitRate:o.bounceRate||0})),r})),p.get("/content/search-terms",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c,siteUrl:a}=await l(s),n=e.getQuery(s),i=n.startDate||"30daysAgo",t=n.endDate||"today";if(a)try{const u=`${H}/sites/${encodeURIComponent(a)}/searchAnalytics/query`,f=await fetch(u,{method:"POST",headers:{Authorization:`Bearer ${d}`,"Content-Type":"application/json"},body:JSON.stringify({startDate:y(i),endDate:y(t),dimensions:["query"],rowLimit:30})});if(f.ok){const w=((await f.json()).rows||[]).map(D=>({query:D.keys[0],clicks:D.clicks,impressions:D.impressions,ctr:D.ctr,position:D.position}));if(w.length>0)return{rows:w,rowCount:w.length,source:"search_console"}}else{const m=await f.text().catch(()=>"");console.error(`[GA Module] Search Console API error (${f.status}):`,m)}}catch(u){console.error("[GA Module] Search Console request failed:",u?.message||u)}else console.log("[GA Module] No siteUrl configured, skipping Search Console");try{const u=await g(d,c,{dateRanges:[{startDate:i,endDate:t}],dimensions:[{name:"sessionGoogleAdsQuery"}],metrics:[{name:"sessions"},{name:"totalUsers"},{name:"engagementRate"}],dimensionFilter:{andGroup:{expressions:[{notExpression:{filter:{fieldName:"sessionGoogleAdsQuery",stringFilter:{value:"(not set)"}}}},{notExpression:{filter:{fieldName:"sessionGoogleAdsQuery",stringFilter:{value:"(not provided)"}}}}]}},orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:30}),f=v(u,"sessionGoogleAdsQuery");if(f.rows.length>0)return{...f,source:"google_ads"}}catch{}const r=await g(d,c,{dateRanges:[{startDate:i,endDate:t}],dimensions:[{name:"landingPagePlusQueryString"}],metrics:[{name:"sessions"},{name:"totalUsers"},{name:"engagementRate"}],dimensionFilter:{andGroup:{expressions:[{filter:{fieldName:"sessionMedium",stringFilter:{matchType:"EXACT",value:"organic"}}},{notExpression:{filter:{fieldName:"landingPagePlusQueryString",stringFilter:{value:"(not set)"}}}}]}},orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:30});return{...v(r,"landingPagePlusQueryString"),source:"organic_landing_pages"}})),p.get("/audience/overview",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"newVsReturning"}],metrics:[{name:"totalUsers"},{name:"sessions"},{name:"engagementRate"},{name:"averageSessionDuration"},{name:"screenPageViewsPerSession"}],metricAggregations:["TOTAL"]});return v(t,"newVsReturning")})),p.get("/audience/technology",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=a.dimension||"browser",o=["browser","operatingSystem","screenResolution"].includes(t)?t:"browser",u=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:o}],metrics:[{name:"totalUsers"},{name:"sessions"},{name:"engagementRate"}],orderBys:[{metric:{metricName:"totalUsers"},desc:!0}],limit:10});return v(u,o)})),p.get("/audience/languages",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"language"}],metrics:[{name:"totalUsers"},{name:"sessions"}],orderBys:[{metric:{metricName:"totalUsers"},desc:!0}],limit:15});return v(t,"language")})),p.get("/audience/hours",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"dayOfWeekName"},{name:"hour"}],metrics:[{name:"sessions"}],orderBys:[{dimension:{dimensionName:"dayOfWeekName"}},{dimension:{dimensionName:"hour"}}],limit:168});return N(t,["dayOfWeekName","hour"])})),p.get("/audience/cities",e.defineEventHandler(async s=>{const{accessToken:d,propertyId:c}=await l(s),a=e.getQuery(s),n=a.startDate||"30daysAgo",i=a.endDate||"today",t=await g(d,c,{dateRanges:[{startDate:n,endDate:i}],dimensions:[{name:"city"},{name:"country"}],metrics:[{name:"totalUsers"},{name:"sessions"}],dimensionFilter:{notExpression:{filter:{fieldName:"city",stringFilter:{value:"(not set)"}}}},orderBys:[{metric:{metricName:"totalUsers"},desc:!0}],limit:20});return N(t,["city","country"])}));function y(s){const d=s.match(/^(\d+)daysAgo$/);if(d){const c=new Date;return c.setDate(c.getDate()-parseInt(d[1],10)),c.toISOString().slice(0,10)}if(s==="today")return new Date().toISOString().slice(0,10);if(s==="yesterday"){const c=new Date;return c.setDate(c.getDate()-1),c.toISOString().slice(0,10)}return s}async function h(s,d,c){const a=`${H}/sites/${encodeURIComponent(d)}/searchAnalytics/query`,n=await fetch(a,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify(c)});if(!n.ok){const i=await n.json().catch(()=>({}));throw e.createError({statusCode:n.status,statusMessage:i?.error?.message||"Search Console API request failed"})}return n.json()}return p.get("/seo/keywords",e.defineEventHandler(async s=>{const{accessToken:d,siteUrl:c}=await l(s);if(!c)throw e.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured. Add it in module settings."});const a=e.getQuery(s),n=y(a.startDate||"30daysAgo"),i=y(a.endDate||"today"),t=Math.min(parseInt(a.limit)||50,100),r=await h(d,c,{startDate:n,endDate:i,dimensions:["query"],rowLimit:t});return{rows:(r.rows||[]).map(o=>({query:o.keys[0],clicks:o.clicks,impressions:o.impressions,ctr:o.ctr,position:o.position})),rowCount:r.rows?.length||0}})),p.get("/seo/pages",e.defineEventHandler(async s=>{const{accessToken:d,siteUrl:c}=await l(s);if(!c)throw e.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured."});const a=e.getQuery(s),n=y(a.startDate||"30daysAgo"),i=y(a.endDate||"today"),t=Math.min(parseInt(a.limit)||50,100),r=await h(d,c,{startDate:n,endDate:i,dimensions:["page"],rowLimit:t});return{rows:(r.rows||[]).map(o=>({page:o.keys[0],clicks:o.clicks,impressions:o.impressions,ctr:o.ctr,position:o.position})),rowCount:r.rows?.length||0}})),p.get("/seo/trends",e.defineEventHandler(async s=>{const{accessToken:d,siteUrl:c}=await l(s);if(!c)throw e.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured."});const a=e.getQuery(s),n=y(a.startDate||"30daysAgo"),i=y(a.endDate||"today"),r=((await h(d,c,{startDate:n,endDate:i,dimensions:["date"],rowLimit:500})).rows||[]).map(u=>({date:u.keys[0],clicks:u.clicks,impressions:u.impressions,ctr:u.ctr,position:u.position})).sort((u,f)=>u.date.localeCompare(f.date)),o=r.reduce((u,f)=>({clicks:u.clicks+f.clicks,impressions:u.impressions+f.impressions}),{clicks:0,impressions:0});return o.ctr=o.impressions>0?o.clicks/o.impressions:0,o.avgPosition=r.length>0?r.reduce((u,f)=>u+f.position,0)/r.length:0,{rows:r,totals:o,rowCount:r.length}})),p.get("/seo/query-pages",e.defineEventHandler(async s=>{const{accessToken:d,siteUrl:c}=await l(s);if(!c)throw e.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured."});const a=e.getQuery(s),n=y(a.startDate||"30daysAgo"),i=y(a.endDate||"today"),t=await h(d,c,{startDate:n,endDate:i,dimensions:["query","page"],rowLimit:100});return{rows:(t.rows||[]).map(r=>({query:r.keys[0],page:r.keys[1],clicks:r.clicks,impressions:r.impressions,ctr:r.ctr,position:r.position})),rowCount:t.rows?.length||0}})),p.handler}function $(C,R=!0){const A=(C.metricHeaders||[]).map(n=>n.name),l=(C.dimensionHeaders||[]).map(n=>n.name).indexOf("date"),g=[];(C.rows||[]).forEach(n=>{const i=l>=0?n.dimensionValues[l]?.value:n.dimensionValues[0]?.value;if(!i||i.length<8)return;const r={date:`${i.slice(0,4)}-${i.slice(4,6)}-${i.slice(6,8)}`};A.forEach((o,u)=>{r[o]=parseFloat(n.metricValues[u]?.value||"0")}),g.push(r)});const y={},h={};C.totals&&C.totals.length>=2?A.forEach((n,i)=>{y[n]=parseFloat(C.totals[0]?.metricValues?.[i]?.value||"0"),h[n]=parseFloat(C.totals[1]?.metricValues?.[i]?.value||"0")}):C.totals&&C.totals.length===1&&A.forEach((n,i)=>{y[n]=parseFloat(C.totals[0]?.metricValues?.[i]?.value||"0")});const s={};A.forEach(n=>{const i=y[n]||0,t=h[n];t!==void 0&&t!==0?s[n]=(i-t)/t*100:s[n]=null});const d=new Map;for(const n of g)d.has(n.date)||d.set(n.date,n);const c=Array.from(d.values()).sort((n,i)=>n.date.localeCompare(i.date)),a=R&&C.totals&&C.totals.length>=2?c.slice(-Math.ceil(c.length/2)):c;return{rows:a,totals:y,previousTotals:h,changes:s,rowCount:a.length}}function v(C,R){const A=(C.metricHeaders||[]).map(l=>l.name),p=(C.rows||[]).map(l=>{const g={[R]:l.dimensionValues[0].value};return A.forEach((y,h)=>{g[y]=parseFloat(l.metricValues[h].value)}),g});return{rows:p,rowCount:C.rowCount||p.length}}function N(C,R){const A=(C.metricHeaders||[]).map(l=>l.name),p=(C.rows||[]).map(l=>{const g={};return R.forEach((y,h)=>{g[y]=l.dimensionValues[h]?.value||""}),A.forEach((y,h)=>{g[y]=parseFloat(l.metricValues[h].value)}),g});return{rows:p,rowCount:C.rowCount||p.length}}function G(C){const{decrypt:R}=C,A=e.createRouter();async function p(l){const{supabase:g,instanceId:y}=l.context.module,{data:h,error:s}=await g.from("project_modules").select("config").eq("id",y).single();if(s||!h?.config)throw e.createError({statusCode:500,statusMessage:"Failed to load module config."});const d=h.config,c=d.githubIntegration,a=d.githubRepo,n=d.githubOwner;if(!c||!a||!n)throw e.createError({statusCode:400,statusMessage:"No GitHub integration configured for this module."});const{data:i,error:t}=await g.from("integrations").select("config").eq("id",c).single();let r=i?.config;if(t||!r){const{data:u,error:f}=await g.from("agency_integrations").select("config").eq("id",c).single();if(f||!u?.config)throw e.createError({statusCode:500,statusMessage:"Failed to load Github credentials."});r=u.config}return{token:R(r.token),repo:a,owner:n}}return A.post("/github/deploy",e.defineEventHandler(async l=>{try{const{token:g,repo:y,owner:h}=await p(l);await fetch(`https://api.github.com/repos/${h}/${y}/dispatches`,{method:"POST",headers:{Authorization:`Bearer ${g}`,Accept:"application/vnd.github+json"},body:JSON.stringify({event_type:"deploy-site"})})}catch(g){throw console.error("Error triggering GitHub deployment:",g),e.createError({statusCode:500,statusMessage:"Failed to trigger deployment."})}})),A.handler}exports.appointments=V;exports.contentManager=G;exports.googleAnalytics=B;exports.products=O;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("h3");require("vue");const I=require("./types-BMraG1kN.js");function Le(e){const{initFirebase:t,normalizeTimestamps:s}=e,c=r.createRouter();async function n(a){const{supabase:i,instanceId:d}=a.context.module,{data:o,error:l}=await i.from("project_modules").select("config").eq("id",d).single();if(l||!o?.config)throw r.createError({statusCode:500,statusMessage:"Failed to load module config."});const u=o.config,f=u.project,g=u.productCollection||"products";if(!f)throw r.createError({statusCode:400,statusMessage:"Products module has no Firebase integration configured."});return{firebase:await t(a,f),collection:g}}return c.get("/products",r.defineEventHandler(async a=>{try{const{firebase:i,collection:d}=await n(a);return(await i.firestore().collection(d).get()).docs.map(l=>s({id:l.id,...l.data()}))}catch(i){throw console.error("Products handler — list error:",i),r.createError({statusCode:i.statusCode||500,statusMessage:i.statusMessage||"Failed to list products"})}})),c.get("/products/:productId",r.defineEventHandler(async a=>{const i=r.getRouterParam(a,"productId");if(!i)throw r.createError({statusCode:400,statusMessage:"Product ID is required."});try{const{firebase:d,collection:o}=await n(a),l=await d.firestore().collection(o).doc(i).get();if(!l.exists)throw r.createError({statusCode:404,statusMessage:"Product not found."});return s({id:l.id,...l.data()})}catch(d){throw console.error("Products handler — get error:",d),r.createError({statusCode:d.statusCode||500,statusMessage:d.statusMessage||"Failed to get product"})}})),c.get("/empty-stock",r.defineEventHandler(async a=>{const d=r.getQuery(a).category;try{const{firebase:o,collection:l}=await n(a);let u=o.firestore().collection(l).where("stock","==",0);d&&(u=u.where("collections","array-contains",d));const g=(await u.get()).docs.map(p=>s({id:p.id,...p.data()}));return{count:g.length,products:g.map(p=>({id:p.id,title:p.title,slug:p.slug,stock:p.stock,price:p.price,currency:p.currency,status:p.status}))}}catch(o){throw console.error("Products handler — empty-stock error:",o),r.createError({statusCode:o.statusCode||500,statusMessage:o.statusMessage||"Failed to query empty stock"})}})),c.handler}function Qe(e){const{initFirebase:t,normalizeTimestamps:s,deleteField:c}=e,n=r.createRouter(),a="privateSettings",i="refundFlow",d="metadataSchema";async function o(m){const{supabase:y,instanceId:h}=m.context.module,{data:w,error:E}=await y.from("project_modules").select("config").eq("id",h).single();if(E||!w?.config)throw r.createError({statusCode:500,statusMessage:"Failed to load module config."});const b=w.config,C=b.project,A=b.reservationsCollection||"bookings_orders",F=b.agendaCollection||"agendas";if(!C)throw r.createError({statusCode:400,statusMessage:"Appointments module has no Firebase integration configured."});return{firebase:await t(m,C),reservationsCollection:A,agendaCollection:F}}function l(m){return(m.reservations||[]).map(h=>({orderId:m.id,reservationId:h.id,customerInfo:m.customerInfo,date:h.date,startTime:h.timeslot?.startTime,endTime:h.timeslot?.endTime,spots:h.spots,status:m.status,reservationStatus:h.status,resourceId:h.resourceId,reservationPrice:h.totalPrice,reservationBasePrice:h.basePrice,reservationAddOnsPrice:h.addOnsPrice,pricingOption:h.pricingOption,amountDue:m.amountDue,amountPaid:m.amountPaid,createdAt:m.createdAt,metadata:m.metadata||{},reservationMetadata:h.metadata||{}}))}function u(m){return{showAmount:m?.showAmount!==!1,fields:Array.isArray(m?.fields)?m.fields:[]}}function f(){return c?{refundFlow:c(),refundFlowEnabled:c(),refundDialog:c()}:{refundFlow:null,refundFlowEnabled:null,refundDialog:null}}function g(m){return Array.isArray(m)?m:[]}function p(){return c?{metadataSchema:c()}:{metadataSchema:null}}return n.get("/agendas",r.defineEventHandler(async m=>{try{const{firebase:y,agendaCollection:h}=await o(m),E=(await y.firestore().collection(h).get()).docs.map(b=>{const C=b.data();return{id:b.id,serviceName:C.serviceName||"",type:C.type||"regular"}});return{count:E.length,agendas:E}}catch(y){throw console.error("Appointments handler — list agendas error:",y),r.createError({statusCode:y.statusCode||500,statusMessage:y.statusMessage||"Failed to list agendas"})}})),n.get("/agendas/:agendaId/opening-hours",r.defineEventHandler(async m=>{const y=r.getRouterParam(m,"agendaId"),w=r.getQuery(m).date;if(!y)throw r.createError({statusCode:400,statusMessage:"Agenda ID is required."});if(!w)throw r.createError({statusCode:400,statusMessage:"Date is required (YYYY-MM-DD)."});try{const{firebase:E,agendaCollection:b}=await o(m),C=await E.firestore().collection(b).doc(y).get();if(!C.exists)throw r.createError({statusCode:404,statusMessage:"Agenda not found."});const A=C.data(),F=(A.resources||[]).filter(H=>H.isActive),R=A.exceptions||[],q=new Date(w+"T00:00:00").getDay(),te=F.map(H=>{const ge=H.openingHours?.[q]||[],se=R.find(O=>{const Ve=w>=O.startDate&&w<=O.endDate,ze=!O.resourceIds||O.resourceIds.length===0||O.resourceIds.includes(H.id);return Ve&&ze});return se?{resourceId:H.id,resourceName:H.name,date:w,dayOfWeek:q,isClosed:se.isClosed,hours:se.isClosed?[]:(se.timeslots||[]).map(O=>({start:O.startTime,end:O.endTime})),isException:!0}:{resourceId:H.id,resourceName:H.name,date:w,dayOfWeek:q,isClosed:ge.length===0,hours:ge,isException:!1}});return{agendaId:y,serviceName:A.serviceName||"",date:w,resources:te}}catch(E){throw console.error("Appointments handler — opening hours error:",E),r.createError({statusCode:E.statusCode||500,statusMessage:E.statusMessage||"Failed to get opening hours"})}})),n.get("/agendas/:agendaId/refund-flow",r.defineEventHandler(async m=>{const y=r.getRouterParam(m,"agendaId");if(!y)throw r.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const{firebase:h,agendaCollection:w}=await o(m),b=h.firestore().collection(w).doc(y),C=b.collection(a).doc(i),[A,F]=await Promise.all([b.get(),C.get()]);if(!A.exists)throw r.createError({statusCode:404,statusMessage:"Agenda not found."});const R=F.exists?F.data()||{}:{};return{refundFlow:R.flow||{},refundFlowEnabled:R.enabled!==!1,refundDialog:u(R.dialog)}}catch(h){throw console.error("Appointments handler — refund flow load error:",h),r.createError({statusCode:h.statusCode||500,statusMessage:h.statusMessage||"Failed to load refund flow"})}})),n.patch("/agendas/:agendaId/refund-flow",r.defineEventHandler(async m=>{const y=r.getRouterParam(m,"agendaId");if(!y)throw r.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const h=await r.readBody(m),{firebase:w,agendaCollection:E}=await o(m),b=w.firestore(),C=b.collection(E).doc(y),A=C.collection(a).doc(i),F=u(h?.refundDialog);if(!(await C.get()).exists)throw r.createError({statusCode:404,statusMessage:"Agenda not found."});const S=b.batch();return S.set(A,{flow:h?.refundFlow||{},enabled:h?.refundFlowEnabled!==!1,dialog:F,updatedAt:new Date().toISOString()},{merge:!0}),S.set(C,f(),{merge:!0}),await S.commit(),{success:!0}}catch(h){throw console.error("Appointments handler — refund flow save error:",h),r.createError({statusCode:h.statusCode||500,statusMessage:h.statusMessage||"Failed to save refund flow"})}})),n.get("/agendas/:agendaId/metadata-schema",r.defineEventHandler(async m=>{const y=r.getRouterParam(m,"agendaId");if(!y)throw r.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const{firebase:h,agendaCollection:w}=await o(m),b=h.firestore().collection(w).doc(y),C=b.collection(a).doc(d),[A,F]=await Promise.all([b.get(),C.get()]);if(!A.exists)throw r.createError({statusCode:404,statusMessage:"Agenda not found."});const R=F.exists?F.data()||{}:{};return{metadataSchema:g(R.schema)}}catch(h){throw console.error("Appointments handler — metadata schema load error:",h),r.createError({statusCode:h.statusCode||500,statusMessage:h.statusMessage||"Failed to load metadata schema"})}})),n.patch("/agendas/:agendaId/metadata-schema",r.defineEventHandler(async m=>{const y=r.getRouterParam(m,"agendaId");if(!y)throw r.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const h=await r.readBody(m),{firebase:w,agendaCollection:E}=await o(m),b=w.firestore(),C=b.collection(E).doc(y),A=C.collection(a).doc(d);if(!(await C.get()).exists)throw r.createError({statusCode:404,statusMessage:"Agenda not found."});const R=b.batch();return R.set(A,{schema:g(h?.metadataSchema),updatedAt:new Date().toISOString()},{merge:!0}),R.set(C,p(),{merge:!0}),await R.commit(),{success:!0}}catch(h){throw console.error("Appointments handler — metadata schema save error:",h),r.createError({statusCode:h.statusCode||500,statusMessage:h.statusMessage||"Failed to save metadata schema"})}})),n.get("/appointments",r.defineEventHandler(async m=>{const y=r.getQuery(m),h=Math.min(Number(y.quantity)||20,100),w=y.status||"confirmed";try{const{firebase:E,reservationsCollection:b}=await o(m),A=(await E.firestore().collection(b).orderBy("createdAt","desc").where("status","==",w).limit(h).get()).docs.flatMap(F=>l(s({id:F.id,...F.data()})));return{count:A.length,appointments:A}}catch(E){throw console.error("Appointments handler — list error:",E),r.createError({statusCode:E.statusCode||500,statusMessage:E.statusMessage||"Failed to list appointments"})}})),n.get("/appointments/find",r.defineEventHandler(async m=>{const y=r.getQuery(m),h=(y.name||"").toLowerCase().trim(),w=(y.email||"").toLowerCase().trim(),E=y.date;try{const{firebase:b,reservationsCollection:C}=await o(m);let A=b.firestore().collection(C);w&&(A=A.where("customerInfo.email","==",w));let R=(await A.orderBy("createdAt","desc").limit(200).get()).docs.flatMap(S=>l(s({id:S.id,...S.data()})));return E&&(R=R.filter(S=>S.date===E)),h&&(R=R.filter(S=>{const q=(S.customerInfo?.firstName||"").toLowerCase(),te=(S.customerInfo?.lastName||"").toLowerCase();return q.includes(h)||te.includes(h)||`${q} ${te}`.includes(h)})),{count:R.length,appointments:R}}catch(b){throw console.error("Appointments handler — find error:",b),r.createError({statusCode:b.statusCode||500,statusMessage:b.statusMessage||"Failed to find appointments"})}})),n.get("/appointments/:appointmentId",r.defineEventHandler(async m=>{const y=r.getRouterParam(m,"appointmentId");if(!y)throw r.createError({statusCode:400,statusMessage:"Appointment ID is required."});try{const{firebase:h,reservationsCollection:w}=await o(m),E=await h.firestore().collection(w).doc(y).get();if(!E.exists)throw r.createError({statusCode:404,statusMessage:"Appointment not found."});const b=s({id:E.id,...E.data()});return{...b,reservations:l(b)}}catch(h){throw console.error("Appointments handler — get error:",h),r.createError({statusCode:h.statusCode||500,statusMessage:h.statusMessage||"Failed to get appointment"})}})),n.handler}const pe="https://analyticsdata.googleapis.com/v1beta",Ge=["https://www.googleapis.com/auth/analytics.readonly","https://www.googleapis.com/auth/webmasters.readonly"],he="https://searchconsole.googleapis.com/webmasters/v3";function Ye(e){const{decrypt:t,getGoogleAccessToken:s}=e,c=r.createRouter();async function n(o){const{supabase:l,instanceId:u}=o.context.module,{data:f,error:g}=await l.from("project_modules").select("config").eq("id",u).single();if(g||!f?.config)throw r.createError({statusCode:500,statusMessage:"Failed to load module config."});const p=f.config,m=p.propertyId,y=p.serviceAccount,h=p.siteUrl||"";if(!y)throw r.createError({statusCode:400,statusMessage:"No Google Service Account configured for this module."});if(!m)throw r.createError({statusCode:400,statusMessage:"No GA4 Property ID configured for this module."});const{data:w,error:E}=await l.from("integrations").select("config").eq("id",y).single();let b=w?.config;if(E||!b){const{data:S,error:q}=await l.from("agency_integrations").select("config").eq("id",y).single();if(q||!S?.config)throw r.createError({statusCode:500,statusMessage:"Failed to load Google Service Account credentials."});b=S.config}const C=t(b.clientEmail),A=t(b.privateKey),F=t(b.projectId);return{accessToken:await s({clientEmail:C,privateKey:A,projectId:F},Ge),propertyId:m,siteUrl:h}}async function a(o,l,u){const f=await fetch(`${pe}/properties/${l}:runReport`,{method:"POST",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},body:JSON.stringify(u)});if(!f.ok){const g=await f.json().catch(()=>({}));throw console.error("GA4 runReport failed:",g),r.createError({statusCode:f.status,statusMessage:g?.error?.message||"GA4 API request failed"})}return f.json()}c.get("/report",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=g.match(/^(\d+)daysAgo$/),y=m?parseInt(m[1],10):30,h=`${y*2}daysAgo`,w=`${y+1}daysAgo`,E=[{name:"sessions"},{name:"totalUsers"},{name:"screenPageViews"},{name:"bounceRate"},{name:"averageSessionDuration"},{name:"newUsers"},{name:"engagementRate"},{name:"sessionsPerUser"},{name:"screenPageViewsPerSession"}];let b,C=!0;try{b=await a(l,u,{dateRanges:[{startDate:g,endDate:p,name:"current"},{startDate:h,endDate:w,name:"previous"}],dimensions:[{name:"date"}],metrics:E,metricAggregations:["TOTAL"],orderBys:[{dimension:{dimensionName:"date"}}]})}catch{C=!1,b=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"date"}],metrics:E,metricAggregations:["TOTAL"],orderBys:[{dimension:{dimensionName:"date"}}]})}return Ke(b,C)})),c.get("/realtime",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=await fetch(`${pe}/properties/${u}:runRealtimeReport`,{method:"POST",headers:{Authorization:`Bearer ${l}`,"Content-Type":"application/json"},body:JSON.stringify({dimensions:[{name:"unifiedScreenName"}],metrics:[{name:"activeUsers"}],orderBys:[{metric:{metricName:"activeUsers"},desc:!0}],limit:5})});if(!f.ok){const y=await f.json().catch(()=>({}));throw r.createError({statusCode:f.status,statusMessage:y?.error?.message||"Realtime API failed"})}const g=await f.json(),p=(g?.rows||[]).reduce((y,h)=>y+parseInt(h.metricValues?.[0]?.value||"0",10),0),m=(g?.rows||[]).map(y=>({page:y.dimensionValues?.[0]?.value||"(not set)",activeUsers:parseInt(y.metricValues?.[0]?.value||"0",10)}));return{activeUsers:p,activePages:m}})),c.get("/top-pages",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=parseInt(f.limit||"10",10),y=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"pagePath"}],metrics:[{name:"screenPageViews"},{name:"totalUsers"},{name:"averageSessionDuration"}],orderBys:[{metric:{metricName:"screenPageViews"},desc:!0}],limit:m*2}),h=M(y,"pagePath"),w=new Map;for(const b of h.rows){const C=b.pagePath.toLowerCase().replace(/\/+$/,""),A=w.get(C);if(A){A.screenPageViews+=b.screenPageViews||0,A.totalUsers+=b.totalUsers||0;const F=A.screenPageViews;F>0&&(A.averageSessionDuration=(A.averageSessionDuration*(F-(b.screenPageViews||0))+(b.averageSessionDuration||0)*(b.screenPageViews||0))/F)}else w.set(C,{...b})}return{rows:[...w.values()].sort((b,C)=>(C.screenPageViews||0)-(b.screenPageViews||0)).slice(0,m),rowCount:h.rowCount}})),c.get("/top-sources",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"sessionSource"}],metrics:[{name:"sessions"},{name:"totalUsers"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:10});return M(m,"sessionSource")})),c.get("/devices",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"deviceCategory"}],metrics:[{name:"sessions"},{name:"totalUsers"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}]});return M(m,"deviceCategory")})),c.get("/countries",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"country"}],metrics:[{name:"sessions"},{name:"totalUsers"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:10});return M(m,"country")})),c.get("/acquisition/channels",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"sessionDefaultChannelGroup"}],metrics:[{name:"sessions"},{name:"totalUsers"},{name:"newUsers"},{name:"engagementRate"},{name:"averageSessionDuration"},{name:"screenPageViewsPerSession"},{name:"conversions"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:15});return M(m,"sessionDefaultChannelGroup")})),c.get("/acquisition/source-medium",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"sessionSourceMedium"}],metrics:[{name:"sessions"},{name:"totalUsers"},{name:"newUsers"},{name:"bounceRate"},{name:"averageSessionDuration"},{name:"conversions"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:20});return M(m,"sessionSourceMedium")})),c.get("/acquisition/referrals",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"sessionSource"}],dimensionFilter:{filter:{fieldName:"sessionMedium",stringFilter:{value:"referral"}}},metrics:[{name:"sessions"},{name:"totalUsers"},{name:"engagementRate"},{name:"averageSessionDuration"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:20});return M(m,"sessionSource")})),c.get("/acquisition/campaigns",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"sessionCampaignName"}],dimensionFilter:{notExpression:{filter:{fieldName:"sessionCampaignName",stringFilter:{value:"(not set)"}}}},metrics:[{name:"sessions"},{name:"totalUsers"},{name:"conversions"},{name:"engagementRate"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:20});return M(m,"sessionCampaignName")})),c.get("/content/all-pages",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=parseInt(f.limit||"50",10),y=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"pagePath"}],metrics:[{name:"screenPageViews"},{name:"totalUsers"},{name:"averageSessionDuration"},{name:"bounceRate"},{name:"engagementRate"},{name:"sessions"},{name:"userEngagementDuration"}],orderBys:[{metric:{metricName:"screenPageViews"},desc:!0}],limit:m});return M(y,"pagePath")})),c.get("/content/landing-pages",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"landingPagePlusQueryString"}],metrics:[{name:"sessions"},{name:"totalUsers"},{name:"bounceRate"},{name:"averageSessionDuration"},{name:"screenPageViews"},{name:"engagementRate"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:30});return M(m,"landingPagePlusQueryString")})),c.get("/content/exit-pages",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"pagePath"}],metrics:[{name:"sessions"},{name:"screenPageViews"},{name:"totalUsers"},{name:"bounceRate"}],orderBys:[{metric:{metricName:"screenPageViews"},desc:!0}],limit:20}),y=M(m,"pagePath");return y.rows=y.rows.map(h=>({...h,exitRate:h.bounceRate||0})),y})),c.get("/content/search-terms",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u,siteUrl:f}=await n(o),g=r.getQuery(o),p=g.startDate||"30daysAgo",m=g.endDate||"today";if(f)try{const w=`${he}/sites/${encodeURIComponent(f)}/searchAnalytics/query`,E=await fetch(w,{method:"POST",headers:{Authorization:`Bearer ${l}`,"Content-Type":"application/json"},body:JSON.stringify({startDate:i(p),endDate:i(m),dimensions:["query"],rowLimit:30})});if(E.ok){const C=((await E.json()).rows||[]).map(A=>({query:A.keys[0],clicks:A.clicks,impressions:A.impressions,ctr:A.ctr,position:A.position}));if(C.length>0)return{rows:C,rowCount:C.length,source:"search_console"}}else{const b=await E.text().catch(()=>"");console.error(`[GA Module] Search Console API error (${E.status}):`,b)}}catch(w){console.error("[GA Module] Search Console request failed:",w?.message||w)}else console.log("[GA Module] No siteUrl configured, skipping Search Console");try{const w=await a(l,u,{dateRanges:[{startDate:p,endDate:m}],dimensions:[{name:"sessionGoogleAdsQuery"}],metrics:[{name:"sessions"},{name:"totalUsers"},{name:"engagementRate"}],dimensionFilter:{andGroup:{expressions:[{notExpression:{filter:{fieldName:"sessionGoogleAdsQuery",stringFilter:{value:"(not set)"}}}},{notExpression:{filter:{fieldName:"sessionGoogleAdsQuery",stringFilter:{value:"(not provided)"}}}}]}},orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:30}),E=M(w,"sessionGoogleAdsQuery");if(E.rows.length>0)return{...E,source:"google_ads"}}catch{}const y=await a(l,u,{dateRanges:[{startDate:p,endDate:m}],dimensions:[{name:"landingPagePlusQueryString"}],metrics:[{name:"sessions"},{name:"totalUsers"},{name:"engagementRate"}],dimensionFilter:{andGroup:{expressions:[{filter:{fieldName:"sessionMedium",stringFilter:{matchType:"EXACT",value:"organic"}}},{notExpression:{filter:{fieldName:"landingPagePlusQueryString",stringFilter:{value:"(not set)"}}}}]}},orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:30});return{...M(y,"landingPagePlusQueryString"),source:"organic_landing_pages"}})),c.get("/audience/overview",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"newVsReturning"}],metrics:[{name:"totalUsers"},{name:"sessions"},{name:"engagementRate"},{name:"averageSessionDuration"},{name:"screenPageViewsPerSession"}],metricAggregations:["TOTAL"]});return M(m,"newVsReturning")})),c.get("/audience/technology",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=f.dimension||"browser",h=["browser","operatingSystem","screenResolution"].includes(m)?m:"browser",w=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:h}],metrics:[{name:"totalUsers"},{name:"sessions"},{name:"engagementRate"}],orderBys:[{metric:{metricName:"totalUsers"},desc:!0}],limit:10});return M(w,h)})),c.get("/audience/languages",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"language"}],metrics:[{name:"totalUsers"},{name:"sessions"}],orderBys:[{metric:{metricName:"totalUsers"},desc:!0}],limit:15});return M(m,"language")})),c.get("/audience/hours",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"dayOfWeekName"},{name:"hour"}],metrics:[{name:"sessions"}],orderBys:[{dimension:{dimensionName:"dayOfWeekName"}},{dimension:{dimensionName:"hour"}}],limit:168});return ye(m,["dayOfWeekName","hour"])})),c.get("/audience/cities",r.defineEventHandler(async o=>{const{accessToken:l,propertyId:u}=await n(o),f=r.getQuery(o),g=f.startDate||"30daysAgo",p=f.endDate||"today",m=await a(l,u,{dateRanges:[{startDate:g,endDate:p}],dimensions:[{name:"city"},{name:"country"}],metrics:[{name:"totalUsers"},{name:"sessions"}],dimensionFilter:{notExpression:{filter:{fieldName:"city",stringFilter:{value:"(not set)"}}}},orderBys:[{metric:{metricName:"totalUsers"},desc:!0}],limit:20});return ye(m,["city","country"])}));function i(o){const l=o.match(/^(\d+)daysAgo$/);if(l){const u=new Date;return u.setDate(u.getDate()-parseInt(l[1],10)),u.toISOString().slice(0,10)}if(o==="today")return new Date().toISOString().slice(0,10);if(o==="yesterday"){const u=new Date;return u.setDate(u.getDate()-1),u.toISOString().slice(0,10)}return o}async function d(o,l,u){const f=`${he}/sites/${encodeURIComponent(l)}/searchAnalytics/query`,g=await fetch(f,{method:"POST",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},body:JSON.stringify(u)});if(!g.ok){const p=await g.json().catch(()=>({}));throw r.createError({statusCode:g.status,statusMessage:p?.error?.message||"Search Console API request failed"})}return g.json()}return c.get("/seo/keywords",r.defineEventHandler(async o=>{const{accessToken:l,siteUrl:u}=await n(o);if(!u)throw r.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured. Add it in module settings."});const f=r.getQuery(o),g=i(f.startDate||"30daysAgo"),p=i(f.endDate||"today"),m=Math.min(parseInt(f.limit)||50,100),y=await d(l,u,{startDate:g,endDate:p,dimensions:["query"],rowLimit:m});return{rows:(y.rows||[]).map(h=>({query:h.keys[0],clicks:h.clicks,impressions:h.impressions,ctr:h.ctr,position:h.position})),rowCount:y.rows?.length||0}})),c.get("/seo/pages",r.defineEventHandler(async o=>{const{accessToken:l,siteUrl:u}=await n(o);if(!u)throw r.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured."});const f=r.getQuery(o),g=i(f.startDate||"30daysAgo"),p=i(f.endDate||"today"),m=Math.min(parseInt(f.limit)||50,100),y=await d(l,u,{startDate:g,endDate:p,dimensions:["page"],rowLimit:m});return{rows:(y.rows||[]).map(h=>({page:h.keys[0],clicks:h.clicks,impressions:h.impressions,ctr:h.ctr,position:h.position})),rowCount:y.rows?.length||0}})),c.get("/seo/trends",r.defineEventHandler(async o=>{const{accessToken:l,siteUrl:u}=await n(o);if(!u)throw r.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured."});const f=r.getQuery(o),g=i(f.startDate||"30daysAgo"),p=i(f.endDate||"today"),y=((await d(l,u,{startDate:g,endDate:p,dimensions:["date"],rowLimit:500})).rows||[]).map(w=>({date:w.keys[0],clicks:w.clicks,impressions:w.impressions,ctr:w.ctr,position:w.position})).sort((w,E)=>w.date.localeCompare(E.date)),h=y.reduce((w,E)=>({clicks:w.clicks+E.clicks,impressions:w.impressions+E.impressions}),{clicks:0,impressions:0});return h.ctr=h.impressions>0?h.clicks/h.impressions:0,h.avgPosition=y.length>0?y.reduce((w,E)=>w+E.position,0)/y.length:0,{rows:y,totals:h,rowCount:y.length}})),c.get("/seo/query-pages",r.defineEventHandler(async o=>{const{accessToken:l,siteUrl:u}=await n(o);if(!u)throw r.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured."});const f=r.getQuery(o),g=i(f.startDate||"30daysAgo"),p=i(f.endDate||"today"),m=await d(l,u,{startDate:g,endDate:p,dimensions:["query","page"],rowLimit:100});return{rows:(m.rows||[]).map(y=>({query:y.keys[0],page:y.keys[1],clicks:y.clicks,impressions:y.impressions,ctr:y.ctr,position:y.position})),rowCount:m.rows?.length||0}})),c.handler}function Ke(e,t=!0){const s=(e.metricHeaders||[]).map(g=>g.name),n=(e.dimensionHeaders||[]).map(g=>g.name).indexOf("date"),a=[];(e.rows||[]).forEach(g=>{const p=n>=0?g.dimensionValues[n]?.value:g.dimensionValues[0]?.value;if(!p||p.length<8)return;const y={date:`${p.slice(0,4)}-${p.slice(4,6)}-${p.slice(6,8)}`};s.forEach((h,w)=>{y[h]=parseFloat(g.metricValues[w]?.value||"0")}),a.push(y)});const i={},d={};e.totals&&e.totals.length>=2?s.forEach((g,p)=>{i[g]=parseFloat(e.totals[0]?.metricValues?.[p]?.value||"0"),d[g]=parseFloat(e.totals[1]?.metricValues?.[p]?.value||"0")}):e.totals&&e.totals.length===1&&s.forEach((g,p)=>{i[g]=parseFloat(e.totals[0]?.metricValues?.[p]?.value||"0")});const o={};s.forEach(g=>{const p=i[g]||0,m=d[g];m!==void 0&&m!==0?o[g]=(p-m)/m*100:o[g]=null});const l=new Map;for(const g of a)l.has(g.date)||l.set(g.date,g);const u=Array.from(l.values()).sort((g,p)=>g.date.localeCompare(p.date)),f=t&&e.totals&&e.totals.length>=2?u.slice(-Math.ceil(u.length/2)):u;return{rows:f,totals:i,previousTotals:d,changes:o,rowCount:f.length}}function M(e,t){const s=(e.metricHeaders||[]).map(n=>n.name),c=(e.rows||[]).map(n=>{const a={[t]:n.dimensionValues[0].value};return s.forEach((i,d)=>{a[i]=parseFloat(n.metricValues[d].value)}),a});return{rows:c,rowCount:e.rowCount||c.length}}function ye(e,t){const s=(e.metricHeaders||[]).map(n=>n.name),c=(e.rows||[]).map(n=>{const a={};return t.forEach((i,d)=>{a[i]=n.dimensionValues[d]?.value||""}),s.forEach((i,d)=>{a[i]=parseFloat(n.metricValues[d].value)}),a});return{rows:c,rowCount:e.rowCount||c.length}}function Je(e){const{decrypt:t}=e,s=r.createRouter();async function c(n){const{supabase:a,instanceId:i}=n.context.module,{data:d,error:o}=await a.from("project_modules").select("config").eq("id",i).single();if(o||!d?.config)throw r.createError({statusCode:500,statusMessage:"Failed to load module config."});const l=d.config,u=l.githubIntegration,f=l.githubRepo,g=l.githubOwner;if(!u||!f||!g)throw r.createError({statusCode:400,statusMessage:"No GitHub integration configured for this module."});const{data:p,error:m}=await a.from("integrations").select("config").eq("id",u).single();let y=p?.config;if(m||!y){const{data:w,error:E}=await a.from("agency_integrations").select("config").eq("id",u).single();if(E||!w?.config)throw r.createError({statusCode:500,statusMessage:"Failed to load Github credentials."});y=w.config}return{token:t(y.token),repo:f,owner:g}}return s.post("/github/deploy",r.defineEventHandler(async n=>{try{const{token:a,repo:i,owner:d}=await c(n);await fetch(`https://api.github.com/repos/${d}/${i}/dispatches`,{method:"POST",headers:{Authorization:`Bearer ${a}`,Accept:"application/vnd.github+json"},body:JSON.stringify({event_type:"deploy-site"})})}catch(a){throw console.error("Error triggering GitHub deployment:",a),r.createError({statusCode:500,statusMessage:"Failed to trigger deployment."})}})),s.handler}const ke="firebaseAuthManager",v={viewUsers:"view-auth-users",viewPii:"view-auth-user-pii",createUsers:"create-auth-users",editUsers:"edit-auth-users",manageStatus:"manage-auth-status",manageSessions:"manage-auth-sessions",manageMfa:"manage-auth-mfa",manageRoles:"manage-auth-roles",manageProfiles:"manage-auth-profiles",deleteUsers:"delete-auth-users"},Te=I.stringType().trim().email().max(254),de=I.stringType().min(1).max(128),Xe=de.refine(e=>e===e.trim(),{message:"Custom UID may not begin or end with whitespace."}),Ue=I.stringType().trim().regex(/^\+[1-9]\d{1,14}$/,"Phone number must use E.164 format."),Ze=I.stringType().email().max(254),We=I.stringType().regex(/^\+[1-9]\d{1,14}$/,"Phone number must use E.164 format."),W=I.stringType().trim().min(3).max(500),Ne=I.stringType().trim().min(1).max(128),et=I.objectType({email:Te,password:I.stringType().min(8).max(128),uid:Xe.optional(),displayName:Ne.optional(),phoneNumber:Ue.optional(),emailVerified:I.booleanType().optional(),disabled:I.booleanType().optional(),createProfile:I.booleanType().optional().default(!1)}).strict(),tt=I.objectType({reason:W,displayName:Ne.nullable().optional(),email:Te.optional(),phoneNumber:Ue.nullable().optional(),emailVerified:I.booleanType().optional()}).strict().refine(e=>z(e,"displayName")||z(e,"email")||z(e,"phoneNumber")||z(e,"emailVerified"),{message:"At least one editable user field is required."}),st=I.objectType({disabled:I.booleanType(),reason:W}).strict(),ne=I.objectType({reason:W}).strict(),rt=I.objectType({role:I.stringType().trim().min(1).max(64).nullable(),reason:W}).strict(),at=I.objectType({reason:W,confirmation:I.stringType().min(1).max(128)}).strict(),ot=new Set(["acr","amr","at_hash","aud","auth_time","azp","cnf","c_hash","email","email_verified","exp","firebase","iat","iss","jti","name","nbf","nonce","phone_number","picture","sub","user_id"]),nt=new Set(["__proto__","constructor","prototype"]);function z(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function L(e){throw r.createError({statusCode:400,statusMessage:e})}function we(e,t){return typeof e=="boolean"?e:e==="true"?!0:e==="false"?!1:t}function xe(e){const t=typeof e=="number"?e:Number(e);return Number.isFinite(t)?Math.min(100,Math.max(10,Math.trunc(t))):50}function it(e){const t=String(e||"users").trim();return t.length===0||t.length>256||t.includes("/")||t==="."||t===".."||/^__.*__$/.test(t)?L("The configured Firestore profile collection is invalid."):t}function K(e,t,s){const c=String(e??t).trim()||t;return c.length>128||!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(c)||nt.has(c)?L(`The configured ${s} field is invalid.`):c}function ct(e){const t=Array.isArray(e)?e:String(e||"").split(","),s=[];for(const c of t){if(typeof c!="string")return L("Configured role options must be strings.");const n=c.trim();if(n){if(n.length>64)return L("Configured role options may not exceed 64 characters.");s.includes(n)||s.push(n)}}return s.length>50?L("No more than 50 role options may be configured."):s}function dt(e){const t=typeof e.project=="string"?e.project.trim():"";t||L("Firebase Auth Manager has no Firebase integration configured.");const s=we(e.manageRoleClaim,!1),c=K(e.roleClaimKey,"role","role claim");s&&ot.has(c)&&L("The configured role claim key is reserved by Firebase.");const n={profileUidField:K(e.profileUidField,"uid","profile UID"),profileNameField:K(e.profileNameField,"displayName","profile name"),profileEmailField:K(e.profileEmailField,"email","profile email"),profileRoleField:K(e.profileRoleField,"role","profile role"),profileStatusField:K(e.profileStatusField,"status","profile status")};return new Set(Object.values(n)).size!==Object.keys(n).length&&L("Each configured Firestore profile field must use a distinct field name."),{project:t,pageSize:xe(e.pageSize),linkFirestoreProfiles:we(e.linkFirestoreProfiles,!0),profileCollection:it(e.profileCollection),...n,manageRoleClaim:s,roleClaimKey:c,roleOptions:ct(e.roleOptions)}}function be(e){const t=Array.isArray(e)?e[0]:e;return typeof t!="string"?void 0:t.trim()||void 0}function Ee(e){const t=Array.isArray(e)?e[0]:e;if(!(typeof t!="string"||t.length===0))return t}function ut(e,t=50){const s=be(e.limit);let c=xe(t);if(s!==void 0){if(!/^\d+$/.test(s))throw r.createError({statusCode:422,statusMessage:"Limit must be a whole number."});if(c=Number(s),c<1||c>100)throw r.createError({statusCode:422,statusMessage:"Limit must be between 1 and 100."})}const n=Ee(e.pageToken);if(n&&n.length>4096)throw r.createError({statusCode:422,statusMessage:"Page token is too long."});const a=Ee(e.search),i=be(e.searchBy);if(i&&!a)throw r.createError({statusCode:422,statusMessage:"Search mode requires a search value."});if(!a)return{limit:c,pageToken:n};if(n)throw r.createError({statusCode:422,statusMessage:"Exact search cannot be combined with a page token."});const d=i||"uid";if(!["uid","email","phone"].includes(d))throw r.createError({statusCode:422,statusMessage:"Search mode must be uid, email, or phone."});if(d==="phone"){const l=We.safeParse(a);if(!l.success)throw r.createError({statusCode:422,statusMessage:"Phone search must use E.164 format."});return{limit:1,search:{kind:"phone",value:l.data}}}if(d==="email"){const l=Ze.safeParse(a);if(!l.success)throw r.createError({statusCode:422,statusMessage:"Email search is invalid."});return{limit:1,search:{kind:"email",value:l.data}}}const o=de.safeParse(a);if(!o.success)throw r.createError({statusCode:422,statusMessage:"UID search is invalid."});return{limit:1,search:{kind:"uid",value:o.data}}}function lt(e){const t=new TextEncoder().encode(e);let s="";for(const c of t)s+=String.fromCharCode(c);return btoa(s).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,"")}function ft(e){if(!/^[A-Za-z0-9_-]{1,1024}$/.test(e))throw r.createError({statusCode:400,statusMessage:"Firebase UID token is invalid."});const t=e.replace(/-/g,"+").replace(/_/g,"/").padEnd(Math.ceil(e.length/4)*4,"=");let s;try{const c=atob(t),n=Uint8Array.from(c,a=>a.charCodeAt(0));s=new TextDecoder("utf-8",{fatal:!0}).decode(n)}catch{throw r.createError({statusCode:400,statusMessage:"Firebase UID token is invalid."})}if(lt(s)!==e||!de.safeParse(s).success)throw r.createError({statusCode:400,statusMessage:"Firebase UID token is invalid."});return s}function ue(e,t){return e.isAdmin===!0||Array.isArray(e.permissions)&&e.permissions.includes(t)}function mt(e,t){return t.kind==="uid"||ue(e,v.viewPii)}function gt(e,t){let s=e.length^t.length;const c=Math.max(e.length,t.length);for(let n=0;n<c;n+=1)s|=(e.charCodeAt(n)||0)^(t.charCodeAt(n)||0);return s===0}function U(e,t){if(!ue(e,t))throw r.createError({statusCode:403,statusMessage:"You do not have permission to perform this action."})}function _e(e){if(!e)return null;const t=e.lastIndexOf("@");if(t<=0)return"••••";const s=e.slice(0,t),c=e.slice(t+1),n=c.lastIndexOf("."),a=n>0?c.slice(0,n):c,i=n>0?c.slice(n):"";return`${s.slice(0,1)}•••@${a.slice(0,1)}•••${i}`}function pt(e){return e?`+••••••${e.replace(/\D/g,"").slice(-2)}`:null}function ht(e){return e.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi,"[redacted-email]").replace(/\+[1-9][\d\s().-]{5,}\d/g,"[redacted-phone]").replace(/\b(password|passcode|token|secret|api[-_\s]?key|authorization)\b\s*[:=]?\s*\S+/gi,"$1 [redacted]")}function qe(e){return e?`${e.slice(0,1)}•••`:null}function P(e){return typeof e=="string"?e:null}function yt(e){const t=[];if(Array.isArray(e?.providerData))for(const s of e.providerData){const c=P(s?.providerId);c&&t.push(c)}return[...new Set(t)].sort()}function He(e){return Array.isArray(e?.multiFactor?.enrolledFactors)?e.multiFactor.enrolledFactors.length:0}function le(e,t){return t.manageRoleClaim?P(e?.customClaims?.[t.roleClaimKey]):null}function wt(){return{enabled:!1,exists:!1,name:null,email:null,role:null,status:null}}function bt(e,t,s){const c=e?P(e[s.profileNameField]):null,n=e?P(e[s.profileEmailField]):null;return{enabled:!0,exists:!!e,name:t?c:qe(c),email:t?n:_e(n),role:t&&e?P(e[s.profileRoleField]):null,status:t&&e?P(e[s.profileStatusField]):null}}function ae(e,t){const s=P(e?.email),c=P(e?.displayName),n=P(e?.phoneNumber);let a;return t.config.linkFirestoreProfiles?t.profileAvailable===!1?a=null:a=bt(t.profile||null,t.canViewPii,t.config):a=wt(),{uid:String(e?.uid||""),displayName:t.canViewPii?c:qe(c),email:t.canViewPii?s:_e(s),phoneNumber:t.canViewPii?n:pt(n),photoURL:t.canViewPii?P(e?.photoURL):null,emailVerified:e?.emailVerified===!0,disabled:e?.disabled===!0,providers:yt(e),createdAt:P(e?.metadata?.creationTime),lastSignInAt:P(e?.metadata?.lastSignInTime),lastRefreshAt:P(e?.metadata?.lastRefreshTime),mfaFactorCount:He(e),role:le(e,t.config),profile:a}}function Ce(e,t){if(!e)return null;const s=ae(e,{canViewPii:!1,config:t,profileAvailable:!1});return{uid:s.uid,displayName:s.displayName,email:s.email,phoneNumber:s.phoneNumber,emailVerified:s.emailVerified,disabled:s.disabled,providers:s.providers,mfaFactorCount:s.mfaFactorCount,role:s.role}}function Et(e,t,s){const c={...e||{}};return s===null?delete c[t]:c[t]=s,c}function Ae(e,t){const s={[t.profileUidField]:String(e.uid),[t.profileStatusField]:e.disabled===!0?"disabled":"active"};typeof e.displayName=="string"&&(s[t.profileNameField]=e.displayName),typeof e.email=="string"&&(s[t.profileEmailField]=e.email);const c=le(e,t);return c!==null&&(s[t.profileRoleField]=c),s}function ee(e){if(!e||typeof e!="object")return;const t=e;if(typeof t.code=="string")return t.code;if(typeof t.errorInfo?.code=="string")return t.errorInfo.code}function Ct(e,t="Firebase Authentication request failed."){const s=ee(e);return s==="auth/user-not-found"?{statusCode:404,statusMessage:"Firebase user not found."}:s&&["auth/email-already-exists","auth/phone-number-already-exists","auth/uid-already-exists"].includes(s)?{statusCode:409,statusMessage:"A Firebase user already uses that identifier."}:s&&(s.startsWith("auth/invalid-")||s==="auth/weak-password"||s==="auth/claims-too-large"||s==="auth/reserved-claims")?{statusCode:422,statusMessage:"Firebase rejected the supplied user data."}:s&&["auth/too-many-requests","auth/quota-exceeded"].includes(s)?{statusCode:429,statusMessage:"Firebase temporarily rate-limited this request."}:s&&["auth/insufficient-permission","auth/project-not-found","auth/configuration-not-found"].includes(s)?{statusCode:424,statusMessage:"The Firebase integration is not authorized for this operation."}:{statusCode:502,statusMessage:t}}function N(e){const t=e?.context?.module;if(!t||!t.supabase||typeof t.instanceId!="string"||typeof t.projectId!="string"||typeof t.userId!="string")throw r.createError({statusCode:500,statusMessage:"Firebase Auth Manager request context is unavailable."});return{...t,isAdmin:t.isAdmin===!0,permissions:Array.isArray(t.permissions)?t.permissions:[]}}async function B(e,t){const s=t.safeParse(await r.readBody(e));if(!s.success)throw r.createError({statusCode:422,statusMessage:s.error.issues[0]?.message||"Request body is invalid."});return s.data}function j(e){const t=r.getRouterParam(e,"uid");if(typeof t!="string")throw r.createError({statusCode:400,statusMessage:"A valid Firebase UID is required."});return ft(t)}function x(e,t){if(typeof e?.statusCode=="number"&&typeof e?.statusMessage=="string")throw e;const s=Ct(e,t),c=ee(e);throw console.error("Firebase Auth Manager request failed",c?{code:c}:{code:"unknown"}),r.createError(s)}function At(e){return ee(e)==="auth/user-not-found"}function ce(e){return ue(e,v.viewPii)}function Dt(e,t){const s=[];for(let c=0;c<e.length;c+=t)s.push(e.slice(c,c+t));return s}function oe(e){return e.length>0&&e!=="."&&e!==".."&&!e.includes("/")&&!/^__.*__$/.test(e)}async function Ft(e,t,s){const c=new Map;if(!s.linkFirestoreProfiles||t.length===0)return c;const n=[...new Set(t.map(u=>String(u.uid)).filter(Boolean))],a=e.collection(s.profileCollection),i=n.filter(oe),d=i.map(u=>a.doc(u)),o=d.length===0?[]:typeof e.getAll=="function"?await e.getAll(...d):await Promise.all(d.map(u=>u.get()));for(let u=0;u<o.length;u+=1){const f=o[u],g=i[u];g&&f?.exists&&c.set(g,f.data())}const l=n.filter(u=>!c.has(u));for(const u of Dt(l,30)){const f=await a.where(s.profileUidField,"in",u).get();for(const g of f.docs||[]){const p=g.data(),m=p[s.profileUidField];typeof m=="string"&&u.includes(m)&&!c.has(m)&&c.set(m,p)}}return c}async function De(e,t,s){const c=e.collection(s.profileCollection);if(oe(t)){const i=await c.doc(t).get();if(i.exists)return{id:String(i.id),data:i.data()}}const a=(await c.where(s.profileUidField,"==",t).limit(1).get()).docs?.[0];return a?{id:String(a.id),data:a.data()}:null}async function _(e,t){const s=N(e),{data:c,error:n}=await s.supabase.from("project_modules").select("type, config").eq("id",s.instanceId).eq("project_id",s.projectId).maybeSingle();if(n)throw r.createError({statusCode:500,statusMessage:"Failed to load Firebase Auth Manager configuration."});if(!c||c.type!==ke||!c.config)throw r.createError({statusCode:404,statusMessage:"Firebase Auth Manager module instance not found."});const a=dt(c.config),{data:i,error:d}=await s.supabase.from("integrations").select("id, project_id, type").eq("id",a.project).eq("project_id",s.projectId).eq("type","FIREBASE").maybeSingle();if(d)throw r.createError({statusCode:500,statusMessage:"Failed to verify the Firebase integration."});if(!i)throw r.createError({statusCode:424,statusMessage:"The configured Firebase integration is unavailable for this project."});const o=await t.initFirebase(e,a.project);return{module:s,config:a,integrationId:a.project,firebase:o,auth:o.auth(),firestore:o.firestore()}}async function $(e,t,s){const c={project_id:t.module.projectId,module_id:t.module.instanceId,user_id:t.module.userId,message:`Firebase Auth: ${s.action}`,level:"INFO",params:{source:ke,action:s.action,targetUid:s.targetUid,integrationId:t.integrationId,reason:ht(s.reason),before:Ce(s.before,t.config),after:Ce(s.after,t.config),...s.details||{}}};try{const{error:n}=await t.module.supabase.from("project_logs").insert(c);return n?(console.error("Firebase Auth Manager audit insert failed",{code:n.code||"unknown"}),!1):!0}catch(n){const a=n&&typeof n=="object"&&"code"in n?String(n.code):"unknown";return console.error("Firebase Auth Manager audit insert failed",{code:a}),!1}}async function Rt(e,t){try{return t.kind==="email"?[await e.getUserByEmail(t.value)]:t.kind==="phone"?[await e.getUserByPhoneNumber(t.value)]:[await e.getUser(t.value)]}catch(s){if(At(s))return[];throw s}}async function Fe(e,t,s){try{return await e.getUser(t)}catch(c){return console.error("Firebase Auth Manager post-mutation refresh failed",{code:ee(c)||"unknown"}),s}}async function Q(e,t){let s=new Map,c=!0;if(t.config.linkFirestoreProfiles)try{s=await Ft(t.firestore,e,t.config)}catch(n){c=!1;const a=ee(n);console.error("Firebase Auth Manager profile join failed",{code:a||"unknown"})}return e.map(n=>ae(n,{canViewPii:ce(t.module),config:t.config,profile:s.get(String(n.uid))||null,profileAvailable:c}))}function It(e){const t=r.createRouter();return t.get("/users",r.defineEventHandler(async s=>{const c=N(s);U(c,v.viewUsers);try{const n=await _(s,e),a=ut(r.getQuery(s),n.config.pageSize);if(a.search&&!mt(n.module,a.search))throw r.createError({statusCode:403,statusMessage:"Viewing personal data is required to search by email or phone number."});let i,d=null;if(a.search)i=await Rt(n.auth,a.search);else{const o=await n.auth.listUsers(a.limit,a.pageToken);i=Array.isArray(o.users)?o.users:[],d=typeof o.pageToken=="string"?o.pageToken:null}return{users:await Q(i,n),nextPageToken:d}}catch(n){x(n,"Failed to list Firebase users.")}})),t.get("/users/:uid",r.defineEventHandler(async s=>{const c=N(s);U(c,v.viewUsers);const n=j(s);try{const a=await _(s,e),i=await a.auth.getUser(n),[d]=await Q([i],a);return{user:d}}catch(a){x(a,"Failed to load the Firebase user.")}})),t.post("/users",r.defineEventHandler(async s=>{const c=N(s);U(c,v.createUsers);const n=await B(s,et);n.createProfile&&U(c,v.manageProfiles);try{const a=await _(s,e);if(n.createProfile&&!a.config.linkFirestoreProfiles)throw r.createError({statusCode:409,statusMessage:"Firestore profile linking is disabled for this module."});if(n.uid&&a.config.linkFirestoreProfiles&&await De(a.firestore,n.uid,a.config))throw r.createError({statusCode:409,statusMessage:"A retained Firestore profile already uses this UID. Resolve that profile before reusing the Authentication UID."});const i=await a.auth.createUser({email:n.email,password:n.password,uid:n.uid,displayName:n.displayName,phoneNumber:n.phoneNumber,emailVerified:n.emailVerified,disabled:n.disabled});let d=null,o=!1,l=null;if(n.createProfile){d=Ae(i,a.config);try{const f=a.firestore.collection(a.config.profileCollection);await(oe(i.uid)?f.doc(i.uid):f.doc()).create(d),o=!0}catch{d=null,l="The Authentication user was created, but its Firestore profile could not be created."}}const u=await $(s,a,{action:"user.create",targetUid:i.uid,reason:"Created through Firebase Auth Manager.",before:null,after:i,details:{profileRequested:n.createProfile===!0,profileCreated:o}});return r.setResponseStatus(s,201),{user:ae(i,{canViewPii:ce(a.module),config:a.config,profile:d}),profileCreated:o,profileError:l,auditRecorded:u}}catch(a){x(a,"Failed to create the Firebase user.")}})),t.patch("/users/:uid",r.defineEventHandler(async s=>{const c=N(s);U(c,v.editUsers);const n=j(s),a=await B(s,tt);try{const i=await _(s,e),d=await i.auth.getUser(n),o={};z(a,"displayName")&&(o.displayName=a.displayName),z(a,"email")&&(o.email=a.email),z(a,"phoneNumber")&&(o.phoneNumber=a.phoneNumber),z(a,"emailVerified")&&(o.emailVerified=a.emailVerified);const l=await i.auth.updateUser(n,o),u=await $(s,i,{action:"user.update",targetUid:n,reason:a.reason,before:d,after:l,details:{changedFieldCount:Object.keys(o).length}}),[f]=await Q([l],i);return{user:f,auditRecorded:u}}catch(i){x(i,"Failed to update the Firebase user.")}})),t.post("/users/:uid/status",r.defineEventHandler(async s=>{const c=N(s);U(c,v.manageStatus);const n=j(s),a=await B(s,st);try{const i=await _(s,e),d=await i.auth.getUser(n),o=d.disabled!==a.disabled,l=o?await i.auth.updateUser(n,{disabled:a.disabled}):d,u=await $(s,i,{action:a.disabled?"user.disable":"user.enable",targetUid:n,reason:a.reason,before:d,after:l,details:{changed:o}}),[f]=await Q([l],i);return{user:f,changed:o,auditRecorded:u}}catch(i){x(i,"Failed to change the Firebase user status.")}})),t.post("/users/:uid/revoke-sessions",r.defineEventHandler(async s=>{const c=N(s);U(c,v.manageSessions);const n=j(s),a=await B(s,ne);try{const i=await _(s,e),d=await i.auth.getUser(n);await i.auth.revokeRefreshTokens(n);const o=await $(s,i,{action:"user.revoke_sessions",targetUid:n,reason:a.reason,before:d,after:d,details:{sessionsRevoked:!0}}),l=await Fe(i.auth,n,d),[u]=await Q([l],i);return{user:u,sessionsRevoked:!0,auditRecorded:o}}catch(i){x(i,"Failed to revoke Firebase user sessions.")}})),t.post("/users/:uid/reset-mfa",r.defineEventHandler(async s=>{const c=N(s);U(c,v.manageMfa);const n=j(s),a=await B(s,ne);try{const i=await _(s,e),d=await i.auth.getUser(n),o=He(d)>0,l=o?await i.auth.updateUser(n,{multiFactor:{enrolledFactors:[]}}):d,u=await $(s,i,{action:"user.reset_mfa",targetUid:n,reason:a.reason,before:d,after:l,details:{changed:o}}),[f]=await Q([l],i);return{user:f,changed:o,auditRecorded:u}}catch(i){x(i,"Failed to reset Firebase multi-factor authentication.")}})),t.post("/users/:uid/role",r.defineEventHandler(async s=>{const c=N(s);U(c,v.manageRoles);const n=j(s),a=await B(s,rt);try{const i=await _(s,e);if(!i.config.manageRoleClaim)throw r.createError({statusCode:409,statusMessage:"Role claim management is disabled for this module."});if(a.role!==null&&!i.config.roleOptions.includes(a.role))throw r.createError({statusCode:422,statusMessage:"Role must be one of the configured values."});const d=await i.auth.getUser(n),l=le(d,i.config)!==a.role;let u=d;if(l){const p=Et(d.customClaims,i.config.roleClaimKey,a.role);await i.auth.setCustomUserClaims(n,p),u={...d,customClaims:p}}const f=await $(s,i,{action:"user.set_role",targetUid:n,reason:a.reason,before:d,after:u,details:{changed:l,role:a.role}});l&&(u=await Fe(i.auth,n,u));const[g]=await Q([u],i);return{user:g,changed:l,auditRecorded:f}}catch(i){x(i,"Failed to update the Firebase role claim.")}})),t.post("/users/:uid/profile",r.defineEventHandler(async s=>{const c=N(s);U(c,v.manageProfiles);const n=j(s),a=await B(s,ne);try{const i=await _(s,e);if(!i.config.linkFirestoreProfiles)throw r.createError({statusCode:409,statusMessage:"Firestore profile linking is disabled for this module."});const d=await i.auth.getUser(n),o=await De(i.firestore,n,i.config);let l=o?.data||null,u=!1;if(!o){l=Ae(d,i.config);const g=i.firestore.collection(i.config.profileCollection);await(oe(n)?g.doc(n):g.doc()).create(l),u=!0}const f=await $(s,i,{action:"user.create_profile",targetUid:n,reason:a.reason,before:d,after:d,details:{changed:u}});return{user:ae(d,{canViewPii:ce(i.module),config:i.config,profile:l}),profileCreated:u,auditRecorded:f}}catch(i){x(i,"Failed to create the Firestore profile.")}})),t.delete("/users/:uid",r.defineEventHandler(async s=>{const c=N(s);U(c,v.deleteUsers);const n=j(s),a=await B(s,at);if(!gt(n,a.confirmation))throw r.createError({statusCode:422,statusMessage:"Deletion confirmation must exactly match the Firebase UID."});try{const i=await _(s,e),d=await i.auth.getUser(n);await i.auth.deleteUser(n);const o=await $(s,i,{action:"user.delete",targetUid:n,reason:a.reason,before:d,after:null,details:{firestoreProfilePreserved:!0}});return{uid:n,deleted:!0,firestoreProfilePreserved:!0,auditRecorded:o}}catch(i){x(i,"Failed to delete the Firebase user.")}})),t.handler}const fe=1,Re=700*1024,Ie=20,Mt=5e4,St=120,vt=80,Me=2e3,Se=120,Pt="studio_forms",Oe=new Set(["draft","published","archived"]),kt=new Set(["__proto__","constructor","prototype"]),k={view:"view-forms",create:"create-form",edit:"edit-form",publish:"publish-form",archive:"archive-form"};function D(e){throw r.createError({statusCode:400,statusMessage:e})}function V(e){throw r.createError({statusCode:409,statusMessage:e})}function me(e,t){(!e||typeof e!="object"||Array.isArray(e))&&D(`${t} must be an object.`);const s=Object.getPrototypeOf(e);return s!==Object.prototype&&s!==null&&D(`${t} must be a plain object.`),e}function Be(e,t,s){typeof e!="string"&&D(`${t} is required.`);const c=e.trim();return c||D(`${t} is required.`),c.length>s&&D(`${t} cannot exceed ${s} characters.`),c}function Tt(e){if(e==null)return"";typeof e!="string"&&D("Form description must be a string.");const t=e.trim();return t.length>Me&&D(`Form description cannot exceed ${Me} characters.`),t}function Ut(e){const t=Be(e,"Form key",vt).toLowerCase();return/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/.test(t)||D("Form key must start with a letter and contain only lowercase letters, numbers, hyphens, or underscores."),t}function Nt(e){if(e==null||e==="")return Pt;typeof e!="string"&&D("The configured forms collection must be a string.");const t=e.trim();(!t||t.length>500)&&D("The configured forms collection path is invalid."),(t.startsWith("/")||t.endsWith("/")||t.includes("//"))&&D("The configured forms collection path is invalid.");const s=t.split("/");s.length%2===0&&D("The configured forms collection path must point to a collection.");for(const c of s)(!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/.test(c)||c==="."||c===".."||/^__.*__$/.test(c))&&D("The configured forms collection path contains an invalid segment.");return t}function xt(e){(!e||typeof e!="object")&&D("Form definition must be an object or array.");const t=new Set;let s=0;const c=(i,d,o)=>{if(s+=1,s>Mt&&D("Form definition is too complex."),d>Ie&&D(`Form definition cannot be nested deeper than ${Ie} levels.`),i===null||typeof i=="string"||typeof i=="boolean")return;if(typeof i=="number"){Number.isFinite(i)||D("Form definition contains an invalid number.");return}if(typeof i!="object"&&D("Form definition contains a value that cannot be stored."),t.has(i)&&D("Form definition cannot contain circular references."),t.add(i),Array.isArray(i)){o&&D("Form definition cannot contain an array directly inside another array.");for(const u of i)c(u,d+1,!0);t.delete(i);return}const l=Object.getPrototypeOf(i);l!==Object.prototype&&l!==null&&D("Form definition can contain only plain JSON objects.");for(const[u,f]of Object.entries(i))kt.has(u)&&D(`Form definition contains a forbidden object key: ${u}.`),new TextEncoder().encode(u).byteLength>1500&&D("Form definition contains an object key that is too long."),c(f,d+1,!1);t.delete(i)};c(e,1,!1);let n;try{n=JSON.stringify(e)}catch{D("Form definition must be valid JSON.")}new TextEncoder().encode(n).byteLength>Re&&D(`Form definition cannot exceed ${Re} bytes.`)}function je(e){const t=me(e,"Request body"),s=t.definition;return xt(s),{name:Be(t.name,"Form name",St),key:Ut(t.key),description:Tt(t.description),definition:s}}function _t(e){return je(e)}function $e(e){return(!Number.isInteger(e)||e<1)&&D("expectedRevision must be a positive integer."),e}function qt(e){const t=me(e,"Request body");return{...je(t),expectedRevision:$e(t.expectedRevision)}}function ie(e){const t=me(e,"Request body");return{expectedRevision:$e(t.expectedRevision)}}function Ht(e){return{id:e.revisionId,formId:e.formId,key:e.form.key,name:e.form.name,description:e.form.description||"",definition:e.form.draftDefinition,schemaVersion:e.form.schemaVersion||fe,revision:e.revision,draftRevision:e.form.draftRevision,previousRevisionId:e.form.publishedRevisionId||null,publishedAt:e.publishedAt,publishedBy:e.publishedBy}}function J(e){typeof e!="string"&&D("Form ID is required.");const t=e.trim();return(!t||t==="."||t===".."||t.includes("/")||/[\u0000-\u001f\u007f]/.test(t)||new TextEncoder().encode(t).byteLength>1500)&&D("Form ID is invalid."),t}function Z(e){const s=e?.context?.module?.userId||e?.context?.user?.id||null;return typeof s=="string"&&s.trim()?s:null}function T(e,t){const s=e?.context?.module;if(!s)throw r.createError({statusCode:500,statusMessage:"Module context is unavailable."});if(!s.userId)throw r.createError({statusCode:401,statusMessage:"Unauthorized"});if(s.isAdmin===!0)return;if(!(Array.isArray(s.permissions)?s.permissions:[]).includes(t))throw r.createError({statusCode:403,statusMessage:`Missing module permission: ${t}.`})}function X(e){if(!e||typeof e!="object"||Array.isArray(e))throw r.createError({statusCode:500,statusMessage:"Stored form data is invalid."});const t=e;if(typeof t.name!="string"||typeof t.key!="string"||!Oe.has(String(t.status))||!Number.isInteger(t.revision)||!Number.isInteger(t.draftRevision)||!t.draftDefinition||typeof t.draftDefinition!="object")throw r.createError({statusCode:500,statusMessage:"Stored form data is invalid."});return t}function re(e,t){e.revision!==t&&V(`Form has changed. Expected revision ${t}, current revision is ${e.revision}.`)}function Ot(e){(Array.isArray(e)||!Array.isArray(e.layout)||e.layout.length===0)&&D("Add at least one form element before publishing.")}function G(e,t,s,c=!0){const{draftDefinition:n,...a}=t,i=c?{id:e,...a,draftDefinition:n}:{id:e,...a},d=s?s(i):i,o=Number.isInteger(d.publishedRevision)?d.publishedRevision:null,l=Number.isInteger(d.publishedDraftRevision)?d.publishedDraftRevision:null;return{id:d.id,name:d.name,key:d.key,description:d.description||"",...c?{definition:n}:{},status:d.status,revision:d.revision,draftRevision:d.draftRevision,publishedRevision:o,publishedRevisionId:d.publishedRevisionId||null,hasUnpublishedChanges:!d.publishedRevisionId||d.draftRevision!==l,schemaVersion:d.schemaVersion||fe,createdAt:d.createdAt||null,createdBy:d.createdBy||null,updatedAt:d.updatedAt||null,updatedBy:d.updatedBy||null,publishedAt:d.publishedAt||null,publishedBy:d.publishedBy||null,archivedAt:d.archivedAt||null,archivedBy:d.archivedBy||null}}function ve(e,t){if(!(e==null||e===""))return(Array.isArray(e)||typeof e!="string")&&D(`${t} must be a single string.`),e.trim()}function Pe(e){if(typeof e=="string"){const t=Date.parse(e);return Number.isNaN(t)?0:t}return e instanceof Date?e.getTime():e&&typeof e.toMillis=="function"?e.toMillis():0}function Y(e,t,s){throw Number.isInteger(t?.statusCode)&&t.statusCode>=400&&t.statusCode<600?t:(console.error(`Form Builder handler — ${e} error:`,t),r.createError({statusCode:500,statusMessage:s}))}function Bt(e){const{initFirebase:t,normalizeTimestamps:s}=e,c=r.createRouter();async function n(a){const i=a?.context?.module,{supabase:d,instanceId:o,projectId:l,moduleType:u}=i||{};if(!d||!o)throw r.createError({statusCode:500,statusMessage:"Module context is unavailable."});const{data:f,error:g}=await d.from("project_modules").select("config, project_id, type").eq("id",o).single();if(g||!f?.config)throw r.createError({statusCode:500,statusMessage:"Failed to load module config."});if(l&&f.project_id!==l)throw r.createError({statusCode:403,statusMessage:"Module project context does not match."});if(u&&f.type!==u)throw r.createError({statusCode:409,statusMessage:"Module type context does not match."});const p=f.config,m=p.databaseIntegration;if(typeof m!="string"||!m.trim())throw r.createError({statusCode:400,statusMessage:"Form Builder has no Firebase integration configured."});if(m.length>200||!/^[A-Za-z0-9_-]+$/.test(m))throw r.createError({statusCode:500,statusMessage:"Configured Firebase integration ID is invalid."});const y=l||f.project_id,{data:h,error:w}=await d.from("integrations").select("id").eq("id",m).eq("project_id",y).eq("type","FIREBASE").single();if(w||!h)throw r.createError({statusCode:400,statusMessage:"Configured Firebase integration is unavailable for this project."});const E=Nt(p.formsCollection),b=J(o),A=(await t(a,m)).firestore();return{firestore:A,forms:A.collection(E).doc(b).collection("forms")}}return c.get("/forms",r.defineEventHandler(async a=>{T(a,k.view);try{const i=r.getQuery(a),d=ve(i.status,"status"),o=ve(i.search,"search");d&&!Oe.has(d)&&D("status must be draft, published, or archived."),o&&o.length>Se&&D(`search cannot exceed ${Se} characters.`);const{forms:l}=await n(a),u=d?await l.where("status","==",d).get():await l.get(),f=o?.toLocaleLowerCase();return u.docs.map(g=>G(g.id,X(g.data()),s,!1)).filter(g=>f?[g.name,g.key,g.description].some(p=>String(p||"").toLocaleLowerCase().includes(f)):!0).sort((g,p)=>Pe(p.updatedAt)-Pe(g.updatedAt))}catch(i){Y("list",i,"Failed to list forms.")}})),c.get("/forms/:formId",r.defineEventHandler(async a=>{T(a,k.view);const i=J(r.getRouterParam(a,"formId"));try{const{forms:d}=await n(a),o=await d.doc(i).get();if(!o.exists)throw r.createError({statusCode:404,statusMessage:"Form not found."});return G(o.id,X(o.data()),s)}catch(d){Y("get",d,"Failed to load form.")}})),c.post("/forms",r.defineEventHandler(async a=>{T(a,k.view),T(a,k.create);try{const i=_t(await r.readBody(a)),d=Z(a),o=new Date().toISOString(),{firestore:l,forms:u}=await n(a),f=u.doc(i.key),g={name:i.name,key:i.key,description:i.description,status:"draft",draftDefinition:i.definition,schemaVersion:fe,revision:1,draftRevision:1,publishedRevision:null,publishedRevisionId:null,publishedDraftRevision:null,createdAt:o,createdBy:d,updatedAt:o,updatedBy:d,draftUpdatedAt:o,draftUpdatedBy:d,publishedAt:null,publishedBy:null,archivedAt:null,archivedBy:null,statusBeforeArchive:null};return await l.runTransaction(async p=>{(await p.get(f)).exists&&V(`A form with key "${i.key}" already exists.`),p.create(f,g)}),G(f.id,g,s)}catch(i){Y("create",i,"Failed to create form.")}})),c.patch("/forms/:formId/draft",r.defineEventHandler(async a=>{T(a,k.view),T(a,k.edit);const i=J(r.getRouterParam(a,"formId"));try{const d=qt(await r.readBody(a)),o=Z(a),l=new Date().toISOString(),{firestore:u,forms:f}=await n(a),g=f.doc(i);return await u.runTransaction(async p=>{const m=await p.get(g);if(!m.exists)throw r.createError({statusCode:404,statusMessage:"Form not found."});const y=X(m.data());re(y,d.expectedRevision),y.status==="archived"&&V("Restore this form before editing its draft."),y.key!==d.key&&V("A form key cannot be changed after the form is created.");const h={name:d.name,key:d.key,description:d.description,draftDefinition:d.definition,revision:y.revision+1,draftRevision:y.draftRevision+1,updatedAt:l,updatedBy:o,draftUpdatedAt:l,draftUpdatedBy:o};return p.update(g,h),G(i,{...y,...h},s)})}catch(d){Y("update draft",d,"Failed to update form draft.")}})),c.post("/forms/:formId/publish",r.defineEventHandler(async a=>{T(a,k.view),T(a,k.publish);const i=J(r.getRouterParam(a,"formId"));try{const{expectedRevision:d}=ie(await r.readBody(a)),o=Z(a),l=new Date().toISOString(),{firestore:u,forms:f}=await n(a),g=f.doc(i),p=g.collection("revisions").doc();return await u.runTransaction(async m=>{const y=await m.get(g);if(!y.exists)throw r.createError({statusCode:404,statusMessage:"Form not found."});const h=X(y.data());re(h,d),h.status==="archived"&&V("Restore this form before publishing it."),Ot(h.draftDefinition),h.publishedRevisionId&&h.publishedDraftRevision===h.draftRevision&&V("This form has no unpublished changes.");const w=h.revision+1,E=Ht({formId:i,form:h,revision:w,revisionId:p.id,publishedAt:l,publishedBy:o}),b={status:"published",revision:w,publishedRevision:w,publishedRevisionId:p.id,publishedDraftRevision:h.draftRevision,publishedAt:l,publishedBy:o,updatedAt:l,updatedBy:o,archivedAt:null,archivedBy:null,statusBeforeArchive:null};return m.create(p,E),m.update(g,b),G(i,{...h,...b},s)})}catch(d){Y("publish",d,"Failed to publish form.")}})),c.post("/forms/:formId/archive",r.defineEventHandler(async a=>{T(a,k.view),T(a,k.archive);const i=J(r.getRouterParam(a,"formId"));try{const{expectedRevision:d}=ie(await r.readBody(a)),o=Z(a),l=new Date().toISOString(),{firestore:u,forms:f}=await n(a),g=f.doc(i);return await u.runTransaction(async p=>{const m=await p.get(g);if(!m.exists)throw r.createError({statusCode:404,statusMessage:"Form not found."});const y=X(m.data());re(y,d),y.status==="archived"&&V("Form is already archived.");const h={status:"archived",statusBeforeArchive:y.status,revision:y.revision+1,archivedAt:l,archivedBy:o,updatedAt:l,updatedBy:o};return p.update(g,h),G(i,{...y,...h},s)})}catch(d){Y("archive",d,"Failed to archive form.")}})),c.post("/forms/:formId/restore",r.defineEventHandler(async a=>{T(a,k.view),T(a,k.archive);const i=J(r.getRouterParam(a,"formId"));try{const{expectedRevision:d}=ie(await r.readBody(a)),o=Z(a),l=new Date().toISOString(),{firestore:u,forms:f}=await n(a),g=f.doc(i);return await u.runTransaction(async p=>{const m=await p.get(g);if(!m.exists)throw r.createError({statusCode:404,statusMessage:"Form not found."});const y=X(m.data());re(y,d),y.status!=="archived"&&V("Only archived forms can be restored.");const w={status:y.publishedRevisionId?"published":"draft",statusBeforeArchive:null,revision:y.revision+1,archivedAt:null,archivedBy:null,updatedAt:l,updatedBy:o};return p.update(g,w),G(i,{...y,...w},s)})}catch(d){Y("restore",d,"Failed to restore form.")}})),c.handler}exports.appointments=Qe;exports.contentManager=Je;exports.firebaseAuthManager=It;exports.formBuilder=Bt;exports.googleAnalytics=Ye;exports.products=Le;