alepha 0.19.3 → 0.19.4

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 (215) hide show
  1. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  2. package/dist/api/audits/index.d.ts +8 -8
  3. package/dist/api/invitations/index.d.ts +790 -0
  4. package/dist/api/invitations/index.d.ts.map +1 -0
  5. package/dist/api/invitations/index.js +665 -0
  6. package/dist/api/invitations/index.js.map +1 -0
  7. package/dist/api/jobs/index.browser.js +8 -9
  8. package/dist/api/jobs/index.browser.js.map +1 -1
  9. package/dist/api/jobs/index.d.ts +99 -43
  10. package/dist/api/jobs/index.d.ts.map +1 -1
  11. package/dist/api/jobs/index.js +257 -40
  12. package/dist/api/jobs/index.js.map +1 -1
  13. package/dist/api/keys/index.d.ts +5 -5
  14. package/dist/api/notifications/index.browser.js +0 -1
  15. package/dist/api/notifications/index.browser.js.map +1 -1
  16. package/dist/api/notifications/index.d.ts +3 -3
  17. package/dist/api/notifications/index.d.ts.map +1 -1
  18. package/dist/api/notifications/index.js +0 -1
  19. package/dist/api/notifications/index.js.map +1 -1
  20. package/dist/api/parameters/index.browser.js +112 -1
  21. package/dist/api/parameters/index.browser.js.map +1 -1
  22. package/dist/api/parameters/index.d.ts +90 -3
  23. package/dist/api/parameters/index.d.ts.map +1 -1
  24. package/dist/api/parameters/index.js +79 -12
  25. package/dist/api/parameters/index.js.map +1 -1
  26. package/dist/{billing → api/payments}/index.d.ts +67 -49
  27. package/dist/api/payments/index.d.ts.map +1 -0
  28. package/dist/{billing → api/payments}/index.js +108 -74
  29. package/dist/api/payments/index.js.map +1 -0
  30. package/dist/api/subscriptions/index.d.ts +1692 -0
  31. package/dist/api/subscriptions/index.d.ts.map +1 -0
  32. package/dist/api/subscriptions/index.js +1870 -0
  33. package/dist/api/subscriptions/index.js.map +1 -0
  34. package/dist/api/users/index.d.ts +18 -2
  35. package/dist/api/users/index.d.ts.map +1 -1
  36. package/dist/api/users/index.js +167 -34
  37. package/dist/api/users/index.js.map +1 -1
  38. package/dist/api/verifications/index.d.ts +13 -13
  39. package/dist/api/workflows/index.browser.js +246 -0
  40. package/dist/api/workflows/index.browser.js.map +1 -0
  41. package/dist/api/workflows/index.d.ts +1618 -0
  42. package/dist/api/workflows/index.d.ts.map +1 -0
  43. package/dist/api/workflows/index.js +1504 -0
  44. package/dist/api/workflows/index.js.map +1 -0
  45. package/dist/cli/core/index.d.ts +44 -28
  46. package/dist/cli/core/index.d.ts.map +1 -1
  47. package/dist/cli/core/index.js +16 -61
  48. package/dist/cli/core/index.js.map +1 -1
  49. package/dist/cli/vendor/index.d.ts +31 -8
  50. package/dist/cli/vendor/index.d.ts.map +1 -1
  51. package/dist/cli/vendor/index.js +79 -24
  52. package/dist/cli/vendor/index.js.map +1 -1
  53. package/dist/core/index.browser.js +21 -2
  54. package/dist/core/index.browser.js.map +1 -1
  55. package/dist/core/index.d.ts +33 -2
  56. package/dist/core/index.d.ts.map +1 -1
  57. package/dist/core/index.js +21 -2
  58. package/dist/core/index.js.map +1 -1
  59. package/dist/core/index.native.js +21 -2
  60. package/dist/core/index.native.js.map +1 -1
  61. package/dist/core/index.workerd.js +21 -2
  62. package/dist/core/index.workerd.js.map +1 -1
  63. package/dist/email/smtp/index.js +24 -8
  64. package/dist/email/smtp/index.js.map +1 -1
  65. package/dist/orm/core/index.browser.js +0 -18
  66. package/dist/orm/core/index.browser.js.map +1 -1
  67. package/dist/orm/core/index.bun.js +0 -17
  68. package/dist/orm/core/index.bun.js.map +1 -1
  69. package/dist/orm/core/index.d.ts +1 -13
  70. package/dist/orm/core/index.d.ts.map +1 -1
  71. package/dist/orm/core/index.js +0 -17
  72. package/dist/orm/core/index.js.map +1 -1
  73. package/dist/orm/postgres/index.bun.js +3 -3
  74. package/dist/orm/postgres/index.bun.js.map +1 -1
  75. package/dist/orm/postgres/index.d.ts.map +1 -1
  76. package/dist/orm/postgres/index.js +3 -3
  77. package/dist/orm/postgres/index.js.map +1 -1
  78. package/dist/react/router/index.browser.js +25 -3
  79. package/dist/react/router/index.browser.js.map +1 -1
  80. package/dist/react/router/index.d.ts +16 -1
  81. package/dist/react/router/index.d.ts.map +1 -1
  82. package/dist/react/router/index.js +25 -3
  83. package/dist/react/router/index.js.map +1 -1
  84. package/dist/security/index.d.ts +28 -0
  85. package/dist/security/index.d.ts.map +1 -1
  86. package/dist/security/index.js +28 -0
  87. package/dist/security/index.js.map +1 -1
  88. package/package.json +37 -20
  89. package/src/api/invitations/__tests__/InvitationService.spec.ts +439 -0
  90. package/src/api/invitations/controllers/AdminInvitationController.ts +86 -0
  91. package/src/api/invitations/controllers/InvitationController.ts +84 -0
  92. package/src/api/invitations/entities/invitations.ts +33 -0
  93. package/src/api/invitations/index.ts +65 -0
  94. package/src/api/invitations/jobs/InvitationJobs.ts +37 -0
  95. package/src/api/invitations/providers/InvitationProvider.ts +45 -0
  96. package/src/api/invitations/schemas/createInvitationSchema.ts +12 -0
  97. package/src/api/invitations/schemas/invitationConfigAtom.ts +20 -0
  98. package/src/api/invitations/schemas/invitationQuerySchema.ts +15 -0
  99. package/src/api/invitations/schemas/invitationResourceSchema.ts +6 -0
  100. package/src/api/invitations/schemas/invitationWithResourceInfoSchema.ts +22 -0
  101. package/src/api/invitations/schemas/myInvitationsQuerySchema.ts +10 -0
  102. package/src/api/invitations/services/InvitationService.ts +556 -0
  103. package/src/api/jobs/__tests__/$job.spec.ts +876 -0
  104. package/src/api/jobs/controllers/AdminJobController.ts +44 -0
  105. package/src/api/jobs/entities/jobExecutionEntity.ts +0 -2
  106. package/src/api/jobs/index.ts +0 -3
  107. package/src/api/jobs/primitives/$job.ts +22 -11
  108. package/src/api/jobs/providers/JobProvider.ts +229 -19
  109. package/src/api/jobs/schemas/jobConfigAtom.ts +4 -0
  110. package/src/api/jobs/schemas/jobCronInfoSchema.ts +1 -0
  111. package/src/api/jobs/schemas/jobExecutionQuerySchema.ts +0 -1
  112. package/src/api/jobs/schemas/jobQueueDepthSchema.ts +1 -0
  113. package/src/api/jobs/schemas/jobRegistrationSchema.ts +1 -6
  114. package/src/api/jobs/services/JobService.ts +51 -12
  115. package/src/api/notifications/schemas/notificationQuerySchema.ts +0 -1
  116. package/src/api/parameters/__tests__/$parameter.spec.ts +327 -0
  117. package/src/api/parameters/controllers/AdminParameterController.ts +29 -3
  118. package/src/api/parameters/index.browser.ts +12 -0
  119. package/src/api/parameters/primitives/$parameter.ts +20 -3
  120. package/src/api/parameters/services/ParameterProvider.ts +48 -7
  121. package/src/{billing → api/payments}/__tests__/PaymentMethodService.spec.ts +32 -6
  122. package/src/api/payments/__tests__/PaymentService.spec.ts +279 -0
  123. package/src/{billing/controllers/AdminBillingController.ts → api/payments/controllers/AdminPaymentController.ts} +26 -21
  124. package/src/{billing/controllers/BillingController.ts → api/payments/controllers/PaymentController.ts} +23 -11
  125. package/src/{billing → api/payments}/entities/paymentIntents.ts +1 -0
  126. package/src/{billing/errors/BillingError.ts → api/payments/errors/PaymentError.ts} +1 -1
  127. package/src/{billing → api/payments}/index.ts +31 -25
  128. package/src/{billing/providers/MemoryBillingProvider.ts → api/payments/providers/MemoryPaymentProvider.ts} +4 -4
  129. package/src/{billing/providers/BillingProvider.ts → api/payments/providers/PaymentProvider.ts} +9 -2
  130. package/src/{billing → api/payments}/services/PaymentMethodService.ts +5 -5
  131. package/src/{billing/services/BillingService.ts → api/payments/services/PaymentService.ts} +94 -18
  132. package/src/api/subscriptions/__tests__/BillingService.spec.ts +218 -0
  133. package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +278 -0
  134. package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +212 -0
  135. package/src/api/subscriptions/controllers/SubscriptionController.ts +189 -0
  136. package/src/api/subscriptions/entities/subscriptionEvents.ts +54 -0
  137. package/src/api/subscriptions/entities/subscriptions.ts +68 -0
  138. package/src/api/subscriptions/index.ts +144 -0
  139. package/src/api/subscriptions/jobs/SubscriptionJobs.ts +382 -0
  140. package/src/api/subscriptions/middleware/$requireLimit.ts +50 -0
  141. package/src/api/subscriptions/middleware/$requirePlan.ts +49 -0
  142. package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +110 -0
  143. package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +8 -0
  144. package/src/api/subscriptions/schemas/changePlanSchema.ts +9 -0
  145. package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +11 -0
  146. package/src/api/subscriptions/schemas/entitlementsSchema.ts +21 -0
  147. package/src/api/subscriptions/schemas/mrrSchema.ts +13 -0
  148. package/src/api/subscriptions/schemas/planDefinitionSchema.ts +71 -0
  149. package/src/api/subscriptions/schemas/planResourceSchema.ts +25 -0
  150. package/src/api/subscriptions/schemas/subscriptionEventResourceSchema.ts +8 -0
  151. package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +19 -0
  152. package/src/api/subscriptions/schemas/subscriptionResourceSchema.ts +6 -0
  153. package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +32 -0
  154. package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +23 -0
  155. package/src/api/subscriptions/services/BillingService.ts +437 -0
  156. package/src/api/subscriptions/services/SubscriptionConfig.ts +56 -0
  157. package/src/api/subscriptions/services/SubscriptionService.ts +867 -0
  158. package/src/api/subscriptions/services/UsageService.ts +118 -0
  159. package/src/api/users/__tests__/AdminUserController.spec.ts +80 -1
  160. package/src/api/users/__tests__/CredentialService.spec.ts +177 -0
  161. package/src/api/users/__tests__/EmailVerification.spec.ts +29 -18
  162. package/src/api/users/__tests__/PasswordReset.spec.ts +3 -0
  163. package/src/api/users/__tests__/RegistrationService.spec.ts +148 -1
  164. package/src/api/users/__tests__/SessionService.spec.ts +142 -1
  165. package/src/api/users/atoms/realmAuthSettingsAtom.ts +10 -1
  166. package/src/api/users/controllers/UserController.ts +3 -8
  167. package/src/api/users/notifications/UserNotifications.ts +23 -0
  168. package/src/api/users/schemas/loginSchema.ts +1 -1
  169. package/src/api/users/services/CredentialService.ts +51 -4
  170. package/src/api/users/services/RegistrationService.ts +38 -9
  171. package/src/api/users/services/SessionService.ts +62 -9
  172. package/src/api/users/services/UserService.ts +21 -12
  173. package/src/api/workflows/__tests__/$workflow.spec.ts +616 -0
  174. package/src/api/workflows/controllers/AdminWorkflowController.ts +191 -0
  175. package/src/api/workflows/entities/workflowExecutions.ts +74 -0
  176. package/src/api/workflows/entities/workflowStepExecutions.ts +74 -0
  177. package/src/api/workflows/entities/workflowStepLogs.ts +13 -0
  178. package/src/api/workflows/index.browser.ts +22 -0
  179. package/src/api/workflows/index.ts +124 -0
  180. package/src/api/workflows/jobs/WorkflowJobs.ts +77 -0
  181. package/src/api/workflows/primitives/$workflow.ts +202 -0
  182. package/src/api/workflows/providers/WorkflowProvider.ts +1284 -0
  183. package/src/api/workflows/schemas/workflowActivitySchema.ts +15 -0
  184. package/src/api/workflows/schemas/workflowConfigAtom.ts +51 -0
  185. package/src/api/workflows/schemas/workflowExecutionDetailSchema.ts +18 -0
  186. package/src/api/workflows/schemas/workflowExecutionQuerySchema.ts +26 -0
  187. package/src/api/workflows/schemas/workflowExecutionResourceSchema.ts +30 -0
  188. package/src/api/workflows/schemas/workflowRegistrationSchema.ts +26 -0
  189. package/src/api/workflows/schemas/workflowStatsSchema.ts +16 -0
  190. package/src/api/workflows/schemas/workflowStepExecutionResourceSchema.ts +15 -0
  191. package/src/api/workflows/services/WorkflowService.ts +382 -0
  192. package/src/cli/core/templates/webAppRouterTs.ts +5 -58
  193. package/src/cli/vendor/__tests__/VendorService.spec.ts +283 -178
  194. package/src/cli/vendor/services/VendorService.ts +126 -27
  195. package/src/core/__tests__/TypeProvider.spec.ts +4 -2
  196. package/src/core/providers/SchemaValidator.ts +1 -1
  197. package/src/core/providers/TypeProvider.ts +46 -3
  198. package/src/orm/__tests__/enums.spec.ts +22 -29
  199. package/src/orm/__tests__/orm-showcase-tests.ts +430 -0
  200. package/src/orm/__tests__/orm-showcase.spec.ts +167 -0
  201. package/src/orm/core/providers/DatabaseTypeProvider.ts +0 -29
  202. package/src/orm/postgres/services/PostgresModelBuilder.ts +3 -6
  203. package/src/react/router/__tests__/$page.browser.spec.tsx +157 -0
  204. package/src/react/router/providers/ReactBrowserProvider.ts +39 -0
  205. package/src/react/router/providers/ReactBrowserRouterProvider.ts +22 -0
  206. package/src/security/__tests__/$secure-combinations.spec.ts +945 -0
  207. package/src/security/primitives/$secure.ts +28 -0
  208. package/dist/billing/index.d.ts.map +0 -1
  209. package/dist/billing/index.js.map +0 -1
  210. package/src/billing/__tests__/BillingService.spec.ts +0 -136
  211. /package/src/{billing → api/payments}/entities/paymentMethods.ts +0 -0
  212. /package/src/{billing → api/payments}/entities/refunds.ts +0 -0
  213. /package/src/{billing → api/payments}/schemas/intentSchemas.ts +0 -0
  214. /package/src/{billing → api/payments}/schemas/paymentMethodSchemas.ts +0 -0
  215. /package/src/{billing → api/payments}/schemas/refundSchemas.ts +0 -0
