@carlonicora/nextjs-jsonapi 6.19.0 → 6.20.0

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 (91) hide show
  1. package/dist/{BlockNoteEditor-5HSL74IX.mjs → BlockNoteEditor-7JRJ3XWV.mjs} +5 -5
  2. package/dist/{BlockNoteEditor-6RZXGMSD.js → BlockNoteEditor-MH2D5L4A.js} +20 -20
  3. package/dist/{BlockNoteEditor-6RZXGMSD.js.map → BlockNoteEditor-MH2D5L4A.js.map} +1 -1
  4. package/dist/{BlockNoteViewer-XYKFY77F.js → BlockNoteViewer-72J3CBAH.js} +4 -4
  5. package/dist/{BlockNoteViewer-XYKFY77F.js.map → BlockNoteViewer-72J3CBAH.js.map} +1 -1
  6. package/dist/{BlockNoteViewer-X4UEVI2A.mjs → BlockNoteViewer-UKYEU3E3.mjs} +3 -3
  7. package/dist/{analytics.modules-Bsacu6ik.d.ts → analytics.modules-C4OT7fVa.d.ts} +1 -0
  8. package/dist/{analytics.modules-txdCLNG-.d.mts → analytics.modules-DEnXnRGt.d.mts} +1 -0
  9. package/dist/billing/index.js +492 -492
  10. package/dist/billing/index.mjs +4 -4
  11. package/dist/{chunk-OKBJEIOP.mjs → chunk-2U57XVNF.mjs} +1 -1
  12. package/dist/chunk-2U57XVNF.mjs.map +1 -0
  13. package/dist/{chunk-KMPBSOFB.mjs → chunk-ASORDSWQ.mjs} +50 -2
  14. package/dist/chunk-ASORDSWQ.mjs.map +1 -0
  15. package/dist/{chunk-B4ZH2FOC.js → chunk-IN2F6HI2.js} +906 -906
  16. package/dist/{chunk-B4ZH2FOC.js.map → chunk-IN2F6HI2.js.map} +1 -1
  17. package/dist/{chunk-DFA566KY.js → chunk-JAOBCIE7.js} +4 -4
  18. package/dist/{chunk-DFA566KY.js.map → chunk-JAOBCIE7.js.map} +1 -1
  19. package/dist/{chunk-YVKWACDP.mjs → chunk-LSZZIQJ5.mjs} +2 -2
  20. package/dist/{chunk-I5BQLY7V.js → chunk-MVAIYPIN.js} +9 -9
  21. package/dist/{chunk-I5BQLY7V.js.map → chunk-MVAIYPIN.js.map} +1 -1
  22. package/dist/{chunk-HEDHJAKT.mjs → chunk-NKXNOGMH.mjs} +6 -6
  23. package/dist/{chunk-OO2DBP6L.js → chunk-SGLFQ55U.js} +942 -894
  24. package/dist/chunk-SGLFQ55U.js.map +1 -0
  25. package/dist/{chunk-TY5YWBLL.mjs → chunk-SJ3K3ATF.mjs} +3 -3
  26. package/dist/{chunk-A7Y5YW7Q.js → chunk-V7CHD7QT.js} +1 -1
  27. package/dist/{chunk-A7Y5YW7Q.js.map → chunk-V7CHD7QT.js.map} +1 -1
  28. package/dist/client/index.js +5 -5
  29. package/dist/client/index.mjs +4 -4
  30. package/dist/components/index.js +5 -5
  31. package/dist/components/index.mjs +4 -4
  32. package/dist/contexts/index.js +5 -5
  33. package/dist/contexts/index.mjs +4 -4
  34. package/dist/core/index.d.mts +2 -1
  35. package/dist/core/index.d.ts +2 -1
  36. package/dist/core/index.js +3 -3
  37. package/dist/core/index.mjs +2 -2
  38. package/dist/email-automations/index.js +118 -118
  39. package/dist/email-automations/index.mjs +4 -4
  40. package/dist/features/analytics/index.d.mts +60 -3
  41. package/dist/features/analytics/index.d.ts +60 -3
  42. package/dist/features/analytics/index.js +320 -128
  43. package/dist/features/analytics/index.js.map +1 -1
  44. package/dist/features/analytics/index.mjs +314 -122
  45. package/dist/features/analytics/index.mjs.map +1 -1
  46. package/dist/features/analytics/server.js +5 -5
  47. package/dist/features/analytics/server.mjs +3 -3
  48. package/dist/features/blog/index.js +151 -151
  49. package/dist/features/blog/index.mjs +4 -4
  50. package/dist/features/blog/server.js +17 -17
  51. package/dist/features/blog/server.mjs +2 -2
  52. package/dist/features/help/index.js +38 -38
  53. package/dist/features/help/index.mjs +4 -4
  54. package/dist/features/tokenusage/index.js +127 -127
  55. package/dist/features/tokenusage/index.mjs +4 -4
  56. package/dist/index.d.mts +1 -1
  57. package/dist/index.d.ts +1 -1
  58. package/dist/index.js +4 -4
  59. package/dist/index.mjs +3 -3
  60. package/dist/offline/index.js +14 -14
  61. package/dist/offline/index.mjs +1 -1
  62. package/dist/server/index.js +4 -4
  63. package/dist/server/index.mjs +3 -3
  64. package/messages/en.json +8 -0
  65. package/messages/it.json +8 -0
  66. package/package.json +1 -1
  67. package/src/core/registry/ModuleRegistry.ts +1 -0
  68. package/src/features/analytics/analytics.modules.ts +6 -0
  69. package/src/features/analytics/components/AnalyticsAdminContainer.tsx +20 -0
  70. package/src/features/analytics/components/AnalyticsDailyUniquesChart.tsx +224 -0
  71. package/src/features/analytics/components/__tests__/AnalyticsDailyUniquesChart.spec.tsx +88 -0
  72. package/src/features/analytics/components/__tests__/AnalyticsPageContainers.spec.tsx +22 -0
  73. package/src/features/analytics/contexts/AnalyticsAdminContext.tsx +15 -2
  74. package/src/features/analytics/contexts/__tests__/AnalyticsAdminContext.spec.tsx +38 -0
  75. package/src/features/analytics/data/AnalyticsAdminService.ts +20 -0
  76. package/src/features/analytics/data/__tests__/AnalyticsAdminService.spec.ts +15 -0
  77. package/src/features/analytics/data/__tests__/models.spec.ts +29 -0
  78. package/src/features/analytics/data/analytics-daily-unique-timeline.interface.ts +11 -0
  79. package/src/features/analytics/data/analytics-daily-unique-timeline.ts +53 -0
  80. package/src/features/analytics/data/analytics.types.ts +10 -0
  81. package/src/features/analytics/data/index.ts +2 -0
  82. package/src/features/analytics/i18n-keys.ts +8 -0
  83. package/src/features/analytics/index.ts +1 -0
  84. package/dist/chunk-KMPBSOFB.mjs.map +0 -1
  85. package/dist/chunk-OKBJEIOP.mjs.map +0 -1
  86. package/dist/chunk-OO2DBP6L.js.map +0 -1
  87. /package/dist/{BlockNoteEditor-5HSL74IX.mjs.map → BlockNoteEditor-7JRJ3XWV.mjs.map} +0 -0
  88. /package/dist/{BlockNoteViewer-X4UEVI2A.mjs.map → BlockNoteViewer-UKYEU3E3.mjs.map} +0 -0
  89. /package/dist/{chunk-YVKWACDP.mjs.map → chunk-LSZZIQJ5.mjs.map} +0 -0
  90. /package/dist/{chunk-HEDHJAKT.mjs.map → chunk-NKXNOGMH.mjs.map} +0 -0
  91. /package/dist/{chunk-TY5YWBLL.mjs.map → chunk-SJ3K3ATF.mjs.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-OO2DBP6L.js","../src/core/abstracts/lastApiResponse.ts","../src/core/abstracts/AbstractService.ts","../src/core/abstracts/ClientAbstractService.ts","../src/core/registry/ModuleRegistrar.ts","../src/core/fields/FieldSelector.ts","../src/core/utils/rehydrate.ts","../src/core/utils/mergeMessages.ts","../src/utils/cn.ts","../src/utils/compose-refs.ts","../src/utils/use-mobile.tsx","../src/utils/date-formatter.ts","../src/utils/exists.ts","../src/utils/table-options.ts","../src/utils/schemas/user.object.schema.ts","../src/utils/schemas/entity.object.schema.ts","../src/utils/getInitials.ts","../src/utils/isRemoteImageSrc.ts","../src/utils/italian-validators.ts","../src/utils/blocknote-diff.util.ts","../src/utils/blocknote-word-diff-renderer.util.ts","../src/utils/icons.tsx","../src/utils/toast.ts","../src/permissions/types.ts","../src/permissions/check.ts","../src/features/account-deletion/data/account-deletion.ts","../src/features/account-deletion/account-deletion.module.ts","../src/features/account-deletion/data/account-deletion.service.ts","../src/features/account-deletion/utils/account-deletion-notice.ts","../src/features/auth/data/totp-authenticator.ts","../src/features/auth/totp-authenticator.module.ts","../src/features/auth/data/totp-setup.ts","../src/features/auth/totp-setup.module.ts","../src/features/auth/data/totp-verify.ts","../src/features/auth/totp-verify.module.ts","../src/features/auth/data/totp-verify-login.ts","../src/features/auth/totp-verify-login.module.ts","../src/features/auth/data/passkey.ts","../src/features/auth/passkey.module.ts","../src/features/auth/data/passkey-registration-options.ts","../src/features/auth/passkey-registration-options.module.ts","../src/features/auth/data/passkey-registration-verify.ts","../src/features/auth/passkey-registration-verify.module.ts","../src/features/auth/data/passkey-rename.ts","../src/features/auth/passkey-rename.module.ts","../src/features/auth/data/passkey-verify-login.ts","../src/features/auth/passkey-verify-login.module.ts","../src/features/auth/data/passkey-authentication-options.ts","../src/features/auth/passkey-authentication-options.module.ts","../src/features/auth/data/two-factor-enable.ts","../src/features/auth/two-factor-enable.module.ts","../src/features/auth/data/two-factor-challenge.ts","../src/features/auth/two-factor-challenge.module.ts","../src/features/auth/data/backup-code-verify.ts","../src/features/auth/backup-code-verify.module.ts","../src/features/auth/config.ts","../src/features/auth/data/auth.ts","../src/features/auth/data/auth.service.ts","../src/features/auth/data/two-factor-status.ts","../src/features/auth/data/two-factor.service.ts","../src/features/auth/data/pending-registration.ts","../src/features/auth/enums/AuthComponent.ts","../src/features/auth/auth.module.ts","../src/features/auth/two-factor-status.module.ts","../src/features/auth/pending-registration.module.ts","../src/features/billing/data/billing.service.ts","../src/features/billing/data/Billing.ts","../src/features/billing/stripe-subscription/data/stripe-subscription.interface.ts","../src/features/billing/data/credit-warning.ts","../src/features/billing/modules/billing.module.ts","../src/features/billing/stripe-customer/data/payment-method.ts","../src/features/billing/stripe-customer/data/stripe-customer.ts","../src/features/billing/stripe-customer/data/stripe-customer.service.ts","../src/features/billing/stripe-customer/stripe-customer.module.ts","../src/features/billing/stripe-customer/stripe-payment-method.module.ts","../src/features/billing/stripe-invoice/data/stripe-invoice.ts","../src/features/billing/stripe-invoice/data/stripe-invoice.interface.ts","../src/features/billing/stripe-invoice/data/stripe-invoice.service.ts","../src/features/billing/stripe-invoice/stripe-invoice.module.ts","../src/features/billing/stripe-price/data/stripe-price.ts","../src/features/billing/stripe-price/data/stripe-price.fields.ts","../src/features/billing/stripe-price/data/stripe-price.service.ts","../src/features/billing/stripe-price/stripe-price.module.ts","../src/features/billing/stripe-product/data/stripe-product.ts","../src/features/billing/stripe-product/data/stripe-product.fields.ts","../src/features/billing/stripe-product/data/stripe-product.service.ts","../src/features/billing/stripe-product/stripe-product.module.ts","../src/features/billing/stripe-subscription/data/stripe-subscription.ts","../src/features/billing/stripe-subscription/data/stripe-subscription.service.ts","../src/features/billing/stripe-subscription/stripe-subscription.module.ts","../src/features/billing/stripe-usage/data/stripe-usage.ts","../src/features/billing/stripe-usage/data/stripe-usage.service.ts","../src/features/billing/stripe-usage/stripe-usage.module.ts","../src/features/billing/stripe-promotion-code/data/stripe-promotion-code.service.ts","../src/features/billing/stripe-promotion-code/data/stripe-promotion-code.ts","../src/features/billing/stripe-promotion-code/stripe-promotion-code.module.ts","../src/features/billing/stripe-coupon/data/stripe-coupon.ts","../src/features/billing/stripe-coupon/data/stripe-coupon.fields.ts","../src/features/billing/stripe-coupon/stripe-coupon.module.ts","../src/features/billing/stripe-coupon/data/stripe-coupon.service.ts","../src/features/billing/stripe-coupon/data/stripe-coupon-code.ts","../src/features/billing/stripe-coupon/data/stripe-coupon-code.fields.ts","../src/features/billing/stripe-coupon/stripe-coupon-code.module.ts","../src/features/billing/stripe-coupon/data/stripe-coupon-code.service.ts","../src/features/billing/stripe-coupon/data/stripe-coupon-offer.ts","../src/features/billing/stripe-coupon/utils/coupon-places.ts","../src/features/billing/components/utils/format-options.ts","../src/features/billing/components/utils/currency.ts","../src/features/billing/stripe-coupon/utils/format-coupon-discount.ts","../src/features/billing/stripe-coupon/utils/to-promotion-code-result.ts","../src/features/billing/stripe-coupon/stripe-coupon-offer.module.ts","../src/features/ai-connection/data/ai-connection.ts","../src/features/ai-connection/data/ai-connection.fields.ts","../src/features/ai-connection/data/ai-connection.service.ts","../src/features/ai-connection/ai-connection.module.ts","../src/features/company/company.module.ts","../src/features/company/data/company.ts","../src/features/company/data/company.fields.ts","../src/features/company/data/company.service.ts","../src/features/company/data/entity-ai-status.ts","../src/features/content/data/content.ts","../src/features/content/data/content.fields.ts","../src/features/content/data/content.service.ts","../src/features/content/content.module.ts","../src/features/how-to/HowToModule.ts","../src/features/how-to/data/HowTo.ts","../src/features/how-to/data/HowToFields.ts","../src/features/how-to/data/HowToService.ts","../src/features/email-automation/EmailAutomationModule.ts","../src/features/email-automation/data/EmailAutomation.ts","../src/features/email-automation/EmailAutomationStepModule.ts","../src/features/email-automation/data/EmailAutomationStep.ts","../src/features/email-automation/EmailAutomationEnrollmentModule.ts","../src/features/email-automation/data/EmailAutomationEnrollment.ts","../src/features/email-automation/EmailAutomationTriggerModule.ts","../src/features/email-automation/data/EmailAutomationTrigger.ts","../src/features/email-automation/EmailAutomationCandidateModule.ts","../src/features/email-automation/data/EmailAutomationCandidate.ts","../src/features/email-automation/data/EmailAutomationService.ts","../src/features/email-automation/data/EmailAutomationStepService.ts","../src/features/email-automation/data/EmailAutomationEnrollmentService.ts","../src/features/email-automation/data/email-automation.fields.ts","../src/features/email-automation/data/email-automation-enrollment.fields.ts","../src/features/email-blast/EmailBlastModule.ts","../src/features/email-blast/data/EmailBlast.ts","../src/features/email-blast/EmailBlastDeliveryModule.ts","../src/features/email-blast/data/EmailBlastDelivery.ts","../src/features/email-blast/EmailBlastAudienceModule.ts","../src/features/email-blast/data/EmailBlastAudience.ts","../src/features/email-blast/data/email-blast.segments.ts","../src/features/email-blast/data/EmailBlastService.ts","../src/features/email-blast/data/EmailBlastDeliveryService.ts","../src/features/email-blast/data/email-blast.fields.ts","../src/features/blog/BlogModules.ts","../src/features/blog/data/BlogCategory.ts","../src/features/blog/data/BlogCategoryTranslation.ts","../src/features/blog/data/BlogPost.ts","../src/features/blog/data/BlogPostTranslation.ts","../src/features/blog/types/blog.constants.ts","../src/features/blog/data/BlogSettings.ts","../src/features/blog/data/BlogPostService.ts","../src/features/blog/data/BlogPostTranslationService.ts","../src/features/blog/data/BlogCategoryService.ts","../src/features/blog/data/BlogCategoryTranslationService.ts","../src/features/blog/data/BlogSettingsService.ts","../src/features/blog/data/BlogPostFields.ts","../src/features/blog/data/BlogCategoryFields.ts","../src/features/handbook/HandbookPageModule.ts","../src/features/handbook/data/HandbookPage.ts","../src/features/handbook/HandbookSectionModule.ts","../src/features/handbook/data/HandbookSection.ts","../src/features/handbook/HandbookThreadMessageModule.ts","../src/features/handbook/data/HandbookThreadMessage.ts","../src/features/handbook/HandbookThreadModule.ts","../src/features/handbook/data/HandbookThread.ts","../src/features/handbook/data/handbookDisplay.ts","../src/features/handbook/data/HandbookPageFields.ts","../src/features/handbook/data/HandbookPageService.ts","../src/features/handbook/data/HandbookSectionService.ts","../src/features/handbook/data/HandbookThreadMessageService.ts","../src/features/handbook/data/HandbookThreadService.ts","../src/features/assistant/AssistantModule.ts","../src/features/assistant/data/Assistant.ts","../src/features/assistant-message/data/AssistantMessage.ts","../src/features/assistant/utils/resolveReferenceableModules.ts","../src/features/assistant/data/AssistantService.ts","../src/features/assistant-message/AssistantMessageModule.ts","../src/features/assistant-message/data/AssistantMessageService.ts","../src/features/assistant-action/AssistantActionModule.ts","../src/features/assistant-action/data/AssistantAction.ts","../src/features/assistant-action/data/AssistantActionService.ts","../src/features/chunk/ChunkModule.ts","../src/features/chunk/data/Chunk.ts","../src/features/feature/data/feature.ts","../src/features/feature/data/feature.service.ts","../src/features/feature/feature.module.ts","../src/features/module/data/module.ts","../src/features/module/module.module.ts","../src/features/notification/data/notification.ts","../src/features/notification/data/notification.fields.ts","../src/features/notification/data/notification.service.ts","../src/features/notification/notification.module.ts","../src/features/push/data/push.ts","../src/features/push/data/push.service.ts","../src/features/push/push.module.ts","../src/features/role/data/role.ts","../src/features/role/data/role.fields.ts","../src/features/role/data/role.service.ts","../src/features/role/role.module.ts","../src/features/s3/data/s3.ts","../src/features/s3/s3.module.ts","../src/features/s3/data/s3.service.ts","../src/features/user/data/user.ts","../src/features/user/data/user.fields.ts","../src/features/user/data/user.service.ts","../src/features/user/author.module.ts","../src/features/user/user.module.ts","../src/features/oauth/data/oauth.ts","../src/features/oauth/oauth.module.ts","../src/features/oauth/data/oauth.service.ts","../src/features/oauth/interfaces/oauth.interface.ts","../src/features/waitlist/data/Waitlist.ts","../src/features/waitlist/data/WaitlistService.ts","../src/features/waitlist/data/waitlist-stats.ts","../src/features/waitlist/data/waitlist.fields.ts","../src/features/waitlist/waitlist.module.ts","../src/features/waitlist/waitlist-stats.module.ts","../src/features/rbac/data/PermissionMapping.ts","../src/features/rbac/data/ModulePaths.ts","../src/features/rbac/data/RbacMatrixModel.ts","../src/features/rbac/rbac.module.ts","../src/features/referral/data/ReferralStats.ts","../src/features/referral/data/ReferralService.ts","../src/features/referral/data/ReferralVisit.ts","../src/features/referral/data/ReferralAdminSummary.ts","../src/features/referral/data/ReferralAdminReferrer.ts","../src/features/referral/data/ReferralAdminReferral.ts","../src/features/referral/data/ReferralAdminService.ts","../src/features/referral/referral.module.ts","../src/features/referral/referral-stats.module.ts","../src/features/referral/referral-admin.modules.ts","../src/offline/sync/models/OfflineChangeSet.ts","../src/offline/sync/models/offline.modules.ts","../src/features/tokenusage/tokenusage-admin.module.ts","../src/features/tokenusage/data/tokenusage-admin-breakdown.ts","../src/features/tokenusage/data/tokenusage-admin-summary.ts","../src/features/tokenusage/data/tokenusage-admin-timeline.ts","../src/features/tokenusage/tokenusage.modules.ts","../src/features/tokenusage/data/tokenusage-report-breakdown.ts","../src/features/tokenusage/data/tokenusage-report-summary.ts","../src/features/tokenusage/data/tokenusage-report-timeline.ts","../src/features/analytics/analytics.modules.ts","../src/features/analytics/data/analytics-breakdown.ts","../src/features/analytics/data/analytics-event.ts","../src/features/analytics/data/analytics-page-view.ts","../src/features/analytics/data/analytics-session.ts","../src/features/analytics/data/analytics-summary.ts","../src/features/analytics/data/analytics-timeline.ts","../src/features/tokenusage/lib/config.ts","../src/features/audit-log/data/audit-log.ts","../src/features/audit-log/data/audit-log.service.ts","../src/features/audit-log/audit-log.module.ts"],"names":["HttpMethod","JsonApiGet","JsonApiPost","JsonApiDelete","ClientHttpMethod","globalErrorHandler","Action","AuthComponent","SubscriptionStatus","InvoiceStatus","StripePriceFields","StripeProductFields","CreditCardIcon","StripeCouponFields","StripeCouponCodeFields","currency","AiConnectionFields","CompanyFields","ContentFields","HowToFields","MailIcon","EmailAutomationFields","EmailAutomationEnrollmentFields","UsersIcon","EmailBlastFields","BlogPostFields","BlogCategoryFields","HandbookPageFields","NotificationFields","RoleFields","UserFields","WaitlistFields","CoinsIcon"],"mappings":"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;AAC7B,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEwB,EAAA;AAClB,IAAA;AACd,EAAA;AAEiC,EAAA;AACnB,IAAA;AACd,EAAA;AAEoC,EAAA;AACtB,IAAA;AACd,EAAA;AAEmC,EAAA;AACrB,IAAA;AACd,EAAA;AAEmC,EAAA;AACrB,IAAA;AACd,EAAA;AAEkC,EAAA;AACpB,IAAA;AACd,EAAA;AAEuC,EAAA;AACzB,IAAA;AACd,EAAA;AAEsC,EAAA;AACxB,IAAA;AACd,EAAA;AAEoD,EAAA;AAC9B,IAAA;AAEM,IAAA;AACE,IAAA;AACL,IAAA;AACD,IAAA;AACA,IAAA;AACO,IAAA;AACH,IAAA;AAER,IAAA;AAEX,IAAA;AACT,EAAA;AAEqB,EAAA;AACH,IAAA;AAClB,EAAA;AACF;AnQwjYiC;AACA;AoQtnY1B;AAA8C,EAAA;AAAA,IAAA;AAAA,EAAA;AACtC,EAAA;AAKU,IAAA;AACD,MAAA;AACd,MAAA;AACkB,MAAA;AACN,MAAA;AACjB,IAAA;AAEmB,IAAA;AACJ,MAAA;AACd,MAAA;AACmB,MAAA;AACN,MAAA;AACd,IAAA;AACH,EAAA;AACF;ApQsnYiC;AACA;AqQ3oYH;AAEpB,EAAA;AACG,EAAA;AACF,EAAA;AACG,EAAA;AALgB;ArQkpYG;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-OO2DBP6L.js","sourcesContent":[null,"/**\n * Last-response store shared by `AbstractService` and `ClientAbstractService`.\n *\n * `useDataListRetriever` reads the total here when the calling service does not\n * forward a `total` ref. The values MUST therefore always describe the most\n * recent API response: writing only when a response carries `meta.total` let a\n * total leak across entities (a judgement list's 407871 showing up under a task\n * list, whose API never returns a total at all).\n *\n * Both service bases write through `setLastApiResponse`. The module has no\n * imports so the client base can use it without pulling in server code — the\n * reason the two bases do not import each other.\n */\nlet lastApiTotal: number | undefined = undefined;\nlet lastApiMeta: Record<string, any> | undefined = undefined;\n\n/** Record the top-level `meta` of the response that just completed. */\nexport function setLastApiResponse(meta: Record<string, any> | undefined): void {\n lastApiMeta = meta;\n lastApiTotal = meta?.total;\n}\n\nexport function getLastApiTotal(): number | undefined {\n return lastApiTotal;\n}\n\nexport function clearLastApiTotal(): void {\n lastApiTotal = undefined;\n}\n\nexport function getLastApiMeta(): Record<string, any> | undefined {\n return lastApiMeta;\n}\n\nexport function clearLastApiMeta(): void {\n lastApiMeta = undefined;\n}\n","import { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\nimport { ApiResponseInterface } from \"../interfaces/ApiResponseInterface\";\nimport { setLastApiResponse } from \"./lastApiResponse\";\nimport { getI18nServerLocale, getI18nSupportedLocales, getI18nDefaultLocale } from \"../../i18n/config\";\n\nexport enum HttpMethod {\n GET = \"GET\",\n POST = \"POST\",\n PUT = \"PUT\",\n PATCH = \"PATCH\",\n DELETE = \"DELETE\",\n}\n\nexport interface NextRef {\n next?: string;\n}\n\nexport interface PreviousRef {\n previous?: string;\n}\n\nexport interface SelfRef {\n self?: string;\n}\n\nexport interface TotalRef {\n total?: number;\n}\n\n// The last response's `total` and full top-level `meta` live in `lastApiResponse`\n// so `ClientAbstractService` writes the same store (pagination goes through it).\n// Re-exported here because consumers import them from this module.\nexport { clearLastApiMeta, clearLastApiTotal, getLastApiMeta, getLastApiTotal } from \"./lastApiResponse\";\n\nlet globalErrorHandler: ((status: number, message: string) => void) | null = null;\n\n/**\n * Set a global error handler for API errors (client-side only).\n * This handler will be called instead of throwing errors.\n */\nexport function setGlobalErrorHandler(handler: (status: number, message: string) => void) {\n globalErrorHandler = handler;\n}\n\n/**\n * Get the current global error handler.\n */\nexport function getGlobalErrorHandler(): ((status: number, message: string) => void) | null {\n return globalErrorHandler;\n}\n\n/**\n * Abstract base class for services that interact with the JSON:API.\n * Extend this class to create feature-specific services.\n */\nexport abstract class AbstractService {\n /**\n * Extract locale from client-side URL pathname\n * URL structure: /{locale}/route-path (e.g., /it/accounts)\n * Fallback chain: URL locale → the app's default locale (first supported locale)\n */\n private static getClientLocale(): string {\n if (typeof window === \"undefined\") {\n return getI18nDefaultLocale(); // Server-side fallback\n }\n\n // Extract locale from URL pathname (first segment after leading slash)\n const pathSegments = window.location.pathname.split(\"/\").filter(Boolean);\n const urlLocale = pathSegments[0];\n\n // Validate against the app's supported locales (default [\"en\"])\n const supportedLocales = getI18nSupportedLocales();\n if (urlLocale && supportedLocales.includes(urlLocale)) {\n return urlLocale;\n }\n\n // Fallback to the app's default locale, never the browser's language\n return getI18nDefaultLocale();\n }\n\n static async next<T>(params: {\n type: ApiRequestDataTypeInterface;\n endpoint: string;\n next?: NextRef;\n previous?: PreviousRef;\n self?: SelfRef;\n total?: TotalRef;\n }): Promise<T> {\n return await this.callApi<T>({\n method: HttpMethod.GET,\n type: params.type,\n endpoint: params.endpoint,\n next: params.next,\n previous: params.previous,\n self: params.self,\n total: params.total,\n });\n }\n\n /**\n * Fetch the previous page of results.\n */\n static async previous<T>(params: {\n type: ApiRequestDataTypeInterface;\n endpoint: string;\n next?: NextRef;\n previous?: PreviousRef;\n self?: SelfRef;\n total?: TotalRef;\n }): Promise<T> {\n return await this.callApi<T>({\n method: HttpMethod.GET,\n type: params.type,\n endpoint: params.endpoint,\n next: params.next,\n previous: params.previous,\n self: params.self,\n total: params.total,\n });\n }\n\n /**\n * Make an API call with automatic environment detection and error handling.\n */\n protected static async callApi<T>(params: {\n type: ApiRequestDataTypeInterface;\n method: HttpMethod;\n endpoint: string;\n companyId?: string;\n input?: any;\n overridesJsonApiCreation?: boolean;\n next?: NextRef;\n previous?: PreviousRef;\n self?: SelfRef;\n total?: TotalRef;\n responseType?: ApiRequestDataTypeInterface;\n files?: { [key: string]: File | Blob } | File | Blob;\n token?: string;\n suppressGlobalError?: boolean;\n /**\n * Per-call override of the API base URL. When omitted, the existing global\n * `NEXT_PUBLIC_API_URL` (or `configureJsonApi({ apiUrl })`) resolution is used —\n * fully backward compatible. Ignored when `endpoint` starts with \"http\" (passthrough).\n */\n baseUrl?: string;\n }): Promise<T> {\n // Dynamic import to avoid bundling issues\n const { JsonApiGet, JsonApiPost, JsonApiPut, JsonApiPatch, JsonApiDelete } =\n await import(\"../../unified/JsonApiRequest\");\n\n let apiResponse: ApiResponseInterface;\n\n // Get language based on environment\n let language = \"en\";\n if (typeof window === \"undefined\") {\n language = await getI18nServerLocale();\n } else {\n // Client-side: extract locale from URL pathname\n language = this.getClientLocale();\n }\n\n switch (params.method) {\n case HttpMethod.GET:\n apiResponse = await JsonApiGet({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n language: language,\n token: params.token,\n baseUrl: params.baseUrl,\n });\n break;\n case HttpMethod.POST:\n apiResponse = await JsonApiPost({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n body: params.input,\n overridesJsonApiCreation: params.overridesJsonApiCreation,\n language: language,\n responseType: params.responseType,\n files: params.files,\n token: params.token,\n baseUrl: params.baseUrl,\n });\n break;\n case HttpMethod.PUT:\n apiResponse = await JsonApiPut({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n body: params.input,\n language: language,\n responseType: params.responseType,\n files: params.files,\n token: params.token,\n baseUrl: params.baseUrl,\n });\n break;\n case HttpMethod.PATCH:\n apiResponse = await JsonApiPatch({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n body: params.input,\n overridesJsonApiCreation: params.overridesJsonApiCreation,\n language: language,\n responseType: params.responseType,\n files: params.files,\n token: params.token,\n baseUrl: params.baseUrl,\n });\n break;\n case HttpMethod.DELETE:\n apiResponse = await JsonApiDelete({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n language: language,\n responseType: params.responseType,\n token: params.token,\n baseUrl: params.baseUrl,\n });\n break;\n default:\n throw new Error(\"Method not found\");\n }\n\n if (!apiResponse.ok) {\n if (globalErrorHandler && typeof window !== \"undefined\" && !params.suppressGlobalError) {\n globalErrorHandler(apiResponse.response, apiResponse.error);\n return undefined as any;\n } else {\n const error = new Error(`${apiResponse.response}:${apiResponse.error}`) as any;\n error.status = apiResponse.response;\n error.digest = `HTTP_${apiResponse.response}`;\n error.body = apiResponse.raw;\n throw error;\n }\n }\n\n if (apiResponse.next && params.next) params.next.next = apiResponse.next;\n if (apiResponse.prev && params.previous) params.previous.previous = apiResponse.prev;\n if (apiResponse.self && params.self) params.self.self = apiResponse.self;\n if (apiResponse.meta?.total !== undefined && params.total) params.total.total = apiResponse.meta.total;\n // Always record this response's meta/total — including when it has none — so a\n // hook falling back to the store never reads another entity's total.\n setLastApiResponse(apiResponse.meta);\n\n return apiResponse.data as T;\n }\n\n /**\n * Make an API call and return both data and meta from the response.\n */\n protected static async callApiWithMeta<T>(params: {\n type: ApiRequestDataTypeInterface;\n method: HttpMethod;\n endpoint: string;\n companyId?: string;\n input?: any;\n overridesJsonApiCreation?: boolean;\n responseType?: ApiRequestDataTypeInterface;\n files?: { [key: string]: File | Blob } | File | Blob;\n suppressGlobalError?: boolean;\n }): Promise<{ data: T; meta?: Record<string, any> }> {\n // Dynamic import to avoid bundling issues\n const { JsonApiGet, JsonApiPost, JsonApiPut, JsonApiPatch, JsonApiDelete } =\n await import(\"../../unified/JsonApiRequest\");\n\n let apiResponse: ApiResponseInterface;\n\n // Get language based on environment\n let language = \"en\";\n if (typeof window === \"undefined\") {\n language = await getI18nServerLocale();\n } else {\n // Client-side: extract locale from URL pathname\n language = this.getClientLocale();\n }\n\n switch (params.method) {\n case HttpMethod.GET:\n apiResponse = await JsonApiGet({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n language: language,\n });\n break;\n case HttpMethod.POST:\n apiResponse = await JsonApiPost({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n body: params.input,\n overridesJsonApiCreation: params.overridesJsonApiCreation,\n language: language,\n responseType: params.responseType,\n files: params.files,\n });\n break;\n case HttpMethod.PUT:\n apiResponse = await JsonApiPut({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n body: params.input,\n language: language,\n responseType: params.responseType,\n files: params.files,\n });\n break;\n case HttpMethod.PATCH:\n apiResponse = await JsonApiPatch({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n body: params.input,\n overridesJsonApiCreation: params.overridesJsonApiCreation,\n language: language,\n responseType: params.responseType,\n files: params.files,\n });\n break;\n case HttpMethod.DELETE:\n apiResponse = await JsonApiDelete({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n language: language,\n responseType: params.responseType,\n });\n break;\n default:\n throw new Error(\"Method not found\");\n }\n\n if (!apiResponse.ok) {\n if (globalErrorHandler && typeof window !== \"undefined\" && !params.suppressGlobalError) {\n globalErrorHandler(apiResponse.response, apiResponse.error);\n return { data: undefined as any, meta: undefined };\n } else {\n const error = new Error(`${apiResponse.response}:${apiResponse.error}`) as any;\n error.status = apiResponse.response;\n error.digest = `HTTP_${apiResponse.response}`;\n error.body = apiResponse.raw;\n throw error;\n }\n }\n\n return {\n data: apiResponse.data as T,\n meta: apiResponse.meta,\n };\n }\n\n /**\n * Get raw JSON:API response data without deserialization.\n */\n protected static async getRawData(params: {\n type: ApiRequestDataTypeInterface;\n method: HttpMethod;\n endpoint: string;\n companyId?: string;\n baseUrl?: string;\n suppressGlobalError?: boolean;\n }): Promise<any> {\n const { JsonApiGet } = await import(\"../../unified/JsonApiRequest\");\n\n let language = \"en\";\n\n if (typeof window === \"undefined\") {\n language = await getI18nServerLocale();\n } else {\n // Client-side: extract locale from URL pathname\n language = this.getClientLocale();\n }\n\n const apiResponse: ApiResponseInterface = await JsonApiGet({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n baseUrl: params.baseUrl,\n language: language,\n });\n\n if (!apiResponse.ok) {\n if (globalErrorHandler && typeof window !== \"undefined\" && !params.suppressGlobalError) {\n globalErrorHandler(apiResponse.response, apiResponse.error);\n return undefined as any;\n } else {\n const error = new Error(`${apiResponse.response}:${apiResponse.error}`) as any;\n error.status = apiResponse.response;\n error.digest = `HTTP_${apiResponse.response}`;\n error.body = apiResponse.raw;\n throw error;\n }\n }\n\n return apiResponse.raw;\n }\n}\n","\"use client\";\n\nimport { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\nimport { ApiResponseInterface } from \"../interfaces/ApiResponseInterface\";\nimport {\n ClientJsonApiGet,\n ClientJsonApiPost,\n ClientJsonApiPut,\n ClientJsonApiPatch,\n ClientJsonApiDelete,\n} from \"../../client/JsonApiClient\";\nimport { setLastApiResponse } from \"./lastApiResponse\";\nimport { getI18nSupportedLocales, getI18nDefaultLocale } from \"../../i18n/config\";\n// Duplicated to avoid importing from AbstractService which pulls in server code\n// These are exported so client services can import them from here instead of core\nexport enum ClientHttpMethod {\n GET = \"GET\",\n POST = \"POST\",\n PUT = \"PUT\",\n PATCH = \"PATCH\",\n DELETE = \"DELETE\",\n}\n\nexport interface ClientNextRef {\n next?: string;\n}\n\nexport interface ClientPreviousRef {\n previous?: string;\n}\n\nexport interface ClientSelfRef {\n self?: string;\n}\n\nexport interface ClientTotalRef {\n total?: number;\n}\n\nlet globalErrorHandler: ((status: number, message: string) => void) | null = null;\n\n/**\n * Set a global error handler for API errors (client-side only).\n * This handler will be called instead of throwing errors.\n */\nexport function setClientGlobalErrorHandler(handler: (status: number, message: string) => void) {\n globalErrorHandler = handler;\n}\n\n/**\n * Get the current global error handler.\n */\nexport function getClientGlobalErrorHandler(): ((status: number, message: string) => void) | null {\n return globalErrorHandler;\n}\n\n/**\n * Client-side abstract base class for services that interact with the JSON:API.\n * Use this for client components.\n */\nexport abstract class ClientAbstractService {\n /**\n * Extract locale from client-side URL pathname\n * URL structure: /{locale}/route-path (e.g., /it/accounts)\n * Fallback chain: URL locale → the app's default locale (first supported locale)\n */\n private static getClientLocale(): string {\n if (typeof window === \"undefined\") {\n return getI18nDefaultLocale(); // Should not happen in client service\n }\n\n // Extract locale from URL pathname (first segment after leading slash)\n const pathSegments = window.location.pathname.split(\"/\").filter(Boolean);\n const urlLocale = pathSegments[0];\n\n // Validate against the app's supported locales (default [\"en\"])\n const supportedLocales = getI18nSupportedLocales();\n if (urlLocale && supportedLocales.includes(urlLocale)) {\n return urlLocale;\n }\n\n // Fallback to the app's default locale, never the browser's language\n return getI18nDefaultLocale();\n }\n\n /**\n * Fetch the next page of results.\n */\n static async next<T>(params: {\n type: ApiRequestDataTypeInterface;\n endpoint: string;\n next?: ClientNextRef;\n previous?: ClientPreviousRef;\n self?: ClientSelfRef;\n total?: ClientTotalRef;\n }): Promise<T> {\n return await this.callApi<T>({\n method: ClientHttpMethod.GET,\n type: params.type,\n endpoint: params.endpoint,\n next: params.next,\n previous: params.previous,\n self: params.self,\n total: params.total,\n });\n }\n\n /**\n * Fetch the previous page of results.\n */\n static async previous<T>(params: {\n type: ApiRequestDataTypeInterface;\n endpoint: string;\n next?: ClientNextRef;\n previous?: ClientPreviousRef;\n self?: ClientSelfRef;\n total?: ClientTotalRef;\n }): Promise<T> {\n return await this.callApi<T>({\n method: ClientHttpMethod.GET,\n type: params.type,\n endpoint: params.endpoint,\n next: params.next,\n previous: params.previous,\n self: params.self,\n total: params.total,\n });\n }\n\n /**\n * Make a client-side API call.\n */\n protected static async callApi<T>(params: {\n type: ApiRequestDataTypeInterface;\n method: ClientHttpMethod;\n endpoint: string;\n companyId?: string;\n input?: any;\n overridesJsonApiCreation?: boolean;\n next?: ClientNextRef;\n previous?: ClientPreviousRef;\n self?: ClientSelfRef;\n total?: ClientTotalRef;\n responseType?: ApiRequestDataTypeInterface;\n files?: { [key: string]: File | Blob } | File | Blob;\n /**\n * Per-call override of the API base URL (mirrors `AbstractService.callApi`'s `baseUrl`).\n * Omitted => existing global `NEXT_PUBLIC_API_URL` resolution, unchanged.\n */\n baseUrl?: string;\n }): Promise<T> {\n let apiResponse: ApiResponseInterface;\n\n // Client-side: extract locale from URL pathname\n const language = this.getClientLocale();\n\n switch (params.method) {\n case ClientHttpMethod.GET:\n apiResponse = await ClientJsonApiGet({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n language: language,\n baseUrl: params.baseUrl,\n });\n break;\n case ClientHttpMethod.POST:\n apiResponse = await ClientJsonApiPost({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n body: params.input,\n overridesJsonApiCreation: params.overridesJsonApiCreation,\n language: language,\n responseType: params.responseType,\n files: params.files,\n baseUrl: params.baseUrl,\n });\n break;\n case ClientHttpMethod.PUT:\n apiResponse = await ClientJsonApiPut({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n body: params.input,\n language: language,\n responseType: params.responseType,\n files: params.files,\n baseUrl: params.baseUrl,\n });\n break;\n case ClientHttpMethod.PATCH:\n apiResponse = await ClientJsonApiPatch({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n body: params.input,\n overridesJsonApiCreation: params.overridesJsonApiCreation,\n language: language,\n responseType: params.responseType,\n files: params.files,\n baseUrl: params.baseUrl,\n });\n break;\n case ClientHttpMethod.DELETE:\n apiResponse = await ClientJsonApiDelete({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n language: language,\n responseType: params.responseType,\n baseUrl: params.baseUrl,\n });\n break;\n default:\n throw new Error(\"Method not found\");\n }\n\n if (!apiResponse.ok) {\n if (globalErrorHandler) {\n globalErrorHandler(apiResponse.response, apiResponse.error);\n return undefined as any;\n } else {\n const error = new Error(`${apiResponse.response}:${apiResponse.error}`) as any;\n error.status = apiResponse.response;\n error.digest = `HTTP_${apiResponse.response}`;\n error.body = apiResponse.raw;\n throw error;\n }\n }\n\n if (apiResponse.next && params.next) params.next.next = apiResponse.next;\n if (apiResponse.prev && params.previous) params.previous.previous = apiResponse.prev;\n if (apiResponse.self && params.self) params.self.self = apiResponse.self;\n if (apiResponse.meta?.total !== undefined && params.total) params.total.total = apiResponse.meta.total;\n // Pagination runs through this base, so it must refresh the shared store too —\n // otherwise `useDataListRetriever` keeps showing the total of whatever list was\n // loaded before this one.\n setLastApiResponse(apiResponse.meta);\n\n return apiResponse.data as T;\n }\n\n /**\n * Get raw JSON:API response data without deserialization.\n */\n protected static async getRawData(params: {\n type: ApiRequestDataTypeInterface;\n method: ClientHttpMethod;\n endpoint: string;\n companyId?: string;\n }): Promise<any> {\n const language = this.getClientLocale();\n\n const apiResponse: ApiResponseInterface = await ClientJsonApiGet({\n classKey: params.type,\n endpoint: params.endpoint,\n companyId: params.companyId,\n language: language,\n });\n\n if (!apiResponse.ok) {\n if (globalErrorHandler) {\n globalErrorHandler(apiResponse.response, apiResponse.error);\n return undefined as any;\n } else {\n const error = new Error(`${apiResponse.response}:${apiResponse.error}`) as any;\n error.status = apiResponse.response;\n error.digest = `HTTP_${apiResponse.response}`;\n error.body = apiResponse.raw;\n throw error;\n }\n }\n\n return apiResponse.raw;\n }\n}\n","import { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\nimport { DataClassRegistry } from \"./DataClassRegistry\";\n\n/**\n * Helper class to bootstrap the registry from a Modules-style class.\n * This supports the pattern where modules are defined as static getters.\n */\nexport class ModuleRegistrar {\n private static _isBootstrapped = false;\n\n /**\n * Bootstrap the registry from a Modules class.\n * Automatically detects static getters and registers their models.\n *\n * @param modulesClass - The Modules class with static getters\n */\n static bootstrap<T extends object>(modulesClass: T): void {\n if (this._isBootstrapped) return;\n\n const data = Object.getOwnPropertyNames(modulesClass)\n .filter((key) => {\n const descriptor = Object.getOwnPropertyDescriptor(modulesClass, key);\n return descriptor && typeof descriptor.get === \"function\";\n })\n .map((key) => (modulesClass as any)[key] as ApiRequestDataTypeInterface);\n\n data.forEach((item) => {\n if (item && item.model) {\n DataClassRegistry.registerObjectClass(item, item.model);\n }\n });\n\n this._isBootstrapped = true;\n }\n\n /**\n * Reset the bootstrapped state. Useful for testing.\n */\n static reset(): void {\n this._isBootstrapped = false;\n DataClassRegistry.clear();\n }\n}\n","export type GetterKeys<T> = {\n [K in keyof T]: T[K] extends () => any ? never : K;\n}[keyof T];\n\nexport type FieldSelector<T> = {\n type: string;\n fields: ReadonlyArray<GetterKeys<T>>;\n};\n\nexport function createJsonApiInclusion<T>(dataType: string, fields: ReadonlyArray<GetterKeys<T>>): FieldSelector<T> {\n return {\n type: dataType,\n fields,\n };\n}\n","import { ApiDataInterface } from \"../interfaces/ApiDataInterface\";\nimport { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\nimport { JsonApiHydratedDataInterface } from \"../interfaces/JsonApiHydratedDataInterface\";\nimport { RehydrationFactory } from \"../factories/RehydrationFactory\";\n\n/**\n * Rehydrate a single dehydrated object back into its typed class instance.\n */\nexport function rehydrate<T extends ApiDataInterface>(\n classKey: ApiRequestDataTypeInterface,\n data: JsonApiHydratedDataInterface,\n): T {\n return RehydrationFactory.rehydrate(classKey, data) as T;\n}\n\n/**\n * Rehydrate a list of dehydrated objects back into typed class instances.\n */\nexport function rehydrateList<T extends ApiDataInterface>(\n classKey: ApiRequestDataTypeInterface,\n data: JsonApiHydratedDataInterface[],\n): T[] {\n return RehydrationFactory.rehydrateList(classKey, data) as T[];\n}\n","/**\n * A next-intl message file: nested objects whose leaves are strings or arrays of\n * strings (read with `t.raw()`).\n */\nexport type MessageTree = { [key: string]: string | string[] | MessageTree };\n\nfunction isMessageTree(value: unknown): value is MessageTree {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction copyLeaf(value: string | string[]): string | string[] {\n return Array.isArray(value) ? [...value] : value;\n}\n\n/**\n * Deep-merges two message trees. At every path the override's leaf wins; a path\n * only in the base stays; a path only in the override is added. A non-object\n * value (a string or an array) replaces, never merges. Neither input is mutated;\n * the result is a new object, and a new array, at every level.\n */\nexport function mergeMessages(base: MessageTree, override: MessageTree): MessageTree {\n const merged: MessageTree = {};\n\n for (const [key, value] of Object.entries(base)) {\n merged[key] = isMessageTree(value) ? mergeMessages(value, {}) : copyLeaf(value);\n }\n\n for (const [key, value] of Object.entries(override)) {\n const current = merged[key];\n merged[key] = isMessageTree(value) ? mergeMessages(isMessageTree(current) ? current : {}, value) : copyLeaf(value);\n }\n\n return merged;\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import * as React from \"react\";\n\ntype PossibleRef<T> = React.Ref<T> | undefined;\n\n/**\n * Set a given ref to a given value\n * This utility takes care of different types of refs: callback refs and RefObject(s)\n */\nfunction setRef<T>(ref: PossibleRef<T>, value: T) {\n if (typeof ref === \"function\") {\n return ref(value);\n }\n\n if (ref !== null && ref !== undefined) {\n ref.current = value;\n }\n}\n\n/**\n * A utility to compose multiple refs together\n * Accepts callback refs and RefObject(s)\n */\nfunction composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n return (node) => {\n let hasCleanup = false;\n const cleanups = refs.map((ref) => {\n const cleanup = setRef(ref, node);\n if (!hasCleanup && typeof cleanup === \"function\") {\n hasCleanup = true;\n }\n return cleanup;\n });\n\n // React <19 will log an error to the console if a callback ref returns a\n // value. We don't use ref cleanups internally so this will only happen if a\n // user's ref callback returns a value, which we only expect if they are\n // using the cleanup functionality added in React 19.\n if (hasCleanup) {\n return () => {\n for (let i = 0; i < cleanups.length; i++) {\n const cleanup = cleanups[i];\n if (typeof cleanup === \"function\") {\n cleanup();\n } else {\n setRef(refs[i], null);\n }\n }\n };\n }\n };\n}\n\n/**\n * A custom hook that composes multiple refs\n * Accepts callback refs and RefObject(s)\n */\nfunction useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n return React.useCallback(composeRefs(...refs), refs);\n}\n\nexport { composeRefs, useComposedRefs };\n","\"use client\";\n\nimport * as React from \"react\";\n\nexport const MOBILE_BREAKPOINT = 768;\nexport const VIEWPORT_COOKIE_NAME = \"viewport_mobile\";\n\n/**\n * The viewport context is keyed on a globalThis Symbol so that tsup's\n * multi-entry `splitting: true` build cannot produce two distinct context\n * objects. If it did, the ViewportProvider (built into the `contexts` entry)\n * and this hook (built into the `core` entry) would silently miss each other:\n * the hook would read `undefined`, fall back to the legacy path, and the\n * mobile-layout flash would return with nothing failing loudly.\n *\n * THIS MODULE IS SERVER-REACHABLE. `core` is deliberately absent from\n * tsup.config.ts's `clientEntries` list, so `dist/core/*` gets NO \"use client\"\n * banner — that is what lets `apps/web/src/instrumentation.ts` import\n * jsonapi.config -> Bootstrapper -> `@carlonicora/nextjs-jsonapi/core` at\n * server startup. Two rules follow, both load-bearing:\n *\n * 1. React is imported as a NAMESPACE. A named\n * `import { createContext } from \"react\"` makes Turbopack statically refuse\n * the module: \"You're importing a module that depends on `createContext`\n * into a React Server Component module.\"\n * 2. Creation is LAZY, never at module scope. On the server React resolves to\n * the vendored RSC build, which omits `createContext` entirely\n * (\"createContext is not a function\" — crashes the dev server at boot).\n *\n * `useIsMobile` only ever runs inside a Client Component render, so calling the\n * accessor from there is safe. The PROVIDER must not live here — it is rendered\n * by the server layouts, so it needs a real \"use client\" boundary and therefore\n * lives in `contexts/ViewportContext.tsx`.\n */\nconst VIEWPORT_CONTEXT_KEY = Symbol.for(\"nextjs-jsonapi:viewportContext\");\n\nconst globalStore = globalThis as unknown as {\n [VIEWPORT_CONTEXT_KEY]?: React.Context<ViewportContextValue>;\n};\n\n/**\n * A boolean once measured or seeded synchronously; a promise while a\n * session-seeded provider waits for the request's snapshot.\n */\nexport type ViewportContextValue = boolean | Promise<boolean> | undefined;\n\n/** Returns the process-wide viewport context, creating it on first use. */\nexport function getViewportContext(): React.Context<ViewportContextValue> {\n if (globalStore[VIEWPORT_CONTEXT_KEY] === undefined) {\n globalStore[VIEWPORT_CONTEXT_KEY] = React.createContext<ViewportContextValue>(undefined);\n }\n return globalStore[VIEWPORT_CONTEXT_KEY]!;\n}\n\n/** @internal — consumed by contexts/ViewportContext.tsx */\nexport function measureViewport(): boolean {\n return window.innerWidth < MOBILE_BREAKPOINT;\n}\n\n/** @internal — consumed by contexts/ViewportContext.tsx */\nexport function persistViewport(isMobile: boolean): void {\n document.cookie = `${VIEWPORT_COOKIE_NAME}=${isMobile ? \"1\" : \"0\"}; path=/; max-age=31536000; samesite=lax`;\n}\n\n/** @internal — consumed by contexts/ViewportContext.tsx */\nexport function subscribeViewport(onChange: () => void): () => void {\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n onChange();\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n}\n\ntype SettledThenable<T> = Promise<T> & { status: \"fulfilled\"; value: T };\n\n/**\n * A promise React reads synchronously: `use()` sees `status: \"fulfilled\"` and\n * returns `value` without suspending. Created lazily, one per value, so the\n * identity is stable across renders.\n */\nconst settledViewportValues = new Map<boolean | undefined, SettledThenable<boolean | undefined>>();\n\nfunction settled(value: boolean | undefined): SettledThenable<boolean | undefined> {\n let thenable = settledViewportValues.get(value);\n if (thenable === undefined) {\n thenable = Object.assign(Promise.resolve(value), { status: \"fulfilled\" as const, value });\n settledViewportValues.set(value, thenable);\n }\n return thenable;\n}\n\nfunction isThenable(value: ViewportContextValue): value is Promise<boolean> {\n return typeof value === \"object\" && value !== null && typeof (value as PromiseLike<boolean>).then === \"function\";\n}\n\nexport function useIsMobile(): boolean {\n const provided = React.useContext(getViewportContext());\n // `React.use` through the namespace import, for the same reason as\n // `createContext` above. Suspends the caller until a session seed settles.\n //\n // `use` is called on EVERY render, never only when the value is a promise. A\n // session-seeded ViewportProvider exposes a promise first and a boolean once\n // its mount-time measurement lands, which can happen while a reader is\n // suspended on the promise. Skipping `use` on the retry makes React report\n // https://react.dev/warnings/conditional-use-of-use. A plain value is wrapped\n // in an already-settled thenable, which `use` returns without suspending.\n const seeded = React.use<boolean | undefined>(isThenable(provided) ? provided : settled(provided));\n const [legacy, setLegacy] = React.useState<boolean>(false);\n\n React.useEffect(() => {\n // A provider owns the value; never run the legacy measurement alongside it.\n if (seeded !== undefined) return;\n return subscribeViewport(() => setLegacy(measureViewport()));\n }, [seeded]);\n\n return seeded ?? legacy;\n}\n","type FormatOption = \"date\" | \"time\" | \"dateTime\" | \"timeSince\" | \"default\";\n\n/**\n * Format a `Date` as `YYYY-MM-DD` using the value's local-time components.\n *\n * Use for JSON:API attributes whose backend field type is `\"date\"` (calendar\n * date with no time component). `JSON.stringify(new Date(...))` would call\n * `.toISOString()`, which UTC-shifts and can lose a day west of UTC; this\n * helper avoids that by emitting the local-date components verbatim.\n *\n * For `\"datetime\"` fields use `.toISOString()` instead — those represent an\n * instant in time and the UTC shift is correct.\n */\nexport const formatLocalDate = (d: Date): string => {\n const year = d.getFullYear();\n const month = String(d.getMonth() + 1).padStart(2, \"0\");\n const day = String(d.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n};\n\n/**\n * The locale `formatDate` should use for an app locale. English keeps \"en-GB\",\n * the formatter's historical default, so English output never changes; any\n * other app locale (\"it\") is used as is.\n */\nexport const dateFormatLocale = (appLocale?: string): string =>\n !appLocale || appLocale.split(\"-\")[0] === \"en\" ? \"en-GB\" : appLocale;\n\nexport const formatDate = (eventDate: Date, formatOption: FormatOption, locale: string = \"en-GB\"): string => {\n const formatPart = (date: Date, options: Intl.DateTimeFormatOptions): string =>\n new Intl.DateTimeFormat(locale, options).format(date);\n\n const now = new Date();\n const diff = now.getTime() - eventDate.getTime();\n\n if (formatOption === \"default\") {\n if (diff < 24 * 60 * 60 * 1000) formatOption = \"timeSince\";\n else formatOption = \"dateTime\";\n }\n\n if (formatOption === \"timeSince\") {\n const seconds = Math.floor(diff / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n\n if (locale.split(\"-\")[0] !== \"en\") {\n const relative = new Intl.RelativeTimeFormat(locale, { numeric: \"always\" });\n if (days > 0) return relative.format(-days, \"day\");\n if (hours > 0) return relative.format(-hours, \"hour\");\n if (minutes > 0) return relative.format(-minutes, \"minute\");\n return relative.format(-seconds, \"second\");\n }\n\n if (days > 0) return `${days} days ago`;\n if (hours > 0) return `${hours} hours ago`;\n if (minutes > 0) return `${minutes} minutes ago`;\n return `${seconds} seconds ago`;\n }\n\n // Define formatting options\n const dateOptions: Intl.DateTimeFormatOptions = {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n };\n const timeOptions: Intl.DateTimeFormatOptions = {\n hour: \"2-digit\",\n minute: \"2-digit\",\n };\n\n // Format based on the option\n switch (formatOption) {\n case \"date\":\n return formatPart(eventDate, dateOptions);\n case \"time\":\n return formatPart(eventDate, timeOptions);\n case \"dateTime\":\n return `${formatPart(eventDate, dateOptions)} ${formatPart(eventDate, timeOptions)}`;\n default:\n throw new Error(\"Invalid format option\");\n }\n};\n\nexport type { FormatOption };\n","export const exists = <T>(itemOrArray: T | T[] | null | undefined): boolean => {\n if (!itemOrArray) return false;\n if (Array.isArray(itemOrArray)) {\n return itemOrArray.length > 0;\n }\n return true;\n};\n","import { Action, ModuleWithPermissions } from \"../permissions\";\nimport { cloneElement, createElement, Fragment, ReactElement } from \"react\";\n\nexport class TableOptions {\n private _components: ReactElement<any>[];\n private _hasPermissionToModule: <M extends ModuleWithPermissions>(params: {\n module: M;\n action: Action;\n data?: any;\n }) => boolean;\n\n constructor(\n hasPermissionToModule: <M extends ModuleWithPermissions>(params: {\n module: M;\n action: Action;\n data?: any;\n }) => boolean,\n ) {\n this._hasPermissionToModule = hasPermissionToModule;\n this._components = [];\n }\n\n addOption<M extends ModuleWithPermissions>(component: ReactElement<any> | null, module?: M, action?: Action): void {\n if (!component || (module && action && !this._hasPermissionToModule({ module, action }))) return;\n this._components.push(component);\n }\n\n getComponents(): ReactElement<any>[] {\n return this._components;\n }\n\n getOptions(): ReactElement<any> | null {\n if (this._components.length === 0) return null;\n\n const response: ReactElement<any>[] = this._components.map((option, index) =>\n cloneElement(option, { key: option.key ?? index }),\n );\n\n return createElement(Fragment, {}, response);\n }\n}\n\nexport function getTableOptions(params: {\n hasPermissionToModule: <M extends ModuleWithPermissions>(params: {\n module: M;\n action: Action;\n data?: any;\n }) => boolean;\n options: { component: ReactElement<any> | null; module?: ModuleWithPermissions; action?: Action }[];\n}): ReactElement<any> | null {\n const tableOptions = new TableOptions(params.hasPermissionToModule);\n params.options.forEach((option) => tableOptions.addOption(option.component, option.module, option.action));\n return tableOptions.getOptions();\n}\n\nexport function getTableComponents(params: {\n hasPermissionToModule: <M extends ModuleWithPermissions>(params: {\n module: M;\n action: Action;\n data?: any;\n }) => boolean;\n options: { component: ReactElement<any> | null; module?: ModuleWithPermissions; action?: Action }[];\n}): ReactElement<any>[] {\n const tableOptions = new TableOptions(params.hasPermissionToModule);\n params.options.forEach((option) => tableOptions.addOption(option.component, option.module, option.action));\n return tableOptions.getComponents();\n}\n","import { z } from \"zod\";\n\nexport const userObjectSchema = z.object({\n id: z.string(),\n name: z.string(),\n avatar: z.string().optional(),\n});\n\nexport type UserObject = z.infer<typeof userObjectSchema>;\n","import { z } from \"zod\";\n\nexport const entityObjectSchema = z.object({\n id: z.uuidv4(),\n name: z.string(),\n});\n\nexport type EntityObject = z.infer<typeof entityObjectSchema>;\n","export function getInitials(name: string): string;\nexport function getInitials(firstName: string, lastName: string): string;\nexport function getInitials(firstOrName: string, lastName?: string): string {\n if (lastName !== undefined) {\n const first = firstOrName?.trim()?.[0] ?? \"\";\n const last = lastName?.trim()?.[0] ?? \"\";\n if (first && last) return (first + last).toUpperCase();\n return (firstOrName || lastName || \"\").slice(0, 2).toUpperCase();\n }\n\n const words = firstOrName.trim().split(/\\s+/);\n if (words.length >= 2) {\n return (words[0][0] + words[1][0]).toUpperCase();\n }\n return firstOrName.slice(0, 2).toUpperCase();\n}\n","/**\n * True when an image src is an absolute remote URL.\n *\n * `next/image` THROWS when it is given an absolute URL whose hostname is not\n * listed in `images.remotePatterns`, which takes down the whole page through\n * the nearest error boundary. Avatars can come from OAuth providers\n * (Discord, Google, ...), so their hostname is user-controlled and can never\n * be reliably enumerated in config. Pass `unoptimized` for these srcs.\n *\n * `blob:` and `data:` srcs are excluded: `next/image` already marks those\n * unoptimized on its own.\n */\nexport function isRemoteImageSrc(src?: string | null): boolean {\n if (!src) return false;\n return src.startsWith(\"http://\") || src.startsWith(\"https://\");\n}\n","import CodiceFiscale from \"codice-fiscale-js\";\n\nexport function validatePartitaIva(partitaIva: string): boolean {\n if (!partitaIva || typeof partitaIva !== \"string\") {\n return false;\n }\n\n const cleaned = partitaIva.replace(/\\s/g, \"\");\n\n if (!/^\\d{11}$/.test(cleaned)) {\n return false;\n }\n\n const digits = cleaned.split(\"\").map(Number);\n\n const oddSum = digits[0] + digits[2] + digits[4] + digits[6] + digits[8] + digits[10];\n\n let evenSum = 0;\n for (let i = 1; i < 10; i += 2) {\n let doubled = digits[i] * 2;\n if (doubled > 9) {\n doubled -= 9;\n }\n evenSum += doubled;\n }\n\n const totalSum = oddSum + evenSum;\n\n return totalSum % 10 === 0;\n}\n\nexport type CodiceFiscaleValidationOptions = {\n /** When true, a valid Partita IVA is also accepted as a valid value. */\n allowPartitaIva?: boolean;\n};\n\nexport function validateCodiceFiscale(codiceFiscale: string, options?: CodiceFiscaleValidationOptions): boolean {\n if (!codiceFiscale || typeof codiceFiscale !== \"string\") {\n return false;\n }\n\n const cleaned = codiceFiscale.replace(/\\s/g, \"\").toUpperCase();\n\n try {\n if (CodiceFiscale.check(cleaned)) {\n return true;\n }\n } catch {\n // fall through to the optional Partita IVA check\n }\n\n return options?.allowPartitaIva === true && validatePartitaIva(cleaned);\n}\n\nexport function validateItalianTaxCode(\n value: string,\n type: \"partitaIva\" | \"codiceFiscale\",\n options?: CodiceFiscaleValidationOptions,\n): boolean {\n if (!value || typeof value !== \"string\") {\n return false;\n }\n\n switch (type) {\n case \"partitaIva\":\n return validatePartitaIva(value);\n case \"codiceFiscale\":\n return validateCodiceFiscale(value, options);\n default:\n return false;\n }\n}\n\nexport function formatPartitaIva(partitaIva: string): string {\n if (!partitaIva) return \"\";\n\n const cleaned = partitaIva.replace(/\\s/g, \"\");\n if (/^\\d{11}$/.test(cleaned)) {\n return `${cleaned.substring(0, 7)} ${cleaned.substring(7, 10)} ${cleaned.substring(10)}`;\n }\n return partitaIva;\n}\n\nexport function formatCodiceFiscale(codiceFiscale: string): string {\n if (!codiceFiscale) return \"\";\n\n const cleaned = codiceFiscale.replace(/\\s/g, \"\").toUpperCase();\n if (/^[A-Z0-9]{16}$/.test(cleaned)) {\n return `${cleaned.substring(0, 6)} ${cleaned.substring(6, 11)} ${cleaned.substring(11, 15)} ${cleaned.substring(15)}`;\n }\n return codiceFiscale;\n}\n","import { PartialBlock } from \"@blocknote/core\";\nimport { v4 } from \"uuid\";\n\nexport interface WordDiff {\n type: \"added\" | \"removed\" | \"unchanged\";\n text: string;\n diffId: string;\n accepted?: boolean;\n rejected?: boolean;\n}\n\nexport interface DiffBlock {\n id?: string;\n type?: string;\n props?: any;\n content?: any;\n children?: DiffBlock[];\n diffType?: \"added\" | \"removed\" | \"modified\" | \"unchanged\";\n originalContent?: any;\n diffId?: string;\n wordDiffs?: WordDiff[];\n accepted?: boolean;\n rejected?: boolean;\n}\n\nexport interface DiffResult {\n blocks: DiffBlock[];\n hasChanges: boolean;\n}\n\nexport interface BlockDiffOptions {\n ignoreIds?: boolean;\n compareContent?: boolean;\n similarityThreshold?: number;\n}\n\n/**\n * BlockNote Diff Utility\n * Implements a sophisticated diff algorithm for BlockNote document structures\n */\nexport class BlockNoteDiffUtil {\n private static readonly DEFAULT_OPTIONS: BlockDiffOptions = {\n ignoreIds: false,\n compareContent: true,\n similarityThreshold: 0.8,\n };\n\n /**\n * Compare two BlockNote documents and return diff result\n */\n static diff(\n originalBlocks: PartialBlock[] = [],\n newBlocks: PartialBlock[] = [],\n options: BlockDiffOptions = {},\n ): DiffResult {\n const opts = { ...this.DEFAULT_OPTIONS, ...options };\n\n // Create a map for efficient lookups\n const originalMap = new Map<string, PartialBlock>();\n const newMap = new Map<string, PartialBlock>();\n\n // Build maps based on block IDs\n originalBlocks.forEach((block) => {\n if (block.id) {\n originalMap.set(block.id, block);\n }\n });\n\n newBlocks.forEach((block) => {\n if (block.id) {\n newMap.set(block.id, block);\n }\n });\n\n const diffBlocks: DiffBlock[] = [];\n const processedNewIds = new Set<string>();\n\n // Process original blocks to find removed and modified blocks\n for (const originalBlock of originalBlocks) {\n const blockId = originalBlock.id;\n\n // Generate ID if missing\n const processId = blockId || v4();\n const newBlock = blockId ? newMap.get(blockId) : null;\n\n if (!newBlock) {\n // Block was removed\n const removedBlock: DiffBlock = {\n ...originalBlock,\n id: processId,\n diffType: \"removed\",\n diffId: `removed-${processId}`,\n };\n diffBlocks.push(removedBlock);\n } else {\n // Block exists in both, check for modifications\n if (blockId) {\n processedNewIds.add(blockId);\n }\n\n if (this.areBlocksEqual(originalBlock, newBlock, opts)) {\n // Block is unchanged\n const unchangedBlock: DiffBlock = {\n ...newBlock,\n diffType: \"unchanged\",\n };\n diffBlocks.push(unchangedBlock);\n } else {\n // Block was modified - perform word-level diff\n const wordDiffs = this.generateWordDiffs(originalBlock.content, newBlock.content, processId);\n const modifiedBlock: DiffBlock = {\n ...newBlock,\n diffType: \"modified\",\n originalContent: originalBlock.content as any,\n diffId: `modified-${processId}`,\n wordDiffs: wordDiffs,\n };\n diffBlocks.push(modifiedBlock);\n }\n }\n }\n\n // Process new blocks to find added blocks\n for (const newBlock of newBlocks) {\n const blockId = newBlock.id;\n\n // Skip if block was already processed (has ID and was found in original)\n if (blockId && processedNewIds.has(blockId)) continue;\n\n // This is a new block (either no ID or ID not found in original)\n const generatedId = blockId || v4();\n const addedBlock: DiffBlock = {\n ...newBlock,\n id: generatedId,\n diffType: \"added\",\n diffId: `added-${generatedId}`,\n };\n diffBlocks.push(addedBlock);\n }\n\n // Sort blocks to maintain original order as much as possible\n const sortedDiffBlocks = this.sortDiffBlocks(diffBlocks, originalBlocks, newBlocks);\n\n return {\n blocks: sortedDiffBlocks,\n hasChanges: sortedDiffBlocks.some((block) => block.diffType !== \"unchanged\"),\n };\n }\n\n /**\n * Compare two blocks for equality\n */\n private static areBlocksEqual(block1: PartialBlock, block2: PartialBlock, options: BlockDiffOptions): boolean {\n // Compare block type\n if (block1.type !== block2.type) {\n return false;\n }\n\n // Compare props (excluding diffType specific props)\n if (!this.deepEqual(block1.props, block2.props)) {\n return false;\n }\n\n // Compare content if enabled\n if (options.compareContent) {\n if (!this.deepEqual(block1.content, block2.content)) {\n return false;\n }\n }\n\n // Compare children recursively\n const children1 = block1.children || [];\n const children2 = block2.children || [];\n\n if (children1.length !== children2.length) {\n return false;\n }\n\n for (let i = 0; i < children1.length; i++) {\n if (!this.areBlocksEqual(children1[i], children2[i], options)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Deep equality comparison for objects\n */\n private static deepEqual(obj1: any, obj2: any): boolean {\n if (obj1 === obj2) return true;\n\n if (obj1 == null || obj2 == null) return obj1 === obj2;\n\n if (typeof obj1 !== typeof obj2) return false;\n\n if (typeof obj1 !== \"object\") return obj1 === obj2;\n\n if (Array.isArray(obj1) !== Array.isArray(obj2)) return false;\n\n if (Array.isArray(obj1)) {\n if (obj1.length !== obj2.length) return false;\n for (let i = 0; i < obj1.length; i++) {\n if (!this.deepEqual(obj1[i], obj2[i])) return false;\n }\n return true;\n }\n\n const keys1 = Object.keys(obj1);\n const keys2 = Object.keys(obj2);\n\n if (keys1.length !== keys2.length) return false;\n\n for (const key of keys1) {\n if (!keys2.includes(key)) return false;\n if (!this.deepEqual(obj1[key], obj2[key])) return false;\n }\n\n return true;\n }\n\n /**\n * Sort diff blocks to maintain logical order\n */\n private static sortDiffBlocks(\n diffBlocks: DiffBlock[],\n originalBlocks: PartialBlock[],\n newBlocks: PartialBlock[],\n ): DiffBlock[] {\n // Create position maps\n const originalPositions = new Map<string, number>();\n const newPositions = new Map<string, number>();\n\n originalBlocks.forEach((block, index) => {\n if (block.id) {\n originalPositions.set(block.id, index);\n }\n });\n\n newBlocks.forEach((block, index) => {\n if (block.id) {\n newPositions.set(block.id, index);\n }\n });\n\n return diffBlocks.sort((a, b) => {\n const aId = a.id;\n const bId = b.id;\n\n if (!aId || !bId) return 0;\n\n // Prioritize by new positions, fallback to original positions\n const aPos = newPositions.get(aId) ?? originalPositions.get(aId) ?? Infinity;\n const bPos = newPositions.get(bId) ?? originalPositions.get(bId) ?? Infinity;\n\n return aPos - bPos;\n });\n }\n\n /**\n * Calculate similarity between two text contents\n */\n private static calculateSimilarity(content1: any[], content2: any[]): number {\n if (!content1 || !content2) return 0;\n\n const text1 = this.extractTextFromContent(content1);\n const text2 = this.extractTextFromContent(content2);\n\n if (text1 === text2) return 1;\n if (!text1 || !text2) return 0;\n\n // Simple Levenshtein distance-based similarity\n const maxLength = Math.max(text1.length, text2.length);\n const distance = this.levenshteinDistance(text1, text2);\n\n return 1 - distance / maxLength;\n }\n\n /**\n * Extract plain text from BlockNote content array\n */\n private static extractTextFromContent(content: any[]): string {\n if (!Array.isArray(content)) return \"\";\n\n return content\n .map((item) => {\n if (item.type === \"text\") {\n return item.text || \"\";\n }\n return \"\";\n })\n .join(\"\");\n }\n\n /**\n * Calculate Levenshtein distance between two strings\n */\n private static levenshteinDistance(str1: string, str2: string): number {\n const matrix = Array(str2.length + 1)\n .fill(null)\n .map(() => Array(str1.length + 1).fill(null));\n\n for (let i = 0; i <= str1.length; i++) {\n matrix[0][i] = i;\n }\n\n for (let j = 0; j <= str2.length; j++) {\n matrix[j][0] = j;\n }\n\n for (let j = 1; j <= str2.length; j++) {\n for (let i = 1; i <= str1.length; i++) {\n const substitutionCost = str1[i - 1] === str2[j - 1] ? 0 : 1;\n matrix[j][i] = Math.min(\n matrix[j][i - 1] + 1, // deletion\n matrix[j - 1][i] + 1, // insertion\n matrix[j - 1][i - 1] + substitutionCost, // substitution\n );\n }\n }\n\n return matrix[str2.length][str1.length];\n }\n\n /**\n * Generate word-level diffs between two content arrays\n */\n static generateWordDiffs(originalContent: any, newContent: any, blockId: string): WordDiff[] {\n const originalText = this.extractTextFromContent(originalContent || []);\n const newText = this.extractTextFromContent(newContent || []);\n\n if (!originalText && !newText) return [];\n if (!originalText) {\n return newText\n .split(/\\s+/)\n .filter((word) => word.trim())\n .map((word, index) => ({\n type: \"added\" as const,\n text: word,\n diffId: `${blockId}-add-${index}`,\n accepted: false,\n rejected: false,\n }));\n }\n if (!newText) {\n return originalText\n .split(/\\s+/)\n .filter((word) => word.trim())\n .map((word, index) => ({\n type: \"removed\" as const,\n text: word,\n diffId: `${blockId}-remove-${index}`,\n accepted: false,\n rejected: false,\n }));\n }\n\n return this.diffWords(originalText, newText, blockId);\n }\n\n /**\n * Perform word-level diff using word-only approach (no space tokenization)\n */\n private static diffWords(text1: string, text2: string, blockId: string): WordDiff[] {\n const words1 = text1.split(/\\s+/).filter((word) => word.length > 0);\n const words2 = text2.split(/\\s+/).filter((word) => word.length > 0);\n\n const diffs = this.myersDiff(words1, words2);\n\n // Consolidate adjacent changes to reduce fragmentation\n const consolidatedDiffs = this.consolidateAdjacentChanges(diffs);\n\n const result: WordDiff[] = [];\n let diffIndex = 0;\n\n for (let i = 0; i < consolidatedDiffs.length; i++) {\n const diff = consolidatedDiffs[i];\n const isLastDiff = i === consolidatedDiffs.length - 1;\n\n switch (diff.type) {\n case \"equal\":\n result.push({\n type: \"unchanged\",\n text: diff.value,\n diffId: `${blockId}-unchanged-${diffIndex++}`,\n accepted: true,\n rejected: false,\n });\n\n // Add space after word if not the last diff\n if (!isLastDiff) {\n result.push({\n type: \"unchanged\",\n text: \" \",\n diffId: `${blockId}-space-${diffIndex++}`,\n accepted: true,\n rejected: false,\n });\n }\n break;\n\n case \"delete\":\n // Check if this is a consolidated change or regular change\n if ((diff as any).consolidated) {\n // This is a consolidated deletion - check if next is consolidated insertion\n const nextDiff = i + 1 < consolidatedDiffs.length ? consolidatedDiffs[i + 1] : null;\n const isConsolidatedReplacement = nextDiff && nextDiff.type === \"insert\" && (nextDiff as any).consolidated;\n\n if (isConsolidatedReplacement) {\n // Use single shared diff ID for the entire consolidated change\n const replacementId = `${blockId}-replace-${diffIndex++}`;\n\n // Add the removed text\n result.push({\n type: \"removed\",\n text: diff.value,\n diffId: replacementId,\n accepted: false,\n rejected: false,\n });\n\n // Add the new text with same diff ID\n result.push({\n type: \"added\",\n text: nextDiff.value,\n diffId: replacementId,\n accepted: false,\n rejected: false,\n });\n\n // Skip the next insert since we handled it\n i++;\n\n // Add space after replacement if not the last\n if (i < consolidatedDiffs.length - 1) {\n result.push({\n type: \"unchanged\",\n text: \" \",\n diffId: `${blockId}-space-${diffIndex++}`,\n accepted: true,\n rejected: false,\n });\n }\n } else {\n // Consolidated deletion without replacement\n result.push({\n type: \"removed\",\n text: diff.value,\n diffId: `${blockId}-remove-${diffIndex++}`,\n accepted: false,\n rejected: false,\n });\n\n // Add space after deletion if not the last\n if (!isLastDiff) {\n result.push({\n type: \"unchanged\",\n text: \" \",\n diffId: `${blockId}-space-${diffIndex++}`,\n accepted: true,\n rejected: false,\n });\n }\n }\n } else {\n // Regular single-word processing\n const nextDiff = i + 1 < consolidatedDiffs.length ? consolidatedDiffs[i + 1] : null;\n const isReplacement =\n nextDiff && nextDiff.type === \"insert\" && this.areWordsSimilar(diff.value, nextDiff.value);\n\n if (isReplacement) {\n // Use single shared diff ID for both operations\n const replacementId = `${blockId}-replace-${diffIndex++}`;\n\n // Add the removed word\n result.push({\n type: \"removed\",\n text: diff.value,\n diffId: replacementId,\n accepted: false,\n rejected: false,\n });\n\n // Add the new word with same diff ID (no space between them)\n result.push({\n type: \"added\",\n text: nextDiff.value,\n diffId: replacementId,\n accepted: false,\n rejected: false,\n });\n\n // Skip the next insert since we handled it\n i++;\n\n // Add space after replacement if not the last\n if (i < consolidatedDiffs.length - 1) {\n result.push({\n type: \"unchanged\",\n text: \" \",\n diffId: `${blockId}-space-${diffIndex++}`,\n accepted: true,\n rejected: false,\n });\n }\n } else {\n // Regular deletion\n result.push({\n type: \"removed\",\n text: diff.value,\n diffId: `${blockId}-remove-${diffIndex++}`,\n accepted: false,\n rejected: false,\n });\n\n // Add space after deletion if not the last\n if (!isLastDiff) {\n result.push({\n type: \"unchanged\",\n text: \" \",\n diffId: `${blockId}-space-${diffIndex++}`,\n accepted: true,\n rejected: false,\n });\n }\n }\n }\n break;\n\n case \"insert\":\n // Only handle if not already processed as part of replacement or consolidation\n if ((diff as any).consolidated) {\n // This should have been handled in the delete case above\n const prevDiff = i > 0 ? consolidatedDiffs[i - 1] : null;\n const wasProcessedAsConsolidatedReplacement =\n prevDiff && prevDiff.type === \"delete\" && (prevDiff as any).consolidated;\n\n if (!wasProcessedAsConsolidatedReplacement) {\n // Standalone consolidated insertion\n result.push({\n type: \"added\",\n text: diff.value,\n diffId: `${blockId}-add-${diffIndex++}`,\n accepted: false,\n rejected: false,\n });\n\n // Add space after insertion if not the last\n if (!isLastDiff) {\n result.push({\n type: \"unchanged\",\n text: \" \",\n diffId: `${blockId}-space-${diffIndex++}`,\n accepted: true,\n rejected: false,\n });\n }\n }\n } else {\n // Regular single-word processing\n const prevDiff = i > 0 ? consolidatedDiffs[i - 1] : null;\n const wasProcessedAsReplacement =\n prevDiff && prevDiff.type === \"delete\" && this.areWordsSimilar(prevDiff.value, diff.value);\n\n if (!wasProcessedAsReplacement) {\n result.push({\n type: \"added\",\n text: diff.value,\n diffId: `${blockId}-add-${diffIndex++}`,\n accepted: false,\n rejected: false,\n });\n\n // Add space after insertion if not the last\n if (!isLastDiff) {\n result.push({\n type: \"unchanged\",\n text: \" \",\n diffId: `${blockId}-space-${diffIndex++}`,\n accepted: true,\n rejected: false,\n });\n }\n }\n }\n break;\n }\n }\n\n return result;\n } /**\n * Improved diff algorithm that better handles word insertions\n * Uses a combination of LCS and heuristics to minimize false changes\n */\n private static myersDiff(a: string[], b: string[]): Array<{ type: \"equal\" | \"delete\" | \"insert\"; value: string }> {\n if (a.length === 0) {\n return b.map((value) => ({ type: \"insert\" as const, value }));\n }\n if (b.length === 0) {\n return a.map((value) => ({ type: \"delete\" as const, value }));\n }\n\n // Use dynamic programming for optimal diff\n const dp = this.computeEditScript(a, b);\n return this.reconstructDiff(a, b, dp);\n }\n\n /**\n * Compute edit script using dynamic programming\n */\n private static computeEditScript(a: string[], b: string[]): number[][] {\n const m = a.length;\n const n = b.length;\n const dp: number[][] = Array(m + 1)\n .fill(null)\n .map(() => Array(n + 1).fill(0));\n\n // Initialize base cases\n for (let i = 0; i <= m; i++) dp[i][0] = i;\n for (let j = 0; j <= n; j++) dp[0][j] = j;\n\n // Fill the DP table\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n if (a[i - 1] === b[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1]; // No operation needed\n } else {\n dp[i][j] =\n 1 +\n Math.min(\n dp[i - 1][j], // Delete from a\n dp[i][j - 1], // Insert into a\n dp[i - 1][j - 1], // Replace\n );\n }\n }\n }\n\n return dp;\n }\n\n /**\n * Reconstruct the actual diff from the DP table\n */\n private static reconstructDiff(\n a: string[],\n b: string[],\n dp: number[][],\n ): Array<{ type: \"equal\" | \"delete\" | \"insert\"; value: string; sharedDiffId?: boolean }> {\n const result: Array<{ type: \"equal\" | \"delete\" | \"insert\"; value: string; sharedDiffId?: boolean }> = [];\n let i = a.length;\n let j = b.length;\n\n while (i > 0 || j > 0) {\n if (i > 0 && j > 0 && a[i - 1] === b[j - 1]) {\n // Equal\n result.unshift({ type: \"equal\", value: a[i - 1] });\n i--;\n j--;\n } else {\n // Determine the best operation with preference for insert/delete over replace\n const deleteCost = i > 0 ? dp[i - 1][j] : Infinity;\n const insertCost = j > 0 ? dp[i][j - 1] : Infinity;\n const replaceCost = i > 0 && j > 0 ? dp[i - 1][j - 1] : Infinity;\n\n const minCost = Math.min(deleteCost, insertCost, replaceCost);\n\n // Prefer insertions and deletions over replacements when costs are equal\n if (minCost === insertCost && j > 0) {\n // Insert\n result.unshift({ type: \"insert\", value: b[j - 1] });\n j--;\n } else if (minCost === deleteCost && i > 0) {\n // Delete\n result.unshift({ type: \"delete\", value: a[i - 1] });\n i--;\n } else if (minCost === replaceCost && i > 0 && j > 0) {\n // Only use replace for similar words, otherwise prefer separate operations\n if (this.areWordsSimilar(a[i - 1], b[j - 1])) {\n // Similar words - treat as replacement with shared diffId\n result.unshift({ type: \"insert\", value: b[j - 1], sharedDiffId: true });\n result.unshift({ type: \"delete\", value: a[i - 1], sharedDiffId: true });\n i--;\n j--;\n } else {\n // Different words - prefer insert over delete when costs are equal\n result.unshift({ type: \"insert\", value: b[j - 1] });\n j--;\n }\n }\n }\n }\n\n return result;\n }\n\n /**\n * Consolidate adjacent changes to reduce fragmentation\n * e.g., if we have: delete \"Key\", insert \"Key!\", delete \"Challenges\"\n * We can consolidate this into: replace [\"Key\", \"Challenges\"] with [\"Key!\"]\n */\n private static consolidateAdjacentChanges(\n diffs: Array<{ type: \"equal\" | \"delete\" | \"insert\"; value: string }>,\n ): Array<{ type: \"equal\" | \"delete\" | \"insert\"; value: string; consolidated?: boolean }> {\n const result = [];\n let i = 0;\n\n while (i < diffs.length) {\n const current = diffs[i];\n\n if (current.type === \"equal\") {\n result.push(current);\n i++;\n continue;\n }\n\n // Look for patterns of adjacent changes that can be consolidated\n const changeSequence = [];\n let j = i;\n\n // Collect adjacent non-equal changes\n while (j < diffs.length && diffs[j].type !== \"equal\") {\n changeSequence.push(diffs[j]);\n j++;\n }\n\n if (changeSequence.length <= 1) {\n // Single change, no consolidation needed\n result.push(current);\n i++;\n continue;\n }\n\n // Check if we should consolidate this sequence\n const shouldConsolidate = this.shouldConsolidateChanges(\n changeSequence as Array<{ type: \"delete\" | \"insert\"; value: string }>,\n );\n\n if (shouldConsolidate) {\n // Create consolidated changes\n const deletedWords = changeSequence.filter((c) => c.type === \"delete\").map((c) => c.value);\n const insertedWords = changeSequence.filter((c) => c.type === \"insert\").map((c) => c.value);\n\n if (deletedWords.length > 0) {\n result.push({\n type: \"delete\" as const,\n value: deletedWords.join(\" \"),\n consolidated: true,\n });\n }\n\n if (insertedWords.length > 0) {\n result.push({\n type: \"insert\" as const,\n value: insertedWords.join(\" \"),\n consolidated: true,\n });\n }\n } else {\n // Don't consolidate, add individual changes\n changeSequence.forEach((change) => result.push(change));\n }\n\n i = j;\n }\n\n return result;\n }\n\n /**\n * Determine if a sequence of changes should be consolidated\n */\n private static shouldConsolidateChanges(changes: Array<{ type: \"delete\" | \"insert\"; value: string }>): boolean {\n // Consolidate if we have both deletions and insertions in the same sequence\n const hasDeletes = changes.some((c) => c.type === \"delete\");\n const hasInserts = changes.some((c) => c.type === \"insert\");\n\n // Only consolidate if we have both types of changes (replacements/modifications)\n // Pure additions or pure deletions are fine as separate changes\n return hasDeletes && hasInserts;\n }\n\n /**\n * Check if two words are similar enough to be considered a replacement\n */\n private static areWordsSimilar(word1: string, word2: string): boolean {\n // If words are exactly the same, they're similar\n if (word1 === word2) return true;\n\n // Consider words similar if they share more than 70% of their characters\n const maxLen = Math.max(word1.length, word2.length);\n const minLen = Math.min(word1.length, word2.length);\n\n // If one word is much longer than the other, they're not similar\n if (maxLen > minLen * 2) {\n return false;\n }\n\n // Special case: if one word is just the other plus punctuation, treat as separate changes\n // e.g., \"Key\" and \"Key!\" should not be considered similar for replacement\n const word1Clean = word1.replace(/[^\\w]/g, \"\");\n const word2Clean = word2.replace(/[^\\w]/g, \"\");\n\n if (word1Clean === word2Clean && word1 !== word2) {\n // One word is just the other with added punctuation - don't treat as replacement\n return false;\n }\n\n // Calculate character overlap using Levenshtein distance\n const distance = this.levenshteinDistance(word1, word2);\n const similarity = 1 - distance / maxLen;\n\n return similarity > 0.7; // Increased threshold to be more conservative\n }\n}\n","import { PartialBlock } from \"@blocknote/core\";\nimport { v4 } from \"uuid\";\nimport { DiffBlock, WordDiff } from \"./blocknote-diff.util\";\n\nexport class BlockNoteWordDiffRendererUtil {\n static renderWordDiffs(\n diffBlocks: DiffBlock[],\n onAcceptChange?: (diffId: string) => void,\n onRejectChange?: (diffId: string) => void,\n acceptedChanges?: Set<string>,\n rejectedChanges?: Set<string>,\n ): PartialBlock[] {\n if (acceptedChanges || rejectedChanges) {\n diffBlocks = this.updateWordDiffStates(diffBlocks, acceptedChanges, rejectedChanges);\n }\n return diffBlocks.map((block) => this.renderDiffBlock(block, onAcceptChange, onRejectChange));\n }\n\n private static updateWordDiffStates(\n diffBlocks: DiffBlock[],\n acceptedChanges?: Set<string>,\n rejectedChanges?: Set<string>,\n ): DiffBlock[] {\n return diffBlocks.map((block) => {\n const updatedBlock = { ...block };\n\n if (updatedBlock.diffId) {\n updatedBlock.accepted = acceptedChanges?.has(updatedBlock.diffId) || false;\n updatedBlock.rejected = rejectedChanges?.has(updatedBlock.diffId) || false;\n }\n\n if (updatedBlock.wordDiffs) {\n updatedBlock.wordDiffs = updatedBlock.wordDiffs.map((wordDiff) => ({\n ...wordDiff,\n accepted: acceptedChanges?.has(wordDiff.diffId) || false,\n rejected: rejectedChanges?.has(wordDiff.diffId) || false,\n }));\n }\n\n if (updatedBlock.children) {\n updatedBlock.children = this.updateWordDiffStates(\n updatedBlock.children as DiffBlock[],\n acceptedChanges,\n rejectedChanges,\n );\n }\n\n return updatedBlock;\n });\n }\n\n private static renderDiffBlock(\n block: DiffBlock,\n onAcceptChange?: (diffId: string) => void,\n onRejectChange?: (diffId: string) => void,\n ): PartialBlock {\n if (block.diffType === \"modified\" && block.wordDiffs) {\n return this.renderWordLevelDiff(block, onAcceptChange, onRejectChange);\n }\n\n if (block.diffType === \"added\" || block.diffType === \"removed\") {\n return this.renderBlockLevelDiff(block, onAcceptChange, onRejectChange);\n }\n\n const baseBlock: PartialBlock = {\n id: block.id || v4(),\n type: (block.type as any) || \"paragraph\",\n props: this.getBlockProps(block),\n content: Array.isArray(block.content) ? block.content : [],\n children: block.children?.map((child) =>\n this.renderDiffBlock(child as DiffBlock, onAcceptChange, onRejectChange),\n ),\n };\n\n return baseBlock;\n }\n\n private static renderBlockLevelDiff(\n block: DiffBlock,\n onAcceptChange?: (diffId: string) => void,\n onRejectChange?: (diffId: string) => void,\n ): PartialBlock {\n if (!block.diffId) {\n return {\n id: block.id || v4(),\n type: (block.type as any) || \"paragraph\",\n props: block.props || {},\n content: Array.isArray(block.content) ? block.content : [],\n children: block.children?.map((child) =>\n this.renderDiffBlock(child as DiffBlock, onAcceptChange, onRejectChange),\n ),\n };\n }\n\n const blockAccepted = block.accepted || false;\n const blockRejected = block.rejected || false;\n\n let content = Array.isArray(block.content) ? [...block.content] : [];\n\n if (block.diffType === \"added\") {\n if (blockRejected) {\n return {\n id: block.id || v4(),\n type: \"paragraph\",\n props: {},\n content: [],\n children: [],\n };\n } else if (!blockAccepted) {\n content = content.map((item: any) => ({\n ...item,\n styles: { ...item.styles, bold: true },\n }));\n\n content.push({\n type: \"diffActions\",\n props: { diffIds: block.diffId },\n });\n }\n } else if (block.diffType === \"removed\") {\n if (blockAccepted) {\n return {\n id: block.id || v4(),\n type: \"paragraph\",\n props: {},\n content: [],\n children: [],\n };\n } else if (!blockRejected) {\n content = content.map((item: any) => ({\n ...item,\n styles: { ...item.styles, strike: true },\n }));\n\n content.push({\n type: \"diffActions\",\n props: { diffIds: block.diffId },\n });\n }\n }\n\n const baseBlock: PartialBlock = {\n id: block.id || v4(),\n type: (block.type as any) || \"paragraph\",\n props: this.getBlockProps(block),\n content: content,\n children: block.children?.map((child) =>\n this.renderDiffBlock(child as DiffBlock, onAcceptChange, onRejectChange),\n ),\n };\n\n return baseBlock;\n }\n\n private static renderWordLevelDiff(\n block: DiffBlock,\n onAcceptChange?: (diffId: string) => void,\n onRejectChange?: (diffId: string) => void,\n ): PartialBlock {\n if (!block.wordDiffs) {\n return {\n id: block.id || v4(),\n type: (block.type as any) || \"paragraph\",\n props: block.props || {},\n content: Array.isArray(block.content) ? block.content : [],\n children: [],\n };\n }\n\n const content = this.groupAndRenderWordDiffs(block.wordDiffs);\n\n return {\n id: block.id || v4(),\n type: (block.type as any) || \"paragraph\",\n props: block.props || {},\n content: Array.isArray(content) ? content : [],\n children:\n block.children?.map((child) => this.renderDiffBlock(child as DiffBlock, onAcceptChange, onRejectChange)) || [],\n };\n }\n\n private static groupAndRenderWordDiffs(wordDiffs: WordDiff[]): any[] {\n const content: any[] = [];\n\n for (let i = 0; i < wordDiffs.length; i++) {\n const wordDiff = wordDiffs[i];\n\n const isLastOfGroup = this.isLastDiffInGroup(wordDiffs, i);\n\n const textContent = this.createTextContent(wordDiff, isLastOfGroup);\n if (textContent) {\n if (Array.isArray(textContent)) {\n content.push(...textContent);\n } else {\n content.push(textContent);\n }\n }\n }\n\n const cleanedContent = this.cleanupSpaces(content);\n\n return cleanedContent;\n }\n\n private static cleanupSpaces(content: any[]): any[] {\n const filtered = content.filter((item) => item !== null && item !== undefined);\n\n const cleaned: any[] = [];\n\n for (let i = 0; i < filtered.length; i++) {\n const current = filtered[i];\n\n if (current.type === \"text\" && current.text === \" \") {\n const lastItem = cleaned[cleaned.length - 1];\n\n if (\n i === 0 ||\n i === filtered.length - 1 ||\n (lastItem && lastItem.type === \"text\" && lastItem.text === \" \") ||\n (lastItem && lastItem.type === \"diffActions\")\n ) {\n continue;\n }\n }\n\n if (current.type === \"diffActions\") {\n const nextItem = i + 1 < filtered.length ? filtered[i + 1] : null;\n\n cleaned.push(current);\n\n if (nextItem && nextItem.type === \"text\" && nextItem.text !== \" \" && nextItem.type !== \"diffActions\") {\n cleaned.push({\n type: \"text\",\n text: \" \",\n styles: {},\n });\n }\n\n continue;\n }\n\n cleaned.push(current);\n }\n\n return cleaned;\n }\n\n private static isLastDiffInGroup(wordDiffs: WordDiff[], currentIndex: number): boolean {\n const currentDiff = wordDiffs[currentIndex];\n\n if (currentDiff.type === \"unchanged\") {\n return false;\n }\n\n for (let i = currentIndex + 1; i < wordDiffs.length; i++) {\n const nextDiff = wordDiffs[i];\n\n if (nextDiff.diffId === currentDiff.diffId && (nextDiff.type === \"added\" || nextDiff.type === \"removed\")) {\n return false;\n }\n\n if (nextDiff.type !== \"unchanged\" && nextDiff.diffId !== currentDiff.diffId) {\n break;\n }\n }\n\n return true;\n }\n\n private static createTextContent(wordDiff: WordDiff, isLastOfGroup?: boolean): any[] | any {\n switch (wordDiff.type) {\n case \"added\":\n if (wordDiff.accepted) {\n return {\n type: \"text\",\n text: wordDiff.text,\n styles: {},\n };\n } else if (wordDiff.rejected) {\n return null;\n } else {\n if (wordDiff.text.trim() === \"\") {\n return {\n type: \"text\",\n text: wordDiff.text,\n styles: { backgroundColor: \"#dcfce7\" },\n };\n } else {\n const baseContent = {\n type: \"text\",\n text: wordDiff.text,\n styles: { bold: true },\n };\n\n if (isLastOfGroup) {\n return [\n baseContent,\n {\n type: \"diffActions\",\n props: { diffIds: wordDiff.diffId },\n },\n ];\n } else {\n return baseContent;\n }\n }\n }\n\n case \"removed\":\n if (wordDiff.accepted) {\n return null;\n } else if (wordDiff.rejected) {\n return {\n type: \"text\",\n text: wordDiff.text,\n styles: {},\n };\n } else {\n if (wordDiff.text.trim() === \"\") {\n return {\n type: \"text\",\n text: wordDiff.text,\n styles: { strike: true },\n };\n } else {\n const baseContent = {\n type: \"text\",\n text: wordDiff.text,\n styles: { strike: true },\n };\n\n if (isLastOfGroup) {\n return [\n baseContent,\n {\n type: \"diffActions\",\n props: { diffIds: wordDiff.diffId },\n },\n ];\n } else {\n return baseContent;\n }\n }\n }\n\n case \"unchanged\":\n default:\n return {\n type: \"text\",\n text: wordDiff.text,\n styles: {},\n };\n }\n }\n\n private static getBlockProps(block: DiffBlock): any {\n const baseProps = block.props || {};\n\n return baseProps;\n }\n\n static generateChangeSummary(diffBlocks: DiffBlock[]): {\n totalWords: number;\n addedWords: number;\n removedWords: number;\n acceptedChanges: number;\n rejectedChanges: number;\n pendingChanges: number;\n } {\n let totalWords = 0;\n let addedWords = 0;\n let removedWords = 0;\n let acceptedChanges = 0;\n let rejectedChanges = 0;\n let pendingChanges = 0;\n\n const processBlock = (block: DiffBlock) => {\n if (block.wordDiffs) {\n block.wordDiffs.forEach((wordDiff) => {\n totalWords++;\n\n if (wordDiff.type === \"added\") {\n addedWords++;\n } else if (wordDiff.type === \"removed\") {\n removedWords++;\n }\n\n if (wordDiff.accepted) {\n acceptedChanges++;\n } else if (wordDiff.rejected) {\n rejectedChanges++;\n } else if (wordDiff.type !== \"unchanged\") {\n pendingChanges++;\n }\n });\n }\n\n if (block.children) {\n block.children.forEach((child) => processBlock(child as DiffBlock));\n }\n };\n\n diffBlocks.forEach(processBlock);\n\n return {\n totalWords,\n addedWords,\n removedWords,\n acceptedChanges,\n rejectedChanges,\n pendingChanges,\n };\n }\n}\n","import { LucideIcon } from \"lucide-react\";\nimport { ReactNode } from \"react\";\nimport { ApiDataInterface, Modules } from \"../core\";\nimport { ContentInterface } from \"../features/content/data/content.interface\";\nimport { ModuleWithPermissions } from \"../permissions\";\nimport { cn } from \"./cn\";\n\nexport const getIconByModule = (params: { module: ModuleWithPermissions; className?: string }): ReactNode => {\n const IconComponent = getLucideIconByModule({ module: params.module });\n if (!IconComponent) return null;\n return <IconComponent className={cn(``, params.className)} />;\n};\n\nexport const getIcon = (params: { element: ApiDataInterface; className?: string }): ReactNode => {\n const IconComponent = getLucideIcon({ element: params.element });\n if (!IconComponent) return null;\n return <IconComponent className={cn(``, params.className)} />;\n};\n\nexport const getIconByModuleName = (params: { name: string; className?: string }): ReactNode => {\n return getIconByModule({ module: Modules.findByModelName(params.name), className: params.className });\n};\n\nexport const getLucideIcon = (params: { element: ApiDataInterface }): LucideIcon | null => {\n if (params.element.type === \"contents\") {\n const contentType = (params.element as ContentInterface).contentType;\n if (!contentType) return null;\n return getLucideIconByModule({ module: Modules.findByModelName(contentType) });\n }\n\n return getLucideIconByModule({ module: Modules.findByName(params.element.type) });\n};\n\nexport const getLucideIconByModule = (params: { module: ModuleWithPermissions }): LucideIcon | null => {\n return params.module.icon ?? null;\n};\n\nexport const getLucideIconByModuleName = (params: { name: string }): LucideIcon | null => {\n return getLucideIconByModule({ module: Modules.findByModelName(params.name) });\n};\n","import { toast as sonnerToast, type ExternalToast } from \"sonner\";\nimport type { ReactElement, JSXElementConstructor, ReactNode } from \"react\";\n\nexport type ToastOptions = {\n description?: ReactNode;\n duration?: number;\n id?: string;\n onDismiss?: () => void;\n action?: ExternalToast[\"action\"];\n dismissible?: boolean;\n};\n\n/** Standard toast - neutral/white */\nexport function showToast(message: string, options?: ToastOptions) {\n return sonnerToast(message, options as ExternalToast);\n}\n\n/** Error toast - destructive red */\nexport function showError(message: string, options?: ToastOptions) {\n return sonnerToast.error(message, options as ExternalToast);\n}\n\n/** Dismiss toast(s) */\nexport function dismissToast(toastId?: string | number) {\n return sonnerToast.dismiss(toastId);\n}\n\n/** Custom toast - ONLY for complex UI like upload progress */\nexport function showCustomToast(\n render: (id: string | number) => ReactElement<unknown, string | JSXElementConstructor<unknown>>,\n options?: {\n id?: string | number;\n duration?: number;\n dismissible?: boolean;\n onDismiss?: () => void;\n },\n) {\n return sonnerToast.custom(render, options);\n}\n","import { LucideIcon } from \"lucide-react\";\nimport { ApiRequestDataTypeInterface } from \"../core/interfaces/ApiRequestDataTypeInterface\";\nimport { FieldSelector } from \"../core/fields/FieldSelector\";\n\n/**\n * Permission actions\n */\nexport enum Action {\n Read = \"read\",\n Create = \"create\",\n Update = \"update\",\n Delete = \"delete\",\n}\n\n/**\n * Generic permission check type.\n * Can be a boolean or a function that checks permissions dynamically.\n * @template T - The data type being checked\n * @template U - The user type (defaults to PermissionUser)\n */\nexport type PermissionCheck<T, U = PermissionUser> = boolean | ((user?: U | string, data?: T) => boolean);\n\n/**\n * Page URL configuration for modules\n */\nexport type PageUrl = {\n pageUrl?: string;\n};\n\n/**\n * Module permission definition wrapper\n */\nexport type ModulePermissionDefinition<T> = {\n interface: T;\n};\n\n/**\n * Base module definition\n */\nexport type ModuleDefinition = {\n pageUrl?: string;\n name: string;\n model: any;\n feature?: string;\n moduleId?: string;\n};\n\n/**\n * Permission configuration for a module.\n * Can be a boolean (allow/deny all) or a string path for dynamic checks.\n */\nexport interface PermissionConfig {\n create: boolean | string;\n read: boolean | string;\n update: boolean | string;\n delete: boolean | string;\n}\n\n/**\n * Generic interface for a module that has permissions.\n * Apps should ensure their Module class implements this.\n */\nexport interface PermissionModule {\n id: string;\n permissions: PermissionConfig;\n}\n\n/**\n * Generic interface for a user that has modules with permissions.\n * Apps should ensure their User class implements this.\n */\nexport interface PermissionUser {\n id: string;\n modules: PermissionModule[];\n}\n\n/**\n * Module definition with permissions - extends ApiRequestDataTypeInterface\n */\nexport type ModuleWithPermissions = ApiRequestDataTypeInterface & {\n pageUrl?: string;\n feature?: string;\n moduleId?: string;\n icon?: LucideIcon;\n inclusions?: Record<\n string,\n {\n types?: string[];\n fields?: FieldSelector<any>[];\n include?: string[];\n }\n >;\n};\n\n/**\n * Factory type for creating module definitions\n */\nexport type ModuleFactory = (params: {\n pageUrl?: string;\n name: string;\n cache?: string | \"days\" | \"default\" | \"hours\" | \"max\" | \"minutes\" | \"seconds\" | \"weeks\";\n model: any;\n feature?: string;\n moduleId?: string;\n icon?: LucideIcon;\n identifier?: string[];\n inclusions?: Record<\n string,\n {\n types?: string[];\n fields?: FieldSelector<any>[];\n include?: string[];\n }\n >;\n}) => ModuleWithPermissions;\n","import { Action, ModuleWithPermissions, PermissionModule, PermissionUser } from \"./types\";\n\n/**\n * Check if a user has permission to perform an action on a module.\n *\n * @param module - The module to check permissions for\n * @param action - The action to check (read, create, update, delete)\n * @param user - The user with their modules and permissions\n * @param data - Optional data object for path-based permission checks\n */\nexport function checkPermissions<T extends PermissionUser>(params: {\n module: ModuleWithPermissions;\n action: Action;\n user: T;\n data?: any;\n}): boolean {\n const selectedModule = params.user.modules.find((module: PermissionModule) => module.id === params.module.moduleId);\n\n if (!selectedModule) return false;\n const permissionConfig = selectedModule.permissions[params.action];\n\n if (!permissionConfig) return false;\n if (typeof permissionConfig === \"boolean\") return permissionConfig as boolean;\n\n if (!params.data) return true;\n\n try {\n const singlePermissionConfig = permissionConfig.split(\"|\").map((p) => p.trim());\n\n for (const path of singlePermissionConfig) {\n if (getValueFromPath(params.data, path, params.user.id)) return true;\n }\n return false;\n } catch {\n if (typeof permissionConfig === \"string\") {\n return getValueFromPath(params.data, permissionConfig, params.user.id);\n }\n }\n\n return false;\n}\n\n/**\n * Check permissions from server context where user object is not fully available.\n *\n * @param module - The module to check permissions for\n * @param action - The action to check\n * @param userId - The user's ID\n * @param selectedModule - The selected module with its permissions\n * @param data - Optional data object for path-based permission checks\n */\nexport function checkPermissionsFromServer(params: {\n module: ModuleWithPermissions;\n action: Action;\n userId: string;\n selectedModule?: PermissionModule;\n data?: any;\n}): boolean {\n if (!params.selectedModule) return false;\n const permissionConfig = params.selectedModule.permissions[params.action];\n\n if (!permissionConfig) return false;\n if (typeof permissionConfig === \"boolean\") return permissionConfig as boolean;\n\n if (!params.data) return true;\n\n try {\n const singlePermissionConfig = permissionConfig.split(\"|\").map((p) => p.trim());\n\n for (const path of singlePermissionConfig) {\n if (getValueFromPath(params.data, path, params.userId)) return true;\n }\n return false;\n } catch {\n if (typeof permissionConfig === \"string\") {\n return getValueFromPath(params.data, permissionConfig, params.userId);\n }\n }\n\n return false;\n}\n\n/**\n * Traverse an object path and check if the value matches the user ID.\n * Handles nested objects, arrays, and various data structures.\n */\nexport function getValueFromPath(obj: any, path: string, userId: string): any {\n const parts = path.split(\".\");\n let current = obj;\n\n for (const part of parts) {\n if (!current) return false;\n\n if (Array.isArray(current)) {\n let found = false;\n for (const item of current) {\n const result = getValueFromPath(item, parts.slice(parts.indexOf(part)).join(\".\"), userId);\n if (result === userId || result === true) {\n found = true;\n break;\n }\n }\n return found;\n } else if (current[part] !== undefined) {\n current = current[part];\n } else {\n return false;\n }\n }\n\n if (Array.isArray(current)) {\n // If final value is an array, check if any element has id matching userId\n return current.some((item: any) => {\n if (typeof item === \"object\" && item.id !== undefined) {\n return item.id.toString() === userId;\n }\n return item.toString() === userId;\n });\n }\n\n // Direct comparison for primitive values or objects with id\n if (typeof current === \"object\" && current.id !== undefined) {\n return current.id.toString() === userId;\n }\n\n return current.toString() === userId;\n}\n","import { v4 } from \"uuid\";\nimport { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { AccountDeletionInput, AccountDeletionInterface } from \"./account-deletion.interface\";\n\nexport class AccountDeletion extends AbstractApiData implements AccountDeletionInterface {\n private _requestedAt?: Date;\n private _scheduledAt?: Date;\n private _hasDeliverableEmail: boolean = false;\n private _hasActiveSubscription: boolean = false;\n private _deletesCompany: boolean = false;\n private _ownedCount: number = 0;\n private _otherTables?: string[];\n\n get requestedAt(): Date | undefined {\n return this._requestedAt;\n }\n\n get scheduledAt(): Date | undefined {\n return this._scheduledAt;\n }\n\n get hasDeliverableEmail(): boolean {\n return this._hasDeliverableEmail;\n }\n\n get hasActiveSubscription(): boolean {\n return this._hasActiveSubscription;\n }\n\n get deletesCompany(): boolean {\n return this._deletesCompany;\n }\n\n get ownedCount(): number {\n return this._ownedCount;\n }\n\n get otherTables(): string[] {\n return this._otherTables ?? [];\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes ?? {};\n\n this._requestedAt = attributes.requestedAt ? new Date(attributes.requestedAt) : undefined;\n this._scheduledAt = attributes.scheduledAt ? new Date(attributes.scheduledAt) : undefined;\n this._hasDeliverableEmail = attributes.hasDeliverableEmail === true;\n this._hasActiveSubscription = attributes.hasActiveSubscription === true;\n this._deletesCompany = attributes.deletesCompany === true;\n this._ownedCount = typeof attributes.ownedCount === \"number\" ? attributes.ownedCount : 0;\n this._otherTables = Array.isArray(attributes.otherTables) ? attributes.otherTables : [];\n\n return this;\n }\n\n createJsonApi(data: AccountDeletionInput) {\n const response: any = {\n data: {\n type: Modules.AccountDeletion.name,\n id: data.id,\n attributes: {\n confirmation: data.confirmation,\n },\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n return response;\n }\n\n /**\n * Body of the public \"Keep my account\" call. The caller is not signed in and\n * has no id to send: the API validates a client UUID and ignores its value.\n */\n createKeepJsonApi(params: { token: string }) {\n return {\n data: {\n type: Modules.AccountDeletion.name,\n id: v4(),\n attributes: {\n token: params.token,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { AccountDeletion } from \"./data/account-deletion\";\n\n/**\n * Opt-in: an app that turns on `accountDeletion` in its API adds\n * `AccountDeletion: AccountDeletionModule(moduleFactory)` to its bootstrapper.\n */\nexport const AccountDeletionModule = (factory: ModuleFactory) =>\n factory({\n name: \"account-deletions\",\n pageUrl: \"/account-deletions\",\n model: AccountDeletion,\n });\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { AccountDeletion } from \"./account-deletion\";\nimport { AccountDeletionInput, AccountDeletionInterface } from \"./account-deletion.interface\";\n\nexport class AccountDeletionService extends AbstractService {\n /**\n * GET the signed-in user's deletion status and what a deletion would remove.\n */\n static async findMine(): Promise<AccountDeletionInterface> {\n return this.callApi<AccountDeletionInterface>({\n type: Modules.AccountDeletion,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.AccountDeletion, id: \"me\" }).generate(),\n });\n }\n\n /**\n * GET another user's deletion status (platform administrators only).\n */\n static async findByUser(params: { userId: string }): Promise<AccountDeletionInterface> {\n return this.callApi<AccountDeletionInterface>({\n type: Modules.AccountDeletion,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.AccountDeletion, id: params.userId }).generate(),\n });\n }\n\n /**\n * POST the signed-in user's own deletion request. `params.id` must be the\n * signed-in user's id.\n */\n static async request(params: AccountDeletionInput): Promise<AccountDeletionInterface> {\n return this.callApi<AccountDeletionInterface>({\n type: Modules.AccountDeletion,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.AccountDeletion }).generate(),\n input: params,\n });\n }\n\n /**\n * POST a deletion request on behalf of another user (platform administrators\n * only). Runs the same 7-day request as the user's own.\n */\n static async requestForUser(params: AccountDeletionInput): Promise<AccountDeletionInterface> {\n return this.callApi<AccountDeletionInterface>({\n type: Modules.AccountDeletion,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.AccountDeletion, id: params.id }).generate(),\n input: params,\n });\n }\n\n /**\n * POST the token from the \"Keep my account\" email link. Public: no session.\n * Throws on any failure, so the page can show the \"link no longer works\" text.\n */\n static async keep(params: { token: string }): Promise<void> {\n const accountDeletion = new AccountDeletion();\n await this.callApi<void>({\n type: Modules.AccountDeletion,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.AccountDeletion, id: \"keep\" }).generate(),\n input: accountDeletion.createKeepJsonApi({ token: params.token }),\n overridesJsonApiCreation: true, // OK - model method provides proper structure\n suppressGlobalError: true,\n });\n }\n}\n","/**\n * One-shot flag between the login that cancelled a pending account deletion and\n * the first page after it, which shows AccountDeletionCancelledNotice. Kept in\n * sessionStorage so it survives the redirect (and the full reload of the\n * Discord / Google path) but not the tab. Storage can be missing or throw\n * (private mode, blocked site data): the notice is then simply not shown.\n */\nexport const ACCOUNT_DELETION_CANCELLED_KEY = \"nja:accountDeletionCancelled\";\n\nexport function markAccountDeletionCancelled(): void {\n try {\n sessionStorage.setItem(ACCOUNT_DELETION_CANCELLED_KEY, \"1\");\n } catch {\n // Storage unavailable: no notice.\n }\n}\n\nexport function consumeAccountDeletionCancelled(): boolean {\n try {\n const value = sessionStorage.getItem(ACCOUNT_DELETION_CANCELLED_KEY);\n if (value === null) return false;\n sessionStorage.removeItem(ACCOUNT_DELETION_CANCELLED_KEY);\n return true;\n } catch {\n return false;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { TotpAuthenticatorInterface } from \"./totp-authenticator.interface\";\n\nexport class TotpAuthenticator extends AbstractApiData implements TotpAuthenticatorInterface {\n private _name: string = \"\";\n private _verified: boolean = false;\n private _lastUsedAt?: Date;\n\n get name(): string {\n return this._name;\n }\n\n get verified(): boolean {\n return this._verified;\n }\n\n get lastUsedAt(): Date | undefined {\n return this._lastUsedAt;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n this._name = data.jsonApi.attributes.name ?? \"\";\n this._verified = data.jsonApi.attributes.verified ?? false;\n this._lastUsedAt = data.jsonApi.attributes.lastUsedAt ? new Date(data.jsonApi.attributes.lastUsedAt) : undefined;\n return this;\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { TotpAuthenticator } from \"./data/totp-authenticator\";\n\nexport const TotpAuthenticatorModule = (factory: ModuleFactory) =>\n factory({\n name: \"totp-authenticators\",\n pageUrl: \"/totp-authenticators\",\n model: TotpAuthenticator,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { TotpSetupInterface } from \"./totp-setup.interface\";\n\nexport type TotpSetupInput = {\n id: string;\n name: string;\n accountName: string;\n};\n\nexport class TotpSetup extends AbstractApiData implements TotpSetupInterface {\n private _qrCodeUri: string = \"\";\n private _secret: string = \"\";\n private _authenticatorId: string = \"\";\n\n get qrCodeUri(): string {\n return this._qrCodeUri;\n }\n\n get secret(): string {\n return this._secret;\n }\n\n get authenticatorId(): string {\n return this._authenticatorId;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n this._qrCodeUri = data.jsonApi.attributes.qrCodeUri ?? \"\";\n this._secret = data.jsonApi.attributes.secret ?? \"\";\n // authenticatorId is the entity ID, not an attribute\n this._authenticatorId = data.jsonApi.id ?? \"\";\n return this;\n }\n\n createJsonApi(data: TotpSetupInput) {\n return {\n data: {\n type: \"totp-authenticators\",\n id: data.id,\n attributes: {\n name: data.name,\n accountName: data.accountName,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { TotpSetup } from \"./data/totp-setup\";\n\nexport const TotpSetupModule = (factory: ModuleFactory) =>\n factory({\n name: \"totp-setup\",\n pageUrl: \"/totp-setup\",\n model: TotpSetup,\n });\n","import { AbstractApiData } from \"../../../core\";\n\nexport type TotpVerifyInput = {\n id: string;\n authenticatorId: string;\n code: string;\n};\n\nexport class TotpVerify extends AbstractApiData {\n createJsonApi(data: TotpVerifyInput) {\n return {\n data: {\n type: \"totp-authenticators\",\n id: data.id,\n attributes: {\n authenticatorId: data.authenticatorId,\n code: data.code,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { TotpVerify } from \"./data/totp-verify\";\n\nexport const TotpVerifyModule = (factory: ModuleFactory) =>\n factory({\n name: \"totp-verify\",\n pageUrl: \"/totp-verify\",\n model: TotpVerify,\n });\n","import { AbstractApiData } from \"../../../core\";\n\nexport type TotpVerifyLoginInput = {\n id: string;\n code: string;\n};\n\nexport class TotpVerifyLogin extends AbstractApiData {\n createJsonApi(data: TotpVerifyLoginInput) {\n return {\n data: {\n type: \"totp-authenticators\",\n id: data.id,\n attributes: {\n code: data.code,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { TotpVerifyLogin } from \"./data/totp-verify-login\";\n\nexport const TotpVerifyLoginModule = (factory: ModuleFactory) =>\n factory({\n name: \"totp-verify-login\",\n pageUrl: \"/totp-verify-login\",\n model: TotpVerifyLogin,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { PasskeyInterface } from \"./passkey.interface\";\n\nexport class Passkey extends AbstractApiData implements PasskeyInterface {\n private _name: string = \"\";\n private _credentialId: string = \"\";\n private _deviceType: string = \"\";\n private _backedUp: boolean = false;\n private _lastUsedAt?: Date;\n\n get name(): string {\n return this._name;\n }\n\n get credentialId(): string {\n return this._credentialId;\n }\n\n get deviceType(): string {\n return this._deviceType;\n }\n\n get backedUp(): boolean {\n return this._backedUp;\n }\n\n get lastUsedAt(): Date | undefined {\n return this._lastUsedAt;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n this._name = data.jsonApi.attributes.name ?? \"\";\n this._credentialId = data.jsonApi.attributes.credentialId ?? \"\";\n this._deviceType = data.jsonApi.attributes.deviceType ?? \"\";\n this._backedUp = data.jsonApi.attributes.backedUp ?? false;\n this._lastUsedAt = data.jsonApi.attributes.lastUsedAt ? new Date(data.jsonApi.attributes.lastUsedAt) : undefined;\n return this;\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { Passkey } from \"./data/passkey\";\n\nexport const PasskeyModule = (factory: ModuleFactory) =>\n factory({\n name: \"passkeys\",\n pageUrl: \"/passkeys\",\n model: Passkey,\n });\n","import { PublicKeyCredentialCreationOptionsJSON } from \"@simplewebauthn/browser\";\nimport { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\n\nexport type PasskeyRegistrationOptionsInput = {\n id: string;\n userName: string;\n userDisplayName?: string;\n};\n\nexport interface PasskeyRegistrationOptionsInterface {\n pendingId: string;\n options: PublicKeyCredentialCreationOptionsJSON;\n}\n\nexport class PasskeyRegistrationOptions extends AbstractApiData implements PasskeyRegistrationOptionsInterface {\n private _pendingId: string = \"\";\n private _options: PublicKeyCredentialCreationOptionsJSON = {} as PublicKeyCredentialCreationOptionsJSON;\n\n get pendingId(): string {\n return this._pendingId;\n }\n\n get options(): PublicKeyCredentialCreationOptionsJSON {\n return this._options;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n this._pendingId = data.jsonApi.attributes.pendingId ?? this.id;\n this._options = data.jsonApi.attributes.options ?? {};\n return this;\n }\n\n createJsonApi(data: PasskeyRegistrationOptionsInput) {\n return {\n data: {\n type: \"passkeys\",\n id: data.id,\n attributes: {\n userName: data.userName,\n userDisplayName: data.userDisplayName,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { PasskeyRegistrationOptions } from \"./data/passkey-registration-options\";\n\nexport const PasskeyRegistrationOptionsModule = (factory: ModuleFactory) =>\n factory({\n name: \"passkey-registration-options\",\n pageUrl: \"/passkeys\",\n model: PasskeyRegistrationOptions,\n });\n","import { RegistrationResponseJSON } from \"@simplewebauthn/browser\";\nimport { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { PasskeyInterface } from \"./passkey.interface\";\n\nexport type PasskeyRegistrationVerifyInput = {\n id: string;\n pendingId: string;\n name: string;\n response: RegistrationResponseJSON;\n};\n\nexport class PasskeyRegistrationVerify extends AbstractApiData implements PasskeyInterface {\n private _name: string = \"\";\n private _credentialId: string = \"\";\n private _deviceType: string = \"\";\n private _backedUp: boolean = false;\n private _lastUsedAt?: Date;\n\n get name(): string {\n return this._name;\n }\n\n get credentialId(): string {\n return this._credentialId;\n }\n\n get deviceType(): string {\n return this._deviceType;\n }\n\n get backedUp(): boolean {\n return this._backedUp;\n }\n\n get lastUsedAt(): Date | undefined {\n return this._lastUsedAt;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n this._name = data.jsonApi.attributes.name ?? \"\";\n this._credentialId = data.jsonApi.attributes.credentialId ?? \"\";\n this._deviceType = data.jsonApi.attributes.deviceType ?? \"\";\n this._backedUp = data.jsonApi.attributes.backedUp ?? false;\n this._lastUsedAt = data.jsonApi.attributes.lastUsedAt ? new Date(data.jsonApi.attributes.lastUsedAt) : undefined;\n return this;\n }\n\n createJsonApi(data: PasskeyRegistrationVerifyInput) {\n return {\n data: {\n type: \"passkeys\",\n id: data.id,\n attributes: {\n pendingId: data.pendingId,\n name: data.name,\n response: data.response,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { PasskeyRegistrationVerify } from \"./data/passkey-registration-verify\";\n\nexport const PasskeyRegistrationVerifyModule = (factory: ModuleFactory) =>\n factory({\n name: \"passkey-registration-verify\",\n pageUrl: \"/passkeys\",\n model: PasskeyRegistrationVerify,\n });\n","import { AbstractApiData } from \"../../../core\";\n\nexport type PasskeyRenameInput = {\n id: string;\n name: string;\n};\n\nexport class PasskeyRename extends AbstractApiData {\n createJsonApi(data: PasskeyRenameInput) {\n return {\n data: {\n type: \"passkeys\",\n id: data.id,\n attributes: {\n name: data.name,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { PasskeyRename } from \"./data/passkey-rename\";\n\nexport const PasskeyRenameModule = (factory: ModuleFactory) =>\n factory({\n name: \"passkey-rename\",\n pageUrl: \"/passkeys-rename\",\n model: PasskeyRename,\n });\n","import { AuthenticationResponseJSON } from \"@simplewebauthn/browser\";\nimport { AbstractApiData } from \"../../../core\";\n\nexport type PasskeyVerifyLoginInput = {\n id: string;\n pendingId: string;\n response: AuthenticationResponseJSON;\n};\n\nexport class PasskeyVerifyLogin extends AbstractApiData {\n createJsonApi(data: PasskeyVerifyLoginInput) {\n return {\n data: {\n type: \"passkeys\",\n id: data.id,\n attributes: {\n pendingId: data.pendingId,\n response: data.response,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { PasskeyVerifyLogin } from \"./data/passkey-verify-login\";\n\nexport const PasskeyVerifyLoginModule = (factory: ModuleFactory) =>\n factory({\n name: \"passkey-verify-login\",\n pageUrl: \"/passkeys\",\n model: PasskeyVerifyLogin,\n });\n","import { PublicKeyCredentialRequestOptionsJSON } from \"@simplewebauthn/browser\";\nimport { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { PasskeyAuthenticationOptionsInterface } from \"./passkey-authentication-options.interface\";\n\nexport type PasskeyAuthenticationOptionsInput = {\n id: string;\n};\n\nexport class PasskeyAuthenticationOptions extends AbstractApiData implements PasskeyAuthenticationOptionsInterface {\n private _pendingId: string = \"\";\n private _options: PublicKeyCredentialRequestOptionsJSON = {} as PublicKeyCredentialRequestOptionsJSON;\n\n get pendingId(): string {\n return this._pendingId;\n }\n\n get options(): PublicKeyCredentialRequestOptionsJSON {\n return this._options;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n this._pendingId = data.jsonApi.attributes.pendingId ?? this.id ?? \"\";\n this._options = data.jsonApi.attributes.options ?? {};\n return this;\n }\n\n createJsonApi(data: PasskeyAuthenticationOptionsInput) {\n return {\n data: {\n type: \"passkey-authentication-options\",\n id: data.id,\n attributes: {},\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { PasskeyAuthenticationOptions } from \"./data/passkey-authentication-options\";\n\nexport const PasskeyAuthenticationOptionsModule = (factory: ModuleFactory) =>\n factory({\n name: \"passkey-authentication-options\",\n pageUrl: \"/passkeys\",\n model: PasskeyAuthenticationOptions,\n });\n","import { AbstractApiData } from \"../../../core\";\n\nexport type TwoFactorEnableInput = {\n id: string;\n preferredMethod: \"totp\" | \"passkey\";\n};\n\nexport class TwoFactorEnable extends AbstractApiData {\n createJsonApi(data: TwoFactorEnableInput) {\n return {\n data: {\n type: \"two-factor-configs\",\n id: data.id,\n attributes: {\n preferredMethod: data.preferredMethod,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { TwoFactorEnable } from \"./data/two-factor-enable\";\n\nexport const TwoFactorEnableModule = (factory: ModuleFactory) =>\n factory({\n name: \"two-factor-enable\",\n pageUrl: \"/two-factor-enable\",\n model: TwoFactorEnable,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { TwoFactorChallengeInterface } from \"./two-factor-challenge.interface\";\n\nexport type TwoFactorChallengeInput = {\n id: string;\n method: \"totp\" | \"passkey\" | \"backup\";\n};\n\nexport class TwoFactorChallenge extends AbstractApiData implements TwoFactorChallengeInterface {\n private _pendingToken: string = \"\";\n private _availableMethods: (\"totp\" | \"passkey\" | \"backup\")[] = [];\n private _expiresAt: Date = new Date();\n\n get pendingToken(): string {\n return this._pendingToken;\n }\n\n get availableMethods(): (\"totp\" | \"passkey\" | \"backup\")[] {\n return this._availableMethods;\n }\n\n get expiresAt(): Date {\n return this._expiresAt;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n this._pendingToken = data.jsonApi.attributes.pendingToken ?? \"\";\n this._availableMethods = data.jsonApi.attributes.availableMethods ?? [];\n this._expiresAt = data.jsonApi.attributes.expiresAt ? new Date(data.jsonApi.attributes.expiresAt) : new Date();\n return this;\n }\n\n createJsonApi(data: TwoFactorChallengeInput) {\n return {\n data: {\n type: \"two-factor-configs\",\n id: data.id,\n attributes: {\n method: data.method,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { TwoFactorChallenge } from \"./data/two-factor-challenge\";\n\nexport const TwoFactorChallengeModule = (factory: ModuleFactory) =>\n factory({\n name: \"two-factor-challenge\",\n pageUrl: \"/two-factor-challenge\",\n model: TwoFactorChallenge,\n });\n","import { AbstractApiData } from \"../../../core\";\n\nexport type BackupCodeVerifyInput = {\n id: string;\n code: string;\n};\n\nexport class BackupCodeVerify extends AbstractApiData {\n createJsonApi(data: BackupCodeVerifyInput) {\n return {\n data: {\n type: \"backup-codes\",\n id: data.id,\n attributes: {\n code: data.code,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { BackupCodeVerify } from \"./data/backup-code-verify\";\n\nexport const BackupCodeVerifyModule = (factory: ModuleFactory) =>\n factory({\n name: \"backup-codes\",\n pageUrl: \"/backup-codes\",\n model: BackupCodeVerify,\n });\n","/**\n * Token parameters for authentication cookie management\n */\nexport interface TokenParams {\n token?: string;\n refreshToken?: string;\n userId?: string;\n companyId?: string;\n roles?: string[];\n features?: string[];\n modules?: {\n id: string;\n permissions: {\n create: boolean | string;\n read: boolean | string;\n update: boolean | string;\n delete: boolean | string;\n };\n }[];\n}\n\n/**\n * Token handler interface for dependency injection\n * Apps must provide implementations of these functions via configureAuth()\n */\nexport interface TokenHandler {\n updateToken: (params: TokenParams) => Promise<void>;\n removeToken: () => Promise<void>;\n}\n\n// Private storage for the injected token handler\nlet _tokenHandler: TokenHandler | null = null;\n\n/**\n * Configure authentication token handling\n * Call this at app startup to provide Server Actions for cookie management\n *\n * @example\n * ```typescript\n * import { configureAuth } from \"@carlonicora/nextjs-jsonapi\";\n * import { updateToken, removeToken } from \"@/features/auth/utils/AuthCookies\";\n *\n * configureAuth({ updateToken, removeToken });\n * ```\n */\nexport function configureAuth(handler: TokenHandler): void {\n _tokenHandler = handler;\n}\n\n/**\n * Internal getter for AuthService to access the token handler\n * @internal\n */\nexport function getTokenHandler(): TokenHandler | null {\n return _tokenHandler;\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { UserInterface } from \"../../user/data/user.interface\";\nimport { AuthInput, AuthInterface } from \"./auth.interface\";\n\nexport class Auth extends AbstractApiData implements AuthInterface {\n private _token?: string;\n private _refreshToken?: string;\n private _user?: UserInterface;\n private _deletionCancelled: boolean = false;\n\n /** True on the session created by the login that cancelled a pending account deletion. */\n get deletionCancelled(): boolean {\n return this._deletionCancelled;\n }\n\n get token(): string {\n if (!this._token) throw new Error(\"Token is not defined\");\n return this._token;\n }\n\n get refreshToken(): string {\n if (!this._refreshToken) throw new Error(\"Refresh token is not defined\");\n return this._refreshToken;\n }\n\n get user(): UserInterface {\n if (!this._user) throw new Error(\"User is not defined\");\n return this._user;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._token = data.jsonApi.attributes.token ?? undefined;\n this._refreshToken = data.jsonApi.attributes.refreshToken ?? undefined;\n this._deletionCancelled = data.jsonApi.attributes.deletionCancelled === true;\n\n this._user = this._readIncluded(data, \"user\", Modules.User) as UserInterface;\n\n return this;\n }\n\n createJsonApi(data: AuthInput) {\n const response: any = {\n data: {\n type: Modules.Auth.name,\n attributes: {},\n },\n };\n\n if (data.id) response.data.id = data.id;\n if (data.email !== undefined) response.data.attributes.email = data.email;\n if (data.name !== undefined) response.data.attributes.name = data.name;\n if (data.companyName !== undefined) response.data.attributes.companyName = data.companyName;\n if (data.password !== undefined) response.data.attributes.password = data.password;\n if (data.termsAcceptedAt !== undefined) response.data.attributes.termsAcceptedAt = data.termsAcceptedAt;\n if (data.marketingConsent !== undefined) response.data.attributes.marketingConsent = data.marketingConsent;\n if (data.marketingConsentAt !== undefined) response.data.attributes.marketingConsentAt = data.marketingConsentAt;\n if (data.inviteCode !== undefined) response.data.attributes.inviteCode = data.inviteCode;\n if (data.referralCode !== undefined) response.data.attributes.referralCode = data.referralCode;\n\n if (data.source !== undefined) {\n const attributes = response.data.attributes;\n if (data.source.sourceRef !== undefined) attributes.sourceRef = data.source.sourceRef;\n if (data.source.sourceUtmSource !== undefined) attributes.sourceUtmSource = data.source.sourceUtmSource;\n if (data.source.sourceUtmMedium !== undefined) attributes.sourceUtmMedium = data.source.sourceUtmMedium;\n if (data.source.sourceUtmCampaign !== undefined) attributes.sourceUtmCampaign = data.source.sourceUtmCampaign;\n if (data.source.sourceReferrer !== undefined) attributes.sourceReferrer = data.source.sourceReferrer;\n if (data.source.sourceLandingPath !== undefined) attributes.sourceLandingPath = data.source.sourceLandingPath;\n }\n\n return response;\n }\n}\n","import { AuthInput, AuthInterface } from \".\";\nimport {\n AbstractService,\n ApiResponseInterface,\n EndpointCreator,\n HttpMethod,\n JsonApiHydratedDataInterface,\n Modules,\n rehydrate,\n} from \"../../../core\";\nimport { JsonApiDelete, JsonApiGet, JsonApiPost } from \"../../../unified\";\nimport { markAccountDeletionCancelled } from \"../../account-deletion/utils/account-deletion-notice\";\nimport { UserInterface } from \"../../user\";\nimport { getTokenHandler } from \"../config\";\nimport { PendingRegistrationInterface } from \"./pending-registration.interface\";\nimport { TwoFactorChallengeInterface } from \"./two-factor-challenge.interface\";\n\nexport class AuthService extends AbstractService {\n static async login(params: {\n email: string;\n password: string;\n language?: string;\n }): Promise<UserInterface | TwoFactorChallengeInterface> {\n const language = params.language || \"en-US\";\n\n const apiResponse: ApiResponseInterface = await JsonApiPost({\n classKey: Modules.Auth,\n endpoint: new EndpointCreator({ endpoint: Modules.Auth, id: \"login\" }).generate(),\n body: { email: params.email, password: params.password } as AuthInput,\n language: language,\n });\n\n if (!apiResponse.ok) throw new Error(apiResponse.error);\n\n // POLYMORPHIC RESPONSE: Login can return EITHER Auth OR TwoFactorChallenge\n // translateResponse used Modules.Auth for rehydration, but backend may return different type\n // Check raw response type and manually rehydrate with correct module if needed\n if (apiResponse.raw?.data?.type === \"two-factor-challenge\") {\n const challenge = rehydrate<TwoFactorChallengeInterface>(Modules.TwoFactorChallenge, {\n jsonApi: apiResponse.raw.data,\n included: apiResponse.raw.included ?? [],\n });\n return challenge;\n }\n\n // Normal auth response\n const auth = apiResponse.data as AuthInterface;\n\n // Login returns only the user to Login.tsx, so the flag is read here.\n if (auth.deletionCancelled) markAccountDeletionCancelled();\n\n // Use injected token handler if configured\n const handler = getTokenHandler();\n if (handler) {\n await handler.updateToken({\n token: auth.token,\n refreshToken: auth.refreshToken,\n userId: auth.user.id,\n companyId: auth.user.company?.id,\n roles: auth.user.roles.map((role) => role.id),\n features: auth.user.company?.features?.map((feature) => feature.id) ?? [],\n modules: auth.user.modules.map((module) => ({\n id: module.id,\n permissions: module.permissions,\n })),\n });\n }\n\n return auth.user;\n }\n\n static async logout(params?: { language?: string }): Promise<void> {\n const language = params?.language || \"en-US\";\n\n await JsonApiDelete({\n classKey: Modules.Auth,\n endpoint: new EndpointCreator({ endpoint: Modules.Auth }).generate(),\n language: language,\n });\n\n // Use injected token handler if configured\n const handler = getTokenHandler();\n if (handler) {\n await handler.removeToken();\n }\n }\n\n static async initialiseForgotPassword(params: { email: string; language?: string }): Promise<void> {\n const language = params.language || \"en-US\";\n\n const response: ApiResponseInterface = await JsonApiPost({\n classKey: Modules.Auth,\n endpoint: new EndpointCreator({ endpoint: Modules.Auth, id: \"forgot\" }).generate(),\n body: { email: params.email } as AuthInput,\n language: language,\n });\n\n if (!response.ok) {\n throw new Error(response.error);\n }\n }\n\n static async register(params: AuthInput): Promise<void> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Auth, id: \"register\" });\n\n await this.callApi({\n type: Modules.Auth,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: params,\n });\n }\n\n static async activate(params: { activationCode: string }): Promise<void> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"activate\",\n childEndpoint: params.activationCode,\n });\n\n await this.callApi({ type: Modules.Auth, method: HttpMethod.POST, endpoint: endpoint.generate() });\n }\n\n static async validateCode(params: { code: string; language?: string }): Promise<void> {\n const language = params.language || \"en-US\";\n\n const apiResponse: ApiResponseInterface = await JsonApiGet({\n classKey: Modules.Auth,\n endpoint: new EndpointCreator({ endpoint: Modules.Auth, id: \"validate\", childEndpoint: params.code }).generate(),\n language: language,\n });\n\n if (!apiResponse.ok) throw new Error(apiResponse.error);\n }\n\n static async resetPassword(params: { code: string; password: string }): Promise<void> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Auth, id: \"reset\", childEndpoint: params.code });\n\n const input: AuthInput = { password: params.password };\n\n await this.callApi({ type: Modules.Auth, method: HttpMethod.POST, endpoint: endpoint.generate(), input: input });\n }\n\n static async acceptInvitation(params: { code: string; password: string }): Promise<void> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Auth, id: \"invitation\", childEndpoint: params.code });\n\n const input: AuthInput = { password: params.password };\n\n await this.callApi({ type: Modules.Auth, method: HttpMethod.POST, endpoint: endpoint.generate(), input: input });\n }\n\n static async findToken(params: { tokenCode: string }): Promise<AuthInterface> {\n return await this.callApi<AuthInterface>({\n type: Modules.Auth,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.Auth }).addAdditionalParam(\"code\", params.tokenCode).generate(),\n });\n }\n\n static async saveToken(params: { dehydratedAuth: JsonApiHydratedDataInterface }): Promise<void> {\n const auth: AuthInterface = rehydrate(Modules.Auth, params.dehydratedAuth) as AuthInterface;\n\n const handler = getTokenHandler();\n if (handler) {\n await handler.updateToken({\n token: auth.token,\n refreshToken: auth.refreshToken,\n userId: auth.user.id,\n companyId: auth.user.company?.id,\n roles: auth.user.roles.map((role) => role.id),\n features: auth.user.company?.features?.map((feature) => feature.id) ?? [],\n modules: auth.user.modules.map((module) => ({\n id: module.id,\n permissions: module.permissions,\n })),\n });\n }\n }\n\n /**\n * Reads an OAuth sign-up waiting for consent. Throws an Error carrying\n * `status` (404 when the pending registration expired); callers catch it.\n */\n static async findPendingRegistration(params: { pendingId: string }): Promise<PendingRegistrationInterface> {\n return this.callApi<PendingRegistrationInterface>({\n type: Modules.PendingRegistration,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"oauth\",\n childEndpoint: Modules.PendingRegistration,\n childId: params.pendingId,\n }).generate(),\n suppressGlobalError: true,\n });\n }\n\n static async completeOAuthRegistration(params: {\n pendingId: string;\n termsAcceptedAt: string;\n marketingConsent: boolean;\n marketingConsentAt: string | null;\n }): Promise<{ code: string }> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Auth, id: \"oauth\", childEndpoint: \"complete\" });\n\n const response: ApiResponseInterface = await JsonApiPost({\n classKey: Modules.Auth,\n endpoint: endpoint.generate(),\n body: params,\n overridesJsonApiCreation: true,\n language: \"en\",\n });\n\n if (!response.ok) throw new Error(response.error);\n\n return response.data as unknown as { code: string };\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { TwoFactorStatusInterface } from \"./two-factor-status.interface\";\nimport { TotpAuthenticatorInterface } from \"./totp-authenticator.interface\";\nimport { PasskeyInterface } from \"./passkey.interface\";\n\nexport class TwoFactorStatus extends AbstractApiData implements TwoFactorStatusInterface {\n private _isEnabled: boolean = false;\n private _preferredMethod?: \"totp\" | \"passkey\";\n private _totpAuthenticators: TotpAuthenticatorInterface[] = [];\n private _passkeys: PasskeyInterface[] = [];\n private _backupCodesCount: number = 0;\n\n get isEnabled(): boolean {\n return this._isEnabled;\n }\n\n get preferredMethod(): \"totp\" | \"passkey\" | undefined {\n return this._preferredMethod;\n }\n\n get totpAuthenticators(): TotpAuthenticatorInterface[] {\n return this._totpAuthenticators;\n }\n\n get passkeys(): PasskeyInterface[] {\n return this._passkeys;\n }\n\n get backupCodesCount(): number {\n return this._backupCodesCount;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n this._isEnabled = data.jsonApi.attributes.isEnabled ?? false;\n this._preferredMethod = data.jsonApi.attributes.preferredMethod;\n this._backupCodesCount = data.jsonApi.attributes.backupCodesCount ?? 0;\n return this;\n }\n}\n","import { AuthenticationResponseJSON, RegistrationResponseJSON } from \"@simplewebauthn/browser\";\nimport { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { getTokenHandler } from \"../config\";\nimport { AuthInterface } from \"./auth.interface\";\nimport { PasskeyInterface } from \"./passkey.interface\";\nimport { PasskeyAuthenticationOptionsInterface } from \"./passkey-authentication-options.interface\";\nimport { PasskeyRegistrationOptionsInterface } from \"./passkey-registration-options\";\nimport { TotpAuthenticatorInterface } from \"./totp-authenticator.interface\";\nimport { TotpSetupInterface } from \"./totp-setup.interface\";\nimport { TwoFactorChallengeInterface } from \"./two-factor-challenge.interface\";\nimport { TwoFactorStatusInterface } from \"./two-factor-status.interface\";\n\nexport class TwoFactorService extends AbstractService {\n // ============================================================\n // Status\n // ============================================================\n\n static async getStatus(): Promise<TwoFactorStatusInterface> {\n return this.callApi<TwoFactorStatusInterface>({\n type: Modules.TwoFactorStatus,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"two-factor\",\n childEndpoint: \"status\",\n }).generate(),\n });\n }\n\n static async enable(params: { id: string; preferredMethod: \"totp\" | \"passkey\" }): Promise<void> {\n await this.callApi({\n type: Modules.TwoFactorEnable,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"two-factor\",\n childEndpoint: \"enable\",\n }).generate(),\n input: params,\n });\n }\n\n static async disable(params: { code: string }): Promise<void> {\n await this.callApi({\n type: Modules.Auth,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"two-factor\",\n childEndpoint: \"disable\",\n }).generate(),\n input: { code: params.code },\n });\n }\n\n // ============================================================\n // TOTP\n // ============================================================\n\n static async setupTotp(params: { id: string; name: string; accountName: string }): Promise<TotpSetupInterface> {\n return this.callApi<TotpSetupInterface>({\n type: Modules.TotpSetup,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"totp\",\n childEndpoint: \"setup\",\n }).generate(),\n input: params,\n });\n }\n\n static async verifyTotpSetup(params: { id: string; authenticatorId: string; code: string }): Promise<void> {\n await this.callApi({\n type: Modules.TotpVerify,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"totp\",\n childEndpoint: \"verify-setup\",\n }).generate(),\n input: params,\n });\n }\n\n static async listTotpAuthenticators(): Promise<TotpAuthenticatorInterface[]> {\n return this.callApi<TotpAuthenticatorInterface[]>({\n type: Modules.TotpAuthenticator,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"totp\",\n childEndpoint: \"authenticators\",\n }).generate(),\n });\n }\n\n static async deleteTotpAuthenticator(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.Auth,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"totp\",\n childEndpoint: \"authenticators\",\n childId: params.id,\n }).generate(),\n });\n }\n\n // ============================================================\n // Passkeys\n // ============================================================\n\n static async getPasskeyRegistrationOptions(params: {\n id: string;\n userName: string;\n userDisplayName?: string;\n }): Promise<PasskeyRegistrationOptionsInterface> {\n return this.callApi<PasskeyRegistrationOptionsInterface>({\n type: Modules.PasskeyRegistrationOptions,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"passkey\",\n childEndpoint: \"register\",\n childId: \"options\",\n }).generate(),\n input: params,\n });\n }\n\n static async verifyPasskeyRegistration(params: {\n id: string;\n pendingId: string;\n name: string;\n response: RegistrationResponseJSON;\n }): Promise<PasskeyInterface> {\n return this.callApi<PasskeyInterface>({\n type: Modules.PasskeyRegistrationVerify,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"passkey\",\n childEndpoint: \"register\",\n childId: \"verify\",\n }).generate(),\n input: params,\n });\n }\n\n static async listPasskeys(): Promise<PasskeyInterface[]> {\n return this.callApi<PasskeyInterface[]>({\n type: Modules.Passkey,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"passkeys\",\n }).generate(),\n });\n }\n\n static async deletePasskey(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.Auth,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"passkeys\",\n childEndpoint: params.id,\n }).generate(),\n });\n }\n\n static async renamePasskey(params: { id: string; name: string }): Promise<PasskeyInterface> {\n return this.callApi<PasskeyInterface>({\n type: Modules.PasskeyRename,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"passkeys\",\n childEndpoint: params.id,\n }).generate(),\n input: { id: params.id, name: params.name },\n });\n }\n\n // ============================================================\n // Backup Codes\n // ============================================================\n\n static async generateBackupCodes(): Promise<string[]> {\n const response = await this.callApi<{ codes: string[] }>({\n type: Modules.Auth,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"backup-codes\",\n childEndpoint: \"generate\",\n }).generate(),\n });\n return response.codes;\n }\n\n static async getBackupCodesCount(): Promise<number> {\n const response = await this.callApi<{ count: number }>({\n type: Modules.Auth,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"backup-codes\",\n childEndpoint: \"count\",\n }).generate(),\n });\n return response.count;\n }\n\n // ============================================================\n // Login 2FA Verification\n // ============================================================\n\n private static async handleSuccessfulAuth(auth: AuthInterface): Promise<AuthInterface> {\n const handler = getTokenHandler();\n if (handler) {\n await handler.updateToken({\n token: auth.token,\n refreshToken: auth.refreshToken,\n userId: auth.user.id,\n companyId: auth.user.company?.id,\n roles: auth.user.roles.map((role) => role.id),\n features: auth.user.company?.features?.map((feature) => feature.id) ?? [],\n modules: auth.user.modules.map((module) => ({\n id: module.id,\n permissions: module.permissions,\n })),\n });\n }\n\n return auth;\n }\n\n static async getChallenge(params: {\n id: string;\n pendingToken: string;\n method: \"totp\" | \"passkey\" | \"backup\";\n }): Promise<TwoFactorChallengeInterface> {\n return this.callApi<TwoFactorChallengeInterface>({\n type: Modules.TwoFactorChallenge,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"two-factor\",\n childEndpoint: \"challenge\",\n }).generate(),\n input: { id: params.id, method: params.method },\n });\n }\n\n static async verifyTotp(params: { id: string; pendingToken: string; code: string }): Promise<AuthInterface> {\n const auth = await this.callApi<AuthInterface>({\n type: Modules.TotpVerifyLogin, // Request: { type: \"totp-authenticators\", attributes: { code } }\n responseType: Modules.Auth, // Response: Auth with user relationship\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"two-factor\",\n childEndpoint: \"verify\",\n childId: \"totp\",\n }).generate(),\n input: { id: params.id, code: params.code },\n token: params.pendingToken,\n });\n\n return this.handleSuccessfulAuth(auth);\n }\n\n static async getPasskeyAuthOptions(params: { pendingToken: string }): Promise<PasskeyAuthenticationOptionsInterface> {\n return this.callApi<PasskeyAuthenticationOptionsInterface>({\n type: Modules.PasskeyAuthenticationOptions,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"two-factor\",\n childEndpoint: \"verify/passkey\",\n childId: \"options\",\n }).generate(),\n token: params.pendingToken,\n });\n }\n\n static async verifyPasskey(params: {\n id: string;\n pendingToken: string;\n pendingId: string;\n credential: AuthenticationResponseJSON;\n }): Promise<AuthInterface> {\n const auth = await this.callApi<AuthInterface>({\n type: Modules.PasskeyVerifyLogin, // Request: passkey verification format\n responseType: Modules.Auth, // Response: Auth with user relationship\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"two-factor\",\n childEndpoint: \"verify\",\n childId: \"passkey\",\n }).generate(),\n input: { id: params.id, pendingId: params.pendingId, response: params.credential },\n token: params.pendingToken,\n });\n\n return this.handleSuccessfulAuth(auth);\n }\n\n static async verifyBackupCode(params: { id: string; pendingToken: string; code: string }): Promise<AuthInterface> {\n const auth = await this.callApi<AuthInterface>({\n type: Modules.BackupCodeVerify, // Request: backup code verification format\n responseType: Modules.Auth, // Response: Auth with user relationship\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Auth,\n id: \"two-factor\",\n childEndpoint: \"verify\",\n childId: \"backup\",\n }).generate(),\n input: { id: params.id, code: params.code },\n token: params.pendingToken,\n });\n\n return this.handleSuccessfulAuth(auth);\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { PendingRegistrationInterface } from \"./pending-registration.interface\";\n\nexport class PendingRegistration extends AbstractApiData implements PendingRegistrationInterface {\n private _name?: string;\n private _email?: string;\n private _avatar?: string;\n private _provider?: string;\n private _hasPass?: boolean;\n private _emailInUse?: boolean;\n\n get name(): string {\n return this._name ?? \"\";\n }\n\n get email(): string {\n return this._email ?? \"\";\n }\n\n get avatar(): string | undefined {\n return this._avatar;\n }\n\n get provider(): string {\n return this._provider ?? \"\";\n }\n\n get hasPass(): boolean {\n return this._hasPass ?? false;\n }\n\n get emailInUse(): boolean {\n return this._emailInUse ?? false;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n this._name = data.jsonApi.attributes.name;\n this._email = data.jsonApi.attributes.email;\n this._avatar = data.jsonApi.attributes.avatar ?? undefined;\n this._provider = data.jsonApi.attributes.provider;\n this._hasPass = data.jsonApi.attributes.hasPass ?? false;\n this._emailInUse = data.jsonApi.attributes.emailInUse ?? false;\n return this;\n }\n\n /** Read-only resource: the payload only identifies it. */\n createJsonApi(data: { id: string }) {\n return {\n data: {\n type: Modules.PendingRegistration.name,\n id: data.id,\n attributes: {},\n },\n };\n }\n}\n","export enum AuthComponent {\n Login,\n ForgotPassword,\n ResetPassword,\n ActivateAccount,\n AcceptInvitation,\n Register,\n Landing,\n TwoFactorChallenge = \"2fa_challenge\",\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { Auth } from \".\";\n\nexport const AuthModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/auth\",\n name: \"auth\",\n model: Auth,\n });\n","import { ModuleFactory } from \"../../permissions\";\nimport { TwoFactorStatus } from \"./data/two-factor-status\";\n\nexport const TwoFactorStatusModule = (factory: ModuleFactory) =>\n factory({\n name: \"two-factor-status\",\n pageUrl: \"/two-factor-status\",\n model: TwoFactorStatus,\n });\n","import { ModuleFactory } from \"../../permissions\";\nimport { PendingRegistration } from \"./data/pending-registration\";\n\nexport const PendingRegistrationModule = (factory: ModuleFactory) =>\n factory({\n name: \"pending-registrations\",\n pageUrl: \"/auth/consent\",\n model: PendingRegistration,\n });\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../core\";\n\n// Import from new sub-modules for backwards compatibility re-exports\nimport { MeterInterface, MeterSummaryInterface } from \"../stripe-usage/data/stripe-usage.interface\";\n\n/**\n * Legacy billing service - only contains meter methods\n * @deprecated Use StripeUsageService for meter methods, StripeInvoiceService for invoices,\n * StripeCustomerService for payment methods\n */\nexport class BillingService extends AbstractService {\n // ============================================================================\n // Meter Methods (kept here for backwards compatibility)\n // ============================================================================\n\n /**\n * List all available usage meters\n * @deprecated Use StripeUsageService.listMeters() instead\n */\n static async listMeters(params?: { next?: NextRef; prev?: PreviousRef }): Promise<MeterInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeUsage,\n childEndpoint: \"meters\",\n });\n\n return this.callApi({\n type: Modules.StripeUsage,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params?.next,\n previous: params?.prev,\n });\n }\n\n /**\n * Get meter summaries for a specific time period\n * @deprecated Use StripeUsageService.getMeterSummaries() instead\n */\n static async getMeterSummaries(params: {\n meterId: string;\n startTime: Date;\n endTime: Date;\n }): Promise<MeterSummaryInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeUsage,\n childEndpoint: `meters/${params.meterId}/summaries`,\n });\n\n endpoint.addAdditionalParam(\"startTime\", params.startTime.toISOString());\n endpoint.addAdditionalParam(\"endTime\", params.endTime.toISOString());\n\n return this.callApi({\n type: Modules.StripeUsage,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\n\n/**\n * Billing is a namespace module used for permissions and non-entity endpoints.\n * It doesn't correspond to a specific backend entity but provides routing for\n * meter-related endpoints and permission checks.\n */\nexport class Billing extends AbstractApiData {\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n return this;\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"Billing is a namespace module and cannot be created\");\n }\n}\n","// ============================================================================\n// Subscription Enums\n// ============================================================================\n\nimport { ApiDataInterface, StripePriceInterface } from \"../../../../core\";\n\nexport enum SubscriptionStatus {\n ACTIVE = \"active\",\n PAST_DUE = \"past_due\",\n UNPAID = \"unpaid\",\n CANCELED = \"canceled\",\n INCOMPLETE = \"incomplete\",\n INCOMPLETE_EXPIRED = \"incomplete_expired\",\n TRIALING = \"trialing\",\n PAUSED = \"paused\",\n}\n\n// ============================================================================\n// Subscription Interfaces\n// ============================================================================\n\n/**\n * The discount Stripe applies to the subscription (read-only, synced from Stripe).\n * Exactly one of percentOff / amountOff is set.\n */\nexport type StripeSubscriptionDiscount = {\n percentOff?: number;\n amountOff?: number; // minor units, in currency\n currency?: string;\n duration: \"forever\" | \"once\" | \"repeating\";\n durationInMonths?: number;\n end?: Date; // when a repeating discount stops\n};\n\nexport interface StripeSubscriptionInterface extends ApiDataInterface {\n get stripeSubscriptionId(): string;\n get status(): SubscriptionStatus;\n get currentPeriodStart(): Date;\n get currentPeriodEnd(): Date;\n get cancelAtPeriodEnd(): boolean;\n get canceledAt(): Date | undefined;\n get trialStart(): Date | undefined;\n get trialEnd(): Date | undefined;\n get price(): StripePriceInterface | undefined;\n get discount(): StripeSubscriptionDiscount | undefined;\n}\n\n// ============================================================================\n// Subscription Input DTOs\n// ============================================================================\n\nexport type StripeSubscriptionInput = {\n id: string;\n // For CREATE - goes to relationships.stripePrice\n priceId?: string;\n // For CHANGE-PLAN - goes to attributes.priceId\n newPriceId?: string;\n // For CANCEL - goes to attributes.cancelImmediately\n cancelImmediately?: boolean;\n // Shared optional fields\n quantity?: number;\n trialPeriodDays?: number;\n paymentMethodId?: string;\n metadata?: Record<string, any>;\n // For CREATE and CHANGE-PLAN - optional promotion code\n promotionCode?: string;\n};\n\n// ============================================================================\n// Subscription Response Types (for SCA payment confirmation)\n// ============================================================================\n\nexport interface StripeSubscriptionCreateMeta {\n clientSecret: string | null;\n paymentIntentId: string | null;\n requiresAction: boolean;\n}\n\nexport interface StripeSubscriptionCreateResponse {\n subscription: StripeSubscriptionInterface;\n meta: StripeSubscriptionCreateMeta;\n}\n","// Credit warning: shared contract with the backend and pure helpers.\n// Keep this file free of any `@stripe/*` import: it is re-exported from the\n// Stripe-free `components` entry, because the banner mounts on every page.\n\nimport type { CompanyInterface } from \"../../company/data/company.interface\";\nimport { SubscriptionStatus } from \"../stripe-subscription/data/stripe-subscription.interface\";\nimport type { StripeSubscriptionInterface } from \"../stripe-subscription/data/stripe-subscription.interface\";\n\n/**\n * The offer the server chose when it sent this period's warning.\n * Mirrors the backend `CreditWarningOffer`.\n */\nexport type CreditWarningOffer = \"subscribe\" | \"upgrade-or-pack\" | \"pack\";\n\n/**\n * Child segment of the dismiss route: `POST companies/:companyId/credit-warning/dismiss`.\n * Must match the backend `CREDIT_WARNING_DISMISS_SEGMENT`.\n */\nexport const CREDIT_WARNING_DISMISS_SEGMENT = \"credit-warning/dismiss\";\n\n/**\n * Whether the in-app credit warning should show for this company.\n *\n * True only when the server sent the warning this period and nobody dismissed it,\n * the company has AI and an active subscription, and the credits left are at or\n * below `100 - thresholdPercent` percent of this period's grant. At 0 credits\n * (or below) it is false: `OutOfCreditsBanner` owns that state.\n */\nexport function isCreditWarningActive(company: CompanyInterface, thresholdPercent: number): boolean {\n if (!company.aiEnabled || !company.isActiveSubscription) return false;\n if (!company.creditWarningSentAt || company.creditWarningDismissedAt) return false;\n\n const granted = company.creditsGrantedThisPeriod;\n if (granted === undefined || granted <= 0) return false;\n\n const monthly = company.availableMonthlyCredits;\n const extra = company.availableExtraCredits;\n // Same \"has credits\" test as useHasCredits / CompanyService.hasAvailableCredits.\n if (!(monthly > 0 || extra > 0)) return false;\n\n return monthly + extra <= (granted * (100 - thresholdPercent)) / 100;\n}\n\n/**\n * The subscription the `?action=change-plan` deep link opens the wizard on: the\n * first active or trialing recurring subscription, in the dashboard's own order.\n * `undefined` means there is none, and the subscribe wizard opens instead.\n */\nexport function resolveChangePlanTarget(\n subscriptions: StripeSubscriptionInterface[],\n): StripeSubscriptionInterface | undefined {\n return subscriptions.find(\n (sub) =>\n (sub.status === SubscriptionStatus.ACTIVE || sub.status === SubscriptionStatus.TRIALING) &&\n sub.price?.priceType === \"recurring\",\n );\n}\n","import { ModuleFactory } from \"../../../permissions\";\nimport { Billing } from \"../data/Billing\";\n\nexport const BillingModule = (factory: ModuleFactory) =>\n factory({\n name: \"billing\",\n pageUrl: \"/billing\",\n model: Billing,\n moduleId: \"3266b307-5a9a-46f9-b78d-631f672f8735\",\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../../core\";\nimport { PaymentMethodInterface } from \"./payment-method.interface\";\n\n/**\n * PaymentMethod class for JSON:API rehydration\n *\n * Transforms flat JSON:API attributes into the nested PaymentMethodInterface structure\n * expected by the frontend components.\n */\nexport class PaymentMethod extends AbstractApiData implements PaymentMethodInterface {\n private _paymentType?: string;\n private _card?: {\n brand: string;\n last4: string;\n expMonth: number;\n expYear: number;\n };\n private _billingDetails?: {\n name?: string;\n email?: string;\n phone?: string;\n address?: {\n city?: string;\n country?: string;\n line1?: string;\n line2?: string;\n postalCode?: string;\n state?: string;\n };\n };\n\n get type(): string {\n if (!this._paymentType) throw new Error(\"type is not defined\");\n return this._paymentType;\n }\n\n get card():\n | {\n brand: string;\n last4: string;\n expMonth: number;\n expYear: number;\n }\n | undefined {\n return this._card;\n }\n\n get billingDetails():\n | {\n name?: string;\n email?: string;\n phone?: string;\n address?: {\n city?: string;\n country?: string;\n line1?: string;\n line2?: string;\n postalCode?: string;\n state?: string;\n };\n }\n | undefined {\n return this._billingDetails;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n\n this._paymentType = attrs.type;\n\n // Build card object from flat attributes\n if (attrs.brand || attrs.last4 || attrs.expMonth || attrs.expYear) {\n this._card = {\n brand: attrs.brand,\n last4: attrs.last4,\n expMonth: attrs.expMonth,\n expYear: attrs.expYear,\n };\n }\n\n // Build billingDetails object from flat attributes\n const hasAddress =\n attrs.billingAddressCity ||\n attrs.billingAddressCountry ||\n attrs.billingAddressLine1 ||\n attrs.billingAddressLine2 ||\n attrs.billingAddressPostalCode ||\n attrs.billingAddressState;\n\n const hasBillingDetails = attrs.billingName || attrs.billingEmail || attrs.billingPhone || hasAddress;\n\n if (hasBillingDetails) {\n this._billingDetails = {\n name: attrs.billingName ?? undefined,\n email: attrs.billingEmail ?? undefined,\n phone: attrs.billingPhone ?? undefined,\n };\n\n if (hasAddress) {\n this._billingDetails.address = {\n city: attrs.billingAddressCity ?? undefined,\n country: attrs.billingAddressCountry ?? undefined,\n line1: attrs.billingAddressLine1 ?? undefined,\n line2: attrs.billingAddressLine2 ?? undefined,\n postalCode: attrs.billingAddressPostalCode ?? undefined,\n state: attrs.billingAddressState ?? undefined,\n };\n }\n }\n\n return this;\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"PaymentMethod is managed by Stripe and cannot be created directly\");\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../../core\";\nimport { StripeCustomerInterface } from \"./stripe-customer.interface\";\n\nexport class StripeCustomer extends AbstractApiData implements StripeCustomerInterface {\n private _stripeCustomerId?: string;\n private _email?: string;\n private _name?: string;\n private _defaultPaymentMethodId?: string;\n private _currency?: string;\n private _balance?: number;\n private _delinquent: boolean = false;\n private _metadata?: Record<string, any>;\n\n get stripeCustomerId(): string {\n if (!this._stripeCustomerId) throw new Error(\"stripeCustomerId is not defined\");\n return this._stripeCustomerId;\n }\n\n get email(): string | undefined {\n return this._email;\n }\n\n get name(): string | undefined {\n return this._name;\n }\n\n get defaultPaymentMethodId(): string | undefined {\n return this._defaultPaymentMethodId;\n }\n\n get currency(): string | undefined {\n return this._currency;\n }\n\n get balance(): number | undefined {\n return this._balance;\n }\n\n get delinquent(): boolean {\n return this._delinquent;\n }\n\n get metadata(): Record<string, any> | undefined {\n return this._metadata;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._stripeCustomerId = data.jsonApi.attributes.stripeCustomerId;\n this._email = data.jsonApi.attributes.email;\n this._name = data.jsonApi.attributes.name;\n this._defaultPaymentMethodId = data.jsonApi.attributes.defaultPaymentMethodId;\n this._currency = data.jsonApi.attributes.currency;\n this._balance = data.jsonApi.attributes.balance;\n this._delinquent = data.jsonApi.attributes.delinquent ?? false;\n\n // Parse metadata if it's a string (backend stores as JSON string)\n this._metadata = data.jsonApi.attributes.metadata\n ? typeof data.jsonApi.attributes.metadata === \"string\"\n ? JSON.parse(data.jsonApi.attributes.metadata)\n : data.jsonApi.attributes.metadata\n : undefined;\n\n return this;\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"BillingCustomer is managed by Stripe and cannot be created directly\");\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../../core\";\nimport { PaymentMethodInterface } from \"./payment-method.interface\";\nimport { StripeCustomerInterface } from \"./stripe-customer.interface\";\n\n/**\n * Customer-facing billing service for managing subscriptions, payments, and usage\n */\nexport class StripeCustomerService extends AbstractService {\n // ============================================================================\n // Customer Methods\n // ============================================================================\n\n /**\n * Get the current user's billing customer record\n */\n static async getCustomer(): Promise<StripeCustomerInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCustomer,\n });\n\n return this.callApi<StripeCustomerInterface>({\n type: Modules.StripeCustomer,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n // A 404 here means the company has no billing customer yet — an expected\n // state that BillingDashboardContainer turns into a setup prompt. Without\n // this, the global error handler swallows it and replaces the page with a\n // 404 screen.\n suppressGlobalError: true,\n });\n }\n\n /**\n * Create a billing customer for the current user\n */\n static async createCustomer(): Promise<StripeCustomerInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCustomer,\n });\n\n return this.callApi<StripeCustomerInterface>({\n type: Modules.StripeCustomer,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Create a setup intent for adding payment methods\n */\n static async createSetupIntent(): Promise<{ clientSecret: string }> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Billing,\n childEndpoint: \"setup-intent\",\n });\n\n return this.callApi({\n type: Modules.Billing,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Create a Stripe customer portal session URL\n */\n static async createPortalSession(): Promise<{ url: string }> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Billing,\n childEndpoint: \"customers/portal-session\",\n });\n\n return this.callApi({\n type: Modules.Billing,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n });\n }\n\n // ============================================================================\n // Payment Method Methods\n // ============================================================================\n\n /**\n * List all payment methods for the current user\n */\n static async listPaymentMethods(params?: { next?: NextRef; prev?: PreviousRef }): Promise<PaymentMethodInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCustomer,\n childEndpoint: \"payment-methods\",\n });\n\n return this.callApi({\n type: Modules.StripePaymentMethod,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params?.next,\n previous: params?.prev,\n });\n }\n\n /**\n * Set the default payment method for the current user\n */\n static async setDefaultPaymentMethod(params: { paymentMethodId: string }): Promise<StripeCustomerInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCustomer,\n childEndpoint: `payment-methods/${params.paymentMethodId}/default`,\n });\n\n return this.callApi<StripeCustomerInterface>({\n type: Modules.StripeCustomer,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Remove a payment method\n */\n static async removePaymentMethod(params: { paymentMethodId: string }): Promise<void> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCustomer,\n childEndpoint: `payment-methods/${params.paymentMethodId}`,\n });\n\n await this.callApi({\n type: Modules.StripeCustomer,\n method: HttpMethod.DELETE,\n endpoint: endpoint.generate(),\n });\n }\n}\n","import { ModuleFactory } from \"../../../permissions\";\nimport { StripeCustomer } from \"./data\";\n\nexport const StripeCustomerModule = (factory: ModuleFactory) =>\n factory({\n name: \"stripe-customers\",\n pageUrl: \"/stripe-customers\",\n model: StripeCustomer,\n moduleId: \"25a80cb3-bf18-47fd-963a-639212519920\",\n });\n","import { ModuleFactory } from \"../../../permissions\";\nimport { PaymentMethod } from \"./data/payment-method\";\n\nexport const StripePaymentMethodModule = (factory: ModuleFactory) =>\n factory({\n name: \"stripe-payment-methods\",\n pageUrl: \"/stripe-payment-methods\",\n model: PaymentMethod,\n moduleId: \"e8f7d6c5-b4a3-4928-8170-1f2e3d4c5b6a\",\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules, StripeSubscriptionInterface } from \"../../../../core\";\nimport { InvoiceStatus, StripeInvoiceInterface } from \"./stripe-invoice.interface\";\n\nexport class StripeInvoice extends AbstractApiData implements StripeInvoiceInterface {\n private _stripeInvoiceId?: string;\n private _stripeInvoiceNumber?: string;\n private _customerId?: string;\n private _subscriptionId?: string;\n private _subscription?: StripeSubscriptionInterface;\n private _status?: InvoiceStatus;\n private _amountDue?: number;\n private _amountPaid?: number;\n private _amountRemaining?: number;\n private _subtotal?: number;\n private _total?: number;\n private _tax?: number;\n private _currency?: string;\n private _periodStart?: Date;\n private _periodEnd?: Date;\n private _dueDate?: Date;\n private _paidAt?: Date;\n private _attemptCount: number = 0;\n private _attempted: boolean = false;\n private _stripeHostedInvoiceUrl?: string;\n private _stripePdfUrl?: string;\n private _paid: boolean = false;\n private _metadata?: Record<string, any>;\n\n get stripeInvoiceId(): string {\n if (!this._stripeInvoiceId) throw new Error(\"stripeInvoiceId is not defined\");\n return this._stripeInvoiceId;\n }\n\n get stripeInvoiceNumber(): string | undefined {\n return this._stripeInvoiceNumber;\n }\n\n get customerId(): string | undefined {\n return this._customerId;\n }\n\n get subscriptionId(): string | undefined {\n return this._subscriptionId;\n }\n\n get subscription(): StripeSubscriptionInterface | undefined {\n return this._subscription;\n }\n\n get status(): InvoiceStatus {\n if (!this._status) throw new Error(\"status is not defined\");\n return this._status;\n }\n\n get amountDue(): number {\n if (this._amountDue === undefined) throw new Error(\"amountDue is not defined\");\n return this._amountDue;\n }\n\n get amountPaid(): number {\n if (this._amountPaid === undefined) throw new Error(\"amountPaid is not defined\");\n return this._amountPaid;\n }\n\n get amountRemaining(): number {\n if (this._amountRemaining === undefined) throw new Error(\"amountRemaining is not defined\");\n return this._amountRemaining;\n }\n\n get subtotal(): number {\n if (this._subtotal === undefined) throw new Error(\"subtotal is not defined\");\n return this._subtotal;\n }\n\n get total(): number {\n if (this._total === undefined) throw new Error(\"total is not defined\");\n return this._total;\n }\n\n get tax(): number | undefined {\n return this._tax;\n }\n\n get currency(): string {\n if (!this._currency) throw new Error(\"currency is not defined\");\n return this._currency;\n }\n\n get periodStart(): Date {\n if (!this._periodStart) throw new Error(\"periodStart is not defined\");\n return this._periodStart;\n }\n\n get periodEnd(): Date {\n if (!this._periodEnd) throw new Error(\"periodEnd is not defined\");\n return this._periodEnd;\n }\n\n get dueDate(): Date | undefined {\n return this._dueDate;\n }\n\n get paidAt(): Date | undefined {\n return this._paidAt;\n }\n\n get attemptCount(): number {\n return this._attemptCount;\n }\n\n get attempted(): boolean {\n return this._attempted;\n }\n\n get stripeHostedInvoiceUrl(): string | undefined {\n return this._stripeHostedInvoiceUrl;\n }\n\n get stripePdfUrl(): string | undefined {\n return this._stripePdfUrl;\n }\n\n get paid(): boolean {\n return this._paid;\n }\n\n get metadata(): Record<string, any> | undefined {\n return this._metadata;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._stripeInvoiceId = data.jsonApi.attributes.stripeInvoiceId;\n this._stripeInvoiceNumber = data.jsonApi.attributes.stripeInvoiceNumber;\n this._customerId = data.jsonApi.attributes.customerId;\n this._subscriptionId = data.jsonApi.attributes.subscriptionId;\n this._status = data.jsonApi.attributes.status;\n this._amountDue = data.jsonApi.attributes.amountDue;\n this._amountPaid = data.jsonApi.attributes.amountPaid;\n this._amountRemaining = data.jsonApi.attributes.amountRemaining;\n this._subtotal = data.jsonApi.attributes.subtotal;\n this._total = data.jsonApi.attributes.total;\n this._tax = data.jsonApi.attributes.tax;\n this._currency = data.jsonApi.attributes.currency;\n\n this._periodStart = data.jsonApi.attributes.periodStart ? new Date(data.jsonApi.attributes.periodStart) : undefined;\n this._periodEnd = data.jsonApi.attributes.periodEnd ? new Date(data.jsonApi.attributes.periodEnd) : undefined;\n this._dueDate = data.jsonApi.attributes.dueDate ? new Date(data.jsonApi.attributes.dueDate) : undefined;\n this._paidAt = data.jsonApi.attributes.paidAt ? new Date(data.jsonApi.attributes.paidAt) : undefined;\n\n this._attemptCount = data.jsonApi.attributes.attemptCount ?? 0;\n this._attempted = data.jsonApi.attributes.attempted ?? false;\n this._stripeHostedInvoiceUrl = data.jsonApi.attributes.stripeHostedInvoiceUrl;\n this._stripePdfUrl = data.jsonApi.attributes.stripePdfUrl;\n this._paid = data.jsonApi.attributes.paid ?? false;\n\n this._metadata = data.jsonApi.attributes.metadata\n ? typeof data.jsonApi.attributes.metadata === \"string\"\n ? JSON.parse(data.jsonApi.attributes.metadata)\n : data.jsonApi.attributes.metadata\n : undefined;\n\n // Hydrate subscription relationship\n this._subscription = this._readIncluded(\n data,\n \"subscription\",\n Modules.StripeSubscription,\n ) as StripeSubscriptionInterface;\n\n return this;\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"Invoice is managed by Stripe and cannot be created directly\");\n }\n}\n","import { ApiDataInterface, StripeSubscriptionInterface } from \"../../../../core\";\n\n// ============================================================================\n// Invoice Enums\n// ============================================================================\n\nexport enum InvoiceStatus {\n DRAFT = \"draft\",\n OPEN = \"open\",\n PAID = \"paid\",\n UNCOLLECTIBLE = \"uncollectible\",\n VOID = \"void\",\n}\n\n// ============================================================================\n// Invoice Interfaces\n// ============================================================================\n\nexport interface StripeInvoiceInterface extends ApiDataInterface {\n get stripeInvoiceId(): string;\n get stripeInvoiceNumber(): string | undefined;\n get customerId(): string | undefined;\n get subscriptionId(): string | undefined;\n get subscription(): StripeSubscriptionInterface | undefined;\n get status(): InvoiceStatus;\n get amountDue(): number;\n get amountPaid(): number;\n get amountRemaining(): number;\n get subtotal(): number;\n get total(): number;\n get tax(): number | undefined;\n get currency(): string;\n get periodStart(): Date;\n get periodEnd(): Date;\n get dueDate(): Date | undefined;\n get paidAt(): Date | undefined;\n get attemptCount(): number;\n get attempted(): boolean;\n get stripeHostedInvoiceUrl(): string | undefined;\n get stripePdfUrl(): string | undefined;\n get paid(): boolean;\n get metadata(): Record<string, any> | undefined;\n}\n\n// ============================================================================\n// Proration Preview Interfaces\n// ============================================================================\n\nexport interface ProrationPreviewInterface {\n amountDue: number;\n currency: string;\n immediateCharge: number;\n prorationDate: Date;\n lineItems: ProrationLineItem[];\n isTrialUpgrade?: boolean;\n}\n\nexport interface ProrationLineItem {\n description: string;\n amount: number;\n proration: boolean;\n period: {\n start: Date;\n end: Date;\n };\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../../core\";\nimport { StripeInvoiceInterface } from \"./stripe-invoice.interface\";\n\n/**\n * Service for managing Stripe invoices\n */\nexport class StripeInvoiceService extends AbstractService {\n /**\n * List all invoices for the current user\n */\n static async listInvoices(params?: {\n status?: string;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<StripeInvoiceInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeInvoice,\n });\n\n if (params?.status) {\n endpoint.addAdditionalParam(\"status\", params.status);\n }\n\n return this.callApi({\n type: Modules.StripeInvoice,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params?.next,\n previous: params?.prev,\n });\n }\n\n /**\n * Get a specific invoice by ID\n */\n static async getInvoice(params: { invoiceId: string }): Promise<StripeInvoiceInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeInvoice,\n id: params.invoiceId,\n });\n\n return this.callApi<StripeInvoiceInterface>({\n type: Modules.StripeInvoice,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Get the upcoming invoice for the current user\n */\n static async getUpcomingInvoice(): Promise<StripeInvoiceInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeInvoice,\n id: \"upcoming\",\n });\n\n return this.callApi<StripeInvoiceInterface>({\n type: Modules.StripeInvoice,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n}\n","import { ModuleFactory } from \"../../../permissions\";\nimport { StripeInvoice } from \"./data/stripe-invoice\";\n\nexport const StripeInvoiceModule = (factory: ModuleFactory) =>\n factory({\n name: \"stripe-invoices\",\n pageUrl: \"/stripe-invoices\",\n model: StripeInvoice,\n moduleId: \"37c73b8b-d3b1-4e5e-8fcb-a66d8ecfc05b\",\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../../core\";\nimport { StripeProductInterface } from \"../../stripe-product\";\nimport { FeatureInterface } from \"../../../feature\";\nimport { PriceRecurring, StripePriceInput, StripePriceInterface } from \"./stripe-price.interface\";\n\nexport class StripePrice extends AbstractApiData implements StripePriceInterface {\n private _stripePriceId?: string;\n private _productId?: string;\n private _product?: StripeProductInterface;\n private _active: boolean = true;\n private _currency?: string;\n private _unitAmount?: number;\n private _recurring?: PriceRecurring;\n private _priceType?: \"one_time\" | \"recurring\";\n private _nickname?: string;\n private _lookupKey?: string;\n private _metadata?: Record<string, any>;\n private _description?: string;\n private _features?: string[];\n private _token?: number;\n private _isTrial?: boolean;\n private _priceFeatures: FeatureInterface[] = []; // Platform Feature entities\n\n get stripePriceId(): string {\n if (!this._stripePriceId) throw new Error(\"stripePriceId is not defined\");\n return this._stripePriceId;\n }\n\n get productId(): string {\n if (!this._productId) throw new Error(\"productId is not defined\");\n return this._productId;\n }\n\n get product(): StripeProductInterface | undefined {\n return this._product;\n }\n\n get active(): boolean {\n return this._active;\n }\n\n get currency(): string {\n if (!this._currency) throw new Error(\"currency is not defined\");\n return this._currency;\n }\n\n get unitAmount(): number | undefined {\n return this._unitAmount;\n }\n\n get recurring(): PriceRecurring | undefined {\n return this._recurring;\n }\n\n get priceType(): \"one_time\" | \"recurring\" {\n if (!this._priceType) throw new Error(\"priceType is not defined\");\n return this._priceType;\n }\n\n get nickname(): string | undefined {\n return this._nickname;\n }\n\n get lookupKey(): string | undefined {\n return this._lookupKey;\n }\n\n get metadata(): Record<string, any> | undefined {\n return this._metadata;\n }\n\n get description(): string | undefined {\n return this._description;\n }\n\n get features(): string[] | undefined {\n return this._features;\n }\n\n get token(): number | undefined {\n return this._token;\n }\n\n get isTrial(): boolean | undefined {\n return this._isTrial;\n }\n\n get priceFeatures(): FeatureInterface[] {\n return this._priceFeatures;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._stripePriceId = data.jsonApi.attributes.stripePriceId;\n this._productId = data.jsonApi.attributes.productId;\n this._active = data.jsonApi.attributes.active ?? true;\n this._currency = data.jsonApi.attributes.currency;\n this._unitAmount = data.jsonApi.attributes.unitAmount;\n this._priceType = data.jsonApi.attributes.priceType;\n\n // Construct recurring object from flat attributes\n if (data.jsonApi.attributes.recurringInterval) {\n this._recurring = {\n interval: data.jsonApi.attributes.recurringInterval,\n intervalCount: data.jsonApi.attributes.recurringIntervalCount ?? 1,\n usageType: data.jsonApi.attributes.recurringUsageType,\n };\n }\n\n this._nickname = data.jsonApi.attributes.nickname;\n this._lookupKey = data.jsonApi.attributes.lookupKey;\n\n this._metadata = data.jsonApi.attributes.metadata\n ? typeof data.jsonApi.attributes.metadata === \"string\"\n ? JSON.parse(data.jsonApi.attributes.metadata)\n : data.jsonApi.attributes.metadata\n : undefined;\n\n this._description = data.jsonApi.attributes.description;\n this._features = data.jsonApi.attributes.features\n ? typeof data.jsonApi.attributes.features === \"string\"\n ? JSON.parse(data.jsonApi.attributes.features)\n : data.jsonApi.attributes.features\n : undefined;\n\n this._token = data.jsonApi.attributes.token;\n this._isTrial = data.jsonApi.attributes.isTrial;\n\n // Hydrate product relationship\n this._product = this._readIncluded(data, \"product\", Modules.StripeProduct) as StripeProductInterface;\n\n // Hydrate feature relationship (JSON:API key is \"features\")\n this._priceFeatures = this._readIncluded(data, \"features\", Modules.Feature) as FeatureInterface[];\n\n return this;\n }\n\n createJsonApi(data: StripePriceInput): any {\n const response: any = {\n data: {\n type: Modules.StripePrice.name,\n id: data.id,\n attributes: {},\n },\n };\n\n if (\"productId\" in data && data.productId) {\n response.data.attributes.productId = data.productId;\n }\n if (\"currency\" in data && data.currency) {\n response.data.attributes.currency = data.currency;\n }\n if (\"unitAmount\" in data && data.unitAmount !== undefined) {\n response.data.attributes.unitAmount = data.unitAmount;\n }\n if (\"active\" in data && data.active !== undefined) {\n response.data.attributes.active = data.active;\n }\n if (\"nickname\" in data && data.nickname !== undefined) {\n response.data.attributes.nickname = data.nickname;\n }\n if (\"metadata\" in data && data.metadata) {\n response.data.attributes.metadata = data.metadata;\n }\n if (\"recurring\" in data && data.recurring) {\n response.data.attributes.recurring = data.recurring;\n }\n if (\"description\" in data && data.description !== undefined) {\n response.data.attributes.description = data.description;\n }\n if (\"features\" in data && data.features !== undefined) {\n response.data.attributes.features = JSON.stringify(data.features);\n }\n if (\"token\" in data && data.token !== undefined) {\n response.data.attributes.token = data.token;\n }\n if (\"isTrial\" in data && data.isTrial !== undefined) {\n response.data.attributes.isTrial = data.isTrial;\n }\n\n // Convert featureIds to JSON:API relationships format\n if (data.featureIds && data.featureIds.length > 0) {\n response.data.relationships = response.data.relationships || {};\n response.data.relationships.features = data.featureIds.map((id) => ({\n type: Modules.Feature.name,\n id,\n }));\n }\n\n return response;\n }\n}\n","export enum StripePriceFields {\n stripePriceId = \"stripePriceId\",\n nickname = \"nickname\",\n amount = \"amount\",\n interval = \"interval\",\n token = \"token\",\n status = \"status\",\n actions = \"actions\",\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../../core\";\nimport { StripePriceInput, StripePriceInterface } from \"./stripe-price.interface\";\n\n/**\n * Admin billing service for managing products and prices\n */\nexport class StripePriceService extends AbstractService {\n /**\n * List all prices (admin)\n */\n static async listPrices(params?: {\n productId?: string;\n active?: boolean;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<StripePriceInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripePrice,\n });\n\n if (params?.productId) {\n endpoint.addAdditionalParam(\"productId\", params.productId);\n }\n if (params?.active !== undefined) {\n endpoint.addAdditionalParam(\"active\", params.active.toString());\n }\n\n return this.callApi({\n type: Modules.StripePrice,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params?.next,\n previous: params?.prev,\n });\n }\n\n /**\n * Get a specific price by ID (admin)\n */\n static async getPrice(params: { id: string }): Promise<StripePriceInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripePrice,\n id: params.id,\n });\n\n return this.callApi<StripePriceInterface>({\n type: Modules.StripePrice,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Create a new price (admin)\n */\n static async createPrice(params: StripePriceInput): Promise<StripePriceInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripePrice,\n });\n\n return this.callApi<StripePriceInterface>({\n type: Modules.StripePrice,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: params,\n });\n }\n\n /**\n * Update an existing price (admin)\n */\n static async updatePrice(params: StripePriceInput): Promise<StripePriceInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripePrice,\n id: params.id,\n });\n\n return this.callApi<StripePriceInterface>({\n type: Modules.StripePrice,\n method: HttpMethod.PUT,\n endpoint: endpoint.generate(),\n input: params,\n });\n }\n\n /**\n * Archive a price (admin)\n *\n * Sets the price as inactive. Archived prices cannot be used for new subscriptions.\n */\n static async archivePrice(params: { id: string }): Promise<void> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripePrice,\n id: params.id,\n childEndpoint: \"archive\",\n });\n\n await this.callApi({\n type: Modules.StripePrice,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Reactivate a price (admin)\n *\n * Sets the price as active. Active prices can be used for new subscriptions.\n */\n static async reactivatePrice(params: { id: string }): Promise<void> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripePrice,\n id: params.id,\n childEndpoint: \"archive\",\n });\n\n await this.callApi({\n type: Modules.StripePrice,\n method: HttpMethod.DELETE,\n endpoint: endpoint.generate(),\n });\n }\n}\n","import { CreditCardIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../../permissions\";\nimport { StripePrice } from \"./data/stripe-price\";\n\nexport const StripePriceModule = (factory: ModuleFactory) =>\n factory({\n name: \"stripe-prices\",\n // The administration route this entity is served at. `name` above stays the\n // API endpoint; this drives generateUrl / rewriteUrl / EditorSheet\n // navigation, so it must match the real Next.js route or RoundPageContainer\n // rewrites the URL to a 404 on the first tab change.\n pageUrl: \"/administration/prices\",\n model: StripePrice,\n moduleId: \"a7d3e5f1-8c9b-4a2e-b6d7-3f1c8e9a4b5d\",\n icon: CreditCardIcon,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules, StripePriceInterface } from \"../../../../core\";\nimport { StripeProductInput, StripeProductInterface } from \"./stripe-product.interface\";\n\nexport class StripeProduct extends AbstractApiData implements StripeProductInterface {\n private _stripeProductId?: string;\n private _name?: string;\n private _description?: string;\n private _active: boolean = true;\n private _metadata?: Record<string, any>;\n\n private _stripePrices: StripePriceInterface[] = [];\n\n get stripeProductId(): string {\n if (!this._stripeProductId) throw new Error(\"stripeProductId is not defined\");\n return this._stripeProductId;\n }\n\n get name(): string {\n if (!this._name) throw new Error(\"name is not defined\");\n return this._name;\n }\n\n get description(): string | undefined {\n return this._description;\n }\n\n get active(): boolean {\n return this._active;\n }\n\n get metadata(): Record<string, any> | undefined {\n return this._metadata;\n }\n\n get stripePrices(): StripePriceInterface[] {\n return this._stripePrices ?? [];\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._stripeProductId = data.jsonApi.attributes.stripeProductId;\n this._name = data.jsonApi.attributes.name;\n this._description = data.jsonApi.attributes.description;\n this._active = data.jsonApi.attributes.active ?? true;\n\n this._metadata = data.jsonApi.attributes.metadata\n ? typeof data.jsonApi.attributes.metadata === \"string\"\n ? JSON.parse(data.jsonApi.attributes.metadata)\n : data.jsonApi.attributes.metadata\n : undefined;\n\n this._stripePrices = this._readIncluded(data, \"stripePrices\", Modules.StripePrice) as StripePriceInterface[];\n\n return this;\n }\n\n createJsonApi(data: StripeProductInput): any {\n const response: any = {\n data: {\n type: Modules.StripeProduct.name,\n id: data.id,\n attributes: {},\n },\n };\n\n if (data.name) response.data.attributes.name = data.name;\n if (data.description !== undefined) response.data.attributes.description = data.description;\n if (data.active !== undefined) response.data.attributes.active = data.active;\n if (data.metadata) response.data.attributes.metadata = data.metadata;\n\n return response;\n }\n}\n","export enum StripeProductFields {\n stripeProductId = \"stripeProductId\",\n name = \"name\",\n status = \"status\",\n prices = \"prices\",\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../../core\";\nimport { StripeProductInput, StripeProductInterface } from \"./stripe-product.interface\";\n\nexport class StripeProductService extends AbstractService {\n /**\n * List all products (admin)\n */\n static async listProducts(params?: {\n active?: boolean;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<StripeProductInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeProduct,\n });\n\n if (params?.active !== undefined) {\n endpoint.addAdditionalParam(\"active\", params.active.toString());\n }\n\n return this.callApi({\n type: Modules.StripeProduct,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params?.next,\n previous: params?.prev,\n });\n }\n\n /**\n * Get a specific product by ID (admin)\n */\n static async getProduct(params: { id: string }): Promise<StripeProductInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeProduct,\n id: params.id,\n });\n\n return this.callApi<StripeProductInterface>({\n type: Modules.StripeProduct,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Create a new product (admin)\n */\n static async createProduct(params: StripeProductInput): Promise<StripeProductInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeProduct,\n });\n\n return this.callApi<StripeProductInterface>({\n type: Modules.StripeProduct,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: params,\n });\n }\n\n /**\n * Update an existing product (admin)\n */\n static async updateProduct(params: StripeProductInput): Promise<StripeProductInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeProduct,\n id: params.id,\n });\n\n return this.callApi<StripeProductInterface>({\n type: Modules.StripeProduct,\n method: HttpMethod.PUT,\n endpoint: endpoint.generate(),\n input: params,\n });\n }\n\n /**\n * Archive a product (admin)\n */\n static async archiveProduct(params: { id: string }): Promise<StripeProductInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeProduct,\n id: params.id,\n childEndpoint: \"archive\",\n });\n\n return this.callApi<StripeProductInterface>({\n type: Modules.StripeProduct,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Reactivate a product (admin) - sets active to true\n */\n static async reactivateProduct(params: { id: string }): Promise<StripeProductInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeProduct,\n id: params.id,\n childEndpoint: \"archive\",\n });\n\n return this.callApi<StripeProductInterface>({\n type: Modules.StripeProduct,\n method: HttpMethod.DELETE,\n endpoint: endpoint.generate(),\n });\n }\n}\n","import { CreditCardIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../../permissions\";\nimport { StripeProduct } from \"./data\";\n\nexport const StripeProductModule = (factory: ModuleFactory) =>\n factory({\n name: \"stripe-products\",\n // The administration route this entity is served at. `name` above stays the\n // API endpoint; this drives generateUrl / rewriteUrl / EditorSheet\n // navigation, so it must match the real Next.js route or RoundPageContainer\n // rewrites the URL to a 404 on the first tab change.\n pageUrl: \"/administration/products\",\n model: StripeProduct,\n moduleId: \"f8c4a1e9-3b2d-4f7a-9e5c-1d8a6b3c9f2e\",\n icon: CreditCardIcon,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../../core\";\nimport { StripePriceInterface } from \"../../stripe-price\";\nimport {\n StripeSubscriptionDiscount,\n StripeSubscriptionInput,\n StripeSubscriptionInterface,\n SubscriptionStatus,\n} from \"./stripe-subscription.interface\";\n\nexport class StripeSubscription extends AbstractApiData implements StripeSubscriptionInterface {\n private _stripeSubscriptionId?: string;\n private _status?: SubscriptionStatus;\n private _currentPeriodStart?: Date;\n private _currentPeriodEnd?: Date;\n private _cancelAtPeriodEnd: boolean = false;\n private _canceledAt?: Date;\n private _trialStart?: Date;\n private _trialEnd?: Date;\n private _price?: StripePriceInterface;\n private _discount?: StripeSubscriptionDiscount;\n\n get stripeSubscriptionId(): string {\n if (!this._stripeSubscriptionId) throw new Error(\"stripeSubscriptionId is not defined\");\n return this._stripeSubscriptionId;\n }\n\n get status(): SubscriptionStatus {\n if (!this._status) throw new Error(\"status is not defined\");\n return this._status;\n }\n\n get currentPeriodStart(): Date {\n if (!this._currentPeriodStart) throw new Error(\"currentPeriodStart is not defined\");\n return this._currentPeriodStart;\n }\n\n get currentPeriodEnd(): Date {\n if (!this._currentPeriodEnd) throw new Error(\"currentPeriodEnd is not defined\");\n return this._currentPeriodEnd;\n }\n\n get cancelAtPeriodEnd(): boolean {\n return this._cancelAtPeriodEnd;\n }\n\n get canceledAt(): Date | undefined {\n return this._canceledAt;\n }\n\n get trialStart(): Date | undefined {\n return this._trialStart;\n }\n\n get trialEnd(): Date | undefined {\n return this._trialEnd;\n }\n\n get price(): StripePriceInterface | undefined {\n return this._price;\n }\n\n get discount(): StripeSubscriptionDiscount | undefined {\n return this._discount;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._stripeSubscriptionId = data.jsonApi.attributes.stripeSubscriptionId;\n this._status = data.jsonApi.attributes.status;\n\n this._currentPeriodStart = data.jsonApi.attributes.currentPeriodStart\n ? new Date(data.jsonApi.attributes.currentPeriodStart)\n : undefined;\n this._currentPeriodEnd = data.jsonApi.attributes.currentPeriodEnd\n ? new Date(data.jsonApi.attributes.currentPeriodEnd)\n : undefined;\n\n this._cancelAtPeriodEnd = data.jsonApi.attributes.cancelAtPeriodEnd ?? false;\n this._canceledAt = data.jsonApi.attributes.canceledAt ? new Date(data.jsonApi.attributes.canceledAt) : undefined;\n\n this._trialStart = data.jsonApi.attributes.trialStart ? new Date(data.jsonApi.attributes.trialStart) : undefined;\n this._trialEnd = data.jsonApi.attributes.trialEnd ? new Date(data.jsonApi.attributes.trialEnd) : undefined;\n\n const attributes = data.jsonApi.attributes;\n const percentOff = attributes.discountPercentOff ?? undefined;\n const amountOff = attributes.discountAmountOff ?? undefined;\n this._discount =\n attributes.discountDuration && (percentOff !== undefined || amountOff !== undefined)\n ? {\n percentOff,\n amountOff,\n currency: attributes.discountCurrency ?? undefined,\n duration: attributes.discountDuration,\n durationInMonths: attributes.discountDurationInMonths ?? undefined,\n end: attributes.discountEnd ? new Date(attributes.discountEnd) : undefined,\n }\n : undefined;\n\n // Hydrate price relationship\n this._price = this._readIncluded(data, \"stripePrice\", Modules.StripePrice) as StripePriceInterface;\n\n return this;\n }\n\n createJsonApi(data: StripeSubscriptionInput): any {\n const response: any = {\n data: {\n type: Modules.StripeSubscription.name,\n id: data.id,\n attributes: {},\n },\n };\n\n // CREATE: priceId goes to relationships\n if (data.priceId) {\n response.data.relationships = {\n stripePrice: {\n data: {\n type: Modules.StripePrice.name,\n id: data.priceId,\n },\n },\n };\n }\n\n // CHANGE-PLAN: newPriceId goes to attributes.priceId\n if (data.newPriceId) {\n response.data.attributes.priceId = data.newPriceId;\n }\n\n // CANCEL: cancelImmediately goes to attributes\n if (data.cancelImmediately !== undefined) {\n response.data.attributes.cancelImmediately = data.cancelImmediately;\n }\n\n // Shared optional fields\n if (data.quantity !== undefined) {\n response.data.attributes.quantity = data.quantity;\n }\n\n if (data.trialPeriodDays !== undefined) {\n response.data.attributes.trialPeriodDays = data.trialPeriodDays;\n }\n\n if (data.paymentMethodId) {\n response.data.attributes.paymentMethodId = data.paymentMethodId;\n }\n\n if (data.metadata) {\n response.data.attributes.metadata = data.metadata;\n }\n\n if (data.promotionCode) {\n response.data.attributes.promotionCode = data.promotionCode;\n }\n return response;\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../../core\";\nimport { ProrationPreviewInterface } from \"../../stripe-invoice/data/stripe-invoice.interface\";\nimport {\n StripeSubscriptionCreateMeta,\n StripeSubscriptionCreateResponse,\n StripeSubscriptionInput,\n StripeSubscriptionInterface,\n} from \"./stripe-subscription.interface\";\n\n/**\n * Customer-facing billing service for managing subscriptions, payments, and usage\n */\nexport class StripeSubscriptionService extends AbstractService {\n // ============================================================================\n // Subscription Methods\n // ============================================================================\n\n /**\n * List all subscriptions for the current user\n */\n static async listSubscriptions(params?: {\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<StripeSubscriptionInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeSubscription,\n });\n\n return this.callApi({\n type: Modules.StripeSubscription,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params?.next,\n previous: params?.prev,\n });\n }\n\n /**\n * Get a specific subscription by ID\n */\n static async getSubscription(params: { subscriptionId: string }): Promise<StripeSubscriptionInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeSubscription,\n id: params.subscriptionId,\n });\n\n return this.callApi<StripeSubscriptionInterface>({\n type: Modules.StripeSubscription,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Create a new subscription\n * Returns subscription data along with meta containing SCA payment confirmation details\n */\n static async createSubscription(params: StripeSubscriptionInput): Promise<StripeSubscriptionCreateResponse> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeSubscription,\n });\n\n const result = await this.callApiWithMeta<StripeSubscriptionInterface>({\n type: Modules.StripeSubscription,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: params,\n // The wizard reads 410 / 409 / 402 / 422 itself; a global handler must not swallow them\n suppressGlobalError: true,\n });\n\n return {\n subscription: result.data,\n meta: (result.meta as StripeSubscriptionCreateMeta) ?? {\n clientSecret: null,\n paymentIntentId: null,\n requiresAction: false,\n },\n };\n }\n\n /**\n * Change the plan of an existing subscription\n */\n static async changePlan(params: StripeSubscriptionInput): Promise<StripeSubscriptionInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeSubscription,\n id: params.id,\n childEndpoint: \"change-plan\",\n });\n\n return this.callApi<StripeSubscriptionInterface>({\n type: Modules.StripeSubscription,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: params,\n // The wizard reads 410 / 409 / 402 / 422 itself; a global handler must not swallow them\n suppressGlobalError: true,\n });\n }\n\n /**\n * Change the plan of an existing subscription, with the payment meta.\n *\n * A trial upgrade charges at once; when the card asks for 3D Secure the API\n * answers with `meta.requiresAction` and the `clientSecret` to confirm in the\n * browser. Without that meta the change needs nothing more.\n */\n static async changePlanWithMeta(params: StripeSubscriptionInput): Promise<StripeSubscriptionCreateResponse> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeSubscription,\n id: params.id,\n childEndpoint: \"change-plan\",\n });\n\n const result = await this.callApiWithMeta<StripeSubscriptionInterface>({\n type: Modules.StripeSubscription,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: params,\n // The wizard reads 410 / 409 / 402 / 422 itself; a global handler must not swallow them\n suppressGlobalError: true,\n });\n\n return {\n subscription: result.data,\n meta: (result.meta as StripeSubscriptionCreateMeta) ?? {\n clientSecret: null,\n paymentIntentId: null,\n requiresAction: false,\n },\n };\n }\n\n /**\n * Get a proration preview for a plan change\n */\n static async getProrationPreview(params: {\n subscriptionId: string;\n newPriceId: string;\n quantity?: number;\n }): Promise<ProrationPreviewInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeSubscription,\n id: params.subscriptionId,\n childEndpoint: \"proration-preview\",\n });\n\n endpoint.addAdditionalParam(\"priceId\", params.newPriceId);\n if (params.quantity) {\n endpoint.addAdditionalParam(\"quantity\", params.quantity.toString());\n }\n\n return this.callApi({\n type: Modules.StripeSubscription,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Cancel a subscription\n */\n static async cancelSubscription(params: StripeSubscriptionInput): Promise<StripeSubscriptionInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeSubscription,\n id: params.id,\n childEndpoint: \"cancel\",\n });\n\n return this.callApi<StripeSubscriptionInterface>({\n type: Modules.StripeSubscription,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: params,\n });\n }\n\n /**\n * Sync a subscription with the latest data from Stripe\n * This is useful after payment confirmation to get the updated status\n */\n static async syncSubscription(params: { subscriptionId: string }): Promise<StripeSubscriptionInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeSubscription,\n id: params.subscriptionId,\n childEndpoint: \"sync\",\n });\n\n return this.callApi<StripeSubscriptionInterface>({\n type: Modules.StripeSubscription,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n // The wizard logs a failed sync and carries on (the webhook catches up): no global error after a paid subscription\n suppressGlobalError: true,\n });\n }\n}\n","import { ModuleFactory } from \"../../../permissions\";\nimport { StripeSubscription } from \"./data\";\n\nexport const StripeSubscriptionModule = (factory: ModuleFactory) =>\n factory({\n name: \"stripe-subscriptions\",\n pageUrl: \"/stripe-subscriptions\",\n model: StripeSubscription,\n moduleId: \"5e8797ef-650b-4dd5-ac79-a2e530a6c7ba\",\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../../core\";\nimport { ReportUsageInput, StripeUsageInterface } from \"./stripe-usage.interface\";\n\nexport class StripeUsage extends AbstractApiData implements StripeUsageInterface {\n private _subscriptionId?: string;\n private _meterId?: string;\n private _meterEventName?: string;\n private _stripeEventId?: string;\n private _quantity?: number;\n private _timestamp?: Date;\n\n get subscriptionId(): string {\n if (!this._subscriptionId) throw new Error(\"subscriptionId is not defined\");\n return this._subscriptionId;\n }\n\n get meterId(): string {\n if (!this._meterId) throw new Error(\"meterId is not defined\");\n return this._meterId;\n }\n\n get meterEventName(): string {\n if (!this._meterEventName) throw new Error(\"meterEventName is not defined\");\n return this._meterEventName;\n }\n\n get stripeEventId(): string {\n if (!this._stripeEventId) throw new Error(\"stripeEventId is not defined\");\n return this._stripeEventId;\n }\n\n get quantity(): number {\n if (this._quantity === undefined) throw new Error(\"quantity is not defined\");\n return this._quantity;\n }\n\n get timestamp(): Date | undefined {\n return this._timestamp;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._subscriptionId = data.jsonApi.attributes.subscriptionId;\n this._meterId = data.jsonApi.attributes.meterId;\n this._meterEventName = data.jsonApi.attributes.meterEventName;\n this._stripeEventId = data.jsonApi.attributes.stripeEventId;\n this._quantity = data.jsonApi.attributes.quantity;\n this._timestamp = data.jsonApi.attributes.timestamp ? new Date(data.jsonApi.attributes.timestamp) : undefined;\n\n return this;\n }\n\n createJsonApi(data: ReportUsageInput): any {\n const response: any = {\n data: {\n type: Modules.StripeUsage.name,\n attributes: {\n subscriptionItemId: data.subscriptionItemId,\n quantity: data.quantity,\n },\n },\n };\n\n if (data.timestamp !== undefined) response.data.attributes.timestamp = data.timestamp;\n if (data.action !== undefined) response.data.attributes.action = data.action;\n\n return response;\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../../core\";\nimport {\n MeterInterface,\n MeterSummaryInterface,\n ReportUsageInput,\n StripeUsageInterface,\n UsageSummaryInterface,\n} from \"./stripe-usage.interface\";\n\n/**\n * Service for managing Stripe usage tracking\n */\nexport class StripeUsageService extends AbstractService {\n // ============================================================================\n // Meter Methods\n // ============================================================================\n\n /**\n * List all available usage meters\n */\n static async listMeters(params?: { next?: NextRef; prev?: PreviousRef }): Promise<MeterInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeUsage,\n childEndpoint: \"meters\",\n });\n\n return this.callApi({\n type: Modules.StripeUsage,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params?.next,\n previous: params?.prev,\n });\n }\n\n /**\n * Get meter summaries for a specific time period\n */\n static async getMeterSummaries(params: {\n meterId: string;\n startTime: Date;\n endTime: Date;\n }): Promise<MeterSummaryInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeUsage,\n childEndpoint: `meters/${params.meterId}/summaries`,\n });\n\n endpoint.addAdditionalParam(\"startTime\", params.startTime.toISOString());\n endpoint.addAdditionalParam(\"endTime\", params.endTime.toISOString());\n\n return this.callApi({\n type: Modules.StripeUsage,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n // ============================================================================\n // Usage Record Methods\n // ============================================================================\n\n /**\n * Report usage for a subscription item\n */\n static async reportUsage(params: ReportUsageInput): Promise<StripeUsageInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeUsage,\n });\n\n return this.callApi<StripeUsageInterface>({\n type: Modules.StripeUsage,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: params,\n });\n }\n\n /**\n * List usage records for a subscription item\n */\n static async listUsageRecords(params: {\n subscriptionItemId: string;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<StripeUsageInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeUsage,\n });\n\n endpoint.addAdditionalParam(\"subscriptionItemId\", params.subscriptionItemId);\n\n return this.callApi({\n type: Modules.StripeUsage,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params?.next,\n previous: params?.prev,\n });\n }\n\n /**\n * Get usage summary for a subscription item\n */\n static async getUsageSummary(params: {\n subscriptionItemId: string;\n start?: Date;\n end?: Date;\n }): Promise<UsageSummaryInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeUsage,\n childEndpoint: \"summary\",\n });\n\n endpoint.addAdditionalParam(\"subscriptionItemId\", params.subscriptionItemId);\n if (params.start) {\n endpoint.addAdditionalParam(\"start\", params.start.toISOString());\n }\n if (params.end) {\n endpoint.addAdditionalParam(\"end\", params.end.toISOString());\n }\n\n return this.callApi({\n type: Modules.StripeUsage,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n}\n","import { ModuleFactory } from \"../../../permissions\";\nimport { StripeUsage } from \"./data/stripe-usage\";\n\nexport const StripeUsageModule = (factory: ModuleFactory) =>\n factory({\n name: \"stripe-usage-records\",\n pageUrl: \"/stripe-usage-records\",\n model: StripeUsage,\n moduleId: \"c2e9f4a6-1d7b-4e3a-8f5c-9b2d6a1e7f3c\",\n });\n","import { Modules } from \"../../../../core\";\nimport { JsonApiPost } from \"../../../../unified/JsonApiRequest\";\nimport { PromotionCodeValidationResult } from \"./stripe-promotion-code.interface\";\nimport { StripePromotionCode } from \"./stripe-promotion-code\";\n\nconst STRIPE_PROMOTION_CODE_ENDPOINT = \"stripe-promotion-codes\";\n\n/**\n * Service for validating Stripe promotion codes\n */\nexport class StripePromotionCodeService {\n /**\n * Validate a promotion code against Stripe\n *\n * @param params.code - The promotion code to validate (e.g., \"SAVE20\")\n * @param params.stripePriceId - Optional price ID to check product restrictions\n * @param params.language - Language code for the request\n * @returns Validation result with discount details if valid\n */\n static async validatePromotionCode(params: {\n code: string;\n stripePriceId?: string;\n language?: string;\n }): Promise<PromotionCodeValidationResult> {\n const response = await JsonApiPost({\n classKey: Modules.StripePromotionCode,\n endpoint: `${STRIPE_PROMOTION_CODE_ENDPOINT}/validate`,\n body: {\n data: {\n type: STRIPE_PROMOTION_CODE_ENDPOINT,\n attributes: {\n code: params.code,\n stripePriceId: params.stripePriceId,\n },\n },\n },\n overridesJsonApiCreation: true,\n language: params.language ?? \"en\",\n });\n\n if (!response.ok) {\n return {\n valid: false,\n code: params.code,\n // No English fallback here: a service cannot translate, so the caller words\n // the missing message in the active language.\n errorMessage: response.error || undefined,\n };\n }\n\n // The response is hydrated as a StripePromotionCode model\n const data = response.data as StripePromotionCode;\n\n return {\n valid: data.valid,\n promotionCodeId: data.promotionCodeId,\n code: data.code,\n discountType: data.discountType,\n discountValue: data.discountValue,\n currency: data.currency,\n duration: data.duration,\n durationInMonths: data.durationInMonths,\n errorMessage: data.errorMessage,\n };\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../../core\";\nimport { PromotionCodeValidationResult } from \"./stripe-promotion-code.interface\";\n\nexport class StripePromotionCode extends AbstractApiData implements PromotionCodeValidationResult {\n private _valid: boolean = false;\n private _promotionCodeId?: string;\n private _code: string = \"\";\n private _discountType?: \"percent_off\" | \"amount_off\";\n private _discountValue?: number;\n private _currency?: string;\n private _duration?: \"forever\" | \"once\" | \"repeating\";\n private _durationInMonths?: number;\n private _errorMessage?: string;\n\n get valid(): boolean {\n return this._valid;\n }\n\n get promotionCodeId(): string | undefined {\n return this._promotionCodeId;\n }\n\n get code(): string {\n return this._code;\n }\n\n get discountType(): \"percent_off\" | \"amount_off\" | undefined {\n return this._discountType;\n }\n\n get discountValue(): number | undefined {\n return this._discountValue;\n }\n\n get currency(): string | undefined {\n return this._currency;\n }\n\n get duration(): \"forever\" | \"once\" | \"repeating\" | undefined {\n return this._duration;\n }\n\n get durationInMonths(): number | undefined {\n return this._durationInMonths;\n }\n\n get errorMessage(): string | undefined {\n return this._errorMessage;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._valid = data.jsonApi.attributes.valid ?? false;\n this._promotionCodeId = data.jsonApi.attributes.promotionCodeId;\n this._code = data.jsonApi.attributes.code ?? \"\";\n this._discountType = data.jsonApi.attributes.discountType;\n this._discountValue = data.jsonApi.attributes.discountValue;\n this._currency = data.jsonApi.attributes.currency;\n this._duration = data.jsonApi.attributes.duration;\n this._durationInMonths = data.jsonApi.attributes.durationInMonths;\n this._errorMessage = data.jsonApi.attributes.errorMessage;\n\n return this;\n }\n}\n","import { ModuleFactory } from \"../../../permissions\";\nimport { StripePromotionCode } from \"./data/stripe-promotion-code\";\n\nexport const StripePromotionCodeModule = (factory: ModuleFactory) =>\n factory({\n name: \"stripe-promotion-codes\",\n pageUrl: \"/stripe-promotion-codes\",\n model: StripePromotionCode,\n moduleId: \"b8e4f6a2-9d0c-5b3f-c7e8-4g2d9f0a5c6e\",\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../../core\";\nimport { StripeCouponCodeInput, StripeCouponCodeInterface } from \"./stripe-coupon-code.interface\";\nimport {\n StripeCouponDiscountType,\n StripeCouponDurationType,\n StripeCouponInput,\n StripeCouponInterface,\n StripeCouponRenameInput,\n} from \"./stripe-coupon.interface\";\n\nexport class StripeCoupon extends AbstractApiData implements StripeCouponInterface {\n private _stripeCouponId?: string;\n private _name?: string;\n private _discountType?: StripeCouponDiscountType;\n private _percentOff?: number;\n private _amountOff?: number;\n private _currency?: string;\n private _duration?: StripeCouponDurationType;\n private _durationInMonths?: number;\n private _stripeProductIds: string[] = [];\n private _redeemBy?: Date;\n private _maxRedemptions?: number;\n private _timesRedeemed: number = 0;\n private _valid: boolean = true;\n private _codes: StripeCouponCodeInterface[] = [];\n\n get stripeCouponId(): string {\n if (!this._stripeCouponId) throw new Error(\"stripeCouponId is not defined\");\n return this._stripeCouponId;\n }\n\n get name(): string {\n return this._name ?? \"\";\n }\n\n get discountType(): StripeCouponDiscountType {\n if (!this._discountType) throw new Error(\"discountType is not defined\");\n return this._discountType;\n }\n\n get percentOff(): number | undefined {\n return this._percentOff;\n }\n\n get amountOff(): number | undefined {\n return this._amountOff;\n }\n\n get currency(): string | undefined {\n return this._currency;\n }\n\n get duration(): StripeCouponDurationType {\n if (!this._duration) throw new Error(\"duration is not defined\");\n return this._duration;\n }\n\n get durationInMonths(): number | undefined {\n return this._durationInMonths;\n }\n\n get stripeProductIds(): string[] {\n return this._stripeProductIds;\n }\n\n get redeemBy(): Date | undefined {\n return this._redeemBy;\n }\n\n get maxRedemptions(): number | undefined {\n return this._maxRedemptions;\n }\n\n get timesRedeemed(): number {\n return this._timesRedeemed;\n }\n\n get valid(): boolean {\n return this._valid;\n }\n\n get codes(): StripeCouponCodeInterface[] {\n return this._codes;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes;\n\n this._stripeCouponId = attributes.stripeCouponId;\n this._name = attributes.name;\n this._discountType = attributes.discountType;\n this._percentOff = attributes.percentOff ?? undefined;\n this._amountOff = attributes.amountOff ?? undefined;\n this._currency = attributes.currency ?? undefined;\n this._duration = attributes.duration;\n this._durationInMonths = attributes.durationInMonths ?? undefined;\n this._stripeProductIds = attributes.stripeProductIds ?? [];\n this._redeemBy = attributes.redeemBy ? new Date(attributes.redeemBy) : undefined;\n this._maxRedemptions = attributes.maxRedemptions ?? undefined;\n this._timesRedeemed = attributes.timesRedeemed ?? 0;\n this._valid = attributes.valid ?? true;\n\n this._codes =\n (this._readIncluded(data, \"codes\", Modules.StripeCouponCode) as StripeCouponCodeInterface[] | undefined) ?? [];\n\n return this;\n }\n\n createJsonApi(data: StripeCouponInput): any {\n const response: any = {\n data: {\n type: Modules.StripeCoupon.name,\n id: data.id,\n attributes: {\n name: data.name,\n discountType: data.discountType,\n duration: data.duration,\n firstCode: StripeCoupon.codeAttributes(data.firstCode),\n },\n },\n };\n\n const attributes = response.data.attributes;\n if (data.stripeCouponId) attributes.stripeCouponId = data.stripeCouponId;\n if (data.discountType === \"percent_off\") attributes.percentOff = data.percentOff;\n else {\n attributes.amountOff = data.amountOff;\n attributes.currency = data.currency;\n }\n if (data.duration === \"repeating\") attributes.durationInMonths = data.durationInMonths;\n if (data.productIds && data.productIds.length > 0) attributes.productIds = data.productIds;\n if (data.redeemBy !== undefined) attributes.redeemBy = data.redeemBy.toISOString();\n if (data.maxRedemptions !== undefined) attributes.maxRedemptions = data.maxRedemptions;\n\n return response;\n }\n\n /**\n * Rename is the only change Stripe allows on a coupon after creation.\n * Dedicated method so the service never needs a generic override.\n */\n createRenameJsonApi(data: StripeCouponRenameInput): any {\n return {\n data: {\n type: Modules.StripeCoupon.name,\n id: data.id,\n attributes: { name: data.name },\n },\n };\n }\n\n /** Attributes of a code, shared by the coupon's first code and StripeCouponCode.createJsonApi. */\n static codeAttributes(code: Omit<StripeCouponCodeInput, \"id\" | \"couponId\">): Record<string, any> {\n const attributes: Record<string, any> = {};\n if (code.code) attributes.code = code.code;\n if (code.firstTimeTransaction !== undefined) attributes.firstTimeTransaction = code.firstTimeTransaction;\n if (code.minimumAmount !== undefined) attributes.minimumAmount = code.minimumAmount;\n if (code.minimumAmountCurrency) attributes.minimumAmountCurrency = code.minimumAmountCurrency;\n if (code.stripeCustomerId) attributes.stripeCustomerId = code.stripeCustomerId;\n if (code.expiresAt !== undefined) attributes.expiresAt = code.expiresAt.toISOString();\n if (code.maxRedemptions !== undefined) attributes.maxRedemptions = code.maxRedemptions;\n if (code.automatic !== undefined) attributes.automatic = code.automatic;\n return attributes;\n }\n}\n","export enum StripeCouponFields {\n name = \"name\",\n discount = \"discount\",\n places = \"places\",\n codes = \"codes\",\n status = \"status\",\n}\n","import { TicketPercentIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../../permissions\";\nimport { StripeCoupon } from \"./data/stripe-coupon\";\n\n/** The coupon endpoint's non-resource segment that re-reads its counts from Stripe. */\nexport const STRIPE_COUPON_REFRESH_SEGMENT = \"refresh\";\n\n/**\n * Route of a coupon's own page (`/administration/coupons/<id>`). It is not the\n * module's pageUrl: there is no coupons list route, the list is the Codes tab of\n * the products page. Links to one coupon build on this instead.\n */\nexport const STRIPE_COUPON_DETAIL_PAGE_URL = \"/administration/coupons\";\n\nexport const StripeCouponModule = (factory: ModuleFactory) =>\n factory({\n name: \"stripe-coupons\",\n // The coupons list lives on the products page (Codes tab), so the module\n // points there, same value as StripeProductModule. Links to a single coupon\n // use STRIPE_COUPON_DETAIL_PAGE_URL.\n pageUrl: \"/administration/products\",\n model: StripeCoupon,\n moduleId: \"50bdb0d2-539c-4358-b609-d6beab248bdb\",\n icon: TicketPercentIcon,\n });\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../../core\";\nimport { STRIPE_COUPON_REFRESH_SEGMENT } from \"../stripe-coupon.module\";\nimport { StripeCoupon } from \"./stripe-coupon\";\nimport { StripeCouponInput, StripeCouponInterface, StripeCouponRenameInput } from \"./stripe-coupon.interface\";\n\n/**\n * Admin billing service for managing coupons\n */\nexport class StripeCouponService extends AbstractService {\n /**\n * List all coupons with their codes (admin)\n */\n static async listCoupons(params?: { next?: NextRef; prev?: PreviousRef }): Promise<StripeCouponInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCoupon,\n });\n\n return this.callApi({\n type: Modules.StripeCoupon,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params?.next,\n previous: params?.prev,\n });\n }\n\n /**\n * Get a specific coupon by ID (admin)\n */\n static async getCoupon(params: { id: string }): Promise<StripeCouponInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCoupon,\n id: params.id,\n });\n\n return this.callApi<StripeCouponInterface>({\n type: Modules.StripeCoupon,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Create a coupon and its first code (admin)\n */\n static async createCoupon(params: StripeCouponInput): Promise<StripeCouponInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCoupon,\n });\n\n return this.callApi<StripeCouponInterface>({\n type: Modules.StripeCoupon,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: params,\n });\n }\n\n /**\n * Rename a coupon (admin). The only coupon field Stripe allows to change.\n */\n static async renameCoupon(params: StripeCouponRenameInput): Promise<StripeCouponInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCoupon,\n id: params.id,\n });\n\n return this.callApi<StripeCouponInterface>({\n type: Modules.StripeCoupon,\n method: HttpMethod.PATCH,\n endpoint: endpoint.generate(),\n input: new StripeCoupon().createRenameJsonApi(params),\n overridesJsonApiCreation: true,\n });\n }\n\n /**\n * Re-read the coupon and its codes from Stripe and store the counts (admin)\n */\n static async refreshCounts(params: { id: string }): Promise<StripeCouponInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCoupon,\n id: params.id,\n childEndpoint: STRIPE_COUPON_REFRESH_SEGMENT,\n });\n\n return this.callApi<StripeCouponInterface>({\n type: Modules.StripeCoupon,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n });\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../../core\";\nimport {\n StripeCouponCodeFlagsInput,\n StripeCouponCodeInput,\n StripeCouponCodeInterface,\n} from \"./stripe-coupon-code.interface\";\nimport { StripeCouponInterface } from \"./stripe-coupon.interface\";\nimport { StripeCoupon } from \"./stripe-coupon\";\n\nexport class StripeCouponCode extends AbstractApiData implements StripeCouponCodeInterface {\n private _stripePromotionCodeId?: string;\n private _code: string = \"\";\n private _active: boolean = true;\n private _automatic: boolean = false;\n private _firstTimeTransaction: boolean = false;\n private _minimumAmount?: number;\n private _minimumAmountCurrency?: string;\n private _stripeCustomerId?: string;\n private _expiresAt?: Date;\n private _maxRedemptions?: number;\n private _timesRedeemed: number = 0;\n private _coupon?: StripeCouponInterface;\n\n get stripePromotionCodeId(): string {\n if (!this._stripePromotionCodeId) throw new Error(\"stripePromotionCodeId is not defined\");\n return this._stripePromotionCodeId;\n }\n\n get code(): string {\n return this._code;\n }\n\n get active(): boolean {\n return this._active;\n }\n\n get automatic(): boolean {\n return this._automatic;\n }\n\n get firstTimeTransaction(): boolean {\n return this._firstTimeTransaction;\n }\n\n get minimumAmount(): number | undefined {\n return this._minimumAmount;\n }\n\n get minimumAmountCurrency(): string | undefined {\n return this._minimumAmountCurrency;\n }\n\n get stripeCustomerId(): string | undefined {\n return this._stripeCustomerId;\n }\n\n get expiresAt(): Date | undefined {\n return this._expiresAt;\n }\n\n get maxRedemptions(): number | undefined {\n return this._maxRedemptions;\n }\n\n get timesRedeemed(): number {\n return this._timesRedeemed;\n }\n\n get coupon(): StripeCouponInterface | undefined {\n return this._coupon;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes;\n\n this._stripePromotionCodeId = attributes.stripePromotionCodeId;\n this._code = attributes.code ?? \"\";\n this._active = attributes.active ?? true;\n this._automatic = attributes.automatic ?? false;\n this._firstTimeTransaction = attributes.firstTimeTransaction ?? false;\n this._minimumAmount = attributes.minimumAmount ?? undefined;\n this._minimumAmountCurrency = attributes.minimumAmountCurrency ?? undefined;\n this._stripeCustomerId = attributes.stripeCustomerId ?? undefined;\n this._expiresAt = attributes.expiresAt ? new Date(attributes.expiresAt) : undefined;\n this._maxRedemptions = attributes.maxRedemptions ?? undefined;\n this._timesRedeemed = attributes.timesRedeemed ?? 0;\n\n this._coupon = this._readIncluded(data, \"coupon\", Modules.StripeCoupon) as StripeCouponInterface | undefined;\n\n return this;\n }\n\n createJsonApi(data: StripeCouponCodeInput): any {\n const { id, couponId, ...code } = data;\n\n return {\n data: {\n type: Modules.StripeCouponCode.name,\n id,\n attributes: StripeCoupon.codeAttributes(code),\n relationships: {\n coupon: {\n data: {\n type: Modules.StripeCoupon.name,\n id: couponId,\n },\n },\n },\n },\n };\n }\n\n /**\n * PATCH body for the flags a code can still change after creation.\n * Only the flags that are set are sent, so switching one never touches the other.\n */\n createFlagsJsonApi(data: StripeCouponCodeFlagsInput): any {\n const attributes: Record<string, boolean> = {};\n if (data.active !== undefined) attributes.active = data.active;\n if (data.automatic !== undefined) attributes.automatic = data.automatic;\n\n return {\n data: {\n type: Modules.StripeCouponCode.name,\n id: data.id,\n attributes,\n },\n };\n }\n}\n","export enum StripeCouponCodeFields {\n code = \"code\",\n places = \"places\",\n expiresAt = \"expiresAt\",\n automatic = \"automatic\",\n status = \"status\",\n actions = \"actions\",\n}\n","import { ModuleFactory } from \"../../../permissions\";\nimport { StripeCouponCode } from \"./data/stripe-coupon-code\";\n\n/** The code endpoint's segment that returns the automatic code for a price. */\nexport const STRIPE_COUPON_CODE_AUTOMATIC_SEGMENT = \"automatic\";\n\nexport const StripeCouponCodeModule = (factory: ModuleFactory) =>\n factory({\n name: \"stripe-coupon-codes\",\n // Codes are listed on the products page (Codes tab), same value as\n // StripeProductModule. A code has no page of its own.\n pageUrl: \"/administration/products\",\n model: StripeCouponCode,\n moduleId: \"85feb4c7-ff22-416a-b856-8dafcf28ff26\",\n });\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../../core\";\nimport { STRIPE_COUPON_CODE_AUTOMATIC_SEGMENT } from \"../stripe-coupon-code.module\";\nimport { StripeCouponCode } from \"./stripe-coupon-code\";\nimport {\n StripeCouponCodeFlagsInput,\n StripeCouponCodeInput,\n StripeCouponCodeInterface,\n} from \"./stripe-coupon-code.interface\";\n\n/**\n * Billing service for the customer-facing codes of a coupon\n */\nexport class StripeCouponCodeService extends AbstractService {\n /**\n * List the codes of a coupon (admin)\n */\n static async listByCoupon(params: {\n couponId: string;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<StripeCouponCodeInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCoupon,\n id: params.couponId,\n childEndpoint: Modules.StripeCouponCode,\n });\n\n return this.callApi({\n type: Modules.StripeCouponCode,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n previous: params.prev,\n });\n }\n\n /**\n * Create a code on an existing coupon (admin)\n */\n static async createCode(params: StripeCouponCodeInput): Promise<StripeCouponCodeInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCouponCode,\n });\n\n return this.callApi<StripeCouponCodeInterface>({\n type: Modules.StripeCouponCode,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: params,\n });\n }\n\n /**\n * Switch \"Apply automatically\" and / or deactivate / reactivate a code (admin)\n */\n static async updateFlags(params: StripeCouponCodeFlagsInput): Promise<StripeCouponCodeInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCouponCode,\n id: params.id,\n });\n\n return this.callApi<StripeCouponCodeInterface>({\n type: Modules.StripeCouponCode,\n method: HttpMethod.PATCH,\n endpoint: endpoint.generate(),\n input: new StripeCouponCode().createFlagsJsonApi(params),\n overridesJsonApiCreation: true,\n });\n }\n\n /**\n * The code the server would apply automatically for this price at a first\n * payment, or null when none applies (logged-in company administrator)\n */\n static async findAutomatic(params: { priceId: string }): Promise<StripeCouponCodeInterface | null> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.StripeCouponCode,\n id: STRIPE_COUPON_CODE_AUTOMATIC_SEGMENT,\n });\n endpoint.addAdditionalParam(\"priceId\", params.priceId);\n\n const codes = await this.callApi<StripeCouponCodeInterface[]>({\n type: Modules.StripeCouponCode,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n // A failed lookup must reach the wizard, which then refuses to show full price as the truth\n suppressGlobalError: true,\n });\n\n return codes?.[0] ?? null;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../../core\";\nimport { StripeCouponOfferInterface } from \"./stripe-coupon-offer.interface\";\nimport { StripeCouponDiscountType, StripeCouponDurationType } from \"./stripe-coupon.interface\";\n\n/** Read-only public resource: never created or updated from the frontend. */\nexport class StripeCouponOffer extends AbstractApiData implements StripeCouponOfferInterface {\n private _discountType?: StripeCouponDiscountType;\n private _percentOff?: number;\n private _amountOff?: number;\n private _currency?: string;\n private _duration?: StripeCouponDurationType;\n private _durationInMonths?: number;\n private _placesLeft: number = 0;\n private _placesTotal: number = 0;\n\n get discountType(): StripeCouponDiscountType {\n if (!this._discountType) throw new Error(\"discountType is not defined\");\n return this._discountType;\n }\n\n get percentOff(): number | undefined {\n return this._percentOff;\n }\n\n get amountOff(): number | undefined {\n return this._amountOff;\n }\n\n get currency(): string | undefined {\n return this._currency;\n }\n\n get duration(): StripeCouponDurationType {\n if (!this._duration) throw new Error(\"duration is not defined\");\n return this._duration;\n }\n\n get durationInMonths(): number | undefined {\n return this._durationInMonths;\n }\n\n get placesLeft(): number {\n return this._placesLeft;\n }\n\n get placesTotal(): number {\n return this._placesTotal;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes;\n\n this._discountType = attributes.discountType;\n this._percentOff = attributes.percentOff ?? undefined;\n this._amountOff = attributes.amountOff ?? undefined;\n this._currency = attributes.currency ?? undefined;\n this._duration = attributes.duration;\n this._durationInMonths = attributes.durationInMonths ?? undefined;\n this._placesLeft = attributes.placesLeft ?? 0;\n this._placesTotal = attributes.placesTotal ?? 0;\n\n return this;\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"StripeCouponOffer is declared by the API and is read-only\");\n }\n}\n","/**\n * Places left on a code: the smaller of (coupon limit - coupon redeemed) and\n * (code limit - code redeemed), counting only the limits that are set, never\n * below zero. `null` = unlimited. Same rule as the backend `placesLeft`.\n */\ntype Redeemable = { maxRedemptions?: number; timesRedeemed: number };\n\nexport function couponPlacesLeft(code: Redeemable, coupon: Redeemable): number | null {\n const remaining = [code, coupon]\n .filter((item) => typeof item.maxRedemptions === \"number\")\n .map((item) => Math.max(0, (item.maxRedemptions as number) - (item.timesRedeemed ?? 0)));\n\n return remaining.length === 0 ? null : Math.min(...remaining);\n}\n\n/** The smaller of the set limits, or `null` when neither is set. */\nexport function couponPlacesTotal(code: Redeemable, coupon: Redeemable): number | null {\n const limits = [code.maxRedemptions, coupon.maxRedemptions].filter(\n (limit): limit is number => typeof limit === \"number\",\n );\n\n return limits.length === 0 ? null : Math.min(...limits);\n}\n","/** A message translator, as returned by `useTranslations()`. */\nexport type BillingTranslator = (key: string, values?: Record<string, any>) => string;\n\n/**\n * What the billing formatters need from the caller's React context. The\n * formatters are plain functions and cannot call hooks, so a component reads\n * `useI18nLocale()` and `useTranslations()` and passes both here.\n *\n * Both are optional so existing callers keep their output: without a `locale`\n * the number formatters use \"en-US\" and `formatDate` uses \"en-GB\" (via\n * `dateFormatLocale`), without `t` they return the English literals they always\n * returned.\n */\nexport type BillingFormatOptions = {\n /** BCP 47 locale for `Intl` (the app's active locale, from `useI18nLocale()`). */\n locale?: string;\n /** Translator for the words the formatters emit. */\n t?: BillingTranslator;\n};\n\n/** Number and currency locale when none is given. Dates use `dateFormatLocale` instead. */\nexport const DEFAULT_BILLING_LOCALE = \"en-US\";\n","import { StripePriceInterface } from \"../../stripe-price/data/stripe-price.interface\";\nimport { BillingFormatOptions, DEFAULT_BILLING_LOCALE } from \"./format-options\";\n\n/** English output kept for callers that pass no translator. */\nfunction formatIntervalEnglish(price: StripePriceInterface): string {\n if (price.priceType === \"one_time\" || !price.recurring) {\n return \"one-time\";\n }\n\n const { interval, intervalCount } = price.recurring;\n\n if (intervalCount === 1) {\n return `/${interval}`;\n }\n\n // Pluralize the interval for counts > 1\n const pluralInterval =\n interval === \"day\" ? \"days\" : interval === \"week\" ? \"weeks\" : interval === \"month\" ? \"months\" : \"years\";\n return `/${intervalCount} ${pluralInterval}`;\n}\n\n/**\n * Format price recurring interval for display\n * @param price - Stripe price object\n * @param options - `t` translates the interval words; without it the output is English\n * @returns Formatted interval string (e.g., \"/month\", \"/year\", \"/2 weeks\", \"one-time\")\n */\nexport function formatInterval(price: StripePriceInterface, options: BillingFormatOptions = {}): string {\n const { t } = options;\n if (!t) return formatIntervalEnglish(price);\n\n if (price.priceType === \"one_time\" || !price.recurring) {\n return t(\"billing.ui.interval.one_time\");\n }\n\n const { interval, intervalCount } = price.recurring;\n\n if (intervalCount === 1) {\n switch (interval) {\n case \"day\":\n return t(\"billing.ui.interval.per_day\");\n case \"week\":\n return t(\"billing.ui.interval.per_week\");\n case \"month\":\n return t(\"billing.ui.interval.per_month\");\n default:\n return t(\"billing.ui.interval.per_year\");\n }\n }\n\n switch (interval) {\n case \"day\":\n return t(\"billing.ui.interval.per_days\", { count: intervalCount });\n case \"week\":\n return t(\"billing.ui.interval.per_weeks\", { count: intervalCount });\n case \"month\":\n return t(\"billing.ui.interval.per_months\", { count: intervalCount });\n default:\n return t(\"billing.ui.interval.per_years\", { count: intervalCount });\n }\n}\n\n/**\n * Format currency amount from cents to localized currency string\n * @param amount - Amount in cents\n * @param currency - Currency code (e.g., \"USD\", \"EUR\", \"GBP\")\n * @param options - `locale` drives number formatting; defaults to \"en-US\"\n * @returns Formatted currency string (e.g., \"$9.99\", \"€9,99\")\n */\nexport function formatCurrency(\n amount: number | undefined,\n currency: string,\n options: BillingFormatOptions = {},\n): string {\n const { locale } = options;\n // No locale: the historical literal. With one, zero goes through Intl like any amount.\n if (amount === undefined && !locale) return \"$0.00\";\n\n // Convert cents to dollars\n const dollars = (amount ?? 0) / 100;\n\n try {\n return new Intl.NumberFormat(locale ?? DEFAULT_BILLING_LOCALE, {\n style: \"currency\",\n currency: currency.toUpperCase(),\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(dollars);\n } catch (error) {\n console.error(\"Error formatting currency:\", error);\n // Fallback if currency code is invalid\n return `$${dollars.toFixed(2)}`;\n }\n}\n\n/**\n * `value.toFixed(digits)` with the locale's decimal separator (\"12.5\" in English,\n * \"12,5\" in Italian). No grouping, so English output matches `toFixed` exactly.\n */\nexport function formatFixed(value: number, locale: string | undefined, digits: number): string {\n return new Intl.NumberFormat(locale ?? DEFAULT_BILLING_LOCALE, {\n minimumFractionDigits: digits,\n maximumFractionDigits: digits,\n useGrouping: false,\n }).format(value);\n}\n","import { formatCurrency } from \"../../components/utils/currency\";\nimport { BillingFormatOptions, BillingTranslator } from \"../../components/utils/format-options\";\nimport { StripeCouponDiscountType, StripeCouponDurationType } from \"../data/stripe-coupon.interface\";\n\nexport type CouponDiscountShape = {\n discountType: StripeCouponDiscountType;\n percentOff?: number;\n amountOff?: number; // minor units\n currency?: string;\n duration: StripeCouponDurationType;\n durationInMonths?: number;\n};\n\n/** \"20% off forever\", \"€5.00 off for 3 months\", \"€5.00 off once\". */\nexport function formatCouponDiscount(\n coupon: CouponDiscountShape,\n t: BillingTranslator,\n fmt: BillingFormatOptions = {},\n): string {\n const amount =\n coupon.discountType === \"percent_off\"\n ? t(\"billing.admin.coupons.discount.percent\", { value: String(coupon.percentOff ?? 0) })\n : t(\"billing.admin.coupons.discount.amount\", {\n amount: formatCurrency(coupon.amountOff, coupon.currency ?? \"eur\", fmt),\n });\n\n // Literal keys (no template) so the message validator sees every one in use.\n const duration =\n coupon.duration === \"repeating\"\n ? t(\"billing.admin.coupons.discount.repeating\", { months: String(coupon.durationInMonths ?? 0) })\n : coupon.duration === \"once\"\n ? t(\"billing.admin.coupons.discount.once\")\n : t(\"billing.admin.coupons.discount.forever\");\n\n return `${amount} ${duration}`;\n}\n","import { PromotionCodeValidationResult } from \"../../stripe-promotion-code/data/stripe-promotion-code.interface\";\nimport { StripeCouponCodeInterface } from \"../data/stripe-coupon-code.interface\";\n\n/**\n * The automatic code as the wizard's promotion-code state, exactly as if the\n * GM had typed and validated it.\n */\nexport function toPromotionCodeResult(code: StripeCouponCodeInterface): PromotionCodeValidationResult {\n const coupon = code.coupon;\n const discountType = coupon?.discountType;\n\n return {\n valid: true,\n automatic: true,\n promotionCodeId: code.stripePromotionCodeId,\n code: code.code,\n discountType,\n discountValue: discountType === \"amount_off\" ? coupon?.amountOff : coupon?.percentOff,\n currency: discountType === \"amount_off\" ? coupon?.currency : undefined,\n duration: coupon?.duration,\n durationInMonths: coupon?.durationInMonths,\n };\n}\n","import { ModuleFactory } from \"../../../permissions\";\nimport { StripeCouponOffer } from \"./data/stripe-coupon-offer\";\n\nexport const StripeCouponOfferModule = (factory: ModuleFactory) =>\n factory({\n name: \"stripe-coupon-offers\",\n pageUrl: \"/pricing\",\n model: StripeCouponOffer,\n moduleId: \"6859d6cc-b94c-4cf7-8a02-b71ad59d75d2\",\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { AiConnectionInput, AiConnectionInterface } from \"./ai-connection.interface\";\n\nexport class AiConnection extends AbstractApiData implements AiConnectionInterface {\n private _name?: string;\n private _connectionType?: string;\n private _provider?: string;\n private _position?: number;\n private _enabled?: boolean;\n private _model?: string;\n private _url?: string;\n private _region?: string;\n private _instance?: string;\n private _apiVersion?: string;\n private _allowFallbacks?: boolean;\n private _reasoningEffort?: string;\n private _maxOutputTokens?: number;\n private _dimensions?: number;\n private _inputCostPer1MTokens?: number;\n private _outputCostPer1MTokens?: number;\n private _cachedInputCostPer1MTokens?: number;\n private _costPerMinute?: number;\n private _costPerPage?: number;\n private _directUrl?: string;\n private _language?: string;\n private _directFormat?: string;\n private _directProvider?: string;\n private _costPerImage?: number;\n private _negativePrompt?: string;\n private _width?: number;\n private _height?: number;\n private _steps?: number;\n private _cfgScale?: number;\n private _safeMode?: boolean;\n private _hideWatermark?: boolean;\n private _imageFormat?: string;\n private _hasApiKey?: boolean;\n private _hasGoogleCredentials?: boolean;\n private _companyId?: string;\n\n get name(): string {\n return this._name ?? \"\";\n }\n\n get connectionType(): string {\n return this._connectionType ?? \"\";\n }\n\n get provider(): string {\n return this._provider ?? \"\";\n }\n\n get position(): number {\n return this._position ?? 0;\n }\n\n get enabled(): boolean {\n return this._enabled ?? true;\n }\n\n get model(): string | undefined {\n return this._model;\n }\n\n get url(): string | undefined {\n return this._url;\n }\n\n get region(): string | undefined {\n return this._region;\n }\n\n get instance(): string | undefined {\n return this._instance;\n }\n\n get apiVersion(): string | undefined {\n return this._apiVersion;\n }\n\n get allowFallbacks(): boolean | undefined {\n return this._allowFallbacks;\n }\n\n get reasoningEffort(): string | undefined {\n return this._reasoningEffort;\n }\n\n get maxOutputTokens(): number | undefined {\n return this._maxOutputTokens;\n }\n\n get dimensions(): number | undefined {\n return this._dimensions;\n }\n\n get inputCostPer1MTokens(): number | undefined {\n return this._inputCostPer1MTokens;\n }\n\n get outputCostPer1MTokens(): number | undefined {\n return this._outputCostPer1MTokens;\n }\n\n get cachedInputCostPer1MTokens(): number | undefined {\n return this._cachedInputCostPer1MTokens;\n }\n\n get costPerMinute(): number | undefined {\n return this._costPerMinute;\n }\n\n get costPerPage(): number | undefined {\n return this._costPerPage;\n }\n\n get directUrl(): string | undefined {\n return this._directUrl;\n }\n\n get language(): string | undefined {\n return this._language;\n }\n\n get directFormat(): string | undefined {\n return this._directFormat;\n }\n\n get directProvider(): string | undefined {\n return this._directProvider;\n }\n\n get costPerImage(): number | undefined {\n return this._costPerImage;\n }\n\n get negativePrompt(): string | undefined {\n return this._negativePrompt;\n }\n\n get width(): number | undefined {\n return this._width;\n }\n\n get height(): number | undefined {\n return this._height;\n }\n\n get steps(): number | undefined {\n return this._steps;\n }\n\n get cfgScale(): number | undefined {\n return this._cfgScale;\n }\n\n get safeMode(): boolean | undefined {\n return this._safeMode;\n }\n\n get hideWatermark(): boolean | undefined {\n return this._hideWatermark;\n }\n\n get imageFormat(): string | undefined {\n return this._imageFormat;\n }\n\n get hasApiKey(): boolean {\n return this._hasApiKey ?? false;\n }\n\n get hasGoogleCredentials(): boolean {\n return this._hasGoogleCredentials ?? false;\n }\n\n get companyId(): string | undefined {\n return this._companyId;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes ?? {};\n\n this._name = attributes.name;\n this._connectionType = attributes.connectionType;\n this._provider = attributes.provider;\n this._position = attributes.position;\n this._enabled = attributes.enabled;\n this._model = attributes.model;\n this._url = attributes.url;\n this._region = attributes.region;\n this._instance = attributes.instance;\n this._apiVersion = attributes.apiVersion;\n this._allowFallbacks = attributes.allowFallbacks;\n this._reasoningEffort = attributes.reasoningEffort;\n this._maxOutputTokens = attributes.maxOutputTokens;\n this._dimensions = attributes.dimensions;\n this._inputCostPer1MTokens = attributes.inputCostPer1MTokens;\n this._outputCostPer1MTokens = attributes.outputCostPer1MTokens;\n this._cachedInputCostPer1MTokens = attributes.cachedInputCostPer1MTokens;\n this._costPerMinute = attributes.costPerMinute;\n this._costPerPage = attributes.costPerPage;\n this._directUrl = attributes.directUrl;\n this._language = attributes.language;\n this._directFormat = attributes.directFormat;\n this._directProvider = attributes.directProvider;\n this._costPerImage = attributes.costPerImage;\n this._negativePrompt = attributes.negativePrompt;\n this._width = attributes.width;\n this._height = attributes.height;\n this._steps = attributes.steps;\n this._cfgScale = attributes.cfgScale;\n this._safeMode = attributes.safeMode;\n this._hideWatermark = attributes.hideWatermark;\n this._imageFormat = attributes.imageFormat;\n this._hasApiKey = attributes.hasApiKey;\n this._hasGoogleCredentials = attributes.hasGoogleCredentials;\n this._companyId = attributes.companyId;\n\n return this;\n }\n\n createJsonApi(data: AiConnectionInput): any {\n const response: any = {\n data: {\n type: Modules.AiConnection.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n const setIfDefined = (key: keyof AiConnectionInput) => {\n if (data[key] !== undefined) response.data.attributes[key] = data[key];\n };\n\n (\n [\n \"name\",\n \"connectionType\",\n \"provider\",\n \"position\",\n \"enabled\",\n \"model\",\n \"url\",\n \"region\",\n \"instance\",\n \"apiVersion\",\n \"allowFallbacks\",\n \"reasoningEffort\",\n \"maxOutputTokens\",\n \"dimensions\",\n \"inputCostPer1MTokens\",\n \"outputCostPer1MTokens\",\n \"cachedInputCostPer1MTokens\",\n \"costPerMinute\",\n \"costPerPage\",\n \"directUrl\",\n \"language\",\n \"directFormat\",\n \"directProvider\",\n \"costPerImage\",\n \"negativePrompt\",\n \"width\",\n \"height\",\n \"steps\",\n \"cfgScale\",\n \"safeMode\",\n \"hideWatermark\",\n \"imageFormat\",\n ] as const\n ).forEach(setIfDefined);\n\n // Secrets: an empty string means \"keep the stored value\" — omit it from the\n // payload entirely so the backend never overwrites what it already holds.\n if (data.apiKey) response.data.attributes.apiKey = data.apiKey;\n if (data.googleCredentialsBase64) {\n response.data.attributes.googleCredentialsBase64 = data.googleCredentialsBase64;\n }\n\n // Scope is chosen at creation and immutable thereafter.\n if (data.companyId) {\n response.data.relationships.company = {\n data: { type: Modules.Company.name, id: data.companyId },\n };\n }\n\n return response;\n }\n\n /**\n * Body for POST /ai-connections/reorder. A dedicated model method so the\n * service never constructs a JSON:API payload itself — the sanctioned pairing\n * for `overridesJsonApiCreation`.\n */\n createReorderJsonApi(params: { ids: string[] }): any {\n return {\n data: {\n type: Modules.AiConnection.name,\n ids: params.ids,\n },\n };\n }\n}\n","export enum AiConnectionFields {\n name = \"name\",\n provider = \"provider\",\n model = \"model\",\n enabled = \"enabled\",\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { AiConnection } from \"./ai-connection\";\nimport {\n AiConnectionEnvDefaults,\n AiConnectionInput,\n AiConnectionInterface,\n AiProviderRegistry,\n} from \"./ai-connection.interface\";\n\nexport class AiConnectionService extends AbstractService {\n /**\n * Full list plus the editor meta in one round-trip: the list endpoint carries\n * the provider registry and the `.env` defaults as top-level JSON:API `meta`.\n */\n static async listConnections(): Promise<{\n connections: AiConnectionInterface[];\n providerRegistry: AiProviderRegistry;\n envDefaults: AiConnectionEnvDefaults;\n }> {\n const endpoint = new EndpointCreator({ endpoint: Modules.AiConnection });\n endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n\n const { data, meta } = await this.callApiWithMeta<AiConnectionInterface[]>({\n type: Modules.AiConnection,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n\n return {\n connections: data ?? [],\n providerRegistry: (meta?.providerRegistry ?? {}) as AiProviderRegistry,\n envDefaults: (meta?.envDefaults ?? {}) as AiConnectionEnvDefaults,\n };\n }\n\n /**\n * Create a connection. The optional `companyId` fixes its scope forever.\n */\n static async create(params: AiConnectionInput): Promise<AiConnectionInterface> {\n return this.callApi<AiConnectionInterface>({\n type: Modules.AiConnection,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.AiConnection }).generate(),\n input: params,\n });\n }\n\n /**\n * Update a connection. Blank/omitted secrets keep the stored values.\n */\n static async update(params: AiConnectionInput): Promise<AiConnectionInterface> {\n return this.callApi<AiConnectionInterface>({\n type: Modules.AiConnection,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({ endpoint: Modules.AiConnection, id: params.id }).generate(),\n input: params,\n });\n }\n\n /**\n * Delete a connection.\n */\n static async delete(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.AiConnection,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.AiConnection, id: params.id }).generate(),\n });\n }\n\n /**\n * Reorder one chain: the ordered id list is renumbered 0..n-1 server-side.\n * Dedicated model method + `overridesJsonApiCreation` — the sanctioned\n * pairing, never a hand-built payload here.\n */\n static async reorder(params: { ids: string[] }): Promise<void> {\n const model = new AiConnection();\n\n await this.callApi({\n type: Modules.AiConnection,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.AiConnection,\n childEndpoint: \"reorder\",\n }).generate(),\n input: model.createReorderJsonApi({ ids: params.ids }),\n overridesJsonApiCreation: true, // OK — the model method provides the structure\n });\n }\n}\n","import { PlugZapIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { AiConnection } from \"./data\";\n\nexport const AiConnectionModule = (factory: ModuleFactory) =>\n factory({\n name: \"ai-connections\",\n // The administration route this entity is served at. `name` above stays the\n // API endpoint; this drives generateUrl / rewriteUrl / EditorSheet\n // navigation, so it must match the real Next.js route or RoundPageContainer\n // rewrites the URL to a 404 on the first tab change.\n pageUrl: \"/administration/ai-connections\",\n model: AiConnection,\n moduleId: \"7d3f2b8a-91c4-4e6d-a5f0-2c9b8e4d1a37\",\n icon: PlugZapIcon,\n });\n","import { Building2Icon } from \"lucide-react\";\nimport { Company } from \".\";\nimport { ModuleFactory } from \"../../permissions\";\n\nexport const CompanyModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/companies\",\n name: \"companies\",\n model: Company,\n moduleId: \"f9e77c8f-bfd1-4fd4-80b0-e1d891ab7113\",\n icon: Building2Icon,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { FeatureInterface } from \"../../feature\";\nimport { ModuleInterface } from \"../../module\";\nimport type { CreditWarningOffer } from \"../../billing/data/credit-warning\";\nimport { CompanyInput, CompanyInterface } from \"./company.interface\";\n\nexport class Company extends AbstractApiData implements CompanyInterface {\n private _name?: string;\n private _logo?: string;\n private _logoUrl?: string;\n private _configurations?: any;\n\n private _isActiveSubscription: boolean = false;\n private _trialEndsAt?: Date;\n\n private _monthlyCredits: number = 0;\n private _availableMonthlyCredits: number = 0;\n private _availableExtraCredits: number = 0;\n private _aiEnabled?: boolean;\n\n private _creditsGrantedThisPeriod?: number;\n private _creditWarningSentAt?: Date;\n private _creditWarningDismissedAt?: Date;\n private _creditWarningOffer?: CreditWarningOffer;\n\n private _features?: FeatureInterface[];\n private _modules?: ModuleInterface[];\n\n private _legal_address?: string;\n private _street_number?: string;\n private _street?: string;\n private _city?: string;\n private _province?: string;\n private _region?: string;\n private _postcode?: string;\n private _country?: string;\n private _country_code?: string;\n private _fiscal_data?: string;\n\n get name(): string {\n if (this._name === undefined) throw new Error(\"Name is not defined\");\n return this._name;\n }\n\n get logo(): string | undefined {\n return this._logo;\n }\n\n get logoUrl(): string | undefined {\n return this._logoUrl;\n }\n\n get isActiveSubscription(): boolean {\n return this._isActiveSubscription ?? false;\n }\n\n get trialEndsAt(): Date | undefined {\n return this._trialEndsAt;\n }\n\n get monthlyCredits(): number {\n return this._monthlyCredits ?? 0;\n }\n\n get availableMonthlyCredits(): number {\n return this._availableMonthlyCredits ?? 0;\n }\n\n get availableExtraCredits(): number {\n return this._availableExtraCredits ?? 0;\n }\n\n get aiEnabled(): boolean {\n return this._aiEnabled ?? true;\n }\n\n get creditsGrantedThisPeriod(): number | undefined {\n return this._creditsGrantedThisPeriod;\n }\n\n get creditWarningSentAt(): Date | undefined {\n return this._creditWarningSentAt;\n }\n\n get creditWarningDismissedAt(): Date | undefined {\n return this._creditWarningDismissedAt;\n }\n\n get creditWarningOffer(): CreditWarningOffer | undefined {\n return this._creditWarningOffer;\n }\n\n get features(): FeatureInterface[] {\n return this._features ?? [];\n }\n\n get modules(): ModuleInterface[] {\n return this._modules ?? [];\n }\n\n get configurations(): any | undefined {\n return this._configurations;\n }\n\n get legal_address(): string | undefined {\n return this._legal_address;\n }\n\n get street_number(): string | undefined {\n return this._street_number;\n }\n\n get street(): string | undefined {\n return this._street;\n }\n\n get city(): string | undefined {\n return this._city;\n }\n\n get province(): string | undefined {\n return this._province;\n }\n\n get region(): string | undefined {\n return this._region;\n }\n\n get postcode(): string | undefined {\n return this._postcode;\n }\n\n get country(): string | undefined {\n return this._country;\n }\n\n get country_code(): string | undefined {\n return this._country_code;\n }\n\n get fiscal_data(): string | undefined {\n return this._fiscal_data;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._name = data.jsonApi.attributes.name;\n this._configurations = data.jsonApi.attributes.configurations\n ? JSON.parse(data.jsonApi.attributes.configurations)\n : undefined;\n this._logo = data.jsonApi.attributes.logo;\n this._logoUrl = data.jsonApi.attributes.logoUrl;\n this._isActiveSubscription = data.jsonApi.attributes.isActiveSubscription ?? false;\n this._trialEndsAt = data.jsonApi.attributes.trialEndsAt ? new Date(data.jsonApi.attributes.trialEndsAt) : undefined;\n this._monthlyCredits = data.jsonApi.attributes.monthlyCredits ?? 0;\n this._availableMonthlyCredits = data.jsonApi.attributes.availableMonthlyCredits ?? 0;\n this._availableExtraCredits = data.jsonApi.attributes.availableExtraCredits ?? 0;\n this._aiEnabled = data.jsonApi.attributes.aiEnabled ?? true;\n\n this._creditsGrantedThisPeriod = data.jsonApi.attributes.creditsGrantedThisPeriod ?? undefined;\n this._creditWarningSentAt = data.jsonApi.attributes.creditWarningSentAt\n ? new Date(data.jsonApi.attributes.creditWarningSentAt)\n : undefined;\n this._creditWarningDismissedAt = data.jsonApi.attributes.creditWarningDismissedAt\n ? new Date(data.jsonApi.attributes.creditWarningDismissedAt)\n : undefined;\n this._creditWarningOffer = data.jsonApi.attributes.creditWarningOffer ?? undefined;\n\n this._legal_address = data.jsonApi.attributes.legal_address;\n this._street_number = data.jsonApi.attributes.street_number;\n this._street = data.jsonApi.attributes.street;\n this._city = data.jsonApi.attributes.city;\n this._province = data.jsonApi.attributes.province;\n this._region = data.jsonApi.attributes.region;\n this._postcode = data.jsonApi.attributes.postcode;\n this._country = data.jsonApi.attributes.country;\n this._country_code = data.jsonApi.attributes.country_code;\n this._fiscal_data = data.jsonApi.attributes.fiscal_data;\n\n this._features = this._readIncluded<FeatureInterface>(data, \"features\", Modules.Feature) as FeatureInterface[];\n this._modules = this._readIncluded<ModuleInterface>(data, \"modules\", Modules.Module) as ModuleInterface[];\n\n return this;\n }\n\n createJsonApi(data: CompanyInput) {\n const response: any = {\n data: {\n type: Modules.Company.name,\n id: data.id,\n attributes: {},\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.name) response.data.attributes.name = data.name;\n if (data.configurations) response.data.attributes.configurations = JSON.stringify(data.configurations);\n if (data.logo) response.data.attributes.logo = data.logo;\n if (data.monthlyCredits !== undefined) response.data.attributes.monthlyCredits = data.monthlyCredits;\n if (data.availableMonthlyCredits !== undefined)\n response.data.attributes.availableMonthlyCredits = data.availableMonthlyCredits;\n if (data.availableExtraCredits !== undefined)\n response.data.attributes.availableExtraCredits = data.availableExtraCredits;\n if (data.aiEnabled !== undefined) response.data.attributes.aiEnabled = data.aiEnabled;\n\n if (data.legal_address !== undefined) response.data.attributes.legal_address = data.legal_address;\n if (data.street_number !== undefined) response.data.attributes.street_number = data.street_number;\n if (data.street !== undefined) response.data.attributes.street = data.street;\n if (data.city !== undefined) response.data.attributes.city = data.city;\n if (data.province !== undefined) response.data.attributes.province = data.province;\n if (data.region !== undefined) response.data.attributes.region = data.region;\n if (data.postcode !== undefined) response.data.attributes.postcode = data.postcode;\n if (data.country !== undefined) response.data.attributes.country = data.country;\n if (data.country_code !== undefined) response.data.attributes.country_code = data.country_code;\n if (data.fiscal_data !== undefined) response.data.attributes.fiscal_data = data.fiscal_data;\n\n if (data.featureIds && data.featureIds.length > 0) {\n response.data.relationships.features = {\n data: data.featureIds.map((featureId) => ({\n type: Modules.Feature.name,\n id: featureId,\n })),\n };\n }\n\n if (data.moduleIds && data.moduleIds.length > 0) {\n response.data.relationships.modules = {\n data: data.moduleIds.map((moduleId) => ({\n type: Modules.Module.name,\n id: moduleId,\n })),\n };\n }\n\n return response;\n }\n}\n","export enum CompanyFields {\n companyId = \"companyId\",\n name = \"name\",\n createdAt = \"createdAt\",\n updatedAt = \"updatedAt\",\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef } from \"../../../core\";\nimport { CREDIT_WARNING_DISMISS_SEGMENT } from \"../../billing/data/credit-warning\";\nimport { CompanyInput, CompanyInterface } from \"./company.interface\";\n\nexport class CompanyService extends AbstractService {\n static async findOne(params: { companyId: string }): Promise<CompanyInterface> {\n return this.callApi<CompanyInterface>({\n type: Modules.Company,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.Company, id: params.companyId }).generate(),\n });\n }\n\n static async findMany(params: { search?: string; next?: NextRef }): Promise<CompanyInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Company });\n\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi<CompanyInterface[]>({\n type: Modules.Company,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async delete(params: { companyId: string }): Promise<void> {\n await this.callApi({\n type: Modules.Company,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.Company, id: params.companyId }).generate(),\n });\n }\n\n static async selfDelete(params: { companyId: string }): Promise<void> {\n await this.callApi({\n type: Modules.Company,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({\n endpoint: Modules.Company,\n id: params.companyId,\n childEndpoint: \"self-delete\",\n }).generate(),\n });\n }\n\n static async create(params: CompanyInput): Promise<CompanyInterface> {\n return this.callApi({\n type: Modules.Company,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.Company }).generate(),\n input: params,\n });\n }\n\n static async update(params: CompanyInput): Promise<CompanyInterface> {\n return this.callApi({\n type: Modules.Company,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({ endpoint: Modules.Company, id: params.id }).generate(),\n input: params,\n });\n }\n\n static async updateConfigurations(params: CompanyInput): Promise<CompanyInterface> {\n return this.callApi({\n type: Modules.Company,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({\n endpoint: Modules.Company,\n id: params.id,\n childEndpoint: \"configurations\",\n }).generate(),\n input: params,\n });\n }\n\n static async activateLicense(params: CompanyInput): Promise<CompanyInterface> {\n return this.callApi({\n type: Modules.Company,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({ endpoint: Modules.Company, id: params.id, childEndpoint: \"license\" }).generate(),\n input: params,\n });\n }\n\n /**\n * Dismisses this period's credit warning for the company. The request has no\n * body: the server stamps the dismissal itself and answers 204.\n */\n static async dismissCreditWarning(params: { companyId: string }): Promise<void> {\n await this.callApi({\n type: Modules.Company,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Company,\n id: params.companyId,\n childEndpoint: CREDIT_WARNING_DISMISS_SEGMENT,\n }).generate(),\n });\n }\n}\n","// Frontend mirror of the backend `EntityAiStatus` enum (apps/api). Keep the\n// exact const-object + type shape and PascalCase values in sync with the\n// backend — this is the shared contract for credit-gated AI processing state.\nexport const EntityAiStatus = {\n Pending: \"Pending\",\n Summarising: \"Summarising\",\n Completed: \"Completed\",\n Failed: \"Failed\",\n PendingCredits: \"PendingCredits\",\n Discarded: \"Discarded\",\n} as const;\n\nexport type EntityAiStatus = (typeof EntityAiStatus)[keyof typeof EntityAiStatus];\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { SearchResultInterface } from \"../../search/interfaces/search.result.interface\";\nimport { UserInterface } from \"../../user\";\nimport { ContentInput, ContentInterface } from \"./content.interface\";\n\nexport class Content extends AbstractApiData implements ContentInterface, SearchResultInterface {\n private _contentType?: string;\n\n private _name?: string;\n private _abstract?: string;\n private _tldr?: string;\n private _aiStatus?: string;\n private _relevance?: number;\n\n private _author?: UserInterface;\n private _editors?: UserInterface[];\n\n get searchResult(): string {\n return this._name ?? \"\";\n }\n\n get contentType(): string | undefined {\n return this._contentType;\n }\n\n get name(): string {\n if (this._name === undefined) throw new Error(\"JsonApi error: content name is missing\");\n return this._name;\n }\n\n get abstract(): string | undefined {\n return this._abstract;\n }\n\n get tldr(): string | undefined {\n return this._tldr;\n }\n\n get aiStatus(): string {\n return this._aiStatus ?? \"\";\n }\n\n get relevance(): number | undefined {\n return this._relevance;\n }\n\n get author(): UserInterface {\n if (this._author === undefined) throw new Error(\"JsonApi error: document author is missing\");\n return this._author;\n }\n\n get editors(): UserInterface[] {\n return this._editors ?? [];\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._contentType = data.jsonApi.meta.contentType;\n\n this._name = data.jsonApi.attributes.name;\n this._abstract = data.jsonApi.attributes.abstract;\n this._tldr = data.jsonApi.attributes.tldr;\n this._aiStatus = data.jsonApi.meta.aiStatus;\n this._relevance = data.jsonApi.meta.relevance;\n\n this._author = this._readIncluded(data, \"author\", Modules.User) as UserInterface;\n this._editors = this._readIncluded(data, \"editors\", Modules.User) as UserInterface[];\n\n return this;\n }\n\n protected addContentInput(response: any, data: ContentInput) {\n if (data.name) response.data.attributes.name = data.name;\n\n if (data.authorId) {\n response.data.relationships.author = {\n data: {\n type: Modules.User.name,\n id: data.authorId,\n },\n };\n }\n }\n}\n","export enum ContentFields {\n contentId = \"contentId\",\n\n name = \"name\",\n relevance = \"relevance\",\n\n authors = \"authors\",\n\n createdAt = \"createdAt\",\n updatedAt = \"updatedAt\",\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../core\";\nimport { ContentInterface } from \"./content.interface\";\n\nexport class ContentService extends AbstractService {\n static async findMany(params: {\n contentIds?: string[];\n search?: string;\n fetchAll?: boolean;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<ContentInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Content });\n\n if (params.contentIds) {\n endpoint.addAdditionalParam(\"contentIds\", params.contentIds.join(\",\"));\n endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n } else {\n if (params.fetchAll) endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n }\n if (Modules.Content.inclusions?.lists?.fields) endpoint.limitToFields(Modules.Content.inclusions.lists.fields);\n if (Modules.Content.inclusions?.lists?.types) endpoint.limitToType(Modules.Content.inclusions.lists.types);\n\n return this.callApi({\n type: Modules.Content,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findRelevant(params: { id: string; next?: NextRef; prev?: PreviousRef }): Promise<ContentInterface[]> {\n const endpoint: EndpointCreator = new EndpointCreator({\n endpoint: Modules.Content,\n id: params.id,\n childEndpoint: \"relevance\",\n });\n\n if (Modules.Content.inclusions?.lists?.fields) endpoint.limitToFields(Modules.Content.inclusions.lists.fields);\n if (Modules.Content.inclusions?.lists?.types) endpoint.limitToType(Modules.Content.inclusions.lists.types);\n\n return this.callApi<ContentInterface[]>({\n type: Modules.Content,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findManyByAuthor(params: {\n userId: string;\n search?: string;\n fetchAll?: boolean;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<ContentInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Author,\n id: params.userId,\n childEndpoint: Modules.Content,\n });\n\n if (params.fetchAll) endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n if (Modules.Content.inclusions?.lists?.fields) endpoint.limitToFields(Modules.Content.inclusions.lists.fields);\n if (Modules.Content.inclusions?.lists?.types) endpoint.limitToType(Modules.Content.inclusions.lists.types);\n\n return this.callApi({\n type: Modules.Content,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n}\n","import { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { Content } from \".\";\n\nexport const ContentModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/contents\",\n name: \"contents\",\n model: Content,\n inclusions: {\n lists: {\n fields: [\n createJsonApiInclusion(\"content\", [`name`, `tldr`, `abstract`, `aiStatus`, `relevance`]),\n createJsonApiInclusion(\"users\", [`name`, `avatar`]),\n createJsonApiInclusion(\"topics\", [`name`]),\n createJsonApiInclusion(\"expertises\", [`name`]),\n ],\n },\n },\n });\n","import { LifeBuoyIcon } from \"lucide-react\";\nimport { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { HowTo } from \"./data/HowTo\";\n\nexport const HowToModule = (factory: ModuleFactory) =>\n factory({\n moduleId: \"6f975207-0df3-4c0d-b541-ed5dc04487b2\",\n pageUrl: \"/administration/howtos\",\n name: \"howtos\",\n model: HowTo,\n icon: LifeBuoyIcon,\n inclusions: {\n lists: {\n fields: [\n createJsonApiInclusion(\"howtos\", [\n \"name\",\n \"description\",\n \"pages\",\n \"howToType\",\n \"slug\",\n \"order\",\n \"summary\",\n \"tags\",\n \"contextualKeys\",\n \"draft\",\n ]),\n ],\n },\n },\n });\n","import { JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { Content } from \"../../content/data/content\";\nimport { HowToInput, HowToInterface } from \"./HowToInterface\";\n\nexport class HowTo extends Content implements HowToInterface {\n private _description?: any;\n private _pages?: string;\n private _howToType?: string;\n private _slug?: string;\n private _order?: number;\n private _summary?: string;\n private _tags?: string[];\n private _contextualKeys?: string[];\n private _draft?: boolean;\n\n /**\n * Parse pages from backend JSON string (handles legacy single string + JSON array)\n */\n static parsePagesFromString(pagesStr?: string): string[] {\n if (!pagesStr) return [];\n try {\n const parsed = JSON.parse(pagesStr);\n return Array.isArray(parsed) ? parsed : [pagesStr];\n } catch {\n // Legacy: treat as single page if not valid JSON\n return pagesStr ? [pagesStr] : [];\n }\n }\n\n /**\n * Serialize pages array to JSON string for backend\n */\n static serializePagesToString(pages: string[]): string | undefined {\n const filtered = pages.filter((p) => p.trim());\n return filtered.length > 0 ? JSON.stringify(filtered) : undefined;\n }\n\n get description(): any {\n return this._description;\n }\n\n get pages(): string | undefined {\n return this._pages;\n }\n\n get howToType(): string | undefined {\n return this._howToType;\n }\n\n get slug(): string | undefined {\n return this._slug;\n }\n\n get order(): number | undefined {\n return this._order;\n }\n\n get summary(): string | undefined {\n return this._summary;\n }\n\n get tags(): string[] {\n return this._tags ?? [];\n }\n\n get contextualKeys(): string[] {\n return this._contextualKeys ?? [];\n }\n\n get draft(): boolean {\n return this._draft ?? false;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const rawDescription = data.jsonApi.attributes.description;\n if (rawDescription === undefined || rawDescription === null || rawDescription === \"\") {\n this._description = undefined;\n } else if (typeof rawDescription === \"string\") {\n try {\n this._description = JSON.parse(rawDescription);\n } catch {\n // Help-content HowTos store plain-text summaries; admin-created HowTos\n // store JSON-stringified BlockNote docs. Fall back to the raw string\n // rather than crashing rehydration for the plain-text case.\n this._description = rawDescription;\n }\n } else {\n this._description = rawDescription;\n }\n this._pages = data.jsonApi.attributes.pages;\n this._howToType = data.jsonApi.attributes.howToType;\n this._slug = data.jsonApi.attributes.slug;\n this._order = data.jsonApi.attributes.order;\n this._summary = data.jsonApi.attributes.summary;\n this._tags = data.jsonApi.attributes.tags ?? [];\n this._contextualKeys = data.jsonApi.attributes.contextualKeys ?? [];\n this._draft = data.jsonApi.attributes.draft ?? false;\n\n return this;\n }\n\n createJsonApi(data: HowToInput) {\n const response: any = {\n data: {\n type: Modules.HowTo.name,\n id: data.id,\n attributes: {},\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n super.addContentInput(response, data);\n\n if (data.description !== undefined) response.data.attributes.description = JSON.stringify(data.description);\n if (data.pages !== undefined) response.data.attributes.pages = data.pages;\n if (data.howToType !== undefined) response.data.attributes.howToType = data.howToType;\n if (data.slug !== undefined) response.data.attributes.slug = data.slug;\n if (data.order !== undefined) response.data.attributes.order = data.order;\n if (data.summary !== undefined) response.data.attributes.summary = data.summary;\n if (data.tags !== undefined) response.data.attributes.tags = data.tags;\n if (data.contextualKeys !== undefined) response.data.attributes.contextualKeys = data.contextualKeys;\n if (data.draft !== undefined) response.data.attributes.draft = data.draft;\n\n return response;\n }\n}\n","export enum HowToFields {\n howToId = \"howToId\",\n\n name = \"name\",\n description = \"description\",\n pages = \"pages\",\n\n createdAt = \"createdAt\",\n updatedAt = \"updatedAt\",\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../core\";\nimport { HowToInput, HowToInterface } from \"./HowToInterface\";\n\nexport class HowToService extends AbstractService {\n static async findOne(params: { id: string }): Promise<HowToInterface> {\n return this.callApi<HowToInterface>({\n type: Modules.HowTo,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.HowTo, id: params.id }).generate(),\n });\n }\n\n static async findMany(\n params: {\n search?: string;\n fetchAll?: boolean;\n next?: NextRef;\n prev?: PreviousRef;\n } = {},\n ): Promise<HowToInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.HowTo });\n\n if (params.fetchAll) endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n if (Modules.HowTo.inclusions?.lists?.fields) endpoint.limitToFields(Modules.HowTo.inclusions.lists.fields);\n if (Modules.HowTo.inclusions?.lists?.types) endpoint.limitToType(Modules.HowTo.inclusions.lists.types);\n\n return this.callApi({\n type: Modules.HowTo,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async create(params: HowToInput): Promise<HowToInterface> {\n return this.callApi({\n type: Modules.HowTo,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.HowTo }).generate(),\n input: params,\n });\n }\n\n static async update(params: HowToInput): Promise<HowToInterface> {\n return this.callApi({\n type: Modules.HowTo,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({ endpoint: Modules.HowTo, id: params.id }).generate(),\n input: params,\n });\n }\n\n static async delete(params: { howToId: string }): Promise<void> {\n await this.callApi({\n type: Modules.HowTo,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.HowTo, id: params.howToId }).generate(),\n });\n }\n\n /**\n * POST /howtos/reindex — re-queues every guide for chunking. Replies 204.\n * Chunking otherwise only happens on create and update, so a guide that was\n * never chunked stays invisible to help-mode retrieval with no way back.\n */\n static async reindex(): Promise<void> {\n await this.callApi({\n type: Modules.HowTo,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.HowTo, id: \"reindex\" }).generate(),\n });\n }\n\n static async findPublished(params: { howToType?: string } = {}): Promise<HowToInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: `public/${Modules.HowTo.name}` });\n if (params.howToType) endpoint.addAdditionalParam(\"type\", params.howToType);\n return this.callApi({\n type: Modules.HowTo,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n static async findPublishedArticle(params: { howToType: string; slug: string }): Promise<HowToInterface> {\n return this.callApi<HowToInterface>({\n type: Modules.HowTo,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({\n endpoint: `public/${Modules.HowTo.name}`,\n id: params.howToType,\n childEndpoint: params.slug,\n }).generate(),\n });\n }\n\n static async findRelated(params: { howToType: string; slug: string }): Promise<HowToInterface[]> {\n return this.callApi({\n type: Modules.HowTo,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({\n endpoint: `public/${Modules.HowTo.name}`,\n id: params.howToType,\n childEndpoint: params.slug,\n childId: \"related\",\n }).generate(),\n });\n }\n\n static async addRelated(params: { howToId: string; relatedId: string }): Promise<HowToInterface> {\n return this.callApi<HowToInterface>({\n type: Modules.HowTo,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.HowTo,\n id: params.howToId,\n childEndpoint: \"related\",\n childId: params.relatedId,\n }).generate(),\n });\n }\n\n static async removeRelated(params: { howToId: string; relatedId: string }): Promise<void> {\n await this.callApi({\n type: Modules.HowTo,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({\n endpoint: Modules.HowTo,\n id: params.howToId,\n childEndpoint: \"related\",\n childId: params.relatedId,\n }).generate(),\n });\n }\n}\n","import { MailIcon } from \"lucide-react\";\nimport { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { EmailAutomation } from \"./data/EmailAutomation\";\n\nexport const EmailAutomationModule = (factory: ModuleFactory) =>\n factory({\n moduleId: \"3f6c1a2e-7b94-4d1e-8c55-9a0b2e4f6d71\",\n pageUrl: \"/administration/email-automations\",\n name: \"email-automations\",\n model: EmailAutomation,\n icon: MailIcon,\n inclusions: {\n lists: {\n fields: [\n createJsonApiInclusion(\"email-automations\", [\n \"name\",\n \"purpose\",\n \"trigger\",\n \"exitTrigger\",\n \"replyTo\",\n \"isActive\",\n \"stepCount\",\n \"activeCount\",\n \"completedCount\",\n \"unsubscribedCount\",\n ]),\n ],\n },\n },\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { EmailAutomationInput, EmailAutomationInterface } from \"./EmailAutomationInterface\";\nimport { EmailPurpose } from \"./EmailPurpose\";\n\nexport class EmailAutomation extends AbstractApiData implements EmailAutomationInterface {\n private _name?: string;\n private _trigger?: string;\n private _exitTrigger?: string;\n private _replyTo?: string;\n private _isActive: boolean = false;\n private _purpose: EmailPurpose = \"marketing\";\n private _stepCount?: number;\n private _activeCount?: number;\n private _completedCount?: number;\n private _unsubscribedCount?: number;\n\n get name(): string {\n return this._name ?? \"\";\n }\n\n get trigger(): string | undefined {\n return this._trigger;\n }\n\n get exitTrigger(): string | undefined {\n return this._exitTrigger;\n }\n\n get replyTo(): string | undefined {\n return this._replyTo;\n }\n\n get isActive(): boolean {\n return this._isActive;\n }\n\n get purpose(): EmailPurpose {\n return this._purpose;\n }\n\n get stepCount(): number {\n return this._stepCount ?? 0;\n }\n\n get activeCount(): number {\n return this._activeCount ?? 0;\n }\n\n get completedCount(): number {\n return this._completedCount ?? 0;\n }\n\n get unsubscribedCount(): number {\n return this._unsubscribedCount ?? 0;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._name = data.jsonApi.attributes.name;\n this._trigger = data.jsonApi.attributes.trigger ?? undefined;\n this._exitTrigger = data.jsonApi.attributes.exitTrigger ?? undefined;\n this._replyTo = data.jsonApi.attributes.replyTo ?? undefined;\n this._isActive = data.jsonApi.attributes.isActive === true;\n // A record saved before purpose existed counts as Marketing.\n this._purpose = data.jsonApi.attributes.purpose === \"service\" ? \"service\" : \"marketing\";\n this._stepCount = data.jsonApi.attributes.stepCount;\n this._activeCount = data.jsonApi.attributes.activeCount;\n this._completedCount = data.jsonApi.attributes.completedCount;\n this._unsubscribedCount = data.jsonApi.attributes.unsubscribedCount;\n\n return this;\n }\n\n createJsonApi(data: EmailAutomationInput) {\n const response: any = {\n data: {\n type: Modules.EmailAutomation.name,\n id: data.id,\n attributes: {},\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.name !== undefined) response.data.attributes.name = data.name;\n if (data.trigger !== undefined) response.data.attributes.trigger = data.trigger;\n if (data.exitTrigger !== undefined) response.data.attributes.exitTrigger = data.exitTrigger;\n if (data.replyTo !== undefined) response.data.attributes.replyTo = data.replyTo;\n if (data.isActive !== undefined) response.data.attributes.isActive = data.isActive;\n if (data.purpose !== undefined) response.data.attributes.purpose = data.purpose;\n\n return response;\n }\n}\n","import { MailIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { EmailAutomationStep } from \"./data/EmailAutomationStep\";\n\nexport const EmailAutomationStepModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: undefined,\n name: \"email-automation-steps\",\n model: EmailAutomationStep,\n icon: MailIcon,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { EmailAutomationInterface } from \"./EmailAutomationInterface\";\nimport { EmailAutomationStepInput, EmailAutomationStepInterface } from \"./EmailAutomationStepInterface\";\n\nexport class EmailAutomationStep extends AbstractApiData implements EmailAutomationStepInterface {\n private _subject?: string;\n private _body?: string;\n private _delayHours?: number;\n private _positionX?: number;\n private _positionY?: number;\n private _sentCount?: number;\n private _automation?: EmailAutomationInterface;\n private _next?: EmailAutomationStepInterface;\n\n get subject(): string {\n return this._subject ?? \"\";\n }\n\n get body(): string {\n return this._body ?? \"\";\n }\n\n get delayHours(): number {\n return this._delayHours ?? 0;\n }\n\n get positionX(): number | undefined {\n return this._positionX;\n }\n\n get positionY(): number | undefined {\n return this._positionY;\n }\n\n get sentCount(): number {\n return this._sentCount ?? 0;\n }\n\n get automation(): EmailAutomationInterface | undefined {\n return this._automation;\n }\n\n get next(): EmailAutomationStepInterface | undefined {\n return this._next;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._subject = data.jsonApi.attributes.subject;\n this._body = data.jsonApi.attributes.body;\n this._delayHours = data.jsonApi.attributes.delayHours;\n this._positionX = data.jsonApi.attributes.positionX ?? undefined;\n this._positionY = data.jsonApi.attributes.positionY ?? undefined;\n this._sentCount = data.jsonApi.attributes.sentCount;\n\n this._automation = this._readIncluded(data, \"automation\", Modules.EmailAutomation) as EmailAutomationInterface;\n this._next = this._readIncluded(data, \"next\", Modules.EmailAutomationStep) as EmailAutomationStepInterface;\n\n return this;\n }\n\n createJsonApi(data: EmailAutomationStepInput) {\n const response: any = {\n data: {\n type: Modules.EmailAutomationStep.name,\n id: data.id,\n attributes: {},\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.subject !== undefined) response.data.attributes.subject = data.subject;\n if (data.body !== undefined) response.data.attributes.body = data.body;\n if (data.delayHours !== undefined) response.data.attributes.delayHours = data.delayHours;\n if (data.positionX !== undefined) response.data.attributes.positionX = data.positionX;\n if (data.positionY !== undefined) response.data.attributes.positionY = data.positionY;\n\n if (data.automationId) {\n response.data.relationships.automation = {\n data: {\n type: Modules.EmailAutomation.name,\n id: data.automationId,\n },\n };\n }\n\n return response;\n }\n\n /**\n * Body of POST email-automation-steps/:stepId/next: the step the chain continues to.\n */\n createNextJsonApi(params: { nextStepId: string }) {\n return {\n data: {\n type: Modules.EmailAutomationStep.name,\n id: params.nextStepId,\n },\n };\n }\n}\n","import { UsersIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { EmailAutomationEnrollment } from \"./data/EmailAutomationEnrollment\";\n\nexport const EmailAutomationEnrollmentModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: undefined,\n name: \"email-automation-enrollments\",\n model: EmailAutomationEnrollment,\n icon: UsersIcon,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport {\n EmailAutomationEnrollmentInput,\n EmailAutomationEnrollmentInterface,\n EmailAutomationEnrollmentStatus,\n} from \"./EmailAutomationEnrollmentInterface\";\nimport { EmailAutomationInterface } from \"./EmailAutomationInterface\";\n\n/** JSON:API type of the POST email-automations/unsubscribe/:token request body (not a module: nothing is stored). */\nexport const EMAIL_AUTOMATION_UNSUBSCRIBE_TYPE = \"email-automation-unsubscribes\";\n\nexport class EmailAutomationEnrollment extends AbstractApiData implements EmailAutomationEnrollmentInterface {\n private _email?: string;\n private _name?: string;\n private _status: EmailAutomationEnrollmentStatus = \"active\";\n private _enrolledAt?: Date;\n private _nextSendAt?: Date;\n private _finishedAt?: Date;\n private _attempts?: number;\n private _lastError?: string;\n private _currentStepSubject?: string;\n private _sentCount?: number;\n private _automation?: EmailAutomationInterface;\n\n get email(): string {\n return this._email ?? \"\";\n }\n\n get name(): string | undefined {\n return this._name;\n }\n\n get status(): EmailAutomationEnrollmentStatus {\n return this._status;\n }\n\n get enrolledAt(): Date {\n return this._enrolledAt ?? new Date(0);\n }\n\n get nextSendAt(): Date | undefined {\n return this._nextSendAt;\n }\n\n get finishedAt(): Date | undefined {\n return this._finishedAt;\n }\n\n get attempts(): number {\n return this._attempts ?? 0;\n }\n\n get lastError(): string | undefined {\n return this._lastError;\n }\n\n get currentStepSubject(): string | undefined {\n return this._currentStepSubject;\n }\n\n get sentCount(): number {\n return this._sentCount ?? 0;\n }\n\n get automation(): EmailAutomationInterface | undefined {\n return this._automation;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes;\n this._email = attributes.email;\n this._name = attributes.name ?? undefined;\n this._status = attributes.status ?? \"active\";\n this._enrolledAt = attributes.enrolledAt ? new Date(attributes.enrolledAt) : undefined;\n this._nextSendAt = attributes.nextSendAt ? new Date(attributes.nextSendAt) : undefined;\n this._finishedAt = attributes.finishedAt ? new Date(attributes.finishedAt) : undefined;\n this._attempts = attributes.attempts;\n this._lastError = attributes.lastError ?? undefined;\n this._currentStepSubject = attributes.currentStepSubject ?? undefined;\n this._sentCount = attributes.sentCount;\n\n this._automation = this._readIncluded(data, \"automation\", Modules.EmailAutomation) as EmailAutomationInterface;\n\n return this;\n }\n\n /**\n * Body of POST email-automations/:id/email-automation-enrollments: an\n * administrator adds a person by hand. The API enrols them at the first step.\n */\n createJsonApi(data: EmailAutomationEnrollmentInput) {\n return {\n data: {\n type: Modules.EmailAutomationEnrollment.name,\n id: data.id,\n attributes: {\n recipientType: data.recipientType,\n recipientId: data.recipientId,\n },\n },\n };\n }\n\n /**\n * Body of POST email-automations/unsubscribe/:token.\n */\n createUnsubscribeJsonApi(params: { scope: \"automation\" | \"all\" }) {\n return {\n data: {\n type: EMAIL_AUTOMATION_UNSUBSCRIBE_TYPE,\n attributes: {\n scope: params.scope,\n },\n },\n };\n }\n}\n","import { ZapIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { EmailAutomationTrigger } from \"./data/EmailAutomationTrigger\";\n\nexport const EmailAutomationTriggerModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: undefined,\n name: \"email-automation-triggers\",\n model: EmailAutomationTrigger,\n icon: ZapIcon,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { EmailAutomationTriggerInterface } from \"./EmailAutomationTriggerInterface\";\n\nexport class EmailAutomationTrigger extends AbstractApiData implements EmailAutomationTriggerInterface {\n private _key?: string;\n private _label?: string;\n\n get key(): string {\n return this._key ?? \"\";\n }\n\n get label(): string {\n return this._label ?? \"\";\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._key = data.jsonApi.attributes.key;\n this._label = data.jsonApi.attributes.label;\n\n return this;\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"EmailAutomationTrigger is declared by the API and is read-only\");\n }\n}\n","import { UserPlusIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { EmailAutomationCandidate } from \"./data/EmailAutomationCandidate\";\n\nexport const EmailAutomationCandidateModule = (factory: ModuleFactory) =>\n factory({\n moduleId: \"5c3e9a71-2b84-4f06-9d1e-7a4b6c8e0f25\",\n pageUrl: undefined,\n name: \"email-automation-candidates\",\n model: EmailAutomationCandidate,\n icon: UserPlusIcon,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport {\n EmailAutomationCandidateInterface,\n EmailAutomationCandidateReason,\n EmailAutomationRecipientType,\n} from \"./EmailAutomationCandidateInterface\";\n\nexport class EmailAutomationCandidate extends AbstractApiData implements EmailAutomationCandidateInterface {\n private _recipientType: EmailAutomationRecipientType = \"user\";\n private _recipientId?: string;\n private _email?: string;\n private _name?: string;\n private _addable?: boolean;\n private _reason?: EmailAutomationCandidateReason;\n\n get recipientType(): EmailAutomationRecipientType {\n return this._recipientType;\n }\n\n get recipientId(): string {\n return this._recipientId ?? \"\";\n }\n\n get email(): string {\n return this._email ?? \"\";\n }\n\n get name(): string | undefined {\n return this._name;\n }\n\n get addable(): boolean {\n return this._addable ?? false;\n }\n\n get reason(): EmailAutomationCandidateReason | undefined {\n return this._reason;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes;\n this._recipientType = attributes.recipientType === \"waitlist\" ? \"waitlist\" : \"user\";\n this._recipientId = attributes.recipientId;\n this._email = attributes.email;\n this._name = attributes.name ?? undefined;\n this._addable = attributes.addable === true;\n this._reason = attributes.reason ?? undefined;\n\n return this;\n }\n\n /**\n * The API computes candidates and never accepts one as a request body; this\n * only identifies the resource.\n */\n createJsonApi(data: { id: string }) {\n return {\n data: {\n type: Modules.EmailAutomationCandidate.name,\n id: data.id,\n },\n };\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../core\";\nimport { EmailAutomationCandidateInterface } from \"./EmailAutomationCandidateInterface\";\nimport {\n EmailAutomationEnrollmentInput,\n EmailAutomationEnrollmentInterface,\n} from \"./EmailAutomationEnrollmentInterface\";\nimport { EmailAutomationInput, EmailAutomationInterface } from \"./EmailAutomationInterface\";\nimport { EmailAutomationTriggerInterface } from \"./EmailAutomationTriggerInterface\";\n\nexport class EmailAutomationService extends AbstractService {\n static async findMany(params: { next?: NextRef; previous?: PreviousRef } = {}): Promise<EmailAutomationInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.EmailAutomation });\n\n if (Modules.EmailAutomation.inclusions?.lists?.fields)\n endpoint.limitToFields(Modules.EmailAutomation.inclusions.lists.fields);\n if (Modules.EmailAutomation.inclusions?.lists?.types)\n endpoint.limitToType(Modules.EmailAutomation.inclusions.lists.types);\n\n return this.callApi<EmailAutomationInterface[]>({\n type: Modules.EmailAutomation,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n previous: params.previous,\n });\n }\n\n static async findOne(params: { id: string }): Promise<EmailAutomationInterface> {\n return this.callApi<EmailAutomationInterface>({\n type: Modules.EmailAutomation,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailAutomation, id: params.id }).generate(),\n });\n }\n\n static async create(params: EmailAutomationInput): Promise<EmailAutomationInterface> {\n return this.callApi<EmailAutomationInterface>({\n type: Modules.EmailAutomation,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailAutomation }).generate(),\n input: params,\n });\n }\n\n static async update(params: EmailAutomationInput): Promise<EmailAutomationInterface> {\n return this.callApi<EmailAutomationInterface>({\n type: Modules.EmailAutomation,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailAutomation, id: params.id }).generate(),\n input: params,\n });\n }\n\n static async delete(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.EmailAutomation,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailAutomation, id: params.id }).generate(),\n });\n }\n\n /**\n * GET email-automations/triggers: the triggers the app declared to the API.\n */\n static async findTriggers(): Promise<EmailAutomationTriggerInterface[]> {\n return this.callApi<EmailAutomationTriggerInterface[]>({\n type: Modules.EmailAutomationTrigger,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailAutomation, id: \"triggers\" }).generate(),\n });\n }\n\n /**\n * POST email-automations/:id/tidy: clears every manual step position. Replies 204.\n */\n static async tidy(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.EmailAutomation,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.EmailAutomation,\n id: params.id,\n childEndpoint: \"tidy\",\n }).generate(),\n });\n }\n\n static async findEnrollments(params: {\n automationId: string;\n status?: string;\n next?: NextRef;\n previous?: PreviousRef;\n }): Promise<EmailAutomationEnrollmentInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.EmailAutomation,\n id: params.automationId,\n childEndpoint: Modules.EmailAutomationEnrollment,\n });\n\n if (params.status) endpoint.addAdditionalParam(\"status\", params.status);\n\n return this.callApi<EmailAutomationEnrollmentInterface[]>({\n type: Modules.EmailAutomationEnrollment,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n previous: params.previous,\n });\n }\n\n /**\n * GET email-automations/:id/email-automation-candidates: users and waitlist\n * entries matching the search, each flagged addable or not by the API. Paged:\n * read through useDataListRetriever, which passes the next/previous refs.\n */\n static async findCandidates(params: {\n automationId: string;\n search?: string;\n next?: NextRef;\n previous?: PreviousRef;\n }): Promise<EmailAutomationCandidateInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.EmailAutomation,\n id: params.automationId,\n childEndpoint: Modules.EmailAutomationCandidate,\n });\n\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi<EmailAutomationCandidateInterface[]>({\n type: Modules.EmailAutomationCandidate,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n previous: params.previous,\n });\n }\n\n /**\n * POST email-automations/:id/email-automation-enrollments: adds one person by\n * hand. The API re-checks every rule and answers 400 with the reason.\n */\n static async addEnrollment(params: {\n automationId: string;\n input: EmailAutomationEnrollmentInput;\n }): Promise<EmailAutomationEnrollmentInterface> {\n return this.callApi<EmailAutomationEnrollmentInterface>({\n type: Modules.EmailAutomationEnrollment,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.EmailAutomation,\n id: params.automationId,\n childEndpoint: Modules.EmailAutomationEnrollment,\n }).generate(),\n input: params.input,\n });\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { EmailAutomationStep } from \"./EmailAutomationStep\";\nimport { EmailAutomationStepInput, EmailAutomationStepInterface } from \"./EmailAutomationStepInterface\";\n\nexport class EmailAutomationStepService extends AbstractService {\n static async findByAutomation(params: { automationId: string }): Promise<EmailAutomationStepInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.EmailAutomation,\n id: params.automationId,\n childEndpoint: Modules.EmailAutomationStep,\n });\n\n endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n\n return this.callApi<EmailAutomationStepInterface[]>({\n type: Modules.EmailAutomationStep,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n static async create(params: EmailAutomationStepInput): Promise<EmailAutomationStepInterface> {\n return this.callApi<EmailAutomationStepInterface>({\n type: Modules.EmailAutomationStep,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailAutomationStep }).generate(),\n input: params,\n });\n }\n\n static async update(params: EmailAutomationStepInput): Promise<EmailAutomationStepInterface> {\n return this.callApi<EmailAutomationStepInterface>({\n type: Modules.EmailAutomationStep,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailAutomationStep, id: params.id }).generate(),\n input: params,\n });\n }\n\n static async delete(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.EmailAutomationStep,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailAutomationStep, id: params.id }).generate(),\n });\n }\n\n /**\n * POST email-automation-steps/:stepId/next: continues the chain from stepId to nextStepId.\n */\n static async setNext(params: { stepId: string; nextStepId: string }): Promise<EmailAutomationStepInterface> {\n const step = new EmailAutomationStep();\n return this.callApi<EmailAutomationStepInterface>({\n type: Modules.EmailAutomationStep,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.EmailAutomationStep,\n id: params.stepId,\n childEndpoint: \"next\",\n }).generate(),\n input: step.createNextJsonApi({ nextStepId: params.nextStepId }),\n overridesJsonApiCreation: true,\n });\n }\n\n /**\n * DELETE email-automation-steps/:stepId/next: the step becomes the end of the chain.\n */\n static async clearNext(params: { stepId: string }): Promise<void> {\n await this.callApi({\n type: Modules.EmailAutomationStep,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({\n endpoint: Modules.EmailAutomationStep,\n id: params.stepId,\n childEndpoint: \"next\",\n }).generate(),\n });\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { EmailAutomationEnrollment } from \"./EmailAutomationEnrollment\";\nimport { EmailAutomationEnrollmentInterface } from \"./EmailAutomationEnrollmentInterface\";\n\nexport class EmailAutomationEnrollmentService extends AbstractService {\n /**\n * POST email-automations/unsubscribe/:token (public). The scope travels both as\n * a query parameter and in the body, as the API reads either.\n */\n static async unsubscribe(params: {\n token: string;\n scope: \"automation\" | \"all\";\n }): Promise<EmailAutomationEnrollmentInterface> {\n const enrollment = new EmailAutomationEnrollment();\n const endpoint = new EndpointCreator({\n endpoint: Modules.EmailAutomation,\n childEndpoint: \"unsubscribe\",\n childId: params.token,\n });\n\n endpoint.addAdditionalParam(\"scope\", params.scope);\n\n return this.callApi<EmailAutomationEnrollmentInterface>({\n type: Modules.EmailAutomationEnrollment,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: enrollment.createUnsubscribeJsonApi({ scope: params.scope }),\n overridesJsonApiCreation: true,\n });\n }\n}\n","export enum EmailAutomationFields {\n name = \"name\",\n purpose = \"purpose\",\n trigger = \"trigger\",\n status = \"status\",\n steps = \"steps\",\n active = \"active\",\n completed = \"completed\",\n unsubscribed = \"unsubscribed\",\n}\n","export enum EmailAutomationEnrollmentFields {\n email = \"email\",\n name = \"name\",\n status = \"status\",\n currentStep = \"currentStep\",\n nextSendAt = \"nextSendAt\",\n sentCount = \"sentCount\",\n}\n","import { SendIcon } from \"lucide-react\";\nimport { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { EmailBlast } from \"./data/EmailBlast\";\n\nexport const EmailBlastModule = (factory: ModuleFactory) =>\n factory({\n moduleId: \"8a1f4c2e-6b3d-4e9a-b7c5-2d0e9f1a6b48\",\n pageUrl: \"/administration/email-blasts\",\n name: \"email-blasts\",\n model: EmailBlast,\n icon: SendIcon,\n inclusions: {\n lists: {\n fields: [\n createJsonApiInclusion(\"email-blasts\", [\n \"subject\",\n \"purpose\",\n \"status\",\n \"sentAt\",\n \"recipientCount\",\n \"sentCount\",\n \"failedCount\",\n \"unsubscribedCount\",\n ]),\n ],\n },\n },\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { EmailPurpose } from \"../../email-automation/data/EmailPurpose\";\nimport { EmailBlastInput, EmailBlastInterface, EmailBlastStatus } from \"./EmailBlastInterface\";\n\nexport class EmailBlast extends AbstractApiData implements EmailBlastInterface {\n private _subject?: string;\n private _body?: string;\n private _replyTo?: string;\n private _purpose: EmailPurpose = \"marketing\";\n private _status: EmailBlastStatus = \"draft\";\n private _sentAt?: Date;\n private _recipientCount?: number;\n private _sentCount?: number;\n private _failedCount?: number;\n private _unsubscribedCount?: number;\n\n get subject(): string {\n return this._subject ?? \"\";\n }\n\n get body(): string {\n return this._body ?? \"\";\n }\n\n get replyTo(): string | undefined {\n return this._replyTo;\n }\n\n get purpose(): EmailPurpose {\n return this._purpose;\n }\n\n get status(): EmailBlastStatus {\n return this._status;\n }\n\n get sentAt(): Date | undefined {\n return this._sentAt;\n }\n\n get recipientCount(): number | undefined {\n return this._recipientCount;\n }\n\n get sentCount(): number {\n return this._sentCount ?? 0;\n }\n\n get failedCount(): number {\n return this._failedCount ?? 0;\n }\n\n get unsubscribedCount(): number {\n return this._unsubscribedCount ?? 0;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes;\n this._subject = attributes.subject;\n this._body = attributes.body;\n this._replyTo = attributes.replyTo ?? undefined;\n // A record saved before purpose existed counts as Marketing.\n this._purpose = attributes.purpose === \"service\" ? \"service\" : \"marketing\";\n this._status = attributes.status ?? \"draft\";\n this._sentAt = attributes.sentAt ? new Date(attributes.sentAt) : undefined;\n this._recipientCount = attributes.recipientCount ?? undefined;\n this._sentCount = attributes.sentCount;\n this._failedCount = attributes.failedCount;\n this._unsubscribedCount = attributes.unsubscribedCount;\n\n return this;\n }\n\n /**\n * Only the editable text and the purpose travel: status, counters and sentAt are set by the API.\n */\n createJsonApi(data: EmailBlastInput) {\n const response: any = {\n data: {\n type: Modules.EmailBlast.name,\n id: data.id,\n attributes: {},\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.subject !== undefined) response.data.attributes.subject = data.subject;\n if (data.body !== undefined) response.data.attributes.body = data.body;\n if (data.replyTo !== undefined) response.data.attributes.replyTo = data.replyTo;\n if (data.purpose !== undefined) response.data.attributes.purpose = data.purpose;\n\n return response;\n }\n}\n","import { MailIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { EmailBlastDelivery } from \"./data/EmailBlastDelivery\";\n\nexport const EmailBlastDeliveryModule = (factory: ModuleFactory) =>\n factory({\n moduleId: \"c47e9b1d-2a5f-4c83-9e60-1b7d4a3f8e25\",\n pageUrl: undefined,\n name: \"email-blast-deliveries\",\n model: EmailBlastDelivery,\n icon: MailIcon,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { EmailBlastDeliveryInterface, EmailBlastDeliveryStatus } from \"./EmailBlastDeliveryInterface\";\n\n/** JSON:API type of the POST email-blasts/unsubscribe/:token request body (not a module: nothing is stored). */\nexport const EMAIL_BLAST_UNSUBSCRIBE_TYPE = \"email-blast-unsubscribes\";\n\nexport class EmailBlastDelivery extends AbstractApiData implements EmailBlastDeliveryInterface {\n private _status: EmailBlastDeliveryStatus = \"pending\";\n\n get status(): EmailBlastDeliveryStatus {\n return this._status;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._status = data.jsonApi.attributes.status ?? \"pending\";\n\n return this;\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"EmailBlastDelivery is created by the API and cannot be created from the client\");\n }\n\n /**\n * Body of POST email-blasts/unsubscribe/:token.\n */\n createUnsubscribeJsonApi() {\n return {\n data: {\n type: EMAIL_BLAST_UNSUBSCRIBE_TYPE,\n },\n };\n }\n}\n","import { UsersIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { EmailBlastAudience } from \"./data/EmailBlastAudience\";\n\nexport const EmailBlastAudienceModule = (factory: ModuleFactory) =>\n factory({\n moduleId: \"e2b6d8f0-4c19-4a7e-8d35-6f9a0c2b7e14\",\n pageUrl: undefined,\n name: \"email-blast-audiences\",\n model: EmailBlastAudience,\n icon: UsersIcon,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { EmailBlastAudienceInterface } from \"./EmailBlastAudienceInterface\";\n\nexport class EmailBlastAudience extends AbstractApiData implements EmailBlastAudienceInterface {\n private _recipientCount?: number;\n\n get recipientCount(): number {\n return this._recipientCount ?? 0;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._recipientCount = data.jsonApi.attributes.recipientCount;\n\n return this;\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"EmailBlastAudience is computed by the API and is read-only\");\n }\n}\n","/**\n * URL segments of the email-blast child routes. The only place these strings\n * are written: services build every child URL from here through EndpointCreator.\n */\nexport const EMAIL_BLAST_SEGMENTS = {\n audience: \"recipient-count\",\n test: \"test\",\n send: \"send\",\n unsubscribe: \"unsubscribe\",\n} as const;\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../core\";\nimport { EMAIL_BLAST_SEGMENTS } from \"./email-blast.segments\";\nimport { EmailBlastAudienceInterface } from \"./EmailBlastAudienceInterface\";\nimport { EmailBlastInput, EmailBlastInterface } from \"./EmailBlastInterface\";\n\nexport class EmailBlastService extends AbstractService {\n static async findMany(params: { next?: NextRef; previous?: PreviousRef } = {}): Promise<EmailBlastInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.EmailBlast });\n\n if (Modules.EmailBlast.inclusions?.lists?.fields)\n endpoint.limitToFields(Modules.EmailBlast.inclusions.lists.fields);\n if (Modules.EmailBlast.inclusions?.lists?.types) endpoint.limitToType(Modules.EmailBlast.inclusions.lists.types);\n\n return this.callApi<EmailBlastInterface[]>({\n type: Modules.EmailBlast,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n previous: params.previous,\n });\n }\n\n static async findOne(params: { id: string }): Promise<EmailBlastInterface> {\n return this.callApi<EmailBlastInterface>({\n type: Modules.EmailBlast,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailBlast, id: params.id }).generate(),\n });\n }\n\n static async create(params: EmailBlastInput): Promise<EmailBlastInterface> {\n return this.callApi<EmailBlastInterface>({\n type: Modules.EmailBlast,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailBlast }).generate(),\n input: params,\n });\n }\n\n static async update(params: EmailBlastInput): Promise<EmailBlastInterface> {\n return this.callApi<EmailBlastInterface>({\n type: Modules.EmailBlast,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailBlast, id: params.id }).generate(),\n input: params,\n });\n }\n\n static async delete(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.EmailBlast,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.EmailBlast, id: params.id }).generate(),\n });\n }\n\n /**\n * GET email-blasts/:id/recipient-count: how many people the blast would reach now.\n */\n static async findAudience(params: { id: string }): Promise<EmailBlastAudienceInterface> {\n return this.callApi<EmailBlastAudienceInterface>({\n type: Modules.EmailBlastAudience,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({\n endpoint: Modules.EmailBlast,\n id: params.id,\n childEndpoint: EMAIL_BLAST_SEGMENTS.audience,\n }).generate(),\n });\n }\n\n /**\n * POST email-blasts/:id/test: sends the draft to the calling administrator. Replies 204.\n */\n static async sendTest(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.EmailBlast,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.EmailBlast,\n id: params.id,\n childEndpoint: EMAIL_BLAST_SEGMENTS.test,\n }).generate(),\n });\n }\n\n /**\n * POST email-blasts/:id/send: starts the send and returns the blast, now `sending`.\n */\n static async send(params: { id: string }): Promise<EmailBlastInterface> {\n return this.callApi<EmailBlastInterface>({\n type: Modules.EmailBlast,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.EmailBlast,\n id: params.id,\n childEndpoint: EMAIL_BLAST_SEGMENTS.send,\n }).generate(),\n });\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { EMAIL_BLAST_SEGMENTS } from \"./email-blast.segments\";\nimport { EmailBlastDelivery } from \"./EmailBlastDelivery\";\n\nexport class EmailBlastDeliveryService extends AbstractService {\n /**\n * POST email-blasts/unsubscribe/:token (public). Replies 204: the token holder\n * receives nothing back.\n */\n static async unsubscribe(params: { token: string }): Promise<void> {\n const delivery = new EmailBlastDelivery();\n\n await this.callApi({\n type: Modules.EmailBlastDelivery,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.EmailBlast,\n id: EMAIL_BLAST_SEGMENTS.unsubscribe,\n childEndpoint: params.token,\n }).generate(),\n input: delivery.createUnsubscribeJsonApi(),\n overridesJsonApiCreation: true,\n });\n }\n}\n","export enum EmailBlastFields {\n subject = \"subject\",\n purpose = \"purpose\",\n status = \"status\",\n sentAt = \"sentAt\",\n sent = \"sent\",\n failed = \"failed\",\n unsubscribed = \"unsubscribed\",\n}\n","import { NewspaperIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { BlogCategory } from \"./data/BlogCategory\";\nimport { BlogCategoryTranslation } from \"./data/BlogCategoryTranslation\";\nimport { BlogPost } from \"./data/BlogPost\";\nimport { BlogPostTranslation } from \"./data/BlogPostTranslation\";\nimport { BlogSettings } from \"./data/BlogSettings\";\nimport { BLOG_MODULE_ID } from \"./types/blog.constants\";\n\nexport const BlogPostModule = (factory: ModuleFactory) =>\n factory({\n moduleId: BLOG_MODULE_ID,\n pageUrl: \"/administration/blog\",\n name: \"blog-posts\",\n model: BlogPost,\n icon: NewspaperIcon,\n });\n\nexport const BlogPostTranslationModule = (factory: ModuleFactory) =>\n factory({\n moduleId: BLOG_MODULE_ID,\n name: \"blog-post-translations\",\n model: BlogPostTranslation,\n icon: NewspaperIcon,\n });\n\nexport const BlogCategoryModule = (factory: ModuleFactory) =>\n factory({\n moduleId: BLOG_MODULE_ID,\n name: \"blog-categories\",\n model: BlogCategory,\n icon: NewspaperIcon,\n });\n\nexport const BlogCategoryTranslationModule = (factory: ModuleFactory) =>\n factory({\n moduleId: BLOG_MODULE_ID,\n name: \"blog-category-translations\",\n model: BlogCategoryTranslation,\n icon: NewspaperIcon,\n });\n\nexport const BlogSettingsModule = (factory: ModuleFactory) =>\n factory({\n moduleId: BLOG_MODULE_ID,\n name: \"blog-settings\",\n model: BlogSettings,\n icon: NewspaperIcon,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { BlogCategoryInput, BlogCategoryInterface } from \"./BlogCategoryInterface\";\nimport { BlogCategoryTranslationInterface } from \"./BlogCategoryTranslationInterface\";\n\nexport class BlogCategory extends AbstractApiData implements BlogCategoryInterface {\n private _originalLanguage?: string;\n private _image?: string;\n private _translations?: BlogCategoryTranslationInterface[];\n\n private _language?: string;\n private _name?: string;\n private _slug?: string;\n private _description?: string;\n\n get originalLanguage(): string {\n return this._originalLanguage ?? \"\";\n }\n\n get image(): string | undefined {\n return this._image;\n }\n\n get translations(): BlogCategoryTranslationInterface[] {\n return this._translations ?? [];\n }\n\n get original(): BlogCategoryTranslationInterface | undefined {\n return this.translations.find((translation) => translation.isOriginal);\n }\n\n get language(): string | undefined {\n return this._language;\n }\n\n get name(): string | undefined {\n return this._name;\n }\n\n get slug(): string | undefined {\n return this._slug;\n }\n\n get description(): string | undefined {\n return this._description;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes ?? {};\n const meta = data.jsonApi.meta ?? {};\n // The resolved-language values are computed on the API. Depending on the\n // descriptor they arrive as attributes or as meta, so both are read.\n const resolved = (key: string) => attributes[key] ?? meta[key] ?? undefined;\n\n this._originalLanguage = attributes.originalLanguage;\n this._image = attributes.image ?? undefined;\n\n this._language = resolved(\"language\");\n this._name = resolved(\"name\");\n this._slug = resolved(\"slug\");\n this._description = resolved(\"description\");\n\n this._translations = this._readIncluded(\n data,\n \"translations\",\n Modules.BlogCategoryTranslation,\n ) as BlogCategoryTranslationInterface[];\n\n return this;\n }\n\n createJsonApi(data: BlogCategoryInput) {\n const response: any = {\n data: {\n type: Modules.BlogCategory.name,\n id: data.id,\n attributes: {},\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.originalLanguage !== undefined) response.data.attributes.originalLanguage = data.originalLanguage;\n if (data.name !== undefined) response.data.attributes.name = data.name;\n if (data.slug !== undefined) response.data.attributes.slug = data.slug;\n if (data.description !== undefined) response.data.attributes.description = data.description;\n if (data.image !== undefined) response.data.attributes.image = data.image;\n\n return response;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { BlogAiStatus, BlogTranslationSource } from \"../types/blog.constants\";\nimport { BlogCategoryInterface } from \"./BlogCategoryInterface\";\nimport { BlogCategoryTranslationInput, BlogCategoryTranslationInterface } from \"./BlogCategoryTranslationInterface\";\n\nexport class BlogCategoryTranslation extends AbstractApiData implements BlogCategoryTranslationInterface {\n private _language?: string;\n private _isOriginal?: boolean;\n private _source?: BlogTranslationSource;\n private _aiStatus?: BlogAiStatus;\n private _name?: string;\n private _slug?: string;\n private _description?: string;\n private _translatedAt?: Date;\n private _category?: BlogCategoryInterface;\n\n get language(): string {\n return this._language ?? \"\";\n }\n\n get isOriginal(): boolean {\n return this._isOriginal ?? false;\n }\n\n get source(): BlogTranslationSource {\n return this._source ?? \"ai\";\n }\n\n get aiStatus(): BlogAiStatus {\n return this._aiStatus ?? \"pending\";\n }\n\n get name(): string {\n return this._name ?? \"\";\n }\n\n get slug(): string {\n return this._slug ?? \"\";\n }\n\n get description(): string | undefined {\n return this._description;\n }\n\n get translatedAt(): Date | undefined {\n return this._translatedAt;\n }\n\n get category(): BlogCategoryInterface | undefined {\n return this._category;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes ?? {};\n\n this._language = attributes.language;\n this._isOriginal = attributes.isOriginal === true;\n this._source = attributes.source;\n this._aiStatus = attributes.aiStatus;\n this._name = attributes.name ?? undefined;\n this._slug = attributes.slug ?? undefined;\n this._description = attributes.description ?? undefined;\n this._translatedAt = attributes.translatedAt ? new Date(attributes.translatedAt) : undefined;\n\n this._category = this._readIncluded(data, \"category\", Modules.BlogCategory) as BlogCategoryInterface;\n\n return this;\n }\n\n createJsonApi(data: BlogCategoryTranslationInput) {\n const response: any = {\n data: {\n type: Modules.BlogCategoryTranslation.name,\n id: data.id,\n attributes: {},\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.name !== undefined) response.data.attributes.name = data.name;\n if (data.slug !== undefined) response.data.attributes.slug = data.slug;\n if (data.description !== undefined) response.data.attributes.description = data.description;\n\n return response;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { UserInterface } from \"../../user/data/user.interface\";\nimport { BlogPostStatus } from \"../types/blog.constants\";\nimport { BlogCategoryInterface } from \"./BlogCategoryInterface\";\nimport { BlogPostInput, BlogPostInterface } from \"./BlogPostInterface\";\nimport { BlogPostTranslationInterface } from \"./BlogPostTranslationInterface\";\n\nexport class BlogPost extends AbstractApiData implements BlogPostInterface {\n private _status?: BlogPostStatus;\n private _originalLanguage?: string;\n private _coverImage?: string;\n private _publishedAt?: Date;\n private _author?: UserInterface;\n private _categories?: BlogCategoryInterface[];\n private _translations?: BlogPostTranslationInterface[];\n\n private _language?: string;\n private _title?: string;\n private _slug?: string;\n private _summary?: string;\n private _body?: any;\n private _coverImageAlt?: string;\n private _readingMinutes?: number;\n private _authorName?: string;\n private _categoryIds?: string[];\n\n get status(): BlogPostStatus {\n return this._status ?? \"draft\";\n }\n\n get originalLanguage(): string {\n return this._originalLanguage ?? \"\";\n }\n\n get coverImage(): string | undefined {\n return this._coverImage;\n }\n\n get publishedAt(): Date | undefined {\n return this._publishedAt;\n }\n\n get author(): UserInterface | undefined {\n return this._author;\n }\n\n get categories(): BlogCategoryInterface[] {\n return this._categories ?? [];\n }\n\n get translations(): BlogPostTranslationInterface[] {\n return this._translations ?? [];\n }\n\n get original(): BlogPostTranslationInterface | undefined {\n return this.translations.find((translation) => translation.isOriginal);\n }\n\n get language(): string | undefined {\n return this._language;\n }\n\n get title(): string | undefined {\n return this._title;\n }\n\n get slug(): string | undefined {\n return this._slug;\n }\n\n get summary(): string | undefined {\n return this._summary;\n }\n\n get body(): any {\n return this._body ?? [];\n }\n\n get coverImageAlt(): string | undefined {\n return this._coverImageAlt;\n }\n\n get readingMinutes(): number | undefined {\n return this._readingMinutes;\n }\n\n get authorName(): string | undefined {\n return this._authorName;\n }\n\n get categoryIds(): string[] {\n return this._categoryIds ?? [];\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes ?? {};\n const meta = data.jsonApi.meta ?? {};\n // The resolved-language values are computed on the API. Depending on the\n // descriptor they arrive as attributes or as meta, so both are read.\n const resolved = (key: string) => attributes[key] ?? meta[key] ?? undefined;\n\n this._status = attributes.status;\n this._originalLanguage = attributes.originalLanguage;\n this._coverImage = attributes.coverImage ?? undefined;\n this._publishedAt = attributes.publishedAt ? new Date(attributes.publishedAt) : undefined;\n\n this._language = resolved(\"language\");\n this._title = resolved(\"title\");\n this._slug = resolved(\"slug\");\n this._summary = resolved(\"summary\");\n const body = resolved(\"body\");\n this._body = body ? JSON.parse(body) : undefined;\n this._coverImageAlt = resolved(\"coverImageAlt\");\n this._readingMinutes = resolved(\"readingMinutes\");\n this._authorName = resolved(\"authorName\");\n this._categoryIds = resolved(\"categoryIds\") ?? [];\n\n this._author = this._readIncluded(data, \"author\", Modules.User) as UserInterface;\n this._categories = this._readIncluded(data, \"categories\", Modules.BlogCategory) as BlogCategoryInterface[];\n this._translations = this._readIncluded(\n data,\n \"translations\",\n Modules.BlogPostTranslation,\n ) as BlogPostTranslationInterface[];\n\n return this;\n }\n\n createJsonApi(data: BlogPostInput) {\n const response: any = {\n data: {\n type: Modules.BlogPost.name,\n id: data.id,\n attributes: {},\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.originalLanguage !== undefined) response.data.attributes.originalLanguage = data.originalLanguage;\n if (data.title !== undefined) response.data.attributes.title = data.title;\n if (data.slug !== undefined) response.data.attributes.slug = data.slug;\n if (data.summary !== undefined) response.data.attributes.summary = data.summary;\n if (data.body !== undefined) response.data.attributes.body = JSON.stringify(data.body);\n if (data.coverImage !== undefined) response.data.attributes.coverImage = data.coverImage;\n if (data.coverImageAlt !== undefined) response.data.attributes.coverImageAlt = data.coverImageAlt;\n\n if (data.categoryIds !== undefined) {\n response.data.relationships.categories = {\n data: data.categoryIds.map((id) => ({ type: Modules.BlogCategory.name, id })),\n };\n }\n\n return response;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { BlogAiStatus, BlogTranslationSource } from \"../types/blog.constants\";\nimport { BlogPostInterface } from \"./BlogPostInterface\";\nimport { BlogPostTranslationInput, BlogPostTranslationInterface } from \"./BlogPostTranslationInterface\";\n\nexport class BlogPostTranslation extends AbstractApiData implements BlogPostTranslationInterface {\n private _language?: string;\n private _isOriginal?: boolean;\n private _source?: BlogTranslationSource;\n private _aiStatus?: BlogAiStatus;\n private _title?: string;\n private _slug?: string;\n private _summary?: string;\n private _body?: any;\n private _coverImageAlt?: string;\n private _readingMinutes?: number;\n private _translatedAt?: Date;\n private _post?: BlogPostInterface;\n\n get language(): string {\n return this._language ?? \"\";\n }\n\n get isOriginal(): boolean {\n return this._isOriginal ?? false;\n }\n\n get source(): BlogTranslationSource {\n return this._source ?? \"ai\";\n }\n\n get aiStatus(): BlogAiStatus {\n return this._aiStatus ?? \"pending\";\n }\n\n get title(): string {\n return this._title ?? \"\";\n }\n\n get slug(): string {\n return this._slug ?? \"\";\n }\n\n get summary(): string | undefined {\n return this._summary;\n }\n\n get body(): any {\n return this._body ?? [];\n }\n\n get coverImageAlt(): string | undefined {\n return this._coverImageAlt;\n }\n\n get readingMinutes(): number {\n return this._readingMinutes ?? 1;\n }\n\n get translatedAt(): Date | undefined {\n return this._translatedAt;\n }\n\n get post(): BlogPostInterface | undefined {\n return this._post;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes ?? {};\n\n this._language = attributes.language;\n this._isOriginal = attributes.isOriginal === true;\n this._source = attributes.source;\n this._aiStatus = attributes.aiStatus;\n this._title = attributes.title ?? undefined;\n this._slug = attributes.slug ?? undefined;\n this._summary = attributes.summary ?? undefined;\n this._body = attributes.body ? JSON.parse(attributes.body) : undefined;\n this._coverImageAlt = attributes.coverImageAlt ?? undefined;\n this._readingMinutes = attributes.readingMinutes ?? undefined;\n this._translatedAt = attributes.translatedAt ? new Date(attributes.translatedAt) : undefined;\n\n this._post = this._readIncluded(data, \"post\", Modules.BlogPost) as BlogPostInterface;\n\n return this;\n }\n\n createJsonApi(data: BlogPostTranslationInput) {\n const response: any = {\n data: {\n type: Modules.BlogPostTranslation.name,\n id: data.id,\n attributes: {},\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.title !== undefined) response.data.attributes.title = data.title;\n if (data.slug !== undefined) response.data.attributes.slug = data.slug;\n if (data.summary !== undefined) response.data.attributes.summary = data.summary;\n if (data.body !== undefined) response.data.attributes.body = JSON.stringify(data.body);\n if (data.coverImageAlt !== undefined) response.data.attributes.coverImageAlt = data.coverImageAlt;\n\n return response;\n }\n}\n","// Mirrors packages/nestjs-neo4jsonapi/src/foundations/blog/blog.constants.ts and\n// CoreModuleId.Blog. The two packages share no code, so the values are repeated.\n\nexport const BLOG_LIST_LAYOUTS = [\"featured\", \"column\", \"rail\"] as const;\nexport type BlogListLayout = (typeof BLOG_LIST_LAYOUTS)[number];\n\nexport const BLOG_POST_STATUSES = [\"draft\", \"published\", \"unpublished\"] as const;\nexport type BlogPostStatus = (typeof BLOG_POST_STATUSES)[number];\n\nexport const BLOG_TRANSLATION_SOURCES = [\"original\", \"ai\", \"manual\"] as const;\nexport type BlogTranslationSource = (typeof BLOG_TRANSLATION_SOURCES)[number];\n\n// Same values as the backend AiStatus enum.\nexport type BlogAiStatus = \"pending\" | \"in_progress\" | \"completed\" | \"failed\";\n\nexport const BLOG_SETTINGS_ID = \"5f3c2a9e-8d41-4b7a-9c2e-1a6b0d4e7f13\";\n\nexport const BLOG_MODULE_ID = \"c1d0e6a2-4b8f-4f3e-9a7d-2e5b8c6f1a90\";\n\n/**\n * Storage root for blog images (covers, category images, images pasted into a\n * post body). Blog content belongs to the platform, not to a company, and the\n * administrators who write it may belong to no company, so the keys are\n * `blog/<folder>/<id>/<file>` instead of `companies/<companyId>/...`. An app's\n * S3 key access policy must allow Administrators on this root.\n */\nexport const BLOG_UPLOAD_ROOT = \"blog\";\nexport const BLOG_POST_UPLOAD_PREFIX = `${BLOG_UPLOAD_ROOT}/blog-posts`;\nexport const BLOG_CATEGORY_UPLOAD_PREFIX = `${BLOG_UPLOAD_ROOT}/blog-categories`;\n\n/** Path prefix of the token-free public blog endpoints (`public/blog-posts`, ...). */\nexport const BLOG_PUBLIC_PREFIX = \"public\";\n\n/** Action segments under a blog resource (`blog-posts/:id/publish`, ...). */\nexport const BLOG_ACTION = {\n publish: \"publish\",\n unpublish: \"unpublish\",\n retranslate: \"retranslate\",\n related: \"related\",\n} as const;\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { BLOG_LIST_LAYOUTS, BlogListLayout } from \"../types/blog.constants\";\nimport { BlogSettingsInput, BlogSettingsInterface } from \"./BlogSettingsInterface\";\n\nexport class BlogSettings extends AbstractApiData implements BlogSettingsInterface {\n private _listLayout?: BlogListLayout;\n\n get listLayout(): BlogListLayout {\n return this._listLayout ?? \"featured\";\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const listLayout = data.jsonApi.attributes?.listLayout;\n this._listLayout = BLOG_LIST_LAYOUTS.includes(listLayout) ? listLayout : undefined;\n\n return this;\n }\n\n createJsonApi(data: BlogSettingsInput) {\n const response: any = {\n data: {\n type: Modules.BlogSettings.name,\n id: data.id,\n attributes: {},\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n response.data.attributes.listLayout = data.listLayout;\n\n return response;\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef } from \"../../../core\";\nimport { BLOG_ACTION } from \"../types/blog.constants\";\nimport { BlogPostInput, BlogPostInterface } from \"./BlogPostInterface\";\n\nexport class BlogPostService extends AbstractService {\n static async findMany(params: { search?: string; next?: NextRef } = {}): Promise<BlogPostInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.BlogPost });\n\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi<BlogPostInterface[]>({\n type: Modules.BlogPost,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findOne(params: { id: string }): Promise<BlogPostInterface> {\n return this.callApi<BlogPostInterface>({\n type: Modules.BlogPost,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogPost, id: params.id }).generate(),\n });\n }\n\n static async create(params: BlogPostInput): Promise<BlogPostInterface> {\n return this.callApi<BlogPostInterface>({\n type: Modules.BlogPost,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogPost }).generate(),\n input: params,\n });\n }\n\n static async update(params: BlogPostInput): Promise<BlogPostInterface> {\n return this.callApi<BlogPostInterface>({\n type: Modules.BlogPost,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogPost, id: params.id }).generate(),\n input: params,\n });\n }\n\n static async delete(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.BlogPost,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogPost, id: params.id }).generate(),\n });\n }\n\n /**\n * POST blog-posts/:id/publish. Sends no body; replies with the post.\n */\n static async publish(params: { id: string }): Promise<BlogPostInterface> {\n return this.callApi<BlogPostInterface>({\n type: Modules.BlogPost,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.BlogPost,\n id: params.id,\n childEndpoint: BLOG_ACTION.publish,\n }).generate(),\n });\n }\n\n /**\n * POST blog-posts/:id/unpublish. Sends no body; replies with the post.\n */\n static async unpublish(params: { id: string }): Promise<BlogPostInterface> {\n return this.callApi<BlogPostInterface>({\n type: Modules.BlogPost,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.BlogPost,\n id: params.id,\n childEndpoint: BLOG_ACTION.unpublish,\n }).generate(),\n });\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { BLOG_ACTION } from \"../types/blog.constants\";\nimport { BlogPostTranslationInput, BlogPostTranslationInterface } from \"./BlogPostTranslationInterface\";\n\nexport class BlogPostTranslationService extends AbstractService {\n static async update(params: BlogPostTranslationInput): Promise<BlogPostTranslationInterface> {\n return this.callApi<BlogPostTranslationInterface>({\n type: Modules.BlogPostTranslation,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogPostTranslation, id: params.id }).generate(),\n input: params,\n });\n }\n\n /**\n * POST blog-post-translations/:id/retranslate. Re-queues this one language\n * from the current original. Replies 204.\n */\n static async retranslate(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.BlogPostTranslation,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.BlogPostTranslation,\n id: params.id,\n childEndpoint: BLOG_ACTION.retranslate,\n }).generate(),\n });\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef } from \"../../../core\";\nimport { BlogCategoryInput, BlogCategoryInterface } from \"./BlogCategoryInterface\";\n\nexport class BlogCategoryService extends AbstractService {\n static async findMany(\n params: { search?: string; fetchAll?: boolean; next?: NextRef } = {},\n ): Promise<BlogCategoryInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.BlogCategory });\n\n if (params.fetchAll) endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi<BlogCategoryInterface[]>({\n type: Modules.BlogCategory,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findOne(params: { id: string }): Promise<BlogCategoryInterface> {\n return this.callApi<BlogCategoryInterface>({\n type: Modules.BlogCategory,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogCategory, id: params.id }).generate(),\n });\n }\n\n static async create(params: BlogCategoryInput): Promise<BlogCategoryInterface> {\n return this.callApi<BlogCategoryInterface>({\n type: Modules.BlogCategory,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogCategory }).generate(),\n input: params,\n });\n }\n\n static async update(params: BlogCategoryInput): Promise<BlogCategoryInterface> {\n return this.callApi<BlogCategoryInterface>({\n type: Modules.BlogCategory,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogCategory, id: params.id }).generate(),\n input: params,\n });\n }\n\n static async delete(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.BlogCategory,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogCategory, id: params.id }).generate(),\n });\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { BLOG_ACTION } from \"../types/blog.constants\";\nimport { BlogCategoryTranslationInput, BlogCategoryTranslationInterface } from \"./BlogCategoryTranslationInterface\";\n\nexport class BlogCategoryTranslationService extends AbstractService {\n static async update(params: BlogCategoryTranslationInput): Promise<BlogCategoryTranslationInterface> {\n return this.callApi<BlogCategoryTranslationInterface>({\n type: Modules.BlogCategoryTranslation,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogCategoryTranslation, id: params.id }).generate(),\n input: params,\n });\n }\n\n /**\n * POST blog-category-translations/:id/retranslate. Re-queues this one\n * language from the current original. Replies 204.\n */\n static async retranslate(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.BlogCategoryTranslation,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.BlogCategoryTranslation,\n id: params.id,\n childEndpoint: BLOG_ACTION.retranslate,\n }).generate(),\n });\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { BlogSettingsInput, BlogSettingsInterface } from \"./BlogSettingsInterface\";\n\nexport class BlogSettingsService extends AbstractService {\n /**\n * GET blog-settings: the stored singleton, or the API's default.\n */\n static async get(): Promise<BlogSettingsInterface> {\n return this.callApi<BlogSettingsInterface>({\n type: Modules.BlogSettings,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogSettings }).generate(),\n });\n }\n\n static async save(params: BlogSettingsInput): Promise<BlogSettingsInterface> {\n return this.callApi<BlogSettingsInterface>({\n type: Modules.BlogSettings,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({ endpoint: Modules.BlogSettings, id: params.id }).generate(),\n input: params,\n });\n }\n}\n","export enum BlogPostFields {\n title = \"title\",\n status = \"status\",\n categories = \"categories\",\n publishedAt = \"publishedAt\",\n languages = \"languages\",\n}\n","export enum BlogCategoryFields {\n name = \"name\",\n languages = \"languages\",\n}\n","import { BookOpenIcon } from \"lucide-react\";\nimport { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { HandbookPage } from \"./data/HandbookPage\";\n\nexport const HandbookPageModule = (factory: ModuleFactory) =>\n factory({\n moduleId: \"b1f1c2d6-6a0e-4e93-9b6d-3f5f4a2c81e7\",\n pageUrl: \"/administration/handbook\",\n name: \"handbookpages\",\n model: HandbookPage,\n icon: BookOpenIcon,\n inclusions: {\n lists: {\n // `content` is deliberately omitted: the list renders titles and\n // status, and a thousand pages of markdown do not belong in a list\n // payload. `section`, `order` and `summary` are the index fields the\n // contents page groups, orders and describes pages by, so they are the\n // one part of the payload the list surface cannot do without.\n //\n // `displayTitle` and `displaySummary` are the translated halves of the\n // two strings the contents page prints, and a sparse fieldset that\n // omitted them would leave every row in English. `displayContent` is\n // NOT here for the same reason `content` is not: it is the whole\n // markdown body, and it belongs to the detail read alone.\n fields: [\n createJsonApiInclusion(\"handbookpages\", [\n \"path\",\n \"title\",\n \"displayTitle\",\n \"section\",\n \"order\",\n \"summary\",\n \"displaySummary\",\n \"wordCount\",\n \"aiStatus\",\n ]),\n ],\n },\n },\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { HandbookPageInput } from \"./HandbookPageInput\";\nimport { HandbookPageInterface } from \"./HandbookPageInterface\";\n\nexport class HandbookPage extends AbstractApiData implements HandbookPageInterface {\n private _path?: string;\n private _title?: string;\n private _content?: string;\n private _contentHash?: string;\n private _wordCount?: number;\n private _aiStatus?: string;\n private _section?: string;\n private _order?: string;\n private _summary?: string;\n private _displayTitle?: string;\n private _displaySummary?: string;\n private _displayContent?: string;\n\n get path(): string {\n return this._path ?? \"\";\n }\n\n get title(): string {\n return this._title ?? \"\";\n }\n\n get content(): string {\n return this._content ?? \"\";\n }\n\n get contentHash(): string {\n return this._contentHash ?? \"\";\n }\n\n get wordCount(): number {\n return this._wordCount ?? 0;\n }\n\n get aiStatus(): string | undefined {\n return this._aiStatus;\n }\n\n get section(): string {\n return this._section ?? \"\";\n }\n\n get order(): string {\n return this._order ?? \"\";\n }\n\n get summary(): string | undefined {\n return this._summary;\n }\n\n get displayTitle(): string | undefined {\n return this._displayTitle;\n }\n\n get displaySummary(): string | undefined {\n return this._displaySummary;\n }\n\n get displayContent(): string | undefined {\n return this._displayContent;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._path = data.jsonApi.attributes.path;\n this._title = data.jsonApi.attributes.title;\n this._content = data.jsonApi.attributes.content;\n this._contentHash = data.jsonApi.attributes.contentHash;\n this._wordCount = data.jsonApi.attributes.wordCount;\n this._aiStatus = data.jsonApi.attributes.aiStatus;\n this._section = data.jsonApi.attributes.section;\n this._order = data.jsonApi.attributes.order;\n this._summary = data.jsonApi.attributes.summary;\n this._displayTitle = data.jsonApi.attributes.displayTitle;\n this._displaySummary = data.jsonApi.attributes.displaySummary;\n this._displayContent = data.jsonApi.attributes.displayContent;\n\n return this;\n }\n\n createJsonApi(data: HandbookPageInput) {\n const response: any = {\n data: {\n type: Modules.HandbookPage.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n return response;\n }\n}\n","import { LibraryIcon } from \"lucide-react\";\nimport { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { HandbookSection } from \"./data/HandbookSection\";\n\n/**\n * One top-level directory of the documentation tree. Like the thread message,\n * it has no `pageUrl` — a section has no page of its own; it is a heading on\n * the contents surface and nothing more.\n */\nexport const HandbookSectionModule = (factory: ModuleFactory) =>\n factory({\n moduleId: \"6b0a2f74-1c53-4a0e-9c1d-2e7b4a9d6f30\",\n name: \"handbooksections\",\n model: HandbookSection,\n icon: LibraryIcon,\n inclusions: {\n lists: {\n // `HandbookSectionService.findMany` is the ONLY read of this resource and\n // it limits to these fields, so the translated halves have to be here:\n // without them every section heading on the contents page and in the\n // reader's navigator would stay English however good the translation is.\n fields: [\n createJsonApiInclusion(\"handbooksections\", [\n \"key\",\n \"title\",\n \"displayTitle\",\n \"summary\",\n \"displaySummary\",\n \"order\",\n ]),\n ],\n },\n },\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { HandbookSectionInput } from \"./HandbookSectionInput\";\nimport { HandbookSectionInterface } from \"./HandbookSectionInterface\";\n\nexport class HandbookSection extends AbstractApiData implements HandbookSectionInterface {\n private _key?: string;\n private _title?: string;\n private _summary?: string;\n private _order?: string;\n private _displayTitle?: string;\n private _displaySummary?: string;\n\n get key(): string {\n return this._key ?? \"\";\n }\n\n get title(): string {\n return this._title ?? \"\";\n }\n\n get summary(): string | undefined {\n return this._summary;\n }\n\n get order(): string {\n return this._order ?? \"\";\n }\n\n get displayTitle(): string | undefined {\n return this._displayTitle;\n }\n\n get displaySummary(): string | undefined {\n return this._displaySummary;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._key = data.jsonApi.attributes.key;\n this._title = data.jsonApi.attributes.title;\n this._summary = data.jsonApi.attributes.summary;\n this._order = data.jsonApi.attributes.order;\n this._displayTitle = data.jsonApi.attributes.displayTitle;\n this._displaySummary = data.jsonApi.attributes.displaySummary;\n\n return this;\n }\n\n /**\n * Nothing calls this: sections are read-only over HTTP and written only by\n * the ingest. It exists because every model implements `createJsonApi()`, and\n * a model that throws on serialisation is a trap for the next writer.\n */\n createJsonApi(data: HandbookSectionInput) {\n const response: any = {\n data: {\n type: Modules.HandbookSection.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.key !== undefined) response.data.attributes.key = data.key;\n if (data.title !== undefined) response.data.attributes.title = data.title;\n if (data.summary !== undefined) response.data.attributes.summary = data.summary;\n if (data.order !== undefined) response.data.attributes.order = data.order;\n\n return response;\n }\n}\n","import { MessageCircleQuestionIcon } from \"lucide-react\";\nimport { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { HandbookThreadMessage } from \"./data/HandbookThreadMessage\";\n\n/**\n * One turn of a handbook conversation. Like the thread, it has no `pageUrl` —\n * a message is only ever read as part of its thread.\n */\nexport const HandbookThreadMessageModule = (factory: ModuleFactory) =>\n factory({\n moduleId: \"bef354f2-70e1-4f4f-9953-10d3d74cad0c\",\n name: \"handbookthreadmessages\",\n model: HandbookThreadMessage,\n icon: MessageCircleQuestionIcon,\n inclusions: {\n lists: {\n fields: [createJsonApiInclusion(\"handbookthreadmessages\", [\"role\", \"content\", \"position\", \"sources\"])],\n },\n },\n });\n","import { v4 as uuidv4 } from \"uuid\";\nimport { AbstractApiData, ApiDataInterface, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport type {\n AssistantMessageRole,\n AssistantMessageType,\n} from \"../../assistant-message/data/AssistantMessageInterface\";\nimport { ChunkInterface, ChunkRelationshipMeta } from \"../../chunk/data/ChunkInterface\";\nimport { HandbookThreadMessageInput, HandbookThreadMessageInterface } from \"./HandbookThreadMessageInterface\";\n\n/**\n * One persisted handbook turn.\n *\n * The getters below are the structural contract `AssistantMessageInterface`\n * asks for, which is what lets `AssistantThread` render handbook messages with\n * no adapter. The handbook has no token accounting, no entity references, no\n * chunk citations and no approval actions, so those answer `undefined` / `[]`\n * rather than being faked.\n */\nexport class HandbookThreadMessage extends AbstractApiData implements HandbookThreadMessageInterface {\n private _role?: AssistantMessageRole;\n private _content?: string;\n private _position?: number;\n private _sources?: string[];\n private _isOptimistic = false;\n\n get role(): AssistantMessageRole {\n return this._role ?? \"assistant\";\n }\n\n get content(): string {\n return this._content ?? \"\";\n }\n\n get position(): number {\n return this._position ?? 0;\n }\n\n get sources(): string[] {\n return this._sources ?? [];\n }\n\n /** The handbook agent returns no follow-ups. */\n get suggestedQuestions(): string[] {\n return [];\n }\n\n /** Token accounting is not billed against a handbook thread. */\n get inputTokens(): number | undefined {\n return undefined;\n }\n\n get outputTokens(): number | undefined {\n return undefined;\n }\n\n /** The handbook answers from pages, never from product entities. */\n get references(): ApiDataInterface[] {\n return [];\n }\n\n /** Cited pages travel in `sources`, not as Chunk resources. */\n get citations(): (ChunkInterface & ChunkRelationshipMeta)[] {\n return [];\n }\n\n /** Set only by `buildLocal`; a rehydrated message is always real. */\n get isOptimistic(): boolean {\n return this._isOptimistic;\n }\n\n /** There is no operator engine behind the handbook, so never an approval request. */\n get messageType(): AssistantMessageType {\n return \"text\";\n }\n\n get actionId(): string | undefined {\n return undefined;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes ?? {};\n this._role = attributes.role as AssistantMessageRole | undefined;\n this._content = attributes.content;\n this._position = typeof attributes.position === \"number\" ? attributes.position : Number(attributes.position ?? 0);\n this._sources = Array.isArray(attributes.sources) ? attributes.sources : [];\n\n return this;\n }\n\n /**\n * The envelope `POST /handbookthreads/:id/handbookthreadmessages` accepts:\n * only the question. Role and position are derived server-side from the\n * thread, and there is no client-chosen id for the pair of messages the turn\n * creates.\n *\n * `handbookPageId` rides the same way `question` does, through this method\n * and never through `overridesJsonApiCreation`. It is written only when the\n * caller supplied it: the DTO takes it as optional, and sending an explicit\n * `null` would fail validation rather than mean \"the whole manual\".\n */\n createJsonApi(data: HandbookThreadMessageInput) {\n return {\n data: {\n type: Modules.HandbookThreadMessage.name,\n attributes: {\n question: data.question,\n ...(data.handbookPageId !== undefined ? { handbookPageId: data.handbookPageId } : {}),\n },\n },\n included: [],\n };\n }\n\n /**\n * A message that exists only on the client: the optimistic user turn while\n * the agent runs, and the failure notice when it does not answer. Mirrors\n * `AssistantMessage.buildLocal`, which the stateless handbook surface used\n * before threads existed.\n */\n static buildLocal(params: {\n role: AssistantMessageRole;\n content: string;\n position: number;\n isOptimistic?: boolean;\n }): HandbookThreadMessage {\n const message = new HandbookThreadMessage();\n message.rehydrate({\n jsonApi: {\n id: uuidv4(),\n type: Modules.HandbookThreadMessage.name,\n attributes: {\n role: params.role,\n content: params.content,\n position: params.position,\n },\n } as any,\n included: [],\n });\n message._isOptimistic = params.isOptimistic ?? false;\n return message;\n }\n}\n","import { MessagesSquareIcon } from \"lucide-react\";\nimport { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { HandbookThread } from \"./data/HandbookThread\";\n\n/**\n * A persisted handbook conversation.\n *\n * It carries no `pageUrl`: a thread is selected inside the ask surface\n * (`/administration/handbook/chat`), never routed to on its own, and a\n * `pageUrl` here would add a phantom entry to the RBAC path map.\n *\n * `lists` deliberately narrows to the title — the sidebar renders nothing else,\n * and side-loading every message of every thread into the list payload is what\n * the single-resource endpoint is for.\n */\nexport const HandbookThreadModule = (factory: ModuleFactory) =>\n factory({\n moduleId: \"aea692c4-7163-49bd-9ddc-1052cc06fa14\",\n name: \"handbookthreads\",\n model: HandbookThread,\n icon: MessagesSquareIcon,\n identifier: [\"title\"],\n inclusions: {\n lists: {\n fields: [createJsonApiInclusion(\"handbookthreads\", [\"title\"])],\n },\n },\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { HandbookThreadInput, HandbookThreadInterface } from \"./HandbookThreadInterface\";\nimport { HandbookThreadMessageInterface } from \"./HandbookThreadMessageInterface\";\n\nexport class HandbookThread extends AbstractApiData implements HandbookThreadInterface {\n private _title?: string;\n private _messageCount?: number;\n private _messages?: HandbookThreadMessageInterface[];\n\n get title(): string {\n return this._title ?? \"\";\n }\n\n get messageCount(): number {\n return this._messageCount ?? 0;\n }\n\n /**\n * The handbook runs one agent and has no engine choice, so nothing routes on\n * this. It exists because `AssistantInterface` declares it, which is what\n * lets the shared chat chrome take a handbook thread unchanged.\n */\n get engine(): string | undefined {\n return undefined;\n }\n\n /** A handbook thread is never bound to a product resource. */\n get boundContentType(): string | undefined {\n return undefined;\n }\n\n get boundContentId(): string | undefined {\n return undefined;\n }\n\n get messages(): HandbookThreadMessageInterface[] {\n return this._messages ?? [];\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes ?? {};\n this._title = attributes.title;\n\n const fromMeta = data.jsonApi.meta?.messageCount;\n this._messageCount =\n typeof fromMeta === \"number\"\n ? fromMeta\n : typeof attributes.messageCount === \"number\"\n ? attributes.messageCount\n : 0;\n\n const messages = this._readIncluded<HandbookThreadMessageInterface>(\n data,\n \"messages\",\n Modules.HandbookThreadMessage,\n );\n this._messages = Array.isArray(messages) ? messages : messages ? [messages] : [];\n\n // `groupThreadsByBucket` buckets the sidebar on `updatedAt` as a Date, and\n // `AbstractApiData` only reads it from `meta`. A thread that carries the\n // timestamp as an attribute instead would otherwise fall back to \"now\" and\n // land every thread in \"Today\".\n if (this._updatedAt === undefined && attributes.updatedAt !== undefined)\n this._updatedAt = new Date(attributes.updatedAt);\n if (this._createdAt === undefined && attributes.createdAt !== undefined)\n this._createdAt = new Date(attributes.createdAt);\n\n return this;\n }\n\n /**\n * The envelope `POST /handbookthreads` accepts: the opening question. The\n * server derives the title from it, so no title is sent and no client id —\n * the thread's identity is the server's to mint.\n *\n * `handbookPageId` rides the same way `question` does, through this method\n * and never through `overridesJsonApiCreation`. It is written only when the\n * caller supplied it: the DTO takes it as optional, and sending an explicit\n * `null` would fail validation rather than mean \"the whole manual\".\n */\n createJsonApi(data: HandbookThreadInput) {\n return {\n data: {\n type: Modules.HandbookThread.name,\n attributes: {\n question: data.question,\n ...(data.handbookPageId !== undefined ? { handbookPageId: data.handbookPageId } : {}),\n },\n },\n included: [],\n };\n }\n\n /**\n * The envelope `PATCH /handbookthreads/:id` accepts. A dedicated method\n * rather than a branch inside `createJsonApi`: the rename sends the title and\n * nothing else, while a create sends the question and nothing else, and the\n * service must never hand-build either.\n */\n createRenameJsonApi(params: { id: string; title: string }) {\n return {\n data: {\n type: Modules.HandbookThread.name,\n id: params.id,\n attributes: {\n title: params.title,\n },\n },\n included: [],\n };\n }\n}\n","import { HandbookPageInterface } from \"./HandbookPageInterface\";\nimport { HandbookSectionInterface } from \"./HandbookSectionInterface\";\n\n/**\n * The handbook is INDEXED in English and SHOWN translated.\n *\n * A page's translation lives on the same node as its English original — matched\n * by `path` at ingest time — and arrives as ordinary attributes beside it.\n * There is no second id, no locale in any URL and no second resource, so\n * nothing about ordering, grouping or section membership changes: `path`,\n * `order` and `section` stay English and are still what every sort reads. Only\n * the rendered words are resolved here.\n *\n * These five functions are the ONE place that resolution happens. A component\n * reaching for `page.title` directly renders English to an Italian reader, and\n * a per-component `??` would be five copies of the same rule to keep in step.\n *\n * Each parameter is a `Pick` of the interface rather than the whole of it, so a\n * caller holding a projection — a row from a sparse fieldset, a spec fixture —\n * passes it without a cast, while a rename on either interface still breaks\n * this file rather than drifting from it.\n */\n\n/** The page title to render: the translation when there is one, else English. */\nexport function handbookPageTitle(page: Pick<HandbookPageInterface, \"title\" | \"displayTitle\">): string {\n return page.displayTitle ?? page.title;\n}\n\n/** The page's one-line description, translated when the ingest matched one. */\nexport function handbookPageSummary(\n page: Pick<HandbookPageInterface, \"summary\" | \"displaySummary\">,\n): string | undefined {\n return page.displaySummary ?? page.summary;\n}\n\n/**\n * The markdown body to render. Detail-only, exactly as `content` is:\n * `displayContent` is the whole file and is absent from every list payload, so\n * this is never called on a row that came out of a list read.\n */\nexport function handbookPageContent(page: Pick<HandbookPageInterface, \"content\" | \"displayContent\">): string {\n return page.displayContent ?? page.content;\n}\n\n/** The section heading to render, translated when the ingest matched one. */\nexport function handbookSectionTitle(section: Pick<HandbookSectionInterface, \"title\" | \"displayTitle\">): string {\n return section.displayTitle ?? section.title;\n}\n\n/** The section blurb to render, translated when the ingest matched one. */\nexport function handbookSectionSummary(\n section: Pick<HandbookSectionInterface, \"summary\" | \"displaySummary\">,\n): string | undefined {\n return section.displaySummary ?? section.summary;\n}\n","/**\n * An ENUM, not a string array: `ContentListTable` indexes its column map by\n * these members and `useDataListRetriever` keys rows by the id member, exactly\n * as HowToFields does.\n *\n * `content` and `contentHash` are deliberately absent: they are detail-only,\n * and a list of a thousand markdown files must not carry their bodies.\n * `displayContent` — the same body in the reader's language — carries the same\n * caveat: it names the field for the detail read and is deliberately NOT in the\n * module's list inclusion.\n *\n * `section`, `order` and `summary` are list-safe for the opposite reason: they\n * are the index fields the contents page groups, orders and describes pages by,\n * so a list that omitted them could not render a table of contents at all.\n * `displayTitle` and `displaySummary` travel with them: the manual is indexed\n * in English and shown translated, so the contents page needs both halves.\n */\nexport enum HandbookPageFields {\n handbookPageId = \"handbookPageId\",\n\n path = \"path\",\n title = \"title\",\n displayTitle = \"displayTitle\",\n section = \"section\",\n order = \"order\",\n summary = \"summary\",\n displaySummary = \"displaySummary\",\n displayContent = \"displayContent\",\n wordCount = \"wordCount\",\n aiStatus = \"aiStatus\",\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../core\";\nimport { HandbookPageInterface } from \"./HandbookPageInterface\";\n\nexport class HandbookPageService extends AbstractService {\n /**\n * `next` / `prev` are not optional extras: `useDataListRetriever` drives\n * pagination by handing the cursor back here, so a findMany that drops them\n * renders exactly one page and no navigation, however many rows exist.\n * Mirrors HowToService.findMany.\n */\n static async findMany(\n params: {\n search?: string;\n fetchAll?: boolean;\n next?: NextRef;\n prev?: PreviousRef;\n } = {},\n ): Promise<HandbookPageInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.HandbookPage });\n\n if (params.fetchAll) endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n if (Modules.HandbookPage.inclusions?.lists?.fields)\n endpoint.limitToFields(Modules.HandbookPage.inclusions.lists.fields);\n if (Modules.HandbookPage.inclusions?.lists?.types)\n endpoint.limitToType(Modules.HandbookPage.inclusions.lists.types);\n\n return this.callApi({\n type: Modules.HandbookPage,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findOne(params: { id: string }): Promise<HandbookPageInterface> {\n return this.callApi<HandbookPageInterface>({\n type: Modules.HandbookPage,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.HandbookPage, id: params.id }).generate(),\n });\n }\n\n /** POST /handbookpages/sync — replies 204; the caller refetches the list. */\n static async sync(): Promise<void> {\n await this.callApi({\n type: Modules.HandbookPage,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.HandbookPage, id: \"sync\" }).generate(),\n });\n }\n\n static async delete(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.HandbookPage,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.HandbookPage, id: params.id }).generate(),\n });\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { HandbookSectionInterface } from \"./HandbookSectionInterface\";\n\nexport class HandbookSectionService extends AbstractService {\n /**\n * Every section in one read, always.\n *\n * There are as many sections as an application has documentation directories\n * — ten or so — so this takes no `fetchAll` argument and no cursor: unlike\n * the page list, nothing here is ever a second page, and a caller that could\n * ask for one page of sections would only be able to get it wrong.\n */\n static async findMany(): Promise<HandbookSectionInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.HandbookSection });\n\n endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n if (Modules.HandbookSection.inclusions?.lists?.fields)\n endpoint.limitToFields(Modules.HandbookSection.inclusions.lists.fields);\n\n return this.callApi({\n type: Modules.HandbookSection,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { HandbookThreadMessageInput } from \"./HandbookThreadMessageInterface\";\n\nexport class HandbookThreadMessageService extends AbstractService {\n /**\n * Appends one question to an existing thread and runs the agent turn.\n *\n * It returns nothing on purpose: the caller reads the turn back with\n * `HandbookThreadService.findOne`, which is the one endpoint the contract\n * guarantees side-loads the full message list. That keeps a single code path\n * for \"load the thread after a mutation\" — the same one the sidebar's select\n * uses — instead of two ways of arriving at the transcript.\n *\n * The API answers 400 when the installation has no usable AI configuration\n * and 403 for a non-administrator; both reach the caller as a thrown error.\n */\n static async ask(params: HandbookThreadMessageInput & { threadId: string }): Promise<void> {\n await this.callApi({\n type: Modules.HandbookThreadMessage,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.HandbookThread,\n id: params.threadId,\n childEndpoint: Modules.HandbookThreadMessage,\n }).generate(),\n input: { id: params.id, question: params.question, handbookPageId: params.handbookPageId },\n suppressGlobalError: true,\n });\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../core\";\nimport { HandbookThread } from \"./HandbookThread\";\nimport { HandbookThreadInput, HandbookThreadInterface } from \"./HandbookThreadInterface\";\n\nexport class HandbookThreadService extends AbstractService {\n /**\n * `next` / `prev` are not optional extras: `useDataListRetriever` drives\n * pagination by handing the cursor back here, so a findMany that drops them\n * renders exactly one page and no navigation, however many rows exist.\n * Mirrors HowToService.findMany.\n */\n static async findMany(\n params: {\n search?: string;\n fetchAll?: boolean;\n next?: NextRef;\n prev?: PreviousRef;\n } = {},\n ): Promise<HandbookThreadInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.HandbookThread });\n\n if (params.fetchAll) endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n if (Modules.HandbookThread.inclusions?.lists?.fields)\n endpoint.limitToFields(Modules.HandbookThread.inclusions.lists.fields);\n if (Modules.HandbookThread.inclusions?.lists?.types)\n endpoint.limitToType(Modules.HandbookThread.inclusions.lists.types);\n\n return this.callApi({\n type: Modules.HandbookThread,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n /** Returns the thread with its messages side-loaded — the sidebar's select. */\n static async findOne(params: { id: string }): Promise<HandbookThreadInterface> {\n return this.callApi<HandbookThreadInterface>({\n type: Modules.HandbookThread,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.HandbookThread, id: params.id }).generate(),\n });\n }\n\n /**\n * Opens a thread with its first question. The response is the thread (title\n * included); the messages are read back with `findOne`, because only the\n * single-resource endpoint side-loads them.\n *\n * The API answers 400 when the installation has no usable AI configuration\n * and 403 for a non-administrator; both reach the caller as a thrown error.\n *\n * `HandbookThreadInput.handbookPageId` scopes retrieval to one page. It rides\n * through the model's `createJsonApi()`, exactly as `question` does — there is\n * no `overridesJsonApiCreation` on this path.\n */\n static async create(params: HandbookThreadInput): Promise<HandbookThreadInterface> {\n return this.callApi<HandbookThreadInterface>({\n type: Modules.HandbookThread,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.HandbookThread }).generate(),\n input: params,\n suppressGlobalError: true,\n });\n }\n\n static async rename(params: { id: string; title: string }): Promise<void> {\n const thread = new HandbookThread();\n await this.callApi({\n type: Modules.HandbookThread,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({ endpoint: Modules.HandbookThread, id: params.id }).generate(),\n input: thread.createRenameJsonApi({ id: params.id, title: params.title }),\n overridesJsonApiCreation: true,\n });\n }\n\n static async delete(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.HandbookThread,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.HandbookThread, id: params.id }).generate(),\n });\n }\n}\n","import { BotIcon } from \"lucide-react\";\nimport { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { Assistant } from \"./data/Assistant\";\n\nexport const AssistantModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/assistants\",\n name: \"assistants\",\n model: Assistant,\n moduleId: \"2b39fd68-6a41-4f73-a2d0-4c8e8e3f9a42\",\n icon: BotIcon,\n identifier: [\"title\"],\n inclusions: {\n lists: {\n fields: [createJsonApiInclusion(\"assistants\", [`title`])],\n },\n },\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { AssistantInput, AssistantInterface } from \"./AssistantInterface\";\n\nexport class Assistant extends AbstractApiData implements AssistantInterface {\n private _title?: string;\n private _messageCount?: number;\n private _engine?: string;\n private _boundContentType?: string;\n private _boundContentId?: string;\n\n get title(): string {\n return this._title ?? \"\";\n }\n\n get messageCount(): number {\n return this._messageCount ?? 0;\n }\n\n get engine(): string | undefined {\n return this._engine;\n }\n\n get boundContentType(): string | undefined {\n return this._boundContentType;\n }\n\n get boundContentId(): string | undefined {\n return this._boundContentId;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n this._title = data.jsonApi.attributes?.title;\n this._engine = data.jsonApi.attributes?.engine;\n const fromMeta = data.jsonApi.meta?.messageCount;\n const fromAttrs = data.jsonApi.attributes?.messageCount;\n this._messageCount = typeof fromMeta === \"number\" ? fromMeta : typeof fromAttrs === \"number\" ? fromAttrs : 0;\n // Read the linkage pointer directly instead of `_readIncluded`: the bound\n // resource is not side-loaded on this endpoint and only its {type, id} is\n // needed to know which scope the thread belongs to.\n const contentLinkage = data.jsonApi.relationships?.content?.data;\n const boundContent = Array.isArray(contentLinkage) ? contentLinkage[0] : contentLinkage;\n this._boundContentType = boundContent?.type;\n this._boundContentId = boundContent?.id;\n return this;\n }\n\n createJsonApi(data: AssistantInput) {\n return {\n data: {\n type: Modules.Assistant.name,\n attributes: {\n // narr8 convention for BlockNote-backed fields: the document travels\n // as a JSON string in the entity's own string attribute, exactly as\n // Npc.description does (`JSON.stringify` out, `JSON.parse` back).\n // The server detects the shape and derives the stored markdown.\n content: data.contentBlocks !== undefined ? JSON.stringify(data.contentBlocks) : data.firstMessage,\n ...(data.title !== undefined ? { title: data.title } : {}),\n ...(data.howToMode !== undefined ? { howToMode: data.howToMode } : {}),\n ...(data.limitToHowToId !== undefined ? { limitToHowToId: data.limitToHowToId } : {}),\n ...(data.handbookMode !== undefined ? { handbookMode: data.handbookMode } : {}),\n ...(data.limitToHandbookPageId !== undefined ? { limitToHandbookPageId: data.limitToHandbookPageId } : {}),\n },\n ...(data.boundContent !== undefined\n ? {\n relationships: {\n content: { data: { type: data.boundContent.type, id: data.boundContent.id } },\n },\n }\n : {}),\n },\n included: [],\n };\n }\n}\n","import { v4 as uuidv4 } from \"uuid\";\nimport { AbstractApiData, ApiDataInterface, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport {\n AssistantMessageInput,\n AssistantMessageInterface,\n AssistantMessageRole,\n AssistantMessageType,\n} from \"./AssistantMessageInterface\";\nimport { resolveReferenceableModules } from \"../../assistant/utils/resolveReferenceableModules\";\nimport { ChunkInterface, ChunkRelationshipMeta } from \"../../chunk/data/ChunkInterface\";\n\nexport class AssistantMessage extends AbstractApiData implements AssistantMessageInterface {\n private _role?: AssistantMessageRole;\n private _content?: string;\n private _position?: number;\n private _suggestedQuestions?: string[];\n private _inputTokens?: number;\n private _outputTokens?: number;\n private _references?: ApiDataInterface[];\n private _citations?: (ChunkInterface & ChunkRelationshipMeta)[];\n private _isOptimistic = false;\n private _messageType?: AssistantMessageType;\n private _actionId?: string;\n\n get role(): AssistantMessageRole {\n return this._role ?? \"assistant\";\n }\n\n get content(): string {\n return this._content ?? \"\";\n }\n\n get position(): number {\n return this._position ?? 0;\n }\n\n get suggestedQuestions(): string[] {\n return this._suggestedQuestions ?? [];\n }\n\n get inputTokens(): number | undefined {\n return this._inputTokens;\n }\n\n get outputTokens(): number | undefined {\n return this._outputTokens;\n }\n\n get references(): ApiDataInterface[] {\n return this._references ?? [];\n }\n\n get citations(): (ChunkInterface & ChunkRelationshipMeta)[] {\n return this._citations ?? [];\n }\n\n get isOptimistic(): boolean {\n return this._isOptimistic;\n }\n\n get messageType(): AssistantMessageType {\n return this._messageType ?? \"text\";\n }\n\n get actionId(): string | undefined {\n return this._actionId;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n const attrs = data.jsonApi.attributes ?? {};\n this._role = attrs.role as AssistantMessageRole | undefined;\n this._content = attrs.content;\n this._position = typeof attrs.position === \"number\" ? attrs.position : Number(attrs.position ?? 0);\n this._suggestedQuestions = Array.isArray(attrs.suggestedQuestions) ? attrs.suggestedQuestions : [];\n this._inputTokens = attrs.inputTokens;\n this._outputTokens = attrs.outputTokens;\n this._messageType = attrs.messageType as AssistantMessageType | undefined;\n // The AssistantAction module is app-registered, so only the raw\n // relationship id is exposed (no hydration of the included resource here).\n const actionRelationship = data.jsonApi.relationships?.action?.data;\n this._actionId = Array.isArray(actionRelationship) ? actionRelationship[0]?.id : actionRelationship?.id;\n const refs = this._readIncludedPolymorphic<ApiDataInterface>(data, \"references\", resolveReferenceableModules());\n this._references = Array.isArray(refs) ? refs : refs ? [refs] : [];\n if (data.jsonApi.relationships?.citations?.data) {\n const citations = this._readIncludedWithMeta<ChunkInterface, ChunkRelationshipMeta>(\n data,\n \"citations\",\n Modules.Chunk,\n );\n this._citations = Array.isArray(citations)\n ? (citations as (ChunkInterface & ChunkRelationshipMeta)[])\n : citations\n ? [citations as ChunkInterface & ChunkRelationshipMeta]\n : [];\n } else {\n this._citations = [];\n }\n return this;\n }\n\n createJsonApi(data: AssistantMessageInput) {\n return {\n data: {\n type: Modules.AssistantMessage.name,\n id: data.id,\n attributes: {\n role: data.role,\n content: data.content,\n position: data.position,\n },\n relationships: {\n assistant: {\n data: { type: Modules.Assistant.name, id: data.assistantId },\n },\n },\n },\n included: [],\n };\n }\n\n /**\n * JSON:API envelope for POST /assistants/:id/assistant-messages.\n * Different from `createJsonApi` (which expects a full message with role/position/assistant ref);\n * the append-to-thread endpoint derives those server-side, so we only send `content` and\n * the optional retrieval-mode flags.\n */\n createAppendMessageJsonApi(params: {\n content: string;\n howToMode?: boolean;\n limitToHowToId?: string;\n handbookMode?: boolean;\n limitToHandbookPageId?: string;\n contentBlocks?: unknown[];\n }) {\n return {\n data: {\n type: Modules.AssistantMessage.name,\n attributes: {\n // narr8 convention for BlockNote-backed fields: the document travels\n // as a JSON string in the field's own string attribute (see\n // Npc.description). The server detects the shape and derives the\n // stored markdown from it.\n content: params.contentBlocks !== undefined ? JSON.stringify(params.contentBlocks) : params.content,\n ...(params.howToMode !== undefined ? { howToMode: params.howToMode } : {}),\n ...(params.limitToHowToId !== undefined ? { limitToHowToId: params.limitToHowToId } : {}),\n ...(params.handbookMode !== undefined ? { handbookMode: params.handbookMode } : {}),\n ...(params.limitToHandbookPageId !== undefined\n ? { limitToHandbookPageId: params.limitToHandbookPageId }\n : {}),\n },\n },\n };\n }\n\n static buildOptimistic(params: { content: string; position: number; assistantId?: string }): AssistantMessage {\n return AssistantMessage.buildLocal({ ...params, role: \"user\", isOptimistic: true });\n }\n\n /**\n * A message that exists only on the client, for a surface that renders the\n * chat presentation without persisting a thread.\n *\n * `buildOptimistic` is the user-turn case and delegates here. The handbook\n * ask surface is the other caller: it has no Assistant and no stored\n * messages, but it renders through MessageList/MessageItem like every other\n * chat in the product, so it needs both roles.\n */\n static buildLocal(params: {\n role: AssistantMessageRole;\n content: string;\n position: number;\n assistantId?: string;\n isOptimistic?: boolean;\n }): AssistantMessage {\n const msg = new AssistantMessage();\n const jsonApi: Record<string, unknown> = {\n id: uuidv4(),\n type: Modules.AssistantMessage.name,\n attributes: {\n role: params.role,\n content: params.content,\n position: params.position,\n },\n };\n if (params.assistantId) {\n jsonApi.relationships = {\n assistant: { data: { type: Modules.Assistant.name, id: params.assistantId } },\n };\n }\n msg.rehydrate({ jsonApi: jsonApi as any, included: [] });\n msg._isOptimistic = params.isOptimistic ?? false;\n return msg;\n }\n}\n","import type { ApiRequestDataTypeInterface } from \"../../../core/interfaces/ApiRequestDataTypeInterface\";\nimport { ModuleRegistry } from \"../../../core/registry/ModuleRegistry\";\n\n/**\n * Returns every registered Module whose model class has a non-empty `identifierFields`\n * (so `entity.identifier` produces a meaningful string). Lazy — reads at call time so\n * modules registered after import-time are picked up.\n */\nexport function resolveReferenceableModules(): ApiRequestDataTypeInterface[] {\n return ModuleRegistry.getAll().filter((m) => {\n const cls = m.model as any;\n return cls && Array.isArray(cls.identifierFields) && cls.identifierFields.length > 0;\n });\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { AssistantMessage } from \"../../assistant-message/data/AssistantMessage\";\nimport { AssistantMessageInterface } from \"../../assistant-message/data/AssistantMessageInterface\";\nimport { AssistantInput, AssistantInterface } from \"./AssistantInterface\";\n\nexport class AssistantService extends AbstractService {\n static async findOne(params: { id: string }): Promise<AssistantInterface> {\n return this.callApi<AssistantInterface>({\n type: Modules.Assistant,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.Assistant, id: params.id }).generate(),\n });\n }\n\n /**\n * Lists threads. `boundType`/`boundId` narrow the list to the threads bound\n * to one resource (e.g. a campaign), so a scoped surface never shows the\n * user's unrelated threads.\n */\n static async findMany(\n params: { fetchAll?: boolean; boundType?: string; boundId?: string } = {},\n ): Promise<AssistantInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Assistant });\n if (params.fetchAll) endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n if (params.boundType) endpoint.addAdditionalParam(\"boundType\", params.boundType);\n if (params.boundId) endpoint.addAdditionalParam(\"boundId\", params.boundId);\n return this.callApi({\n type: Modules.Assistant,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n static async create(params: AssistantInput): Promise<AssistantInterface> {\n return this.callApi({\n type: Modules.Assistant,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.Assistant }).generate(),\n input: params,\n });\n }\n\n /**\n * Creates an assistant whose first turn runs on the operator engine\n * (durable checkpointing + approval gates). Mirrors `create()` against the\n * standalone operator module's create route (`POST operator`).\n */\n static async createOperator(params: AssistantInput): Promise<AssistantInterface> {\n return this.callApi({\n type: Modules.Assistant,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: \"operator\" }).generate(),\n input: params,\n });\n }\n\n /**\n * Sends a new user message to an existing assistant thread. The agent turn\n * runs synchronously; the response is a two-element list: [user, assistant].\n *\n * Uses the dedicated AssistantMessage.createAppendMessageJsonApi method to\n * build the JSON:API envelope; this is the architecture-compliant pairing\n * with `overridesJsonApiCreation: true`.\n */\n static async appendMessage(params: {\n assistantId: string;\n content: string;\n howToMode?: boolean;\n limitToHowToId?: string;\n handbookMode?: boolean;\n limitToHandbookPageId?: string;\n /** BlockNote document. Serialised by the model into `content`; never its own attribute. */\n contentBlocks?: unknown[];\n }): Promise<AssistantMessageInterface[]> {\n const message = new AssistantMessage();\n return this.callApi<AssistantMessageInterface[]>({\n type: Modules.AssistantMessage,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Assistant,\n id: params.assistantId,\n childEndpoint: Modules.AssistantMessage,\n }).generate(),\n input: message.createAppendMessageJsonApi({\n content: params.content,\n howToMode: params.howToMode,\n limitToHowToId: params.limitToHowToId,\n handbookMode: params.handbookMode,\n limitToHandbookPageId: params.limitToHandbookPageId,\n contentBlocks: params.contentBlocks,\n }),\n overridesJsonApiCreation: true,\n });\n }\n\n /**\n * Operator-engine variant of `appendMessage()`. Targets\n * `POST operator/:assistantId/assistant-messages`; the turn may freeze on a\n * destructive tool call, in which case the returned list ends with an\n * `approval-request` assistant message linked to a pending AssistantAction.\n */\n static async appendMessageOperator(params: {\n assistantId: string;\n content: string;\n /** BlockNote document. Serialised by the model into `content`; never its own attribute. */\n contentBlocks?: unknown[];\n }): Promise<AssistantMessageInterface[]> {\n const message = new AssistantMessage();\n return this.callApi<AssistantMessageInterface[]>({\n type: Modules.AssistantMessage,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: \"operator\",\n id: params.assistantId,\n childEndpoint: Modules.AssistantMessage,\n }).generate(),\n input: message.createAppendMessageJsonApi({\n content: params.content,\n contentBlocks: params.contentBlocks,\n }),\n overridesJsonApiCreation: true,\n });\n }\n\n static async rename(params: { id: string; title: string }): Promise<void> {\n await this.callApi({\n type: Modules.Assistant,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({ endpoint: Modules.Assistant, id: params.id }).generate(),\n input: {\n data: {\n type: Modules.Assistant.name,\n id: params.id,\n attributes: { title: params.title },\n },\n },\n overridesJsonApiCreation: true,\n });\n }\n\n static async delete(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.Assistant,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.Assistant, id: params.id }).generate(),\n });\n }\n}\n","import { MessageSquareIcon } from \"lucide-react\";\nimport { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { AssistantMessage } from \"./data/AssistantMessage\";\n\nexport const AssistantMessageModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/assistant-messages\",\n name: \"assistant-messages\",\n model: AssistantMessage,\n moduleId: \"5b2e10e4-3a01-4a59-9f0f-3c4a6c6a8e11\",\n icon: MessageSquareIcon,\n inclusions: {\n lists: {\n types: [`references`, `citations`, `citations.source`, `citations.source.author`],\n fields: [\n createJsonApiInclusion(\"assistant-messages\", [\n `role`,\n `content`,\n `position`,\n `suggestedQuestions`,\n `inputTokens`,\n `outputTokens`,\n `messageType`,\n `references`,\n `citations`,\n `action`,\n ]),\n createJsonApiInclusion(\"chunks\", [`content`, `nodeId`, `nodeType`, `imagePath`, `source`]),\n createJsonApiInclusion(\"users\", [`name`, `avatar`]),\n ],\n },\n },\n });\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef } from \"../../../core\";\nimport { AssistantMessageInterface } from \"./AssistantMessageInterface\";\n\nexport class AssistantMessageService extends AbstractService {\n static async findByAssistant(params: { assistantId: string; next?: NextRef }): Promise<AssistantMessageInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Assistant,\n id: params.assistantId,\n childEndpoint: Modules.AssistantMessage,\n });\n if (Modules.AssistantMessage.inclusions?.lists?.fields)\n endpoint.limitToFields(Modules.AssistantMessage.inclusions.lists.fields);\n if (Modules.AssistantMessage.inclusions?.lists?.types)\n endpoint.limitToType(Modules.AssistantMessage.inclusions.lists.types);\n return this.callApi({\n type: Modules.AssistantMessage,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findOne(params: { id: string }): Promise<AssistantMessageInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.AssistantMessage,\n id: params.id,\n });\n if (Modules.AssistantMessage.inclusions?.lists?.fields)\n endpoint.limitToFields(Modules.AssistantMessage.inclusions.lists.fields);\n if (Modules.AssistantMessage.inclusions?.lists?.types)\n endpoint.limitToType(Modules.AssistantMessage.inclusions.lists.types);\n return this.callApi<AssistantMessageInterface>({\n type: Modules.AssistantMessage,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n static async delete(params: { id: string }): Promise<void> {\n await this.callApi({\n type: Modules.AssistantMessage,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({\n endpoint: Modules.AssistantMessage,\n id: params.id,\n }).generate(),\n });\n }\n}\n","import { ShieldCheckIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { AssistantAction } from \"./data/AssistantAction\";\n\nexport const AssistantActionModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/assistant-actions\",\n name: \"assistant-actions\",\n model: AssistantAction,\n identifier: [\"summary\"],\n moduleId: \"e3c27517-bac0-43cb-adff-cc20c4210c12\",\n icon: ShieldCheckIcon,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { AssistantActionInput, AssistantActionInterface, AssistantActionStatus } from \"./AssistantActionInterface\";\n\nexport class AssistantAction extends AbstractApiData implements AssistantActionInterface {\n private _status?: AssistantActionStatus;\n private _toolName?: string;\n private _toolArgs?: string;\n private _proposal?: string;\n private _summary?: string;\n private _resolvedAt?: Date;\n private _expiresAt?: Date;\n\n get status(): AssistantActionStatus {\n // Fail-safe: a missing status must never render actionable Approve/Deny buttons, so default to the non-actionable \"expired\".\n return this._status ?? \"expired\";\n }\n\n get toolName(): string {\n return this._toolName ?? \"\";\n }\n\n get toolArgs(): string {\n return this._toolArgs ?? \"\";\n }\n\n get proposal(): string {\n return this._proposal ?? \"\";\n }\n\n get summary(): string {\n return this._summary ?? \"\";\n }\n\n get resolvedAt(): Date | undefined {\n return this._resolvedAt;\n }\n\n get expiresAt(): Date | undefined {\n return this._expiresAt;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._status = data.jsonApi.attributes.status;\n this._toolName = data.jsonApi.attributes.toolName;\n this._toolArgs = data.jsonApi.attributes.toolArgs;\n this._proposal = data.jsonApi.attributes.proposal;\n this._summary = data.jsonApi.attributes.summary;\n this._resolvedAt = data.jsonApi.attributes.resolvedAt ? new Date(data.jsonApi.attributes.resolvedAt) : undefined;\n this._expiresAt = data.jsonApi.attributes.expiresAt ? new Date(data.jsonApi.attributes.expiresAt) : undefined;\n\n return this;\n }\n\n /**\n * Minimal payload (id only): the frontend never POSTs assistant actions —\n * approve/deny are body-less POSTs handled by AssistantActionService.\n */\n createJsonApi(data: AssistantActionInput) {\n return {\n data: {\n type: Modules.AssistantAction.name,\n id: data.id,\n attributes: {},\n },\n included: [],\n };\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { AssistantMessageInterface } from \"../../assistant-message/data/AssistantMessageInterface\";\nimport { AssistantActionInterface } from \"./AssistantActionInterface\";\n\nexport class AssistantActionService extends AbstractService {\n /**\n * GET single assistant action by ID\n */\n static async findOne(params: { id: string }): Promise<AssistantActionInterface> {\n return this.callApi<AssistantActionInterface>({\n type: Modules.AssistantAction,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({\n endpoint: Modules.AssistantAction,\n id: params.id,\n }).generate(),\n });\n }\n\n /**\n * POST (body-less) approve action — resumes the paused run and\n * returns the resumed assistant message.\n */\n static async approve(params: { id: string }): Promise<AssistantMessageInterface> {\n return this.callApi<AssistantMessageInterface>({\n type: Modules.AssistantMessage,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.AssistantAction,\n id: params.id,\n childEndpoint: \"approve\",\n }).generate(),\n });\n }\n\n /**\n * POST (body-less) deny action — resumes the paused run with a denial and\n * returns the resumed assistant message.\n */\n static async deny(params: { id: string }): Promise<AssistantMessageInterface> {\n return this.callApi<AssistantMessageInterface>({\n type: Modules.AssistantMessage,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.AssistantAction,\n id: params.id,\n childEndpoint: \"deny\",\n }).generate(),\n });\n }\n}\n","import { FileTextIcon } from \"lucide-react\";\nimport { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { Chunk } from \"./data/Chunk\";\n\nexport const ChunkModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/chunks\",\n name: \"chunks\",\n model: Chunk,\n moduleId: \"9b1c2f64-7ea3-4df5-8b2a-91b6a8d0e3a4\",\n icon: FileTextIcon,\n inclusions: {\n lists: {\n fields: [createJsonApiInclusion<Chunk>(\"chunks\", [\"content\", \"nodeId\", \"nodeType\", \"imagePath\", \"source\"])],\n },\n },\n });\n","import { AbstractApiData, ApiDataInterface, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { resolveReferenceableModules } from \"../../assistant/utils/resolveReferenceableModules\";\nimport { ChunkInterface } from \"./ChunkInterface\";\n\nexport class Chunk extends AbstractApiData implements ChunkInterface {\n private _content?: string;\n private _nodeId?: string;\n private _nodeType?: string;\n private _imagePath?: string;\n private _source?: ApiDataInterface;\n\n get content(): string {\n return this._content ?? \"\";\n }\n get nodeId(): string | undefined {\n return this._nodeId;\n }\n get nodeType(): string | undefined {\n return this._nodeType;\n }\n get imagePath(): string | undefined {\n return this._imagePath;\n }\n get source(): ApiDataInterface | undefined {\n return this._source;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n const attrs = data.jsonApi.attributes ?? {};\n const meta = data.jsonApi.meta ?? {};\n this._content = attrs.content;\n this._nodeId = meta.nodeId ?? attrs.nodeId;\n this._nodeType = meta.nodeType ?? attrs.nodeType;\n this._imagePath = attrs.imagePath;\n\n const src = this._readIncludedPolymorphic<ApiDataInterface>(data, \"source\", resolveReferenceableModules());\n this._source = Array.isArray(src) ? src[0] : src;\n return this;\n }\n\n createJsonApi() {\n // Chunks are read-only from the assistant context. Provide a minimal stub.\n return {\n data: { type: Modules.Chunk.name, id: this.id, attributes: {}, relationships: {} },\n included: [],\n };\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { ModuleInterface } from \"../../module\";\nimport { FeatureInterface } from \"./feature.interface\";\n\nexport class Feature extends AbstractApiData implements FeatureInterface {\n private _name?: string;\n private _isCore?: boolean;\n\n private _modules: ModuleInterface[] = [];\n\n get name(): string {\n return this._name ?? \"\";\n }\n\n get isCore(): boolean {\n return this._isCore == true ? true : false;\n }\n\n get modules(): ModuleInterface[] {\n return this._modules;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._name = data.jsonApi.attributes.name;\n this._isCore = data.jsonApi.attributes.isCore ?? false;\n\n this._modules = this._readIncluded(data, `modules`, Modules.Module) as ModuleInterface[];\n\n return this;\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef } from \"../../../core\";\nimport { FeatureInterface } from \"./feature.interface\";\n\nexport class FeatureService extends AbstractService {\n static async findMany(params: { companyId?: string; search?: string; next?: NextRef }): Promise<FeatureInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Feature });\n\n if (params.companyId) endpoint.endpoint(Modules.Company).id(params.companyId).childEndpoint(Modules.Feature);\n\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi<FeatureInterface[]>({\n type: Modules.Feature,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { Feature } from \"./data/feature\";\n\nexport const FeatureModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/features\",\n name: \"features\",\n model: Feature,\n moduleId: \"025fdd23-2803-4360-9fd9-eaa3612c2e23\",\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { ModuleInterface } from \"./module.interface\";\n\nexport class Module extends AbstractApiData implements ModuleInterface {\n private _name?: string;\n private _permissions?: {\n create: boolean | string;\n read: boolean | string;\n update: boolean | string;\n delete: boolean | string;\n };\n\n get name(): string {\n if (!this._name) throw new Error(\"Name is not defined\");\n return this._name ?? \"\";\n }\n\n get permissions(): {\n create: boolean | string;\n read: boolean | string;\n update: boolean | string;\n delete: boolean | string;\n } {\n if (!this._permissions) throw new Error(\"Permissions is not defined\");\n return this._permissions;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._name = data.jsonApi.attributes.name;\n this._permissions = data.jsonApi.meta.permissions;\n\n return this;\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { Module } from \"./data\";\n\nexport const ModuleModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/modules\",\n name: \"modules\",\n model: Module,\n moduleId: \"25ffd868-8341-4ca7-963b-6e1c56b03b1d\",\n });\n","import { AbstractApiData, ApiDataInterface, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { NotificationInput, NotificationInterface } from \"./notification.interface\";\n\nexport class Notification extends AbstractApiData implements NotificationInterface {\n private _notificationType?: string;\n private _isRead?: boolean;\n private _message?: string;\n private _actionUrl?: string;\n\n private _actor?: ApiDataInterface;\n private _subject?: ApiDataInterface;\n\n get notificationType(): string {\n if (this._notificationType === undefined) throw new Error(\"notificationType is not set\");\n return this._notificationType;\n }\n\n get isRead(): boolean {\n return this._isRead === undefined ? false : this._isRead;\n }\n\n get message(): string | undefined {\n return this._message;\n }\n\n get actionUrl(): string | undefined {\n return this._actionUrl;\n }\n\n get actor(): ApiDataInterface | undefined {\n return this._actor;\n }\n\n get subject(): ApiDataInterface | undefined {\n return this._subject;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._notificationType = data.jsonApi.attributes.notificationType;\n this._isRead = data.jsonApi.attributes.isRead;\n this._message = data.jsonApi.attributes.message;\n this._actionUrl = data.jsonApi.attributes.actionUrl;\n\n const rels = (data.jsonApi.relationships ?? {}) as Record<string, { data?: { type: string; id: string } }>;\n\n const actorType = rels.actor?.data?.type;\n this._actor = actorType\n ? (this._readIncluded(data, \"actor\", Modules.findByName(actorType)) as ApiDataInterface)\n : undefined;\n\n const subjectName = Object.keys(rels).find((name) => name !== \"actor\" && rels[name]?.data?.type);\n const subjectType = subjectName ? rels[subjectName]?.data?.type : undefined;\n this._subject =\n subjectName && subjectType\n ? (this._readIncluded(data, subjectName, Modules.findByName(subjectType)) as ApiDataInterface)\n : undefined;\n\n return this;\n }\n\n createJsonApi(data: NotificationInput) {\n const response: any = {\n data: {\n type: Modules.Notification.name,\n id: data.id,\n attributes: {\n isRead: data.isRead,\n },\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n return response;\n }\n}\n","export enum NotificationFields {\n notificationId = \"notificationId\",\n\n name = \"name\",\n\n icon = \"icon\",\n description = \"description\",\n actions = \"actions\",\n\n createdAt = \"createdAt\",\n updatedAt = \"updatedAt\",\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef } from \"../../../core\";\nimport { NotificationInterface } from \"./notification.interface\";\n\nexport class NotificationService extends AbstractService {\n static async findMany(params: {\n isArchived?: boolean;\n next?: NextRef;\n suppressGlobalError?: boolean;\n }): Promise<NotificationInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Notification });\n\n if (params.isArchived) endpoint.addAdditionalParam(\"isArchived\", \"true\");\n\n return this.callApi<NotificationInterface[]>({\n type: Modules.Notification,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n suppressGlobalError: params.suppressGlobalError,\n });\n }\n\n static async markAsRead(params: { data: any }): Promise<void> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Notification });\n await this.callApi({\n type: Modules.Notification,\n method: HttpMethod.PATCH,\n endpoint: endpoint.generate(),\n input: params.data,\n overridesJsonApiCreation: true,\n });\n }\n\n static async archive(params: { id: string }): Promise<void> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Notification, id: params.id, childEndpoint: \"archive\" });\n\n await this.callApi({ type: Modules.Notification, method: HttpMethod.POST, endpoint: endpoint.generate() });\n }\n}\n","import { BellIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { Notification } from \"./data/notification\";\n\nexport const NotificationModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/notifications\",\n name: \"notifications\",\n model: Notification,\n icon: BellIcon,\n moduleId: \"9259d704-c670-4e77-a3a1-a728ffc5be3d\",\n });\n","import { AbstractApiData, Modules } from \"../../../core\";\nimport { PushInput, PushInterface } from \"./push.interface\";\n\nexport class Push extends AbstractApiData implements PushInterface {\n createJsonApi(data: PushInput) {\n const response: any = {\n data: {\n type: Modules.Push.name,\n attributes: {\n key: data.key,\n },\n },\n included: [],\n };\n\n return response;\n }\n}\n","import { AbstractService, HttpMethod } from \"../../../core\";\nimport { EndpointCreator } from \"../../../core\";\nimport { Modules } from \"../../../core\";\n\nexport class PushService extends AbstractService {\n static async register(params: { data: any }): Promise<void> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Push });\n\n await this.callApi({\n type: Modules.Push,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: params.data,\n overridesJsonApiCreation: true,\n });\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { Push } from \"./data/push\";\n\nexport const PushModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/push\",\n name: \"push\",\n model: Push,\n moduleId: \"\",\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { FeatureInterface } from \"../../feature\";\nimport { RoleInput, RoleInterface } from \"./role.interface\";\n\nexport class Role extends AbstractApiData implements RoleInterface {\n private _name?: string;\n private _description?: string;\n private _isSelectable?: boolean;\n\n private _requiredFeature?: FeatureInterface;\n\n get name(): string {\n if (!this._name) throw new Error(\"Name is not defined\");\n return this._name;\n }\n\n get description(): string {\n return this._description ?? \"\";\n }\n\n get isSelectable(): boolean {\n return this._isSelectable ?? false;\n }\n\n get requiredFeature(): FeatureInterface | undefined {\n return this._requiredFeature;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._name = data.jsonApi.attributes.name ?? undefined;\n this._description = data.jsonApi.attributes.description ?? undefined;\n this._isSelectable = data.jsonApi.attributes.isSelectable ?? undefined;\n\n this._requiredFeature = this._readIncluded<FeatureInterface>(\n data,\n \"requiredFeature\",\n Modules.Feature,\n ) as FeatureInterface;\n\n return this;\n }\n\n createJsonApi(data: RoleInput) {\n const response: any = {\n data: {\n type: Modules.Role.name,\n id: data.id,\n attributes: {\n name: data.name,\n },\n meta: {},\n },\n included: [],\n };\n\n if (data.description !== undefined) response.data.attributes.description = data.description;\n\n return response;\n }\n}\n","export enum RoleFields {\n roleId = \"roleId\",\n name = \"name\",\n description = \"description\",\n\n createdAt = \"createdAt\",\n updatedAt = \"updatedAt\",\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../core\";\nimport { RoleInput, RoleInterface } from \"./role.interface\";\n\nexport class RoleService extends AbstractService {\n static async findById(params: { roleId: string }): Promise<RoleInterface> {\n return this.callApi<RoleInterface>({\n type: Modules.Role,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.Role, id: params.roleId }).generate(),\n });\n }\n\n static async findAllRoles(params: { search?: string; next?: NextRef }): Promise<RoleInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Role });\n\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi({\n type: Modules.Role,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findAllRolesByUser(params: {\n userId: string;\n search?: string;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<RoleInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.User, id: params.userId, childEndpoint: Modules.Role });\n\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi({\n type: Modules.Role,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findAllRolesUserNotIn(params: {\n userId: string;\n search?: string;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<RoleInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.User,\n id: params.userId,\n childEndpoint: Modules.Role,\n }).addAdditionalParam(\"userNotIn\", \"true\");\n\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi({\n type: Modules.Role,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async addUserToRole(params: { userId: string; roleId: string }): Promise<void> {\n this.callApi<RoleInterface>({\n type: Modules.Role,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: Modules.Role,\n id: params.roleId,\n childEndpoint: Modules.User,\n childId: params.userId,\n }).generate(),\n });\n }\n\n static async removeUserFromRole(params: { userId: string; roleId: string }): Promise<void> {\n this.callApi<RoleInterface>({\n type: Modules.Role,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({\n endpoint: Modules.Role,\n id: params.roleId,\n childEndpoint: Modules.User,\n childId: params.userId,\n }).generate(),\n });\n }\n\n static async create(params: { roleId: string; name: string; description?: string }): Promise<RoleInterface> {\n return this.callApi<RoleInterface>({\n type: Modules.Role,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.Role }).generate(),\n input: { id: params.roleId, name: params.name, description: params.description } as RoleInput,\n });\n }\n\n static async update(params: { roleId: string; name: string; description?: string }): Promise<RoleInterface> {\n return this.callApi<RoleInterface>({\n type: Modules.Role,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({ endpoint: Modules.Role, id: params.roleId }).generate(),\n input: { id: params.roleId, name: params.name, description: params.description } as RoleInput,\n });\n }\n\n static async delete(params: { roleId: string }): Promise<RoleInterface> {\n return this.callApi<RoleInterface>({\n type: Modules.Role,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.Role, id: params.roleId }).generate(),\n });\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { Role } from \"./data/role\";\n\nexport const RoleModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/roles\",\n name: \"roles\",\n model: Role,\n moduleId: \"9f6416e6-7b9b-4e1a-a99f-833191eca8a9\",\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { S3Input, S3Interface } from \"./s3.interface\";\n\nexport class S3 extends AbstractApiData implements S3Interface {\n private _url?: string;\n private _storageType?: string;\n private _contentType?: string;\n private _blobType?: string;\n private _acl?: string;\n\n get url(): string {\n if (!this._url) throw new Error(\"Image URL is not set.\");\n return this._url;\n }\n\n get headers(): Record<string, string> {\n const headers: Record<string, string> = {};\n\n if (this._contentType) {\n headers[\"Content-Type\"] = this._contentType;\n }\n\n if (this._blobType) {\n headers[\"x-ms-blob-type\"] = this._blobType;\n }\n\n if (this._acl) {\n headers[\"x-amz-acl\"] = this._acl;\n }\n\n return headers;\n }\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._url = data.jsonApi.attributes.url ?? undefined;\n this._storageType = data.jsonApi.attributes.storageType ?? undefined;\n this._contentType = data.jsonApi.attributes.contentType ?? undefined;\n this._blobType = data.jsonApi.attributes.blobType ?? undefined;\n this._acl = data.jsonApi.attributes.acl ?? undefined;\n\n return this;\n }\n\n createJsonApi(data: S3Input) {\n const response: any = {\n data: {\n type: Modules.S3.name,\n attributes: {\n key: data.key,\n },\n },\n included: [],\n };\n\n if (data.contentType) response.data.attributes.contentType = data.contentType;\n\n return response;\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { S3 } from \"./data/s3\";\n\nexport const S3Module = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/s3\",\n name: \"s3\",\n model: S3,\n moduleId: \"db41ba46-e171-4324-8845-99353eba8568\",\n });\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { S3Interface } from \"./s3.interface\";\n\nexport class S3Service extends AbstractService {\n static async getPreSignedUrl(params: { key: string; contentType: string; isPublic?: boolean }): Promise<S3Interface> {\n const endpoint = new EndpointCreator({ endpoint: Modules.S3 })\n .addAdditionalParam(\"key\", params.key)\n .addAdditionalParam(\"contentType\", params.contentType);\n\n if (params.isPublic) endpoint.addAdditionalParam(\"isPublic\", \"true\");\n\n return this.callApi<S3Interface>({ type: Modules.S3, method: HttpMethod.GET, endpoint: endpoint.generate() });\n }\n\n /**\n * Presigns `key` and PUTs `file` to the presigned S3 URL. The PUT goes straight\n * to object storage (not the JSON:API backend), so it cannot use callApi.\n * Throws when the upload is rejected.\n */\n static async upload(params: { key: string; contentType: string; file: Blob; isPublic?: boolean }): Promise<void> {\n const s3 = await this.getPreSignedUrl({\n key: params.key,\n contentType: params.contentType,\n isPublic: params.isPublic,\n });\n\n const response = await fetch(s3.url, { method: \"PUT\", headers: s3.headers, body: params.file });\n\n if (!response.ok) throw new Error(`S3 upload failed: ${response.status}`);\n }\n\n static async getSignedUrl(params: { key: string; isPublic?: boolean }): Promise<S3Interface> {\n const endpoint = new EndpointCreator({ endpoint: Modules.S3, id: \"sign\" }).addAdditionalParam(\"key\", params.key);\n\n if (params.isPublic) endpoint.addAdditionalParam(\"isPublic\", \"true\");\n\n return this.callApi<S3Interface>({ type: Modules.S3, method: HttpMethod.GET, endpoint: endpoint.generate() });\n }\n\n static async deleteFile(params: { key: string }): Promise<void> {\n this.callApi<S3Interface>({\n type: Modules.S3,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.S3 }).addAdditionalParam(\"key\", params.key).generate(),\n });\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { CompanyInterface } from \"../../company/data/company.interface\";\nimport { ModuleInterface } from \"../../module\";\nimport { RoleInterface } from \"../../role\";\nimport { SearchResultInterface } from \"../../search\";\nimport { UserInput, UserInterface } from \"./user.interface\";\n\nexport class User extends AbstractApiData implements UserInterface, SearchResultInterface {\n private _name?: string;\n private _email?: string;\n private _title?: string;\n private _bio?: string;\n private _avatar?: string;\n private _avatarUrl?: string;\n private _phone?: string;\n private _rate?: number;\n private _language?: string;\n\n private _isActivated?: boolean;\n private _isDeleted?: boolean;\n private _twoFactorEnabled: boolean = false;\n private _lastLogin?: Date;\n\n private _relevance?: number;\n\n private _roles: RoleInterface[] = [];\n private _company?: CompanyInterface;\n private _modules: ModuleInterface[] = [];\n\n get searchResult(): string {\n return this._name ?? \"\";\n }\n\n get name(): string {\n return this._name ?? \"\";\n }\n\n get email(): string {\n return this._email ?? \"\";\n }\n\n get title(): string {\n return this._title ?? \"\";\n }\n\n get bio(): string {\n return this._bio ?? \"\";\n }\n\n get avatar(): string | undefined {\n return this._avatar;\n }\n\n get avatarUrl(): string | undefined {\n return this._avatarUrl;\n }\n\n get phone(): string | undefined {\n return this._phone;\n }\n\n get rate(): number | undefined {\n return this._rate;\n }\n\n get language(): string | undefined {\n return this._language;\n }\n\n get relevance(): number | undefined {\n return this._relevance;\n }\n\n get isActivated(): boolean {\n return this._isActivated ?? false;\n }\n\n get isDeleted(): boolean {\n return this._isDeleted ?? false;\n }\n\n get twoFactorEnabled(): boolean {\n return this._twoFactorEnabled;\n }\n\n get lastLogin(): Date | undefined {\n return this._lastLogin;\n }\n\n get roles(): RoleInterface[] {\n return this._roles;\n }\n\n get company(): CompanyInterface | undefined {\n return this._company;\n }\n\n get modules(): ModuleInterface[] {\n return this._modules;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._name = data.jsonApi.attributes.name;\n this._email = data.jsonApi.attributes.email;\n this._title = data.jsonApi.attributes.title;\n this._bio = data.jsonApi.attributes.bio;\n this._phone = data.jsonApi.attributes.phone;\n this._rate = data.jsonApi.attributes.rate;\n this._language = data.jsonApi.attributes.language;\n\n this._avatar = data.jsonApi.attributes.avatar;\n this._avatarUrl = data.jsonApi.attributes.avatarUrl;\n\n this._isActivated = data.jsonApi.meta.isActive;\n this._isDeleted = data.jsonApi.meta.isDeleted;\n this._twoFactorEnabled = data.jsonApi.attributes.twoFactorEnabled ?? false;\n this._lastLogin = data.jsonApi.meta.lastLogin ? new Date(data.jsonApi.meta.lastLogin) : undefined;\n\n this._relevance = data.jsonApi.meta.relevance;\n\n this._roles = this._readIncluded(data, \"roles\", Modules.Role) as RoleInterface[];\n this._company = this._readIncluded(data, \"company\", Modules.Company) as CompanyInterface;\n this._modules = this._readIncluded(data, \"modules\", Modules.Module) as ModuleInterface[];\n\n return this;\n }\n\n createJsonApi(data: UserInput) {\n const response: any = {\n data: {\n type: Modules.User.name,\n id: data.id,\n attributes: {},\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.name !== undefined) response.data.attributes.name = data.name;\n if (data.email !== undefined) response.data.attributes.email = data.email;\n if (data.title !== undefined) response.data.attributes.title = data.title;\n if (data.bio !== undefined) response.data.attributes.bio = data.bio;\n if (data.phone !== undefined) response.data.attributes.phone = data.phone;\n if (data.password !== undefined) response.data.attributes.password = data.password;\n if (data.sendInvitationEmail) response.data.attributes.sendInvitationEmail = true;\n if (data.adminCreated) response.data.attributes.adminCreated = true;\n if (data.avatar !== undefined) response.data.attributes.avatar = data.avatar;\n if (data.rate !== undefined) response.data.attributes.rate = data.rate;\n if (data.language !== undefined) response.data.attributes.language = data.language;\n\n if (data.roleIds) {\n response.data.relationships.roles = {\n data: data.roleIds.map((roleId) => ({\n type: Modules.Role.name,\n id: roleId,\n })),\n };\n }\n\n if (data.companyId) {\n response.data.relationships.company = {\n data: {\n type: Modules.Company.name,\n id: data.companyId,\n },\n };\n }\n\n return response;\n }\n}\n","export enum UserFields {\n userId = \"userId\",\n name = \"name\",\n email = \"email\",\n company = \"company\",\n relevance = \"relevance\",\n createdAt = \"createdAt\",\n updatedAt = \"updatedAt\",\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../core\";\nimport { UserInput, UserInterface } from \"./user.interface\";\n\nexport class UserService extends AbstractService {\n static async findFullUser(): Promise<UserInterface> {\n const endpoint = new EndpointCreator({ endpoint: Modules.User, id: \"me\", childEndpoint: \"full\" });\n\n return this.callApi<UserInterface>({\n type: Modules.User,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n static async findById(params: { userId: string }): Promise<UserInterface> {\n return this.callApi<UserInterface>({\n type: Modules.User,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.User, id: params.userId }).generate(),\n });\n }\n\n static async findByEmail(params: { email: string }): Promise<UserInterface> {\n const endpoint = new EndpointCreator({ endpoint: Modules.User, id: \"email\", childEndpoint: params.email });\n\n return this.callApi<UserInterface>({ type: Modules.User, method: HttpMethod.GET, endpoint: endpoint.generate() });\n }\n\n static async findMany(params: {\n roleId?: string;\n search?: string;\n fetchAll?: boolean;\n includeDeleted?: boolean;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<UserInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.User });\n\n if (params.roleId) endpoint.addAdditionalParam(\"roleId\", params.roleId);\n\n if (params.includeDeleted) endpoint.addAdditionalParam(\"includeDeleted\", \"true\");\n if (params.fetchAll) endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findManyByContentIds(params: {\n contentIds: string[];\n search?: string;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<UserInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.User });\n\n endpoint.addAdditionalParam(\"contentIds\", params.contentIds.map((id) => id).join(\",\"));\n endpoint.addAdditionalParam(\"includeDeleted\", \"true\");\n endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findRelevant(params: { id: string; next?: NextRef; prev?: PreviousRef }): Promise<UserInterface[]> {\n const endpoint: EndpointCreator = new EndpointCreator({\n endpoint: Modules.Content,\n id: params.id,\n childEndpoint: \"user-relevance\",\n });\n\n if (Modules.User.inclusions?.lists?.fields) endpoint.limitToFields(Modules.User.inclusions.lists.fields);\n if (Modules.User.inclusions?.lists?.types) endpoint.limitToType(Modules.User.inclusions.lists.types);\n\n return this.callApi<UserInterface[]>({\n type: Modules.User,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findManyForAmin(params: {\n companyId?: string;\n search?: string;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<UserInterface[]> {\n if (!params.companyId) return [];\n\n const endpoint = new EndpointCreator({\n endpoint: Modules.Company,\n id: params.companyId,\n childEndpoint: Modules.User,\n });\n\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n previous: params.prev,\n });\n }\n\n static async findAllUsers(params: {\n companyId: string;\n search?: string;\n limitToRoles?: string[];\n isDeleted?: boolean;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<UserInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Company,\n id: params.companyId,\n childEndpoint: Modules.User,\n });\n\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n if (params.isDeleted) endpoint.addAdditionalParam(\"isDeleted\", \"true\");\n if (params.limitToRoles && params.limitToRoles.length > 0)\n endpoint.addAdditionalParam(\"limitToRoles\", params.limitToRoles.join(\",\"));\n\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findAllUsersByRole(params: {\n roleId: string;\n search?: string;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<UserInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Role, id: params.roleId, childEndpoint: Modules.User });\n\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async findAllUsersNotInRole(params: {\n roleId: string;\n search?: string;\n next?: NextRef;\n prev?: PreviousRef;\n }): Promise<UserInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Role,\n id: params.roleId,\n childEndpoint: Modules.User,\n }).addAdditionalParam(\"notInRole\", \"true\");\n\n if (params.search) endpoint.addAdditionalParam(\"search\", params.search);\n\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async create(params: UserInput): Promise<UserInterface> {\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.User }).generate(),\n companyId: params.companyId,\n input: params,\n });\n }\n\n static async reactivate(params: { userId: string }): Promise<UserInterface> {\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({ endpoint: Modules.User, id: params.userId }).generate(),\n });\n }\n\n static async sendInvitation(params: { userId: string; companyId?: string }): Promise<void> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.User,\n id: params.userId,\n childEndpoint: \"send-invitation-email\",\n });\n\n if (params.companyId) endpoint.addAdditionalParam(\"companyId\", params.companyId);\n\n this.callApi({\n type: Modules.User,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n });\n }\n\n static async update(params: UserInput): Promise<UserInterface> {\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.PUT,\n endpoint: new EndpointCreator({ endpoint: Modules.User, id: params.id }).generate(),\n companyId: params.companyId,\n input: params,\n });\n }\n\n static async patch(params: { id: string } & Partial<Omit<UserInput, \"id\">>): Promise<UserInterface> {\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({ endpoint: Modules.User, id: params.id }).generate(),\n input: params,\n });\n }\n\n static async patchAvatar(params: { id: string; avatar: string }): Promise<UserInterface> {\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({ endpoint: Modules.User, id: params.id, childEndpoint: \"avatar\" }).generate(),\n input: params,\n });\n }\n\n static async patchLanguage(params: { id: string; language: string }): Promise<UserInterface> {\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({ endpoint: Modules.User, id: params.id, childEndpoint: \"language\" }).generate(),\n input: params,\n });\n }\n\n static async patchRate(params: UserInput): Promise<UserInterface> {\n return this.callApi({\n type: Modules.User,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({ endpoint: Modules.User, id: params.id, childEndpoint: \"rates\" }).generate(),\n companyId: params.companyId,\n input: params,\n });\n }\n\n static async delete(params: { userId: string; companyId: string }): Promise<void> {\n await this.callApi({\n type: Modules.User,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: Modules.User, id: params.userId }).generate(),\n companyId: params.companyId,\n });\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { User } from \"./data\";\n\nexport const AuthorModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/authors\",\n name: \"authors\",\n model: User,\n moduleId: \"04cfc677-0fd2-4f5e-adf4-2483a00c0277\",\n });\n","import { UsersIcon } from \"lucide-react\";\nimport { createJsonApiInclusion } from \"../../core\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { User } from \"./data/user\";\n\nexport const UserModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/users\",\n name: \"users\",\n model: User,\n moduleId: \"04cfc677-0fd2-4f5e-adf4-2483a00c0277\",\n icon: UsersIcon,\n inclusions: {\n lists: {\n fields: [\n createJsonApiInclusion(\"users\", [`name`, `email`, `avatar`, `title`]),\n createJsonApiInclusion(\"usertopics\", [`level`]),\n createJsonApiInclusion(\"userexpertises\", [`level`]),\n createJsonApiInclusion(\"topics\", [`name`]),\n ],\n },\n },\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { OAuthClientInput, OAuthClientInterface, OAuthConsentRequest } from \"../interfaces/oauth.interface\";\n\n/**\n * OAuth client data model\n * Represents a registered OAuth application that can request access tokens\n */\nexport class OAuthClient extends AbstractApiData implements OAuthClientInterface {\n private _clientId?: string;\n private _name?: string;\n private _description?: string;\n private _redirectUris: string[] = [];\n private _allowedScopes: string[] = [];\n private _allowedGrantTypes: string[] = [];\n private _isConfidential: boolean = true;\n private _isActive: boolean = true;\n\n get clientId(): string {\n return this._clientId ?? this.id;\n }\n\n get name(): string {\n if (!this._name) throw new Error(\"Name is not defined\");\n return this._name;\n }\n\n get description(): string | undefined {\n return this._description;\n }\n\n get redirectUris(): string[] {\n return this._redirectUris;\n }\n\n get allowedScopes(): string[] {\n return this._allowedScopes;\n }\n\n get allowedGrantTypes(): string[] {\n return this._allowedGrantTypes;\n }\n\n get isConfidential(): boolean {\n return this._isConfidential;\n }\n\n get isActive(): boolean {\n return this._isActive;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes || {};\n\n this._clientId = attrs.clientId ?? this._id;\n this._name = attrs.name;\n this._description = attrs.description;\n this._redirectUris = attrs.redirectUris ?? [];\n this._allowedScopes = attrs.allowedScopes ?? [];\n this._allowedGrantTypes = attrs.allowedGrantTypes ?? [];\n this._isConfidential = attrs.isConfidential ?? true;\n this._isActive = attrs.isActive ?? true;\n\n return this;\n }\n\n createJsonApi(data: OAuthClientInput) {\n const response: any = {\n data: {\n type: \"oauth-clients\",\n attributes: {},\n },\n };\n\n if (data.id) response.data.id = data.id;\n if (data.name !== undefined) response.data.attributes.name = data.name;\n if (data.description !== undefined) response.data.attributes.description = data.description;\n if (data.redirectUris !== undefined) response.data.attributes.redirectUris = data.redirectUris;\n if (data.allowedScopes !== undefined) response.data.attributes.allowedScopes = data.allowedScopes;\n if (data.allowedGrantTypes !== undefined) response.data.attributes.allowedGrantTypes = data.allowedGrantTypes;\n if (data.isConfidential !== undefined) response.data.attributes.isConfidential = data.isConfidential;\n if (data.isActive !== undefined) response.data.attributes.isActive = data.isActive;\n\n return response;\n }\n\n /**\n * Body for POST /oauth/authorize/approve.\n * The authorize endpoints speak the RFC 6749 wire format (snake_case keys,\n * no JSON:API envelope), so this dedicated method owns that shape and the\n * service sends it with `overridesJsonApiCreation`.\n */\n createApproveAuthorizationJsonApi(params: OAuthConsentRequest) {\n return {\n client_id: params.clientId,\n redirect_uri: params.redirectUri,\n scope: params.scope,\n state: params.state,\n code_challenge: params.codeChallenge,\n code_challenge_method: params.codeChallengeMethod,\n company_id: params.companyId,\n };\n }\n\n /**\n * Body for POST /oauth/authorize/deny (RFC 6749 wire format, see above).\n */\n createDenyAuthorizationJsonApi(params: OAuthConsentRequest) {\n return {\n client_id: params.clientId,\n redirect_uri: params.redirectUri,\n state: params.state,\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { OAuthClient } from \"./data/oauth\";\n\nexport const OAuthModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/oauth\",\n name: \"oauth-clients\",\n model: OAuthClient,\n });\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef } from \"../../../core\";\nimport {\n OAuthClientCreateRequest,\n OAuthClientCreateResponse,\n OAuthClientInput,\n OAuthClientInterface,\n OAuthConsentInfo,\n OAuthConsentRequest,\n} from \"../interfaces/oauth.interface\";\nimport { OAuthClient } from \"./oauth\";\n\n/**\n * Service for OAuth client management and authorization consent flow.\n *\n * Client Management endpoints:\n * - GET /oauth/clients - List all clients for current user\n * - GET /oauth/clients/:clientId - Get single client\n * - POST /oauth/clients - Create new client (returns secret once)\n * - PATCH /oauth/clients/:clientId - Update client\n * - DELETE /oauth/clients/:clientId - Delete client\n * - POST /oauth/clients/:clientId/regenerate-secret - Regenerate client secret\n *\n * Consent Flow endpoints:\n * - GET /oauth/authorize/info - Get client info for consent screen\n * - POST /oauth/authorize/approve - Approve authorization\n * - POST /oauth/authorize/deny - Deny authorization\n */\nexport class OAuthService extends AbstractService {\n // ==========================================\n // CLIENT MANAGEMENT\n // ==========================================\n\n /**\n * List all OAuth clients for the current user\n */\n static async listClients(params?: { next?: NextRef }): Promise<OAuthClientInterface[]> {\n return this.callApi<OAuthClientInterface[]>({\n type: Modules.OAuth,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: \"oauth/clients\" }).generate(),\n next: params?.next,\n });\n }\n\n /**\n * Get a single OAuth client by ID\n */\n static async getClient(params: { clientId: string }): Promise<OAuthClientInterface> {\n return this.callApi<OAuthClientInterface>({\n type: Modules.OAuth,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: \"oauth/clients\", id: params.clientId }).generate(),\n });\n }\n\n /**\n * Create a new OAuth client\n * @returns The created client AND the client secret (shown only once!)\n */\n static async createClient(data: OAuthClientCreateRequest): Promise<OAuthClientCreateResponse> {\n const result = await this.callApiWithMeta<OAuthClientInterface>({\n type: Modules.OAuth,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: \"oauth/clients\" }).generate(),\n input: data,\n });\n\n return {\n client: result.data,\n clientSecret: result.meta?.clientSecret as string | undefined,\n };\n }\n\n /**\n * Update an existing OAuth client\n */\n static async updateClient(params: {\n clientId: string;\n data: Partial<OAuthClientInput>;\n }): Promise<OAuthClientInterface> {\n return this.callApi<OAuthClientInterface>({\n type: Modules.OAuth,\n method: HttpMethod.PATCH,\n endpoint: new EndpointCreator({ endpoint: \"oauth/clients\", id: params.clientId }).generate(),\n input: { id: params.clientId, ...params.data },\n });\n }\n\n /**\n * Delete an OAuth client\n */\n static async deleteClient(params: { clientId: string }): Promise<void> {\n await this.callApi({\n type: Modules.OAuth,\n method: HttpMethod.DELETE,\n endpoint: new EndpointCreator({ endpoint: \"oauth/clients\", id: params.clientId }).generate(),\n });\n }\n\n /**\n * Regenerate the client secret\n * @returns The new client secret (shown only once!)\n */\n static async regenerateSecret(params: { clientId: string }): Promise<{ clientSecret: string }> {\n const result = await this.callApiWithMeta<OAuthClientInterface>({\n type: Modules.OAuth,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({\n endpoint: \"oauth/clients\",\n id: params.clientId,\n childEndpoint: \"regenerate-secret\",\n }).generate(),\n });\n\n return {\n clientSecret: result.meta?.clientSecret as string,\n };\n }\n\n // ==========================================\n // CONSENT FLOW\n // ==========================================\n\n /**\n * Get client information for the consent screen\n * Called when user is redirected to /oauth/authorize\n */\n static async getAuthorizationInfo(params: OAuthConsentRequest): Promise<OAuthConsentInfo> {\n const endpoint = new EndpointCreator({ endpoint: \"oauth/authorize/info\" });\n\n // Add query parameters\n endpoint.addAdditionalParam(\"client_id\", params.clientId);\n endpoint.addAdditionalParam(\"redirect_uri\", params.redirectUri);\n if (params.scope) endpoint.addAdditionalParam(\"scope\", params.scope);\n if (params.state) endpoint.addAdditionalParam(\"state\", params.state);\n if (params.codeChallenge) endpoint.addAdditionalParam(\"code_challenge\", params.codeChallenge);\n if (params.codeChallengeMethod) endpoint.addAdditionalParam(\"code_challenge_method\", params.codeChallengeMethod);\n\n return this.callApi<OAuthConsentInfo>({\n type: Modules.OAuth,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Approve the authorization request\n * @returns Redirect URL with authorization code\n */\n static async approveAuthorization(params: OAuthConsentRequest): Promise<{ redirectUrl: string }> {\n const result = await this.callApiWithMeta<unknown>({\n type: Modules.OAuth,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: \"oauth/authorize/approve\" }).generate(),\n input: new OAuthClient().createApproveAuthorizationJsonApi(params),\n overridesJsonApiCreation: true,\n });\n\n return {\n redirectUrl: result.meta?.redirectUrl as string,\n };\n }\n\n /**\n * Deny the authorization request\n * @returns Redirect URL with error=access_denied\n */\n static async denyAuthorization(params: OAuthConsentRequest): Promise<{ redirectUrl: string }> {\n const result = await this.callApiWithMeta<unknown>({\n type: Modules.OAuth,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: \"oauth/authorize/deny\" }).generate(),\n input: new OAuthClient().createDenyAuthorizationJsonApi(params),\n overridesJsonApiCreation: true,\n });\n\n return {\n redirectUrl: result.meta?.redirectUrl as string,\n };\n }\n}\n","import { ApiDataInterface } from \"../../../core\";\n\n/**\n * OAuth client application interface\n * Represents a registered OAuth application that can request access tokens\n */\nexport interface OAuthClientInterface extends ApiDataInterface {\n /** The public client identifier (UUID format) */\n get clientId(): string;\n /** Human-readable application name */\n get name(): string;\n /** Optional description of the application */\n get description(): string | undefined;\n /** Array of allowed redirect URIs (exact match validation) */\n get redirectUris(): string[];\n /** Array of scopes this client can request */\n get allowedScopes(): string[];\n /** Supported grant types (authorization_code, client_credentials, refresh_token) */\n get allowedGrantTypes(): string[];\n /** True for server-side apps (can keep secret secure), false for mobile/desktop apps */\n get isConfidential(): boolean;\n /** Whether the client is currently active */\n get isActive(): boolean;\n /** When the client was created */\n get createdAt(): Date;\n /** When the client was last updated */\n get updatedAt(): Date;\n}\n\n/**\n * Input type for OAuth client CRUD operations\n */\nexport type OAuthClientInput = {\n id?: string;\n name?: string;\n description?: string;\n redirectUris?: string[];\n allowedScopes?: string[];\n allowedGrantTypes?: string[];\n isConfidential?: boolean;\n isActive?: boolean;\n};\n\n/**\n * Request body for creating a new OAuth client\n */\nexport interface OAuthClientCreateRequest {\n /** Required: Human-readable application name */\n name: string;\n /** Optional: Description of the application */\n description?: string;\n /** Required: At least one redirect URI */\n redirectUris: string[];\n /** Required: Array of scopes the client needs */\n allowedScopes: string[];\n /** Optional: Grant types (defaults to authorization_code + refresh_token) */\n allowedGrantTypes?: string[];\n /** Required: Whether this is a confidential client */\n isConfidential: boolean;\n}\n\n/**\n * Response when creating a client (includes one-time secret)\n */\nexport interface OAuthClientCreateResponse {\n client: OAuthClientInterface;\n /** Only returned on creation - must be saved immediately */\n clientSecret?: string;\n}\n\n/**\n * Parameters for the OAuth authorization consent flow\n * Passed via URL query parameters to the consent page\n */\nexport interface OAuthConsentRequest {\n /** The client_id requesting authorization */\n clientId: string;\n /** Where to redirect after authorization */\n redirectUri: string;\n /**\n * Space-separated list of requested scopes. Optional per RFC 6749 §3.3 —\n * when omitted, the authorization server defaults to the client's\n * registered scopes (MCP clients like Claude Code omit it).\n */\n scope?: string;\n /** CSRF protection token (passed back on redirect) */\n state?: string;\n /** PKCE code challenge (required for public clients) */\n codeChallenge?: string;\n /** PKCE method: 'S256' (recommended) or 'plain' */\n codeChallengeMethod?: string;\n /** The studio (company) the issued tokens are bound to; sent as `company_id` on approve */\n companyId?: string;\n}\n\n/**\n * Scope information for display in consent screen\n */\nexport interface OAuthScopeInfo {\n /** The scope identifier (e.g., 'read', 'mcp') */\n scope: string;\n /** Human-readable scope name */\n name: string;\n /** Description of what this scope allows */\n description: string;\n /** Optional icon identifier */\n icon?: string;\n}\n\n/**\n * Client info returned for consent screen display\n */\nexport interface OAuthConsentInfo {\n client: OAuthClientInterface;\n scopes: OAuthScopeInfo[];\n /** The caller's companies, ordered by name; the user picks one when there is more than one */\n companies?: Array<{ id: string; name: string }>;\n}\n\n/**\n * Default scope display configuration\n * Maps scope identifiers to human-readable info\n */\nexport const OAUTH_SCOPE_DISPLAY: Record<string, OAuthScopeInfo> = {\n read: {\n scope: \"read\",\n name: \"Read Access\",\n description: \"Read access to your data\",\n icon: \"eye\",\n },\n write: {\n scope: \"write\",\n name: \"Write Access\",\n description: \"Write access to your data\",\n icon: \"pencil\",\n },\n profile: {\n scope: \"profile\",\n name: \"View Profile\",\n description: \"Access your name and email\",\n icon: \"user\",\n },\n mcp: {\n scope: \"mcp\",\n name: \"MCP Server Access\",\n description: \"Let an AI assistant read and act on your data through the MCP server\",\n icon: \"shield\",\n },\n admin: {\n scope: \"admin\",\n name: \"Administrative Access\",\n description: \"Full administrative access to your account\",\n icon: \"shield\",\n },\n};\n\n/**\n * Available scopes list for the scope selector\n */\nexport const AVAILABLE_OAUTH_SCOPES: OAuthScopeInfo[] = Object.values(OAUTH_SCOPE_DISPLAY);\n\n/**\n * Default grant types for new clients\n */\nexport const DEFAULT_GRANT_TYPES = [\"authorization_code\", \"refresh_token\"];\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { WaitlistInput, WaitlistInterface, WaitlistStatus } from \"./WaitlistInterface\";\n\nexport class Waitlist extends AbstractApiData implements WaitlistInterface {\n private _email?: string;\n private _gdprConsent: boolean = false;\n private _gdprConsentAt?: string;\n private _marketingConsent?: boolean;\n private _marketingConsentAt?: string;\n private _questionnaire?: Record<string, any>;\n private _status: WaitlistStatus = \"pending\";\n private _confirmedAt?: string;\n private _invitedAt?: string;\n private _registeredAt?: string;\n private _sourceRef?: string;\n private _sourceUtmSource?: string;\n private _sourceUtmMedium?: string;\n private _sourceUtmCampaign?: string;\n private _sourceReferrer?: string;\n private _sourceLandingPath?: string;\n\n get email(): string {\n return this._email ?? \"\";\n }\n\n get gdprConsent(): boolean {\n return this._gdprConsent;\n }\n\n get gdprConsentAt(): string {\n return this._gdprConsentAt ?? \"\";\n }\n\n get marketingConsent(): boolean | undefined {\n return this._marketingConsent;\n }\n\n get marketingConsentAt(): string | undefined {\n return this._marketingConsentAt;\n }\n\n get questionnaire(): Record<string, any> | undefined {\n return this._questionnaire;\n }\n\n get status(): WaitlistStatus {\n return this._status;\n }\n\n get confirmedAt(): string | undefined {\n return this._confirmedAt;\n }\n\n get invitedAt(): string | undefined {\n return this._invitedAt;\n }\n\n get registeredAt(): string | undefined {\n return this._registeredAt;\n }\n\n get sourceRef(): string | undefined {\n return this._sourceRef;\n }\n\n get sourceUtmSource(): string | undefined {\n return this._sourceUtmSource;\n }\n\n get sourceUtmMedium(): string | undefined {\n return this._sourceUtmMedium;\n }\n\n get sourceUtmCampaign(): string | undefined {\n return this._sourceUtmCampaign;\n }\n\n get sourceReferrer(): string | undefined {\n return this._sourceReferrer;\n }\n\n get sourceLandingPath(): string | undefined {\n return this._sourceLandingPath;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._email = data.jsonApi.attributes.email;\n this._gdprConsent = data.jsonApi.attributes.gdprConsent;\n this._gdprConsentAt = data.jsonApi.attributes.gdprConsentAt;\n this._marketingConsent = data.jsonApi.attributes.marketingConsent;\n this._marketingConsentAt = data.jsonApi.attributes.marketingConsentAt;\n // The API's descriptor transform parses the stored JSON string into an object\n const questionnaire = data.jsonApi.attributes.questionnaire;\n this._questionnaire = questionnaire && typeof questionnaire === \"object\" ? questionnaire : undefined;\n this._status = data.jsonApi.attributes.status;\n this._confirmedAt = data.jsonApi.attributes.confirmedAt;\n this._invitedAt = data.jsonApi.attributes.invitedAt;\n this._registeredAt = data.jsonApi.attributes.registeredAt;\n this._sourceRef = data.jsonApi.attributes.sourceRef;\n this._sourceUtmSource = data.jsonApi.attributes.sourceUtmSource;\n this._sourceUtmMedium = data.jsonApi.attributes.sourceUtmMedium;\n this._sourceUtmCampaign = data.jsonApi.attributes.sourceUtmCampaign;\n this._sourceReferrer = data.jsonApi.attributes.sourceReferrer;\n this._sourceLandingPath = data.jsonApi.attributes.sourceLandingPath;\n\n return this;\n }\n\n createJsonApi(data: WaitlistInput) {\n const response: any = {\n data: {\n type: Modules.Waitlist.name,\n id: data.id,\n attributes: {\n email: data.email,\n gdprConsent: data.gdprConsent,\n gdprConsentAt: data.gdprConsentAt,\n },\n meta: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.marketingConsent !== undefined) {\n response.data.attributes.marketingConsent = data.marketingConsent;\n }\n\n if (data.marketingConsentAt !== undefined) {\n response.data.attributes.marketingConsentAt = data.marketingConsentAt;\n }\n\n // JSON.stringify questionnaire for backend storage\n if (data.questionnaire !== undefined) {\n response.data.attributes.questionnaire = JSON.stringify(data.questionnaire);\n }\n\n if (data.source !== undefined) {\n const attributes = response.data.attributes;\n if (data.source.sourceRef !== undefined) attributes.sourceRef = data.source.sourceRef;\n if (data.source.sourceUtmSource !== undefined) attributes.sourceUtmSource = data.source.sourceUtmSource;\n if (data.source.sourceUtmMedium !== undefined) attributes.sourceUtmMedium = data.source.sourceUtmMedium;\n if (data.source.sourceUtmCampaign !== undefined) attributes.sourceUtmCampaign = data.source.sourceUtmCampaign;\n if (data.source.sourceReferrer !== undefined) attributes.sourceReferrer = data.source.sourceReferrer;\n if (data.source.sourceLandingPath !== undefined) attributes.sourceLandingPath = data.source.sourceLandingPath;\n }\n\n return response;\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef, PreviousRef } from \"../../../core\";\nimport { InviteValidation, WaitlistInput, WaitlistInterface } from \"./WaitlistInterface\";\nimport { WaitlistStatsInterface } from \"./waitlist-stats.interface\";\n\nexport class WaitlistService extends AbstractService {\n /**\n * Submit to waitlist (public)\n * Uses Waitlist.createJsonApi() to transform WaitlistInput to JSON:API format\n */\n static async submit(params: WaitlistInput): Promise<WaitlistInterface> {\n return this.callApi<WaitlistInterface>({\n type: Modules.Waitlist,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.Waitlist }).generate(),\n input: params,\n });\n }\n\n /**\n * Confirm email (public)\n */\n static async confirm(code: string): Promise<WaitlistInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Waitlist,\n childEndpoint: \"confirm\",\n childId: code,\n });\n\n return this.callApi<WaitlistInterface>({\n type: Modules.Waitlist,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * List all waitlist entries (admin)\n * Uses cursor-based pagination with NextRef/PreviousRef\n */\n static async findMany(params?: {\n status?: string;\n search?: string;\n fetchAll?: boolean;\n next?: NextRef;\n previous?: PreviousRef;\n }): Promise<WaitlistInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Waitlist });\n\n if (params?.status) endpoint.addAdditionalParam(\"status\", params.status);\n if (params?.search) endpoint.addAdditionalParam(\"search\", params.search);\n if (params?.fetchAll) endpoint.addAdditionalParam(\"fetchAll\", \"true\");\n\n return this.callApi<WaitlistInterface[]>({\n type: Modules.Waitlist,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params?.next,\n previous: params?.previous,\n });\n }\n\n /**\n * Send invite (admin)\n */\n static async invite(id: string): Promise<WaitlistInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Waitlist,\n id,\n childEndpoint: \"invite\",\n });\n\n return this.callApi<WaitlistInterface>({\n type: Modules.Waitlist,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Batch invite (admin)\n * Non-standard batch operation - uses custom JSON:API format\n */\n static async inviteBatch(ids: string[]): Promise<{ invited: number; failed: number }> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Waitlist,\n childEndpoint: \"invite-batch\",\n });\n\n return this.callApi({\n type: Modules.Waitlist,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: {\n data: {\n type: \"waitlist-batch-invites\",\n attributes: { ids },\n },\n },\n overridesJsonApiCreation: true,\n });\n }\n\n /**\n * Get statistics (admin): GET /waitlists/stats, rehydrated as waitlist-stats\n */\n static async getStats(): Promise<WaitlistStatsInterface> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Waitlist,\n id: \"stats\",\n });\n\n return this.callApi<WaitlistStatsInterface>({\n type: Modules.WaitlistStats,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Validate invite code (public) - calls auth endpoint\n */\n static async validateInvite(code: string): Promise<InviteValidation | null> {\n try {\n const endpoint = new EndpointCreator({\n endpoint: Modules.Waitlist,\n childEndpoint: \"invite\",\n childId: code,\n });\n\n const response = await this.callApiWithMeta<any>({\n type: Modules.Auth,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n\n // Response structure: data._jsonApi.attributes contains the actual values\n const attributes = response.data?._jsonApi?.attributes;\n\n return {\n email: attributes?.email,\n valid: attributes?.valid ?? false,\n };\n } catch (_error) {\n return null;\n }\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { WaitlistStatsInterface } from \"./waitlist-stats.interface\";\n\nexport class WaitlistStats extends AbstractApiData implements WaitlistStatsInterface {\n private _pending: number = 0;\n private _confirmed: number = 0;\n private _invited: number = 0;\n private _registered: number = 0;\n private _total: number = 0;\n\n get pending(): number {\n return this._pending;\n }\n\n get confirmed(): number {\n return this._confirmed;\n }\n\n get invited(): number {\n return this._invited;\n }\n\n get registered(): number {\n return this._registered;\n }\n\n get total(): number {\n return this._total;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._pending = attrs.pending ?? 0;\n this._confirmed = attrs.confirmed ?? 0;\n this._invited = attrs.invited ?? 0;\n this._registered = attrs.registered ?? 0;\n this._total = attrs.total ?? 0;\n\n return this;\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"WaitlistStats is read-only and cannot be created\");\n }\n}\n","export enum WaitlistFields {\n waitlistId = \"waitlistId\",\n email = \"email\",\n status = \"status\",\n createdAt = \"createdAt\",\n source = \"source\",\n questionnaire = \"questionnaire\",\n actions = \"actions\",\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { Waitlist } from \"./data/Waitlist\";\n\nexport const WaitlistModule = (factory: ModuleFactory) =>\n factory({\n name: \"waitlists\",\n pageUrl: \"/waitlists\",\n model: Waitlist,\n });\n","import { ModuleFactory } from \"../../permissions\";\nimport { WaitlistStats } from \"./data/waitlist-stats\";\n\nexport const WaitlistStatsModule = (factory: ModuleFactory) =>\n factory({\n name: \"waitlist-stats\",\n pageUrl: \"/waitlist-stats\",\n model: WaitlistStats,\n });\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { PermissionMappingInterface } from \"./PermissionMappingInterface\";\n\nexport class PermissionMapping extends AbstractApiData implements PermissionMappingInterface {\n private _roleId?: string;\n private _moduleId?: string;\n private _permissions?: {\n create?: boolean | string;\n read?: boolean | string;\n update?: boolean | string;\n delete?: boolean | string;\n };\n\n get roleId(): string {\n if (!this._roleId) throw new Error(\"JsonApi error: permission mapping roleId is missing\");\n return this._roleId;\n }\n\n get moduleId(): string {\n if (!this._moduleId) throw new Error(\"JsonApi error: permission mapping moduleId is missing\");\n return this._moduleId;\n }\n\n get permissions(): {\n create?: boolean | string;\n read?: boolean | string;\n update?: boolean | string;\n delete?: boolean | string;\n } {\n if (!this._permissions) throw new Error(\"JsonApi error: permission mapping permissions is missing\");\n return this._permissions;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._roleId = data.jsonApi.attributes.roleId;\n this._moduleId = data.jsonApi.attributes.moduleId;\n this._permissions = data.jsonApi.meta?.permissions;\n\n return this;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { ModulePathsInterface } from \"./ModulePathsInterface\";\n\nexport class ModulePaths extends AbstractApiData implements ModulePathsInterface {\n private _moduleId?: string;\n private _paths?: string[];\n\n get moduleId(): string {\n if (!this._moduleId) throw new Error(\"JsonApi error: module paths moduleId is missing\");\n return this._moduleId;\n }\n\n get paths(): string[] {\n return this._paths ?? [];\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._moduleId = data.jsonApi.attributes.moduleId;\n this._paths = data.jsonApi.attributes.paths;\n\n return this;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport type { RbacMatrix } from \"./RbacTypes\";\n\n/**\n * Input shape accepted by `RbacMatrixModel.createJsonApi()` for a PUT request\n * to the dev matrix endpoint.\n */\nexport interface RbacMatrixInput {\n matrix: RbacMatrix;\n roleNames: Record<string, string>;\n moduleNames: Record<string, string>;\n outputPath: string;\n}\n\n/**\n * Frontend model for the dev-only `rbac-matrix` JSON:API resource.\n *\n * Backend contract (see `rbac-dev.controller.ts`):\n * - `GET /_dev/rbac/matrix` → `{ data: { type: \"rbac-matrix\", id: \"singleton\",\n * attributes: { matrix } } }`\n * - `PUT /_dev/rbac/matrix` body: `{ data: { type: \"rbac-matrix\", attributes:\n * { matrix, roleNames, moduleNames, outputPath } } }`\n * → `{ data: { type: \"rbac-matrix\", id: \"singleton\", attributes: { bytesWritten, path } } }`\n *\n * The resource is a singleton (`id: \"singleton\"`) so there is no collection\n * listing; the \"read\" and \"write\" shapes share a single model with optional\n * fields populated depending on which endpoint produced the response.\n */\nexport class RbacMatrixModel extends AbstractApiData {\n private _matrix?: RbacMatrix;\n private _modulePaths?: Record<string, readonly string[]>;\n private _bytesWritten?: number;\n private _path?: string;\n\n /** The RBAC matrix object (populated after a GET). */\n get matrix(): RbacMatrix | undefined {\n return this._matrix;\n }\n\n /**\n * UUID-keyed map of each module's known BFS relationship paths (populated\n * after a GET). Fed to the permission picker as scope suggestions.\n */\n get modulePaths(): Record<string, readonly string[]> | undefined {\n return this._modulePaths;\n }\n\n /** Bytes written to the permissions.ts file (populated after a PUT). */\n get bytesWritten(): number | undefined {\n return this._bytesWritten;\n }\n\n /** Resolved absolute output path (populated after a PUT). */\n get path(): string | undefined {\n return this._path;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes ?? {};\n this._matrix = attrs.matrix;\n this._modulePaths = attrs.modulePaths;\n this._bytesWritten = attrs.bytesWritten;\n this._path = attrs.path;\n\n return this;\n }\n\n createJsonApi(data: RbacMatrixInput): any {\n return {\n data: {\n type: \"rbac-matrix\",\n id: \"singleton\",\n attributes: {\n matrix: data.matrix,\n roleNames: data.roleNames,\n moduleNames: data.moduleNames,\n outputPath: data.outputPath,\n },\n },\n };\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { PermissionMapping } from \"./data/PermissionMapping\";\nimport { ModulePaths } from \"./data/ModulePaths\";\nimport { RbacMatrixModel } from \"./data/RbacMatrixModel\";\n\nexport const PermissionMappingModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/rbac/permission-mappings\",\n name: \"rbac/permission-mappings\",\n model: PermissionMapping,\n moduleId: \"f3aef019-c75f-4ca8-a9b5-a7495d901fc8\",\n });\n\nexport const ModulePathsModule = (factory: ModuleFactory) =>\n factory({\n pageUrl: \"/rbac/module-paths\",\n name: \"rbac/module-paths\",\n model: ModulePaths,\n moduleId: \"f4fb3f01-a947-4c2e-89c8-354a518cdb13\",\n });\n\n/**\n * Dev-only matrix module. The `name` is the URL path of the dev singleton\n * endpoint (`GET|PUT _dev/rbac/matrix`), NOT a plural resource collection.\n * This module is only useful when the backend is running with `devMode: true`\n * on `RbacModule.register`.\n */\nexport const RbacMatrixModule = (factory: ModuleFactory) =>\n factory({\n name: \"_dev/rbac/matrix\",\n model: RbacMatrixModel,\n });\n","import { AbstractApiData } from \"../../../core\";\nimport { JsonApiHydratedDataInterface } from \"../../../core/interfaces/JsonApiHydratedDataInterface\";\nimport { ReferralStatsInterface } from \"../interfaces\";\n\nexport class ReferralStats extends AbstractApiData implements ReferralStatsInterface {\n private _referralCode?: string;\n private _completedReferrals?: number;\n private _totalTokensEarned?: number;\n\n get referralCode(): string {\n return this._referralCode ?? \"\";\n }\n get completedReferrals(): number {\n return this._completedReferrals ?? 0;\n }\n get totalTokensEarned(): number {\n return this._totalTokensEarned ?? 0;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n this._referralCode = data.jsonApi.attributes.referralCode;\n this._completedReferrals = data.jsonApi.attributes.completedReferrals;\n this._totalTokensEarned = data.jsonApi.attributes.totalTokensEarned;\n return this;\n }\n\n createJsonApi() {\n return {};\n }\n}\n","import { AbstractService, HttpMethod } from \"../../../core\";\nimport { EndpointCreator } from \"../../../core\";\nimport { Modules } from \"../../../core\";\n\nimport { ReferralStatsInterface } from \"../interfaces\";\n\nexport class ReferralService extends AbstractService {\n /**\n * Get referral stats for the current company.\n * Returns deserialized attributes from JSON:API response.\n */\n static async getMyReferralStats(): Promise<ReferralStatsInterface> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Referral, id: \"stats\" });\n\n return this.callApi<ReferralStatsInterface>({\n type: Modules.ReferralStats,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /**\n * Send a referral invitation email.\n * Uses overridesJsonApiCreation since the endpoint accepts a simple JSON body, not JSON:API format.\n * Returns 204 No Content on success.\n */\n static async sendReferralEmail(email: string): Promise<void> {\n const endpoint = new EndpointCreator({ endpoint: Modules.Referral, id: \"invite\" });\n\n await this.callApi<void>({\n type: Modules.Referral,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n input: { email },\n overridesJsonApiCreation: true,\n // The widget turns the error code into a translated message; the global\n // handler would show the API's English text and swallow the failure.\n suppressGlobalError: true,\n });\n }\n\n /**\n * Count one visit to a referral link. Answers 204 whatever the code.\n */\n static async recordVisit(params: { referralCode: string }): Promise<void> {\n const endpoint = new EndpointCreator({ endpoint: Modules.ReferralVisit });\n\n await this.callApi<void>({\n type: Modules.ReferralVisit,\n method: HttpMethod.POST,\n endpoint: endpoint.generate(),\n // The request layer builds the body through ReferralVisit.createJsonApi().\n input: { referralCode: params.referralCode },\n // A failed beacon must never show an error to a visitor.\n suppressGlobalError: true,\n });\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\n\n/**\n * One visit to a referral link, posted once per browser when the referral\n * cookie is first set. The API answers 204, so there is nothing to read back.\n * The body carries no id: the server generates the visit id.\n */\nexport class ReferralVisit extends AbstractApiData {\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n return this;\n }\n\n createJsonApi(data: { referralCode: string }) {\n return {\n data: {\n type: Modules.ReferralVisit.name,\n attributes: { referralCode: data.referralCode },\n },\n };\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { ReferralAdminSummaryInterface } from \"../interfaces/referral-admin.interface\";\n\n/** One window of the referral report tiles: `current` or `previous`. Read-only. */\nexport class ReferralAdminSummary extends AbstractApiData implements ReferralAdminSummaryInterface {\n private _window?: string;\n private _visits?: number;\n private _invitesSent?: number;\n private _signups?: number;\n private _paying?: number;\n private _creditsGiven?: number;\n private _sharingCompanies?: number;\n private _eligibleCompanies?: number;\n\n get window(): string {\n return this._window ?? \"\";\n }\n get visits(): number {\n return this._visits ?? 0;\n }\n get invitesSent(): number {\n return this._invitesSent ?? 0;\n }\n get signups(): number {\n return this._signups ?? 0;\n }\n get paying(): number {\n return this._paying ?? 0;\n }\n get creditsGiven(): number {\n return this._creditsGiven ?? 0;\n }\n get sharingCompanies(): number {\n return this._sharingCompanies ?? 0;\n }\n get eligibleCompanies(): number {\n return this._eligibleCompanies ?? 0;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._window = attrs.window;\n this._visits = attrs.visits;\n this._invitesSent = attrs.invitesSent;\n this._signups = attrs.signups;\n this._paying = attrs.paying;\n this._creditsGiven = attrs.creditsGiven;\n this._sharingCompanies = attrs.sharingCompanies;\n this._eligibleCompanies = attrs.eligibleCompanies;\n\n return this;\n }\n\n /** The resource is read-only: there are no attributes to send. */\n createJsonApi(data: { id: string }) {\n return { data: { type: Modules.ReferralAdminSummary.name, id: data.id, attributes: {} } };\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { ReferralAdminReferrerInterface } from \"../interfaces/referral-admin.interface\";\n\n/** One referrer company in the referral ranking. Read-only. */\nexport class ReferralAdminReferrer extends AbstractApiData implements ReferralAdminReferrerInterface {\n private _name?: string;\n private _email?: string;\n private _visits?: number;\n private _invitesSent?: number;\n private _signupsLink?: number;\n private _signupsDirect?: number;\n private _paying?: number;\n private _creditsEarned?: number;\n private _lastInviteAt?: Date;\n\n get name(): string {\n return this._name ?? \"\";\n }\n get email(): string {\n return this._email ?? \"\";\n }\n get visits(): number {\n return this._visits ?? 0;\n }\n get invitesSent(): number {\n return this._invitesSent ?? 0;\n }\n get signupsLink(): number {\n return this._signupsLink ?? 0;\n }\n get signupsDirect(): number {\n return this._signupsDirect ?? 0;\n }\n get paying(): number {\n return this._paying ?? 0;\n }\n get creditsEarned(): number {\n return this._creditsEarned ?? 0;\n }\n get lastInviteAt(): Date | undefined {\n return this._lastInviteAt;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._name = attrs.name;\n this._email = attrs.email;\n this._visits = attrs.visits;\n this._invitesSent = attrs.invitesSent;\n this._signupsLink = attrs.signupsLink;\n this._signupsDirect = attrs.signupsDirect;\n this._paying = attrs.paying;\n this._creditsEarned = attrs.creditsEarned;\n this._lastInviteAt = attrs.lastInviteAt ? new Date(attrs.lastInviteAt) : undefined;\n\n return this;\n }\n\n /** The resource is read-only: there are no attributes to send. */\n createJsonApi(data: { id: string }) {\n return { data: { type: Modules.ReferralAdminReferrer.name, id: data.id, attributes: {} } };\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { ReferralAdminReferralInterface } from \"../interfaces/referral-admin.interface\";\n\n/** One referral in the administrative list: the new account and who brought it. Read-only. */\nexport class ReferralAdminReferral extends AbstractApiData implements ReferralAdminReferralInterface {\n private _name?: string;\n private _email?: string;\n private _referrerName?: string;\n private _referrerEmail?: string;\n private _source?: string;\n private _status?: string;\n private _referredAt?: Date;\n private _completedAt?: Date;\n private _tokensAwarded?: number;\n\n get name(): string {\n return this._name ?? \"\";\n }\n get email(): string {\n return this._email ?? \"\";\n }\n get referrerName(): string {\n return this._referrerName ?? \"\";\n }\n get referrerEmail(): string {\n return this._referrerEmail ?? \"\";\n }\n get source(): string {\n return this._source ?? \"\";\n }\n get status(): string {\n return this._status ?? \"\";\n }\n get referredAt(): Date {\n return this._referredAt ?? new Date(0);\n }\n get completedAt(): Date | undefined {\n return this._completedAt;\n }\n get tokensAwarded(): number | undefined {\n return this._tokensAwarded;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._name = attrs.name;\n this._email = attrs.email;\n this._referrerName = attrs.referrerName;\n this._referrerEmail = attrs.referrerEmail;\n this._source = attrs.source;\n this._status = attrs.status;\n this._referredAt = attrs.referredAt ? new Date(attrs.referredAt) : undefined;\n this._completedAt = attrs.completedAt ? new Date(attrs.completedAt) : undefined;\n this._tokensAwarded = attrs.tokensAwarded ?? undefined;\n\n return this;\n }\n\n /** The resource is read-only: there are no attributes to send. */\n createJsonApi(data: { id: string }) {\n return { data: { type: Modules.ReferralAdminReferral.name, id: data.id, attributes: {} } };\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport {\n ReferralAdminPeriod,\n ReferralAdminReferralInterface,\n ReferralAdminReferrerInterface,\n ReferralAdminSort,\n ReferralAdminSummaryInterface,\n} from \"../interfaces/referral-admin.interface\";\n\n/** Reads of the administrative referral report. Every route is administrator-only. */\nexport class ReferralAdminService extends AbstractService {\n /** Headline tiles: one row per window (`current`, plus `previous` unless the period is `all`). */\n static async getSummary(params: { period: ReferralAdminPeriod }): Promise<ReferralAdminSummaryInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.ReferralAdminSummary });\n endpoint.addAdditionalParam(\"period\", params.period);\n\n return this.callApi<ReferralAdminSummaryInterface[]>({\n type: Modules.ReferralAdminSummary,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /** The referrers ranking, ordered by the allowlisted sort. */\n static async getReferrers(params: {\n period: ReferralAdminPeriod;\n sort: ReferralAdminSort;\n }): Promise<ReferralAdminReferrerInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.ReferralAdminReferrer });\n endpoint.addAdditionalParam(\"period\", params.period);\n endpoint.addAdditionalParam(\"sort\", params.sort);\n\n return this.callApi<ReferralAdminReferrerInterface[]>({\n type: Modules.ReferralAdminReferrer,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /** Every referral in the period, newest first. */\n static async getReferrals(params: { period: ReferralAdminPeriod }): Promise<ReferralAdminReferralInterface[]> {\n const endpoint = new EndpointCreator({ endpoint: Modules.ReferralAdminReferral });\n endpoint.addAdditionalParam(\"period\", params.period);\n\n return this.callApi<ReferralAdminReferralInterface[]>({\n type: Modules.ReferralAdminReferral,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { ReferralService } from \"./data/ReferralService\";\n\nexport const ReferralModule = (factory: ModuleFactory) =>\n factory({\n name: \"referrals\",\n pageUrl: \"/referrals\",\n model: ReferralService,\n });\n","import { ModuleFactory } from \"../../permissions\";\nimport { ReferralStats } from \"./data/ReferralStats\";\n\nexport const ReferralStatsModule = (factory: ModuleFactory) =>\n factory({\n name: \"referral-stats\",\n pageUrl: \"/referral-stats\",\n model: ReferralStats,\n });\n","import { GiftIcon } from \"lucide-react\";\nimport { ModuleFactory, ModuleWithPermissions } from \"../../permissions\";\nimport { ReferralAdminReferral } from \"./data/ReferralAdminReferral\";\nimport { ReferralAdminReferrer } from \"./data/ReferralAdminReferrer\";\nimport { ReferralAdminSummary } from \"./data/ReferralAdminSummary\";\nimport { ReferralVisit } from \"./data/ReferralVisit\";\n\n/**\n * The referral visit and administrative report resources, as one object a\n * consuming app spreads into its `allModules` (same rationale as\n * `analyticsModules`). An app records link visits only once it spreads this.\n * None has a pageUrl: these are not navigable resources.\n */\nexport const referralAdminModules = (factory: ModuleFactory) =>\n ({\n ReferralVisit: factory({ name: \"referrals/visits\", model: ReferralVisit }),\n ReferralAdminSummary: factory({\n name: \"referrals/administration/summary\",\n model: ReferralAdminSummary,\n icon: GiftIcon,\n }),\n ReferralAdminReferrer: factory({\n name: \"referrals/administration/referrers\",\n model: ReferralAdminReferrer,\n icon: GiftIcon,\n }),\n ReferralAdminReferral: factory({\n name: \"referrals/administration/referrals\",\n model: ReferralAdminReferral,\n icon: GiftIcon,\n }),\n }) satisfies Record<string, ModuleWithPermissions>;\n","import { AbstractApiData } from \"../../../core/abstracts/AbstractApiData\";\nimport { JsonApiHydratedDataInterface } from \"../../../core/interfaces/JsonApiHydratedDataInterface\";\nimport { Modules } from \"../../../core/registry/ModuleRegistry\";\nimport { OfflineChangeResult, OfflineChangeSetInput, OfflineChangeSetInterface } from \"./OfflineChangeSetInterface\";\n\n/**\n * The package of every waiting change the device sends in ONE request\n * (`POST /offline-change-sets`). The answer carries one result per change.\n */\nexport class OfflineChangeSet extends AbstractApiData implements OfflineChangeSetInterface {\n private _results?: OfflineChangeResult[];\n\n get results(): OfflineChangeResult[] {\n return this._results ?? [];\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._results = data.jsonApi.attributes?.results ?? [];\n\n return this;\n }\n\n createJsonApi(data: OfflineChangeSetInput) {\n return {\n data: {\n type: Modules.OfflineChangeSet.name,\n id: data.id,\n attributes: { changes: data.changes },\n },\n };\n }\n}\n","import { ModuleFactory, ModuleWithPermissions } from \"../../../permissions/types\";\nimport { RootSync } from \"./RootSync\";\nimport { OfflineChangeSet } from \"./OfflineChangeSet\";\n\n/**\n * The offline resources, as one object an app that opts into offline mode\n * spreads into its `allModules` (same rationale as `analyticsModules`).\n * Neither has a pageUrl: these are not navigable resources.\n */\nexport const offlineModules = (factory: ModuleFactory) =>\n ({\n OfflineChangeSet: factory({ name: \"offline-change-sets\", model: OfflineChangeSet }),\n RootSync: factory({ name: \"root-syncs\", model: RootSync }),\n }) satisfies Record<string, ModuleWithPermissions>;\n","import { CoinsIcon } from \"lucide-react\";\nimport { ModuleFactory } from \"../../permissions\";\nimport { TokenUsageAdminBreakdown } from \"./data/tokenusage-admin-breakdown\";\nimport { TokenUsageAdminSummary } from \"./data/tokenusage-admin-summary\";\nimport { TokenUsageAdminTimeline } from \"./data/tokenusage-admin-timeline\";\n\n/**\n * Read-only reporting resources behind the administrative token-usage page.\n * `name` is the endpoint path EndpointCreator builds URLs from; there is no\n * pageUrl because these are not navigable resources.\n *\n * Distinct from the consuming app's own TokenUsage module (name \"tokenusages\",\n * pageUrl \"/tokenusage\") — that one is an AppModuleDefinitions entry the package\n * cannot reference, which is why these three exist.\n *\n * PREFER `tokenUsageModules(factory)` from `./tokenusage.modules` — it returns\n * these three plus the three self-service report modules as one spreadable\n * object. These individual factories are kept for backward compatibility.\n */\nexport const TokenUsageAdminSummaryModule = (factory: ModuleFactory) =>\n factory({ name: \"tokenusages/administration/summary\", model: TokenUsageAdminSummary, icon: CoinsIcon });\n\nexport const TokenUsageAdminTimelineModule = (factory: ModuleFactory) =>\n factory({ name: \"tokenusages/administration/timeline\", model: TokenUsageAdminTimeline, icon: CoinsIcon });\n\nexport const TokenUsageAdminBreakdownModule = (factory: ModuleFactory) =>\n factory({ name: \"tokenusages/administration/breakdown\", model: TokenUsageAdminBreakdown, icon: CoinsIcon });\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { TokenUsageAdminBreakdownInterface } from \"./tokenusage-admin-breakdown.interface\";\nimport { TokenUsageAdminBreakdownInput } from \"./tokenusage-admin.types\";\n\n/**\n * One ranked row of a breakdown. The same shape serves all three dimensions —\n * company, user and operation — which is why the three \"by-X\" views collapse\n * into a single `breakdown?dimension=` call.\n *\n * `activeUsers` / `monthlyCredits` / `availableMonthlyCredits` are populated only\n * for `dimension=company`; they are absent on user and operation rows.\n */\nexport class TokenUsageAdminBreakdown extends AbstractApiData implements TokenUsageAdminBreakdownInterface {\n private _label: string = \"\";\n private _sublabel?: string;\n private _cost: number = 0;\n private _credits: number = 0;\n private _tokensIn: number = 0;\n private _tokensOut: number = 0;\n private _cached: number = 0;\n private _calls: number = 0;\n private _activeUsers?: number;\n private _monthlyCredits?: number;\n private _availableMonthlyCredits?: number;\n\n get label(): string {\n return this._label;\n }\n\n get sublabel(): string | undefined {\n return this._sublabel;\n }\n\n get cost(): number {\n return this._cost;\n }\n\n get credits(): number {\n return this._credits;\n }\n\n get tokensIn(): number {\n return this._tokensIn;\n }\n\n get tokensOut(): number {\n return this._tokensOut;\n }\n\n get cached(): number {\n return this._cached;\n }\n\n get calls(): number {\n return this._calls;\n }\n\n get activeUsers(): number | undefined {\n return this._activeUsers;\n }\n\n get monthlyCredits(): number | undefined {\n return this._monthlyCredits;\n }\n\n get availableMonthlyCredits(): number | undefined {\n return this._availableMonthlyCredits;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._label = attrs.label ?? \"\";\n this._sublabel = attrs.sublabel ?? undefined;\n this._cost = attrs.cost ?? 0;\n this._credits = attrs.credits ?? 0;\n this._tokensIn = attrs.tokensIn ?? 0;\n this._tokensOut = attrs.tokensOut ?? 0;\n this._cached = attrs.cached ?? 0;\n this._calls = attrs.calls ?? 0;\n this._activeUsers = attrs.activeUsers ?? undefined;\n this._monthlyCredits = attrs.monthlyCredits ?? undefined;\n this._availableMonthlyCredits = attrs.availableMonthlyCredits ?? undefined;\n\n return this;\n }\n\n createJsonApi(data: TokenUsageAdminBreakdownInput) {\n const response: any = {\n data: {\n type: Modules.TokenUsageAdminBreakdown.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.label !== undefined) response.data.attributes.label = data.label;\n if (data.sublabel !== undefined) response.data.attributes.sublabel = data.sublabel;\n if (data.cost !== undefined) response.data.attributes.cost = data.cost;\n if (data.credits !== undefined) response.data.attributes.credits = data.credits;\n if (data.tokensIn !== undefined) response.data.attributes.tokensIn = data.tokensIn;\n if (data.tokensOut !== undefined) response.data.attributes.tokensOut = data.tokensOut;\n if (data.cached !== undefined) response.data.attributes.cached = data.cached;\n if (data.calls !== undefined) response.data.attributes.calls = data.calls;\n if (data.activeUsers !== undefined) response.data.attributes.activeUsers = data.activeUsers;\n if (data.monthlyCredits !== undefined) response.data.attributes.monthlyCredits = data.monthlyCredits;\n if (data.availableMonthlyCredits !== undefined)\n response.data.attributes.availableMonthlyCredits = data.availableMonthlyCredits;\n\n return response;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { TokenUsageAdminSummaryInterface } from \"./tokenusage-admin-summary.interface\";\nimport { TokenUsageAdminSummaryInput } from \"./tokenusage-admin.types\";\n\n/**\n * One row of the administrative summary: a cost centre observed over one time\n * window. Six rows are returned per request — scope crossed with window — so the\n * KPI tiles can render a value and its delta without a second call.\n */\nexport class TokenUsageAdminSummary extends AbstractApiData implements TokenUsageAdminSummaryInterface {\n private _scope: string = \"\";\n private _window: string = \"\";\n private _cost: number = 0;\n private _credits: number = 0;\n private _tokensIn: number = 0;\n private _tokensOut: number = 0;\n private _cached: number = 0;\n private _calls: number = 0;\n\n get scope(): string {\n return this._scope;\n }\n\n get window(): string {\n return this._window;\n }\n\n get cost(): number {\n return this._cost;\n }\n\n get credits(): number {\n return this._credits;\n }\n\n get tokensIn(): number {\n return this._tokensIn;\n }\n\n get tokensOut(): number {\n return this._tokensOut;\n }\n\n get cached(): number {\n return this._cached;\n }\n\n get calls(): number {\n return this._calls;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._scope = attrs.scope ?? \"\";\n this._window = attrs.window ?? \"\";\n this._cost = attrs.cost ?? 0;\n this._credits = attrs.credits ?? 0;\n this._tokensIn = attrs.tokensIn ?? 0;\n this._tokensOut = attrs.tokensOut ?? 0;\n this._cached = attrs.cached ?? 0;\n this._calls = attrs.calls ?? 0;\n\n return this;\n }\n\n createJsonApi(data: TokenUsageAdminSummaryInput) {\n const response: any = {\n data: {\n type: Modules.TokenUsageAdminSummary.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.scope !== undefined) response.data.attributes.scope = data.scope;\n if (data.window !== undefined) response.data.attributes.window = data.window;\n if (data.cost !== undefined) response.data.attributes.cost = data.cost;\n if (data.credits !== undefined) response.data.attributes.credits = data.credits;\n if (data.tokensIn !== undefined) response.data.attributes.tokensIn = data.tokensIn;\n if (data.tokensOut !== undefined) response.data.attributes.tokensOut = data.tokensOut;\n if (data.cached !== undefined) response.data.attributes.cached = data.cached;\n if (data.calls !== undefined) response.data.attributes.calls = data.calls;\n\n return response;\n }\n}\n","import { AbstractApiData, formatLocalDate, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { TokenUsageAdminTimelineInterface } from \"./tokenusage-admin-timeline.interface\";\nimport { TokenUsageAdminTimelineInput } from \"./tokenusage-admin.types\";\n\n/**\n * One (bucket, series) cell of the usage-over-time chart. Rows arrive flat; the\n * chart pivots them into stacked columns.\n */\nexport class TokenUsageAdminTimeline extends AbstractApiData implements TokenUsageAdminTimelineInterface {\n private _bucket: Date = new Date(0);\n private _series: string = \"\";\n private _cost: number = 0;\n private _credits: number = 0;\n private _tokensIn: number = 0;\n private _tokensOut: number = 0;\n private _cached: number = 0;\n private _calls: number = 0;\n\n get bucket(): Date {\n return this._bucket;\n }\n\n get series(): string {\n return this._series;\n }\n\n get cost(): number {\n return this._cost;\n }\n\n get credits(): number {\n return this._credits;\n }\n\n get tokensIn(): number {\n return this._tokensIn;\n }\n\n get tokensOut(): number {\n return this._tokensOut;\n }\n\n get cached(): number {\n return this._cached;\n }\n\n get calls(): number {\n return this._calls;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n // Wire format is \"YYYY-MM-DD\"; the interface promises a Date.\n this._bucket = attrs.bucket ? new Date(attrs.bucket) : new Date(0);\n this._series = attrs.series ?? \"\";\n this._cost = attrs.cost ?? 0;\n this._credits = attrs.credits ?? 0;\n this._tokensIn = attrs.tokensIn ?? 0;\n this._tokensOut = attrs.tokensOut ?? 0;\n this._cached = attrs.cached ?? 0;\n this._calls = attrs.calls ?? 0;\n\n return this;\n }\n\n createJsonApi(data: TokenUsageAdminTimelineInput) {\n const response: any = {\n data: {\n type: Modules.TokenUsageAdminTimeline.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n // `bucket` is type: \"date\" on the backend descriptor. formatLocalDate is\n // mandatory — passing the raw Date lets JSON.stringify call .toISOString(),\n // which UTC-shifts and can move the bucket a day. Imported from the package\n // core, never reimplemented inline.\n if (data.bucket !== undefined) response.data.attributes.bucket = formatLocalDate(data.bucket);\n if (data.series !== undefined) response.data.attributes.series = data.series;\n if (data.cost !== undefined) response.data.attributes.cost = data.cost;\n if (data.credits !== undefined) response.data.attributes.credits = data.credits;\n if (data.tokensIn !== undefined) response.data.attributes.tokensIn = data.tokensIn;\n if (data.tokensOut !== undefined) response.data.attributes.tokensOut = data.tokensOut;\n if (data.cached !== undefined) response.data.attributes.cached = data.cached;\n if (data.calls !== undefined) response.data.attributes.calls = data.calls;\n\n return response;\n }\n}\n","import { CoinsIcon } from \"lucide-react\";\nimport { ModuleFactory, ModuleWithPermissions } from \"../../permissions\";\nimport { TokenUsageAdminBreakdown } from \"./data/tokenusage-admin-breakdown\";\nimport { TokenUsageAdminSummary } from \"./data/tokenusage-admin-summary\";\nimport { TokenUsageAdminTimeline } from \"./data/tokenusage-admin-timeline\";\nimport { TokenUsageReportBreakdown } from \"./data/tokenusage-report-breakdown\";\nimport { TokenUsageReportSummary } from \"./data/tokenusage-report-summary\";\nimport { TokenUsageReportTimeline } from \"./data/tokenusage-report-timeline\";\n\n/**\n * Every read-only reporting resource behind the two token-usage pages, as one\n * object a consuming app spreads into its `allModules`.\n *\n * A bundle rather than six separate factories because registration is the APP'S\n * job — `allModules` is also the source of `AllModuleDefinitions` and what\n * `DataClassRegistry.bootstrap()` reads, so the package cannot self-register.\n * Six names is six chances to forget one, and a forgotten name is not a compile\n * error: `FoundationModuleDefinitions` declares them all, so `Modules.X`\n * typechecks and is `undefined` at runtime. One spread cannot be partially done.\n *\n * `name` is the endpoint path EndpointCreator builds URLs from. None has a\n * pageUrl — these are not navigable resources.\n *\n * The return type is the six named keys, NOT `Record<string,\n * ModuleWithPermissions>`: the consuming app derives `AllModuleDefinitions`\n * from `typeof allModules`, so spreading an index-signature type in would give\n * that object a string index signature and make every `Modules.<anything>`\n * lookup typecheck. `satisfies` keeps the constraint without widening.\n */\nexport const tokenUsageModules = (factory: ModuleFactory) =>\n ({\n TokenUsageAdminSummary: factory({\n name: \"tokenusages/administration/summary\",\n model: TokenUsageAdminSummary,\n icon: CoinsIcon,\n }),\n TokenUsageAdminTimeline: factory({\n name: \"tokenusages/administration/timeline\",\n model: TokenUsageAdminTimeline,\n icon: CoinsIcon,\n }),\n TokenUsageAdminBreakdown: factory({\n name: \"tokenusages/administration/breakdown\",\n model: TokenUsageAdminBreakdown,\n icon: CoinsIcon,\n }),\n TokenUsageReportSummary: factory({ name: \"tokenusages/reports/summary\", model: TokenUsageReportSummary }),\n TokenUsageReportTimeline: factory({ name: \"tokenusages/reports/timeline\", model: TokenUsageReportTimeline }),\n TokenUsageReportBreakdown: factory({ name: \"tokenusages/reports/breakdown\", model: TokenUsageReportBreakdown }),\n }) satisfies Record<string, ModuleWithPermissions>;\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { TokenUsageReportBreakdownInterface } from \"./tokenusage-report-breakdown.interface\";\nimport { TokenUsageReportBreakdownInput } from \"./tokenusage-report.types\";\n\n/**\n * One ranked row of a self-service breakdown. The same shape serves both\n * dimensions — operation and target — which is why the two \"by-X\" panels\n * collapse into a single `breakdown?dimension=` call.\n *\n * Deliberately narrower than the administrative row: no `activeUsers`,\n * `monthlyCredits` or `availableMonthlyCredits`, which are company-fleet\n * figures a single company has no business seeing.\n */\nexport class TokenUsageReportBreakdown extends AbstractApiData implements TokenUsageReportBreakdownInterface {\n private _label: string = \"\";\n private _sublabel?: string;\n private _cost: number = 0;\n private _credits: number = 0;\n private _tokensIn: number = 0;\n private _tokensOut: number = 0;\n private _cached: number = 0;\n private _calls: number = 0;\n\n get label(): string {\n return this._label;\n }\n\n get sublabel(): string | undefined {\n return this._sublabel;\n }\n\n get cost(): number {\n return this._cost;\n }\n\n get credits(): number {\n return this._credits;\n }\n\n get tokensIn(): number {\n return this._tokensIn;\n }\n\n get tokensOut(): number {\n return this._tokensOut;\n }\n\n get cached(): number {\n return this._cached;\n }\n\n get calls(): number {\n return this._calls;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._label = attrs.label ?? \"\";\n this._sublabel = attrs.sublabel ?? undefined;\n this._cost = attrs.cost ?? 0;\n this._credits = attrs.credits ?? 0;\n this._tokensIn = attrs.tokensIn ?? 0;\n this._tokensOut = attrs.tokensOut ?? 0;\n this._cached = attrs.cached ?? 0;\n this._calls = attrs.calls ?? 0;\n\n return this;\n }\n\n createJsonApi(data: TokenUsageReportBreakdownInput) {\n const response: any = {\n data: {\n type: Modules.TokenUsageReportBreakdown.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.label !== undefined) response.data.attributes.label = data.label;\n if (data.sublabel !== undefined) response.data.attributes.sublabel = data.sublabel;\n if (data.cost !== undefined) response.data.attributes.cost = data.cost;\n if (data.credits !== undefined) response.data.attributes.credits = data.credits;\n if (data.tokensIn !== undefined) response.data.attributes.tokensIn = data.tokensIn;\n if (data.tokensOut !== undefined) response.data.attributes.tokensOut = data.tokensOut;\n if (data.cached !== undefined) response.data.attributes.cached = data.cached;\n if (data.calls !== undefined) response.data.attributes.calls = data.calls;\n\n return response;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { TokenUsageReportSummaryInterface } from \"./tokenusage-report-summary.interface\";\nimport { TokenUsageReportSummaryInput } from \"./tokenusage-report.types\";\n\n/**\n * One row of the self-service summary: the caller's own company observed over\n * one time window. Two rows are returned per request — \"current\" and the\n * equal-length \"previous\" span — so the KPI tiles can render a value and its\n * delta without a second call.\n *\n * There is no `scope` here: a company only ever sees itself, so the\n * customer/platform split that the administrative summary carries is absent.\n */\nexport class TokenUsageReportSummary extends AbstractApiData implements TokenUsageReportSummaryInterface {\n private _window: string = \"\";\n private _cost: number = 0;\n private _credits: number = 0;\n private _tokensIn: number = 0;\n private _tokensOut: number = 0;\n private _cached: number = 0;\n private _calls: number = 0;\n\n get window(): string {\n return this._window;\n }\n\n get cost(): number {\n return this._cost;\n }\n\n get credits(): number {\n return this._credits;\n }\n\n get tokensIn(): number {\n return this._tokensIn;\n }\n\n get tokensOut(): number {\n return this._tokensOut;\n }\n\n get cached(): number {\n return this._cached;\n }\n\n get calls(): number {\n return this._calls;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._window = attrs.window ?? \"\";\n this._cost = attrs.cost ?? 0;\n this._credits = attrs.credits ?? 0;\n this._tokensIn = attrs.tokensIn ?? 0;\n this._tokensOut = attrs.tokensOut ?? 0;\n this._cached = attrs.cached ?? 0;\n this._calls = attrs.calls ?? 0;\n\n return this;\n }\n\n createJsonApi(data: TokenUsageReportSummaryInput) {\n const response: any = {\n data: {\n type: Modules.TokenUsageReportSummary.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.window !== undefined) response.data.attributes.window = data.window;\n if (data.cost !== undefined) response.data.attributes.cost = data.cost;\n if (data.credits !== undefined) response.data.attributes.credits = data.credits;\n if (data.tokensIn !== undefined) response.data.attributes.tokensIn = data.tokensIn;\n if (data.tokensOut !== undefined) response.data.attributes.tokensOut = data.tokensOut;\n if (data.cached !== undefined) response.data.attributes.cached = data.cached;\n if (data.calls !== undefined) response.data.attributes.calls = data.calls;\n\n return response;\n }\n}\n","import { AbstractApiData, formatLocalDate, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { TokenUsageReportTimelineInterface } from \"./tokenusage-report-timeline.interface\";\nimport { TokenUsageReportTimelineInput } from \"./tokenusage-report.types\";\n\n/**\n * One (bucket, series) cell of the self-service usage-over-time chart. Rows\n * arrive flat; the chart pivots them into stacked columns.\n */\nexport class TokenUsageReportTimeline extends AbstractApiData implements TokenUsageReportTimelineInterface {\n private _bucket: Date = new Date(0);\n private _series: string = \"\";\n private _cost: number = 0;\n private _credits: number = 0;\n private _tokensIn: number = 0;\n private _tokensOut: number = 0;\n private _cached: number = 0;\n private _calls: number = 0;\n\n get bucket(): Date {\n return this._bucket;\n }\n\n get series(): string {\n return this._series;\n }\n\n get cost(): number {\n return this._cost;\n }\n\n get credits(): number {\n return this._credits;\n }\n\n get tokensIn(): number {\n return this._tokensIn;\n }\n\n get tokensOut(): number {\n return this._tokensOut;\n }\n\n get cached(): number {\n return this._cached;\n }\n\n get calls(): number {\n return this._calls;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n // Wire format is \"YYYY-MM-DD\"; the interface promises a Date.\n this._bucket = attrs.bucket ? new Date(attrs.bucket) : new Date(0);\n this._series = attrs.series ?? \"\";\n this._cost = attrs.cost ?? 0;\n this._credits = attrs.credits ?? 0;\n this._tokensIn = attrs.tokensIn ?? 0;\n this._tokensOut = attrs.tokensOut ?? 0;\n this._cached = attrs.cached ?? 0;\n this._calls = attrs.calls ?? 0;\n\n return this;\n }\n\n createJsonApi(data: TokenUsageReportTimelineInput) {\n const response: any = {\n data: {\n type: Modules.TokenUsageReportTimeline.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n // `bucket` is type: \"date\" on the backend descriptor. formatLocalDate is\n // mandatory — passing the raw Date lets JSON.stringify call .toISOString(),\n // which UTC-shifts and can move the bucket a day. Imported from the package\n // core, never reimplemented inline.\n if (data.bucket !== undefined) response.data.attributes.bucket = formatLocalDate(data.bucket);\n if (data.series !== undefined) response.data.attributes.series = data.series;\n if (data.cost !== undefined) response.data.attributes.cost = data.cost;\n if (data.credits !== undefined) response.data.attributes.credits = data.credits;\n if (data.tokensIn !== undefined) response.data.attributes.tokensIn = data.tokensIn;\n if (data.tokensOut !== undefined) response.data.attributes.tokensOut = data.tokensOut;\n if (data.cached !== undefined) response.data.attributes.cached = data.cached;\n if (data.calls !== undefined) response.data.attributes.calls = data.calls;\n\n return response;\n }\n}\n","import { ChartNoAxesCombinedIcon } from \"lucide-react\";\nimport { ModuleFactory, ModuleWithPermissions } from \"../../permissions\";\nimport { AnalyticsBreakdown } from \"./data/analytics-breakdown\";\nimport { AnalyticsEvent } from \"./data/analytics-event\";\nimport { AnalyticsPageView } from \"./data/analytics-page-view\";\nimport { AnalyticsSession } from \"./data/analytics-session\";\nimport { AnalyticsSummary } from \"./data/analytics-summary\";\nimport { AnalyticsTimeline } from \"./data/analytics-timeline\";\n\n/**\n * Every resource behind the analytics tracker and dashboard, as one object a\n * consuming app spreads into its `allModules`. Same rationale as\n * `tokenUsageModules`: one spread cannot be partially done, and a forgotten\n * name is `undefined` at runtime even though `Modules.X` typechecks.\n *\n * `AnalyticsPageView` is only a child segment: the service builds\n * `analytics/administration/sessions/{id}/pageviews` with EndpointCreator's\n * `childEndpoint`. None has a pageUrl — these are not navigable resources.\n *\n * `satisfies` keeps the constraint without widening the return type to an\n * index signature (see tokenusage.modules.ts).\n */\nexport const analyticsModules = (factory: ModuleFactory) =>\n ({\n AnalyticsEvent: factory({ name: \"analytics/events\", model: AnalyticsEvent }),\n AnalyticsSummary: factory({\n name: \"analytics/administration/summary\",\n model: AnalyticsSummary,\n icon: ChartNoAxesCombinedIcon,\n }),\n AnalyticsTimeline: factory({\n name: \"analytics/administration/timeline\",\n model: AnalyticsTimeline,\n icon: ChartNoAxesCombinedIcon,\n }),\n AnalyticsBreakdown: factory({\n name: \"analytics/administration/breakdown\",\n model: AnalyticsBreakdown,\n icon: ChartNoAxesCombinedIcon,\n }),\n AnalyticsSession: factory({\n name: \"analytics/administration/sessions\",\n model: AnalyticsSession,\n icon: ChartNoAxesCombinedIcon,\n }),\n AnalyticsPageView: factory({ name: \"pageviews\", model: AnalyticsPageView, icon: ChartNoAxesCombinedIcon }),\n }) satisfies Record<string, ModuleWithPermissions>;\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { AnalyticsBreakdownInterface } from \"./analytics-breakdown.interface\";\nimport { AnalyticsBreakdownInput } from \"./analytics.types\";\n\n/** One row of a breakdown table: a key (source, route, …) and its counts. */\nexport class AnalyticsBreakdown extends AbstractApiData implements AnalyticsBreakdownInterface {\n private _dimension: string = \"\";\n private _key: string = \"\";\n private _visitors: number = 0;\n private _sessions: number = 0;\n private _pageViews: number = 0;\n\n get dimension(): string {\n return this._dimension;\n }\n\n get key(): string {\n return this._key;\n }\n\n get visitors(): number {\n return this._visitors;\n }\n\n get sessions(): number {\n return this._sessions;\n }\n\n get pageViews(): number {\n return this._pageViews;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._dimension = attrs.dimension ?? \"\";\n this._key = attrs.key ?? \"\";\n this._visitors = attrs.visitors ?? 0;\n this._sessions = attrs.sessions ?? 0;\n this._pageViews = attrs.pageViews ?? 0;\n\n return this;\n }\n\n createJsonApi(data: AnalyticsBreakdownInput) {\n const response: any = {\n data: {\n type: Modules.AnalyticsBreakdown.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.dimension !== undefined) response.data.attributes.dimension = data.dimension;\n if (data.key !== undefined) response.data.attributes.key = data.key;\n if (data.visitors !== undefined) response.data.attributes.visitors = data.visitors;\n if (data.sessions !== undefined) response.data.attributes.sessions = data.sessions;\n if (data.pageViews !== undefined) response.data.attributes.pageViews = data.pageViews;\n\n return response;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface } from \"../../../core\";\nimport { AnalyticsEventInterface } from \"./analytics-event.interface\";\nimport { AnalyticsEventInput } from \"./analytics.types\";\n\n/**\n * JSON:API resource type of a posted event. It must match `analyticsEventMeta.type`\n * in the API, whose DTO rejects any other value. It is not `Modules.AnalyticsEvent.name`:\n * that is the endpoint path (\"analytics/events\"), the same split the auth models\n * make (e.g. backup-code-verify.ts posts type \"backup-codes\").\n */\nconst ANALYTICS_EVENT_TYPE = \"analytics-events\";\n\n/**\n * One page view as the tracker posts it. The API answers with the stored event,\n * so rehydrate reads back the two attributes every event carries.\n */\nexport class AnalyticsEvent extends AbstractApiData implements AnalyticsEventInterface {\n private _path: string = \"\";\n private _section: string = \"\";\n\n get path(): string {\n return this._path;\n }\n\n get section(): string {\n return this._section;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._path = attrs.path ?? \"\";\n this._section = attrs.section ?? \"\";\n\n return this;\n }\n\n createJsonApi(data: AnalyticsEventInput) {\n const response: any = {\n data: {\n type: ANALYTICS_EVENT_TYPE,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n response.data.attributes.path = data.path;\n response.data.attributes.section = data.section;\n if (data.referrer !== undefined) response.data.attributes.referrer = data.referrer;\n if (data.utmSource !== undefined) response.data.attributes.utmSource = data.utmSource;\n if (data.utmMedium !== undefined) response.data.attributes.utmMedium = data.utmMedium;\n if (data.utmCampaign !== undefined) response.data.attributes.utmCampaign = data.utmCampaign;\n if (data.utmTerm !== undefined) response.data.attributes.utmTerm = data.utmTerm;\n if (data.utmContent !== undefined) response.data.attributes.utmContent = data.utmContent;\n if (data.visitorId !== undefined) response.data.attributes.visitorId = data.visitorId;\n if (data.screenWidth !== undefined) response.data.attributes.screenWidth = data.screenWidth;\n if (data.consentGranted !== undefined) response.data.attributes.consentGranted = data.consentGranted;\n\n return response;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { AnalyticsPageViewInterface } from \"./analytics-page-view.interface\";\nimport { AnalyticsPageViewInput } from \"./analytics.types\";\n\n/** One step of a session journey. */\nexport class AnalyticsPageView extends AbstractApiData implements AnalyticsPageViewInterface {\n private _path: string = \"\";\n private _route: string = \"\";\n private _section: string = \"\";\n\n get path(): string {\n return this._path;\n }\n\n get route(): string {\n return this._route;\n }\n\n get section(): string {\n return this._section;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n // `createdAt` is inherited: the serialiser sends it in meta and\n // super.rehydrate() parses it into a Date. Honour it as an attribute too,\n // should the descriptor ever list it among the fields.\n if (attrs.createdAt) this._createdAt = new Date(attrs.createdAt);\n this._path = attrs.path ?? \"\";\n this._route = attrs.route ?? \"\";\n this._section = attrs.section ?? \"\";\n\n return this;\n }\n\n createJsonApi(data: AnalyticsPageViewInput) {\n const response: any = {\n data: {\n type: Modules.AnalyticsPageView.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n // type: \"datetime\" on the backend descriptor: an ISO 8601 instant.\n if (data.createdAt !== undefined) response.data.attributes.createdAt = data.createdAt.toISOString();\n if (data.path !== undefined) response.data.attributes.path = data.path;\n if (data.route !== undefined) response.data.attributes.route = data.route;\n if (data.section !== undefined) response.data.attributes.section = data.section;\n\n return response;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { UserInterface } from \"../../user/data/user.interface\";\nimport { AnalyticsSessionInterface } from \"./analytics-session.interface\";\nimport { AnalyticsSessionInput } from \"./analytics.types\";\n\n/** One visit: a run of page views from one visitor with no 30-minute gap. */\nexport class AnalyticsSession extends AbstractApiData implements AnalyticsSessionInterface {\n private _visitorId: string = \"\";\n private _consented: boolean = false;\n private _startedAt: Date = new Date(0);\n private _lastSeenAt: Date = new Date(0);\n private _pageViews: number = 0;\n private _section: string = \"\";\n private _landingRoute: string = \"\";\n private _referrerHost?: string;\n private _utmSource?: string;\n private _utmMedium?: string;\n private _utmCampaign?: string;\n private _utmTerm?: string;\n private _utmContent?: string;\n private _deviceType: string = \"\";\n private _user?: UserInterface;\n\n get visitorId(): string {\n return this._visitorId;\n }\n\n get consented(): boolean {\n return this._consented;\n }\n\n get startedAt(): Date {\n return this._startedAt;\n }\n\n get lastSeenAt(): Date {\n return this._lastSeenAt;\n }\n\n get pageViews(): number {\n return this._pageViews;\n }\n\n get section(): string {\n return this._section;\n }\n\n get landingRoute(): string {\n return this._landingRoute;\n }\n\n get referrerHost(): string | undefined {\n return this._referrerHost;\n }\n\n get utmSource(): string | undefined {\n return this._utmSource;\n }\n\n get utmMedium(): string | undefined {\n return this._utmMedium;\n }\n\n get utmCampaign(): string | undefined {\n return this._utmCampaign;\n }\n\n get utmTerm(): string | undefined {\n return this._utmTerm;\n }\n\n get utmContent(): string | undefined {\n return this._utmContent;\n }\n\n get deviceType(): string {\n return this._deviceType;\n }\n\n get user(): UserInterface | undefined {\n return this._user;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._visitorId = attrs.visitorId ?? \"\";\n this._consented = attrs.consented ?? false;\n // Wire format is an ISO 8601 instant; the interface promises a Date.\n this._startedAt = attrs.startedAt ? new Date(attrs.startedAt) : new Date(0);\n this._lastSeenAt = attrs.lastSeenAt ? new Date(attrs.lastSeenAt) : new Date(0);\n this._pageViews = attrs.pageViews ?? 0;\n this._section = attrs.section ?? \"\";\n this._landingRoute = attrs.landingRoute ?? \"\";\n this._referrerHost = attrs.referrerHost ?? undefined;\n this._utmSource = attrs.utmSource ?? undefined;\n this._utmMedium = attrs.utmMedium ?? undefined;\n this._utmCampaign = attrs.utmCampaign ?? undefined;\n this._utmTerm = attrs.utmTerm ?? undefined;\n this._utmContent = attrs.utmContent ?? undefined;\n this._deviceType = attrs.deviceType ?? \"\";\n\n this._user = this._readIncluded(data, \"user\", Modules.User) as UserInterface | undefined;\n\n return this;\n }\n\n createJsonApi(data: AnalyticsSessionInput) {\n const response: any = {\n data: {\n type: Modules.AnalyticsSession.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.visitorId !== undefined) response.data.attributes.visitorId = data.visitorId;\n if (data.consented !== undefined) response.data.attributes.consented = data.consented;\n // Both are type: \"datetime\" on the backend descriptor: ISO 8601 instants.\n if (data.startedAt !== undefined) response.data.attributes.startedAt = data.startedAt.toISOString();\n if (data.lastSeenAt !== undefined) response.data.attributes.lastSeenAt = data.lastSeenAt.toISOString();\n if (data.pageViews !== undefined) response.data.attributes.pageViews = data.pageViews;\n if (data.section !== undefined) response.data.attributes.section = data.section;\n if (data.landingRoute !== undefined) response.data.attributes.landingRoute = data.landingRoute;\n if (data.referrerHost !== undefined) response.data.attributes.referrerHost = data.referrerHost;\n if (data.utmSource !== undefined) response.data.attributes.utmSource = data.utmSource;\n if (data.utmMedium !== undefined) response.data.attributes.utmMedium = data.utmMedium;\n if (data.utmCampaign !== undefined) response.data.attributes.utmCampaign = data.utmCampaign;\n if (data.utmTerm !== undefined) response.data.attributes.utmTerm = data.utmTerm;\n if (data.utmContent !== undefined) response.data.attributes.utmContent = data.utmContent;\n if (data.deviceType !== undefined) response.data.attributes.deviceType = data.deviceType;\n\n if (data.userId) {\n response.data.relationships.user = {\n data: { type: Modules.User.name, id: data.userId },\n };\n }\n\n return response;\n }\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { AnalyticsSummaryInterface } from \"./analytics-summary.interface\";\nimport { AnalyticsSummaryInput } from \"./analytics.types\";\n\n/** One (section, window) row of the headline tiles: current or previous period. */\nexport class AnalyticsSummary extends AbstractApiData implements AnalyticsSummaryInterface {\n private _section: string = \"\";\n private _window: string = \"\";\n private _visitors: number = 0;\n private _sessions: number = 0;\n private _pageViews: number = 0;\n private _pagesPerSession: number = 0;\n private _consentShare: number = 0;\n\n get section(): string {\n return this._section;\n }\n\n get window(): string {\n return this._window;\n }\n\n get visitors(): number {\n return this._visitors;\n }\n\n get sessions(): number {\n return this._sessions;\n }\n\n get pageViews(): number {\n return this._pageViews;\n }\n\n get pagesPerSession(): number {\n return this._pagesPerSession;\n }\n\n get consentShare(): number {\n return this._consentShare;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n this._section = attrs.section ?? \"\";\n this._window = attrs.window ?? \"\";\n this._visitors = attrs.visitors ?? 0;\n this._sessions = attrs.sessions ?? 0;\n this._pageViews = attrs.pageViews ?? 0;\n this._pagesPerSession = attrs.pagesPerSession ?? 0;\n this._consentShare = attrs.consentShare ?? 0;\n\n return this;\n }\n\n createJsonApi(data: AnalyticsSummaryInput) {\n const response: any = {\n data: {\n type: Modules.AnalyticsSummary.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n if (data.section !== undefined) response.data.attributes.section = data.section;\n if (data.window !== undefined) response.data.attributes.window = data.window;\n if (data.visitors !== undefined) response.data.attributes.visitors = data.visitors;\n if (data.sessions !== undefined) response.data.attributes.sessions = data.sessions;\n if (data.pageViews !== undefined) response.data.attributes.pageViews = data.pageViews;\n if (data.pagesPerSession !== undefined) response.data.attributes.pagesPerSession = data.pagesPerSession;\n if (data.consentShare !== undefined) response.data.attributes.consentShare = data.consentShare;\n\n return response;\n }\n}\n","import { AbstractApiData, formatLocalDate, JsonApiHydratedDataInterface, Modules } from \"../../../core\";\nimport { AnalyticsTimelineInterface } from \"./analytics-timeline.interface\";\nimport { AnalyticsTimelineInput } from \"./analytics.types\";\n\n/** One (bucket, section) point of the visitors-over-time chart. */\nexport class AnalyticsTimeline extends AbstractApiData implements AnalyticsTimelineInterface {\n private _bucket: Date = new Date(0);\n private _section: string = \"\";\n private _visitors: number = 0;\n private _sessions: number = 0;\n private _pageViews: number = 0;\n\n get bucket(): Date {\n return this._bucket;\n }\n\n get section(): string {\n return this._section;\n }\n\n get visitors(): number {\n return this._visitors;\n }\n\n get sessions(): number {\n return this._sessions;\n }\n\n get pageViews(): number {\n return this._pageViews;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attrs = data.jsonApi.attributes;\n // Wire format is \"YYYY-MM-DD\"; the interface promises a Date.\n this._bucket = attrs.bucket ? new Date(attrs.bucket) : new Date(0);\n this._section = attrs.section ?? \"\";\n this._visitors = attrs.visitors ?? 0;\n this._sessions = attrs.sessions ?? 0;\n this._pageViews = attrs.pageViews ?? 0;\n\n return this;\n }\n\n createJsonApi(data: AnalyticsTimelineInput) {\n const response: any = {\n data: {\n type: Modules.AnalyticsTimeline.name,\n id: data.id,\n attributes: {},\n relationships: {},\n },\n included: [],\n };\n\n // `bucket` is type: \"date\" on the backend descriptor: formatLocalDate, never the raw Date.\n if (data.bucket !== undefined) response.data.attributes.bucket = formatLocalDate(data.bucket);\n if (data.section !== undefined) response.data.attributes.section = data.section;\n if (data.visitors !== undefined) response.data.attributes.visitors = data.visitors;\n if (data.sessions !== undefined) response.data.attributes.sessions = data.sessions;\n if (data.pageViews !== undefined) response.data.attributes.pageViews = data.pageViews;\n\n return response;\n }\n}\n","/**\n * The currency the token-usage surfaces render monetary values in.\n *\n * The package cannot infer this: the figures come from whatever per-million-token\n * rates the consuming app configured, and those are denominated in whatever\n * currency that app buys inference in — EUR for one consumer, USD for another\n * whose rates are OpenRouter list prices. So the app declares it once at\n * bootstrap, next to configureJsonApi().\n *\n * EUR is the default purely for backward compatibility: the previous\n * implementation hard-coded a euro sign, so an app that never calls this keeps\n * rendering exactly what it rendered before.\n */\nlet currency = \"EUR\";\n\nexport function configureTokenUsage(config: { currency: string }): void {\n currency = config.currency;\n}\n\nexport function getTokenUsageCurrency(): string {\n return currency;\n}\n","import { AbstractApiData, JsonApiHydratedDataInterface, Modules, UserInterface } from \"../../../core\";\nimport { AuditLogInterface } from \"./audit-log.interface\";\n\nexport class AuditLog extends AbstractApiData implements AuditLogInterface {\n private _kind: \"audit\" | \"comment\" = \"audit\";\n private _action?: string;\n private _fieldName?: string;\n private _oldValue?: string;\n private _newValue?: string;\n private _content?: string;\n private _annotationId?: string;\n private _user?: UserInterface;\n\n get kind(): \"audit\" | \"comment\" {\n return this._kind;\n }\n\n get action(): string | undefined {\n return this._action;\n }\n\n get fieldName(): string | undefined {\n return this._fieldName;\n }\n\n get oldValue(): string | undefined {\n return this._oldValue;\n }\n\n get newValue(): string | undefined {\n return this._newValue;\n }\n\n get content(): string | undefined {\n return this._content;\n }\n\n get annotationId(): string | undefined {\n return this._annotationId;\n }\n\n get user(): UserInterface | undefined {\n return this._user;\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n this._kind = data.jsonApi.attributes.kind ?? \"audit\";\n this._action = data.jsonApi.attributes.action ?? undefined;\n this._fieldName = data.jsonApi.attributes.fieldName ?? undefined;\n this._oldValue = data.jsonApi.attributes.oldValue ?? undefined;\n this._newValue = data.jsonApi.attributes.newValue ?? undefined;\n this._content = data.jsonApi.attributes.content ?? undefined;\n this._annotationId = data.jsonApi.attributes.annotationId ?? undefined;\n\n this._user = this._readIncluded(data, \"user\", Modules.User) as UserInterface | undefined;\n\n return this;\n }\n\n createJsonApi(): any {\n throw new Error(\"AuditLog is read-only\");\n }\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef } from \"../../../core\";\nimport { AuditLogInterface } from \"./audit-log.interface\";\n\nexport class AuditLogService extends AbstractService {\n static async findActivityByEntity(params: {\n entityType: string;\n entityId: string;\n next?: NextRef;\n }): Promise<AuditLogInterface[]> {\n const endpoint = new EndpointCreator({\n endpoint: Modules.AuditLog,\n id: \"activity\",\n childEndpoint: params.entityType,\n childId: params.entityId,\n });\n\n return this.callApi({\n type: Modules.AuditLog,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n}\n","import { ModuleFactory } from \"../../permissions\";\nimport { AuditLog } from \"./data\";\n\nexport const AuditLogModule = (factory: ModuleFactory) =>\n factory({\n name: \"audit-logs\",\n pageUrl: \"/audit-logs\",\n model: AuditLog,\n moduleId: \"b7e2f4a1-9c3d-4e8b-a6d5-2f1c8e7b3a90\",\n });\n"]}