@oneclick.dev/cms-core-modules 0.0.123 → 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 (223) 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-MDGolnIP.mjs → ContentEditor-D754iLJS.mjs} +7985 -26820
  5. package/dist/{Detail-Dk8E6_uF.mjs → Detail-BOxWQ1QQ.mjs} +15 -14
  6. package/dist/{Detail-Cn9Bk0u0.mjs → Detail-BfKW0uZE.mjs} +26 -25
  7. package/dist/Detail-DFVu2F38.mjs +11 -0
  8. package/dist/Detail-DJea_fCd.js +1 -0
  9. package/dist/Detail-DPJacXfm.js +1 -0
  10. package/dist/Detail-DSLZ7_bM.js +1 -0
  11. package/dist/Detail-DUGThL9Y.js +1 -0
  12. package/dist/{Detail-B_scnidb.mjs → Detail-D_LlEeHu.mjs} +35 -34
  13. package/dist/Editor-D4evcdhv.mjs +768 -0
  14. package/dist/Editor-gLLtuJbY.js +1 -0
  15. package/dist/FormStatusBadge.vue_vue_type_script_setup_true_lang-BjKpsqT9.js +1 -0
  16. package/dist/FormStatusBadge.vue_vue_type_script_setup_true_lang-D5v5AhLe.mjs +76 -0
  17. package/dist/Knowledge-B66vxz--.js +1 -0
  18. package/dist/Knowledge-BnMTTuJ7.mjs +742 -0
  19. package/dist/Overview-0hFXiuYo.js +1 -0
  20. package/dist/Overview-BE4uYPx0.mjs +745 -0
  21. package/dist/Overview-BTx5LF4J.js +1 -0
  22. package/dist/Overview-CCu2PHai.mjs +289 -0
  23. package/dist/Overview-CQRpFhbJ.mjs +286 -0
  24. package/dist/Overview-CTs7sX4d.js +1 -0
  25. package/dist/Overview-Cj89XXKN.js +1 -0
  26. package/dist/Overview-D2u-leeO.js +1 -0
  27. package/dist/Overview-D_2wozCG.mjs +1733 -0
  28. package/dist/Overview-D_c18mbR.mjs +248 -0
  29. package/dist/Overview-Dffo-zH4.mjs +11 -0
  30. package/dist/Overview-g4Rcj245.js +1 -0
  31. package/dist/{TableCalculationWidget-CrqcLGfU.mjs → TableCalculationWidget-B_LFoOYm.mjs} +3 -3
  32. package/dist/{TableCalculationWidget-CkQPrVUd.js → TableCalculationWidget-CahOIcbk.js} +1 -1
  33. package/dist/TableDataWidget-BwqWFH4A.js +1 -0
  34. package/dist/TableDataWidget-RsVxcDsL.mjs +112 -0
  35. package/dist/TableOverviewCard-BPWCqo56.js +1 -0
  36. package/dist/{TableOverviewCard-Ci0d4o4w.mjs → TableOverviewCard-D667DDag.mjs} +113 -127
  37. package/dist/TableResultView.vue_vue_type_script_setup_true_lang-C3XiOXgb.js +1 -0
  38. package/dist/TableResultView.vue_vue_type_script_setup_true_lang-sp0TLio0.mjs +547 -0
  39. package/dist/{TableRowActionCard-BhdnnPaq.mjs → TableRowActionCard-BPYJ-Kjy.mjs} +95 -101
  40. package/dist/TableRowActionCard-Bnqj1YIe.js +1 -0
  41. package/dist/TableView-CwhZ9YN2.js +4 -0
  42. package/dist/TableView-DX9PKS8N.mjs +9673 -0
  43. package/dist/TemplateBuilder.vue_vue_type_script_setup_true_lang-BgDdzbLe.js +1 -0
  44. package/dist/TemplateBuilder.vue_vue_type_script_setup_true_lang-D85V4nrQ.mjs +2886 -0
  45. package/dist/Test-CzmOmi53.mjs +447 -0
  46. package/dist/Test-USDtNFY1.js +1 -0
  47. package/dist/UserDetail-DpUL3mhk.mjs +1483 -0
  48. package/dist/UserDetail-KRIfxaYd.js +1 -0
  49. package/dist/cms-core-modules.css +1 -1
  50. package/dist/dateRangePresets-CaPVagHY.mjs +453 -0
  51. package/dist/dateRangePresets-gxQCiZ6c.js +1 -0
  52. package/dist/index-6hZ6MYY6.js +1 -0
  53. package/dist/index-BVmmicu3.mjs +14 -0
  54. package/dist/index-CQuN5rKx.js +1 -0
  55. package/dist/index-ChHD_1Cf.js +144 -0
  56. package/dist/index-CnDGeInm.js +1 -0
  57. package/dist/index-CqDTc5Ig.mjs +20 -0
  58. package/dist/index-CvV4nVWI.js +1 -0
  59. package/dist/index-CyvvAdt4.js +1 -0
  60. package/dist/index-DH2z0W_w.mjs +19012 -0
  61. package/dist/index-DIRD7t5_.mjs +20 -0
  62. package/dist/index-DZKzj48V.mjs +17 -0
  63. package/dist/index-OJuBYR60.mjs +57 -0
  64. package/dist/index.cjs.js +1 -1
  65. package/dist/index.mjs +480 -103
  66. package/dist/orders-C_sz8J9b.js +1 -0
  67. package/dist/{orders-BfTLIm7U.mjs → orders-Cgdggzvx.mjs} +84 -84
  68. package/dist/server-handlers.cjs.js +1 -1
  69. package/dist/server-handlers.mjs +2696 -605
  70. package/dist/src/appointments/chat-components/AppointmentListTable.vue.d.ts +3 -0
  71. package/dist/src/appointments/index.d.ts +93 -3
  72. package/dist/src/appointments/reservation-summary.d.ts +48 -0
  73. package/dist/src/appointments/server.d.ts +1 -0
  74. package/dist/src/appointments/tools.d.ts +93 -3
  75. package/dist/src/contentManager/components/content-editor/tiptap-extensions/Container.d.ts +1 -1
  76. package/dist/src/contentManager/components/content-editor/tiptap-extensions/Row.d.ts +7 -0
  77. package/dist/src/contentManager/components/content-editor/tiptap-extensions/Stack.d.ts +7 -0
  78. package/dist/src/contentManager/components/content-editor/tiptap-extensions/helpers/resizeHistory.d.ts +9 -0
  79. package/dist/src/contentManager/components/content-editor/tiptap-menus/ElementEditorMenu.vue.d.ts +3 -1
  80. package/dist/src/filezone/config.d.ts +31 -0
  81. package/dist/src/filezone/index.d.ts +43 -0
  82. package/dist/src/filezone/index.vue.d.ts +2 -0
  83. package/dist/src/filezone/metadata.d.ts +2 -0
  84. package/dist/src/filezone/pages/Overview.vue.d.ts +4 -0
  85. package/dist/src/filezone/permissions.d.ts +5 -0
  86. package/dist/src/filezone/routes.d.ts +5 -0
  87. package/dist/src/filezone/server.d.ts +36 -0
  88. package/dist/src/firebaseAuthManager/components/DeleteUserDialog.vue.d.ts +25 -0
  89. package/dist/src/firebaseAuthManager/components/ReasonActionDialog.vue.d.ts +23 -0
  90. package/dist/src/firebaseAuthManager/components/UserStatusBadge.vue.d.ts +5 -0
  91. package/dist/src/firebaseAuthManager/config.d.ts +151 -0
  92. package/dist/src/firebaseAuthManager/index.d.ts +163 -0
  93. package/dist/src/firebaseAuthManager/index.vue.d.ts +14 -0
  94. package/dist/src/firebaseAuthManager/metadata.d.ts +2 -0
  95. package/dist/src/firebaseAuthManager/pages/Overview.vue.d.ts +2 -0
  96. package/dist/src/firebaseAuthManager/pages/UserDetail.vue.d.ts +2 -0
  97. package/dist/src/firebaseAuthManager/permissions.d.ts +5 -0
  98. package/dist/src/firebaseAuthManager/routes.d.ts +5 -0
  99. package/dist/src/firebaseAuthManager/server.d.ts +162 -0
  100. package/dist/src/firebaseAuthManager/types.d.ts +84 -0
  101. package/dist/src/formBuilder/components/FormPreviewDialog.vue.d.ts +12 -0
  102. package/dist/src/formBuilder/components/FormStatusBadge.vue.d.ts +9 -0
  103. package/dist/src/formBuilder/config.d.ts +31 -0
  104. package/dist/src/formBuilder/index.d.ts +44 -0
  105. package/dist/src/formBuilder/index.vue.d.ts +14 -0
  106. package/dist/src/formBuilder/lib/forms.d.ts +11 -0
  107. package/dist/src/formBuilder/metadata.d.ts +2 -0
  108. package/dist/src/formBuilder/pages/Editor.vue.d.ts +2 -0
  109. package/dist/src/formBuilder/pages/Overview.vue.d.ts +2 -0
  110. package/dist/src/formBuilder/permissions.d.ts +5 -0
  111. package/dist/src/formBuilder/routes.d.ts +5 -0
  112. package/dist/src/formBuilder/server.d.ts +87 -0
  113. package/dist/src/formBuilder/tools.d.ts +1 -0
  114. package/dist/src/formBuilder/types.d.ts +46 -0
  115. package/dist/src/helpbot/composables/useHelpbotApi.d.ts +18 -0
  116. package/dist/src/helpbot/config.d.ts +17 -0
  117. package/dist/src/helpbot/index.d.ts +29 -0
  118. package/dist/src/helpbot/index.vue.d.ts +2 -0
  119. package/dist/src/helpbot/metadata.d.ts +2 -0
  120. package/dist/src/helpbot/pages/Knowledge.vue.d.ts +4 -0
  121. package/dist/src/helpbot/pages/Overview.vue.d.ts +2 -0
  122. package/dist/src/helpbot/pages/Test.vue.d.ts +4 -0
  123. package/dist/src/helpbot/permissions.d.ts +5 -0
  124. package/dist/src/helpbot/routes.d.ts +5 -0
  125. package/dist/src/helpbot/types.d.ts +42 -0
  126. package/dist/src/index.d.ts +5 -0
  127. package/dist/src/mailTemplates/builder-types.d.ts +40 -0
  128. package/dist/src/mailTemplates/components/BlockRenderer.vue.d.ts +24 -0
  129. package/dist/src/mailTemplates/components/DesignPanel.vue.d.ts +13 -0
  130. package/dist/src/mailTemplates/components/EmailCanvas.vue.d.ts +44 -0
  131. package/dist/src/mailTemplates/components/RichTextEditor.vue.d.ts +10 -0
  132. package/dist/src/mailTemplates/components/RightSidebar.vue.d.ts +18 -0
  133. package/dist/src/mailTemplates/components/SettingsPanel.vue.d.ts +15 -0
  134. package/dist/src/mailTemplates/components/TemplateBuilder.vue.d.ts +2 -0
  135. package/dist/src/mailTemplates/components/TemplateList.vue.d.ts +19 -1
  136. package/dist/src/mailTemplates/components/rich-text.d.ts +6 -0
  137. package/dist/src/mailTemplates/config.d.ts +10 -4
  138. package/dist/src/mailTemplates/defaults.d.ts +10 -0
  139. package/dist/src/mailTemplates/editor-state.d.ts +21 -0
  140. package/dist/src/mailTemplates/index.d.ts +11 -5
  141. package/dist/src/mailTemplates/index.vue.d.ts +2 -4
  142. package/dist/src/mailTemplates/metadata.d.ts +1 -1
  143. package/dist/src/mailTemplates/pages/Detail.vue.d.ts +1 -13
  144. package/dist/src/mailTemplates/pages/Overview.vue.d.ts +1 -13
  145. package/dist/src/mailTemplates/server.d.ts +23 -0
  146. package/dist/src/server-handlers.d.ts +6 -0
  147. package/dist/src/table/badge-filter-utils.d.ts +8 -0
  148. package/dist/src/table/chat-components/TableOverviewCard.vue.d.ts +2 -7
  149. package/dist/src/table/chat-components/TableResultView.vue.d.ts +3 -7
  150. package/dist/src/table/components/TableDisplayCell.vue.d.ts +8 -0
  151. package/dist/src/table/components/columns/BadgeColumnSettings.vue.d.ts +9 -0
  152. package/dist/src/table/components/columns/ColumnColorSelect.vue.d.ts +11 -0
  153. package/dist/src/table/components/columns/ColumnDisplaySettings.vue.d.ts +9 -0
  154. package/dist/src/table/components/columns/ColumnTypeSelect.vue.d.ts +11 -0
  155. package/dist/src/table/components/columns/ColumnValueSourceInput.vue.d.ts +15 -0
  156. package/dist/src/table/components/columns/DialogTableColumnItem.vue.d.ts +8 -11
  157. package/dist/src/table/components/columns/ProgressColumnSettings.vue.d.ts +8 -0
  158. package/dist/src/table/components/stats/StatDialog.vue.d.ts +6 -10
  159. package/dist/src/table/components/stats/Stats.vue.d.ts +9 -15
  160. package/dist/src/table/composables/useTableClock.d.ts +4 -0
  161. package/dist/src/table/display-utils.d.ts +35 -0
  162. package/dist/src/table/row-value.d.ts +1 -0
  163. package/dist/src/table/stat-utils.d.ts +9 -0
  164. package/dist/src/table/tool-utils.d.ts +1 -1
  165. package/dist/src/table/types.d.ts +54 -1
  166. package/dist/src/whatsappBusiness/analytics.d.ts +4 -0
  167. package/dist/src/whatsappBusiness/components/AccountPanel.vue.d.ts +2 -0
  168. package/dist/src/whatsappBusiness/components/InsightsPanel.vue.d.ts +5 -0
  169. package/dist/src/whatsappBusiness/components/TemplateDialog.vue.d.ts +14 -0
  170. package/dist/src/whatsappBusiness/components/TemplatesPanel.vue.d.ts +5 -0
  171. package/dist/src/whatsappBusiness/components/presentation.d.ts +4 -0
  172. package/dist/src/whatsappBusiness/config.d.ts +51 -0
  173. package/dist/src/whatsappBusiness/connection.d.ts +77 -0
  174. package/dist/src/whatsappBusiness/index.d.ts +63 -0
  175. package/dist/src/whatsappBusiness/index.vue.d.ts +5 -0
  176. package/dist/src/whatsappBusiness/metadata.d.ts +2 -0
  177. package/dist/src/whatsappBusiness/pages/Overview.vue.d.ts +2 -0
  178. package/dist/src/whatsappBusiness/permissions.d.ts +5 -0
  179. package/dist/src/whatsappBusiness/provider.d.ts +21 -0
  180. package/dist/src/whatsappBusiness/routes.d.ts +5 -0
  181. package/dist/src/whatsappBusiness/server.d.ts +14 -0
  182. package/dist/src/whatsappBusiness/templates.d.ts +94 -0
  183. package/dist/src/whatsappBusiness/types.d.ts +162 -0
  184. package/dist/{tool-sandbox-DoF1PsyS.js → tool-sandbox-BSkKdH7V.js} +1 -1
  185. package/dist/{tool-sandbox-DdkMLkDK.mjs → tool-sandbox-DU9b1B7v.mjs} +1 -1
  186. package/dist/types-9N-DKxNj.mjs +250 -0
  187. package/dist/types-B5MifmeF.mjs +2696 -0
  188. package/dist/types-Cem0vPo_.js +1 -0
  189. package/dist/types-D5dgTskW.js +1 -0
  190. package/dist/useHelpbotApi-BIIungOh.js +1 -0
  191. package/dist/useHelpbotApi-HC9JSiSn.mjs +229 -0
  192. package/dist/{useTableAgentTools-D5mzGf3A.js → useTableAgentTools-BrUot49I.js} +1 -1
  193. package/dist/{useTableAgentTools-DcN3kYRL.mjs → useTableAgentTools-r5InEI4p.mjs} +8 -8
  194. package/dist/vee-validate-zod-CWZwNTWE.js +1 -0
  195. package/dist/vee-validate-zod-D0WaDgDZ.mjs +1780 -0
  196. package/package.json +2 -2
  197. package/src/appointments/tools.ts +52 -2
  198. package/src/formBuilder/tools.ts +1 -0
  199. package/dist/AppointmentListTable-DZJNmTMb.mjs +0 -177
  200. package/dist/AppointmentListTable-Dieu9US_.js +0 -1
  201. package/dist/ContentEditor-C-Wm48ai.js +0 -467
  202. package/dist/Detail-BCUUNWvp.js +0 -1
  203. package/dist/Detail-Byk781b4.mjs +0 -26
  204. package/dist/Detail-DGg_xXZc.js +0 -1
  205. package/dist/Detail-Sc2qZdxj.js +0 -1
  206. package/dist/Detail-mvSGbnFU.js +0 -1
  207. package/dist/Overview-BDlBCUI_.js +0 -1
  208. package/dist/Overview-Su8ZUbMs.mjs +0 -26
  209. package/dist/TableDataWidget-7qvC3Egd.js +0 -1
  210. package/dist/TableDataWidget-DMTSoGRO.mjs +0 -121
  211. package/dist/TableOverviewCard-AFq-puJs.js +0 -1
  212. package/dist/TableResultView.vue_vue_type_script_setup_true_lang-CBn-Oxl9.mjs +0 -534
  213. package/dist/TableResultView.vue_vue_type_script_setup_true_lang-C_Lv-SmJ.js +0 -1
  214. package/dist/TableRowActionCard-IzB6tNN-.js +0 -1
  215. package/dist/TableView-BkwoKREo.js +0 -4
  216. package/dist/TableView-C4Zyp8d7.mjs +0 -8228
  217. package/dist/TemplateList.vue_vue_type_script_setup_true_lang-B_AtWnHa.js +0 -1
  218. package/dist/TemplateList.vue_vue_type_script_setup_true_lang-Cmovy7kk.mjs +0 -40
  219. package/dist/dateRangePresets-B-l5b_wR.mjs +0 -48
  220. package/dist/dateRangePresets-CPWqst_x.js +0 -1
  221. package/dist/orders-DUa2PVoa.js +0 -1
  222. package/dist/vee-validate-zod-SjDT8X35.mjs +0 -4404
  223. package/dist/vee-validate-zod-VCmLnrtQ.js +0 -1
@@ -1,400 +1,502 @@
1
- import { createRouter as E, defineEventHandler as h, createError as p, getRouterParam as F, getQuery as I, readBody as O } from "h3";
1
+ import { createRouter as J, defineEventHandler as b, createError as g, getRouterParam as U, getQuery as v, readBody as j, setResponseStatus as os, setHeader as se, setResponseHeader as ns } from "h3";
2
2
  import "vue";