@@ -0,0 +1,84 @@
1
+ import { $inject, t } from "alepha";
2
+ import { $secure } from "alepha/security";
3
+ import { $action, okSchema } from "alepha/server";
4
+ import { createInvitationSchema } from "../schemas/createInvitationSchema.ts";
5
+ import { invitationResourceSchema } from "../schemas/invitationResourceSchema.ts";
6
+ import { invitationWithResourceInfoSchema } from "../schemas/invitationWithResourceInfoSchema.ts";
7
+ import { myInvitationsQuerySchema } from "../schemas/myInvitationsQuerySchema.ts";
8
+ import { InvitationService } from "../services/InvitationService.ts";
9
+
10
+ export class InvitationController {
11
+ protected readonly url = "/invitations";
12
+ protected readonly group = "invitations";
13
+ protected readonly invitationService = $inject(InvitationService);
14
+
15
+ /**
16
+ * Create a new invitation.
17
+ */
18
+ public readonly createInvitation = $action({
19
+ method: "POST",
20
+ path: this.url,
21
+ group: this.group,
22
+ use: [$secure({ permissions: ["invitation:create"] })],
23
+ description: "Create a new invitation",
24
+ schema: {
25
+ body: createInvitationSchema,
26
+ response: invitationResourceSchema,
27
+ },
28
+ handler: ({ body, user }) => this.invitationService.create(body, user),
29
+ });
30
+
31
+ /**
32
+ * List invitations for the current user.
33
+ */
34
+ public readonly getMyInvitations = $action({
35
+ path: `${this.url}/mine`,
36
+ group: this.group,
37
+ use: [$secure()],
38
+ description: "List invitations for the current user",
39
+ schema: {
40
+ query: myInvitationsQuerySchema,
41
+ response: t.array(invitationWithResourceInfoSchema),
42
+ },
43
+ handler: ({ query, user }) =>
44
+ this.invitationService.findByEmail(user.email!, query),
45
+ });
46
+
47
+ /**
48
+ * Accept an invitation.
49
+ */
50
+ public readonly acceptInvitation = $action({
51
+ method: "POST",
52
+ path: `${this.url}/:id/accept`,
53
+ group: this.group,
54
+ use: [$secure()],
55
+ description: "Accept an invitation",
56
+ schema: {
57
+ params: t.object({ id: t.uuid() }),
58
+ response: okSchema,
59
+ },
60
+ handler: async ({ params, user }) => {
61
+ await this.invitationService.accept(params.id, user);
62
+ return { ok: true };
63
+ },
64
+ });
65
+
66
+ /**
67
+ * Decline an invitation.
68
+ */
69
+ public readonly declineInvitation = $action({
70
+ method: "POST",
71
+ path: `${this.url}/:id/decline`,
72
+ group: this.group,
73
+ use: [$secure()],
74
+ description: "Decline an invitation",
75
+ schema: {
76
+ params: t.object({ id: t.uuid() }),
77
+ response: okSchema,
78
+ },
79
+ handler: async ({ params, user }) => {
80
+ await this.invitationService.decline(params.id, user);
81
+ return { ok: true };
82
+ },
83
+ });
84
+ }
@@ -0,0 +1,33 @@
1
+ import { type Static, t } from "alepha";
2
+ import { users } from "alepha/api/users";
3
+ import { $entity, db } from "alepha/orm";
4
+
5
+ export const invitations = $entity({
6
+ name: "invitations",
7
+ schema: t.object({
8
+ id: db.primaryKey(t.uuid()),
9
+ version: db.version(),
10
+ createdAt: db.createdAt(),
11
+ updatedAt: db.updatedAt(),
12
+ invitedBy: db.ref(t.uuid(), () => users.cols.id, { onDelete: "cascade" }),
13
+ email: t.string({ format: "email" }),
14
+ resourceType: t.text({ minLength: 1, maxLength: 100 }),
15
+ resourceId: t.text({ minLength: 1, maxLength: 255 }),
16
+ status: t.enum(["pending", "accepted", "declined", "expired", "revoked"]),
17
+ roles: t.optional(t.array(t.text())),
18
+ metadata: t.optional(t.record(t.text(), t.any())),
19
+ token: t.text(),
20
+ expiresAt: t.datetime(),
21
+ resolvedAt: t.optional(t.datetime()),
22
+ resolvedBy: t.optional(db.ref(t.uuid(), () => users.cols.id)),
23
+ }),
24
+ indexes: [
25
+ { columns: ["email", "status"] },
26
+ { columns: ["resourceType", "resourceId", "email", "status"] },
27
+ { columns: ["invitedBy"] },
28
+ { columns: ["expiresAt"] },
29
+ { columns: ["token"], unique: true },
30
+ ],
31
+ });
32
+
33
+ export type InvitationEntity = Static<typeof invitations.schema>;
@@ -0,0 +1,65 @@
1
+ import { $module } from "alepha";
2
+ import { AdminInvitationController } from "./controllers/AdminInvitationController.ts";
3
+ import { InvitationController } from "./controllers/InvitationController.ts";
4
+ import { InvitationJobs } from "./jobs/InvitationJobs.ts";
5
+ import { InvitationService } from "./services/InvitationService.ts";
6
+
7
+ export * from "./controllers/AdminInvitationController.ts";
8
+ export * from "./controllers/InvitationController.ts";
9
+ export * from "./entities/invitations.ts";
10
+ export * from "./jobs/InvitationJobs.ts";
11
+ export * from "./providers/InvitationProvider.ts";
12
+ export * from "./schemas/createInvitationSchema.ts";
13
+ export * from "./schemas/invitationConfigAtom.ts";
14
+ export * from "./schemas/invitationQuerySchema.ts";
15
+ export * from "./schemas/invitationResourceSchema.ts";
16
+ export * from "./schemas/invitationWithResourceInfoSchema.ts";
17
+ export * from "./schemas/myInvitationsQuerySchema.ts";
18
+ export * from "./services/InvitationService.ts";
19
+
20
+ declare module "alepha" {
21
+ interface Hooks {
22
+ "invitation:created": {
23
+ invitation: import("./entities/invitations.ts").InvitationEntity;
24
+ token: string;
25
+ inviter: { id: string; email?: string };
26
+ };
27
+ "invitation:accepted": {
28
+ invitation: import("./entities/invitations.ts").InvitationEntity;
29
+ acceptedBy: { id: string; email?: string };
30
+ };
31
+ "invitation:declined": {
32
+ invitation: import("./entities/invitations.ts").InvitationEntity;
33
+ declinedBy: { id: string; email?: string };
34
+ };
35
+ "invitation:expired": {
36
+ invitation: import("./entities/invitations.ts").InvitationEntity;
37
+ };
38
+ "invitation:revoked": {
39
+ invitation: import("./entities/invitations.ts").InvitationEntity;
40
+ revokedBy: { id: string };
41
+ };
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Invitation management module — create, accept, decline, revoke, and expire invitations.
47
+ *
48
+ * @module alepha.api.invitations
49
+ */
50
+ export const AlephaApiInvitations = $module({
51
+ name: "alepha.api.invitations",
52
+ services: [
53
+ InvitationService,
54
+ InvitationJobs,
55
+ InvitationController,
56
+ AdminInvitationController,
57
+ ],
58
+ register: (alepha) => {
59
+ alepha
60
+ .with(InvitationService)
61
+ .with(InvitationJobs)
62
+ .with(InvitationController)
63
+ .with(AdminInvitationController);
64
+ },
65
+ });
@@ -0,0 +1,37 @@
1
+ import { $inject } from "alepha";
2
+ import { $job } from "alepha/api/jobs";
3
+ import { $logger } from "alepha/logger";
4
+ import { InvitationService } from "../services/InvitationService.ts";
5
+
6
+ export class InvitationJobs {
7
+ protected readonly log = $logger();
8
+ protected readonly invitationService = $inject(InvitationService);
9
+
10
+ /**
11
+ * Expire pending invitations that have passed their expiration date.
12
+ */
13
+ public readonly expireInvitations = $job({
14
+ cron: "0 * * * *",
15
+ lock: true,
16
+ handler: async () => {
17
+ const count = await this.invitationService.expirePending();
18
+ if (count > 0) {
19
+ this.log.info(`Expired ${count} invitations`);
20
+ }
21
+ },
22
+ });
23
+
24
+ /**
25
+ * Purge old resolved invitations.
26
+ */
27
+ public readonly purgeInvitations = $job({
28
+ cron: "0 3 * * *",
29
+ lock: true,
30
+ handler: async () => {
31
+ const count = await this.invitationService.purgeResolved();
32
+ if (count > 0) {
33
+ this.log.info(`Purged ${count} old invitations`);
34
+ }
35
+ },
36
+ });
37
+ }
@@ -0,0 +1,45 @@
1
+ import type { InvitationEntity } from "../entities/invitations.ts";
2
+
3
+ /**
4
+ * Abstract provider that apps implement to customize invitation behavior
5
+ * per resource type.
6
+ */
7
+ export abstract class InvitationProvider {
8
+ /**
9
+ * Validate that the resource exists and the inviter has permission to invite.
10
+ * Throw BadRequestError/ForbiddenError to reject.
11
+ */
12
+ abstract validateResource(
13
+ resourceType: string,
14
+ resourceId: string,
15
+ inviter: { id: string; email?: string },
16
+ ): Promise<void>;
17
+
18
+ /**
19
+ * Check if the email is already a member of the resource.
20
+ * Return true to reject the invitation as duplicate membership.
21
+ */
22
+ abstract isMember(
23
+ resourceType: string,
24
+ resourceId: string,
25
+ email: string,
26
+ userId?: string,
27
+ ): Promise<boolean>;
28
+
29
+ /**
30
+ * Called when an invitation is accepted.
31
+ * Create membership records, assign roles, etc.
32
+ */
33
+ abstract onAccept(
34
+ invitation: InvitationEntity,
35
+ acceptedBy: { id: string; email?: string },
36
+ ): Promise<void>;
37
+
38
+ /**
39
+ * Return display info for the resource (used in API responses).
40
+ */
41
+ abstract getResourceInfo(
42
+ resourceType: string,
43
+ resourceId: string,
44
+ ): Promise<{ name: string; description?: string; url?: string }>;
45
+ }
@@ -0,0 +1,12 @@
1
+ import type { Static } from "alepha";
2
+ import { t } from "alepha";
3
+
4
+ export const createInvitationSchema = t.object({
5
+ email: t.string({ format: "email" }),
6
+ resourceType: t.text({ minLength: 1, maxLength: 100 }),
7
+ resourceId: t.text({ minLength: 1, maxLength: 255 }),
8
+ roles: t.optional(t.array(t.text())),
9
+ metadata: t.optional(t.record(t.text(), t.any())),
10
+ });
11
+
12
+ export type CreateInvitation = Static<typeof createInvitationSchema>;
@@ -0,0 +1,20 @@
1
+ import { $atom, t } from "alepha";
2
+
3
+ export const invitationConfigAtom = $atom({
4
+ name: "alepha.api.invitations.config",
5
+ schema: t.object({
6
+ expirationDays: t.integer({ minimum: 1, maximum: 90 }),
7
+ maxPendingPerResource: t.integer({ minimum: 1, maximum: 500 }),
8
+ maxPendingPerInviter: t.integer({
9
+ minimum: 1,
10
+ maximum: 1000,
11
+ }),
12
+ purgeDays: t.integer({ minimum: 0, maximum: 365 }),
13
+ }),
14
+ default: {
15
+ expirationDays: 7,
16
+ maxPendingPerResource: 50,
17
+ maxPendingPerInviter: 100,
18
+ purgeDays: 90,
19
+ },
20
+ });
@@ -0,0 +1,15 @@
1
+ import type { Static } from "alepha";
2
+ import { t } from "alepha";
3
+ import { pageQuerySchema } from "alepha/orm";
4
+
5
+ export const invitationQuerySchema = t.extend(pageQuerySchema, {
6
+ email: t.optional(t.text({ description: "Filter by invited email" })),
7
+ resourceType: t.optional(t.text({ description: "Filter by resource type" })),
8
+ resourceId: t.optional(t.text({ description: "Filter by resource ID" })),
9
+ status: t.optional(
10
+ t.enum(["pending", "accepted", "declined", "expired", "revoked"]),
11
+ ),
12
+ invitedBy: t.optional(t.uuid()),
13
+ });
14
+
15
+ export type InvitationQuery = Static<typeof invitationQuerySchema>;
@@ -0,0 +1,6 @@
1
+ import type { Static } from "alepha";
2
+ import { invitations } from "../entities/invitations.ts";
3
+
4
+ export const invitationResourceSchema = invitations.schema;
5
+
6
+ export type InvitationResource = Static<typeof invitationResourceSchema>;
@@ -0,0 +1,22 @@
1
+ import type { Static } from "alepha";
2
+ import { t } from "alepha";
3
+
4
+ export const invitationWithResourceInfoSchema = t.object({
5
+ id: t.uuid(),
6
+ email: t.string({ format: "email" }),
7
+ resourceType: t.text(),
8
+ resourceId: t.text(),
9
+ resourceName: t.text(),
10
+ resourceUrl: t.optional(t.text()),
11
+ invitedBy: t.uuid(),
12
+ inviterName: t.optional(t.text()),
13
+ inviterEmail: t.optional(t.string({ format: "email" })),
14
+ roles: t.optional(t.array(t.text())),
15
+ status: t.enum(["pending", "accepted", "declined", "expired", "revoked"]),
16
+ createdAt: t.datetime(),
17
+ expiresAt: t.datetime(),
18
+ });
19
+
20
+ export type InvitationWithResourceInfo = Static<
21
+ typeof invitationWithResourceInfoSchema
22
+ >;
@@ -0,0 +1,10 @@
1
+ import type { Static } from "alepha";
2
+ import { t } from "alepha";
3
+
4
+ export const myInvitationsQuerySchema = t.object({
5
+ status: t.optional(
6
+ t.enum(["pending", "accepted", "declined", "expired", "revoked"]),
7
+ ),
8
+ });
9
+
10
+ export type MyInvitationsQuery = Static<typeof myInvitationsQuerySchema>;