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

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 (149) hide show
  1. package/dist/AppointmentListTable-CWzbwqR-.js +1 -0
  2. package/dist/AppointmentListTable-DoZuBstk.mjs +270 -0
  3. package/dist/ContentEditor-BlNbqkAz.js +324 -0
  4. package/dist/{ContentEditor-DX1bTQKs.mjs → ContentEditor-D754iLJS.mjs} +8074 -27026
  5. package/dist/{Detail-B7pe808q.mjs → Detail-BOxWQ1QQ.mjs} +2 -2
  6. package/dist/{Detail-B_Q7Qkuz.mjs → Detail-BfKW0uZE.mjs} +2 -2
  7. package/dist/Detail-DFVu2F38.mjs +11 -0
  8. package/dist/{Detail-t3tOcy3X.js → Detail-DJea_fCd.js} +1 -1
  9. package/dist/{Detail-CNiLIEh9.js → Detail-DPJacXfm.js} +1 -1
  10. package/dist/Detail-DSLZ7_bM.js +1 -0
  11. package/dist/{Detail-BESGmY7z.js → Detail-DUGThL9Y.js} +1 -1
  12. package/dist/{Detail-DNxAzeCs.mjs → Detail-D_LlEeHu.mjs} +2 -2
  13. package/dist/Overview-0hFXiuYo.js +1 -0
  14. package/dist/Overview-BTx5LF4J.js +1 -0
  15. package/dist/Overview-CCu2PHai.mjs +289 -0
  16. package/dist/Overview-D2u-leeO.js +1 -0
  17. package/dist/Overview-D_2wozCG.mjs +1733 -0
  18. package/dist/Overview-Dffo-zH4.mjs +11 -0
  19. package/dist/{TableCalculationWidget-CrqcLGfU.mjs → TableCalculationWidget-B_LFoOYm.mjs} +3 -3
  20. package/dist/{TableCalculationWidget-CkQPrVUd.js → TableCalculationWidget-CahOIcbk.js} +1 -1
  21. package/dist/TableDataWidget-BwqWFH4A.js +1 -0
  22. package/dist/TableDataWidget-RsVxcDsL.mjs +112 -0
  23. package/dist/TableOverviewCard-BPWCqo56.js +1 -0
  24. package/dist/{TableOverviewCard-Ci0d4o4w.mjs → TableOverviewCard-D667DDag.mjs} +113 -127
  25. package/dist/TableResultView.vue_vue_type_script_setup_true_lang-C3XiOXgb.js +1 -0
  26. package/dist/TableResultView.vue_vue_type_script_setup_true_lang-sp0TLio0.mjs +547 -0
  27. package/dist/{TableRowActionCard-BhdnnPaq.mjs → TableRowActionCard-BPYJ-Kjy.mjs} +95 -101
  28. package/dist/TableRowActionCard-Bnqj1YIe.js +1 -0
  29. package/dist/TableView-CwhZ9YN2.js +4 -0
  30. package/dist/TableView-DX9PKS8N.mjs +9673 -0
  31. package/dist/TemplateBuilder.vue_vue_type_script_setup_true_lang-BgDdzbLe.js +1 -0
  32. package/dist/TemplateBuilder.vue_vue_type_script_setup_true_lang-D85V4nrQ.mjs +2886 -0
  33. package/dist/cms-core-modules.css +1 -1
  34. package/dist/dateRangePresets-CaPVagHY.mjs +453 -0
  35. package/dist/dateRangePresets-gxQCiZ6c.js +1 -0
  36. package/dist/index-6hZ6MYY6.js +1 -0
  37. package/dist/index-BVmmicu3.mjs +14 -0
  38. package/dist/index-ChHD_1Cf.js +144 -0
  39. package/dist/index-CvV4nVWI.js +1 -0
  40. package/dist/index-DH2z0W_w.mjs +19012 -0
  41. package/dist/index-DZKzj48V.mjs +17 -0
  42. package/dist/index.cjs.js +1 -1
  43. package/dist/index.mjs +237 -103
  44. package/dist/orders-C_sz8J9b.js +1 -0
  45. package/dist/{orders-BfTLIm7U.mjs → orders-Cgdggzvx.mjs} +84 -84
  46. package/dist/server-handlers.cjs.js +1 -1
  47. package/dist/server-handlers.mjs +2055 -1123
  48. package/dist/src/appointments/chat-components/AppointmentListTable.vue.d.ts +3 -0
  49. package/dist/src/appointments/index.d.ts +93 -3
  50. package/dist/src/appointments/reservation-summary.d.ts +48 -0
  51. package/dist/src/appointments/server.d.ts +1 -0
  52. package/dist/src/appointments/tools.d.ts +93 -3
  53. package/dist/src/filezone/config.d.ts +31 -0
  54. package/dist/src/filezone/index.d.ts +43 -0
  55. package/dist/src/filezone/index.vue.d.ts +2 -0
  56. package/dist/src/filezone/metadata.d.ts +2 -0
  57. package/dist/src/filezone/pages/Overview.vue.d.ts +4 -0
  58. package/dist/src/filezone/permissions.d.ts +5 -0
  59. package/dist/src/filezone/routes.d.ts +5 -0
  60. package/dist/src/filezone/server.d.ts +36 -0
  61. package/dist/src/index.d.ts +2 -0
  62. package/dist/src/mailTemplates/builder-types.d.ts +40 -0
  63. package/dist/src/mailTemplates/components/BlockRenderer.vue.d.ts +24 -0
  64. package/dist/src/mailTemplates/components/DesignPanel.vue.d.ts +13 -0
  65. package/dist/src/mailTemplates/components/EmailCanvas.vue.d.ts +44 -0
  66. package/dist/src/mailTemplates/components/RichTextEditor.vue.d.ts +10 -0
  67. package/dist/src/mailTemplates/components/RightSidebar.vue.d.ts +18 -0
  68. package/dist/src/mailTemplates/components/SettingsPanel.vue.d.ts +15 -0
  69. package/dist/src/mailTemplates/components/TemplateBuilder.vue.d.ts +2 -0
  70. package/dist/src/mailTemplates/components/TemplateList.vue.d.ts +19 -1
  71. package/dist/src/mailTemplates/components/rich-text.d.ts +6 -0
  72. package/dist/src/mailTemplates/config.d.ts +10 -4
  73. package/dist/src/mailTemplates/defaults.d.ts +10 -0
  74. package/dist/src/mailTemplates/editor-state.d.ts +21 -0
  75. package/dist/src/mailTemplates/index.d.ts +11 -5
  76. package/dist/src/mailTemplates/index.vue.d.ts +2 -4
  77. package/dist/src/mailTemplates/metadata.d.ts +1 -1
  78. package/dist/src/mailTemplates/pages/Detail.vue.d.ts +1 -13
  79. package/dist/src/mailTemplates/pages/Overview.vue.d.ts +1 -13
  80. package/dist/src/mailTemplates/server.d.ts +23 -0
  81. package/dist/src/server-handlers.d.ts +4 -0
  82. package/dist/src/table/badge-filter-utils.d.ts +8 -0
  83. package/dist/src/table/chat-components/TableOverviewCard.vue.d.ts +2 -7
  84. package/dist/src/table/chat-components/TableResultView.vue.d.ts +3 -7
  85. package/dist/src/table/components/TableDisplayCell.vue.d.ts +8 -0
  86. package/dist/src/table/components/columns/BadgeColumnSettings.vue.d.ts +9 -0
  87. package/dist/src/table/components/columns/ColumnColorSelect.vue.d.ts +11 -0
  88. package/dist/src/table/components/columns/ColumnDisplaySettings.vue.d.ts +9 -0
  89. package/dist/src/table/components/columns/ColumnTypeSelect.vue.d.ts +11 -0
  90. package/dist/src/table/components/columns/ColumnValueSourceInput.vue.d.ts +15 -0
  91. package/dist/src/table/components/columns/DialogTableColumnItem.vue.d.ts +8 -11
  92. package/dist/src/table/components/columns/ProgressColumnSettings.vue.d.ts +8 -0
  93. package/dist/src/table/components/stats/StatDialog.vue.d.ts +6 -10
  94. package/dist/src/table/components/stats/Stats.vue.d.ts +9 -15
  95. package/dist/src/table/composables/useTableClock.d.ts +4 -0
  96. package/dist/src/table/display-utils.d.ts +35 -0
  97. package/dist/src/table/row-value.d.ts +1 -0
  98. package/dist/src/table/stat-utils.d.ts +9 -0
  99. package/dist/src/table/tool-utils.d.ts +1 -1
  100. package/dist/src/table/types.d.ts +54 -1
  101. package/dist/src/whatsappBusiness/analytics.d.ts +4 -0
  102. package/dist/src/whatsappBusiness/components/AccountPanel.vue.d.ts +2 -0
  103. package/dist/src/whatsappBusiness/components/InsightsPanel.vue.d.ts +5 -0
  104. package/dist/src/whatsappBusiness/components/TemplateDialog.vue.d.ts +14 -0
  105. package/dist/src/whatsappBusiness/components/TemplatesPanel.vue.d.ts +5 -0
  106. package/dist/src/whatsappBusiness/components/presentation.d.ts +4 -0
  107. package/dist/src/whatsappBusiness/config.d.ts +51 -0
  108. package/dist/src/whatsappBusiness/connection.d.ts +77 -0
  109. package/dist/src/whatsappBusiness/index.d.ts +63 -0
  110. package/dist/src/whatsappBusiness/index.vue.d.ts +5 -0
  111. package/dist/src/whatsappBusiness/metadata.d.ts +2 -0
  112. package/dist/src/whatsappBusiness/pages/Overview.vue.d.ts +2 -0
  113. package/dist/src/whatsappBusiness/permissions.d.ts +5 -0
  114. package/dist/src/whatsappBusiness/provider.d.ts +21 -0
  115. package/dist/src/whatsappBusiness/routes.d.ts +5 -0
  116. package/dist/src/whatsappBusiness/server.d.ts +14 -0
  117. package/dist/src/whatsappBusiness/templates.d.ts +94 -0
  118. package/dist/src/whatsappBusiness/types.d.ts +162 -0
  119. package/dist/{tool-sandbox-DoF1PsyS.js → tool-sandbox-BSkKdH7V.js} +1 -1
  120. package/dist/{tool-sandbox-DdkMLkDK.mjs → tool-sandbox-DU9b1B7v.mjs} +1 -1
  121. package/dist/{types-ibC3-e1N.mjs → types-B5MifmeF.mjs} +339 -273
  122. package/dist/types-D5dgTskW.js +1 -0
  123. package/dist/{useTableAgentTools-D5mzGf3A.js → useTableAgentTools-BrUot49I.js} +1 -1
  124. package/dist/{useTableAgentTools-DcN3kYRL.mjs → useTableAgentTools-r5InEI4p.mjs} +8 -8
  125. package/dist/{vee-validate-zod-BQElPD0m.js → vee-validate-zod-CWZwNTWE.js} +1 -1
  126. package/dist/{vee-validate-zod-CN16K2x9.mjs → vee-validate-zod-D0WaDgDZ.mjs} +1 -1
  127. package/package.json +2 -2
  128. package/src/appointments/tools.ts +52 -2
  129. package/dist/AppointmentListTable-DZJNmTMb.mjs +0 -177
  130. package/dist/AppointmentListTable-Dieu9US_.js +0 -1
  131. package/dist/ContentEditor-BcIUumah.js +0 -467
  132. package/dist/Detail-Byk781b4.mjs +0 -26
  133. package/dist/Detail-mvSGbnFU.js +0 -1
  134. package/dist/Overview-BDlBCUI_.js +0 -1
  135. package/dist/Overview-Su8ZUbMs.mjs +0 -26
  136. package/dist/TableDataWidget-7qvC3Egd.js +0 -1
  137. package/dist/TableDataWidget-DMTSoGRO.mjs +0 -121
  138. package/dist/TableOverviewCard-AFq-puJs.js +0 -1
  139. package/dist/TableResultView.vue_vue_type_script_setup_true_lang-CBn-Oxl9.mjs +0 -534
  140. package/dist/TableResultView.vue_vue_type_script_setup_true_lang-C_Lv-SmJ.js +0 -1
  141. package/dist/TableRowActionCard-IzB6tNN-.js +0 -1
  142. package/dist/TableView-6mdrL3zx.js +0 -4
  143. package/dist/TableView-Du5nOE6R.mjs +0 -8228
  144. package/dist/TemplateList.vue_vue_type_script_setup_true_lang-B_AtWnHa.js +0 -1
  145. package/dist/TemplateList.vue_vue_type_script_setup_true_lang-Cmovy7kk.mjs +0 -40
  146. package/dist/dateRangePresets-B-l5b_wR.mjs +0 -48
  147. package/dist/dateRangePresets-CPWqst_x.js +0 -1
  148. package/dist/orders-DUa2PVoa.js +0 -1
  149. package/dist/types-BMraG1kN.js +0 -1