3
- function z(A) {
4
- const { initFirebase: P, normalizeTimestamps: S } = A, f = E();
5
- async function u(l) {
6
- const { supabase: y, instanceId: D } = l.context.module, { data: t, error: c } = await y.from("project_modules").select("config").eq("id", D).single();
7
- if (c || !t?.config)
8
- throw p({ statusCode: 500, statusMessage: "Failed to load module config." });
9
- const i = t.config, s = i.project, a = i.productCollection || "products";
10
- if (!s)
11
- throw p({ statusCode: 400, statusMessage: "Products module has no Firebase integration configured." });
12
- return { firebase: await P(l, s), collection: a };
3
+ import { s as x, o as O, b as _e, a as it, e as Ke, d as rs, l as ke } from "./types-B5MifmeF.mjs";
4
+ function Ma(e) {
5
+ const { initFirebase: t, normalizeTimestamps: s } = e, r = J();
6
+ async function n(a) {
7
+ const { supabase: c, instanceId: i } = a.context.module, { data: o, error: d } = await c.from("project_modules").select("config").eq("id", i).single();
8
+ if (d || !o?.config)
9
+ throw g({ statusCode: 500, statusMessage: "Failed to load module config." });
10
+ const u = o.config, l = u.project, f = u.productCollection || "products";
11
+ if (!l)
12
+ throw g({ statusCode: 400, statusMessage: "Products module has no Firebase integration configured." });
13
+ return { firebase: await t(a, l), collection: f };
13
14
  }
14
- return f.get("/products", h(async (l) => {
15
+ return r.get("/products", b(async (a) => {
15
16
  try {
16
- const { firebase: y, collection: D } = await u(l);
17
- return (await y.firestore().collection(D).get()).docs.map((c) => S({
18
- id: c.id,
19
- ...c.data()
17
+ const { firebase: c, collection: i } = await n(a);
18
+ return (await c.firestore().collection(i).get()).docs.map((d) => s({
19
+ id: d.id,
20
+ ...d.data()
20
21
  }));
21
- } catch (y) {
22
- throw console.error("Products handler — list error:", y), p({
23
- statusCode: y.statusCode || 500,
24
- statusMessage: y.statusMessage || "Failed to list products"
22
+ } catch (c) {
23
+ throw console.error("Products handler — list error:", c), g({
24
+ statusCode: c.statusCode || 500,
25
+ statusMessage: c.statusMessage || "Failed to list products"
25
26
  });
26
27
  }
27
- })), f.get("/products/:productId", h(async (l) => {
28
- const y = F(l, "productId");
29
- if (!y)
30
- throw p({ statusCode: 400, statusMessage: "Product ID is required." });
28
+ })), r.get("/products/:productId", b(async (a) => {
29
+ const c = U(a, "productId");
30
+ if (!c)
31
+ throw g({ statusCode: 400, statusMessage: "Product ID is required." });
31
32
  try {
32
- const { firebase: D, collection: t } = await u(l), c = await D.firestore().collection(t).doc(y).get();
33
- if (!c.exists)
34
- throw p({ statusCode: 404, statusMessage: "Product not found." });
35
- return S({
36
- id: c.id,
37
- ...c.data()
33
+ const { firebase: i, collection: o } = await n(a), d = await i.firestore().collection(o).doc(c).get();
34
+ if (!d.exists)
35
+ throw g({ statusCode: 404, statusMessage: "Product not found." });
36
+ return s({
37
+ id: d.id,
38
+ ...d.data()
38
39
  });
39
- } catch (D) {
40
- throw console.error("Products handler — get error:", D), p({
41
- statusCode: D.statusCode || 500,
42
- statusMessage: D.statusMessage || "Failed to get product"
40
+ } catch (i) {
41
+ throw console.error("Products handler — get error:", i), g({
42
+ statusCode: i.statusCode || 500,
43
+ statusMessage: i.statusMessage || "Failed to get product"
43
44
  });
44
45
  }
45
- })), f.get("/empty-stock", h(async (l) => {
46
- const D = I(l).category;
46
+ })), r.get("/empty-stock", b(async (a) => {
47
+ const i = v(a).category;
47
48
  try {
48
- const { firebase: t, collection: c } = await u(l);
49
- let i = t.firestore().collection(c).where("stock", "==", 0);
50
- D && (i = i.where("collections", "array-contains", D));
51
- const a = (await i.get()).docs.map((r) => S({
52
- id: r.id,
53
- ...r.data()
49
+ const { firebase: o, collection: d } = await n(a);
50
+ let u = o.firestore().collection(d).where("stock", "==", 0);
51
+ i && (u = u.where("collections", "array-contains", i));
52
+ const f = (await u.get()).docs.map((p) => s({
53
+ id: p.id,
54
+ ...p.data()
54
55
  }));
55
56
  return {
56
- count: a.length,
57
- products: a.map((r) => ({
58
- id: r.id,
59
- title: r.title,
60
- slug: r.slug,
61
- stock: r.stock,
62
- price: r.price,
63
- currency: r.currency,
64
- status: r.status
57
+ count: f.length,
58
+ products: f.map((p) => ({
59
+ id: p.id,
60
+ title: p.title,
61
+ slug: p.slug,
62
+ stock: p.stock,
63
+ price: p.price,
64
+ currency: p.currency,
65
+ status: p.status
65
66
  }))
66
67
  };
67
- } catch (t) {
68
- throw console.error("Products handler — empty-stock error:", t), p({
69
- statusCode: t.statusCode || 500,
70
- statusMessage: t.statusMessage || "Failed to query empty stock"
68
+ } catch (o) {
69
+ throw console.error("Products handler — empty-stock error:", o), g({
70
+ statusCode: o.statusCode || 500,
71
+ statusMessage: o.statusMessage || "Failed to query empty stock"
71
72
  });
72
73
  }
73
- })), f.handler;
74
- }
75
- function J(A) {
76
- const { initFirebase: P, normalizeTimestamps: S, deleteField: f } = A, u = E(), l = "privateSettings", y = "refundFlow", D = "metadataSchema";
77
- async function t(e) {
78
- const { supabase: n, instanceId: o } = e.context.module, { data: d, error: g } = await n.from("project_modules").select("config").eq("id", o).single();
79
- if (g || !d?.config)
80
- throw p({ statusCode: 500, statusMessage: "Failed to load module config." });
81
- const m = d.config, w = m.project, C = m.reservationsCollection || "bookings_orders", R = m.agendaCollection || "agendas";
82
- if (!w)
83
- throw p({ statusCode: 400, statusMessage: "Appointments module has no Firebase integration configured." });
84
- return { firebase: await P(e, w), reservationsCollection: C, agendaCollection: R };
74
+ })), r.handler;
75
+ }
76
+ const ct = 500, is = ["confirmed", "pending", "cancelled", "all"], cs = ["active", "approved", "needs_approval", "cancelled", "rejected", "all"];
77
+ function ne(e) {
78
+ throw g({ statusCode: 400, statusMessage: e });
79
+ }
80
+ function Ae(e, t, s) {
81
+ const r = e[t];
82
+ return r === void 0 && s !== void 0 ? s : ((typeof r != "string" || !r.trim()) && ne(`${t} must be a non-empty string.`), r.trim());
83
+ }
84
+ function dt(e, t, s, r, n = Number.MAX_SAFE_INTEGER) {
85
+ if (e[t] === void 0) return s;
86
+ const a = Ae(e, t), c = Number(a);
87
+ return (!/^\d+$/.test(a) || !Number.isSafeInteger(c) || c < r || c > n) && ne(`${t} must be an integer between ${r} and ${n}.`), c;
88
+ }
89
+ function q(e) {
90
+ return typeof e == "string" ? e : null;
91
+ }
92
+ function ds(e) {
93
+ if (typeof e != "number" && (typeof e != "string" || !e.trim())) return null;
94
+ const t = Number(e);
95
+ return Number.isFinite(t) ? t : null;
96
+ }
97
+ function ut(e, t, s) {
98
+ const r = { yesterday: -1, today: 0, tomorrow: 1 };
99
+ if (Object.prototype.hasOwnProperty.call(r, e)) {
100
+ const a = /* @__PURE__ */ new Date(`${t}T00:00:00.000Z`);
101
+ return a.setUTCDate(a.getUTCDate() + r[e]), a.toISOString().slice(0, 10);
102
+ }
103
+ /^\d{4}-\d{2}-\d{2}$/.test(e) || ne(`${s} must be YYYY-MM-DD, today, tomorrow, or yesterday.`);
104
+ const n = /* @__PURE__ */ new Date(`${e}T00:00:00.000Z`);
105
+ return (!Number.isFinite(n.getTime()) || n.toISOString().slice(0, 10) !== e) && ne(`${s} must be a valid calendar date.`), e;
106
+ }
107
+ async function us(e, t, s, r, n = /* @__PURE__ */ new Date()) {
108
+ let a;
109
+ try {
110
+ const I = new Intl.DateTimeFormat("en-US", { timeZone: r, year: "numeric", month: "2-digit", day: "2-digit" }).formatToParts(n), M = (D) => I.find((_) => _.type === D).value;
111
+ a = `${M("year")}-${M("month")}-${M("day")}`;
112
+ } catch {
113
+ ne("The project timezone is invalid.");
85
114
  }
86
- function c(e) {
87
- return (e.reservations || []).map((o) => ({
88
- orderId: e.id,
89
- reservationId: o.id,
90
- customerInfo: e.customerInfo,
91
- date: o.date,
92
- startTime: o.timeslot?.startTime,
93
- endTime: o.timeslot?.endTime,
94
- spots: o.spots,
95
- status: e.status,
96
- reservationStatus: o.status,
97
- resourceId: o.resourceId,
98
- reservationPrice: o.totalPrice,
99
- reservationBasePrice: o.basePrice,
100
- reservationAddOnsPrice: o.addOnsPrice,
101
- pricingOption: o.pricingOption,
102
- amountDue: e.amountDue,
103
- amountPaid: e.amountPaid,
104
- createdAt: e.createdAt,
105
- metadata: e.metadata || {},
106
- reservationMetadata: o.metadata || {}
115
+ const c = ut(Ae(s, "startDate"), a, "startDate"), i = ut(Ae(s, "endDate", c), a, "endDate");
116
+ i < c && ne("endDate must be on or after startDate.");
117
+ const o = s.agendaId === void 0 ? null : Ae(s, "agendaId"), d = Ae(s, "orderStatus", "confirmed"), u = Ae(s, "reservationStatus", "active");
118
+ is.includes(d) || ne("Invalid orderStatus."), cs.includes(u) || ne("Invalid reservationStatus.");
119
+ const l = dt(s, "limit", 100, 1, 200), f = dt(s, "offset", 0, 0);
120
+ let p = e.collection(t);
121
+ o && (p = p.where("agendaId", "==", o)), d !== "all" && (p = p.where("status", "==", d)), p = p.orderBy("__name__").select("status", "agendaId", "reservations", "customerInfo").limit(ct);
122
+ const m = { reservationCount: 0, orderCount: 0, spotCount: 0 }, h = /* @__PURE__ */ new Map(), y = [];
123
+ let w;
124
+ for (; ; ) {
125
+ const I = await (w ? p.startAfter(w) : p).get();
126
+ for (const M of I.docs) {
127
+ const D = M.data(), _ = /* @__PURE__ */ new Set();
128
+ for (const T of Array.isArray(D.reservations) ? D.reservations : []) {
129
+ if (!T || typeof T.date != "string") continue;
130
+ const E = T.date;
131
+ if (E < c || E > i || u === "active" && ["cancelled", "rejected"].includes(T.status) || u !== "active" && u !== "all" && T.status !== u) continue;
132
+ const V = T.spots == null ? 1 : Number(T.spots), P = Number.isFinite(V) && V >= 0 ? V : 1, K = h.get(E) || { reservationCount: 0, orderCount: 0, spotCount: 0 };
133
+ K.reservationCount++, K.spotCount += P, h.set(E, K), _.add(E), m.reservationCount >= f && y.length < l && y.push({
134
+ orderId: M.id,
135
+ reservationId: q(T.id),
136
+ agendaId: q(D.agendaId),
137
+ date: E,
138
+ startTime: q(T.timeslot?.startTime),
139
+ endTime: q(T.timeslot?.endTime),
140
+ spots: P,
141
+ status: q(D.status),
142
+ reservationStatus: q(T.status),
143
+ resourceId: q(T.resourceId) ?? q(T.resource?.id),
144
+ resourceName: q(T.resource?.name) ?? q(T.resourceName),
145
+ customerInfo: {
146
+ firstName: q(D.customerInfo?.firstName),
147
+ lastName: q(D.customerInfo?.lastName),
148
+ email: q(D.customerInfo?.email)
149
+ },
150
+ reservationPrice: ds(T.totalPrice)
151
+ }), m.reservationCount++, m.spotCount += P;
152
+ }
153
+ _.size && m.orderCount++;
154
+ for (const T of _) h.get(T).orderCount++;
155
+ }
156
+ if (I.docs.length < ct) break;
157
+ w = I.docs[I.docs.length - 1];
158
+ }
159
+ const C = f + y.length, A = C < m.reservationCount;
160
+ return {
161
+ startDate: c,
162
+ endDate: i,
163
+ timeZone: r,
164
+ filters: { agendaId: o, orderStatus: d, reservationStatus: u },
165
+ ...m,
166
+ byDate: [...h].sort(([I], [M]) => I.localeCompare(M)).map(([I, M]) => ({ date: I, ...M })),
167
+ appointments: y,
168
+ pagination: { offset: f, limit: l, returnedCount: y.length, hasMore: A, nextOffset: A ? C : null }
169
+ };
170
+ }
171
+ function Sa(e) {
172
+ const { initFirebase: t, normalizeTimestamps: s, deleteField: r } = e, n = J(), a = "privateSettings", c = "refundFlow", i = "metadataSchema";
173
+ async function o(m) {
174
+ const { supabase: h, instanceId: y } = m.context.module, { data: w, error: C } = await h.from("project_modules").select("config, project_id").eq("id", y).single();
175
+ if (C || !w?.config)
176
+ throw g({ statusCode: 500, statusMessage: "Failed to load module config." });
177
+ const A = w.config, I = A.project, M = A.reservationsCollection || "bookings_orders", D = A.agendaCollection || "agendas";
178
+ if (!I)
179
+ throw g({ statusCode: 400, statusMessage: "Appointments module has no Firebase integration configured." });
180
+ return { firebase: await t(m, I), reservationsCollection: M, agendaCollection: D, projectId: w.project_id };
181
+ }
182
+ function d(m) {
183
+ return (m.reservations || []).map((y) => ({
184
+ orderId: m.id,
185
+ agendaId: m.agendaId,
186
+ reservationId: y.id,
187
+ customerInfo: m.customerInfo,
188
+ date: y.date,
189
+ startTime: y.timeslot?.startTime,
190
+ endTime: y.timeslot?.endTime,
191
+ spots: y.spots,
192
+ status: m.status,
193
+ reservationStatus: y.status,
194
+ resourceId: y.resourceId,
195
+ reservationPrice: y.totalPrice,
196
+ reservationBasePrice: y.basePrice,
197
+ reservationAddOnsPrice: y.addOnsPrice,
198
+ pricingOption: y.pricingOption,
199
+ amountDue: m.amountDue,
200
+ amountPaid: m.amountPaid,
201
+ createdAt: m.createdAt,
202
+ metadata: m.metadata || {},
203
+ reservationMetadata: y.metadata || {}
107
204
  }));
108
205
  }
109
- function i(e) {
206
+ function u(m) {
110
207
  return {
111
- showAmount: e?.showAmount !== !1,
112
- fields: Array.isArray(e?.fields) ? e.fields : []
208
+ showAmount: m?.showAmount !== !1,
209
+ fields: Array.isArray(m?.fields) ? m.fields : []
113
210
  };
114
211
  }
115
- function s() {
116
- return f ? {
117
- refundFlow: f(),
118
- refundFlowEnabled: f(),
119
- refundDialog: f()
212
+ function l() {
213
+ return r ? {
214
+ refundFlow: r(),
215
+ refundFlowEnabled: r(),
216
+ refundDialog: r()
120
217
  } : {
121
218
  refundFlow: null,
122
219
  refundFlowEnabled: null,
123
220
  refundDialog: null
124
221
  };
125
222
  }
126
- function a(e) {
127
- return Array.isArray(e) ? e : [];
223
+ function f(m) {
224
+ return Array.isArray(m) ? m : [];
128
225
  }
129
- function r() {
130
- return f ? {
131
- metadataSchema: f()
226
+ function p() {
227
+ return r ? {
228
+ metadataSchema: r()
132
229
  } : {
133
230
  metadataSchema: null
134
231
  };
135
232
  }
136
- return u.get("/agendas", h(async (e) => {
233
+ return n.get("/agendas", b(async (m) => {
137
234
  try {
138
- const { firebase: n, agendaCollection: o } = await t(e), g = (await n.firestore().collection(o).get()).docs.map((m) => {
139
- const w = m.data();
235
+ const { firebase: h, agendaCollection: y } = await o(m), C = (await h.firestore().collection(y).get()).docs.map((A) => {
236
+ const I = A.data();
140
237
  return {
141
- id: m.id,
142
- serviceName: w.serviceName || "",
143
- type: w.type || "regular"
238
+ id: A.id,
239
+ serviceName: I.serviceName || "",
240
+ type: I.type || "regular"
144
241
  };
145
242
  });
146
243
  return {
147
- count: g.length,
148
- agendas: g
244
+ count: C.length,
245
+ agendas: C
149
246
  };
150
- } catch (n) {
151
- throw console.error("Appointments handler — list agendas error:", n), p({
152
- statusCode: n.statusCode || 500,
153
- statusMessage: n.statusMessage || "Failed to list agendas"
247
+ } catch (h) {
248
+ throw console.error("Appointments handler — list agendas error:", h), g({
249
+ statusCode: h.statusCode || 500,
250
+ statusMessage: h.statusMessage || "Failed to list agendas"
154
251
  });
155
252
  }
156
- })), u.get("/agendas/:agendaId/opening-hours", h(async (e) => {
157
- const n = F(e, "agendaId"), d = I(e).date;
158
- if (!n)
159
- throw p({ statusCode: 400, statusMessage: "Agenda ID is required." });
160
- if (!d)
161
- throw p({ statusCode: 400, statusMessage: "Date is required (YYYY-MM-DD)." });
253
+ })), n.get("/agendas/:agendaId/opening-hours", b(async (m) => {
254
+ const h = U(m, "agendaId"), w = v(m).date;
255
+ if (!h)
256
+ throw g({ statusCode: 400, statusMessage: "Agenda ID is required." });
257
+ if (!w)
258
+ throw g({ statusCode: 400, statusMessage: "Date is required (YYYY-MM-DD)." });
162
259
  try {
163
- const { firebase: g, agendaCollection: m } = await t(e), w = await g.firestore().collection(m).doc(n).get();
164
- if (!w.exists)
165
- throw p({ statusCode: 404, statusMessage: "Agenda not found." });
166
- const C = w.data(), R = (C.resources || []).filter((v) => v.isActive), M = C.exceptions || [], q = (/* @__PURE__ */ new Date(d + "T00:00:00")).getDay(), N = R.map((v) => {
167
- const V = v.openingHours?.[q] || [], U = M.find((T) => {
168
- const G = d >= T.startDate && d <= T.endDate, j = !T.resourceIds || T.resourceIds.length === 0 || T.resourceIds.includes(v.id);
169
- return G && j;
260
+ const { firebase: C, agendaCollection: A } = await o(m), I = await C.firestore().collection(A).doc(h).get();
261
+ if (!I.exists)
262
+ throw g({ statusCode: 404, statusMessage: "Agenda not found." });
263
+ const M = I.data(), D = (M.resources || []).filter((P) => P.isActive), _ = M.exceptions || [], E = (/* @__PURE__ */ new Date(w + "T00:00:00")).getDay(), V = D.map((P) => {
264
+ const K = P.openingHours?.[E] || [], Q = _.find((N) => {
265
+ const de = w >= N.startDate && w <= N.endDate, Z = !N.resourceIds || N.resourceIds.length === 0 || N.resourceIds.includes(P.id);
266
+ return de && Z;
170
267
  });
171
- return U ? {
172
- resourceId: v.id,
173
- resourceName: v.name,
174
- date: d,
175
- dayOfWeek: q,
176
- isClosed: U.isClosed,
177
- hours: U.isClosed ? [] : (U.timeslots || []).map((T) => ({
178
- start: T.startTime,
179
- end: T.endTime
268
+ return Q ? {
269
+ resourceId: P.id,
270
+ resourceName: P.name,
271
+ date: w,
272
+ dayOfWeek: E,
273
+ isClosed: Q.isClosed,
274
+ hours: Q.isClosed ? [] : (Q.timeslots || []).map((N) => ({
275
+ start: N.startTime,
276
+ end: N.endTime
180
277
  })),
181
278
  isException: !0
182
279
  } : {
183
- resourceId: v.id,
184
- resourceName: v.name,
185
- date: d,
186
- dayOfWeek: q,
187
- isClosed: V.length === 0,
188
- hours: V,
280
+ resourceId: P.id,
281
+ resourceName: P.name,
282
+ date: w,
283
+ dayOfWeek: E,
284
+ isClosed: K.length === 0,
285
+ hours: K,
189
286
  isException: !1
190
287
  };
191
288
  });
192
289
  return {
193
- agendaId: n,
194
- serviceName: C.serviceName || "",
195
- date: d,
196
- resources: N
290
+ agendaId: h,
291
+ serviceName: M.serviceName || "",
292
+ date: w,
293
+ resources: V
197
294
  };
198
- } catch (g) {
199
- throw console.error("Appointments handler — opening hours error:", g), p({
200
- statusCode: g.statusCode || 500,
201
- statusMessage: g.statusMessage || "Failed to get opening hours"
295
+ } catch (C) {
296
+ throw console.error("Appointments handler — opening hours error:", C), g({
297
+ statusCode: C.statusCode || 500,
298
+ statusMessage: C.statusMessage || "Failed to get opening hours"
202
299
  });
203
300
  }
204
- })), u.get("/agendas/:agendaId/refund-flow", h(async (e) => {
205
- const n = F(e, "agendaId");
206
- if (!n)
207
- throw p({ statusCode: 400, statusMessage: "Agenda ID is required." });
301
+ })), n.get("/agendas/:agendaId/refund-flow", b(async (m) => {
302
+ const h = U(m, "agendaId");
303
+ if (!h)
304
+ throw g({ statusCode: 400, statusMessage: "Agenda ID is required." });
208
305
  try {
209
- const { firebase: o, agendaCollection: d } = await t(e), m = o.firestore().collection(d).doc(n), w = m.collection(l).doc(y), [C, R] = await Promise.all([
210
- m.get(),
211
- w.get()
306
+ const { firebase: y, agendaCollection: w } = await o(m), A = y.firestore().collection(w).doc(h), I = A.collection(a).doc(c), [M, D] = await Promise.all([
307
+ A.get(),
308
+ I.get()
212
309
  ]);
213
- if (!C.exists)
214
- throw p({ statusCode: 404, statusMessage: "Agenda not found." });
215
- const M = R.exists ? R.data() || {} : {};
310
+ if (!M.exists)
311
+ throw g({ statusCode: 404, statusMessage: "Agenda not found." });
312
+ const _ = D.exists ? D.data() || {} : {};
216
313
  return {
217
- refundFlow: M.flow || {},
218
- refundFlowEnabled: M.enabled !== !1,
219
- refundDialog: i(M.dialog)
314
+ refundFlow: _.flow || {},
315
+ refundFlowEnabled: _.enabled !== !1,
316
+ refundDialog: u(_.dialog)
220
317
  };
221
- } catch (o) {
222
- throw console.error("Appointments handler — refund flow load error:", o), p({
223
- statusCode: o.statusCode || 500,
224
- statusMessage: o.statusMessage || "Failed to load refund flow"
318
+ } catch (y) {
319
+ throw console.error("Appointments handler — refund flow load error:", y), g({
320
+ statusCode: y.statusCode || 500,
321
+ statusMessage: y.statusMessage || "Failed to load refund flow"
225
322
  });
226
323
  }
227
- })), u.patch("/agendas/:agendaId/refund-flow", h(async (e) => {
228
- const n = F(e, "agendaId");
229
- if (!n)
230
- throw p({ statusCode: 400, statusMessage: "Agenda ID is required." });
324
+ })), n.patch("/agendas/:agendaId/refund-flow", b(async (m) => {
325
+ const h = U(m, "agendaId");
326
+ if (!h)
327
+ throw g({ statusCode: 400, statusMessage: "Agenda ID is required." });
231
328
  try {
232
- const o = await O(e), { firebase: d, agendaCollection: g } = await t(e), m = d.firestore(), w = m.collection(g).doc(n), C = w.collection(l).doc(y), R = i(o?.refundDialog);
233
- if (!(await w.get()).exists)
234
- throw p({ statusCode: 404, statusMessage: "Agenda not found." });
235
- const k = m.batch();
236
- return k.set(C, {
237
- flow: o?.refundFlow || {},
238
- enabled: o?.refundFlowEnabled !== !1,
239
- dialog: R,
329
+ const y = await j(m), { firebase: w, agendaCollection: C } = await o(m), A = w.firestore(), I = A.collection(C).doc(h), M = I.collection(a).doc(c), D = u(y?.refundDialog);
330
+ if (!(await I.get()).exists)
331
+ throw g({ statusCode: 404, statusMessage: "Agenda not found." });
332
+ const T = A.batch();
333
+ return T.set(M, {
334
+ flow: y?.refundFlow || {},
335
+ enabled: y?.refundFlowEnabled !== !1,
336
+ dialog: D,
240
337
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
241
- }, { merge: !0 }), k.set(w, s(), { merge: !0 }), await k.commit(), { success: !0 };
242
- } catch (o) {
243
- throw console.error("Appointments handler — refund flow save error:", o), p({
244
- statusCode: o.statusCode || 500,
245
- statusMessage: o.statusMessage || "Failed to save refund flow"
338
+ }, { merge: !0 }), T.set(I, l(), { merge: !0 }), await T.commit(), { success: !0 };
339
+ } catch (y) {
340
+ throw console.error("Appointments handler — refund flow save error:", y), g({
341
+ statusCode: y.statusCode || 500,
342
+ statusMessage: y.statusMessage || "Failed to save refund flow"
246
343
  });
247
344
  }
248
- })), u.get("/agendas/:agendaId/metadata-schema", h(async (e) => {
249
- const n = F(e, "agendaId");
250
- if (!n)
251
- throw p({ statusCode: 400, statusMessage: "Agenda ID is required." });
345
+ })), n.get("/agendas/:agendaId/metadata-schema", b(async (m) => {
346
+ const h = U(m, "agendaId");
347
+ if (!h)
348
+ throw g({ statusCode: 400, statusMessage: "Agenda ID is required." });
252
349
  try {
253
- const { firebase: o, agendaCollection: d } = await t(e), m = o.firestore().collection(d).doc(n), w = m.collection(l).doc(D), [C, R] = await Promise.all([
254
- m.get(),
255
- w.get()
350
+ const { firebase: y, agendaCollection: w } = await o(m), A = y.firestore().collection(w).doc(h), I = A.collection(a).doc(i), [M, D] = await Promise.all([
351
+ A.get(),
352
+ I.get()
256
353
  ]);
257
- if (!C.exists)
258
- throw p({ statusCode: 404, statusMessage: "Agenda not found." });
259
- const M = R.exists ? R.data() || {} : {};
354
+ if (!M.exists)
355
+ throw g({ statusCode: 404, statusMessage: "Agenda not found." });
356
+ const _ = D.exists ? D.data() || {} : {};
260
357
  return {
261
- metadataSchema: a(M.schema)
358
+ metadataSchema: f(_.schema)
262
359
  };
263
- } catch (o) {
264
- throw console.error("Appointments handler — metadata schema load error:", o), p({
265
- statusCode: o.statusCode || 500,
266
- statusMessage: o.statusMessage || "Failed to load metadata schema"
360
+ } catch (y) {
361
+ throw console.error("Appointments handler — metadata schema load error:", y), g({
362
+ statusCode: y.statusCode || 500,
363
+ statusMessage: y.statusMessage || "Failed to load metadata schema"
267
364
  });
268
365
  }
269
- })), u.patch("/agendas/:agendaId/metadata-schema", h(async (e) => {
270
- const n = F(e, "agendaId");
271
- if (!n)
272
- throw p({ statusCode: 400, statusMessage: "Agenda ID is required." });
366
+ })), n.patch("/agendas/:agendaId/metadata-schema", b(async (m) => {
367
+ const h = U(m, "agendaId");
368
+ if (!h)
369
+ throw g({ statusCode: 400, statusMessage: "Agenda ID is required." });
273
370
  try {
274
- const o = await O(e), { firebase: d, agendaCollection: g } = await t(e), m = d.firestore(), w = m.collection(g).doc(n), C = w.collection(l).doc(D);
275
- if (!(await w.get()).exists)
276
- throw p({ statusCode: 404, statusMessage: "Agenda not found." });
277
- const M = m.batch();
278
- return M.set(C, {
279
- schema: a(o?.metadataSchema),
371
+ const y = await j(m), { firebase: w, agendaCollection: C } = await o(m), A = w.firestore(), I = A.collection(C).doc(h), M = I.collection(a).doc(i);
372
+ if (!(await I.get()).exists)
373
+ throw g({ statusCode: 404, statusMessage: "Agenda not found." });
374
+ const _ = A.batch();
375
+ return _.set(M, {
376
+ schema: f(y?.metadataSchema),
280
377
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
281
- }, { merge: !0 }), M.set(w, r(), { merge: !0 }), await M.commit(), { success: !0 };
282
- } catch (o) {
283
- throw console.error("Appointments handler — metadata schema save error:", o), p({
284
- statusCode: o.statusCode || 500,
285
- statusMessage: o.statusMessage || "Failed to save metadata schema"
378
+ }, { merge: !0 }), _.set(I, p(), { merge: !0 }), await _.commit(), { success: !0 };
379
+ } catch (y) {
380
+ throw console.error("Appointments handler — metadata schema save error:", y), g({
381
+ statusCode: y.statusCode || 500,
382
+ statusMessage: y.statusMessage || "Failed to save metadata schema"
286
383
  });
287
384
  }
288
- })), u.get("/appointments", h(async (e) => {
289
- const n = I(e), o = Math.min(Number(n.quantity) || 20, 100), d = n.status || "confirmed";
385
+ })), n.get("/appointments/summary", b(async (m) => {
386
+ const { firebase: h, reservationsCollection: y, projectId: w } = await o(m), { data: C, error: A } = await m.context.module.supabase.from("projects").select("timezone").eq("id", w).single();
387
+ if (A || !C)
388
+ throw g({ statusCode: 500, statusMessage: "Failed to load the booking project timezone." });
389
+ return us(h.firestore(), y, v(m), C.timezone || "UTC");
390
+ })), n.get("/appointments", b(async (m) => {
391
+ const h = v(m), y = Math.min(Number(h.quantity) || 20, 100), w = h.status || "confirmed";
290
392
  try {
291
- const { firebase: g, reservationsCollection: m } = await t(e), C = (await g.firestore().collection(m).orderBy("createdAt", "desc").where("status", "==", d).limit(o).get()).docs.flatMap(
292
- (R) => c(S({ id: R.id, ...R.data() }))
393
+ const { firebase: C, reservationsCollection: A } = await o(m), M = (await C.firestore().collection(A).orderBy("createdAt", "desc").where("status", "==", w).limit(y).get()).docs.flatMap(
394
+ (D) => d(s({ id: D.id, ...D.data() }))
293
395
  );
294
396
  return {
295
- count: C.length,
296
- appointments: C
397
+ count: M.length,
398
+ appointments: M
297
399
  };
298
- } catch (g) {
299
- throw console.error("Appointments handler — list error:", g), p({
300
- statusCode: g.statusCode || 500,
301
- statusMessage: g.statusMessage || "Failed to list appointments"
400
+ } catch (C) {
401
+ throw console.error("Appointments handler — list error:", C), g({
402
+ statusCode: C.statusCode || 500,
403
+ statusMessage: C.statusMessage || "Failed to list appointments"
302
404
  });
303
405
  }
304
- })), u.get("/appointments/find", h(async (e) => {
305
- const n = I(e), o = (n.name || "").toLowerCase().trim(), d = (n.email || "").toLowerCase().trim(), g = n.date;
406
+ })), n.get("/appointments/find", b(async (m) => {
407
+ const h = v(m), y = (h.name || "").toLowerCase().trim(), w = (h.email || "").toLowerCase().trim(), C = h.date;
306
408
  try {
307
- const { firebase: m, reservationsCollection: w } = await t(e);
308
- let C = m.firestore().collection(w);
309
- d && (C = C.where("customerInfo.email", "==", d));
310
- let M = (await C.orderBy("createdAt", "desc").limit(200).get()).docs.flatMap(
311
- (k) => c(S({ id: k.id, ...k.data() }))
409
+ const { firebase: A, reservationsCollection: I } = await o(m);
410
+ let M = A.firestore().collection(I);
411
+ w && (M = M.where("customerInfo.email", "==", w));
412
+ let _ = (await M.orderBy("createdAt", "desc").limit(200).get()).docs.flatMap(
413
+ (T) => d(s({ id: T.id, ...T.data() }))
312
414
  );
313
- return g && (M = M.filter((k) => k.date === g)), o && (M = M.filter((k) => {
314
- const q = (k.customerInfo?.firstName || "").toLowerCase(), N = (k.customerInfo?.lastName || "").toLowerCase();
315
- return q.includes(o) || N.includes(o) || `${q} ${N}`.includes(o);
415
+ return C && (_ = _.filter((T) => T.date === C)), y && (_ = _.filter((T) => {
416
+ const E = (T.customerInfo?.firstName || "").toLowerCase(), V = (T.customerInfo?.lastName || "").toLowerCase();
417
+ return E.includes(y) || V.includes(y) || `${E} ${V}`.includes(y);
316
418
  })), {
317
- count: M.length,
318
- appointments: M
419
+ count: _.length,
420
+ appointments: _
319
421
  };
320
- } catch (m) {
321
- throw console.error("Appointments handler — find error:", m), p({
322
- statusCode: m.statusCode || 500,
323
- statusMessage: m.statusMessage || "Failed to find appointments"
422
+ } catch (A) {
423
+ throw console.error("Appointments handler — find error:", A), g({
424
+ statusCode: A.statusCode || 500,
425
+ statusMessage: A.statusMessage || "Failed to find appointments"
324
426
  });
325
427
  }
326
- })), u.get("/appointments/:appointmentId", h(async (e) => {
327
- const n = F(e, "appointmentId");
328
- if (!n)
329
- throw p({ statusCode: 400, statusMessage: "Appointment ID is required." });
428
+ })), n.get("/appointments/:appointmentId", b(async (m) => {
429
+ const h = U(m, "appointmentId");
430
+ if (!h)
431
+ throw g({ statusCode: 400, statusMessage: "Appointment ID is required." });
330
432
  try {
331
- const { firebase: o, reservationsCollection: d } = await t(e), g = await o.firestore().collection(d).doc(n).get();
332
- if (!g.exists)
333
- throw p({ statusCode: 404, statusMessage: "Appointment not found." });
334
- const m = S({ id: g.id, ...g.data() });
433
+ const { firebase: y, reservationsCollection: w } = await o(m), C = await y.firestore().collection(w).doc(h).get();
434
+ if (!C.exists)
435
+ throw g({ statusCode: 404, statusMessage: "Appointment not found." });
436
+ const A = s({ id: C.id, ...C.data() });
335
437
  return {
336
- ...m,
337
- reservations: c(m)
438
+ ...A,
439
+ reservations: d(A)
338
440
  };
339
- } catch (o) {
340
- throw console.error("Appointments handler — get error:", o), p({
341
- statusCode: o.statusCode || 500,
342
- statusMessage: o.statusMessage || "Failed to get appointment"
441
+ } catch (y) {
442
+ throw console.error("Appointments handler — get error:", y), g({
443
+ statusCode: y.statusCode || 500,
444
+ statusMessage: y.statusMessage || "Failed to get appointment"
343
445
  });
344
446
  }
345
- })), u.handler;
447
+ })), n.handler;
346
448
  }
347
- const x = "https://analyticsdata.googleapis.com/v1beta", H = [
449
+ const lt = "https://analyticsdata.googleapis.com/v1beta", ls = [
348
450
  "https://www.googleapis.com/auth/analytics.readonly",
349
451
  "https://www.googleapis.com/auth/webmasters.readonly"
350
- ], B = "https://searchconsole.googleapis.com/webmasters/v3";
351
- function W(A) {
352
- const { decrypt: P, getGoogleAccessToken: S } = A, f = E();
353
- async function u(t) {
354
- const { supabase: c, instanceId: i } = t.context.module, { data: s, error: a } = await c.from("project_modules").select("config").eq("id", i).single();
355
- if (a || !s?.config)
356
- throw p({ statusCode: 500, statusMessage: "Failed to load module config." });
357
- const r = s.config, e = r.propertyId, n = r.serviceAccount, o = r.siteUrl || "";
358
- if (!n)
359
- throw p({ statusCode: 400, statusMessage: "No Google Service Account configured for this module." });
360
- if (!e)
361
- throw p({ statusCode: 400, statusMessage: "No GA4 Property ID configured for this module." });
362
- const { data: d, error: g } = await c.from("integrations").select("config").eq("id", n).single();
363
- let m = d?.config;
364
- if (g || !m) {
365
- const { data: k, error: q } = await c.from("agency_integrations").select("config").eq("id", n).single();
366
- if (q || !k?.config)
367
- throw p({ statusCode: 500, statusMessage: "Failed to load Google Service Account credentials." });
368
- m = k.config;
452
+ ], mt = "https://searchconsole.googleapis.com/webmasters/v3";
453
+ function Da(e) {
454
+ const { decrypt: t, getGoogleAccessToken: s } = e, r = J();
455
+ async function n(o) {
456
+ const { supabase: d, instanceId: u } = o.context.module, { data: l, error: f } = await d.from("project_modules").select("config").eq("id", u).single();
457
+ if (f || !l?.config)
458
+ throw g({ statusCode: 500, statusMessage: "Failed to load module config." });
459
+ const p = l.config, m = p.propertyId, h = p.serviceAccount, y = p.siteUrl || "";
460
+ if (!h)
461
+ throw g({ statusCode: 400, statusMessage: "No Google Service Account configured for this module." });
462
+ if (!m)
463
+ throw g({ statusCode: 400, statusMessage: "No GA4 Property ID configured for this module." });
464
+ const { data: w, error: C } = await d.from("integrations").select("config").eq("id", h).single();
465
+ let A = w?.config;
466
+ if (C || !A) {
467
+ const { data: T, error: E } = await d.from("agency_integrations").select("config").eq("id", h).single();
468
+ if (E || !T?.config)
469
+ throw g({ statusCode: 500, statusMessage: "Failed to load Google Service Account credentials." });
470
+ A = T.config;
369
471
  }
370
- const w = P(m.clientEmail), C = P(m.privateKey), R = P(m.projectId);
371
- return { accessToken: await S(
372
- { clientEmail: w, privateKey: C, projectId: R },
373
- H
374
- ), propertyId: e, siteUrl: o };
472
+ const I = t(A.clientEmail), M = t(A.privateKey), D = t(A.projectId);
473
+ return { accessToken: await s(
474
+ { clientEmail: I, privateKey: M, projectId: D },
475
+ ls
476
+ ), propertyId: m, siteUrl: y };
375
477
  }
376
- async function l(t, c, i) {
377
- const s = await fetch(`${x}/properties/${c}:runReport`, {
478
+ async function a(o, d, u) {
479
+ const l = await fetch(`${lt}/properties/${d}:runReport`, {
378
480
  method: "POST",
379
481
  headers: {
380
- Authorization: `Bearer ${t}`,
482
+ Authorization: `Bearer ${o}`,
381
483
  "Content-Type": "application/json"
382
484
  },
383
- body: JSON.stringify(i)
485
+ body: JSON.stringify(u)
384
486
  });
385
- if (!s.ok) {
386
- const a = await s.json().catch(() => ({}));
387
- throw console.error("GA4 runReport failed:", a), p({
388
- statusCode: s.status,
389
- statusMessage: a?.error?.message || "GA4 API request failed"
487
+ if (!l.ok) {
488
+ const f = await l.json().catch(() => ({}));
489
+ throw console.error("GA4 runReport failed:", f), g({
490
+ statusCode: l.status,
491
+ statusMessage: f?.error?.message || "GA4 API request failed"
390
492
  });
391
493
  }
392
- return s.json();
494
+ return l.json();
393
495
  }
394
- f.get(
496
+ r.get(
395
497
  "/report",
396
- h(async (t) => {
397
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = a.match(/^(\d+)daysAgo$/), n = e ? parseInt(e[1], 10) : 30, o = `${n * 2}daysAgo`, d = `${n + 1}daysAgo`, g = [
498
+ b(async (o) => {
499
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = f.match(/^(\d+)daysAgo$/), h = m ? parseInt(m[1], 10) : 30, y = `${h * 2}daysAgo`, w = `${h + 1}daysAgo`, C = [
398
500
  { name: "sessions" },
399
501
  { name: "totalUsers" },
400
502
  { name: "screenPageViews" },
@@ -405,38 +507,38 @@ function W(A) {
405
507
  { name: "sessionsPerUser" },
406
508
  { name: "screenPageViewsPerSession" }
407
509
  ];
408
- let m, w = !0;
510
+ let A, I = !0;
409
511
  try {
410
- m = await l(c, i, {
512
+ A = await a(d, u, {
411
513
  dateRanges: [
412
- { startDate: a, endDate: r, name: "current" },
413
- { startDate: o, endDate: d, name: "previous" }
514
+ { startDate: f, endDate: p, name: "current" },
515
+ { startDate: y, endDate: w, name: "previous" }
414
516
  ],
415
517
  dimensions: [{ name: "date" }],
416
- metrics: g,
518
+ metrics: C,
417
519
  metricAggregations: ["TOTAL"],
418
520
  orderBys: [{ dimension: { dimensionName: "date" } }]
419
521
  });
420
522
  } catch {
421
- w = !1, m = await l(c, i, {
422
- dateRanges: [{ startDate: a, endDate: r }],
523
+ I = !1, A = await a(d, u, {
524
+ dateRanges: [{ startDate: f, endDate: p }],
423
525
  dimensions: [{ name: "date" }],
424
- metrics: g,
526
+ metrics: C,
425
527
  metricAggregations: ["TOTAL"],
426
528
  orderBys: [{ dimension: { dimensionName: "date" } }]
427
529
  });
428
530
  }
429
- return L(m, w);
531
+ return ms(A, I);
430
532
  })
431
- ), f.get(
533
+ ), r.get(
432
534
  "/realtime",
433
- h(async (t) => {
434
- const { accessToken: c, propertyId: i } = await u(t), s = await fetch(
435
- `${x}/properties/${i}:runRealtimeReport`,
535
+ b(async (o) => {
536
+ const { accessToken: d, propertyId: u } = await n(o), l = await fetch(
537
+ `${lt}/properties/${u}:runRealtimeReport`,
436
538
  {
437
539
  method: "POST",
438
540
  headers: {
439
- Authorization: `Bearer ${c}`,
541
+ Authorization: `Bearer ${d}`,
440
542
  "Content-Type": "application/json"
441
543
  },
442
544
  body: JSON.stringify({
@@ -447,24 +549,24 @@ function W(A) {
447
549
  })
448
550
  }
449
551
  );
450
- if (!s.ok) {
451
- const n = await s.json().catch(() => ({}));
452
- throw p({ statusCode: s.status, statusMessage: n?.error?.message || "Realtime API failed" });
552
+ if (!l.ok) {
553
+ const h = await l.json().catch(() => ({}));
554
+ throw g({ statusCode: l.status, statusMessage: h?.error?.message || "Realtime API failed" });
453
555
  }
454
- const a = await s.json(), r = (a?.rows || []).reduce(
455
- (n, o) => n + parseInt(o.metricValues?.[0]?.value || "0", 10),
556
+ const f = await l.json(), p = (f?.rows || []).reduce(
557
+ (h, y) => h + parseInt(y.metricValues?.[0]?.value || "0", 10),
456
558
  0
457
- ), e = (a?.rows || []).map((n) => ({
458
- page: n.dimensionValues?.[0]?.value || "(not set)",
459
- activeUsers: parseInt(n.metricValues?.[0]?.value || "0", 10)
559
+ ), m = (f?.rows || []).map((h) => ({
560
+ page: h.dimensionValues?.[0]?.value || "(not set)",
561
+ activeUsers: parseInt(h.metricValues?.[0]?.value || "0", 10)
460
562
  }));
461
- return { activeUsers: r, activePages: e };
563
+ return { activeUsers: p, activePages: m };
462
564
  })
463
- ), f.get(
565
+ ), r.get(
464
566
  "/top-pages",
465
- h(async (t) => {
466
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = parseInt(s.limit || "10", 10), n = await l(c, i, {
467
- dateRanges: [{ startDate: a, endDate: r }],
567
+ b(async (o) => {
568
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = parseInt(l.limit || "10", 10), h = await a(d, u, {
569
+ dateRanges: [{ startDate: f, endDate: p }],
468
570
  dimensions: [{ name: "pagePath" }],
469
571
  metrics: [
470
572
  { name: "screenPageViews" },
@@ -472,25 +574,25 @@ function W(A) {
472
574
  { name: "averageSessionDuration" }
473
575
  ],
474
576
  orderBys: [{ metric: { metricName: "screenPageViews" }, desc: !0 }],
475
- limit: e * 2
577
+ limit: m * 2
476
578
  // fetch extra to account for duplicates before aggregation
477
- }), o = b(n, "pagePath"), d = /* @__PURE__ */ new Map();
478
- for (const m of o.rows) {
479
- const w = m.pagePath.toLowerCase().replace(/\/+$/, ""), C = d.get(w);
480
- if (C) {
481
- C.screenPageViews += m.screenPageViews || 0, C.totalUsers += m.totalUsers || 0;
482
- const R = C.screenPageViews;
483
- R > 0 && (C.averageSessionDuration = (C.averageSessionDuration * (R - (m.screenPageViews || 0)) + (m.averageSessionDuration || 0) * (m.screenPageViews || 0)) / R);
579
+ }), y = k(h, "pagePath"), w = /* @__PURE__ */ new Map();
580
+ for (const A of y.rows) {
581
+ const I = A.pagePath.toLowerCase().replace(/\/+$/, ""), M = w.get(I);
582
+ if (M) {
583
+ M.screenPageViews += A.screenPageViews || 0, M.totalUsers += A.totalUsers || 0;
584
+ const D = M.screenPageViews;
585
+ D > 0 && (M.averageSessionDuration = (M.averageSessionDuration * (D - (A.screenPageViews || 0)) + (A.averageSessionDuration || 0) * (A.screenPageViews || 0)) / D);
484
586
  } else
485
- d.set(w, { ...m });
587
+ w.set(I, { ...A });
486
588
  }
487
- return { rows: [...d.values()].sort((m, w) => (w.screenPageViews || 0) - (m.screenPageViews || 0)).slice(0, e), rowCount: o.rowCount };
589
+ return { rows: [...w.values()].sort((A, I) => (I.screenPageViews || 0) - (A.screenPageViews || 0)).slice(0, m), rowCount: y.rowCount };
488
590
  })
489
- ), f.get(
591
+ ), r.get(
490
592
  "/top-sources",
491
- h(async (t) => {
492
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
493
- dateRanges: [{ startDate: a, endDate: r }],
593
+ b(async (o) => {
594
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
595
+ dateRanges: [{ startDate: f, endDate: p }],
494
596
  dimensions: [{ name: "sessionSource" }],
495
597
  metrics: [
496
598
  { name: "sessions" },
@@ -499,13 +601,13 @@ function W(A) {
499
601
  orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
500
602
  limit: 10
501
603
  });
502
- return b(e, "sessionSource");
604
+ return k(m, "sessionSource");
503
605
  })
504
- ), f.get(
606
+ ), r.get(
505
607
  "/devices",
506
- h(async (t) => {
507
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
508
- dateRanges: [{ startDate: a, endDate: r }],
608
+ b(async (o) => {
609
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
610
+ dateRanges: [{ startDate: f, endDate: p }],
509
611
  dimensions: [{ name: "deviceCategory" }],
510
612
  metrics: [
511
613
  { name: "sessions" },
@@ -513,13 +615,13 @@ function W(A) {
513
615
  ],
514
616
  orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }]
515
617
  });
516
- return b(e, "deviceCategory");
618
+ return k(m, "deviceCategory");
517
619
  })
518
- ), f.get(
620
+ ), r.get(
519
621
  "/countries",
520
- h(async (t) => {
521
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
522
- dateRanges: [{ startDate: a, endDate: r }],
622
+ b(async (o) => {
623
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
624
+ dateRanges: [{ startDate: f, endDate: p }],
523
625
  dimensions: [{ name: "country" }],
524
626
  metrics: [
525
627
  { name: "sessions" },
@@ -528,13 +630,13 @@ function W(A) {
528
630
  orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
529
631
  limit: 10
530
632
  });
531
- return b(e, "country");
633
+ return k(m, "country");
532
634
  })
533
- ), f.get(
635
+ ), r.get(
534
636
  "/acquisition/channels",
535
- h(async (t) => {
536
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
537
- dateRanges: [{ startDate: a, endDate: r }],
637
+ b(async (o) => {
638
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
639
+ dateRanges: [{ startDate: f, endDate: p }],
538
640
  dimensions: [{ name: "sessionDefaultChannelGroup" }],
539
641
  metrics: [
540
642
  { name: "sessions" },
@@ -548,13 +650,13 @@ function W(A) {
548
650
  orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
549
651
  limit: 15
550
652
  });
551
- return b(e, "sessionDefaultChannelGroup");
653
+ return k(m, "sessionDefaultChannelGroup");
552
654
  })
553
- ), f.get(
655
+ ), r.get(
554
656
  "/acquisition/source-medium",
555
- h(async (t) => {
556
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
557
- dateRanges: [{ startDate: a, endDate: r }],
657
+ b(async (o) => {
658
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
659
+ dateRanges: [{ startDate: f, endDate: p }],
558
660
  dimensions: [{ name: "sessionSourceMedium" }],
559
661
  metrics: [
560
662
  { name: "sessions" },
@@ -567,13 +669,13 @@ function W(A) {
567
669
  orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
568
670
  limit: 20
569
671
  });
570
- return b(e, "sessionSourceMedium");
672
+ return k(m, "sessionSourceMedium");
571
673
  })
572
- ), f.get(
674
+ ), r.get(
573
675
  "/acquisition/referrals",
574
- h(async (t) => {
575
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
576
- dateRanges: [{ startDate: a, endDate: r }],
676
+ b(async (o) => {
677
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
678
+ dateRanges: [{ startDate: f, endDate: p }],
577
679
  dimensions: [{ name: "sessionSource" }],
578
680
  dimensionFilter: {
579
681
  filter: {
@@ -590,13 +692,13 @@ function W(A) {
590
692
  orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
591
693
  limit: 20
592
694
  });
593
- return b(e, "sessionSource");
695
+ return k(m, "sessionSource");
594
696
  })
595
- ), f.get(
697
+ ), r.get(
596
698
  "/acquisition/campaigns",
597
- h(async (t) => {
598
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
599
- dateRanges: [{ startDate: a, endDate: r }],
699
+ b(async (o) => {
700
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
701
+ dateRanges: [{ startDate: f, endDate: p }],
600
702
  dimensions: [{ name: "sessionCampaignName" }],
601
703
  dimensionFilter: {
602
704
  notExpression: {
@@ -615,13 +717,13 @@ function W(A) {
615
717
  orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
616
718
  limit: 20
617
719
  });
618
- return b(e, "sessionCampaignName");
720
+ return k(m, "sessionCampaignName");
619
721
  })
620
- ), f.get(
722
+ ), r.get(
621
723
  "/content/all-pages",
622
- h(async (t) => {
623
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = parseInt(s.limit || "50", 10), n = await l(c, i, {
624
- dateRanges: [{ startDate: a, endDate: r }],
724
+ b(async (o) => {
725
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = parseInt(l.limit || "50", 10), h = await a(d, u, {
726
+ dateRanges: [{ startDate: f, endDate: p }],
625
727
  dimensions: [{ name: "pagePath" }],
626
728
  metrics: [
627
729
  { name: "screenPageViews" },
@@ -633,15 +735,15 @@ function W(A) {
633
735
  { name: "userEngagementDuration" }
634
736
  ],
635
737
  orderBys: [{ metric: { metricName: "screenPageViews" }, desc: !0 }],
636
- limit: e
738
+ limit: m
637
739
  });
638
- return b(n, "pagePath");
740
+ return k(h, "pagePath");
639
741
  })
640
- ), f.get(
742
+ ), r.get(
641
743
  "/content/landing-pages",
642
- h(async (t) => {
643
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
644
- dateRanges: [{ startDate: a, endDate: r }],
744
+ b(async (o) => {
745
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
746
+ dateRanges: [{ startDate: f, endDate: p }],
645
747
  dimensions: [{ name: "landingPagePlusQueryString" }],
646
748
  metrics: [
647
749
  { name: "sessions" },
@@ -654,13 +756,13 @@ function W(A) {
654
756
  orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
655
757
  limit: 30
656
758
  });
657
- return b(e, "landingPagePlusQueryString");
759
+ return k(m, "landingPagePlusQueryString");
658
760
  })
659
- ), f.get(
761
+ ), r.get(
660
762
  "/content/exit-pages",
661
- h(async (t) => {
662
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
663
- dateRanges: [{ startDate: a, endDate: r }],
763
+ b(async (o) => {
764
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
765
+ dateRanges: [{ startDate: f, endDate: p }],
664
766
  dimensions: [{ name: "pagePath" }],
665
767
  metrics: [
666
768
  { name: "sessions" },
@@ -670,60 +772,60 @@ function W(A) {
670
772
  ],
671
773
  orderBys: [{ metric: { metricName: "screenPageViews" }, desc: !0 }],
672
774
  limit: 20
673
- }), n = b(e, "pagePath");
674
- return n.rows = n.rows.map((o) => ({
675
- ...o,
676
- exitRate: o.bounceRate || 0
677
- })), n;
775
+ }), h = k(m, "pagePath");
776
+ return h.rows = h.rows.map((y) => ({
777
+ ...y,
778
+ exitRate: y.bounceRate || 0
779
+ })), h;
678
780
  })
679
- ), f.get(
781
+ ), r.get(
680
782
  "/content/search-terms",
681
- h(async (t) => {
682
- const { accessToken: c, propertyId: i, siteUrl: s } = await u(t), a = I(t), r = a.startDate || "30daysAgo", e = a.endDate || "today";
683
- if (s)
783
+ b(async (o) => {
784
+ const { accessToken: d, propertyId: u, siteUrl: l } = await n(o), f = v(o), p = f.startDate || "30daysAgo", m = f.endDate || "today";
785
+ if (l)
684
786
  try {
685
- const d = `${B}/sites/${encodeURIComponent(s)}/searchAnalytics/query`, g = await fetch(
686
- d,
787
+ const w = `${mt}/sites/${encodeURIComponent(l)}/searchAnalytics/query`, C = await fetch(
788
+ w,
687
789
  {
688
790
  method: "POST",
689
791
  headers: {
690
- Authorization: `Bearer ${c}`,
792
+ Authorization: `Bearer ${d}`,
691
793
  "Content-Type": "application/json"
692
794
  },
693
795
  body: JSON.stringify({
694
- startDate: y(r),
695
- endDate: y(e),
796
+ startDate: c(p),
797
+ endDate: c(m),
696
798
  dimensions: ["query"],
697
799
  rowLimit: 30
698
800
  })
699
801
  }
700
802
  );
701
- if (g.ok) {
702
- const w = ((await g.json()).rows || []).map((C) => ({
703
- query: C.keys[0],
704
- clicks: C.clicks,
705
- impressions: C.impressions,
706
- ctr: C.ctr,
707
- position: C.position
803
+ if (C.ok) {
804
+ const I = ((await C.json()).rows || []).map((M) => ({
805
+ query: M.keys[0],
806
+ clicks: M.clicks,
807
+ impressions: M.impressions,
808
+ ctr: M.ctr,
809
+ position: M.position
708
810
  }));
709
- if (w.length > 0)
811
+ if (I.length > 0)
710
812
  return {
711
- rows: w,
712
- rowCount: w.length,
813
+ rows: I,
814
+ rowCount: I.length,
713
815
  source: "search_console"
714
816
  };
715
817
  } else {
716
- const m = await g.text().catch(() => "");
717
- console.error(`[GA Module] Search Console API error (${g.status}):`, m);
818
+ const A = await C.text().catch(() => "");
819
+ console.error(`[GA Module] Search Console API error (${C.status}):`, A);
718
820
  }
719
- } catch (d) {
720
- console.error("[GA Module] Search Console request failed:", d?.message || d);
821
+ } catch (w) {
822
+ console.error("[GA Module] Search Console request failed:", w?.message || w);
721
823
  }
722
824
  else
723
825
  console.log("[GA Module] No siteUrl configured, skipping Search Console");
724
826
  try {
725
- const d = await l(c, i, {
726
- dateRanges: [{ startDate: r, endDate: e }],
827
+ const w = await a(d, u, {
828
+ dateRanges: [{ startDate: p, endDate: m }],
727
829
  dimensions: [{ name: "sessionGoogleAdsQuery" }],
728
830
  metrics: [
729
831
  { name: "sessions" },
@@ -754,13 +856,13 @@ function W(A) {
754
856
  },
755
857
  orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
756
858
  limit: 30
757
- }), g = b(d, "sessionGoogleAdsQuery");
758
- if (g.rows.length > 0)
759
- return { ...g, source: "google_ads" };
859
+ }), C = k(w, "sessionGoogleAdsQuery");
860
+ if (C.rows.length > 0)
861
+ return { ...C, source: "google_ads" };
760
862
  } catch {
761
863
  }
762
- const n = await l(c, i, {
763
- dateRanges: [{ startDate: r, endDate: e }],
864
+ const h = await a(d, u, {
865
+ dateRanges: [{ startDate: p, endDate: m }],
764
866
  dimensions: [{ name: "landingPagePlusQueryString" }],
765
867
  metrics: [
766
868
  { name: "sessions" },
@@ -793,13 +895,13 @@ function W(A) {
793
895
  orderBys: [{ metric: { metricName: "sessions" }, desc: !0 }],
794
896
  limit: 30
795
897
  });
796
- return { ...b(n, "landingPagePlusQueryString"), source: "organic_landing_pages" };
898
+ return { ...k(h, "landingPagePlusQueryString"), source: "organic_landing_pages" };
797
899
  })
798
- ), f.get(
900
+ ), r.get(
799
901
  "/audience/overview",
800
- h(async (t) => {
801
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
802
- dateRanges: [{ startDate: a, endDate: r }],
902
+ b(async (o) => {
903
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
904
+ dateRanges: [{ startDate: f, endDate: p }],
803
905
  dimensions: [{ name: "newVsReturning" }],
804
906
  metrics: [
805
907
  { name: "totalUsers" },
@@ -810,14 +912,14 @@ function W(A) {
810
912
  ],
811
913
  metricAggregations: ["TOTAL"]
812
914
  });
813
- return b(e, "newVsReturning");
915
+ return k(m, "newVsReturning");
814
916
  })
815
- ), f.get(
917
+ ), r.get(
816
918
  "/audience/technology",
817
- h(async (t) => {
818
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = s.dimension || "browser", o = ["browser", "operatingSystem", "screenResolution"].includes(e) ? e : "browser", d = await l(c, i, {
819
- dateRanges: [{ startDate: a, endDate: r }],
820
- dimensions: [{ name: o }],
919
+ b(async (o) => {
920
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = l.dimension || "browser", y = ["browser", "operatingSystem", "screenResolution"].includes(m) ? m : "browser", w = await a(d, u, {
921
+ dateRanges: [{ startDate: f, endDate: p }],
922
+ dimensions: [{ name: y }],
821
923
  metrics: [
822
924
  { name: "totalUsers" },
823
925
  { name: "sessions" },
@@ -826,13 +928,13 @@ function W(A) {
826
928
  orderBys: [{ metric: { metricName: "totalUsers" }, desc: !0 }],
827
929
  limit: 10
828
930
  });
829
- return b(d, o);
931
+ return k(w, y);
830
932
  })
831
- ), f.get(
933
+ ), r.get(
832
934
  "/audience/languages",
833
- h(async (t) => {
834
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
835
- dateRanges: [{ startDate: a, endDate: r }],
935
+ b(async (o) => {
936
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
937
+ dateRanges: [{ startDate: f, endDate: p }],
836
938
  dimensions: [{ name: "language" }],
837
939
  metrics: [
838
940
  { name: "totalUsers" },
@@ -841,13 +943,13 @@ function W(A) {
841
943
  orderBys: [{ metric: { metricName: "totalUsers" }, desc: !0 }],
842
944
  limit: 15
843
945
  });
844
- return b(e, "language");
946
+ return k(m, "language");
845
947
  })
846
- ), f.get(
948
+ ), r.get(
847
949
  "/audience/hours",
848
- h(async (t) => {
849
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
850
- dateRanges: [{ startDate: a, endDate: r }],
950
+ b(async (o) => {
951
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
952
+ dateRanges: [{ startDate: f, endDate: p }],
851
953
  dimensions: [{ name: "dayOfWeekName" }, { name: "hour" }],
852
954
  metrics: [{ name: "sessions" }],
853
955
  orderBys: [
@@ -857,13 +959,13 @@ function W(A) {
857
959
  limit: 168
858
960
  // 7 days × 24 hours
859
961
  });
860
- return $(e, ["dayOfWeekName", "hour"]);
962
+ return ft(m, ["dayOfWeekName", "hour"]);
861
963
  })
862
- ), f.get(
964
+ ), r.get(
863
965
  "/audience/cities",
864
- h(async (t) => {
865
- const { accessToken: c, propertyId: i } = await u(t), s = I(t), a = s.startDate || "30daysAgo", r = s.endDate || "today", e = await l(c, i, {
866
- dateRanges: [{ startDate: a, endDate: r }],
966
+ b(async (o) => {
967
+ const { accessToken: d, propertyId: u } = await n(o), l = v(o), f = l.startDate || "30daysAgo", p = l.endDate || "today", m = await a(d, u, {
968
+ dateRanges: [{ startDate: f, endDate: p }],
867
969
  dimensions: [{ name: "city" }, { name: "country" }],
868
970
  metrics: [
869
971
  { name: "totalUsers" },
@@ -880,236 +982,2225 @@ function W(A) {
880
982
  orderBys: [{ metric: { metricName: "totalUsers" }, desc: !0 }],
881
983
  limit: 20
882
984
  });
883
- return $(e, ["city", "country"]);
985
+ return ft(m, ["city", "country"]);
884
986
  })
885
987
  );
886
- function y(t) {
887
- const c = t.match(/^(\d+)daysAgo$/);
888
- if (c) {
889
- const i = /* @__PURE__ */ new Date();
890
- return i.setDate(i.getDate() - parseInt(c[1], 10)), i.toISOString().slice(0, 10);
988
+ function c(o) {
989
+ const d = o.match(/^(\d+)daysAgo$/);
990
+ if (d) {
991
+ const u = /* @__PURE__ */ new Date();
992
+ return u.setDate(u.getDate() - parseInt(d[1], 10)), u.toISOString().slice(0, 10);
891
993
  }
892
- if (t === "today") return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
893
- if (t === "yesterday") {
894
- const i = /* @__PURE__ */ new Date();
895
- return i.setDate(i.getDate() - 1), i.toISOString().slice(0, 10);
994
+ if (o === "today") return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
995
+ if (o === "yesterday") {
996
+ const u = /* @__PURE__ */ new Date();
997
+ return u.setDate(u.getDate() - 1), u.toISOString().slice(0, 10);
896
998
  }
897
- return t;
999
+ return o;
898
1000
  }
899
- async function D(t, c, i) {
900
- const s = `${B}/sites/${encodeURIComponent(c)}/searchAnalytics/query`, a = await fetch(s, {
1001
+ async function i(o, d, u) {
1002
+ const l = `${mt}/sites/${encodeURIComponent(d)}/searchAnalytics/query`, f = await fetch(l, {
901
1003
  method: "POST",
902
1004
  headers: {
903
- Authorization: `Bearer ${t}`,
1005
+ Authorization: `Bearer ${o}`,
904
1006
  "Content-Type": "application/json"
905
1007
  },
906
- body: JSON.stringify(i)
1008
+ body: JSON.stringify(u)
907
1009
  });
908
- if (!a.ok) {
909
- const r = await a.json().catch(() => ({}));
910
- throw p({
911
- statusCode: a.status,
912
- statusMessage: r?.error?.message || "Search Console API request failed"
1010
+ if (!f.ok) {
1011
+ const p = await f.json().catch(() => ({}));
1012
+ throw g({
1013
+ statusCode: f.status,
1014
+ statusMessage: p?.error?.message || "Search Console API request failed"
913
1015
  });
914
1016
  }
915
- return a.json();
1017
+ return f.json();
916
1018
  }
917
- return f.get(
1019
+ return r.get(
918
1020
  "/seo/keywords",
919
- h(async (t) => {
920
- const { accessToken: c, siteUrl: i } = await u(t);
921
- if (!i)
922
- throw p({ statusCode: 400, statusMessage: "Search Console Site URL is not configured. Add it in module settings." });
923
- const s = I(t), a = y(s.startDate || "30daysAgo"), r = y(s.endDate || "today"), e = Math.min(parseInt(s.limit) || 50, 100), n = await D(c, i, {
924
- startDate: a,
925
- endDate: r,
1021
+ b(async (o) => {
1022
+ const { accessToken: d, siteUrl: u } = await n(o);
1023
+ if (!u)
1024
+ throw g({ statusCode: 400, statusMessage: "Search Console Site URL is not configured. Add it in module settings." });
1025
+ const l = v(o), f = c(l.startDate || "30daysAgo"), p = c(l.endDate || "today"), m = Math.min(parseInt(l.limit) || 50, 100), h = await i(d, u, {
1026
+ startDate: f,
1027
+ endDate: p,
926
1028
  dimensions: ["query"],
927
- rowLimit: e
1029
+ rowLimit: m
928
1030
  });
929
1031
  return {
930
- rows: (n.rows || []).map((o) => ({
931
- query: o.keys[0],
932
- clicks: o.clicks,
933
- impressions: o.impressions,
934
- ctr: o.ctr,
935
- position: o.position
1032
+ rows: (h.rows || []).map((y) => ({
1033
+ query: y.keys[0],
1034
+ clicks: y.clicks,
1035
+ impressions: y.impressions,
1036
+ ctr: y.ctr,
1037
+ position: y.position
936
1038
  })),
937
- rowCount: n.rows?.length || 0
1039
+ rowCount: h.rows?.length || 0
938
1040
  };
939
1041
  })
940
- ), f.get(
1042
+ ), r.get(
941
1043
  "/seo/pages",
942
- h(async (t) => {
943
- const { accessToken: c, siteUrl: i } = await u(t);
944
- if (!i)
945
- throw p({ statusCode: 400, statusMessage: "Search Console Site URL is not configured." });
946
- const s = I(t), a = y(s.startDate || "30daysAgo"), r = y(s.endDate || "today"), e = Math.min(parseInt(s.limit) || 50, 100), n = await D(c, i, {
947
- startDate: a,
948
- endDate: r,
1044
+ b(async (o) => {
1045
+ const { accessToken: d, siteUrl: u } = await n(o);
1046
+ if (!u)
1047
+ throw g({ statusCode: 400, statusMessage: "Search Console Site URL is not configured." });
1048
+ const l = v(o), f = c(l.startDate || "30daysAgo"), p = c(l.endDate || "today"), m = Math.min(parseInt(l.limit) || 50, 100), h = await i(d, u, {
1049
+ startDate: f,
1050
+ endDate: p,
949
1051
  dimensions: ["page"],
950
- rowLimit: e
1052
+ rowLimit: m
951
1053
  });
952
1054
  return {
953
- rows: (n.rows || []).map((o) => ({
954
- page: o.keys[0],
955
- clicks: o.clicks,
956
- impressions: o.impressions,
957
- ctr: o.ctr,
958
- position: o.position
1055
+ rows: (h.rows || []).map((y) => ({
1056
+ page: y.keys[0],
1057
+ clicks: y.clicks,
1058
+ impressions: y.impressions,
1059
+ ctr: y.ctr,
1060
+ position: y.position
959
1061
  })),
960
- rowCount: n.rows?.length || 0
1062
+ rowCount: h.rows?.length || 0
961
1063
  };
962
1064
  })
963
- ), f.get(
1065
+ ), r.get(
964
1066
  "/seo/trends",
965
- h(async (t) => {
966
- const { accessToken: c, siteUrl: i } = await u(t);
967
- if (!i)
968
- throw p({ statusCode: 400, statusMessage: "Search Console Site URL is not configured." });
969
- const s = I(t), a = y(s.startDate || "30daysAgo"), r = y(s.endDate || "today"), n = ((await D(c, i, {
970
- startDate: a,
971
- endDate: r,
1067
+ b(async (o) => {
1068
+ const { accessToken: d, siteUrl: u } = await n(o);
1069
+ if (!u)
1070
+ throw g({ statusCode: 400, statusMessage: "Search Console Site URL is not configured." });
1071
+ const l = v(o), f = c(l.startDate || "30daysAgo"), p = c(l.endDate || "today"), h = ((await i(d, u, {
1072
+ startDate: f,
1073
+ endDate: p,
972
1074
  dimensions: ["date"],
973
1075
  rowLimit: 500
974
- })).rows || []).map((d) => ({
975
- date: d.keys[0],
976
- clicks: d.clicks,
977
- impressions: d.impressions,
978
- ctr: d.ctr,
979
- position: d.position
980
- })).sort((d, g) => d.date.localeCompare(g.date)), o = n.reduce(
981
- (d, g) => ({
982
- clicks: d.clicks + g.clicks,
983
- impressions: d.impressions + g.impressions
1076
+ })).rows || []).map((w) => ({
1077
+ date: w.keys[0],
1078
+ clicks: w.clicks,
1079
+ impressions: w.impressions,
1080
+ ctr: w.ctr,
1081
+ position: w.position
1082
+ })).sort((w, C) => w.date.localeCompare(C.date)), y = h.reduce(
1083
+ (w, C) => ({
1084
+ clicks: w.clicks + C.clicks,
1085
+ impressions: w.impressions + C.impressions
984
1086
  }),
985
1087
  { clicks: 0, impressions: 0 }
986
1088
  );
987
- return o.ctr = o.impressions > 0 ? o.clicks / o.impressions : 0, o.avgPosition = n.length > 0 ? n.reduce((d, g) => d + g.position, 0) / n.length : 0, { rows: n, totals: o, rowCount: n.length };
1089
+ return y.ctr = y.impressions > 0 ? y.clicks / y.impressions : 0, y.avgPosition = h.length > 0 ? h.reduce((w, C) => w + C.position, 0) / h.length : 0, { rows: h, totals: y, rowCount: h.length };
988
1090
  })
989
- ), f.get(
1091
+ ), r.get(
990
1092
  "/seo/query-pages",
991
- h(async (t) => {
992
- const { accessToken: c, siteUrl: i } = await u(t);
993
- if (!i)
994
- throw p({ statusCode: 400, statusMessage: "Search Console Site URL is not configured." });
995
- const s = I(t), a = y(s.startDate || "30daysAgo"), r = y(s.endDate || "today"), e = await D(c, i, {
996
- startDate: a,
997
- endDate: r,
1093
+ b(async (o) => {
1094
+ const { accessToken: d, siteUrl: u } = await n(o);
1095
+ if (!u)
1096
+ throw g({ statusCode: 400, statusMessage: "Search Console Site URL is not configured." });
1097
+ const l = v(o), f = c(l.startDate || "30daysAgo"), p = c(l.endDate || "today"), m = await i(d, u, {
1098
+ startDate: f,
1099
+ endDate: p,
998
1100
  dimensions: ["query", "page"],
999
1101
  rowLimit: 100
1000
1102
  });
1001
1103
  return {
1002
- rows: (e.rows || []).map((n) => ({
1003
- query: n.keys[0],
1004
- page: n.keys[1],
1005
- clicks: n.clicks,
1006
- impressions: n.impressions,
1007
- ctr: n.ctr,
1008
- position: n.position
1104
+ rows: (m.rows || []).map((h) => ({
1105
+ query: h.keys[0],
1106
+ page: h.keys[1],
1107
+ clicks: h.clicks,
1108
+ impressions: h.impressions,
1109
+ ctr: h.ctr,
1110
+ position: h.position
1009
1111
  })),
1010
- rowCount: e.rows?.length || 0
1112
+ rowCount: m.rows?.length || 0
1011
1113
  };
1012
1114
  })
1013
- ), f.handler;
1014
- }
1015
- function L(A, P = !0) {
1016
- const S = (A.metricHeaders || []).map((a) => a.name), u = (A.dimensionHeaders || []).map((a) => a.name).indexOf("date"), l = [];
1017
- (A.rows || []).forEach((a) => {
1018
- const r = u >= 0 ? a.dimensionValues[u]?.value : a.dimensionValues[0]?.value;
1019
- if (!r || r.length < 8) return;
1020
- const n = { date: `${r.slice(0, 4)}-${r.slice(4, 6)}-${r.slice(6, 8)}` };
1021
- S.forEach((o, d) => {
1022
- n[o] = parseFloat(a.metricValues[d]?.value || "0");
1023
- }), l.push(n);
1115
+ ), r.handler;
1116
+ }
1117
+ function ms(e, t = !0) {
1118
+ const s = (e.metricHeaders || []).map((f) => f.name), n = (e.dimensionHeaders || []).map((f) => f.name).indexOf("date"), a = [];
1119
+ (e.rows || []).forEach((f) => {
1120
+ const p = n >= 0 ? f.dimensionValues[n]?.value : f.dimensionValues[0]?.value;
1121
+ if (!p || p.length < 8) return;
1122
+ const h = { date: `${p.slice(0, 4)}-${p.slice(4, 6)}-${p.slice(6, 8)}` };
1123
+ s.forEach((y, w) => {
1124
+ h[y] = parseFloat(f.metricValues[w]?.value || "0");
1125
+ }), a.push(h);
1024
1126
  });
1025
- const y = {}, D = {};
1026
- A.totals && A.totals.length >= 2 ? S.forEach((a, r) => {
1027
- y[a] = parseFloat(A.totals[0]?.metricValues?.[r]?.value || "0"), D[a] = parseFloat(A.totals[1]?.metricValues?.[r]?.value || "0");
1028
- }) : A.totals && A.totals.length === 1 && S.forEach((a, r) => {
1029
- y[a] = parseFloat(A.totals[0]?.metricValues?.[r]?.value || "0");
1127
+ const c = {}, i = {};
1128
+ e.totals && e.totals.length >= 2 ? s.forEach((f, p) => {
1129
+ c[f] = parseFloat(e.totals[0]?.metricValues?.[p]?.value || "0"), i[f] = parseFloat(e.totals[1]?.metricValues?.[p]?.value || "0");
1130
+ }) : e.totals && e.totals.length === 1 && s.forEach((f, p) => {
1131
+ c[f] = parseFloat(e.totals[0]?.metricValues?.[p]?.value || "0");
1030
1132
  });
1031
- const t = {};
1032
- S.forEach((a) => {
1033
- const r = y[a] || 0, e = D[a];
1034
- e !== void 0 && e !== 0 ? t[a] = (r - e) / e * 100 : t[a] = null;
1133
+ const o = {};
1134
+ s.forEach((f) => {
1135
+ const p = c[f] || 0, m = i[f];
1136
+ m !== void 0 && m !== 0 ? o[f] = (p - m) / m * 100 : o[f] = null;
1035
1137
  });
1036
- const c = /* @__PURE__ */ new Map();
1037
- for (const a of l)
1038
- c.has(a.date) || c.set(a.date, a);
1039
- const i = Array.from(c.values()).sort((a, r) => a.date.localeCompare(r.date)), s = P && A.totals && A.totals.length >= 2 ? i.slice(-Math.ceil(i.length / 2)) : i;
1138
+ const d = /* @__PURE__ */ new Map();
1139
+ for (const f of a)
1140
+ d.has(f.date) || d.set(f.date, f);
1141
+ const u = Array.from(d.values()).sort((f, p) => f.date.localeCompare(p.date)), l = t && e.totals && e.totals.length >= 2 ? u.slice(-Math.ceil(u.length / 2)) : u;
1040
1142
  return {
1041
- rows: s,
1042
- totals: y,
1043
- previousTotals: D,
1044
- changes: t,
1045
- rowCount: s.length
1143
+ rows: l,
1144
+ totals: c,
1145
+ previousTotals: i,
1146
+ changes: o,
1147
+ rowCount: l.length
1046
1148
  };
1047
1149
  }
1048
- function b(A, P) {
1049
- const S = (A.metricHeaders || []).map((u) => u.name), f = (A.rows || []).map((u) => {
1050
- const l = {
1051
- [P]: u.dimensionValues[0].value
1150
+ function k(e, t) {
1151
+ const s = (e.metricHeaders || []).map((n) => n.name), r = (e.rows || []).map((n) => {
1152
+ const a = {
1153
+ [t]: n.dimensionValues[0].value
1052
1154
  };
1053
- return S.forEach((y, D) => {
1054
- l[y] = parseFloat(u.metricValues[D].value);
1055
- }), l;
1155
+ return s.forEach((c, i) => {
1156
+ a[c] = parseFloat(n.metricValues[i].value);
1157
+ }), a;
1056
1158
  });
1057
- return { rows: f, rowCount: A.rowCount || f.length };
1058
- }
1059
- function $(A, P) {
1060
- const S = (A.metricHeaders || []).map((u) => u.name), f = (A.rows || []).map((u) => {
1061
- const l = {};
1062
- return P.forEach((y, D) => {
1063
- l[y] = u.dimensionValues[D]?.value || "";
1064
- }), S.forEach((y, D) => {
1065
- l[y] = parseFloat(u.metricValues[D].value);
1066
- }), l;
1159
+ return { rows: r, rowCount: e.rowCount || r.length };
1160
+ }
1161
+ function ft(e, t) {
1162
+ const s = (e.metricHeaders || []).map((n) => n.name), r = (e.rows || []).map((n) => {
1163
+ const a = {};
1164
+ return t.forEach((c, i) => {
1165
+ a[c] = n.dimensionValues[i]?.value || "";
1166
+ }), s.forEach((c, i) => {
1167
+ a[c] = parseFloat(n.metricValues[i].value);
1168
+ }), a;
1067
1169
  });
1068
- return { rows: f, rowCount: A.rowCount || f.length };
1069
- }
1070
- function Y(A) {
1071
- const { decrypt: P } = A, S = E();
1072
- async function f(u) {
1073
- const { supabase: l, instanceId: y } = u.context.module, { data: D, error: t } = await l.from("project_modules").select("config").eq("id", y).single();
1074
- if (t || !D?.config)
1075
- throw p({ statusCode: 500, statusMessage: "Failed to load module config." });
1076
- const c = D.config, i = c.githubIntegration, s = c.githubRepo, a = c.githubOwner;
1077
- if (!i || !s || !a)
1078
- throw p({ statusCode: 400, statusMessage: "No GitHub integration configured for this module." });
1079
- const { data: r, error: e } = await l.from("integrations").select("config").eq("id", i).single();
1080
- let n = r?.config;
1081
- if (e || !n) {
1082
- const { data: d, error: g } = await l.from("agency_integrations").select("config").eq("id", i).single();
1083
- if (g || !d?.config)
1084
- throw p({ statusCode: 500, statusMessage: "Failed to load Github credentials." });
1085
- n = d.config;
1170
+ return { rows: r, rowCount: e.rowCount || r.length };
1171
+ }
1172
+ function Ta(e) {
1173
+ const { decrypt: t } = e, s = J();
1174
+ async function r(n) {
1175
+ const { supabase: a, instanceId: c } = n.context.module, { data: i, error: o } = await a.from("project_modules").select("config").eq("id", c).single();
1176
+ if (o || !i?.config)
1177
+ throw g({ statusCode: 500, statusMessage: "Failed to load module config." });
1178
+ const d = i.config, u = d.githubIntegration, l = d.githubRepo, f = d.githubOwner;
1179
+ if (!u || !l || !f)
1180
+ throw g({ statusCode: 400, statusMessage: "No GitHub integration configured for this module." });
1181
+ const { data: p, error: m } = await a.from("integrations").select("config").eq("id", u).single();
1182
+ let h = p?.config;
1183
+ if (m || !h) {
1184
+ const { data: w, error: C } = await a.from("agency_integrations").select("config").eq("id", u).single();
1185
+ if (C || !w?.config)
1186
+ throw g({ statusCode: 500, statusMessage: "Failed to load Github credentials." });
1187
+ h = w.config;
1086
1188
  }
1087
- return { token: P(n.token), repo: s, owner: a };
1189
+ return { token: t(h.token), repo: l, owner: f };
1088
1190
  }
1089
- return S.post(
1191
+ return s.post(
1090
1192
  "/github/deploy",
1091
- h(async (u) => {
1193
+ b(async (n) => {
1092
1194
  try {
1093
- const { token: l, repo: y, owner: D } = await f(u);
1094
- await fetch(`https://api.github.com/repos/${D}/${y}/dispatches`, {
1195
+ const { token: a, repo: c, owner: i } = await r(n);
1196
+ await fetch(`https://api.github.com/repos/${i}/${c}/dispatches`, {
1095
1197
  method: "POST",
1096
1198
  headers: {
1097
- Authorization: `Bearer ${l}`,
1199
+ Authorization: `Bearer ${a}`,
1098
1200
  Accept: "application/vnd.github+json"
1099
1201
  },
1100
1202
  body: JSON.stringify({
1101
1203
  event_type: "deploy-site"
1102
1204
  })
1103
1205
  });
1104
- } catch (l) {
1105
- throw console.error("Error triggering GitHub deployment:", l), p({ statusCode: 500, statusMessage: "Failed to trigger deployment." });
1206
+ } catch (a) {
1207
+ throw console.error("Error triggering GitHub deployment:", a), g({ statusCode: 500, statusMessage: "Failed to trigger deployment." });
1106
1208
  }
1107
1209
  })
1108
- ), S.handler;
1210
+ ), s.handler;
1211
+ }
1212
+ const Ot = "firebaseAuthManager", $ = {
1213
+ viewUsers: "view-auth-users",
1214
+ viewPii: "view-auth-user-pii",
1215
+ createUsers: "create-auth-users",
1216
+ editUsers: "edit-auth-users",
1217
+ manageStatus: "manage-auth-status",
1218
+ manageSessions: "manage-auth-sessions",
1219
+ manageMfa: "manage-auth-mfa",
1220
+ manageRoles: "manage-auth-roles",
1221
+ manageProfiles: "manage-auth-profiles",
1222
+ deleteUsers: "delete-auth-users"
1223
+ }, jt = x().trim().email().max(254), Qe = x().min(1).max(128), fs = Qe.refine((e) => e === e.trim(), {
1224
+ message: "Custom UID may not begin or end with whitespace."
1225
+ }), qt = x().trim().regex(/^\+[1-9]\d{1,14}$/, "Phone number must use E.164 format."), ps = x().email().max(254), gs = x().regex(/^\+[1-9]\d{1,14}$/, "Phone number must use E.164 format."), Re = x().trim().min(3).max(500), $t = x().trim().min(1).max(128), hs = O({
1226
+ email: jt,
1227
+ password: x().min(8).max(128),
1228
+ uid: fs.optional(),
1229
+ displayName: $t.optional(),
1230
+ phoneNumber: qt.optional(),
1231
+ emailVerified: _e().optional(),
1232
+ disabled: _e().optional(),
1233
+ createProfile: _e().optional().default(!1)
1234
+ }).strict(), ys = O({
1235
+ reason: Re,
1236
+ displayName: $t.nullable().optional(),
1237
+ email: jt.optional(),
1238
+ phoneNumber: qt.nullable().optional(),
1239
+ emailVerified: _e().optional()
1240
+ }).strict().refine((e) => re(e, "displayName") || re(e, "email") || re(e, "phoneNumber") || re(e, "emailVerified"), {
1241
+ message: "At least one editable user field is required."
1242
+ }), ws = O({
1243
+ disabled: _e(),
1244
+ reason: Re
1245
+ }).strict(), $e = O({
1246
+ reason: Re
1247
+ }).strict(), bs = O({
1248
+ role: x().trim().min(1).max(64).nullable(),
1249
+ reason: Re
1250
+ }).strict(), Cs = O({
1251
+ reason: Re,
1252
+ // Deliberately do not trim this value: destructive confirmation must be an
1253
+ // exact byte-for-byte match with the UID shown to the operator.
1254
+ confirmation: x().min(1).max(128)
1255
+ }).strict(), As = /* @__PURE__ */ new Set([
1256
+ "acr",
1257
+ "amr",
1258
+ "at_hash",
1259
+ "aud",
1260
+ "auth_time",
1261
+ "azp",
1262
+ "cnf",
1263
+ "c_hash",
1264
+ "email",
1265
+ "email_verified",
1266
+ "exp",
1267
+ "firebase",
1268
+ "iat",
1269
+ "iss",
1270
+ "jti",
1271
+ "name",
1272
+ "nbf",
1273
+ "nonce",
1274
+ "phone_number",
1275
+ "picture",
1276
+ "sub",
1277
+ "user_id"
1278
+ ]), Is = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1279
+ function re(e, t) {
1280
+ return Object.prototype.hasOwnProperty.call(e, t);
1281
+ }
1282
+ function ce(e) {
1283
+ throw g({ statusCode: 400, statusMessage: e });
1284
+ }
1285
+ function pt(e, t) {
1286
+ return typeof e == "boolean" ? e : e === "true" ? !0 : e === "false" ? !1 : t;
1287
+ }
1288
+ function Bt(e) {
1289
+ const t = typeof e == "number" ? e : Number(e);
1290
+ return Number.isFinite(t) ? Math.min(100, Math.max(10, Math.trunc(t))) : 50;
1291
+ }
1292
+ function Ms(e) {
1293
+ const t = String(e || "users").trim();
1294
+ return t.length === 0 || t.length > 256 || t.includes("/") || t === "." || t === ".." || /^__.*__$/.test(t) ? ce("The configured Firestore profile collection is invalid.") : t;
1295
+ }
1296
+ function ge(e, t, s) {
1297
+ const r = String(e ?? t).trim() || t;
1298
+ return r.length > 128 || !/^[A-Za-z_][A-Za-z0-9_-]*$/.test(r) || Is.has(r) ? ce(`The configured ${s} field is invalid.`) : r;
1299
+ }
1300
+ function Ss(e) {
1301
+ const t = Array.isArray(e) ? e : String(e || "").split(","), s = [];
1302
+ for (const r of t) {
1303
+ if (typeof r != "string")
1304
+ return ce("Configured role options must be strings.");
1305
+ const n = r.trim();
1306
+ if (n) {
1307
+ if (n.length > 64)
1308
+ return ce("Configured role options may not exceed 64 characters.");
1309
+ s.includes(n) || s.push(n);
1310
+ }
1311
+ }
1312
+ return s.length > 50 ? ce("No more than 50 role options may be configured.") : s;
1313
+ }
1314
+ function Ds(e) {
1315
+ const t = typeof e.project == "string" ? e.project.trim() : "";
1316
+ t || ce("Firebase Auth Manager has no Firebase integration configured.");
1317
+ const s = pt(e.manageRoleClaim, !1), r = ge(e.roleClaimKey, "role", "role claim");
1318
+ s && As.has(r) && ce("The configured role claim key is reserved by Firebase.");
1319
+ const n = {
1320
+ profileUidField: ge(e.profileUidField, "uid", "profile UID"),
1321
+ profileNameField: ge(e.profileNameField, "displayName", "profile name"),
1322
+ profileEmailField: ge(e.profileEmailField, "email", "profile email"),
1323
+ profileRoleField: ge(e.profileRoleField, "role", "profile role"),
1324
+ profileStatusField: ge(e.profileStatusField, "status", "profile status")
1325
+ };
1326
+ return new Set(Object.values(n)).size !== Object.keys(n).length && ce("Each configured Firestore profile field must use a distinct field name."), {
1327
+ project: t,
1328
+ pageSize: Bt(e.pageSize),
1329
+ linkFirestoreProfiles: pt(e.linkFirestoreProfiles, !0),
1330
+ profileCollection: Ms(e.profileCollection),
1331
+ ...n,
1332
+ manageRoleClaim: s,
1333
+ roleClaimKey: r,
1334
+ roleOptions: Ss(e.roleOptions)
1335
+ };
1336
+ }
1337
+ function gt(e) {
1338
+ const t = Array.isArray(e) ? e[0] : e;
1339
+ return typeof t != "string" ? void 0 : t.trim() || void 0;
1340
+ }
1341
+ function ht(e) {
1342
+ const t = Array.isArray(e) ? e[0] : e;
1343
+ if (!(typeof t != "string" || t.length === 0))
1344
+ return t;
1345
+ }
1346
+ function Ts(e, t = 50) {
1347
+ const s = gt(e.limit);
1348
+ let r = Bt(t);
1349
+ if (s !== void 0) {
1350
+ if (!/^\d+$/.test(s))
1351
+ throw g({ statusCode: 422, statusMessage: "Limit must be a whole number." });
1352
+ if (r = Number(s), r < 1 || r > 100)
1353
+ throw g({ statusCode: 422, statusMessage: "Limit must be between 1 and 100." });
1354
+ }
1355
+ const n = ht(e.pageToken);
1356
+ if (n && n.length > 4096)
1357
+ throw g({ statusCode: 422, statusMessage: "Page token is too long." });
1358
+ const a = ht(e.search), c = gt(e.searchBy);
1359
+ if (c && !a)
1360
+ throw g({ statusCode: 422, statusMessage: "Search mode requires a search value." });
1361
+ if (!a) return { limit: r, pageToken: n };
1362
+ if (n)
1363
+ throw g({ statusCode: 422, statusMessage: "Exact search cannot be combined with a page token." });
1364
+ const i = c || "uid";
1365
+ if (!["uid", "email", "phone"].includes(i))
1366
+ throw g({ statusCode: 422, statusMessage: "Search mode must be uid, email, or phone." });
1367
+ if (i === "phone") {
1368
+ const d = gs.safeParse(a);
1369
+ if (!d.success)
1370
+ throw g({ statusCode: 422, statusMessage: "Phone search must use E.164 format." });
1371
+ return { limit: 1, search: { kind: "phone", value: d.data } };
1372
+ }
1373
+ if (i === "email") {
1374
+ const d = ps.safeParse(a);
1375
+ if (!d.success)
1376
+ throw g({ statusCode: 422, statusMessage: "Email search is invalid." });
1377
+ return { limit: 1, search: { kind: "email", value: d.data } };
1378
+ }
1379
+ const o = Qe.safeParse(a);
1380
+ if (!o.success)
1381
+ throw g({ statusCode: 422, statusMessage: "UID search is invalid." });
1382
+ return { limit: 1, search: { kind: "uid", value: o.data } };
1383
+ }
1384
+ function vs(e) {
1385
+ const t = new TextEncoder().encode(e);
1386
+ let s = "";
1387
+ for (const r of t) s += String.fromCharCode(r);
1388
+ return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
1389
+ }
1390
+ function _s(e) {
1391
+ if (!/^[A-Za-z0-9_-]{1,1024}$/.test(e))
1392
+ throw g({ statusCode: 400, statusMessage: "Firebase UID token is invalid." });
1393
+ const t = e.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(e.length / 4) * 4, "=");
1394
+ let s;
1395
+ try {
1396
+ const r = atob(t), n = Uint8Array.from(r, (a) => a.charCodeAt(0));
1397
+ s = new TextDecoder("utf-8", { fatal: !0 }).decode(n);
1398
+ } catch {
1399
+ throw g({ statusCode: 400, statusMessage: "Firebase UID token is invalid." });
1400
+ }
1401
+ if (vs(s) !== e || !Qe.safeParse(s).success)
1402
+ throw g({ statusCode: 400, statusMessage: "Firebase UID token is invalid." });
1403
+ return s;
1404
+ }
1405
+ function Ze(e, t) {
1406
+ return e.isAdmin === !0 || Array.isArray(e.permissions) && e.permissions.includes(t);
1407
+ }
1408
+ function Rs(e, t) {
1409
+ return t.kind === "uid" || Ze(e, $.viewPii);
1410
+ }
1411
+ function xs(e, t) {
1412
+ let s = e.length ^ t.length;
1413
+ const r = Math.max(e.length, t.length);
1414
+ for (let n = 0; n < r; n += 1)
1415
+ s |= (e.charCodeAt(n) || 0) ^ (t.charCodeAt(n) || 0);
1416
+ return s === 0;
1417
+ }
1418
+ function H(e, t) {
1419
+ if (!Ze(e, t))
1420
+ throw g({ statusCode: 403, statusMessage: "You do not have permission to perform this action." });
1421
+ }
1422
+ function Lt(e) {
1423
+ if (!e) return null;
1424
+ const t = e.lastIndexOf("@");
1425
+ if (t <= 0) return "••••";
1426
+ const s = e.slice(0, t), r = e.slice(t + 1), n = r.lastIndexOf("."), a = n > 0 ? r.slice(0, n) : r, c = n > 0 ? r.slice(n) : "";
1427
+ return `${s.slice(0, 1)}•••@${a.slice(0, 1)}•••${c}`;
1428
+ }
1429
+ function Fs(e) {
1430
+ return e ? `+••••••${e.replace(/\D/g, "").slice(-2)}` : null;
1431
+ }
1432
+ function Ns(e) {
1433
+ 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(
1434
+ /\b(password|passcode|token|secret|api[-_\s]?key|authorization)\b\s*[:=]?\s*\S+/gi,
1435
+ "$1 [redacted]"
1436
+ );
1437
+ }
1438
+ function zt(e) {
1439
+ return e ? `${e.slice(0, 1)}•••` : null;
1440
+ }
1441
+ function B(e) {
1442
+ return typeof e == "string" ? e : null;
1443
+ }
1444
+ function Es(e) {
1445
+ const t = [];
1446
+ if (Array.isArray(e?.providerData))
1447
+ for (const s of e.providerData) {
1448
+ const r = B(s?.providerId);
1449
+ r && t.push(r);
1450
+ }
1451
+ return [...new Set(t)].sort();
1452
+ }
1453
+ function Vt(e) {
1454
+ return Array.isArray(e?.multiFactor?.enrolledFactors) ? e.multiFactor.enrolledFactors.length : 0;
1455
+ }
1456
+ function Xe(e, t) {
1457
+ return t.manageRoleClaim ? B(e?.customClaims?.[t.roleClaimKey]) : null;
1458
+ }
1459
+ function Ps() {
1460
+ return {
1461
+ enabled: !1,
1462
+ exists: !1,
1463
+ name: null,
1464
+ email: null,
1465
+ role: null,
1466
+ status: null
1467
+ };
1468
+ }
1469
+ function ks(e, t, s) {
1470
+ const r = e ? B(e[s.profileNameField]) : null, n = e ? B(e[s.profileEmailField]) : null;
1471
+ return {
1472
+ enabled: !0,
1473
+ exists: !!e,
1474
+ name: t ? r : zt(r),
1475
+ email: t ? n : Lt(n),
1476
+ role: t && e ? B(e[s.profileRoleField]) : null,
1477
+ status: t && e ? B(e[s.profileStatusField]) : null
1478
+ };
1479
+ }
1480
+ function je(e, t) {
1481
+ const s = B(e?.email), r = B(e?.displayName), n = B(e?.phoneNumber);
1482
+ let a;
1483
+ return t.config.linkFirestoreProfiles ? t.profileAvailable === !1 ? a = null : a = ks(t.profile || null, t.canViewPii, t.config) : a = Ps(), {
1484
+ uid: String(e?.uid || ""),
1485
+ displayName: t.canViewPii ? r : zt(r),
1486
+ email: t.canViewPii ? s : Lt(s),
1487
+ phoneNumber: t.canViewPii ? n : Fs(n),
1488
+ photoURL: t.canViewPii ? B(e?.photoURL) : null,
1489
+ emailVerified: e?.emailVerified === !0,
1490
+ disabled: e?.disabled === !0,
1491
+ providers: Es(e),
1492
+ createdAt: B(e?.metadata?.creationTime),
1493
+ lastSignInAt: B(e?.metadata?.lastSignInTime),
1494
+ lastRefreshAt: B(e?.metadata?.lastRefreshTime),
1495
+ mfaFactorCount: Vt(e),
1496
+ role: Xe(e, t.config),
1497
+ profile: a
1498
+ };
1499
+ }
1500
+ function yt(e, t) {
1501
+ if (!e) return null;
1502
+ const s = je(e, {
1503
+ canViewPii: !1,
1504
+ config: t,
1505
+ profileAvailable: !1
1506
+ });
1507
+ return {
1508
+ uid: s.uid,
1509
+ displayName: s.displayName,
1510
+ email: s.email,
1511
+ phoneNumber: s.phoneNumber,
1512
+ emailVerified: s.emailVerified,
1513
+ disabled: s.disabled,
1514
+ providers: s.providers,
1515
+ mfaFactorCount: s.mfaFactorCount,
1516
+ role: s.role
1517
+ };
1518
+ }
1519
+ function Us(e, t, s) {
1520
+ const r = { ...e || {} };
1521
+ return s === null ? delete r[t] : r[t] = s, r;
1522
+ }
1523
+ function wt(e, t) {
1524
+ const s = {
1525
+ [t.profileUidField]: String(e.uid),
1526
+ [t.profileStatusField]: e.disabled === !0 ? "disabled" : "active"
1527
+ };
1528
+ typeof e.displayName == "string" && (s[t.profileNameField] = e.displayName), typeof e.email == "string" && (s[t.profileEmailField] = e.email);
1529
+ const r = Xe(e, t);
1530
+ return r !== null && (s[t.profileRoleField] = r), s;
1531
+ }
1532
+ function xe(e) {
1533
+ if (!e || typeof e != "object") return;
1534
+ const t = e;
1535
+ if (typeof t.code == "string") return t.code;
1536
+ if (typeof t.errorInfo?.code == "string") return t.errorInfo.code;
1537
+ }
1538
+ function Os(e, t = "Firebase Authentication request failed.") {
1539
+ const s = xe(e);
1540
+ return s === "auth/user-not-found" ? { statusCode: 404, statusMessage: "Firebase user not found." } : s && [
1541
+ "auth/email-already-exists",
1542
+ "auth/phone-number-already-exists",
1543
+ "auth/uid-already-exists"
1544
+ ].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 && [
1545
+ "auth/too-many-requests",
1546
+ "auth/quota-exceeded"
1547
+ ].includes(s) ? { statusCode: 429, statusMessage: "Firebase temporarily rate-limited this request." } : s && [
1548
+ "auth/insufficient-permission",
1549
+ "auth/project-not-found",
1550
+ "auth/configuration-not-found"
1551
+ ].includes(s) ? { statusCode: 424, statusMessage: "The Firebase integration is not authorized for this operation." } : { statusCode: 502, statusMessage: t };
1552
+ }
1553
+ function W(e) {
1554
+ const t = e?.context?.module;
1555
+ if (!t || !t.supabase || typeof t.instanceId != "string" || typeof t.projectId != "string" || typeof t.userId != "string")
1556
+ throw g({ statusCode: 500, statusMessage: "Firebase Auth Manager request context is unavailable." });
1557
+ return {
1558
+ ...t,
1559
+ isAdmin: t.isAdmin === !0,
1560
+ permissions: Array.isArray(t.permissions) ? t.permissions : []
1561
+ };
1562
+ }
1563
+ async function X(e, t) {
1564
+ const s = t.safeParse(await j(e));
1565
+ if (!s.success)
1566
+ throw g({
1567
+ statusCode: 422,
1568
+ statusMessage: s.error.issues[0]?.message || "Request body is invalid."
1569
+ });
1570
+ return s.data;
1571
+ }
1572
+ function ee(e) {
1573
+ const t = U(e, "uid");
1574
+ if (typeof t != "string")
1575
+ throw g({ statusCode: 400, statusMessage: "A valid Firebase UID is required." });
1576
+ return _s(t);
1577
+ }
1578
+ function G(e, t) {
1579
+ if (typeof e?.statusCode == "number" && typeof e?.statusMessage == "string")
1580
+ throw e;
1581
+ const s = Os(e, t), r = xe(e);
1582
+ throw console.error("Firebase Auth Manager request failed", r ? { code: r } : { code: "unknown" }), g(s);
1583
+ }
1584
+ function js(e) {
1585
+ return xe(e) === "auth/user-not-found";
1586
+ }
1587
+ function Ye(e) {
1588
+ return Ze(e, $.viewPii);
1589
+ }
1590
+ function qs(e, t) {
1591
+ const s = [];
1592
+ for (let r = 0; r < e.length; r += t)
1593
+ s.push(e.slice(r, r + t));
1594
+ return s;
1595
+ }
1596
+ function qe(e) {
1597
+ return e.length > 0 && e !== "." && e !== ".." && !e.includes("/") && !/^__.*__$/.test(e);
1598
+ }
1599
+ async function $s(e, t, s) {
1600
+ const r = /* @__PURE__ */ new Map();
1601
+ if (!s.linkFirestoreProfiles || t.length === 0) return r;
1602
+ const n = [...new Set(t.map((u) => String(u.uid)).filter(Boolean))], a = e.collection(s.profileCollection), c = n.filter(qe), i = c.map((u) => a.doc(u)), o = i.length === 0 ? [] : typeof e.getAll == "function" ? await e.getAll(...i) : await Promise.all(i.map((u) => u.get()));
1603
+ for (let u = 0; u < o.length; u += 1) {
1604
+ const l = o[u], f = c[u];
1605
+ f && l?.exists && r.set(f, l.data());
1606
+ }
1607
+ const d = n.filter((u) => !r.has(u));
1608
+ for (const u of qs(d, 30)) {
1609
+ const l = await a.where(s.profileUidField, "in", u).get();
1610
+ for (const f of l.docs || []) {
1611
+ const p = f.data(), m = p[s.profileUidField];
1612
+ typeof m == "string" && u.includes(m) && !r.has(m) && r.set(m, p);
1613
+ }
1614
+ }
1615
+ return r;
1616
+ }
1617
+ async function bt(e, t, s) {
1618
+ const r = e.collection(s.profileCollection);
1619
+ if (qe(t)) {
1620
+ const c = await r.doc(t).get();
1621
+ if (c.exists)
1622
+ return { id: String(c.id), data: c.data() };
1623
+ }
1624
+ const a = (await r.where(s.profileUidField, "==", t).limit(1).get()).docs?.[0];
1625
+ return a ? { id: String(a.id), data: a.data() } : null;
1626
+ }
1627
+ async function Y(e, t) {
1628
+ const s = W(e), { data: r, error: n } = await s.supabase.from("project_modules").select("type, config").eq("id", s.instanceId).eq("project_id", s.projectId).maybeSingle();
1629
+ if (n)
1630
+ throw g({ statusCode: 500, statusMessage: "Failed to load Firebase Auth Manager configuration." });
1631
+ if (!r || r.type !== Ot || !r.config)
1632
+ throw g({ statusCode: 404, statusMessage: "Firebase Auth Manager module instance not found." });
1633
+ const a = Ds(r.config), { data: c, error: i } = await s.supabase.from("integrations").select("id, project_id, type").eq("id", a.project).eq("project_id", s.projectId).eq("type", "FIREBASE").maybeSingle();
1634
+ if (i)
1635
+ throw g({ statusCode: 500, statusMessage: "Failed to verify the Firebase integration." });
1636
+ if (!c)
1637
+ throw g({ statusCode: 424, statusMessage: "The configured Firebase integration is unavailable for this project." });
1638
+ const o = await t.initFirebase(e, a.project);
1639
+ return {
1640
+ module: s,
1641
+ config: a,
1642
+ integrationId: a.project,
1643
+ firebase: o,
1644
+ auth: o.auth(),
1645
+ firestore: o.firestore()
1646
+ };
1647
+ }
1648
+ async function te(e, t, s) {
1649
+ const r = {
1650
+ project_id: t.module.projectId,
1651
+ module_id: t.module.instanceId,
1652
+ user_id: t.module.userId,
1653
+ message: `Firebase Auth: ${s.action}`,
1654
+ level: "INFO",
1655
+ params: {
1656
+ source: Ot,
1657
+ action: s.action,
1658
+ targetUid: s.targetUid,
1659
+ integrationId: t.integrationId,
1660
+ reason: Ns(s.reason),
1661
+ before: yt(s.before, t.config),
1662
+ after: yt(s.after, t.config),
1663
+ ...s.details || {}
1664
+ }
1665
+ };
1666
+ try {
1667
+ const { error: n } = await t.module.supabase.from("project_logs").insert(r);
1668
+ return n ? (console.error("Firebase Auth Manager audit insert failed", { code: n.code || "unknown" }), !1) : !0;
1669
+ } catch (n) {
1670
+ const a = n && typeof n == "object" && "code" in n ? String(n.code) : "unknown";
1671
+ return console.error("Firebase Auth Manager audit insert failed", { code: a }), !1;
1672
+ }
1673
+ }
1674
+ async function Bs(e, t) {
1675
+ try {
1676
+ return t.kind === "email" ? [await e.getUserByEmail(t.value)] : t.kind === "phone" ? [await e.getUserByPhoneNumber(t.value)] : [await e.getUser(t.value)];
1677
+ } catch (s) {
1678
+ if (js(s)) return [];
1679
+ throw s;
1680
+ }
1681
+ }
1682
+ async function Ct(e, t, s) {
1683
+ try {
1684
+ return await e.getUser(t);
1685
+ } catch (r) {
1686
+ return console.error("Firebase Auth Manager post-mutation refresh failed", {
1687
+ code: xe(r) || "unknown"
1688
+ }), s;
1689
+ }
1690
+ }
1691
+ async function ue(e, t) {
1692
+ let s = /* @__PURE__ */ new Map(), r = !0;
1693
+ if (t.config.linkFirestoreProfiles)
1694
+ try {
1695
+ s = await $s(t.firestore, e, t.config);
1696
+ } catch (n) {
1697
+ r = !1;
1698
+ const a = xe(n);
1699
+ console.error("Firebase Auth Manager profile join failed", { code: a || "unknown" });
1700
+ }
1701
+ return e.map((n) => je(n, {
1702
+ canViewPii: Ye(t.module),
1703
+ config: t.config,
1704
+ profile: s.get(String(n.uid)) || null,
1705
+ profileAvailable: r
1706
+ }));
1707
+ }
1708
+ function va(e) {
1709
+ const t = J();
1710
+ return t.get("/users", b(async (s) => {
1711
+ const r = W(s);
1712
+ H(r, $.viewUsers);
1713
+ try {
1714
+ const n = await Y(s, e), a = Ts(v(s), n.config.pageSize);
1715
+ if (a.search && !Rs(n.module, a.search))
1716
+ throw g({
1717
+ statusCode: 403,
1718
+ statusMessage: "Viewing personal data is required to search by email or phone number."
1719
+ });
1720
+ let c, i = null;
1721
+ if (a.search)
1722
+ c = await Bs(n.auth, a.search);
1723
+ else {
1724
+ const o = await n.auth.listUsers(a.limit, a.pageToken);
1725
+ c = Array.isArray(o.users) ? o.users : [], i = typeof o.pageToken == "string" ? o.pageToken : null;
1726
+ }
1727
+ return {
1728
+ users: await ue(c, n),
1729
+ nextPageToken: i
1730
+ };
1731
+ } catch (n) {
1732
+ G(n, "Failed to list Firebase users.");
1733
+ }
1734
+ })), t.get("/users/:uid", b(async (s) => {
1735
+ const r = W(s);
1736
+ H(r, $.viewUsers);
1737
+ const n = ee(s);
1738
+ try {
1739
+ const a = await Y(s, e), c = await a.auth.getUser(n), [i] = await ue([c], a);
1740
+ return { user: i };
1741
+ } catch (a) {
1742
+ G(a, "Failed to load the Firebase user.");
1743
+ }
1744
+ })), t.post("/users", b(async (s) => {
1745
+ const r = W(s);
1746
+ H(r, $.createUsers);
1747
+ const n = await X(s, hs);
1748
+ n.createProfile && H(r, $.manageProfiles);
1749
+ try {
1750
+ const a = await Y(s, e);
1751
+ if (n.createProfile && !a.config.linkFirestoreProfiles)
1752
+ throw g({ statusCode: 409, statusMessage: "Firestore profile linking is disabled for this module." });
1753
+ if (n.uid && a.config.linkFirestoreProfiles && await bt(a.firestore, n.uid, a.config))
1754
+ throw g({
1755
+ statusCode: 409,
1756
+ statusMessage: "A retained Firestore profile already uses this UID. Resolve that profile before reusing the Authentication UID."
1757
+ });
1758
+ const c = await a.auth.createUser({
1759
+ email: n.email,
1760
+ password: n.password,
1761
+ uid: n.uid,
1762
+ displayName: n.displayName,
1763
+ phoneNumber: n.phoneNumber,
1764
+ emailVerified: n.emailVerified,
1765
+ disabled: n.disabled
1766
+ });
1767
+ let i = null, o = !1, d = null;
1768
+ if (n.createProfile) {
1769
+ i = wt(c, a.config);
1770
+ try {
1771
+ const l = a.firestore.collection(a.config.profileCollection);
1772
+ await (qe(c.uid) ? l.doc(c.uid) : l.doc()).create(i), o = !0;
1773
+ } catch {
1774
+ i = null, d = "The Authentication user was created, but its Firestore profile could not be created.";
1775
+ }
1776
+ }
1777
+ const u = await te(s, a, {
1778
+ action: "user.create",
1779
+ targetUid: c.uid,
1780
+ reason: "Created through Firebase Auth Manager.",
1781
+ before: null,
1782
+ after: c,
1783
+ details: {
1784
+ profileRequested: n.createProfile === !0,
1785
+ profileCreated: o
1786
+ }
1787
+ });
1788
+ return os(s, 201), {
1789
+ user: je(c, {
1790
+ canViewPii: Ye(a.module),
1791
+ config: a.config,
1792
+ profile: i
1793
+ }),
1794
+ profileCreated: o,
1795
+ profileError: d,
1796
+ auditRecorded: u
1797
+ };
1798
+ } catch (a) {
1799
+ G(a, "Failed to create the Firebase user.");
1800
+ }
1801
+ })), t.patch("/users/:uid", b(async (s) => {
1802
+ const r = W(s);
1803
+ H(r, $.editUsers);
1804
+ const n = ee(s), a = await X(s, ys);
1805
+ try {
1806
+ const c = await Y(s, e), i = await c.auth.getUser(n), o = {};
1807
+ re(a, "displayName") && (o.displayName = a.displayName), re(a, "email") && (o.email = a.email), re(a, "phoneNumber") && (o.phoneNumber = a.phoneNumber), re(a, "emailVerified") && (o.emailVerified = a.emailVerified);
1808
+ const d = await c.auth.updateUser(n, o), u = await te(s, c, {
1809
+ action: "user.update",
1810
+ targetUid: n,
1811
+ reason: a.reason,
1812
+ before: i,
1813
+ after: d,
1814
+ details: { changedFieldCount: Object.keys(o).length }
1815
+ }), [l] = await ue([d], c);
1816
+ return { user: l, auditRecorded: u };
1817
+ } catch (c) {
1818
+ G(c, "Failed to update the Firebase user.");
1819
+ }
1820
+ })), t.post("/users/:uid/status", b(async (s) => {
1821
+ const r = W(s);
1822
+ H(r, $.manageStatus);
1823
+ const n = ee(s), a = await X(s, ws);
1824
+ try {
1825
+ const c = await Y(s, e), i = await c.auth.getUser(n), o = i.disabled !== a.disabled, d = o ? await c.auth.updateUser(n, { disabled: a.disabled }) : i, u = await te(s, c, {
1826
+ action: a.disabled ? "user.disable" : "user.enable",
1827
+ targetUid: n,
1828
+ reason: a.reason,
1829
+ before: i,
1830
+ after: d,
1831
+ details: { changed: o }
1832
+ }), [l] = await ue([d], c);
1833
+ return { user: l, changed: o, auditRecorded: u };
1834
+ } catch (c) {
1835
+ G(c, "Failed to change the Firebase user status.");
1836
+ }
1837
+ })), t.post("/users/:uid/revoke-sessions", b(async (s) => {
1838
+ const r = W(s);
1839
+ H(r, $.manageSessions);
1840
+ const n = ee(s), a = await X(s, $e);
1841
+ try {
1842
+ const c = await Y(s, e), i = await c.auth.getUser(n);
1843
+ await c.auth.revokeRefreshTokens(n);
1844
+ const o = await te(s, c, {
1845
+ action: "user.revoke_sessions",
1846
+ targetUid: n,
1847
+ reason: a.reason,
1848
+ before: i,
1849
+ after: i,
1850
+ details: { sessionsRevoked: !0 }
1851
+ }), d = await Ct(c.auth, n, i), [u] = await ue([d], c);
1852
+ return { user: u, sessionsRevoked: !0, auditRecorded: o };
1853
+ } catch (c) {
1854
+ G(c, "Failed to revoke Firebase user sessions.");
1855
+ }
1856
+ })), t.post("/users/:uid/reset-mfa", b(async (s) => {
1857
+ const r = W(s);
1858
+ H(r, $.manageMfa);
1859
+ const n = ee(s), a = await X(s, $e);
1860
+ try {
1861
+ const c = await Y(s, e), i = await c.auth.getUser(n), o = Vt(i) > 0, d = o ? await c.auth.updateUser(n, { multiFactor: { enrolledFactors: [] } }) : i, u = await te(s, c, {
1862
+ action: "user.reset_mfa",
1863
+ targetUid: n,
1864
+ reason: a.reason,
1865
+ before: i,
1866
+ after: d,
1867
+ details: { changed: o }
1868
+ }), [l] = await ue([d], c);
1869
+ return { user: l, changed: o, auditRecorded: u };
1870
+ } catch (c) {
1871
+ G(c, "Failed to reset Firebase multi-factor authentication.");
1872
+ }
1873
+ })), t.post("/users/:uid/role", b(async (s) => {
1874
+ const r = W(s);
1875
+ H(r, $.manageRoles);
1876
+ const n = ee(s), a = await X(s, bs);
1877
+ try {
1878
+ const c = await Y(s, e);
1879
+ if (!c.config.manageRoleClaim)
1880
+ throw g({ statusCode: 409, statusMessage: "Role claim management is disabled for this module." });
1881
+ if (a.role !== null && !c.config.roleOptions.includes(a.role))
1882
+ throw g({ statusCode: 422, statusMessage: "Role must be one of the configured values." });
1883
+ const i = await c.auth.getUser(n), d = Xe(i, c.config) !== a.role;
1884
+ let u = i;
1885
+ if (d) {
1886
+ const p = Us(i.customClaims, c.config.roleClaimKey, a.role);
1887
+ await c.auth.setCustomUserClaims(n, p), u = { ...i, customClaims: p };
1888
+ }
1889
+ const l = await te(s, c, {
1890
+ action: "user.set_role",
1891
+ targetUid: n,
1892
+ reason: a.reason,
1893
+ before: i,
1894
+ after: u,
1895
+ details: { changed: d, role: a.role }
1896
+ });
1897
+ d && (u = await Ct(c.auth, n, u));
1898
+ const [f] = await ue([u], c);
1899
+ return { user: f, changed: d, auditRecorded: l };
1900
+ } catch (c) {
1901
+ G(c, "Failed to update the Firebase role claim.");
1902
+ }
1903
+ })), t.post("/users/:uid/profile", b(async (s) => {
1904
+ const r = W(s);
1905
+ H(r, $.manageProfiles);
1906
+ const n = ee(s), a = await X(s, $e);
1907
+ try {
1908
+ const c = await Y(s, e);
1909
+ if (!c.config.linkFirestoreProfiles)
1910
+ throw g({ statusCode: 409, statusMessage: "Firestore profile linking is disabled for this module." });
1911
+ const i = await c.auth.getUser(n), o = await bt(c.firestore, n, c.config);
1912
+ let d = o?.data || null, u = !1;
1913
+ if (!o) {
1914
+ d = wt(i, c.config);
1915
+ const f = c.firestore.collection(c.config.profileCollection);
1916
+ await (qe(n) ? f.doc(n) : f.doc()).create(d), u = !0;
1917
+ }
1918
+ const l = await te(s, c, {
1919
+ action: "user.create_profile",
1920
+ targetUid: n,
1921
+ reason: a.reason,
1922
+ before: i,
1923
+ after: i,
1924
+ details: { changed: u }
1925
+ });
1926
+ return {
1927
+ user: je(i, {
1928
+ canViewPii: Ye(c.module),
1929
+ config: c.config,
1930
+ profile: d
1931
+ }),
1932
+ profileCreated: u,
1933
+ auditRecorded: l
1934
+ };
1935
+ } catch (c) {
1936
+ G(c, "Failed to create the Firestore profile.");
1937
+ }
1938
+ })), t.delete("/users/:uid", b(async (s) => {
1939
+ const r = W(s);
1940
+ H(r, $.deleteUsers);
1941
+ const n = ee(s), a = await X(s, Cs);
1942
+ if (!xs(n, a.confirmation))
1943
+ throw g({ statusCode: 422, statusMessage: "Deletion confirmation must exactly match the Firebase UID." });
1944
+ try {
1945
+ const c = await Y(s, e), i = await c.auth.getUser(n);
1946
+ await c.auth.deleteUser(n);
1947
+ const o = await te(s, c, {
1948
+ action: "user.delete",
1949
+ targetUid: n,
1950
+ reason: a.reason,
1951
+ before: i,
1952
+ after: null,
1953
+ details: { firestoreProfilePreserved: !0 }
1954
+ });
1955
+ return {
1956
+ uid: n,
1957
+ deleted: !0,
1958
+ firestoreProfilePreserved: !0,
1959
+ auditRecorded: o
1960
+ };
1961
+ } catch (c) {
1962
+ G(c, "Failed to delete the Firebase user.");
1963
+ }
1964
+ })), t.handler;
1965
+ }
1966
+ const et = 1, At = 700 * 1024, It = 20, Ls = 5e4, zs = 120, Vs = 80, Mt = 2e3, St = 120, Hs = "studio_forms", Ht = /* @__PURE__ */ new Set(["draft", "published", "archived"]), Ws = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]), L = {
1967
+ view: "view-forms",
1968
+ create: "create-form",
1969
+ edit: "edit-form",
1970
+ publish: "publish-form",
1971
+ archive: "archive-form"
1972
+ };
1973
+ function R(e) {
1974
+ throw g({ statusCode: 400, statusMessage: e });
1975
+ }
1976
+ function ae(e) {
1977
+ throw g({ statusCode: 409, statusMessage: e });
1978
+ }
1979
+ function tt(e, t) {
1980
+ (!e || typeof e != "object" || Array.isArray(e)) && R(`${t} must be an object.`);
1981
+ const s = Object.getPrototypeOf(e);
1982
+ return s !== Object.prototype && s !== null && R(`${t} must be a plain object.`), e;
1983
+ }
1984
+ function Wt(e, t, s) {
1985
+ typeof e != "string" && R(`${t} is required.`);
1986
+ const r = e.trim();
1987
+ return r || R(`${t} is required.`), r.length > s && R(`${t} cannot exceed ${s} characters.`), r;
1988
+ }
1989
+ function Gs(e) {
1990
+ if (e == null) return "";
1991
+ typeof e != "string" && R("Form description must be a string.");
1992
+ const t = e.trim();
1993
+ return t.length > Mt && R(`Form description cannot exceed ${Mt} characters.`), t;
1994
+ }
1995
+ function Ys(e) {
1996
+ const t = Wt(e, "Form key", Vs).toLowerCase();
1997
+ return /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/.test(t) || R("Form key must start with a letter and contain only lowercase letters, numbers, hyphens, or underscores."), t;
1998
+ }
1999
+ function Js(e) {
2000
+ if (e == null || e === "")
2001
+ return Hs;
2002
+ typeof e != "string" && R("The configured forms collection must be a string.");
2003
+ const t = e.trim();
2004
+ (!t || t.length > 500) && R("The configured forms collection path is invalid."), (t.startsWith("/") || t.endsWith("/") || t.includes("//")) && R("The configured forms collection path is invalid.");
2005
+ const s = t.split("/");
2006
+ s.length % 2 === 0 && R("The configured forms collection path must point to a collection.");
2007
+ for (const r of s)
2008
+ (!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/.test(r) || r === "." || r === ".." || /^__.*__$/.test(r)) && R("The configured forms collection path contains an invalid segment.");
2009
+ return t;
2010
+ }
2011
+ function Ks(e) {
2012
+ (!e || typeof e != "object") && R("Form definition must be an object or array.");
2013
+ const t = /* @__PURE__ */ new Set();
2014
+ let s = 0;
2015
+ const r = (c, i, o) => {
2016
+ if (s += 1, s > Ls && R("Form definition is too complex."), i > It && R(`Form definition cannot be nested deeper than ${It} levels.`), c === null || typeof c == "string" || typeof c == "boolean") return;
2017
+ if (typeof c == "number") {
2018
+ Number.isFinite(c) || R("Form definition contains an invalid number.");
2019
+ return;
2020
+ }
2021
+ if (typeof c != "object" && R("Form definition contains a value that cannot be stored."), t.has(c) && R("Form definition cannot contain circular references."), t.add(c), Array.isArray(c)) {
2022
+ o && R("Form definition cannot contain an array directly inside another array.");
2023
+ for (const u of c) r(u, i + 1, !0);
2024
+ t.delete(c);
2025
+ return;
2026
+ }
2027
+ const d = Object.getPrototypeOf(c);
2028
+ d !== Object.prototype && d !== null && R("Form definition can contain only plain JSON objects.");
2029
+ for (const [u, l] of Object.entries(c))
2030
+ Ws.has(u) && R(`Form definition contains a forbidden object key: ${u}.`), new TextEncoder().encode(u).byteLength > 1500 && R("Form definition contains an object key that is too long."), r(l, i + 1, !1);
2031
+ t.delete(c);
2032
+ };
2033
+ r(e, 1, !1);
2034
+ let n;
2035
+ try {
2036
+ n = JSON.stringify(e);
2037
+ } catch {
2038
+ R("Form definition must be valid JSON.");
2039
+ }
2040
+ new TextEncoder().encode(n).byteLength > At && R(`Form definition cannot exceed ${At} bytes.`);
2041
+ }
2042
+ function Gt(e) {
2043
+ const t = tt(e, "Request body"), s = t.definition;
2044
+ return Ks(s), {
2045
+ name: Wt(t.name, "Form name", zs),
2046
+ key: Ys(t.key),
2047
+ description: Gs(t.description),
2048
+ definition: s
2049
+ };
2050
+ }
2051
+ function Qs(e) {
2052
+ return Gt(e);
2053
+ }
2054
+ function Yt(e) {
2055
+ return (!Number.isInteger(e) || e < 1) && R("expectedRevision must be a positive integer."), e;
2056
+ }
2057
+ function Zs(e) {
2058
+ const t = tt(e, "Request body");
2059
+ return {
2060
+ ...Gt(t),
2061
+ expectedRevision: Yt(t.expectedRevision)
2062
+ };
2063
+ }
2064
+ function Be(e) {
2065
+ const t = tt(e, "Request body");
2066
+ return { expectedRevision: Yt(t.expectedRevision) };
2067
+ }
2068
+ function Xs(e) {
2069
+ return {
2070
+ id: e.revisionId,
2071
+ formId: e.formId,
2072
+ key: e.form.key,
2073
+ name: e.form.name,
2074
+ description: e.form.description || "",
2075
+ definition: e.form.draftDefinition,
2076
+ schemaVersion: e.form.schemaVersion || et,
2077
+ revision: e.revision,
2078
+ draftRevision: e.form.draftRevision,
2079
+ previousRevisionId: e.form.publishedRevisionId || null,
2080
+ publishedAt: e.publishedAt,
2081
+ publishedBy: e.publishedBy
2082
+ };
2083
+ }
2084
+ function he(e) {
2085
+ typeof e != "string" && R("Form ID is required.");
2086
+ const t = e.trim();
2087
+ return (!t || t === "." || t === ".." || t.includes("/") || /[\u0000-\u001f\u007f]/.test(t) || new TextEncoder().encode(t).byteLength > 1500) && R("Form ID is invalid."), t;
2088
+ }
2089
+ function Me(e) {
2090
+ const s = e?.context?.module?.userId || e?.context?.user?.id || null;
2091
+ return typeof s == "string" && s.trim() ? s : null;
2092
+ }
2093
+ function z(e, t) {
2094
+ const s = e?.context?.module;
2095
+ if (!s)
2096
+ throw g({ statusCode: 500, statusMessage: "Module context is unavailable." });
2097
+ if (!s.userId)
2098
+ throw g({ statusCode: 401, statusMessage: "Unauthorized" });
2099
+ if (s.isAdmin === !0) return;
2100
+ if (!(Array.isArray(s.permissions) ? s.permissions : []).includes(t))
2101
+ throw g({ statusCode: 403, statusMessage: `Missing module permission: ${t}.` });
2102
+ }
2103
+ function ye(e) {
2104
+ if (!e || typeof e != "object" || Array.isArray(e))
2105
+ throw g({ statusCode: 500, statusMessage: "Stored form data is invalid." });
2106
+ const t = e;
2107
+ if (typeof t.name != "string" || typeof t.key != "string" || !Ht.has(String(t.status)) || !Number.isInteger(t.revision) || !Number.isInteger(t.draftRevision) || !t.draftDefinition || typeof t.draftDefinition != "object")
2108
+ throw g({ statusCode: 500, statusMessage: "Stored form data is invalid." });
2109
+ return t;
2110
+ }
2111
+ function Ne(e, t) {
2112
+ e.revision !== t && ae(`Form has changed. Expected revision ${t}, current revision is ${e.revision}.`);
2113
+ }
2114
+ function ea(e) {
2115
+ (Array.isArray(e) || !Array.isArray(e.layout) || e.layout.length === 0) && R("Add at least one form element before publishing.");
2116
+ }
2117
+ function le(e, t, s, r = !0) {
2118
+ const { draftDefinition: n, ...a } = t, c = r ? { id: e, ...a, draftDefinition: n } : { id: e, ...a }, i = s ? s(c) : c, o = Number.isInteger(i.publishedRevision) ? i.publishedRevision : null, d = Number.isInteger(i.publishedDraftRevision) ? i.publishedDraftRevision : null;
2119
+ return {
2120
+ id: i.id,
2121
+ name: i.name,
2122
+ key: i.key,
2123
+ description: i.description || "",
2124
+ ...r ? { definition: n } : {},
2125
+ status: i.status,
2126
+ revision: i.revision,
2127
+ draftRevision: i.draftRevision,
2128
+ publishedRevision: o,
2129
+ publishedRevisionId: i.publishedRevisionId || null,
2130
+ hasUnpublishedChanges: !i.publishedRevisionId || i.draftRevision !== d,
2131
+ schemaVersion: i.schemaVersion || et,
2132
+ createdAt: i.createdAt || null,
2133
+ createdBy: i.createdBy || null,
2134
+ updatedAt: i.updatedAt || null,
2135
+ updatedBy: i.updatedBy || null,
2136
+ publishedAt: i.publishedAt || null,
2137
+ publishedBy: i.publishedBy || null,
2138
+ archivedAt: i.archivedAt || null,
2139
+ archivedBy: i.archivedBy || null
2140
+ };
2141
+ }
2142
+ function Dt(e, t) {
2143
+ if (!(e == null || e === ""))
2144
+ return (Array.isArray(e) || typeof e != "string") && R(`${t} must be a single string.`), e.trim();
2145
+ }
2146
+ function Tt(e) {
2147
+ if (typeof e == "string") {
2148
+ const t = Date.parse(e);
2149
+ return Number.isNaN(t) ? 0 : t;
2150
+ }
2151
+ return e instanceof Date ? e.getTime() : e && typeof e.toMillis == "function" ? e.toMillis() : 0;
2152
+ }
2153
+ function me(e, t, s) {
2154
+ throw Number.isInteger(t?.statusCode) && t.statusCode >= 400 && t.statusCode < 600 ? t : (console.error(`Form Builder handler — ${e} error:`, t), g({ statusCode: 500, statusMessage: s }));
2155
+ }
2156
+ function _a(e) {
2157
+ const { initFirebase: t, normalizeTimestamps: s } = e, r = J();
2158
+ async function n(a) {
2159
+ const c = a?.context?.module, { supabase: i, instanceId: o, projectId: d, moduleType: u } = c || {};
2160
+ if (!i || !o)
2161
+ throw g({ statusCode: 500, statusMessage: "Module context is unavailable." });
2162
+ const { data: l, error: f } = await i.from("project_modules").select("config, project_id, type").eq("id", o).single();
2163
+ if (f || !l?.config)
2164
+ throw g({ statusCode: 500, statusMessage: "Failed to load module config." });
2165
+ if (d && l.project_id !== d)
2166
+ throw g({ statusCode: 403, statusMessage: "Module project context does not match." });
2167
+ if (u && l.type !== u)
2168
+ throw g({ statusCode: 409, statusMessage: "Module type context does not match." });
2169
+ const p = l.config, m = p.databaseIntegration;
2170
+ if (typeof m != "string" || !m.trim())
2171
+ throw g({
2172
+ statusCode: 400,
2173
+ statusMessage: "Form Builder has no Firebase integration configured."
2174
+ });
2175
+ if (m.length > 200 || !/^[A-Za-z0-9_-]+$/.test(m))
2176
+ throw g({ statusCode: 500, statusMessage: "Configured Firebase integration ID is invalid." });
2177
+ const h = d || l.project_id, { data: y, error: w } = await i.from("integrations").select("id").eq("id", m).eq("project_id", h).eq("type", "FIREBASE").single();
2178
+ if (w || !y)
2179
+ throw g({
2180
+ statusCode: 400,
2181
+ statusMessage: "Configured Firebase integration is unavailable for this project."
2182
+ });
2183
+ const C = Js(p.formsCollection), A = he(o), M = (await t(a, m)).firestore();
2184
+ return {
2185
+ firestore: M,
2186
+ // Access is granted per module instance in Studio, so storage must be
2187
+ // isolated at the same boundary even when multiple instances retain the
2188
+ // default root collection.
2189
+ forms: M.collection(C).doc(A).collection("forms")
2190
+ };
2191
+ }
2192
+ return r.get("/forms", b(async (a) => {
2193
+ z(a, L.view);
2194
+ try {
2195
+ const c = v(a), i = Dt(c.status, "status"), o = Dt(c.search, "search");
2196
+ i && !Ht.has(i) && R("status must be draft, published, or archived."), o && o.length > St && R(`search cannot exceed ${St} characters.`);
2197
+ const { forms: d } = await n(a), u = i ? await d.where("status", "==", i).get() : await d.get(), l = o?.toLocaleLowerCase();
2198
+ return u.docs.map((f) => le(
2199
+ f.id,
2200
+ ye(f.data()),
2201
+ s,
2202
+ !1
2203
+ )).filter((f) => l ? [f.name, f.key, f.description].some((p) => String(p || "").toLocaleLowerCase().includes(l)) : !0).sort((f, p) => Tt(p.updatedAt) - Tt(f.updatedAt));
2204
+ } catch (c) {
2205
+ me("list", c, "Failed to list forms.");
2206
+ }
2207
+ })), r.get("/forms/:formId", b(async (a) => {
2208
+ z(a, L.view);
2209
+ const c = he(U(a, "formId"));
2210
+ try {
2211
+ const { forms: i } = await n(a), o = await i.doc(c).get();
2212
+ if (!o.exists)
2213
+ throw g({ statusCode: 404, statusMessage: "Form not found." });
2214
+ return le(o.id, ye(o.data()), s);
2215
+ } catch (i) {
2216
+ me("get", i, "Failed to load form.");
2217
+ }
2218
+ })), r.post("/forms", b(async (a) => {
2219
+ z(a, L.view), z(a, L.create);
2220
+ try {
2221
+ const c = Qs(await j(a)), i = Me(a), o = (/* @__PURE__ */ new Date()).toISOString(), { firestore: d, forms: u } = await n(a), l = u.doc(c.key), f = {
2222
+ name: c.name,
2223
+ key: c.key,
2224
+ description: c.description,
2225
+ status: "draft",
2226
+ draftDefinition: c.definition,
2227
+ schemaVersion: et,
2228
+ revision: 1,
2229
+ draftRevision: 1,
2230
+ publishedRevision: null,
2231
+ publishedRevisionId: null,
2232
+ publishedDraftRevision: null,
2233
+ createdAt: o,
2234
+ createdBy: i,
2235
+ updatedAt: o,
2236
+ updatedBy: i,
2237
+ draftUpdatedAt: o,
2238
+ draftUpdatedBy: i,
2239
+ publishedAt: null,
2240
+ publishedBy: null,
2241
+ archivedAt: null,
2242
+ archivedBy: null,
2243
+ statusBeforeArchive: null
2244
+ };
2245
+ return await d.runTransaction(async (p) => {
2246
+ (await p.get(l)).exists && ae(`A form with key "${c.key}" already exists.`), p.create(l, f);
2247
+ }), le(l.id, f, s);
2248
+ } catch (c) {
2249
+ me("create", c, "Failed to create form.");
2250
+ }
2251
+ })), r.patch("/forms/:formId/draft", b(async (a) => {
2252
+ z(a, L.view), z(a, L.edit);
2253
+ const c = he(U(a, "formId"));
2254
+ try {
2255
+ const i = Zs(await j(a)), o = Me(a), d = (/* @__PURE__ */ new Date()).toISOString(), { firestore: u, forms: l } = await n(a), f = l.doc(c);
2256
+ return await u.runTransaction(async (p) => {
2257
+ const m = await p.get(f);
2258
+ if (!m.exists)
2259
+ throw g({ statusCode: 404, statusMessage: "Form not found." });
2260
+ const h = ye(m.data());
2261
+ Ne(h, i.expectedRevision), h.status === "archived" && ae("Restore this form before editing its draft."), h.key !== i.key && ae("A form key cannot be changed after the form is created.");
2262
+ const y = {
2263
+ name: i.name,
2264
+ key: i.key,
2265
+ description: i.description,
2266
+ draftDefinition: i.definition,
2267
+ revision: h.revision + 1,
2268
+ draftRevision: h.draftRevision + 1,
2269
+ updatedAt: d,
2270
+ updatedBy: o,
2271
+ draftUpdatedAt: d,
2272
+ draftUpdatedBy: o
2273
+ };
2274
+ return p.update(f, y), le(c, { ...h, ...y }, s);
2275
+ });
2276
+ } catch (i) {
2277
+ me("update draft", i, "Failed to update form draft.");
2278
+ }
2279
+ })), r.post("/forms/:formId/publish", b(async (a) => {
2280
+ z(a, L.view), z(a, L.publish);
2281
+ const c = he(U(a, "formId"));
2282
+ try {
2283
+ const { expectedRevision: i } = Be(await j(a)), o = Me(a), d = (/* @__PURE__ */ new Date()).toISOString(), { firestore: u, forms: l } = await n(a), f = l.doc(c), p = f.collection("revisions").doc();
2284
+ return await u.runTransaction(async (m) => {
2285
+ const h = await m.get(f);
2286
+ if (!h.exists)
2287
+ throw g({ statusCode: 404, statusMessage: "Form not found." });
2288
+ const y = ye(h.data());
2289
+ Ne(y, i), y.status === "archived" && ae("Restore this form before publishing it."), ea(y.draftDefinition), y.publishedRevisionId && y.publishedDraftRevision === y.draftRevision && ae("This form has no unpublished changes.");
2290
+ const w = y.revision + 1, C = Xs({
2291
+ formId: c,
2292
+ form: y,
2293
+ revision: w,
2294
+ revisionId: p.id,
2295
+ publishedAt: d,
2296
+ publishedBy: o
2297
+ }), A = {
2298
+ status: "published",
2299
+ revision: w,
2300
+ publishedRevision: w,
2301
+ publishedRevisionId: p.id,
2302
+ publishedDraftRevision: y.draftRevision,
2303
+ publishedAt: d,
2304
+ publishedBy: o,
2305
+ updatedAt: d,
2306
+ updatedBy: o,
2307
+ archivedAt: null,
2308
+ archivedBy: null,
2309
+ statusBeforeArchive: null
2310
+ };
2311
+ return m.create(p, C), m.update(f, A), le(c, { ...y, ...A }, s);
2312
+ });
2313
+ } catch (i) {
2314
+ me("publish", i, "Failed to publish form.");
2315
+ }
2316
+ })), r.post("/forms/:formId/archive", b(async (a) => {
2317
+ z(a, L.view), z(a, L.archive);
2318
+ const c = he(U(a, "formId"));
2319
+ try {
2320
+ const { expectedRevision: i } = Be(await j(a)), o = Me(a), d = (/* @__PURE__ */ new Date()).toISOString(), { firestore: u, forms: l } = await n(a), f = l.doc(c);
2321
+ return await u.runTransaction(async (p) => {
2322
+ const m = await p.get(f);
2323
+ if (!m.exists)
2324
+ throw g({ statusCode: 404, statusMessage: "Form not found." });
2325
+ const h = ye(m.data());
2326
+ Ne(h, i), h.status === "archived" && ae("Form is already archived.");
2327
+ const y = {
2328
+ status: "archived",
2329
+ statusBeforeArchive: h.status,
2330
+ revision: h.revision + 1,
2331
+ archivedAt: d,
2332
+ archivedBy: o,
2333
+ updatedAt: d,
2334
+ updatedBy: o
2335
+ };
2336
+ return p.update(f, y), le(c, { ...h, ...y }, s);
2337
+ });
2338
+ } catch (i) {
2339
+ me("archive", i, "Failed to archive form.");
2340
+ }
2341
+ })), r.post("/forms/:formId/restore", b(async (a) => {
2342
+ z(a, L.view), z(a, L.archive);
2343
+ const c = he(U(a, "formId"));
2344
+ try {
2345
+ const { expectedRevision: i } = Be(await j(a)), o = Me(a), d = (/* @__PURE__ */ new Date()).toISOString(), { firestore: u, forms: l } = await n(a), f = l.doc(c);
2346
+ return await u.runTransaction(async (p) => {
2347
+ const m = await p.get(f);
2348
+ if (!m.exists)
2349
+ throw g({ statusCode: 404, statusMessage: "Form not found." });
2350
+ const h = ye(m.data());
2351
+ Ne(h, i), h.status !== "archived" && ae("Only archived forms can be restored.");
2352
+ const w = {
2353
+ status: h.publishedRevisionId ? "published" : "draft",
2354
+ statusBeforeArchive: null,
2355
+ revision: h.revision + 1,
2356
+ archivedAt: null,
2357
+ archivedBy: null,
2358
+ updatedAt: d,
2359
+ updatedBy: o
2360
+ };
2361
+ return p.update(f, w), le(c, { ...h, ...w }, s);
2362
+ });
2363
+ } catch (i) {
2364
+ me("restore", i, "Failed to restore form.");
2365
+ }
2366
+ })), r.handler;
2367
+ }
2368
+ const Jt = ["authentication", "billing", "notifications", "onboarding", "system"], Kt = ["draft", "active", "inactive"];
2369
+ function ta(e) {
2370
+ const t = e.context?.module;
2371
+ if (!t?.supabase || !t.instanceId || !t.projectId || !t.userId || t.moduleType !== "mailTemplates")
2372
+ throw g({ statusCode: 403, statusMessage: "Mail Templates module access is required." });
2373
+ return t;
2374
+ }
2375
+ function we(e, t) {
2376
+ const s = ta(e), r = Array.isArray(t) ? t : t ? [t] : [];
2377
+ if (r.length && !s.isAdmin && !r.some((n) => s.permissions?.includes(n)))
2378
+ throw g({ statusCode: 403, statusMessage: "You do not have permission for this template action." });
2379
+ return se(e, "Cache-Control", "private, no-store"), s;
2380
+ }
2381
+ function sa(e) {
2382
+ const t = typeof e == "string" ? e.trim() : "";
2383
+ if (!t || t.length > 512 || /[\\\u0000-\u001f\u007f]/.test(t) || t.split("/").length % 2 !== 1 || t.split("/").some((s) => !s || s === "." || s === ".." || /^__.*__$/.test(s)))
2384
+ throw g({ statusCode: 400, statusMessage: "Configure a valid Firestore templates collection." });
2385
+ return t;
2386
+ }
2387
+ function Le(e) {
2388
+ const t = U(e, "templateId");
2389
+ if (!t || t.length > 200 || /[\/\\\u0000-\u001f\u007f]/.test(t) || t === "." || t === ".." || /^__.*__$/.test(t))
2390
+ throw g({ statusCode: 400, statusMessage: "Invalid template ID." });
2391
+ return t;
2392
+ }
2393
+ function Ue(e, t, s, r = !1) {
2394
+ if (r && e === void 0) return "";
2395
+ if (typeof e != "string" || e.length > s || !r && !e.trim())
2396
+ throw g({ statusCode: 400, statusMessage: `${t} is required and must be at most ${s} characters.` });
2397
+ return e.trim();
2398
+ }
2399
+ function ze(e) {
2400
+ if (!e || !Array.isArray(e.blocks) || e.blocks.length > 200 || JSON.stringify(e.blocks).length > 4e5)
2401
+ throw g({ statusCode: 400, statusMessage: "Provide up to 200 email blocks (400 KB maximum)." });
2402
+ if (!e.styles || typeof e.styles != "object" || Array.isArray(e.styles) || JSON.stringify(e.styles).length > 2e4)
2403
+ throw g({ statusCode: 400, statusMessage: "Provide valid email styles." });
2404
+ return { blocks: e.blocks, styles: e.styles, preheader: Ue(e.preheader, "Preheader", 300, !0) };
2405
+ }
2406
+ function aa(e) {
2407
+ if (e.preserveVariables === !0) return {};
2408
+ const t = e.variables && typeof e.variables == "object" && !Array.isArray(e.variables) ? e.variables : {};
2409
+ if (JSON.stringify(t).length > 5e4) throw g({ statusCode: 400, statusMessage: "Preview variables are too large." });
2410
+ return { year: (/* @__PURE__ */ new Date()).getFullYear().toString(), ...Object.fromEntries(Object.entries(t).filter(([s, r]) => /^\w+$/.test(s) && typeof r == "string")) };
2411
+ }
2412
+ function oa(e, t) {
2413
+ return e.replace(/\{\{(\w+)\}\}/g, (s, r) => Object.prototype.hasOwnProperty.call(t, r) ? t[r] : s);
2414
+ }
2415
+ function Se(e) {
2416
+ throw Number.isInteger(e?.statusCode) && e.statusCode >= 400 && e.statusCode < 600 ? e : g({ statusCode: 500, statusMessage: "The template request could not be completed. Check the Firebase integration and try again." });
2417
+ }
2418
+ function Ve(e, t = !0) {
2419
+ const s = e.data() || {};
2420
+ return {
2421
+ id: e.id,
2422
+ name: typeof s.name == "string" ? s.name : "Untitled template",
2423
+ subject: typeof s.subject == "string" ? s.subject : "",
2424
+ description: typeof s.description == "string" ? s.description : "",
2425
+ preheader: typeof s.preheader == "string" ? s.preheader : "",
2426
+ category: Jt.includes(s.category) ? s.category : "notifications",
2427
+ status: Kt.includes(s.status) ? s.status : "draft",
2428
+ blocks: Array.isArray(s.blocks) ? s.blocks : [],
2429
+ styles: s.styles && typeof s.styles == "object" ? s.styles : {},
2430
+ createdAt: typeof s.createdAt == "string" ? s.createdAt : s.createdAt?.toDate?.().toISOString() || null,
2431
+ updatedAt: typeof s.updatedAt == "string" ? s.updatedAt : s.updatedAt?.toDate?.().toISOString() || null,
2432
+ ...t ? { mjml: typeof s.mjml == "string" ? s.mjml : "", html: typeof s.html == "string" ? s.html : "" } : {}
2433
+ };
2434
+ }
2435
+ function Ra(e) {
2436
+ const t = J();
2437
+ async function s(a, c) {
2438
+ const { data: i, error: o } = await c.supabase.from("project_modules").select("type, project_id, config").eq("id", c.instanceId).maybeSingle();
2439
+ if (o || i?.type !== "mailTemplates" || i.project_id !== c.projectId)
2440
+ throw g({ statusCode: 404, statusMessage: "Mail Templates module is unavailable." });
2441
+ const d = i.config?.project;
2442
+ if (typeof d != "string" || !d) throw g({ statusCode: 400, statusMessage: "Choose a Firebase integration in the module settings." });
2443
+ const u = sa(i.config?.templateCollection || "mail-templates"), { data: l, error: f } = await c.supabase.from("integrations").select("id, type").eq("id", d).eq("project_id", c.projectId).maybeSingle();
2444
+ if (f) throw g({ statusCode: 500, statusMessage: "Could not verify the Firebase integration." });
2445
+ if (!l || l.type !== "FIREBASE")
2446
+ throw g({ statusCode: 400, statusMessage: "The Firebase integration is unavailable for this project." });
2447
+ const m = (await e.initFirebase(a, l.id)).firestore();
2448
+ return { db: m, collection: m.collection(u) };
2449
+ }
2450
+ function r(a, c = {}) {
2451
+ try {
2452
+ const i = e.mailTemplates.render({ ...ze(a), variables: c });
2453
+ if (i.errors.length) throw g({ statusCode: 400, statusMessage: "Fix the MJML validation errors before saving.", data: { errors: i.errors } });
2454
+ return i;
2455
+ } catch (i) {
2456
+ throw i.statusCode ? i : g({ statusCode: 400, statusMessage: "The email contains invalid blocks or styles." });
2457
+ }
2458
+ }
2459
+ function n(a) {
2460
+ const c = Ue(a?.name, "Template name", 120), i = Ue(a?.subject, "Subject", 300), o = Ue(a?.description, "Description", 1e3, !0), d = a.category ?? "notifications", u = a.status ?? "draft";
2461
+ if (!Jt.includes(d) || !Kt.includes(u)) throw g({ statusCode: 400, statusMessage: "Invalid template category or status." });
2462
+ const l = r(a), f = { name: c, subject: i, description: o, category: d, status: u, ...ze(a), html: l.html, mjml: l.mjml };
2463
+ if (new TextEncoder().encode(JSON.stringify(f)).length > 9e5) throw g({ statusCode: 400, statusMessage: "This compiled template exceeds the storage limit. Reduce the content." });
2464
+ return f;
2465
+ }
2466
+ return t.get("/templates", b(async (a) => {
2467
+ const c = we(a);
2468
+ try {
2469
+ const { collection: i } = await s(a, c), o = await i.limit(101).get();
2470
+ if (o.docs.length > 100) throw g({ statusCode: 400, statusMessage: "This collection contains more than 100 templates. Configure a dedicated templates collection." });
2471
+ return o.docs.map((d) => Ve(d, !1)).sort((d, u) => (u.updatedAt || "").localeCompare(d.updatedAt || "") || d.name.localeCompare(u.name));
2472
+ } catch (i) {
2473
+ Se(i);
2474
+ }
2475
+ })), t.get("/templates/:templateId", b(async (a) => {
2476
+ const c = we(a), i = Le(a);
2477
+ try {
2478
+ const { collection: o } = await s(a, c), d = await o.doc(i).get();
2479
+ if (!d.exists) throw g({ statusCode: 404, statusMessage: "Template not found." });
2480
+ return Ve(d);
2481
+ } catch (o) {
2482
+ Se(o);
2483
+ }
2484
+ })), t.post("/templates", b(async (a) => {
2485
+ const c = we(a, "create-template"), i = n(await j(a));
2486
+ try {
2487
+ const { collection: o } = await s(a, c), d = o.doc(), u = (/* @__PURE__ */ new Date()).toISOString(), l = { ...i, createdAt: u, updatedAt: u };
2488
+ return await d.create(l), { id: d.id, ...l };
2489
+ } catch (o) {
2490
+ Se(o);
2491
+ }
2492
+ })), t.patch("/templates/:templateId", b(async (a) => {
2493
+ const c = we(a, "edit-template"), i = Le(a), o = await j(a), d = n(o);
2494
+ try {
2495
+ const { db: u, collection: l } = await s(a, c), f = l.doc(i);
2496
+ return await u.runTransaction(async (p) => {
2497
+ const m = await p.get(f);
2498
+ if (!m.exists) throw g({ statusCode: 404, statusMessage: "Template not found." });
2499
+ const h = Ve(m);
2500
+ if (o.updatedAt !== void 0 && o.updatedAt !== h.updatedAt)
2501
+ throw g({ statusCode: 409, statusMessage: "This template was updated elsewhere. Reload it before saving." });
2502
+ const y = Math.max(Date.now(), (Date.parse(h.updatedAt || "") || 0) + 1), w = { ...d, updatedAt: new Date(y).toISOString() };
2503
+ return p.update(f, w), { ...h, ...w };
2504
+ });
2505
+ } catch (u) {
2506
+ Se(u);
2507
+ }
2508
+ })), t.delete("/templates/:templateId", b(async (a) => {
2509
+ const c = we(a, "delete-template"), i = Le(a);
2510
+ try {
2511
+ const { db: o, collection: d } = await s(a, c), u = d.doc(i);
2512
+ return await o.runTransaction(async (l) => {
2513
+ if (!(await l.get(u)).exists) throw g({ statusCode: 404, statusMessage: "Template not found." });
2514
+ l.delete(u);
2515
+ }), { success: !0 };
2516
+ } catch (o) {
2517
+ Se(o);
2518
+ }
2519
+ })), t.post("/render", b(async (a) => {
2520
+ we(a);
2521
+ const c = await j(a), i = ze(c), o = aa(c);
2522
+ try {
2523
+ return { ...e.mailTemplates.render({ ...i, variables: o }), subject: oa(typeof c.subject == "string" ? c.subject : "", o) };
2524
+ } catch {
2525
+ throw g({ statusCode: 400, statusMessage: "The email contains invalid blocks or styles." });
2526
+ }
2527
+ })), t.handler;
2528
+ }
2529
+ const na = /* @__PURE__ */ new Set(["FIREBASE", "FTP", "SFTP", "S3", "CLOUDFLARE_R2"]);
2530
+ function ra(e) {
2531
+ const t = e?.context?.module;
2532
+ if (!t?.supabase || !t?.instanceId || !t?.projectId || t.moduleType !== "filezone")
2533
+ throw g({ statusCode: 500, statusMessage: "File Zone context is unavailable." });
2534
+ return t;
2535
+ }
2536
+ function He(e, t = !1) {
2537
+ const s = ra(e);
2538
+ if (t ? !s.isAdmin : !s.isAdmin && !s.permissions.includes("view-files"))
2539
+ throw g({ statusCode: 403, statusMessage: t ? "Only Studio administrators can upload files." : "You cannot view files in this module." });
2540
+ return s;
2541
+ }
2542
+ function De(e, t = !1) {
2543
+ if (e == null || e === "") {
2544
+ if (t) return "";
2545
+ throw g({ statusCode: 400, statusMessage: "A file path is required." });
2546
+ }
2547
+ if (typeof e != "string" || e.length > 1024 || e.startsWith("/") || e.endsWith("/") || /[\\\u0000-\u001f\u007f]/.test(e) || e.split("/").some((s) => !s || s === "." || s === ".."))
2548
+ throw g({ statusCode: 400, statusMessage: "Invalid storage path." });
2549
+ return e;
2550
+ }
2551
+ function vt(e) {
2552
+ const t = e.replace(/\\/g, "/").split("/").pop()?.trim() || "";
2553
+ if (!t || t.length > 180 || /[\u0000-\u001f\u007f]/.test(t) || t === "." || t === "..")
2554
+ throw g({ statusCode: 400, statusMessage: "File name is invalid or too long." });
2555
+ return t;
2556
+ }
2557
+ function Ee(...e) {
2558
+ return e.filter(Boolean).join("/");
2559
+ }
2560
+ function _t(e) {
2561
+ 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}-/, "");
2562
+ }
2563
+ function Rt(e) {
2564
+ const t = e.split(".").pop()?.toLowerCase() || "";
2565
+ 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";
2566
+ }
2567
+ function We(e, t) {
2568
+ throw Number.isInteger(e?.statusCode) && e.statusCode >= 400 && e.statusCode < 600 ? e : (console.error("File Zone " + t + " failed:", e), g({ statusCode: 500, statusMessage: "Could not " + t + " file." }));
2569
+ }
2570
+ function xa({ filezone: e }) {
2571
+ const t = J();
2572
+ async function s(r) {
2573
+ const { data: n, error: a } = await r.supabase.from("project_modules").select("type, project_id, config").eq("id", r.instanceId).maybeSingle();
2574
+ if (a || n?.type !== "filezone" || n.project_id !== r.projectId)
2575
+ throw g({ statusCode: 404, statusMessage: "File Zone module is unavailable." });
2576
+ const c = n.config?.storageIntegration;
2577
+ if (typeof c != "string" || !/^[a-f0-9-]{36}$/i.test(c))
2578
+ throw g({ statusCode: 400, statusMessage: "File Zone has no valid storage integration." });
2579
+ const { data: i, error: o } = await r.supabase.from("integrations").select("id, type, config").eq("id", c).eq("project_id", r.projectId).maybeSingle();
2580
+ if (o) throw g({ statusCode: 500, statusMessage: "Could not check storage integration." });
2581
+ if (!i || !na.has(i.type))
2582
+ throw g({ statusCode: 400, statusMessage: "The storage integration is unavailable for this project." });
2583
+ return { provider: i, root: De(n.config?.folderPath, !0) };
2584
+ }
2585
+ return t.get("/files", b(async (r) => {
2586
+ const n = He(r), a = v(r), c = De(a.folder, !0), i = a.cursor === void 0 ? null : typeof a.cursor == "string" && a.cursor.length <= 4096 ? a.cursor : null;
2587
+ if (a.cursor !== void 0 && i === null) throw g({ statusCode: 400, statusMessage: "Invalid file page." });
2588
+ try {
2589
+ const { provider: o, root: d } = await s(n), { objects: u, nextCursor: l } = await e.list(r, o, Ee(d, c), i), f = u.flatMap((p) => {
2590
+ const m = d ? p.key.startsWith(d + "/") ? p.key.slice(d.length + 1) : "" : p.key;
2591
+ if (!m || m.split("/").slice(0, -1).join("/") !== c) return [];
2592
+ try {
2593
+ De(m);
2594
+ } catch {
2595
+ return [];
2596
+ }
2597
+ const h = _t(p.name);
2598
+ return [{
2599
+ path: m,
2600
+ name: h,
2601
+ directory: p.directory,
2602
+ size_bytes: p.size,
2603
+ content_type: p.directory ? null : p.contentType || Rt(h),
2604
+ modified_at: p.modified
2605
+ }];
2606
+ });
2607
+ return se(r, "Cache-Control", "private, no-store"), { files: f, nextCursor: l };
2608
+ } catch (o) {
2609
+ We(o, "list");
2610
+ }
2611
+ })), t.post("/files", b(async (r) => {
2612
+ const n = He(r, !0), a = De(v(r).folder, !0), { provider: c, root: i } = await s(n);
2613
+ let o;
2614
+ try {
2615
+ o = await e.parseUpload(r);
2616
+ const d = vt(o.name);
2617
+ if (!Number.isInteger(o.size) || o.size < 1 || o.size > e.maxBytes)
2618
+ throw g({ statusCode: 413, statusMessage: "File must be between 1 byte and 25 MB." });
2619
+ const u = globalThis.crypto.randomUUID() + "-" + d, l = Ee(i, a, u), f = o.contentType && /^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/i.test(o.contentType) ? o.contentType.toLowerCase() : "application/octet-stream";
2620
+ try {
2621
+ await e.upload(r, c, l, o.localPath, f);
2622
+ } catch (p) {
2623
+ throw await e.remove(r, c, l).catch((m) => console.error("File Zone partial upload cleanup failed:", m)), p;
2624
+ }
2625
+ return se(r, "Cache-Control", "private, no-store"), {
2626
+ path: Ee(a, u),
2627
+ name: d,
2628
+ directory: !1,
2629
+ size_bytes: o.size,
2630
+ content_type: f,
2631
+ modified_at: (/* @__PURE__ */ new Date()).toISOString()
2632
+ };
2633
+ } catch (d) {
2634
+ We(d, "upload");
2635
+ } finally {
2636
+ await o?.cleanup();
2637
+ }
2638
+ })), t.get("/files/content", b(async (r) => {
2639
+ const n = He(r), a = v(r), c = De(a.path), i = _t(vt(c.split("/").pop())), { provider: o, root: d } = await s(n);
2640
+ try {
2641
+ const u = await e.read(r, o, Ee(d, c)), l = Rt(i), f = a.view === "1" && /^(application\/pdf|image\/(jpeg|png|gif|webp))$/.test(l), p = i.replace(/[^\x20-\x7e]|["\\]/g, "_");
2642
+ return se(r, "Content-Type", f ? l : "application/octet-stream"), se(r, "Content-Disposition", (f ? "inline" : "attachment") + '; filename="' + p + `"; filename*=UTF-8''` + encodeURIComponent(i)), se(r, "X-Content-Type-Options", "nosniff"), se(r, "Cache-Control", "private, no-store"), se(r, "Content-Security-Policy", "sandbox"), u;
2643
+ } catch (u) {
2644
+ We(u, "read");
2645
+ }
2646
+ })), t.handler;
2647
+ }
2648
+ const ia = "https://graph.facebook.com/v25.0/";
2649
+ class F extends Error {
2650
+ constructor(t, s, r = null) {
2651
+ super(s), this.statusCode = t, this.code = r, this.name = "WhatsAppProviderError";
2652
+ }
2653
+ statusCode;
2654
+ code;
2655
+ }
2656
+ function Qt(e = fetch) {
2657
+ return async (t, s, r = {}) => {
2658
+ if (!/^\d{1,30}(?:\/(?:phone_numbers|message_templates|template_analytics|whatsapp_business_profile))?$/.test(s))
2659
+ throw new F(500, "Invalid WhatsApp API operation.");
2660
+ const n = new URL(s, ia);
2661
+ for (const [i, o] of Object.entries(r.query || {}))
2662
+ o !== void 0 && i !== "access_token" && n.searchParams.set(i, String(o));
2663
+ const a = new AbortController(), c = setTimeout(() => a.abort(), Math.max(1, Math.min(15e3, r.timeoutMs || 15e3)));
2664
+ try {
2665
+ const i = await e(n.toString(), {
2666
+ method: r.method || "GET",
2667
+ headers: { Authorization: `Bearer ${t}`, "Content-Type": "application/json" },
2668
+ ...r.body ? { body: JSON.stringify(r.body) } : {},
2669
+ signal: a.signal,
2670
+ redirect: "error"
2671
+ });
2672
+ if (Number(i.headers.get("content-length")) > 4e6)
2673
+ throw new F(502, "Meta returned too much data. Narrow the request.");
2674
+ const o = i.body?.getReader(), d = [];
2675
+ let u = 0;
2676
+ if (o)
2677
+ for (; ; ) {
2678
+ const m = await o.read();
2679
+ if (m.done) break;
2680
+ if (u += m.value.byteLength, u > 4e6)
2681
+ throw await o.cancel(), new F(502, "Meta returned too much data. Narrow the request.");
2682
+ d.push(m.value);
2683
+ }
2684
+ const l = new Uint8Array(u);
2685
+ let f = 0;
2686
+ for (const m of d)
2687
+ l.set(m, f), f += m.byteLength;
2688
+ let p;
2689
+ try {
2690
+ p = JSON.parse(new TextDecoder().decode(l));
2691
+ } catch {
2692
+ throw new F(502, "Meta returned an unreadable response.");
2693
+ }
2694
+ if (!i.ok || p?.error) {
2695
+ const m = Number.isSafeInteger(p?.error?.code) ? p.error.code : null;
2696
+ throw m === 190 || i.status === 401 ? new F(424, "The WhatsApp access token is invalid or expired. Reconnect it in Studio project integrations.", m) : [10, 200, 294].includes(m) || i.status === 403 ? new F(424, "Meta denied this operation. Check the token permissions and access to this WhatsApp account.", m) : [4, 17, 32, 613, 80007, 130429].includes(m) || i.status === 429 ? new F(429, "Meta is limiting requests. Wait before trying again.", m) : new F(
2697
+ i.status >= 500 ? 502 : 422,
2698
+ `Meta could not complete this request${m === null ? "" : ` (code ${m})`}. Check the account and template requirements.`,
2699
+ m
2700
+ );
2701
+ }
2702
+ if (!p || typeof p != "object" || Array.isArray(p))
2703
+ throw new F(502, "Meta returned an unexpected response.");
2704
+ return p;
2705
+ } catch (i) {
2706
+ throw i instanceof F ? i : new F(
2707
+ a.signal.aborted ? 504 : 502,
2708
+ a.signal.aborted ? "The WhatsApp request timed out. Refresh before retrying a change." : "Could not reach the WhatsApp API."
2709
+ );
2710
+ } finally {
2711
+ clearTimeout(c);
2712
+ }
2713
+ };
2714
+ }
2715
+ function Je(e, t, s) {
2716
+ const r = Date.now() + s;
2717
+ let n = 0;
2718
+ return async (a, c, i = {}) => {
2719
+ const o = r - Date.now();
2720
+ if (++n > t || o <= 0)
2721
+ throw new F(504, "The WhatsApp data request reached its time or page limit. Try a shorter period.");
2722
+ return e(a, c, { ...i, timeoutMs: Math.min(15e3, o) });
2723
+ };
2724
+ }
2725
+ function fe(e) {
2726
+ throw e instanceof F ? g({ statusCode: e.statusCode, statusMessage: e.message }) : g({ statusCode: 502, statusMessage: "Could not complete the WhatsApp request." });
2727
+ }
2728
+ function S(e, t = 2048) {
2729
+ return typeof e == "string" && e.length ? e.slice(0, t) : null;
2730
+ }
2731
+ function Te(e) {
2732
+ const t = e.paging?.cursors?.after;
2733
+ return e.paging?.next && typeof t == "string" && t.length <= 2048 && !/[\r\n]/.test(t) ? t : null;
2734
+ }
2735
+ function ve(e) {
2736
+ if (!Array.isArray(e.data) || e.data.length > 1e4)
2737
+ throw new F(502, "Meta returned an unexpected response.");
2738
+ return e.data.filter((t) => t !== null && typeof t == "object" && !Array.isArray(t));
2739
+ }
2740
+ const st = (e) => !/[{}]/.test(e), Ge = x().trim().min(1).max(25).refine(st, "Button labels cannot contain variables."), ca = rs("type", [
2741
+ O({ type: ke("QUICK_REPLY"), text: Ge }).strict(),
2742
+ O({ type: ke("URL"), text: Ge, url: x().trim().url().max(2e3).refine((e) => /^https:\/\//i.test(e) && st(e), "Use a static HTTPS URL without variables.") }).strict(),
2743
+ O({ type: ke("PHONE_NUMBER"), text: Ge, phoneNumber: x().regex(/^\+[1-9]\d{1,14}$/, "Use an international phone number starting with +.") }).strict()
2744
+ ]), Zt = O({
2745
+ name: x().min(1).max(512).regex(/^[a-z0-9_]+$/, "Template names use lowercase letters, numbers and underscores."),
2746
+ language: x().regex(/^[a-z]{2,3}(?:_[A-Z]{2})?$/, "Use a WhatsApp language code, for example en_US or nl."),
2747
+ category: Ke(["UTILITY", "MARKETING"]),
2748
+ headerText: x().max(60).optional(),
2749
+ headerExample: x().trim().max(1024).optional(),
2750
+ bodyText: x().min(1).max(1024).refine((e) => e.trim().length > 0, "Body text is required."),
2751
+ bodyExamples: it(x().trim().max(1024)).max(20).optional(),
2752
+ footerText: x().max(60).refine(st, "Footer text cannot contain variables.").optional(),
2753
+ buttons: it(ca).max(3).optional()
2754
+ }).strict();
2755
+ function ie(e) {
2756
+ const t = [...e.matchAll(/\{\{([1-9]\d*)\}\}/g)].map((n) => n[1]), s = e.replace(/\{\{[1-9]\d*\}\}/g, "");
2757
+ if (/[{}]/.test(s)) throw new Error("Use numbered variables such as {{1}} and {{2}}.");
2758
+ const r = [...new Set(t)].sort((n, a) => Number(n) - Number(a));
2759
+ if (r.some((n, a) => Number(n) !== a + 1)) throw new Error("Number variables consecutively starting at {{1}}.");
2760
+ return r;
2761
+ }
2762
+ function Xt(e, t) {
2763
+ const s = ie(e.headerText || ""), r = ie(e.bodyText);
2764
+ if (s.length > 1) throw new Error("A text header supports one numbered variable.");
2765
+ if (r.length > 20) throw new Error("This editor supports up to 20 body variables.");
2766
+ if (t && s.length && !e.headerExample?.trim()) throw new Error("Provide an example for the header variable.");
2767
+ if (t && (e.bodyExamples?.length || 0) !== r.length) throw new Error("Provide exactly one example for each body variable.");
2768
+ if (t && e.bodyExamples?.some((c) => !c.trim())) throw new Error("Variable examples cannot be empty.");
2769
+ const n = e.buttons || [], a = n.filter((c) => c.type === "QUICK_REPLY").length;
2770
+ if (a && a !== n.length) throw new Error("Use either quick replies or action buttons in this editor.");
2771
+ if (n.filter((c) => c.type === "URL").length > 1 || n.filter((c) => c.type === "PHONE_NUMBER").length > 1)
2772
+ throw new Error("Use at most one website and one phone button.");
2773
+ }
2774
+ function xt(e) {
2775
+ const t = Zt.safeParse(e);
2776
+ if (!t.success) throw g({ statusCode: 400, statusMessage: t.error.issues[0]?.message || "Invalid template." });
2777
+ try {
2778
+ Xt(t.data, !0);
2779
+ } catch (s) {
2780
+ throw g({ statusCode: 400, statusMessage: s.message });
2781
+ }
2782
+ return t.data;
2783
+ }
2784
+ function Ft(e) {
2785
+ const t = [];
2786
+ return e.headerText && t.push({
2787
+ type: "HEADER",
2788
+ format: "TEXT",
2789
+ text: e.headerText,
2790
+ ...ie(e.headerText).length ? { example: { header_text: [e.headerExample] } } : {}
2791
+ }), t.push({
2792
+ type: "BODY",
2793
+ text: e.bodyText,
2794
+ ...ie(e.bodyText).length ? { example: { body_text: [e.bodyExamples] } } : {}
2795
+ }), 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;
2796
+ }
2797
+ const Pe = (e, t) => Object.keys(e).every((s) => t.includes(s));
2798
+ function da(e) {
2799
+ if (!["UTILITY", "MARKETING"].includes(e.category) || e.parameter_format && e.parameter_format !== "POSITIONAL" || e.source === "AUTO_GENERATED" || !Array.isArray(e.components)) return null;
2800
+ const t = { name: e.name, language: e.language, category: e.category, bodyText: "" }, s = /* @__PURE__ */ new Set();
2801
+ for (const n of e.components) {
2802
+ if (!n || typeof n != "object" || s.has(n.type) || (s.add(n.type), !Pe(n, ["type", "format", "text", "example", "buttons"]))) return null;
2803
+ if (n.type === "HEADER") {
2804
+ if (n.format !== "TEXT" || typeof n.text != "string" || n.buttons || n.example && !Pe(n.example, ["header_text"])) return null;
2805
+ t.headerText = n.text, t.headerExample = n.example?.header_text?.[0] || "";
2806
+ } else if (n.type === "BODY") {
2807
+ if (typeof n.text != "string" || n.format || n.buttons || n.example && !Pe(n.example, ["body_text"])) return null;
2808
+ t.bodyText = n.text, t.bodyExamples = n.example?.body_text?.[0] || [];
2809
+ } else if (n.type === "FOOTER") {
2810
+ if (typeof n.text != "string" || n.format || n.buttons || n.example) return null;
2811
+ t.footerText = n.text;
2812
+ } else if (n.type === "BUTTONS") {
2813
+ if (!Array.isArray(n.buttons) || n.format || n.text || n.example) return null;
2814
+ t.buttons = [];
2815
+ for (const a of n.buttons) {
2816
+ if (!a || typeof a != "object" || !Pe(a, ["type", "text", "url", "phone_number"])) return null;
2817
+ if (a.type === "QUICK_REPLY" && !a.url && !a.phone_number) t.buttons.push({ type: "QUICK_REPLY", text: a.text });
2818
+ else if (a.type === "URL" && !a.phone_number) t.buttons.push({ type: "URL", text: a.text, url: a.url });
2819
+ else if (a.type === "PHONE_NUMBER" && !a.url) t.buttons.push({ type: "PHONE_NUMBER", text: a.text, phoneNumber: a.phone_number });
2820
+ else return null;
2821
+ }
2822
+ } else return null;
2823
+ }
2824
+ if (!s.has("BODY")) return null;
2825
+ const r = Zt.safeParse(t);
2826
+ if (!r.success) return null;
2827
+ try {
2828
+ Xt(r.data, !1);
2829
+ } catch {
2830
+ return null;
2831
+ }
2832
+ return r.data;
2833
+ }
2834
+ function Nt(e) {
2835
+ const t = da(e), s = ["APPROVED", "REJECTED", "PAUSED"].includes(e.status), r = (Array.isArray(e.components) ? e.components : []).slice(0, 20).map((n) => ({
2836
+ type: S(n?.type, 64) || "UNKNOWN",
2837
+ ...S(n?.format, 64) ? { format: n.format } : {},
2838
+ ...S(n?.text, 32768) ? { text: n.text.slice(0, 32768) } : {},
2839
+ ...Array.isArray(n?.buttons) ? { buttons: n.buttons.slice(0, 20).map((a) => ({
2840
+ type: S(a?.type, 64) || "UNKNOWN",
2841
+ ...S(a?.text, 1024) ? { text: a.text.slice(0, 1024) } : {},
2842
+ ...S(a?.url, 2048) ? { url: a.url.slice(0, 2048) } : {},
2843
+ ...S(a?.phone_number, 64) ? { phoneNumber: a.phone_number.slice(0, 64) } : {}
2844
+ })) } : {}
2845
+ }));
2846
+ return {
2847
+ id: S(e.id, 30) || "",
2848
+ name: S(e.name, 512) || "",
2849
+ language: S(e.language, 20) || "",
2850
+ category: S(e.category, 64) || "UNKNOWN",
2851
+ status: S(e.status, 64) || "UNKNOWN",
2852
+ quality: S(e.quality_score?.score, 64),
2853
+ rejectionReason: S(e.rejected_reason),
2854
+ components: r,
2855
+ editable: t !== null && s,
2856
+ 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.",
2857
+ draft: t
2858
+ };
2859
+ }
2860
+ function ua(e, t) {
2861
+ if (!e.editable || !e.draft) throw g({ statusCode: 409, statusMessage: e.editReason || "This template cannot be edited here." });
2862
+ if (t.name !== e.name || t.language !== e.language || t.category !== e.category)
2863
+ throw g({ statusCode: 400, statusMessage: "Name, language and category must remain unchanged. Create a new template to change them." });
2864
+ const s = e.draft;
2865
+ if (JSON.stringify(ie(s.bodyText)) !== JSON.stringify(ie(t.bodyText)) || JSON.stringify(ie(s.headerText || "")) !== JSON.stringify(ie(t.headerText || "")))
2866
+ throw g({ statusCode: 400, statusMessage: "Create a new template to change variables used by existing automations." });
2867
+ if (JSON.stringify((s.buttons || []).map((r) => r.type)) !== JSON.stringify((t.buttons || []).map((r) => r.type)))
2868
+ throw g({ statusCode: 400, statusMessage: "Create a new template to change button count, types or order used by existing automations." });
2869
+ }
2870
+ const pe = (e) => typeof e == "number" && Number.isFinite(e) && e >= 0 ? e : null, oe = (e) => e.length && e.every((t) => t !== null) ? e.reduce((t, s) => t + s, 0) : null;
2871
+ function la(e) {
2872
+ if (!e.analytics || !Array.isArray(e.analytics.data_points))
2873
+ return { state: "unavailable", reason: "Meta did not provide message analytics for this account and period.", data: null };
2874
+ 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: pe(s.sent), delivered: pe(s.delivered) }));
2875
+ return t.length !== e.analytics.data_points.length ? { state: "unavailable", reason: "Meta returned incomplete message analytics.", data: null } : {
2876
+ state: t.length ? "available" : "empty",
2877
+ reason: t.length ? null : "Meta returned no message data for this period.",
2878
+ data: { sent: oe(t.map((s) => s.sent)), delivered: oe(t.map((s) => s.delivered)), points: t }
2879
+ };
2880
+ }
2881
+ function ma(e, t, s, r) {
2882
+ if (e.some((o) => !Array.isArray(o.data_points)))
2883
+ return { state: "unavailable", reason: "Meta returned an unsupported template analytics response.", data: null };
2884
+ const n = new Map(t.map((o) => [o.id, o])), a = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Set();
2885
+ for (const o of e)
2886
+ for (const d of o.data_points) {
2887
+ if (!d || !n.has(d.template_id) || !Number.isSafeInteger(d.start) || !Number.isSafeInteger(d.end)) continue;
2888
+ const u = `${d.template_id}:${d.start}:${d.end}`;
2889
+ if (c.has(u)) continue;
2890
+ c.add(u);
2891
+ const l = a.get(d.template_id) || [];
2892
+ l.push(d), a.set(d.template_id, l);
2893
+ }
2894
+ const i = t.map((o) => {
2895
+ const d = a.get(o.id) || [];
2896
+ return {
2897
+ id: o.id,
2898
+ name: S(o.name, 512) || o.id,
2899
+ language: S(o.language, 20) || "",
2900
+ sent: oe(d.map((u) => pe(u.sent))),
2901
+ delivered: oe(d.map((u) => pe(u.delivered))),
2902
+ read: oe(d.map((u) => pe(u.read))),
2903
+ clicked: oe(d.map((u) => Array.isArray(u.clicked) ? u.clicked.length ? oe(u.clicked.map((l) => pe(l?.count))) : 0 : null)),
2904
+ amountSpent: oe(d.map((u) => Array.isArray(u.cost) ? pe(u.cost.find((l) => l?.type === "amount_spent")?.value) : null))
2905
+ };
2906
+ });
2907
+ return {
2908
+ state: c.size ? "available" : "empty",
2909
+ reason: r ? "Only part of the account could be loaded. Metrics shown cover completed template queries only." : c.size ? null : "Meta returned no template metrics for this period. Missing values are not zero.",
2910
+ data: { templates: i, currency: s, truncated: r }
2911
+ };
2912
+ }
2913
+ const Fe = x().regex(/^\d{1,30}$/, "Use the numeric ID from Meta."), es = x().trim().min(10, "Enter a valid access token.").max(16384).regex(/^\S+$/, "The token cannot contain whitespace."), at = O({
2914
+ wabaId: Fe,
2915
+ accessToken: es
2916
+ }).strict(), fa = at.extend({ phoneNumberId: Fe }), pa = at.extend({ phoneNumberId: Fe.optional() }), ts = "id,display_phone_number,verified_name,quality_rating,code_verification_status";
2917
+ function ss(e) {
2918
+ return {
2919
+ id: S(e.id, 30) || "",
2920
+ displayPhoneNumber: S(e.display_phone_number, 64),
2921
+ verifiedName: S(e.verified_name, 256),
2922
+ qualityRating: S(e.quality_rating, 64),
2923
+ verificationStatus: S(e.code_verification_status, 64)
2924
+ };
2925
+ }
2926
+ async function ga(e, t = Qt()) {
2927
+ const s = pa.safeParse(e);
2928
+ if (!s.success) throw new F(400, s.error.issues[0]?.message || "Invalid WhatsApp connection.");
2929
+ const { wabaId: r, accessToken: n, phoneNumberId: a } = s.data, c = 5, i = Je(t, c + 1, 3e4), o = await i(n, r, { query: { fields: "id,name" } });
2930
+ if (o.id !== r) throw new F(422, "Meta did not return the requested WhatsApp Business account.");
2931
+ const d = [];
2932
+ let u = null;
2933
+ const l = /* @__PURE__ */ new Set();
2934
+ for (let f = 0; f < c; f++) {
2935
+ const p = await i(n, `${r}/phone_numbers`, { query: { fields: ts, limit: 100, after: u || void 0 } });
2936
+ for (const m of ve(p))
2937
+ Fe.safeParse(m.id).success && !d.some((h) => h.id === m.id) && d.push(ss(m));
2938
+ if (u = Te(p), !u) break;
2939
+ if (l.has(u)) throw new F(502, "Meta returned a repeated page. Try again later.");
2940
+ l.add(u);
2941
+ }
2942
+ if (a && !d.some((f) => f.id === a))
2943
+ throw new F(400, u ? "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.");
2944
+ return { wabaId: r, wabaName: S(o.name, 256), phones: d, truncated: u !== null };
2945
+ }
2946
+ const Fa = {
2947
+ verify: ga,
2948
+ testSchema: at,
2949
+ connectSchema: fa,
2950
+ toHttpError: fe
2951
+ }, Et = "whatsappBusiness", Pt = "id,name,language,category,status,quality_score,rejected_reason,components,parameter_format,source", kt = 20, Ie = Fe, ha = O({
2952
+ wabaId: Ie,
2953
+ phoneNumberId: Ie,
2954
+ displayPhoneNumber: x().max(64).nullish().transform((e) => e || ""),
2955
+ verifiedName: x().max(256).nullish().transform((e) => e || ""),
2956
+ accessToken: x().min(1).max(32768)
2957
+ }), ya = x().min(1).max(2048).refine((e) => !/[\r\n]/.test(e)), Ut = O({
2958
+ search: x().trim().min(1).max(200).optional(),
2959
+ name: x().min(1).max(512).regex(/^[a-z0-9_]+$/).optional(),
2960
+ after: ya.optional(),
2961
+ status: Ke(["APPROVED", "ARCHIVED", "DELETED", "DISABLED", "IN_APPEAL", "LIMIT_EXCEEDED", "PAUSED", "PENDING", "PENDING_DELETION", "REJECTED"]).optional()
2962
+ }).strict(), wa = (e) => g({ statusCode: 400, statusMessage: e });
2963
+ function be(e, t) {
2964
+ const s = e.safeParse(t);
2965
+ if (!s.success) throw wa(s.error.issues[0]?.message || "Invalid request.");
2966
+ return s.data;
2967
+ }
2968
+ function Oe(e) {
2969
+ return {
2970
+ view: e.isAdmin || e.permissions.includes("view-whatsapp"),
2971
+ manageTemplates: e.isAdmin || e.permissions.includes("manage-whatsapp-templates"),
2972
+ manageInsights: e.isAdmin || e.permissions.includes("manage-whatsapp-insights")
2973
+ };
2974
+ }
2975
+ function ba(e, t) {
2976
+ return {
2977
+ connected: t !== null,
2978
+ canManageConnection: e.isAdmin,
2979
+ permissions: Oe(e),
2980
+ wabaId: t?.wabaId || null,
2981
+ phoneNumberId: t?.phoneNumberId || null,
2982
+ displayPhoneNumber: t?.displayPhoneNumber || null,
2983
+ verifiedName: t?.verifiedName || null,
2984
+ updatedAt: null
2985
+ };
2986
+ }
2987
+ function Ce(e) {
2988
+ return { state: "unavailable", reason: e instanceof F ? e.message : "Meta did not make this information available.", data: null };
2989
+ }
2990
+ function Na(e) {
2991
+ const t = e.graphRequest || Qt(), s = J();
2992
+ async function r(i, o = "view") {
2993
+ ns(i, "Cache-Control", "private, no-store");
2994
+ const d = i.context.module;
2995
+ if (!d?.supabase || !d.instanceId || !d.projectId || d.moduleType !== Et || !d.userId || !Array.isArray(d.permissions))
2996
+ throw g({ statusCode: 403, statusMessage: "WhatsApp module access is unavailable." });
2997
+ if (!Oe(d).view || !Oe(d)[o])
2998
+ throw g({ statusCode: 403, statusMessage: "You do not have permission for this WhatsApp action." });
2999
+ const { data: u, error: l } = await d.supabase.from("project_modules").select("id, config").eq("id", d.instanceId).eq("project_id", d.projectId).eq("type", Et).maybeSingle();
3000
+ if (l) throw g({ statusCode: 503, statusMessage: "Could not verify the WhatsApp module." });
3001
+ if (!u) throw g({ statusCode: 404, statusMessage: "WhatsApp module not found in this project." });
3002
+ const f = u.config?.account;
3003
+ if (f && !x().uuid().safeParse(f).success)
3004
+ throw g({ statusCode: 424, statusMessage: "Select a WhatsApp integration from this project in the module settings." });
3005
+ return { ...d, accountId: f || void 0 };
3006
+ }
3007
+ async function n(i) {
3008
+ if (!i.accountId) return null;
3009
+ const { data: o, error: d } = await i.supabase.from("integrations").select("id, config").eq("id", i.accountId).eq("project_id", i.projectId).eq("type", "WHATSAPP").maybeSingle();
3010
+ if (d) throw g({ statusCode: 503, statusMessage: "Could not load the selected WhatsApp integration." });
3011
+ if (!o) throw g({ statusCode: 424, statusMessage: "The selected WhatsApp integration is unavailable in this project. Update the module settings." });
3012
+ const u = ha.safeParse(o.config);
3013
+ if (!u.success) throw g({ statusCode: 424, statusMessage: "The selected WhatsApp integration is incomplete. Reconnect it in project integrations." });
3014
+ return u.data;
3015
+ }
3016
+ async function a(i) {
3017
+ const o = await n(i);
3018
+ if (!o) throw g({ statusCode: 424, statusMessage: "Select a WhatsApp integration in the module settings first." });
3019
+ let d;
3020
+ try {
3021
+ if (d = e.decrypt(o.accessToken), !es.safeParse(d).success) throw new Error("Invalid stored token");
3022
+ } catch {
3023
+ throw g({ statusCode: 503, statusMessage: "The saved WhatsApp credential could not be read. Ask an administrator to reconnect it." });
3024
+ }
3025
+ return { row: o, token: d };
3026
+ }
3027
+ async function c(i, o, d, u) {
3028
+ const l = Je(t, kt, 3e4);
3029
+ let f = null;
3030
+ const p = /* @__PURE__ */ new Set();
3031
+ for (let m = 0; m < kt; m++) {
3032
+ const h = await l(i, `${o}/message_templates`, {
3033
+ query: { fields: Pt, limit: 100, name: u, after: f || void 0 }
3034
+ }), y = ve(h).find((w) => w.id === d);
3035
+ if (y) return Nt(y);
3036
+ if (f = Te(h), !f) throw g({ statusCode: 404, statusMessage: "Template not found in the connected WhatsApp account." });
3037
+ if (p.has(f)) throw new F(502, "Meta returned a repeated page. Try again later.");
3038
+ p.add(f);
3039
+ }
3040
+ throw g({ statusCode: 409, statusMessage: "This account has too many templates to search at once. Open the template from an exact-name search." });
3041
+ }
3042
+ return s.get("/connection", b(async (i) => {
3043
+ const o = await r(i);
3044
+ return ba(o, await n(o));
3045
+ })), s.get("/templates", b(async (i) => {
3046
+ const o = await r(i), { row: d, token: u } = await a(o), l = be(Ut, v(i));
3047
+ try {
3048
+ const f = await t(u, `${d.wabaId}/message_templates`, {
3049
+ query: { fields: Pt, limit: 50, name: l.name, name_or_content: l.search, status: l.status ? JSON.stringify([l.status]) : void 0, after: l.after }
3050
+ });
3051
+ return { templates: ve(f).filter((p) => !l.name || p.name === l.name).map(Nt), nextCursor: Te(f) };
3052
+ } catch (f) {
3053
+ fe(f);
3054
+ }
3055
+ })), s.get("/templates/:id", b(async (i) => {
3056
+ const o = await r(i), { row: d, token: u } = await a(o), l = be(Ie, U(i, "id")), f = be(O({ name: Ut.shape.name }).strict(), v(i));
3057
+ try {
3058
+ return await c(u, d.wabaId, l, f.name);
3059
+ } catch (p) {
3060
+ throw p instanceof F && fe(p), p;
3061
+ }
3062
+ })), s.post("/templates", b(async (i) => {
3063
+ const o = await r(i, "manageTemplates"), { row: d, token: u } = await a(o), l = xt(await j(i));
3064
+ try {
3065
+ const f = await t(u, `${d.wabaId}/message_templates`, { method: "POST", body: {
3066
+ name: l.name,
3067
+ language: l.language,
3068
+ category: l.category,
3069
+ parameter_format: "POSITIONAL",
3070
+ components: Ft(l)
3071
+ } });
3072
+ if (!Ie.safeParse(f.id).success) throw new F(502, "Meta did not confirm the template ID. Refresh the template list before trying again.");
3073
+ return { id: f.id, status: S(f.status, 64), category: S(f.category, 64), success: !0 };
3074
+ } catch (f) {
3075
+ fe(f);
3076
+ }
3077
+ })), s.patch("/templates/:id", b(async (i) => {
3078
+ const o = await r(i, "manageTemplates"), { row: d, token: u } = await a(o), l = be(Ie, U(i, "id")), f = xt(await j(i));
3079
+ let p;
3080
+ try {
3081
+ p = await c(u, d.wabaId, l, f.name);
3082
+ } catch (m) {
3083
+ throw m instanceof F && fe(m), m;
3084
+ }
3085
+ ua(p, f);
3086
+ try {
3087
+ if ((await t(u, l, { method: "POST", body: { components: Ft(f) } })).success !== !0) throw new F(502, "Meta did not confirm the change. Refresh the template before trying again.");
3088
+ return { id: l, status: null, category: p.category, success: !0 };
3089
+ } catch (m) {
3090
+ fe(m);
3091
+ }
3092
+ })), s.get("/overview", b(async (i) => {
3093
+ const o = await r(i), { row: d, token: u } = await a(o), [l, f, p, m] = await Promise.allSettled([
3094
+ t(u, d.wabaId, { query: { fields: "id,name,status,currency,business_verification_status,account_review_status" } }),
3095
+ t(u, d.phoneNumberId, { query: { fields: ts } }),
3096
+ t(u, d.phoneNumberId, { query: { fields: "health_status" } }),
3097
+ t(u, `${d.phoneNumberId}/whatsapp_business_profile`, { query: { fields: "about,address,description,email,vertical,websites" } })
3098
+ ]), h = m.status === "fulfilled" && Array.isArray(m.value.data) ? m.value.data[0] : null;
3099
+ return {
3100
+ account: l.status === "fulfilled" ? { state: "available", reason: null, data: {
3101
+ id: d.wabaId,
3102
+ name: S(l.value.name, 256),
3103
+ status: S(l.value.status, 64),
3104
+ currency: S(l.value.currency, 10),
3105
+ verificationStatus: S(l.value.business_verification_status, 64),
3106
+ reviewStatus: S(l.value.account_review_status, 64)
3107
+ } } : Ce(l.reason),
3108
+ phone: f.status === "fulfilled" ? { state: "available", reason: null, data: ss(f.value) } : Ce(f.reason),
3109
+ health: p.status === "fulfilled" && p.value.health_status ? { state: "available", reason: null, data: {
3110
+ canSendMessage: S(p.value.health_status.can_send_message, 64),
3111
+ entities: (Array.isArray(p.value.health_status.entities) ? p.value.health_status.entities : []).slice(0, 100).map((y) => ({
3112
+ id: S(y.id, 30),
3113
+ type: S(y.entity_type, 64),
3114
+ canSendMessage: S(y.can_send_message, 64)
3115
+ }))
3116
+ } } : Ce(p.status === "rejected" ? p.reason : null),
3117
+ profile: h ? { state: "available", reason: null, data: {
3118
+ about: S(h.about),
3119
+ address: S(h.address),
3120
+ description: S(h.description),
3121
+ email: S(h.email, 254),
3122
+ vertical: S(h.vertical, 64),
3123
+ 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))
3124
+ } } : Ce(m.status === "rejected" ? m.reason : null)
3125
+ };
3126
+ })), s.get("/insights", b(async (i) => {
3127
+ const o = Je(t, 18, 2e4), d = await r(i), { row: u, token: l } = await a(d), f = be(O({ days: Ke(["7", "30", "90"]).optional() }).strict(), v(i)), p = Number(f.days || 30), m = e.now?.() || /* @__PURE__ */ new Date(), h = Math.floor(Date.UTC(m.getUTCFullYear(), m.getUTCMonth(), m.getUTCDate()) / 1e3), y = h - p * 86400, w = u.displayPhoneNumber.replace(/\D/g, ""), [C, A] = await Promise.allSettled([
3128
+ o(l, u.wabaId, { query: { fields: "id,is_enabled_for_insights,currency" } }),
3129
+ /^\d{6,15}$/.test(w) ? o(l, u.wabaId, { query: { fields: `analytics.start(${y}).end(${h}).granularity(DAY).phone_numbers(${JSON.stringify([w])})` } }) : Promise.reject(new F(424, "The connected phone number is unavailable. Reconnect it to load message analytics."))
3130
+ ]), I = C.status === "fulfilled" && typeof C.value.is_enabled_for_insights == "boolean" ? C.value.is_enabled_for_insights : null, M = C.status === "fulfilled" ? S(C.value.currency, 10) : null;
3131
+ let D;
3132
+ if (I === !1) D = { state: "unavailable", reason: "Template insights have not been enabled for this WhatsApp account.", data: null };
3133
+ else
3134
+ try {
3135
+ const _ = await o(l, `${u.wabaId}/message_templates`, { query: { fields: "id,name,language", limit: 50 } }), T = ve(_).filter((N) => Ie.safeParse(N.id).success).slice(0, 50), E = [];
3136
+ let V = Te(_) !== null;
3137
+ const P = [], K = Array.from({ length: Math.ceil(T.length / 10) }, (N, de) => T.slice(de * 10, de * 10 + 10)), Q = await Promise.allSettled(K.map(async (N) => {
3138
+ const de = [];
3139
+ let Z = null;
3140
+ const ot = /* @__PURE__ */ new Set();
3141
+ for (let nt = 0; nt < 3; nt++) {
3142
+ const rt = await o(l, `${u.wabaId}/template_analytics`, { query: {
3143
+ start: y,
3144
+ end: h,
3145
+ granularity: "DAILY",
3146
+ metric_types: JSON.stringify(["sent", "delivered", "read", "clicked"]),
3147
+ template_ids: JSON.stringify(N.map((as) => as.id)),
3148
+ use_waba_timezone: !1,
3149
+ limit: 1e3,
3150
+ after: Z || void 0
3151
+ } });
3152
+ if (de.push(...ve(rt)), Z = Te(rt), !Z) break;
3153
+ if (ot.has(Z)) throw new F(502, "Meta returned repeated analytics pages. Try again later.");
3154
+ ot.add(Z);
3155
+ }
3156
+ if (Z) throw new F(504, "Meta returned more analytics pages than this dashboard can load at once.");
3157
+ return { batch: N, batchPoints: de };
3158
+ }));
3159
+ for (const N of Q) {
3160
+ if (N.status === "rejected") {
3161
+ V = !0;
3162
+ continue;
3163
+ }
3164
+ E.push(...N.value.batchPoints), P.push(...N.value.batch);
3165
+ }
3166
+ if (Q.length && !P.length) throw Q.find((N) => N.status === "rejected").reason;
3167
+ D = ma(E, P, M, V);
3168
+ } catch (_) {
3169
+ D = Ce(_);
3170
+ }
3171
+ return {
3172
+ days: p,
3173
+ start: y,
3174
+ end: h,
3175
+ timezone: "UTC",
3176
+ phoneNumber: u.displayPhoneNumber,
3177
+ enabled: I,
3178
+ canEnable: Oe(d).manageInsights && I !== !0,
3179
+ messages: A.status === "fulfilled" ? la(A.value) : Ce(A.reason),
3180
+ templates: D
3181
+ };
3182
+ })), s.post("/insights/enable", b(async (i) => {
3183
+ const o = await r(i, "manageInsights");
3184
+ be(O({ confirm: ke(!0) }).strict(), await j(i));
3185
+ const { row: d, token: u } = await a(o);
3186
+ try {
3187
+ const l = await t(u, d.wabaId, { method: "POST", body: { is_enabled_for_insights: !0 } });
3188
+ if (l.id !== d.wabaId && l.success !== !0) throw new F(502, "Meta did not confirm insights activation. Refresh before trying again.");
3189
+ return { enabled: !0 };
3190
+ } catch (l) {
3191
+ fe(l);
3192
+ }
3193
+ })), s.handler;
1109
3194
  }
1110
3195
  export {
1111
- J as appointments,
1112
- Y as contentManager,
1113
- W as googleAnalytics,
1114
- z as products
3196
+ Sa as appointments,
3197
+ Ta as contentManager,
3198
+ xa as filezone,
3199
+ va as firebaseAuthManager,
3200
+ _a as formBuilder,
3201
+ Da as googleAnalytics,
3202
+ Ra as mailTemplates,
3203
+ Ma as products,
3204
+ Na as whatsappBusiness,
3205
+ Fa as whatsappBusinessConnection
1115
3206
  };