@@ -1 +1 @@
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;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("h3");require("vue");const C=require("./types-D5dgTskW.js");function Wt(e){const{initFirebase:t,normalizeTimestamps:s}=e,i=a.createRouter();async function n(r){const{supabase:d,instanceId:c}=r.context.module,{data:o,error:u}=await d.from("project_modules").select("config").eq("id",c).single();if(u||!o?.config)throw a.createError({statusCode:500,statusMessage:"Failed to load module config."});const l=o.config,m=l.project,p=l.productCollection||"products";if(!m)throw a.createError({statusCode:400,statusMessage:"Products module has no Firebase integration configured."});return{firebase:await t(r,m),collection:p}}return i.get("/products",a.defineEventHandler(async r=>{try{const{firebase:d,collection:c}=await n(r);return(await d.firestore().collection(c).get()).docs.map(u=>s({id:u.id,...u.data()}))}catch(d){throw console.error("Products handler — list error:",d),a.createError({statusCode:d.statusCode||500,statusMessage:d.statusMessage||"Failed to list products"})}})),i.get("/products/:productId",a.defineEventHandler(async r=>{const d=a.getRouterParam(r,"productId");if(!d)throw a.createError({statusCode:400,statusMessage:"Product ID is required."});try{const{firebase:c,collection:o}=await n(r),u=await c.firestore().collection(o).doc(d).get();if(!u.exists)throw a.createError({statusCode:404,statusMessage:"Product not found."});return s({id:u.id,...u.data()})}catch(c){throw console.error("Products handler — get error:",c),a.createError({statusCode:c.statusCode||500,statusMessage:c.statusMessage||"Failed to get product"})}})),i.get("/empty-stock",a.defineEventHandler(async r=>{const c=a.getQuery(r).category;try{const{firebase:o,collection:u}=await n(r);let l=o.firestore().collection(u).where("stock","==",0);c&&(l=l.where("collections","array-contains",c));const p=(await l.get()).docs.map(g=>s({id:g.id,...g.data()}));return{count:p.length,products:p.map(g=>({id:g.id,title:g.title,slug:g.slug,stock:g.stock,price:g.price,currency:g.currency,status:g.status}))}}catch(o){throw console.error("Products handler — empty-stock error:",o),a.createError({statusCode:o.statusCode||500,statusMessage:o.statusMessage||"Failed to query empty stock"})}})),i.handler}const Ke=500,Gt=["confirmed","pending","cancelled","all"],Yt=["active","approved","needs_approval","cancelled","rejected","all"];function Z(e){throw a.createError({statusCode:400,statusMessage:e})}function pe(e,t,s){const i=e[t];return i===void 0&&s!==void 0?s:((typeof i!="string"||!i.trim())&&Z(`${t} must be a non-empty string.`),i.trim())}function Ze(e,t,s,i,n=Number.MAX_SAFE_INTEGER){if(e[t]===void 0)return s;const r=pe(e,t),d=Number(r);return(!/^\d+$/.test(r)||!Number.isSafeInteger(d)||d<i||d>n)&&Z(`${t} must be an integer between ${i} and ${n}.`),d}function P(e){return typeof e=="string"?e:null}function Jt(e){if(typeof e!="number"&&(typeof e!="string"||!e.trim()))return null;const t=Number(e);return Number.isFinite(t)?t:null}function Xe(e,t,s){const i={yesterday:-1,today:0,tomorrow:1};if(Object.prototype.hasOwnProperty.call(i,e)){const r=new Date(`${t}T00:00:00.000Z`);return r.setUTCDate(r.getUTCDate()+i[e]),r.toISOString().slice(0,10)}/^\d{4}-\d{2}-\d{2}$/.test(e)||Z(`${s} must be YYYY-MM-DD, today, tomorrow, or yesterday.`);const n=new Date(`${e}T00:00:00.000Z`);return(!Number.isFinite(n.getTime())||n.toISOString().slice(0,10)!==e)&&Z(`${s} must be a valid calendar date.`),e}async function Kt(e,t,s,i,n=new Date){let r;try{const A=new Intl.DateTimeFormat("en-US",{timeZone:i,year:"numeric",month:"2-digit",day:"2-digit"}).formatToParts(n),I=T=>A.find(S=>S.type===T).value;r=`${I("year")}-${I("month")}-${I("day")}`}catch{Z("The project timezone is invalid.")}const d=Xe(pe(s,"startDate"),r,"startDate"),c=Xe(pe(s,"endDate",d),r,"endDate");c<d&&Z("endDate must be on or after startDate.");const o=s.agendaId===void 0?null:pe(s,"agendaId"),u=pe(s,"orderStatus","confirmed"),l=pe(s,"reservationStatus","active");Gt.includes(u)||Z("Invalid orderStatus."),Yt.includes(l)||Z("Invalid reservationStatus.");const m=Ze(s,"limit",100,1,200),p=Ze(s,"offset",0,0);let g=e.collection(t);o&&(g=g.where("agendaId","==",o)),u!=="all"&&(g=g.where("status","==",u)),g=g.orderBy("__name__").select("status","agendaId","reservations","customerInfo").limit(Ke);const f={reservationCount:0,orderCount:0,spotCount:0},h=new Map,y=[];let w;for(;;){const A=await(w?g.startAfter(w):g).get();for(const I of A.docs){const T=I.data(),S=new Set;for(const M of Array.isArray(T.reservations)?T.reservations:[]){if(!M||typeof M.date!="string")continue;const x=M.date;if(x<d||x>c||l==="active"&&["cancelled","rejected"].includes(M.status)||l!=="active"&&l!=="all"&&M.status!==l)continue;const q=M.spots==null?1:Number(M.spots),F=Number.isFinite(q)&&q>=0?q:1,z=h.get(x)||{reservationCount:0,orderCount:0,spotCount:0};z.reservationCount++,z.spotCount+=F,h.set(x,z),S.add(x),f.reservationCount>=p&&y.length<m&&y.push({orderId:I.id,reservationId:P(M.id),agendaId:P(T.agendaId),date:x,startTime:P(M.timeslot?.startTime),endTime:P(M.timeslot?.endTime),spots:F,status:P(T.status),reservationStatus:P(M.status),resourceId:P(M.resourceId)??P(M.resource?.id),resourceName:P(M.resource?.name)??P(M.resourceName),customerInfo:{firstName:P(T.customerInfo?.firstName),lastName:P(T.customerInfo?.lastName),email:P(T.customerInfo?.email)},reservationPrice:Jt(M.totalPrice)}),f.reservationCount++,f.spotCount+=F}S.size&&f.orderCount++;for(const M of S)h.get(M).orderCount++}if(A.docs.length<Ke)break;w=A.docs[A.docs.length-1]}const b=p+y.length,E=b<f.reservationCount;return{startDate:d,endDate:c,timeZone:i,filters:{agendaId:o,orderStatus:u,reservationStatus:l},...f,byDate:[...h].sort(([A],[I])=>A.localeCompare(I)).map(([A,I])=>({date:A,...I})),appointments:y,pagination:{offset:p,limit:m,returnedCount:y.length,hasMore:E,nextOffset:E?b:null}}}function Zt(e){const{initFirebase:t,normalizeTimestamps:s,deleteField:i}=e,n=a.createRouter(),r="privateSettings",d="refundFlow",c="metadataSchema";async function o(f){const{supabase:h,instanceId:y}=f.context.module,{data:w,error:b}=await h.from("project_modules").select("config, project_id").eq("id",y).single();if(b||!w?.config)throw a.createError({statusCode:500,statusMessage:"Failed to load module config."});const E=w.config,A=E.project,I=E.reservationsCollection||"bookings_orders",T=E.agendaCollection||"agendas";if(!A)throw a.createError({statusCode:400,statusMessage:"Appointments module has no Firebase integration configured."});return{firebase:await t(f,A),reservationsCollection:I,agendaCollection:T,projectId:w.project_id}}function u(f){return(f.reservations||[]).map(y=>({orderId:f.id,agendaId:f.agendaId,reservationId:y.id,customerInfo:f.customerInfo,date:y.date,startTime:y.timeslot?.startTime,endTime:y.timeslot?.endTime,spots:y.spots,status:f.status,reservationStatus:y.status,resourceId:y.resourceId,reservationPrice:y.totalPrice,reservationBasePrice:y.basePrice,reservationAddOnsPrice:y.addOnsPrice,pricingOption:y.pricingOption,amountDue:f.amountDue,amountPaid:f.amountPaid,createdAt:f.createdAt,metadata:f.metadata||{},reservationMetadata:y.metadata||{}}))}function l(f){return{showAmount:f?.showAmount!==!1,fields:Array.isArray(f?.fields)?f.fields:[]}}function m(){return i?{refundFlow:i(),refundFlowEnabled:i(),refundDialog:i()}:{refundFlow:null,refundFlowEnabled:null,refundDialog:null}}function p(f){return Array.isArray(f)?f:[]}function g(){return i?{metadataSchema:i()}:{metadataSchema:null}}return n.get("/agendas",a.defineEventHandler(async f=>{try{const{firebase:h,agendaCollection:y}=await o(f),b=(await h.firestore().collection(y).get()).docs.map(E=>{const A=E.data();return{id:E.id,serviceName:A.serviceName||"",type:A.type||"regular"}});return{count:b.length,agendas:b}}catch(h){throw console.error("Appointments handler — list agendas error:",h),a.createError({statusCode:h.statusCode||500,statusMessage:h.statusMessage||"Failed to list agendas"})}})),n.get("/agendas/:agendaId/opening-hours",a.defineEventHandler(async f=>{const h=a.getRouterParam(f,"agendaId"),w=a.getQuery(f).date;if(!h)throw a.createError({statusCode:400,statusMessage:"Agenda ID is required."});if(!w)throw a.createError({statusCode:400,statusMessage:"Date is required (YYYY-MM-DD)."});try{const{firebase:b,agendaCollection:E}=await o(f),A=await b.firestore().collection(E).doc(h).get();if(!A.exists)throw a.createError({statusCode:404,statusMessage:"Agenda not found."});const I=A.data(),T=(I.resources||[]).filter(F=>F.isActive),S=I.exceptions||[],x=new Date(w+"T00:00:00").getDay(),q=T.map(F=>{const z=F.openingHours?.[x]||[],V=S.find(_=>{const se=w>=_.startDate&&w<=_.endDate,Q=!_.resourceIds||_.resourceIds.length===0||_.resourceIds.includes(F.id);return se&&Q});return V?{resourceId:F.id,resourceName:F.name,date:w,dayOfWeek:x,isClosed:V.isClosed,hours:V.isClosed?[]:(V.timeslots||[]).map(_=>({start:_.startTime,end:_.endTime})),isException:!0}:{resourceId:F.id,resourceName:F.name,date:w,dayOfWeek:x,isClosed:z.length===0,hours:z,isException:!1}});return{agendaId:h,serviceName:I.serviceName||"",date:w,resources:q}}catch(b){throw console.error("Appointments handler — opening hours error:",b),a.createError({statusCode:b.statusCode||500,statusMessage:b.statusMessage||"Failed to get opening hours"})}})),n.get("/agendas/:agendaId/refund-flow",a.defineEventHandler(async f=>{const h=a.getRouterParam(f,"agendaId");if(!h)throw a.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const{firebase:y,agendaCollection:w}=await o(f),E=y.firestore().collection(w).doc(h),A=E.collection(r).doc(d),[I,T]=await Promise.all([E.get(),A.get()]);if(!I.exists)throw a.createError({statusCode:404,statusMessage:"Agenda not found."});const S=T.exists?T.data()||{}:{};return{refundFlow:S.flow||{},refundFlowEnabled:S.enabled!==!1,refundDialog:l(S.dialog)}}catch(y){throw console.error("Appointments handler — refund flow load error:",y),a.createError({statusCode:y.statusCode||500,statusMessage:y.statusMessage||"Failed to load refund flow"})}})),n.patch("/agendas/:agendaId/refund-flow",a.defineEventHandler(async f=>{const h=a.getRouterParam(f,"agendaId");if(!h)throw a.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const y=await a.readBody(f),{firebase:w,agendaCollection:b}=await o(f),E=w.firestore(),A=E.collection(b).doc(h),I=A.collection(r).doc(d),T=l(y?.refundDialog);if(!(await A.get()).exists)throw a.createError({statusCode:404,statusMessage:"Agenda not found."});const M=E.batch();return M.set(I,{flow:y?.refundFlow||{},enabled:y?.refundFlowEnabled!==!1,dialog:T,updatedAt:new Date().toISOString()},{merge:!0}),M.set(A,m(),{merge:!0}),await M.commit(),{success:!0}}catch(y){throw console.error("Appointments handler — refund flow save error:",y),a.createError({statusCode:y.statusCode||500,statusMessage:y.statusMessage||"Failed to save refund flow"})}})),n.get("/agendas/:agendaId/metadata-schema",a.defineEventHandler(async f=>{const h=a.getRouterParam(f,"agendaId");if(!h)throw a.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const{firebase:y,agendaCollection:w}=await o(f),E=y.firestore().collection(w).doc(h),A=E.collection(r).doc(c),[I,T]=await Promise.all([E.get(),A.get()]);if(!I.exists)throw a.createError({statusCode:404,statusMessage:"Agenda not found."});const S=T.exists?T.data()||{}:{};return{metadataSchema:p(S.schema)}}catch(y){throw console.error("Appointments handler — metadata schema load error:",y),a.createError({statusCode:y.statusCode||500,statusMessage:y.statusMessage||"Failed to load metadata schema"})}})),n.patch("/agendas/:agendaId/metadata-schema",a.defineEventHandler(async f=>{const h=a.getRouterParam(f,"agendaId");if(!h)throw a.createError({statusCode:400,statusMessage:"Agenda ID is required."});try{const y=await a.readBody(f),{firebase:w,agendaCollection:b}=await o(f),E=w.firestore(),A=E.collection(b).doc(h),I=A.collection(r).doc(c);if(!(await A.get()).exists)throw a.createError({statusCode:404,statusMessage:"Agenda not found."});const S=E.batch();return S.set(I,{schema:p(y?.metadataSchema),updatedAt:new Date().toISOString()},{merge:!0}),S.set(A,g(),{merge:!0}),await S.commit(),{success:!0}}catch(y){throw console.error("Appointments handler — metadata schema save error:",y),a.createError({statusCode:y.statusCode||500,statusMessage:y.statusMessage||"Failed to save metadata schema"})}})),n.get("/appointments/summary",a.defineEventHandler(async f=>{const{firebase:h,reservationsCollection:y,projectId:w}=await o(f),{data:b,error:E}=await f.context.module.supabase.from("projects").select("timezone").eq("id",w).single();if(E||!b)throw a.createError({statusCode:500,statusMessage:"Failed to load the booking project timezone."});return Kt(h.firestore(),y,a.getQuery(f),b.timezone||"UTC")})),n.get("/appointments",a.defineEventHandler(async f=>{const h=a.getQuery(f),y=Math.min(Number(h.quantity)||20,100),w=h.status||"confirmed";try{const{firebase:b,reservationsCollection:E}=await o(f),I=(await b.firestore().collection(E).orderBy("createdAt","desc").where("status","==",w).limit(y).get()).docs.flatMap(T=>u(s({id:T.id,...T.data()})));return{count:I.length,appointments:I}}catch(b){throw console.error("Appointments handler — list error:",b),a.createError({statusCode:b.statusCode||500,statusMessage:b.statusMessage||"Failed to list appointments"})}})),n.get("/appointments/find",a.defineEventHandler(async f=>{const h=a.getQuery(f),y=(h.name||"").toLowerCase().trim(),w=(h.email||"").toLowerCase().trim(),b=h.date;try{const{firebase:E,reservationsCollection:A}=await o(f);let I=E.firestore().collection(A);w&&(I=I.where("customerInfo.email","==",w));let S=(await I.orderBy("createdAt","desc").limit(200).get()).docs.flatMap(M=>u(s({id:M.id,...M.data()})));return b&&(S=S.filter(M=>M.date===b)),y&&(S=S.filter(M=>{const x=(M.customerInfo?.firstName||"").toLowerCase(),q=(M.customerInfo?.lastName||"").toLowerCase();return x.includes(y)||q.includes(y)||`${x} ${q}`.includes(y)})),{count:S.length,appointments:S}}catch(E){throw console.error("Appointments handler — find error:",E),a.createError({statusCode:E.statusCode||500,statusMessage:E.statusMessage||"Failed to find appointments"})}})),n.get("/appointments/:appointmentId",a.defineEventHandler(async f=>{const h=a.getRouterParam(f,"appointmentId");if(!h)throw a.createError({statusCode:400,statusMessage:"Appointment ID is required."});try{const{firebase:y,reservationsCollection:w}=await o(f),b=await y.firestore().collection(w).doc(h).get();if(!b.exists)throw a.createError({statusCode:404,statusMessage:"Appointment not found."});const E=s({id:b.id,...b.data()});return{...E,reservations:u(E)}}catch(y){throw console.error("Appointments handler — get error:",y),a.createError({statusCode:y.statusCode||500,statusMessage:y.statusMessage||"Failed to get appointment"})}})),n.handler}const et="https://analyticsdata.googleapis.com/v1beta",Xt=["https://www.googleapis.com/auth/analytics.readonly","https://www.googleapis.com/auth/webmasters.readonly"],tt="https://searchconsole.googleapis.com/webmasters/v3";function es(e){const{decrypt:t,getGoogleAccessToken:s}=e,i=a.createRouter();async function n(o){const{supabase:u,instanceId:l}=o.context.module,{data:m,error:p}=await u.from("project_modules").select("config").eq("id",l).single();if(p||!m?.config)throw a.createError({statusCode:500,statusMessage:"Failed to load module config."});const g=m.config,f=g.propertyId,h=g.serviceAccount,y=g.siteUrl||"";if(!h)throw a.createError({statusCode:400,statusMessage:"No Google Service Account configured for this module."});if(!f)throw a.createError({statusCode:400,statusMessage:"No GA4 Property ID configured for this module."});const{data:w,error:b}=await u.from("integrations").select("config").eq("id",h).single();let E=w?.config;if(b||!E){const{data:M,error:x}=await u.from("agency_integrations").select("config").eq("id",h).single();if(x||!M?.config)throw a.createError({statusCode:500,statusMessage:"Failed to load Google Service Account credentials."});E=M.config}const A=t(E.clientEmail),I=t(E.privateKey),T=t(E.projectId);return{accessToken:await s({clientEmail:A,privateKey:I,projectId:T},Xt),propertyId:f,siteUrl:y}}async function r(o,u,l){const m=await fetch(`${et}/properties/${u}:runReport`,{method:"POST",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},body:JSON.stringify(l)});if(!m.ok){const p=await m.json().catch(()=>({}));throw console.error("GA4 runReport failed:",p),a.createError({statusCode:m.status,statusMessage:p?.error?.message||"GA4 API request failed"})}return m.json()}i.get("/report",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=p.match(/^(\d+)daysAgo$/),h=f?parseInt(f[1],10):30,y=`${h*2}daysAgo`,w=`${h+1}daysAgo`,b=[{name:"sessions"},{name:"totalUsers"},{name:"screenPageViews"},{name:"bounceRate"},{name:"averageSessionDuration"},{name:"newUsers"},{name:"engagementRate"},{name:"sessionsPerUser"},{name:"screenPageViewsPerSession"}];let E,A=!0;try{E=await r(u,l,{dateRanges:[{startDate:p,endDate:g,name:"current"},{startDate:y,endDate:w,name:"previous"}],dimensions:[{name:"date"}],metrics:b,metricAggregations:["TOTAL"],orderBys:[{dimension:{dimensionName:"date"}}]})}catch{A=!1,E=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],dimensions:[{name:"date"}],metrics:b,metricAggregations:["TOTAL"],orderBys:[{dimension:{dimensionName:"date"}}]})}return ts(E,A)})),i.get("/realtime",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=await fetch(`${et}/properties/${l}:runRealtimeReport`,{method:"POST",headers:{Authorization:`Bearer ${u}`,"Content-Type":"application/json"},body:JSON.stringify({dimensions:[{name:"unifiedScreenName"}],metrics:[{name:"activeUsers"}],orderBys:[{metric:{metricName:"activeUsers"},desc:!0}],limit:5})});if(!m.ok){const h=await m.json().catch(()=>({}));throw a.createError({statusCode:m.status,statusMessage:h?.error?.message||"Realtime API failed"})}const p=await m.json(),g=(p?.rows||[]).reduce((h,y)=>h+parseInt(y.metricValues?.[0]?.value||"0",10),0),f=(p?.rows||[]).map(h=>({page:h.dimensionValues?.[0]?.value||"(not set)",activeUsers:parseInt(h.metricValues?.[0]?.value||"0",10)}));return{activeUsers:g,activePages:f}})),i.get("/top-pages",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=parseInt(m.limit||"10",10),h=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],dimensions:[{name:"pagePath"}],metrics:[{name:"screenPageViews"},{name:"totalUsers"},{name:"averageSessionDuration"}],orderBys:[{metric:{metricName:"screenPageViews"},desc:!0}],limit:f*2}),y=N(h,"pagePath"),w=new Map;for(const E of y.rows){const A=E.pagePath.toLowerCase().replace(/\/+$/,""),I=w.get(A);if(I){I.screenPageViews+=E.screenPageViews||0,I.totalUsers+=E.totalUsers||0;const T=I.screenPageViews;T>0&&(I.averageSessionDuration=(I.averageSessionDuration*(T-(E.screenPageViews||0))+(E.averageSessionDuration||0)*(E.screenPageViews||0))/T)}else w.set(A,{...E})}return{rows:[...w.values()].sort((E,A)=>(A.screenPageViews||0)-(E.screenPageViews||0)).slice(0,f),rowCount:y.rowCount}})),i.get("/top-sources",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],dimensions:[{name:"sessionSource"}],metrics:[{name:"sessions"},{name:"totalUsers"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:10});return N(f,"sessionSource")})),i.get("/devices",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],dimensions:[{name:"deviceCategory"}],metrics:[{name:"sessions"},{name:"totalUsers"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}]});return N(f,"deviceCategory")})),i.get("/countries",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],dimensions:[{name:"country"}],metrics:[{name:"sessions"},{name:"totalUsers"}],orderBys:[{metric:{metricName:"sessions"},desc:!0}],limit:10});return N(f,"country")})),i.get("/acquisition/channels",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],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 N(f,"sessionDefaultChannelGroup")})),i.get("/acquisition/source-medium",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],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 N(f,"sessionSourceMedium")})),i.get("/acquisition/referrals",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],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 N(f,"sessionSource")})),i.get("/acquisition/campaigns",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],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 N(f,"sessionCampaignName")})),i.get("/content/all-pages",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=parseInt(m.limit||"50",10),h=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],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:f});return N(h,"pagePath")})),i.get("/content/landing-pages",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],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 N(f,"landingPagePlusQueryString")})),i.get("/content/exit-pages",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],dimensions:[{name:"pagePath"}],metrics:[{name:"sessions"},{name:"screenPageViews"},{name:"totalUsers"},{name:"bounceRate"}],orderBys:[{metric:{metricName:"screenPageViews"},desc:!0}],limit:20}),h=N(f,"pagePath");return h.rows=h.rows.map(y=>({...y,exitRate:y.bounceRate||0})),h})),i.get("/content/search-terms",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l,siteUrl:m}=await n(o),p=a.getQuery(o),g=p.startDate||"30daysAgo",f=p.endDate||"today";if(m)try{const w=`${tt}/sites/${encodeURIComponent(m)}/searchAnalytics/query`,b=await fetch(w,{method:"POST",headers:{Authorization:`Bearer ${u}`,"Content-Type":"application/json"},body:JSON.stringify({startDate:d(g),endDate:d(f),dimensions:["query"],rowLimit:30})});if(b.ok){const A=((await b.json()).rows||[]).map(I=>({query:I.keys[0],clicks:I.clicks,impressions:I.impressions,ctr:I.ctr,position:I.position}));if(A.length>0)return{rows:A,rowCount:A.length,source:"search_console"}}else{const E=await b.text().catch(()=>"");console.error(`[GA Module] Search Console API error (${b.status}):`,E)}}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 r(u,l,{dateRanges:[{startDate:g,endDate:f}],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}),b=N(w,"sessionGoogleAdsQuery");if(b.rows.length>0)return{...b,source:"google_ads"}}catch{}const h=await r(u,l,{dateRanges:[{startDate:g,endDate:f}],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{...N(h,"landingPagePlusQueryString"),source:"organic_landing_pages"}})),i.get("/audience/overview",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],dimensions:[{name:"newVsReturning"}],metrics:[{name:"totalUsers"},{name:"sessions"},{name:"engagementRate"},{name:"averageSessionDuration"},{name:"screenPageViewsPerSession"}],metricAggregations:["TOTAL"]});return N(f,"newVsReturning")})),i.get("/audience/technology",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=m.dimension||"browser",y=["browser","operatingSystem","screenResolution"].includes(f)?f:"browser",w=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],dimensions:[{name:y}],metrics:[{name:"totalUsers"},{name:"sessions"},{name:"engagementRate"}],orderBys:[{metric:{metricName:"totalUsers"},desc:!0}],limit:10});return N(w,y)})),i.get("/audience/languages",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],dimensions:[{name:"language"}],metrics:[{name:"totalUsers"},{name:"sessions"}],orderBys:[{metric:{metricName:"totalUsers"},desc:!0}],limit:15});return N(f,"language")})),i.get("/audience/hours",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],dimensions:[{name:"dayOfWeekName"},{name:"hour"}],metrics:[{name:"sessions"}],orderBys:[{dimension:{dimensionName:"dayOfWeekName"}},{dimension:{dimensionName:"hour"}}],limit:168});return st(f,["dayOfWeekName","hour"])})),i.get("/audience/cities",a.defineEventHandler(async o=>{const{accessToken:u,propertyId:l}=await n(o),m=a.getQuery(o),p=m.startDate||"30daysAgo",g=m.endDate||"today",f=await r(u,l,{dateRanges:[{startDate:p,endDate:g}],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 st(f,["city","country"])}));function d(o){const u=o.match(/^(\d+)daysAgo$/);if(u){const l=new Date;return l.setDate(l.getDate()-parseInt(u[1],10)),l.toISOString().slice(0,10)}if(o==="today")return new Date().toISOString().slice(0,10);if(o==="yesterday"){const l=new Date;return l.setDate(l.getDate()-1),l.toISOString().slice(0,10)}return o}async function c(o,u,l){const m=`${tt}/sites/${encodeURIComponent(u)}/searchAnalytics/query`,p=await fetch(m,{method:"POST",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},body:JSON.stringify(l)});if(!p.ok){const g=await p.json().catch(()=>({}));throw a.createError({statusCode:p.status,statusMessage:g?.error?.message||"Search Console API request failed"})}return p.json()}return i.get("/seo/keywords",a.defineEventHandler(async o=>{const{accessToken:u,siteUrl:l}=await n(o);if(!l)throw a.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured. Add it in module settings."});const m=a.getQuery(o),p=d(m.startDate||"30daysAgo"),g=d(m.endDate||"today"),f=Math.min(parseInt(m.limit)||50,100),h=await c(u,l,{startDate:p,endDate:g,dimensions:["query"],rowLimit:f});return{rows:(h.rows||[]).map(y=>({query:y.keys[0],clicks:y.clicks,impressions:y.impressions,ctr:y.ctr,position:y.position})),rowCount:h.rows?.length||0}})),i.get("/seo/pages",a.defineEventHandler(async o=>{const{accessToken:u,siteUrl:l}=await n(o);if(!l)throw a.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured."});const m=a.getQuery(o),p=d(m.startDate||"30daysAgo"),g=d(m.endDate||"today"),f=Math.min(parseInt(m.limit)||50,100),h=await c(u,l,{startDate:p,endDate:g,dimensions:["page"],rowLimit:f});return{rows:(h.rows||[]).map(y=>({page:y.keys[0],clicks:y.clicks,impressions:y.impressions,ctr:y.ctr,position:y.position})),rowCount:h.rows?.length||0}})),i.get("/seo/trends",a.defineEventHandler(async o=>{const{accessToken:u,siteUrl:l}=await n(o);if(!l)throw a.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured."});const m=a.getQuery(o),p=d(m.startDate||"30daysAgo"),g=d(m.endDate||"today"),h=((await c(u,l,{startDate:p,endDate:g,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,b)=>w.date.localeCompare(b.date)),y=h.reduce((w,b)=>({clicks:w.clicks+b.clicks,impressions:w.impressions+b.impressions}),{clicks:0,impressions:0});return y.ctr=y.impressions>0?y.clicks/y.impressions:0,y.avgPosition=h.length>0?h.reduce((w,b)=>w+b.position,0)/h.length:0,{rows:h,totals:y,rowCount:h.length}})),i.get("/seo/query-pages",a.defineEventHandler(async o=>{const{accessToken:u,siteUrl:l}=await n(o);if(!l)throw a.createError({statusCode:400,statusMessage:"Search Console Site URL is not configured."});const m=a.getQuery(o),p=d(m.startDate||"30daysAgo"),g=d(m.endDate||"today"),f=await c(u,l,{startDate:p,endDate:g,dimensions:["query","page"],rowLimit:100});return{rows:(f.rows||[]).map(h=>({query:h.keys[0],page:h.keys[1],clicks:h.clicks,impressions:h.impressions,ctr:h.ctr,position:h.position})),rowCount:f.rows?.length||0}})),i.handler}function ts(e,t=!0){const s=(e.metricHeaders||[]).map(p=>p.name),n=(e.dimensionHeaders||[]).map(p=>p.name).indexOf("date"),r=[];(e.rows||[]).forEach(p=>{const g=n>=0?p.dimensionValues[n]?.value:p.dimensionValues[0]?.value;if(!g||g.length<8)return;const h={date:`${g.slice(0,4)}-${g.slice(4,6)}-${g.slice(6,8)}`};s.forEach((y,w)=>{h[y]=parseFloat(p.metricValues[w]?.value||"0")}),r.push(h)});const d={},c={};e.totals&&e.totals.length>=2?s.forEach((p,g)=>{d[p]=parseFloat(e.totals[0]?.metricValues?.[g]?.value||"0"),c[p]=parseFloat(e.totals[1]?.metricValues?.[g]?.value||"0")}):e.totals&&e.totals.length===1&&s.forEach((p,g)=>{d[p]=parseFloat(e.totals[0]?.metricValues?.[g]?.value||"0")});const o={};s.forEach(p=>{const g=d[p]||0,f=c[p];f!==void 0&&f!==0?o[p]=(g-f)/f*100:o[p]=null});const u=new Map;for(const p of r)u.has(p.date)||u.set(p.date,p);const l=Array.from(u.values()).sort((p,g)=>p.date.localeCompare(g.date)),m=t&&e.totals&&e.totals.length>=2?l.slice(-Math.ceil(l.length/2)):l;return{rows:m,totals:d,previousTotals:c,changes:o,rowCount:m.length}}function N(e,t){const s=(e.metricHeaders||[]).map(n=>n.name),i=(e.rows||[]).map(n=>{const r={[t]:n.dimensionValues[0].value};return s.forEach((d,c)=>{r[d]=parseFloat(n.metricValues[c].value)}),r});return{rows:i,rowCount:e.rowCount||i.length}}function st(e,t){const s=(e.metricHeaders||[]).map(n=>n.name),i=(e.rows||[]).map(n=>{const r={};return t.forEach((d,c)=>{r[d]=n.dimensionValues[c]?.value||""}),s.forEach((d,c)=>{r[d]=parseFloat(n.metricValues[c].value)}),r});return{rows:i,rowCount:e.rowCount||i.length}}function ss(e){const{decrypt:t}=e,s=a.createRouter();async function i(n){const{supabase:r,instanceId:d}=n.context.module,{data:c,error:o}=await r.from("project_modules").select("config").eq("id",d).single();if(o||!c?.config)throw a.createError({statusCode:500,statusMessage:"Failed to load module config."});const u=c.config,l=u.githubIntegration,m=u.githubRepo,p=u.githubOwner;if(!l||!m||!p)throw a.createError({statusCode:400,statusMessage:"No GitHub integration configured for this module."});const{data:g,error:f}=await r.from("integrations").select("config").eq("id",l).single();let h=g?.config;if(f||!h){const{data:w,error:b}=await r.from("agency_integrations").select("config").eq("id",l).single();if(b||!w?.config)throw a.createError({statusCode:500,statusMessage:"Failed to load Github credentials."});h=w.config}return{token:t(h.token),repo:m,owner:p}}return s.post("/github/deploy",a.defineEventHandler(async n=>{try{const{token:r,repo:d,owner:c}=await i(n);await fetch(`https://api.github.com/repos/${c}/${d}/dispatches`,{method:"POST",headers:{Authorization:`Bearer ${r}`,Accept:"application/vnd.github+json"},body:JSON.stringify({event_type:"deploy-site"})})}catch(r){throw console.error("Error triggering GitHub deployment:",r),a.createError({statusCode:500,statusMessage:"Failed to trigger deployment."})}})),s.handler}const Mt="firebaseAuthManager",k={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"},St=C.stringType().trim().email().max(254),Be=C.stringType().min(1).max(128),as=Be.refine(e=>e===e.trim(),{message:"Custom UID may not begin or end with whitespace."}),Dt=C.stringType().trim().regex(/^\+[1-9]\d{1,14}$/,"Phone number must use E.164 format."),rs=C.stringType().email().max(254),os=C.stringType().regex(/^\+[1-9]\d{1,14}$/,"Phone number must use E.164 format."),Ce=C.stringType().trim().min(3).max(500),Rt=C.stringType().trim().min(1).max(128),ns=C.objectType({email:St,password:C.stringType().min(8).max(128),uid:as.optional(),displayName:Rt.optional(),phoneNumber:Dt.optional(),emailVerified:C.booleanType().optional(),disabled:C.booleanType().optional(),createProfile:C.booleanType().optional().default(!1)}).strict(),is=C.objectType({reason:Ce,displayName:Rt.nullable().optional(),email:St.optional(),phoneNumber:Dt.nullable().optional(),emailVerified:C.booleanType().optional()}).strict().refine(e=>X(e,"displayName")||X(e,"email")||X(e,"phoneNumber")||X(e,"emailVerified"),{message:"At least one editable user field is required."}),cs=C.objectType({disabled:C.booleanType(),reason:Ce}).strict(),xe=C.objectType({reason:Ce}).strict(),ds=C.objectType({role:C.stringType().trim().min(1).max(64).nullable(),reason:Ce}).strict(),us=C.objectType({reason:Ce,confirmation:C.stringType().min(1).max(128)}).strict(),ls=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"]),ms=new Set(["__proto__","constructor","prototype"]);function X(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function te(e){throw a.createError({statusCode:400,statusMessage:e})}function at(e,t){return typeof e=="boolean"?e:e==="true"?!0:e==="false"?!1:t}function _t(e){const t=typeof e=="number"?e:Number(e);return Number.isFinite(t)?Math.min(100,Math.max(10,Math.trunc(t))):50}function fs(e){const t=String(e||"users").trim();return t.length===0||t.length>256||t.includes("/")||t==="."||t===".."||/^__.*__$/.test(t)?te("The configured Firestore profile collection is invalid."):t}function ce(e,t,s){const i=String(e??t).trim()||t;return i.length>128||!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(i)||ms.has(i)?te(`The configured ${s} field is invalid.`):i}function ps(e){const t=Array.isArray(e)?e:String(e||"").split(","),s=[];for(const i of t){if(typeof i!="string")return te("Configured role options must be strings.");const n=i.trim();if(n){if(n.length>64)return te("Configured role options may not exceed 64 characters.");s.includes(n)||s.push(n)}}return s.length>50?te("No more than 50 role options may be configured."):s}function gs(e){const t=typeof e.project=="string"?e.project.trim():"";t||te("Firebase Auth Manager has no Firebase integration configured.");const s=at(e.manageRoleClaim,!1),i=ce(e.roleClaimKey,"role","role claim");s&&ls.has(i)&&te("The configured role claim key is reserved by Firebase.");const n={profileUidField:ce(e.profileUidField,"uid","profile UID"),profileNameField:ce(e.profileNameField,"displayName","profile name"),profileEmailField:ce(e.profileEmailField,"email","profile email"),profileRoleField:ce(e.profileRoleField,"role","profile role"),profileStatusField:ce(e.profileStatusField,"status","profile status")};return new Set(Object.values(n)).size!==Object.keys(n).length&&te("Each configured Firestore profile field must use a distinct field name."),{project:t,pageSize:_t(e.pageSize),linkFirestoreProfiles:at(e.linkFirestoreProfiles,!0),profileCollection:fs(e.profileCollection),...n,manageRoleClaim:s,roleClaimKey:i,roleOptions:ps(e.roleOptions)}}function rt(e){const t=Array.isArray(e)?e[0]:e;return typeof t!="string"?void 0:t.trim()||void 0}function ot(e){const t=Array.isArray(e)?e[0]:e;if(!(typeof t!="string"||t.length===0))return t}function hs(e,t=50){const s=rt(e.limit);let i=_t(t);if(s!==void 0){if(!/^\d+$/.test(s))throw a.createError({statusCode:422,statusMessage:"Limit must be a whole number."});if(i=Number(s),i<1||i>100)throw a.createError({statusCode:422,statusMessage:"Limit must be between 1 and 100."})}const n=ot(e.pageToken);if(n&&n.length>4096)throw a.createError({statusCode:422,statusMessage:"Page token is too long."});const r=ot(e.search),d=rt(e.searchBy);if(d&&!r)throw a.createError({statusCode:422,statusMessage:"Search mode requires a search value."});if(!r)return{limit:i,pageToken:n};if(n)throw a.createError({statusCode:422,statusMessage:"Exact search cannot be combined with a page token."});const c=d||"uid";if(!["uid","email","phone"].includes(c))throw a.createError({statusCode:422,statusMessage:"Search mode must be uid, email, or phone."});if(c==="phone"){const u=os.safeParse(r);if(!u.success)throw a.createError({statusCode:422,statusMessage:"Phone search must use E.164 format."});return{limit:1,search:{kind:"phone",value:u.data}}}if(c==="email"){const u=rs.safeParse(r);if(!u.success)throw a.createError({statusCode:422,statusMessage:"Email search is invalid."});return{limit:1,search:{kind:"email",value:u.data}}}const o=Be.safeParse(r);if(!o.success)throw a.createError({statusCode:422,statusMessage:"UID search is invalid."});return{limit:1,search:{kind:"uid",value:o.data}}}function ys(e){const t=new TextEncoder().encode(e);let s="";for(const i of t)s+=String.fromCharCode(i);return btoa(s).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,"")}function ws(e){if(!/^[A-Za-z0-9_-]{1,1024}$/.test(e))throw a.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 i=atob(t),n=Uint8Array.from(i,r=>r.charCodeAt(0));s=new TextDecoder("utf-8",{fatal:!0}).decode(n)}catch{throw a.createError({statusCode:400,statusMessage:"Firebase UID token is invalid."})}if(ys(s)!==e||!Be.safeParse(s).success)throw a.createError({statusCode:400,statusMessage:"Firebase UID token is invalid."});return s}function $e(e,t){return e.isAdmin===!0||Array.isArray(e.permissions)&&e.permissions.includes(t)}function bs(e,t){return t.kind==="uid"||$e(e,k.viewPii)}function Es(e,t){let s=e.length^t.length;const i=Math.max(e.length,t.length);for(let n=0;n<i;n+=1)s|=(e.charCodeAt(n)||0)^(t.charCodeAt(n)||0);return s===0}function H(e,t){if(!$e(e,t))throw a.createError({statusCode:403,statusMessage:"You do not have permission to perform this action."})}function xt(e){if(!e)return null;const t=e.lastIndexOf("@");if(t<=0)return"••••";const s=e.slice(0,t),i=e.slice(t+1),n=i.lastIndexOf("."),r=n>0?i.slice(0,n):i,d=n>0?i.slice(n):"";return`${s.slice(0,1)}•••@${r.slice(0,1)}•••${d}`}function Cs(e){return e?`+••••••${e.replace(/\D/g,"").slice(-2)}`:null}function As(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 Ft(e){return e?`${e.slice(0,1)}•••`:null}function U(e){return typeof e=="string"?e:null}function Is(e){const t=[];if(Array.isArray(e?.providerData))for(const s of e.providerData){const i=U(s?.providerId);i&&t.push(i)}return[...new Set(t)].sort()}function Nt(e){return Array.isArray(e?.multiFactor?.enrolledFactors)?e.multiFactor.enrolledFactors.length:0}function Le(e,t){return t.manageRoleClaim?U(e?.customClaims?.[t.roleClaimKey]):null}function vs(){return{enabled:!1,exists:!1,name:null,email:null,role:null,status:null}}function Ts(e,t,s){const i=e?U(e[s.profileNameField]):null,n=e?U(e[s.profileEmailField]):null;return{enabled:!0,exists:!!e,name:t?i:Ft(i),email:t?n:xt(n),role:t&&e?U(e[s.profileRoleField]):null,status:t&&e?U(e[s.profileStatusField]):null}}function Re(e,t){const s=U(e?.email),i=U(e?.displayName),n=U(e?.phoneNumber);let r;return t.config.linkFirestoreProfiles?t.profileAvailable===!1?r=null:r=Ts(t.profile||null,t.canViewPii,t.config):r=vs(),{uid:String(e?.uid||""),displayName:t.canViewPii?i:Ft(i),email:t.canViewPii?s:xt(s),phoneNumber:t.canViewPii?n:Cs(n),photoURL:t.canViewPii?U(e?.photoURL):null,emailVerified:e?.emailVerified===!0,disabled:e?.disabled===!0,providers:Is(e),createdAt:U(e?.metadata?.creationTime),lastSignInAt:U(e?.metadata?.lastSignInTime),lastRefreshAt:U(e?.metadata?.lastRefreshTime),mfaFactorCount:Nt(e),role:Le(e,t.config),profile:r}}function nt(e,t){if(!e)return null;const s=Re(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 Ms(e,t,s){const i={...e||{}};return s===null?delete i[t]:i[t]=s,i}function it(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 i=Le(e,t);return i!==null&&(s[t.profileRoleField]=i),s}function Ae(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 Ss(e,t="Firebase Authentication request failed."){const s=Ae(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 B(e){const t=e?.context?.module;if(!t||!t.supabase||typeof t.instanceId!="string"||typeof t.projectId!="string"||typeof t.userId!="string")throw a.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 W(e,t){const s=t.safeParse(await a.readBody(e));if(!s.success)throw a.createError({statusCode:422,statusMessage:s.error.issues[0]?.message||"Request body is invalid."});return s.data}function G(e){const t=a.getRouterParam(e,"uid");if(typeof t!="string")throw a.createError({statusCode:400,statusMessage:"A valid Firebase UID is required."});return ws(t)}function $(e,t){if(typeof e?.statusCode=="number"&&typeof e?.statusMessage=="string")throw e;const s=Ss(e,t),i=Ae(e);throw console.error("Firebase Auth Manager request failed",i?{code:i}:{code:"unknown"}),a.createError(s)}function Ds(e){return Ae(e)==="auth/user-not-found"}function qe(e){return $e(e,k.viewPii)}function Rs(e,t){const s=[];for(let i=0;i<e.length;i+=t)s.push(e.slice(i,i+t));return s}function _e(e){return e.length>0&&e!=="."&&e!==".."&&!e.includes("/")&&!/^__.*__$/.test(e)}async function _s(e,t,s){const i=new Map;if(!s.linkFirestoreProfiles||t.length===0)return i;const n=[...new Set(t.map(l=>String(l.uid)).filter(Boolean))],r=e.collection(s.profileCollection),d=n.filter(_e),c=d.map(l=>r.doc(l)),o=c.length===0?[]:typeof e.getAll=="function"?await e.getAll(...c):await Promise.all(c.map(l=>l.get()));for(let l=0;l<o.length;l+=1){const m=o[l],p=d[l];p&&m?.exists&&i.set(p,m.data())}const u=n.filter(l=>!i.has(l));for(const l of Rs(u,30)){const m=await r.where(s.profileUidField,"in",l).get();for(const p of m.docs||[]){const g=p.data(),f=g[s.profileUidField];typeof f=="string"&&l.includes(f)&&!i.has(f)&&i.set(f,g)}}return i}async function ct(e,t,s){const i=e.collection(s.profileCollection);if(_e(t)){const d=await i.doc(t).get();if(d.exists)return{id:String(d.id),data:d.data()}}const r=(await i.where(s.profileUidField,"==",t).limit(1).get()).docs?.[0];return r?{id:String(r.id),data:r.data()}:null}async function L(e,t){const s=B(e),{data:i,error:n}=await s.supabase.from("project_modules").select("type, config").eq("id",s.instanceId).eq("project_id",s.projectId).maybeSingle();if(n)throw a.createError({statusCode:500,statusMessage:"Failed to load Firebase Auth Manager configuration."});if(!i||i.type!==Mt||!i.config)throw a.createError({statusCode:404,statusMessage:"Firebase Auth Manager module instance not found."});const r=gs(i.config),{data:d,error:c}=await s.supabase.from("integrations").select("id, project_id, type").eq("id",r.project).eq("project_id",s.projectId).eq("type","FIREBASE").maybeSingle();if(c)throw a.createError({statusCode:500,statusMessage:"Failed to verify the Firebase integration."});if(!d)throw a.createError({statusCode:424,statusMessage:"The configured Firebase integration is unavailable for this project."});const o=await t.initFirebase(e,r.project);return{module:s,config:r,integrationId:r.project,firebase:o,auth:o.auth(),firestore:o.firestore()}}async function Y(e,t,s){const i={project_id:t.module.projectId,module_id:t.module.instanceId,user_id:t.module.userId,message:`Firebase Auth: ${s.action}`,level:"INFO",params:{source:Mt,action:s.action,targetUid:s.targetUid,integrationId:t.integrationId,reason:As(s.reason),before:nt(s.before,t.config),after:nt(s.after,t.config),...s.details||{}}};try{const{error:n}=await t.module.supabase.from("project_logs").insert(i);return n?(console.error("Firebase Auth Manager audit insert failed",{code:n.code||"unknown"}),!1):!0}catch(n){const r=n&&typeof n=="object"&&"code"in n?String(n.code):"unknown";return console.error("Firebase Auth Manager audit insert failed",{code:r}),!1}}async function xs(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(Ds(s))return[];throw s}}async function dt(e,t,s){try{return await e.getUser(t)}catch(i){return console.error("Firebase Auth Manager post-mutation refresh failed",{code:Ae(i)||"unknown"}),s}}async function ae(e,t){let s=new Map,i=!0;if(t.config.linkFirestoreProfiles)try{s=await _s(t.firestore,e,t.config)}catch(n){i=!1;const r=Ae(n);console.error("Firebase Auth Manager profile join failed",{code:r||"unknown"})}return e.map(n=>Re(n,{canViewPii:qe(t.module),config:t.config,profile:s.get(String(n.uid))||null,profileAvailable:i}))}function Fs(e){const t=a.createRouter();return t.get("/users",a.defineEventHandler(async s=>{const i=B(s);H(i,k.viewUsers);try{const n=await L(s,e),r=hs(a.getQuery(s),n.config.pageSize);if(r.search&&!bs(n.module,r.search))throw a.createError({statusCode:403,statusMessage:"Viewing personal data is required to search by email or phone number."});let d,c=null;if(r.search)d=await xs(n.auth,r.search);else{const o=await n.auth.listUsers(r.limit,r.pageToken);d=Array.isArray(o.users)?o.users:[],c=typeof o.pageToken=="string"?o.pageToken:null}return{users:await ae(d,n),nextPageToken:c}}catch(n){$(n,"Failed to list Firebase users.")}})),t.get("/users/:uid",a.defineEventHandler(async s=>{const i=B(s);H(i,k.viewUsers);const n=G(s);try{const r=await L(s,e),d=await r.auth.getUser(n),[c]=await ae([d],r);return{user:c}}catch(r){$(r,"Failed to load the Firebase user.")}})),t.post("/users",a.defineEventHandler(async s=>{const i=B(s);H(i,k.createUsers);const n=await W(s,ns);n.createProfile&&H(i,k.manageProfiles);try{const r=await L(s,e);if(n.createProfile&&!r.config.linkFirestoreProfiles)throw a.createError({statusCode:409,statusMessage:"Firestore profile linking is disabled for this module."});if(n.uid&&r.config.linkFirestoreProfiles&&await ct(r.firestore,n.uid,r.config))throw a.createError({statusCode:409,statusMessage:"A retained Firestore profile already uses this UID. Resolve that profile before reusing the Authentication UID."});const d=await r.auth.createUser({email:n.email,password:n.password,uid:n.uid,displayName:n.displayName,phoneNumber:n.phoneNumber,emailVerified:n.emailVerified,disabled:n.disabled});let c=null,o=!1,u=null;if(n.createProfile){c=it(d,r.config);try{const m=r.firestore.collection(r.config.profileCollection);await(_e(d.uid)?m.doc(d.uid):m.doc()).create(c),o=!0}catch{c=null,u="The Authentication user was created, but its Firestore profile could not be created."}}const l=await Y(s,r,{action:"user.create",targetUid:d.uid,reason:"Created through Firebase Auth Manager.",before:null,after:d,details:{profileRequested:n.createProfile===!0,profileCreated:o}});return a.setResponseStatus(s,201),{user:Re(d,{canViewPii:qe(r.module),config:r.config,profile:c}),profileCreated:o,profileError:u,auditRecorded:l}}catch(r){$(r,"Failed to create the Firebase user.")}})),t.patch("/users/:uid",a.defineEventHandler(async s=>{const i=B(s);H(i,k.editUsers);const n=G(s),r=await W(s,is);try{const d=await L(s,e),c=await d.auth.getUser(n),o={};X(r,"displayName")&&(o.displayName=r.displayName),X(r,"email")&&(o.email=r.email),X(r,"phoneNumber")&&(o.phoneNumber=r.phoneNumber),X(r,"emailVerified")&&(o.emailVerified=r.emailVerified);const u=await d.auth.updateUser(n,o),l=await Y(s,d,{action:"user.update",targetUid:n,reason:r.reason,before:c,after:u,details:{changedFieldCount:Object.keys(o).length}}),[m]=await ae([u],d);return{user:m,auditRecorded:l}}catch(d){$(d,"Failed to update the Firebase user.")}})),t.post("/users/:uid/status",a.defineEventHandler(async s=>{const i=B(s);H(i,k.manageStatus);const n=G(s),r=await W(s,cs);try{const d=await L(s,e),c=await d.auth.getUser(n),o=c.disabled!==r.disabled,u=o?await d.auth.updateUser(n,{disabled:r.disabled}):c,l=await Y(s,d,{action:r.disabled?"user.disable":"user.enable",targetUid:n,reason:r.reason,before:c,after:u,details:{changed:o}}),[m]=await ae([u],d);return{user:m,changed:o,auditRecorded:l}}catch(d){$(d,"Failed to change the Firebase user status.")}})),t.post("/users/:uid/revoke-sessions",a.defineEventHandler(async s=>{const i=B(s);H(i,k.manageSessions);const n=G(s),r=await W(s,xe);try{const d=await L(s,e),c=await d.auth.getUser(n);await d.auth.revokeRefreshTokens(n);const o=await Y(s,d,{action:"user.revoke_sessions",targetUid:n,reason:r.reason,before:c,after:c,details:{sessionsRevoked:!0}}),u=await dt(d.auth,n,c),[l]=await ae([u],d);return{user:l,sessionsRevoked:!0,auditRecorded:o}}catch(d){$(d,"Failed to revoke Firebase user sessions.")}})),t.post("/users/:uid/reset-mfa",a.defineEventHandler(async s=>{const i=B(s);H(i,k.manageMfa);const n=G(s),r=await W(s,xe);try{const d=await L(s,e),c=await d.auth.getUser(n),o=Nt(c)>0,u=o?await d.auth.updateUser(n,{multiFactor:{enrolledFactors:[]}}):c,l=await Y(s,d,{action:"user.reset_mfa",targetUid:n,reason:r.reason,before:c,after:u,details:{changed:o}}),[m]=await ae([u],d);return{user:m,changed:o,auditRecorded:l}}catch(d){$(d,"Failed to reset Firebase multi-factor authentication.")}})),t.post("/users/:uid/role",a.defineEventHandler(async s=>{const i=B(s);H(i,k.manageRoles);const n=G(s),r=await W(s,ds);try{const d=await L(s,e);if(!d.config.manageRoleClaim)throw a.createError({statusCode:409,statusMessage:"Role claim management is disabled for this module."});if(r.role!==null&&!d.config.roleOptions.includes(r.role))throw a.createError({statusCode:422,statusMessage:"Role must be one of the configured values."});const c=await d.auth.getUser(n),u=Le(c,d.config)!==r.role;let l=c;if(u){const g=Ms(c.customClaims,d.config.roleClaimKey,r.role);await d.auth.setCustomUserClaims(n,g),l={...c,customClaims:g}}const m=await Y(s,d,{action:"user.set_role",targetUid:n,reason:r.reason,before:c,after:l,details:{changed:u,role:r.role}});u&&(l=await dt(d.auth,n,l));const[p]=await ae([l],d);return{user:p,changed:u,auditRecorded:m}}catch(d){$(d,"Failed to update the Firebase role claim.")}})),t.post("/users/:uid/profile",a.defineEventHandler(async s=>{const i=B(s);H(i,k.manageProfiles);const n=G(s),r=await W(s,xe);try{const d=await L(s,e);if(!d.config.linkFirestoreProfiles)throw a.createError({statusCode:409,statusMessage:"Firestore profile linking is disabled for this module."});const c=await d.auth.getUser(n),o=await ct(d.firestore,n,d.config);let u=o?.data||null,l=!1;if(!o){u=it(c,d.config);const p=d.firestore.collection(d.config.profileCollection);await(_e(n)?p.doc(n):p.doc()).create(u),l=!0}const m=await Y(s,d,{action:"user.create_profile",targetUid:n,reason:r.reason,before:c,after:c,details:{changed:l}});return{user:Re(c,{canViewPii:qe(d.module),config:d.config,profile:u}),profileCreated:l,auditRecorded:m}}catch(d){$(d,"Failed to create the Firestore profile.")}})),t.delete("/users/:uid",a.defineEventHandler(async s=>{const i=B(s);H(i,k.deleteUsers);const n=G(s),r=await W(s,us);if(!Es(n,r.confirmation))throw a.createError({statusCode:422,statusMessage:"Deletion confirmation must exactly match the Firebase UID."});try{const d=await L(s,e),c=await d.auth.getUser(n);await d.auth.deleteUser(n);const o=await Y(s,d,{action:"user.delete",targetUid:n,reason:r.reason,before:c,after:null,details:{firestoreProfilePreserved:!0}});return{uid:n,deleted:!0,firestoreProfilePreserved:!0,auditRecorded:o}}catch(d){$(d,"Failed to delete the Firebase user.")}})),t.handler}const ze=1,ut=700*1024,lt=20,Ns=5e4,Ps=120,ks=80,mt=2e3,ft=120,Us="studio_forms",Pt=new Set(["draft","published","archived"]),js=new Set(["__proto__","constructor","prototype"]),j={view:"view-forms",create:"create-form",edit:"edit-form",publish:"publish-form",archive:"archive-form"};function D(e){throw a.createError({statusCode:400,statusMessage:e})}function J(e){throw a.createError({statusCode:409,statusMessage:e})}function Ve(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 kt(e,t,s){typeof e!="string"&&D(`${t} is required.`);const i=e.trim();return i||D(`${t} is required.`),i.length>s&&D(`${t} cannot exceed ${s} characters.`),i}function Os(e){if(e==null)return"";typeof e!="string"&&D("Form description must be a string.");const t=e.trim();return t.length>mt&&D(`Form description cannot exceed ${mt} characters.`),t}function qs(e){const t=kt(e,"Form key",ks).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 Hs(e){if(e==null||e==="")return Us;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 i of s)(!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/.test(i)||i==="."||i===".."||/^__.*__$/.test(i))&&D("The configured forms collection path contains an invalid segment.");return t}function Bs(e){(!e||typeof e!="object")&&D("Form definition must be an object or array.");const t=new Set;let s=0;const i=(d,c,o)=>{if(s+=1,s>Ns&&D("Form definition is too complex."),c>lt&&D(`Form definition cannot be nested deeper than ${lt} levels.`),d===null||typeof d=="string"||typeof d=="boolean")return;if(typeof d=="number"){Number.isFinite(d)||D("Form definition contains an invalid number.");return}if(typeof d!="object"&&D("Form definition contains a value that cannot be stored."),t.has(d)&&D("Form definition cannot contain circular references."),t.add(d),Array.isArray(d)){o&&D("Form definition cannot contain an array directly inside another array.");for(const l of d)i(l,c+1,!0);t.delete(d);return}const u=Object.getPrototypeOf(d);u!==Object.prototype&&u!==null&&D("Form definition can contain only plain JSON objects.");for(const[l,m]of Object.entries(d))js.has(l)&&D(`Form definition contains a forbidden object key: ${l}.`),new TextEncoder().encode(l).byteLength>1500&&D("Form definition contains an object key that is too long."),i(m,c+1,!1);t.delete(d)};i(e,1,!1);let n;try{n=JSON.stringify(e)}catch{D("Form definition must be valid JSON.")}new TextEncoder().encode(n).byteLength>ut&&D(`Form definition cannot exceed ${ut} bytes.`)}function Ut(e){const t=Ve(e,"Request body"),s=t.definition;return Bs(s),{name:kt(t.name,"Form name",Ps),key:qs(t.key),description:Os(t.description),definition:s}}function $s(e){return Ut(e)}function jt(e){return(!Number.isInteger(e)||e<1)&&D("expectedRevision must be a positive integer."),e}function Ls(e){const t=Ve(e,"Request body");return{...Ut(t),expectedRevision:jt(t.expectedRevision)}}function Fe(e){const t=Ve(e,"Request body");return{expectedRevision:jt(t.expectedRevision)}}function zs(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||ze,revision:e.revision,draftRevision:e.form.draftRevision,previousRevisionId:e.form.publishedRevisionId||null,publishedAt:e.publishedAt,publishedBy:e.publishedBy}}function de(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 he(e){const s=e?.context?.module?.userId||e?.context?.user?.id||null;return typeof s=="string"&&s.trim()?s:null}function O(e,t){const s=e?.context?.module;if(!s)throw a.createError({statusCode:500,statusMessage:"Module context is unavailable."});if(!s.userId)throw a.createError({statusCode:401,statusMessage:"Unauthorized"});if(s.isAdmin===!0)return;if(!(Array.isArray(s.permissions)?s.permissions:[]).includes(t))throw a.createError({statusCode:403,statusMessage:`Missing module permission: ${t}.`})}function ue(e){if(!e||typeof e!="object"||Array.isArray(e))throw a.createError({statusCode:500,statusMessage:"Stored form data is invalid."});const t=e;if(typeof t.name!="string"||typeof t.key!="string"||!Pt.has(String(t.status))||!Number.isInteger(t.revision)||!Number.isInteger(t.draftRevision)||!t.draftDefinition||typeof t.draftDefinition!="object")throw a.createError({statusCode:500,statusMessage:"Stored form data is invalid."});return t}function ve(e,t){e.revision!==t&&J(`Form has changed. Expected revision ${t}, current revision is ${e.revision}.`)}function Vs(e){(Array.isArray(e)||!Array.isArray(e.layout)||e.layout.length===0)&&D("Add at least one form element before publishing.")}function re(e,t,s,i=!0){const{draftDefinition:n,...r}=t,d=i?{id:e,...r,draftDefinition:n}:{id:e,...r},c=s?s(d):d,o=Number.isInteger(c.publishedRevision)?c.publishedRevision:null,u=Number.isInteger(c.publishedDraftRevision)?c.publishedDraftRevision:null;return{id:c.id,name:c.name,key:c.key,description:c.description||"",...i?{definition:n}:{},status:c.status,revision:c.revision,draftRevision:c.draftRevision,publishedRevision:o,publishedRevisionId:c.publishedRevisionId||null,hasUnpublishedChanges:!c.publishedRevisionId||c.draftRevision!==u,schemaVersion:c.schemaVersion||ze,createdAt:c.createdAt||null,createdBy:c.createdBy||null,updatedAt:c.updatedAt||null,updatedBy:c.updatedBy||null,publishedAt:c.publishedAt||null,publishedBy:c.publishedBy||null,archivedAt:c.archivedAt||null,archivedBy:c.archivedBy||null}}function pt(e,t){if(!(e==null||e===""))return(Array.isArray(e)||typeof e!="string")&&D(`${t} must be a single string.`),e.trim()}function gt(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 oe(e,t,s){throw Number.isInteger(t?.statusCode)&&t.statusCode>=400&&t.statusCode<600?t:(console.error(`Form Builder handler — ${e} error:`,t),a.createError({statusCode:500,statusMessage:s}))}function Qs(e){const{initFirebase:t,normalizeTimestamps:s}=e,i=a.createRouter();async function n(r){const d=r?.context?.module,{supabase:c,instanceId:o,projectId:u,moduleType:l}=d||{};if(!c||!o)throw a.createError({statusCode:500,statusMessage:"Module context is unavailable."});const{data:m,error:p}=await c.from("project_modules").select("config, project_id, type").eq("id",o).single();if(p||!m?.config)throw a.createError({statusCode:500,statusMessage:"Failed to load module config."});if(u&&m.project_id!==u)throw a.createError({statusCode:403,statusMessage:"Module project context does not match."});if(l&&m.type!==l)throw a.createError({statusCode:409,statusMessage:"Module type context does not match."});const g=m.config,f=g.databaseIntegration;if(typeof f!="string"||!f.trim())throw a.createError({statusCode:400,statusMessage:"Form Builder has no Firebase integration configured."});if(f.length>200||!/^[A-Za-z0-9_-]+$/.test(f))throw a.createError({statusCode:500,statusMessage:"Configured Firebase integration ID is invalid."});const h=u||m.project_id,{data:y,error:w}=await c.from("integrations").select("id").eq("id",f).eq("project_id",h).eq("type","FIREBASE").single();if(w||!y)throw a.createError({statusCode:400,statusMessage:"Configured Firebase integration is unavailable for this project."});const b=Hs(g.formsCollection),E=de(o),I=(await t(r,f)).firestore();return{firestore:I,forms:I.collection(b).doc(E).collection("forms")}}return i.get("/forms",a.defineEventHandler(async r=>{O(r,j.view);try{const d=a.getQuery(r),c=pt(d.status,"status"),o=pt(d.search,"search");c&&!Pt.has(c)&&D("status must be draft, published, or archived."),o&&o.length>ft&&D(`search cannot exceed ${ft} characters.`);const{forms:u}=await n(r),l=c?await u.where("status","==",c).get():await u.get(),m=o?.toLocaleLowerCase();return l.docs.map(p=>re(p.id,ue(p.data()),s,!1)).filter(p=>m?[p.name,p.key,p.description].some(g=>String(g||"").toLocaleLowerCase().includes(m)):!0).sort((p,g)=>gt(g.updatedAt)-gt(p.updatedAt))}catch(d){oe("list",d,"Failed to list forms.")}})),i.get("/forms/:formId",a.defineEventHandler(async r=>{O(r,j.view);const d=de(a.getRouterParam(r,"formId"));try{const{forms:c}=await n(r),o=await c.doc(d).get();if(!o.exists)throw a.createError({statusCode:404,statusMessage:"Form not found."});return re(o.id,ue(o.data()),s)}catch(c){oe("get",c,"Failed to load form.")}})),i.post("/forms",a.defineEventHandler(async r=>{O(r,j.view),O(r,j.create);try{const d=$s(await a.readBody(r)),c=he(r),o=new Date().toISOString(),{firestore:u,forms:l}=await n(r),m=l.doc(d.key),p={name:d.name,key:d.key,description:d.description,status:"draft",draftDefinition:d.definition,schemaVersion:ze,revision:1,draftRevision:1,publishedRevision:null,publishedRevisionId:null,publishedDraftRevision:null,createdAt:o,createdBy:c,updatedAt:o,updatedBy:c,draftUpdatedAt:o,draftUpdatedBy:c,publishedAt:null,publishedBy:null,archivedAt:null,archivedBy:null,statusBeforeArchive:null};return await u.runTransaction(async g=>{(await g.get(m)).exists&&J(`A form with key "${d.key}" already exists.`),g.create(m,p)}),re(m.id,p,s)}catch(d){oe("create",d,"Failed to create form.")}})),i.patch("/forms/:formId/draft",a.defineEventHandler(async r=>{O(r,j.view),O(r,j.edit);const d=de(a.getRouterParam(r,"formId"));try{const c=Ls(await a.readBody(r)),o=he(r),u=new Date().toISOString(),{firestore:l,forms:m}=await n(r),p=m.doc(d);return await l.runTransaction(async g=>{const f=await g.get(p);if(!f.exists)throw a.createError({statusCode:404,statusMessage:"Form not found."});const h=ue(f.data());ve(h,c.expectedRevision),h.status==="archived"&&J("Restore this form before editing its draft."),h.key!==c.key&&J("A form key cannot be changed after the form is created.");const y={name:c.name,key:c.key,description:c.description,draftDefinition:c.definition,revision:h.revision+1,draftRevision:h.draftRevision+1,updatedAt:u,updatedBy:o,draftUpdatedAt:u,draftUpdatedBy:o};return g.update(p,y),re(d,{...h,...y},s)})}catch(c){oe("update draft",c,"Failed to update form draft.")}})),i.post("/forms/:formId/publish",a.defineEventHandler(async r=>{O(r,j.view),O(r,j.publish);const d=de(a.getRouterParam(r,"formId"));try{const{expectedRevision:c}=Fe(await a.readBody(r)),o=he(r),u=new Date().toISOString(),{firestore:l,forms:m}=await n(r),p=m.doc(d),g=p.collection("revisions").doc();return await l.runTransaction(async f=>{const h=await f.get(p);if(!h.exists)throw a.createError({statusCode:404,statusMessage:"Form not found."});const y=ue(h.data());ve(y,c),y.status==="archived"&&J("Restore this form before publishing it."),Vs(y.draftDefinition),y.publishedRevisionId&&y.publishedDraftRevision===y.draftRevision&&J("This form has no unpublished changes.");const w=y.revision+1,b=zs({formId:d,form:y,revision:w,revisionId:g.id,publishedAt:u,publishedBy:o}),E={status:"published",revision:w,publishedRevision:w,publishedRevisionId:g.id,publishedDraftRevision:y.draftRevision,publishedAt:u,publishedBy:o,updatedAt:u,updatedBy:o,archivedAt:null,archivedBy:null,statusBeforeArchive:null};return f.create(g,b),f.update(p,E),re(d,{...y,...E},s)})}catch(c){oe("publish",c,"Failed to publish form.")}})),i.post("/forms/:formId/archive",a.defineEventHandler(async r=>{O(r,j.view),O(r,j.archive);const d=de(a.getRouterParam(r,"formId"));try{const{expectedRevision:c}=Fe(await a.readBody(r)),o=he(r),u=new Date().toISOString(),{firestore:l,forms:m}=await n(r),p=m.doc(d);return await l.runTransaction(async g=>{const f=await g.get(p);if(!f.exists)throw a.createError({statusCode:404,statusMessage:"Form not found."});const h=ue(f.data());ve(h,c),h.status==="archived"&&J("Form is already archived.");const y={status:"archived",statusBeforeArchive:h.status,revision:h.revision+1,archivedAt:u,archivedBy:o,updatedAt:u,updatedBy:o};return g.update(p,y),re(d,{...h,...y},s)})}catch(c){oe("archive",c,"Failed to archive form.")}})),i.post("/forms/:formId/restore",a.defineEventHandler(async r=>{O(r,j.view),O(r,j.archive);const d=de(a.getRouterParam(r,"formId"));try{const{expectedRevision:c}=Fe(await a.readBody(r)),o=he(r),u=new Date().toISOString(),{firestore:l,forms:m}=await n(r),p=m.doc(d);return await l.runTransaction(async g=>{const f=await g.get(p);if(!f.exists)throw a.createError({statusCode:404,statusMessage:"Form not found."});const h=ue(f.data());ve(h,c),h.status!=="archived"&&J("Only archived forms can be restored.");const w={status:h.publishedRevisionId?"published":"draft",statusBeforeArchive:null,revision:h.revision+1,archivedAt:null,archivedBy:null,updatedAt:u,updatedBy:o};return g.update(p,w),re(d,{...h,...w},s)})}catch(c){oe("restore",c,"Failed to restore form.")}})),i.handler}const Ot=["authentication","billing","notifications","onboarding","system"],qt=["draft","active","inactive"];function Ws(e){const t=e.context?.module;if(!t?.supabase||!t.instanceId||!t.projectId||!t.userId||t.moduleType!=="mailTemplates")throw a.createError({statusCode:403,statusMessage:"Mail Templates module access is required."});return t}function le(e,t){const s=Ws(e),i=Array.isArray(t)?t:t?[t]:[];if(i.length&&!s.isAdmin&&!i.some(n=>s.permissions?.includes(n)))throw a.createError({statusCode:403,statusMessage:"You do not have permission for this template action."});return a.setHeader(e,"Cache-Control","private, no-store"),s}function Gs(e){const t=typeof e=="string"?e.trim():"";if(!t||t.length>512||/[\\\u0000-\u001f\u007f]/.test(t)||t.split("/").length%2!==1||t.split("/").some(s=>!s||s==="."||s===".."||/^__.*__$/.test(s)))throw a.createError({statusCode:400,statusMessage:"Configure a valid Firestore templates collection."});return t}function Ne(e){const t=a.getRouterParam(e,"templateId");if(!t||t.length>200||/[\/\\\u0000-\u001f\u007f]/.test(t)||t==="."||t===".."||/^__.*__$/.test(t))throw a.createError({statusCode:400,statusMessage:"Invalid template ID."});return t}function Se(e,t,s,i=!1){if(i&&e===void 0)return"";if(typeof e!="string"||e.length>s||!i&&!e.trim())throw a.createError({statusCode:400,statusMessage:`${t} is required and must be at most ${s} characters.`});return e.trim()}function Pe(e){if(!e||!Array.isArray(e.blocks)||e.blocks.length>200||JSON.stringify(e.blocks).length>4e5)throw a.createError({statusCode:400,statusMessage:"Provide up to 200 email blocks (400 KB maximum)."});if(!e.styles||typeof e.styles!="object"||Array.isArray(e.styles)||JSON.stringify(e.styles).length>2e4)throw a.createError({statusCode:400,statusMessage:"Provide valid email styles."});return{blocks:e.blocks,styles:e.styles,preheader:Se(e.preheader,"Preheader",300,!0)}}function Ys(e){if(e.preserveVariables===!0)return{};const t=e.variables&&typeof e.variables=="object"&&!Array.isArray(e.variables)?e.variables:{};if(JSON.stringify(t).length>5e4)throw a.createError({statusCode:400,statusMessage:"Preview variables are too large."});return{year:new Date().getFullYear().toString(),...Object.fromEntries(Object.entries(t).filter(([s,i])=>/^\w+$/.test(s)&&typeof i=="string"))}}function Js(e,t){return e.replace(/\{\{(\w+)\}\}/g,(s,i)=>Object.prototype.hasOwnProperty.call(t,i)?t[i]:s)}function ye(e){throw Number.isInteger(e?.statusCode)&&e.statusCode>=400&&e.statusCode<600?e:a.createError({statusCode:500,statusMessage:"The template request could not be completed. Check the Firebase integration and try again."})}function ke(e,t=!0){const s=e.data()||{};return{id:e.id,name:typeof s.name=="string"?s.name:"Untitled template",subject:typeof s.subject=="string"?s.subject:"",description:typeof s.description=="string"?s.description:"",preheader:typeof s.preheader=="string"?s.preheader:"",category:Ot.includes(s.category)?s.category:"notifications",status:qt.includes(s.status)?s.status:"draft",blocks:Array.isArray(s.blocks)?s.blocks:[],styles:s.styles&&typeof s.styles=="object"?s.styles:{},createdAt:typeof s.createdAt=="string"?s.createdAt:s.createdAt?.toDate?.().toISOString()||null,updatedAt:typeof s.updatedAt=="string"?s.updatedAt:s.updatedAt?.toDate?.().toISOString()||null,...t?{mjml:typeof s.mjml=="string"?s.mjml:"",html:typeof s.html=="string"?s.html:""}:{}}}function Ks(e){const t=a.createRouter();async function s(r,d){const{data:c,error:o}=await d.supabase.from("project_modules").select("type, project_id, config").eq("id",d.instanceId).maybeSingle();if(o||c?.type!=="mailTemplates"||c.project_id!==d.projectId)throw a.createError({statusCode:404,statusMessage:"Mail Templates module is unavailable."});const u=c.config?.project;if(typeof u!="string"||!u)throw a.createError({statusCode:400,statusMessage:"Choose a Firebase integration in the module settings."});const l=Gs(c.config?.templateCollection||"mail-templates"),{data:m,error:p}=await d.supabase.from("integrations").select("id, type").eq("id",u).eq("project_id",d.projectId).maybeSingle();if(p)throw a.createError({statusCode:500,statusMessage:"Could not verify the Firebase integration."});if(!m||m.type!=="FIREBASE")throw a.createError({statusCode:400,statusMessage:"The Firebase integration is unavailable for this project."});const f=(await e.initFirebase(r,m.id)).firestore();return{db:f,collection:f.collection(l)}}function i(r,d={}){try{const c=e.mailTemplates.render({...Pe(r),variables:d});if(c.errors.length)throw a.createError({statusCode:400,statusMessage:"Fix the MJML validation errors before saving.",data:{errors:c.errors}});return c}catch(c){throw c.statusCode?c:a.createError({statusCode:400,statusMessage:"The email contains invalid blocks or styles."})}}function n(r){const d=Se(r?.name,"Template name",120),c=Se(r?.subject,"Subject",300),o=Se(r?.description,"Description",1e3,!0),u=r.category??"notifications",l=r.status??"draft";if(!Ot.includes(u)||!qt.includes(l))throw a.createError({statusCode:400,statusMessage:"Invalid template category or status."});const m=i(r),p={name:d,subject:c,description:o,category:u,status:l,...Pe(r),html:m.html,mjml:m.mjml};if(new TextEncoder().encode(JSON.stringify(p)).length>9e5)throw a.createError({statusCode:400,statusMessage:"This compiled template exceeds the storage limit. Reduce the content."});return p}return t.get("/templates",a.defineEventHandler(async r=>{const d=le(r);try{const{collection:c}=await s(r,d),o=await c.limit(101).get();if(o.docs.length>100)throw a.createError({statusCode:400,statusMessage:"This collection contains more than 100 templates. Configure a dedicated templates collection."});return o.docs.map(u=>ke(u,!1)).sort((u,l)=>(l.updatedAt||"").localeCompare(u.updatedAt||"")||u.name.localeCompare(l.name))}catch(c){ye(c)}})),t.get("/templates/:templateId",a.defineEventHandler(async r=>{const d=le(r),c=Ne(r);try{const{collection:o}=await s(r,d),u=await o.doc(c).get();if(!u.exists)throw a.createError({statusCode:404,statusMessage:"Template not found."});return ke(u)}catch(o){ye(o)}})),t.post("/templates",a.defineEventHandler(async r=>{const d=le(r,"create-template"),c=n(await a.readBody(r));try{const{collection:o}=await s(r,d),u=o.doc(),l=new Date().toISOString(),m={...c,createdAt:l,updatedAt:l};return await u.create(m),{id:u.id,...m}}catch(o){ye(o)}})),t.patch("/templates/:templateId",a.defineEventHandler(async r=>{const d=le(r,"edit-template"),c=Ne(r),o=await a.readBody(r),u=n(o);try{const{db:l,collection:m}=await s(r,d),p=m.doc(c);return await l.runTransaction(async g=>{const f=await g.get(p);if(!f.exists)throw a.createError({statusCode:404,statusMessage:"Template not found."});const h=ke(f);if(o.updatedAt!==void 0&&o.updatedAt!==h.updatedAt)throw a.createError({statusCode:409,statusMessage:"This template was updated elsewhere. Reload it before saving."});const y=Math.max(Date.now(),(Date.parse(h.updatedAt||"")||0)+1),w={...u,updatedAt:new Date(y).toISOString()};return g.update(p,w),{...h,...w}})}catch(l){ye(l)}})),t.delete("/templates/:templateId",a.defineEventHandler(async r=>{const d=le(r,"delete-template"),c=Ne(r);try{const{db:o,collection:u}=await s(r,d),l=u.doc(c);return await o.runTransaction(async m=>{if(!(await m.get(l)).exists)throw a.createError({statusCode:404,statusMessage:"Template not found."});m.delete(l)}),{success:!0}}catch(o){ye(o)}})),t.post("/render",a.defineEventHandler(async r=>{le(r);const d=await a.readBody(r),c=Pe(d),o=Ys(d);try{return{...e.mailTemplates.render({...c,variables:o}),subject:Js(typeof d.subject=="string"?d.subject:"",o)}}catch{throw a.createError({statusCode:400,statusMessage:"The email contains invalid blocks or styles."})}})),t.handler}const Zs=new Set(["FIREBASE","FTP","SFTP","S3","CLOUDFLARE_R2"]);function Xs(e){const t=e?.context?.module;if(!t?.supabase||!t?.instanceId||!t?.projectId||t.moduleType!=="filezone")throw a.createError({statusCode:500,statusMessage:"File Zone context is unavailable."});return t}function Ue(e,t=!1){const s=Xs(e);if(t?!s.isAdmin:!s.isAdmin&&!s.permissions.includes("view-files"))throw a.createError({statusCode:403,statusMessage:t?"Only Studio administrators can upload files.":"You cannot view files in this module."});return s}function we(e,t=!1){if(e==null||e===""){if(t)return"";throw a.createError({statusCode:400,statusMessage:"A file path is required."})}if(typeof e!="string"||e.length>1024||e.startsWith("/")||e.endsWith("/")||/[\\\u0000-\u001f\u007f]/.test(e)||e.split("/").some(s=>!s||s==="."||s===".."))throw a.createError({statusCode:400,statusMessage:"Invalid storage path."});return e}function ht(e){const t=e.replace(/\\/g,"/").split("/").pop()?.trim()||"";if(!t||t.length>180||/[\u0000-\u001f\u007f]/.test(t)||t==="."||t==="..")throw a.createError({statusCode:400,statusMessage:"File name is invalid or too long."});return t}function Te(...e){return e.filter(Boolean).join("/")}function yt(e){return e.replace(/^[a-f0-9]{8}-[a-f0-9]{4}-[1-8][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}-/,"")}function wt(e){const t=e.split(".").pop()?.toLowerCase()||"";return{pdf:"application/pdf",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",txt:"text/plain",csv:"text/csv"}[t]||"application/octet-stream"}function je(e,t){throw Number.isInteger(e?.statusCode)&&e.statusCode>=400&&e.statusCode<600?e:(console.error("File Zone "+t+" failed:",e),a.createError({statusCode:500,statusMessage:"Could not "+t+" file."}))}function ea({filezone:e}){const t=a.createRouter();async function s(i){const{data:n,error:r}=await i.supabase.from("project_modules").select("type, project_id, config").eq("id",i.instanceId).maybeSingle();if(r||n?.type!=="filezone"||n.project_id!==i.projectId)throw a.createError({statusCode:404,statusMessage:"File Zone module is unavailable."});const d=n.config?.storageIntegration;if(typeof d!="string"||!/^[a-f0-9-]{36}$/i.test(d))throw a.createError({statusCode:400,statusMessage:"File Zone has no valid storage integration."});const{data:c,error:o}=await i.supabase.from("integrations").select("id, type, config").eq("id",d).eq("project_id",i.projectId).maybeSingle();if(o)throw a.createError({statusCode:500,statusMessage:"Could not check storage integration."});if(!c||!Zs.has(c.type))throw a.createError({statusCode:400,statusMessage:"The storage integration is unavailable for this project."});return{provider:c,root:we(n.config?.folderPath,!0)}}return t.get("/files",a.defineEventHandler(async i=>{const n=Ue(i),r=a.getQuery(i),d=we(r.folder,!0),c=r.cursor===void 0?null:typeof r.cursor=="string"&&r.cursor.length<=4096?r.cursor:null;if(r.cursor!==void 0&&c===null)throw a.createError({statusCode:400,statusMessage:"Invalid file page."});try{const{provider:o,root:u}=await s(n),{objects:l,nextCursor:m}=await e.list(i,o,Te(u,d),c),p=l.flatMap(g=>{const f=u?g.key.startsWith(u+"/")?g.key.slice(u.length+1):"":g.key;if(!f||f.split("/").slice(0,-1).join("/")!==d)return[];try{we(f)}catch{return[]}const h=yt(g.name);return[{path:f,name:h,directory:g.directory,size_bytes:g.size,content_type:g.directory?null:g.contentType||wt(h),modified_at:g.modified}]});return a.setHeader(i,"Cache-Control","private, no-store"),{files:p,nextCursor:m}}catch(o){je(o,"list")}})),t.post("/files",a.defineEventHandler(async i=>{const n=Ue(i,!0),r=we(a.getQuery(i).folder,!0),{provider:d,root:c}=await s(n);let o;try{o=await e.parseUpload(i);const u=ht(o.name);if(!Number.isInteger(o.size)||o.size<1||o.size>e.maxBytes)throw a.createError({statusCode:413,statusMessage:"File must be between 1 byte and 25 MB."});const l=globalThis.crypto.randomUUID()+"-"+u,m=Te(c,r,l),p=o.contentType&&/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/i.test(o.contentType)?o.contentType.toLowerCase():"application/octet-stream";try{await e.upload(i,d,m,o.localPath,p)}catch(g){throw await e.remove(i,d,m).catch(f=>console.error("File Zone partial upload cleanup failed:",f)),g}return a.setHeader(i,"Cache-Control","private, no-store"),{path:Te(r,l),name:u,directory:!1,size_bytes:o.size,content_type:p,modified_at:new Date().toISOString()}}catch(u){je(u,"upload")}finally{await o?.cleanup()}})),t.get("/files/content",a.defineEventHandler(async i=>{const n=Ue(i),r=a.getQuery(i),d=we(r.path),c=yt(ht(d.split("/").pop())),{provider:o,root:u}=await s(n);try{const l=await e.read(i,o,Te(u,d)),m=wt(c),p=r.view==="1"&&/^(application\/pdf|image\/(jpeg|png|gif|webp))$/.test(m),g=c.replace(/[^\x20-\x7e]|["\\]/g,"_");return a.setHeader(i,"Content-Type",p?m:"application/octet-stream"),a.setHeader(i,"Content-Disposition",(p?"inline":"attachment")+'; filename="'+g+`"; filename*=UTF-8''`+encodeURIComponent(c)),a.setHeader(i,"X-Content-Type-Options","nosniff"),a.setHeader(i,"Cache-Control","private, no-store"),a.setHeader(i,"Content-Security-Policy","sandbox"),l}catch(l){je(l,"read")}})),t.handler}const ta="https://graph.facebook.com/v25.0/";class R extends Error{constructor(t,s,i=null){super(s),this.statusCode=t,this.code=i,this.name="WhatsAppProviderError"}statusCode;code}function Ht(e=fetch){return async(t,s,i={})=>{if(!/^\d{1,30}(?:\/(?:phone_numbers|message_templates|template_analytics|whatsapp_business_profile))?$/.test(s))throw new R(500,"Invalid WhatsApp API operation.");const n=new URL(s,ta);for(const[c,o]of Object.entries(i.query||{}))o!==void 0&&c!=="access_token"&&n.searchParams.set(c,String(o));const r=new AbortController,d=setTimeout(()=>r.abort(),Math.max(1,Math.min(15e3,i.timeoutMs||15e3)));try{const c=await e(n.toString(),{method:i.method||"GET",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},...i.body?{body:JSON.stringify(i.body)}:{},signal:r.signal,redirect:"error"});if(Number(c.headers.get("content-length"))>4e6)throw new R(502,"Meta returned too much data. Narrow the request.");const o=c.body?.getReader(),u=[];let l=0;if(o)for(;;){const f=await o.read();if(f.done)break;if(l+=f.value.byteLength,l>4e6)throw await o.cancel(),new R(502,"Meta returned too much data. Narrow the request.");u.push(f.value)}const m=new Uint8Array(l);let p=0;for(const f of u)m.set(f,p),p+=f.byteLength;let g;try{g=JSON.parse(new TextDecoder().decode(m))}catch{throw new R(502,"Meta returned an unreadable response.")}if(!c.ok||g?.error){const f=Number.isSafeInteger(g?.error?.code)?g.error.code:null;throw f===190||c.status===401?new R(424,"The WhatsApp access token is invalid or expired. Reconnect it in Studio project integrations.",f):[10,200,294].includes(f)||c.status===403?new R(424,"Meta denied this operation. Check the token permissions and access to this WhatsApp account.",f):[4,17,32,613,80007,130429].includes(f)||c.status===429?new R(429,"Meta is limiting requests. Wait before trying again.",f):new R(c.status>=500?502:422,`Meta could not complete this request${f===null?"":` (code ${f})`}. Check the account and template requirements.`,f)}if(!g||typeof g!="object"||Array.isArray(g))throw new R(502,"Meta returned an unexpected response.");return g}catch(c){throw c instanceof R?c:new R(r.signal.aborted?504:502,r.signal.aborted?"The WhatsApp request timed out. Refresh before retrying a change.":"Could not reach the WhatsApp API.")}finally{clearTimeout(d)}}}function He(e,t,s){const i=Date.now()+s;let n=0;return async(r,d,c={})=>{const o=i-Date.now();if(++n>t||o<=0)throw new R(504,"The WhatsApp data request reached its time or page limit. Try a shorter period.");return e(r,d,{...c,timeoutMs:Math.min(15e3,o)})}}function ne(e){throw e instanceof R?a.createError({statusCode:e.statusCode,statusMessage:e.message}):a.createError({statusCode:502,statusMessage:"Could not complete the WhatsApp request."})}function v(e,t=2048){return typeof e=="string"&&e.length?e.slice(0,t):null}function be(e){const t=e.paging?.cursors?.after;return e.paging?.next&&typeof t=="string"&&t.length<=2048&&!/[\r\n]/.test(t)?t:null}function Ee(e){if(!Array.isArray(e.data)||e.data.length>1e4)throw new R(502,"Meta returned an unexpected response.");return e.data.filter(t=>t!==null&&typeof t=="object"&&!Array.isArray(t))}const Qe=e=>!/[{}]/.test(e),Oe=C.stringType().trim().min(1).max(25).refine(Qe,"Button labels cannot contain variables."),sa=C.discriminatedUnionType("type",[C.objectType({type:C.literalType("QUICK_REPLY"),text:Oe}).strict(),C.objectType({type:C.literalType("URL"),text:Oe,url:C.stringType().trim().url().max(2e3).refine(e=>/^https:\/\//i.test(e)&&Qe(e),"Use a static HTTPS URL without variables.")}).strict(),C.objectType({type:C.literalType("PHONE_NUMBER"),text:Oe,phoneNumber:C.stringType().regex(/^\+[1-9]\d{1,14}$/,"Use an international phone number starting with +.")}).strict()]),Bt=C.objectType({name:C.stringType().min(1).max(512).regex(/^[a-z0-9_]+$/,"Template names use lowercase letters, numbers and underscores."),language:C.stringType().regex(/^[a-z]{2,3}(?:_[A-Z]{2})?$/,"Use a WhatsApp language code, for example en_US or nl."),category:C.enumType(["UTILITY","MARKETING"]),headerText:C.stringType().max(60).optional(),headerExample:C.stringType().trim().max(1024).optional(),bodyText:C.stringType().min(1).max(1024).refine(e=>e.trim().length>0,"Body text is required."),bodyExamples:C.arrayType(C.stringType().trim().max(1024)).max(20).optional(),footerText:C.stringType().max(60).refine(Qe,"Footer text cannot contain variables.").optional(),buttons:C.arrayType(sa).max(3).optional()}).strict();function ee(e){const t=[...e.matchAll(/\{\{([1-9]\d*)\}\}/g)].map(n=>n[1]),s=e.replace(/\{\{[1-9]\d*\}\}/g,"");if(/[{}]/.test(s))throw new Error("Use numbered variables such as {{1}} and {{2}}.");const i=[...new Set(t)].sort((n,r)=>Number(n)-Number(r));if(i.some((n,r)=>Number(n)!==r+1))throw new Error("Number variables consecutively starting at {{1}}.");return i}function $t(e,t){const s=ee(e.headerText||""),i=ee(e.bodyText);if(s.length>1)throw new Error("A text header supports one numbered variable.");if(i.length>20)throw new Error("This editor supports up to 20 body variables.");if(t&&s.length&&!e.headerExample?.trim())throw new Error("Provide an example for the header variable.");if(t&&(e.bodyExamples?.length||0)!==i.length)throw new Error("Provide exactly one example for each body variable.");if(t&&e.bodyExamples?.some(d=>!d.trim()))throw new Error("Variable examples cannot be empty.");const n=e.buttons||[],r=n.filter(d=>d.type==="QUICK_REPLY").length;if(r&&r!==n.length)throw new Error("Use either quick replies or action buttons in this editor.");if(n.filter(d=>d.type==="URL").length>1||n.filter(d=>d.type==="PHONE_NUMBER").length>1)throw new Error("Use at most one website and one phone button.")}function bt(e){const t=Bt.safeParse(e);if(!t.success)throw a.createError({statusCode:400,statusMessage:t.error.issues[0]?.message||"Invalid template."});try{$t(t.data,!0)}catch(s){throw a.createError({statusCode:400,statusMessage:s.message})}return t.data}function Et(e){const t=[];return e.headerText&&t.push({type:"HEADER",format:"TEXT",text:e.headerText,...ee(e.headerText).length?{example:{header_text:[e.headerExample]}}:{}}),t.push({type:"BODY",text:e.bodyText,...ee(e.bodyText).length?{example:{body_text:[e.bodyExamples]}}:{}}),e.footerText&&t.push({type:"FOOTER",text:e.footerText}),e.buttons?.length&&t.push({type:"BUTTONS",buttons:e.buttons.map(s=>s.type==="PHONE_NUMBER"?{type:s.type,text:s.text,phone_number:s.phoneNumber}:{...s})}),t}const Me=(e,t)=>Object.keys(e).every(s=>t.includes(s));function aa(e){if(!["UTILITY","MARKETING"].includes(e.category)||e.parameter_format&&e.parameter_format!=="POSITIONAL"||e.source==="AUTO_GENERATED"||!Array.isArray(e.components))return null;const t={name:e.name,language:e.language,category:e.category,bodyText:""},s=new Set;for(const n of e.components){if(!n||typeof n!="object"||s.has(n.type)||(s.add(n.type),!Me(n,["type","format","text","example","buttons"])))return null;if(n.type==="HEADER"){if(n.format!=="TEXT"||typeof n.text!="string"||n.buttons||n.example&&!Me(n.example,["header_text"]))return null;t.headerText=n.text,t.headerExample=n.example?.header_text?.[0]||""}else if(n.type==="BODY"){if(typeof n.text!="string"||n.format||n.buttons||n.example&&!Me(n.example,["body_text"]))return null;t.bodyText=n.text,t.bodyExamples=n.example?.body_text?.[0]||[]}else if(n.type==="FOOTER"){if(typeof n.text!="string"||n.format||n.buttons||n.example)return null;t.footerText=n.text}else if(n.type==="BUTTONS"){if(!Array.isArray(n.buttons)||n.format||n.text||n.example)return null;t.buttons=[];for(const r of n.buttons){if(!r||typeof r!="object"||!Me(r,["type","text","url","phone_number"]))return null;if(r.type==="QUICK_REPLY"&&!r.url&&!r.phone_number)t.buttons.push({type:"QUICK_REPLY",text:r.text});else if(r.type==="URL"&&!r.phone_number)t.buttons.push({type:"URL",text:r.text,url:r.url});else if(r.type==="PHONE_NUMBER"&&!r.url)t.buttons.push({type:"PHONE_NUMBER",text:r.text,phoneNumber:r.phone_number});else return null}}else return null}if(!s.has("BODY"))return null;const i=Bt.safeParse(t);if(!i.success)return null;try{$t(i.data,!1)}catch{return null}return i.data}function Ct(e){const t=aa(e),s=["APPROVED","REJECTED","PAUSED"].includes(e.status),i=(Array.isArray(e.components)?e.components:[]).slice(0,20).map(n=>({type:v(n?.type,64)||"UNKNOWN",...v(n?.format,64)?{format:n.format}:{},...v(n?.text,32768)?{text:n.text.slice(0,32768)}:{},...Array.isArray(n?.buttons)?{buttons:n.buttons.slice(0,20).map(r=>({type:v(r?.type,64)||"UNKNOWN",...v(r?.text,1024)?{text:r.text.slice(0,1024)}:{},...v(r?.url,2048)?{url:r.url.slice(0,2048)}:{},...v(r?.phone_number,64)?{phoneNumber:r.phone_number.slice(0,64)}:{}}))}:{}}));return{id:v(e.id,30)||"",name:v(e.name,512)||"",language:v(e.language,20)||"",category:v(e.category,64)||"UNKNOWN",status:v(e.status,64)||"UNKNOWN",quality:v(e.quality_score?.score,64),rejectionReason:v(e.rejected_reason),components:i,editable:t!==null&&s,editReason:t?s?null:"Meta permits editing only approved, rejected or paused templates.":"This template uses a format this editor cannot safely change. Manage it in WhatsApp Manager.",draft:t}}function ra(e,t){if(!e.editable||!e.draft)throw a.createError({statusCode:409,statusMessage:e.editReason||"This template cannot be edited here."});if(t.name!==e.name||t.language!==e.language||t.category!==e.category)throw a.createError({statusCode:400,statusMessage:"Name, language and category must remain unchanged. Create a new template to change them."});const s=e.draft;if(JSON.stringify(ee(s.bodyText))!==JSON.stringify(ee(t.bodyText))||JSON.stringify(ee(s.headerText||""))!==JSON.stringify(ee(t.headerText||"")))throw a.createError({statusCode:400,statusMessage:"Create a new template to change variables used by existing automations."});if(JSON.stringify((s.buttons||[]).map(i=>i.type))!==JSON.stringify((t.buttons||[]).map(i=>i.type)))throw a.createError({statusCode:400,statusMessage:"Create a new template to change button count, types or order used by existing automations."})}const ie=e=>typeof e=="number"&&Number.isFinite(e)&&e>=0?e:null,K=e=>e.length&&e.every(t=>t!==null)?e.reduce((t,s)=>t+s,0):null;function oa(e){if(!e.analytics||!Array.isArray(e.analytics.data_points))return{state:"unavailable",reason:"Meta did not provide message analytics for this account and period.",data:null};const t=e.analytics.data_points.filter(s=>Number.isSafeInteger(s?.start)&&Number.isSafeInteger(s?.end)).map(s=>({start:s.start,end:s.end,sent:ie(s.sent),delivered:ie(s.delivered)}));return t.length!==e.analytics.data_points.length?{state:"unavailable",reason:"Meta returned incomplete message analytics.",data:null}:{state:t.length?"available":"empty",reason:t.length?null:"Meta returned no message data for this period.",data:{sent:K(t.map(s=>s.sent)),delivered:K(t.map(s=>s.delivered)),points:t}}}function na(e,t,s,i){if(e.some(o=>!Array.isArray(o.data_points)))return{state:"unavailable",reason:"Meta returned an unsupported template analytics response.",data:null};const n=new Map(t.map(o=>[o.id,o])),r=new Map,d=new Set;for(const o of e)for(const u of o.data_points){if(!u||!n.has(u.template_id)||!Number.isSafeInteger(u.start)||!Number.isSafeInteger(u.end))continue;const l=`${u.template_id}:${u.start}:${u.end}`;if(d.has(l))continue;d.add(l);const m=r.get(u.template_id)||[];m.push(u),r.set(u.template_id,m)}const c=t.map(o=>{const u=r.get(o.id)||[];return{id:o.id,name:v(o.name,512)||o.id,language:v(o.language,20)||"",sent:K(u.map(l=>ie(l.sent))),delivered:K(u.map(l=>ie(l.delivered))),read:K(u.map(l=>ie(l.read))),clicked:K(u.map(l=>Array.isArray(l.clicked)?l.clicked.length?K(l.clicked.map(m=>ie(m?.count))):0:null)),amountSpent:K(u.map(l=>Array.isArray(l.cost)?ie(l.cost.find(m=>m?.type==="amount_spent")?.value):null))}});return{state:d.size?"available":"empty",reason:i?"Only part of the account could be loaded. Metrics shown cover completed template queries only.":d.size?null:"Meta returned no template metrics for this period. Missing values are not zero.",data:{templates:c,currency:s,truncated:i}}}const Ie=C.stringType().regex(/^\d{1,30}$/,"Use the numeric ID from Meta."),Lt=C.stringType().trim().min(10,"Enter a valid access token.").max(16384).regex(/^\S+$/,"The token cannot contain whitespace."),We=C.objectType({wabaId:Ie,accessToken:Lt}).strict(),ia=We.extend({phoneNumberId:Ie}),ca=We.extend({phoneNumberId:Ie.optional()}),zt="id,display_phone_number,verified_name,quality_rating,code_verification_status";function Vt(e){return{id:v(e.id,30)||"",displayPhoneNumber:v(e.display_phone_number,64),verifiedName:v(e.verified_name,256),qualityRating:v(e.quality_rating,64),verificationStatus:v(e.code_verification_status,64)}}async function da(e,t=Ht()){const s=ca.safeParse(e);if(!s.success)throw new R(400,s.error.issues[0]?.message||"Invalid WhatsApp connection.");const{wabaId:i,accessToken:n,phoneNumberId:r}=s.data,d=5,c=He(t,d+1,3e4),o=await c(n,i,{query:{fields:"id,name"}});if(o.id!==i)throw new R(422,"Meta did not return the requested WhatsApp Business account.");const u=[];let l=null;const m=new Set;for(let p=0;p<d;p++){const g=await c(n,`${i}/phone_numbers`,{query:{fields:zt,limit:100,after:l||void 0}});for(const f of Ee(g))Ie.safeParse(f.id).success&&!u.some(h=>h.id===f.id)&&u.push(Vt(f));if(l=be(g),!l)break;if(m.has(l))throw new R(502,"Meta returned a repeated page. Try again later.");m.add(l)}if(r&&!u.some(p=>p.id===r))throw new R(400,l?"The selected number could not be verified within this account. Use a token with access to the correct number.":"The selected phone number does not belong to this WhatsApp Business account.");return{wabaId:i,wabaName:v(o.name,256),phones:u,truncated:l!==null}}const ua={verify:da,testSchema:We,connectSchema:ia,toHttpError:ne},At="whatsappBusiness",It="id,name,language,category,status,quality_score,rejected_reason,components,parameter_format,source",vt=20,ge=Ie,la=C.objectType({wabaId:ge,phoneNumberId:ge,displayPhoneNumber:C.stringType().max(64).nullish().transform(e=>e||""),verifiedName:C.stringType().max(256).nullish().transform(e=>e||""),accessToken:C.stringType().min(1).max(32768)}),ma=C.stringType().min(1).max(2048).refine(e=>!/[\r\n]/.test(e)),Tt=C.objectType({search:C.stringType().trim().min(1).max(200).optional(),name:C.stringType().min(1).max(512).regex(/^[a-z0-9_]+$/).optional(),after:ma.optional(),status:C.enumType(["APPROVED","ARCHIVED","DELETED","DISABLED","IN_APPEAL","LIMIT_EXCEEDED","PAUSED","PENDING","PENDING_DELETION","REJECTED"]).optional()}).strict(),fa=e=>a.createError({statusCode:400,statusMessage:e});function me(e,t){const s=e.safeParse(t);if(!s.success)throw fa(s.error.issues[0]?.message||"Invalid request.");return s.data}function De(e){return{view:e.isAdmin||e.permissions.includes("view-whatsapp"),manageTemplates:e.isAdmin||e.permissions.includes("manage-whatsapp-templates"),manageInsights:e.isAdmin||e.permissions.includes("manage-whatsapp-insights")}}function pa(e,t){return{connected:t!==null,canManageConnection:e.isAdmin,permissions:De(e),wabaId:t?.wabaId||null,phoneNumberId:t?.phoneNumberId||null,displayPhoneNumber:t?.displayPhoneNumber||null,verifiedName:t?.verifiedName||null,updatedAt:null}}function fe(e){return{state:"unavailable",reason:e instanceof R?e.message:"Meta did not make this information available.",data:null}}function ga(e){const t=e.graphRequest||Ht(),s=a.createRouter();async function i(c,o="view"){a.setResponseHeader(c,"Cache-Control","private, no-store");const u=c.context.module;if(!u?.supabase||!u.instanceId||!u.projectId||u.moduleType!==At||!u.userId||!Array.isArray(u.permissions))throw a.createError({statusCode:403,statusMessage:"WhatsApp module access is unavailable."});if(!De(u).view||!De(u)[o])throw a.createError({statusCode:403,statusMessage:"You do not have permission for this WhatsApp action."});const{data:l,error:m}=await u.supabase.from("project_modules").select("id, config").eq("id",u.instanceId).eq("project_id",u.projectId).eq("type",At).maybeSingle();if(m)throw a.createError({statusCode:503,statusMessage:"Could not verify the WhatsApp module."});if(!l)throw a.createError({statusCode:404,statusMessage:"WhatsApp module not found in this project."});const p=l.config?.account;if(p&&!C.stringType().uuid().safeParse(p).success)throw a.createError({statusCode:424,statusMessage:"Select a WhatsApp integration from this project in the module settings."});return{...u,accountId:p||void 0}}async function n(c){if(!c.accountId)return null;const{data:o,error:u}=await c.supabase.from("integrations").select("id, config").eq("id",c.accountId).eq("project_id",c.projectId).eq("type","WHATSAPP").maybeSingle();if(u)throw a.createError({statusCode:503,statusMessage:"Could not load the selected WhatsApp integration."});if(!o)throw a.createError({statusCode:424,statusMessage:"The selected WhatsApp integration is unavailable in this project. Update the module settings."});const l=la.safeParse(o.config);if(!l.success)throw a.createError({statusCode:424,statusMessage:"The selected WhatsApp integration is incomplete. Reconnect it in project integrations."});return l.data}async function r(c){const o=await n(c);if(!o)throw a.createError({statusCode:424,statusMessage:"Select a WhatsApp integration in the module settings first."});let u;try{if(u=e.decrypt(o.accessToken),!Lt.safeParse(u).success)throw new Error("Invalid stored token")}catch{throw a.createError({statusCode:503,statusMessage:"The saved WhatsApp credential could not be read. Ask an administrator to reconnect it."})}return{row:o,token:u}}async function d(c,o,u,l){const m=He(t,vt,3e4);let p=null;const g=new Set;for(let f=0;f<vt;f++){const h=await m(c,`${o}/message_templates`,{query:{fields:It,limit:100,name:l,after:p||void 0}}),y=Ee(h).find(w=>w.id===u);if(y)return Ct(y);if(p=be(h),!p)throw a.createError({statusCode:404,statusMessage:"Template not found in the connected WhatsApp account."});if(g.has(p))throw new R(502,"Meta returned a repeated page. Try again later.");g.add(p)}throw a.createError({statusCode:409,statusMessage:"This account has too many templates to search at once. Open the template from an exact-name search."})}return s.get("/connection",a.defineEventHandler(async c=>{const o=await i(c);return pa(o,await n(o))})),s.get("/templates",a.defineEventHandler(async c=>{const o=await i(c),{row:u,token:l}=await r(o),m=me(Tt,a.getQuery(c));try{const p=await t(l,`${u.wabaId}/message_templates`,{query:{fields:It,limit:50,name:m.name,name_or_content:m.search,status:m.status?JSON.stringify([m.status]):void 0,after:m.after}});return{templates:Ee(p).filter(g=>!m.name||g.name===m.name).map(Ct),nextCursor:be(p)}}catch(p){ne(p)}})),s.get("/templates/:id",a.defineEventHandler(async c=>{const o=await i(c),{row:u,token:l}=await r(o),m=me(ge,a.getRouterParam(c,"id")),p=me(C.objectType({name:Tt.shape.name}).strict(),a.getQuery(c));try{return await d(l,u.wabaId,m,p.name)}catch(g){throw g instanceof R&&ne(g),g}})),s.post("/templates",a.defineEventHandler(async c=>{const o=await i(c,"manageTemplates"),{row:u,token:l}=await r(o),m=bt(await a.readBody(c));try{const p=await t(l,`${u.wabaId}/message_templates`,{method:"POST",body:{name:m.name,language:m.language,category:m.category,parameter_format:"POSITIONAL",components:Et(m)}});if(!ge.safeParse(p.id).success)throw new R(502,"Meta did not confirm the template ID. Refresh the template list before trying again.");return{id:p.id,status:v(p.status,64),category:v(p.category,64),success:!0}}catch(p){ne(p)}})),s.patch("/templates/:id",a.defineEventHandler(async c=>{const o=await i(c,"manageTemplates"),{row:u,token:l}=await r(o),m=me(ge,a.getRouterParam(c,"id")),p=bt(await a.readBody(c));let g;try{g=await d(l,u.wabaId,m,p.name)}catch(f){throw f instanceof R&&ne(f),f}ra(g,p);try{if((await t(l,m,{method:"POST",body:{components:Et(p)}})).success!==!0)throw new R(502,"Meta did not confirm the change. Refresh the template before trying again.");return{id:m,status:null,category:g.category,success:!0}}catch(f){ne(f)}})),s.get("/overview",a.defineEventHandler(async c=>{const o=await i(c),{row:u,token:l}=await r(o),[m,p,g,f]=await Promise.allSettled([t(l,u.wabaId,{query:{fields:"id,name,status,currency,business_verification_status,account_review_status"}}),t(l,u.phoneNumberId,{query:{fields:zt}}),t(l,u.phoneNumberId,{query:{fields:"health_status"}}),t(l,`${u.phoneNumberId}/whatsapp_business_profile`,{query:{fields:"about,address,description,email,vertical,websites"}})]),h=f.status==="fulfilled"&&Array.isArray(f.value.data)?f.value.data[0]:null;return{account:m.status==="fulfilled"?{state:"available",reason:null,data:{id:u.wabaId,name:v(m.value.name,256),status:v(m.value.status,64),currency:v(m.value.currency,10),verificationStatus:v(m.value.business_verification_status,64),reviewStatus:v(m.value.account_review_status,64)}}:fe(m.reason),phone:p.status==="fulfilled"?{state:"available",reason:null,data:Vt(p.value)}:fe(p.reason),health:g.status==="fulfilled"&&g.value.health_status?{state:"available",reason:null,data:{canSendMessage:v(g.value.health_status.can_send_message,64),entities:(Array.isArray(g.value.health_status.entities)?g.value.health_status.entities:[]).slice(0,100).map(y=>({id:v(y.id,30),type:v(y.entity_type,64),canSendMessage:v(y.can_send_message,64)}))}}:fe(g.status==="rejected"?g.reason:null),profile:h?{state:"available",reason:null,data:{about:v(h.about),address:v(h.address),description:v(h.description),email:v(h.email,254),vertical:v(h.vertical,64),websites:(Array.isArray(h.websites)?h.websites:[]).filter(y=>typeof y=="string"&&/^https?:\/\//i.test(y)).slice(0,10).map(y=>y.slice(0,2048))}}:fe(f.status==="rejected"?f.reason:null)}})),s.get("/insights",a.defineEventHandler(async c=>{const o=He(t,18,2e4),u=await i(c),{row:l,token:m}=await r(u),p=me(C.objectType({days:C.enumType(["7","30","90"]).optional()}).strict(),a.getQuery(c)),g=Number(p.days||30),f=e.now?.()||new Date,h=Math.floor(Date.UTC(f.getUTCFullYear(),f.getUTCMonth(),f.getUTCDate())/1e3),y=h-g*86400,w=l.displayPhoneNumber.replace(/\D/g,""),[b,E]=await Promise.allSettled([o(m,l.wabaId,{query:{fields:"id,is_enabled_for_insights,currency"}}),/^\d{6,15}$/.test(w)?o(m,l.wabaId,{query:{fields:`analytics.start(${y}).end(${h}).granularity(DAY).phone_numbers(${JSON.stringify([w])})`}}):Promise.reject(new R(424,"The connected phone number is unavailable. Reconnect it to load message analytics."))]),A=b.status==="fulfilled"&&typeof b.value.is_enabled_for_insights=="boolean"?b.value.is_enabled_for_insights:null,I=b.status==="fulfilled"?v(b.value.currency,10):null;let T;if(A===!1)T={state:"unavailable",reason:"Template insights have not been enabled for this WhatsApp account.",data:null};else try{const S=await o(m,`${l.wabaId}/message_templates`,{query:{fields:"id,name,language",limit:50}}),M=Ee(S).filter(_=>ge.safeParse(_.id).success).slice(0,50),x=[];let q=be(S)!==null;const F=[],z=Array.from({length:Math.ceil(M.length/10)},(_,se)=>M.slice(se*10,se*10+10)),V=await Promise.allSettled(z.map(async _=>{const se=[];let Q=null;const Ge=new Set;for(let Ye=0;Ye<3;Ye++){const Je=await o(m,`${l.wabaId}/template_analytics`,{query:{start:y,end:h,granularity:"DAILY",metric_types:JSON.stringify(["sent","delivered","read","clicked"]),template_ids:JSON.stringify(_.map(Qt=>Qt.id)),use_waba_timezone:!1,limit:1e3,after:Q||void 0}});if(se.push(...Ee(Je)),Q=be(Je),!Q)break;if(Ge.has(Q))throw new R(502,"Meta returned repeated analytics pages. Try again later.");Ge.add(Q)}if(Q)throw new R(504,"Meta returned more analytics pages than this dashboard can load at once.");return{batch:_,batchPoints:se}}));for(const _ of V){if(_.status==="rejected"){q=!0;continue}x.push(..._.value.batchPoints),F.push(..._.value.batch)}if(V.length&&!F.length)throw V.find(_=>_.status==="rejected").reason;T=na(x,F,I,q)}catch(S){T=fe(S)}return{days:g,start:y,end:h,timezone:"UTC",phoneNumber:l.displayPhoneNumber,enabled:A,canEnable:De(u).manageInsights&&A!==!0,messages:E.status==="fulfilled"?oa(E.value):fe(E.reason),templates:T}})),s.post("/insights/enable",a.defineEventHandler(async c=>{const o=await i(c,"manageInsights");me(C.objectType({confirm:C.literalType(!0)}).strict(),await a.readBody(c));const{row:u,token:l}=await r(o);try{const m=await t(l,u.wabaId,{method:"POST",body:{is_enabled_for_insights:!0}});if(m.id!==u.wabaId&&m.success!==!0)throw new R(502,"Meta did not confirm insights activation. Refresh before trying again.");return{enabled:!0}}catch(m){ne(m)}})),s.handler}exports.appointments=Zt;exports.contentManager=ss;exports.filezone=ea;exports.firebaseAuthManager=Fs;exports.formBuilder=Qs;exports.googleAnalytics=es;exports.mailTemplates=Ks;exports.products=Wt;exports.whatsappBusiness=ga;exports.whatsappBusinessConnection=ua;