alepha 0.19.2 → 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 (241) 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 +90 -34
  10. package/dist/api/jobs/index.d.ts.map +1 -1
  11. package/dist/api/jobs/index.js +267 -44
  12. package/dist/api/jobs/index.js.map +1 -1
  13. package/dist/api/notifications/index.browser.js +0 -1
  14. package/dist/api/notifications/index.browser.js.map +1 -1
  15. package/dist/api/notifications/index.d.ts +3 -3
  16. package/dist/api/notifications/index.d.ts.map +1 -1
  17. package/dist/api/notifications/index.js +0 -1
  18. package/dist/api/notifications/index.js.map +1 -1
  19. package/dist/api/parameters/index.browser.js +112 -1
  20. package/dist/api/parameters/index.browser.js.map +1 -1
  21. package/dist/api/parameters/index.d.ts +90 -3
  22. package/dist/api/parameters/index.d.ts.map +1 -1
  23. package/dist/api/parameters/index.js +79 -12
  24. package/dist/api/parameters/index.js.map +1 -1
  25. package/dist/{billing → api/payments}/index.d.ts +67 -49
  26. package/dist/api/payments/index.d.ts.map +1 -0
  27. package/dist/{billing → api/payments}/index.js +108 -74
  28. package/dist/api/payments/index.js.map +1 -0
  29. package/dist/api/subscriptions/index.d.ts +1692 -0
  30. package/dist/api/subscriptions/index.d.ts.map +1 -0
  31. package/dist/api/subscriptions/index.js +1870 -0
  32. package/dist/api/subscriptions/index.js.map +1 -0
  33. package/dist/api/users/index.d.ts +27 -21
  34. package/dist/api/users/index.d.ts.map +1 -1
  35. package/dist/api/users/index.js +167 -34
  36. package/dist/api/users/index.js.map +1 -1
  37. package/dist/api/workflows/index.browser.js +246 -0
  38. package/dist/api/workflows/index.browser.js.map +1 -0
  39. package/dist/api/workflows/index.d.ts +1618 -0
  40. package/dist/api/workflows/index.d.ts.map +1 -0
  41. package/dist/api/workflows/index.js +1504 -0
  42. package/dist/api/workflows/index.js.map +1 -0
  43. package/dist/cli/config/index.d.ts +6 -28
  44. package/dist/cli/config/index.d.ts.map +1 -1
  45. package/dist/cli/config/index.js +5 -10
  46. package/dist/cli/config/index.js.map +1 -1
  47. package/dist/cli/core/index.d.ts +11669 -208
  48. package/dist/cli/core/index.d.ts.map +1 -1
  49. package/dist/cli/core/index.js +60 -69
  50. package/dist/cli/core/index.js.map +1 -1
  51. package/dist/cli/devtools/index.d.ts +5 -0
  52. package/dist/cli/devtools/index.d.ts.map +1 -1
  53. package/dist/cli/devtools/index.js +4 -0
  54. package/dist/cli/devtools/index.js.map +1 -1
  55. package/dist/cli/platform/index.d.ts +69 -64
  56. package/dist/cli/platform/index.d.ts.map +1 -1
  57. package/dist/cli/platform/index.js +6 -2
  58. package/dist/cli/platform/index.js.map +1 -1
  59. package/dist/cli/vendor/index.d.ts +38 -10
  60. package/dist/cli/vendor/index.d.ts.map +1 -1
  61. package/dist/cli/vendor/index.js +85 -26
  62. package/dist/cli/vendor/index.js.map +1 -1
  63. package/dist/core/index.browser.js +21 -2
  64. package/dist/core/index.browser.js.map +1 -1
  65. package/dist/core/index.d.ts +33 -2
  66. package/dist/core/index.d.ts.map +1 -1
  67. package/dist/core/index.js +25 -2
  68. package/dist/core/index.js.map +1 -1
  69. package/dist/core/index.native.js +25 -2
  70. package/dist/core/index.native.js.map +1 -1
  71. package/dist/core/index.workerd.js +25 -2
  72. package/dist/core/index.workerd.js.map +1 -1
  73. package/dist/email/smtp/index.js +24 -8
  74. package/dist/email/smtp/index.js.map +1 -1
  75. package/dist/logger/index.d.ts.map +1 -1
  76. package/dist/logger/index.js +1 -1
  77. package/dist/logger/index.js.map +1 -1
  78. package/dist/orm/core/index.browser.js +0 -18
  79. package/dist/orm/core/index.browser.js.map +1 -1
  80. package/dist/orm/core/index.bun.js +25 -73
  81. package/dist/orm/core/index.bun.js.map +1 -1
  82. package/dist/orm/core/index.d.ts +10 -32
  83. package/dist/orm/core/index.d.ts.map +1 -1
  84. package/dist/orm/core/index.js +25 -73
  85. package/dist/orm/core/index.js.map +1 -1
  86. package/dist/orm/postgres/index.bun.js +3 -3
  87. package/dist/orm/postgres/index.bun.js.map +1 -1
  88. package/dist/orm/postgres/index.d.ts +2 -1
  89. package/dist/orm/postgres/index.d.ts.map +1 -1
  90. package/dist/orm/postgres/index.js +3 -3
  91. package/dist/orm/postgres/index.js.map +1 -1
  92. package/dist/react/router/index.browser.js +25 -3
  93. package/dist/react/router/index.browser.js.map +1 -1
  94. package/dist/react/router/index.d.ts +16 -1
  95. package/dist/react/router/index.d.ts.map +1 -1
  96. package/dist/react/router/index.js +25 -3
  97. package/dist/react/router/index.js.map +1 -1
  98. package/dist/security/index.d.ts +28 -0
  99. package/dist/security/index.d.ts.map +1 -1
  100. package/dist/security/index.js +28 -0
  101. package/dist/security/index.js.map +1 -1
  102. package/package.json +37 -20
  103. package/src/api/invitations/__tests__/InvitationService.spec.ts +439 -0
  104. package/src/api/invitations/controllers/AdminInvitationController.ts +86 -0
  105. package/src/api/invitations/controllers/InvitationController.ts +84 -0
  106. package/src/api/invitations/entities/invitations.ts +33 -0
  107. package/src/api/invitations/index.ts +65 -0
  108. package/src/api/invitations/jobs/InvitationJobs.ts +37 -0
  109. package/src/api/invitations/providers/InvitationProvider.ts +45 -0
  110. package/src/api/invitations/schemas/createInvitationSchema.ts +12 -0
  111. package/src/api/invitations/schemas/invitationConfigAtom.ts +20 -0
  112. package/src/api/invitations/schemas/invitationQuerySchema.ts +15 -0
  113. package/src/api/invitations/schemas/invitationResourceSchema.ts +6 -0
  114. package/src/api/invitations/schemas/invitationWithResourceInfoSchema.ts +22 -0
  115. package/src/api/invitations/schemas/myInvitationsQuerySchema.ts +10 -0
  116. package/src/api/invitations/services/InvitationService.ts +556 -0
  117. package/src/api/jobs/__tests__/$job.spec.ts +876 -0
  118. package/src/api/jobs/controllers/AdminJobController.ts +44 -0
  119. package/src/api/jobs/entities/jobExecutionEntity.ts +0 -2
  120. package/src/api/jobs/index.ts +0 -3
  121. package/src/api/jobs/primitives/$job.ts +22 -11
  122. package/src/api/jobs/providers/JobProvider.ts +239 -25
  123. package/src/api/jobs/schemas/jobConfigAtom.ts +4 -0
  124. package/src/api/jobs/schemas/jobCronInfoSchema.ts +1 -0
  125. package/src/api/jobs/schemas/jobExecutionQuerySchema.ts +0 -1
  126. package/src/api/jobs/schemas/jobQueueDepthSchema.ts +1 -0
  127. package/src/api/jobs/schemas/jobRegistrationSchema.ts +1 -6
  128. package/src/api/jobs/services/JobService.ts +51 -12
  129. package/src/api/notifications/schemas/notificationQuerySchema.ts +0 -1
  130. package/src/api/parameters/__tests__/$parameter.spec.ts +327 -0
  131. package/src/api/parameters/controllers/AdminParameterController.ts +29 -3
  132. package/src/api/parameters/index.browser.ts +12 -0
  133. package/src/api/parameters/primitives/$parameter.ts +20 -3
  134. package/src/api/parameters/services/ParameterProvider.ts +48 -7
  135. package/src/{billing → api/payments}/__tests__/PaymentMethodService.spec.ts +32 -6
  136. package/src/api/payments/__tests__/PaymentService.spec.ts +279 -0
  137. package/src/{billing/controllers/AdminBillingController.ts → api/payments/controllers/AdminPaymentController.ts} +26 -21
  138. package/src/{billing/controllers/BillingController.ts → api/payments/controllers/PaymentController.ts} +23 -11
  139. package/src/{billing → api/payments}/entities/paymentIntents.ts +1 -0
  140. package/src/{billing/errors/BillingError.ts → api/payments/errors/PaymentError.ts} +1 -1
  141. package/src/{billing → api/payments}/index.ts +31 -25
  142. package/src/{billing/providers/MemoryBillingProvider.ts → api/payments/providers/MemoryPaymentProvider.ts} +4 -4
  143. package/src/{billing/providers/BillingProvider.ts → api/payments/providers/PaymentProvider.ts} +9 -2
  144. package/src/{billing → api/payments}/services/PaymentMethodService.ts +5 -5
  145. package/src/{billing/services/BillingService.ts → api/payments/services/PaymentService.ts} +94 -18
  146. package/src/api/subscriptions/__tests__/BillingService.spec.ts +218 -0
  147. package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +278 -0
  148. package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +212 -0
  149. package/src/api/subscriptions/controllers/SubscriptionController.ts +189 -0
  150. package/src/api/subscriptions/entities/subscriptionEvents.ts +54 -0
  151. package/src/api/subscriptions/entities/subscriptions.ts +68 -0
  152. package/src/api/subscriptions/index.ts +144 -0
  153. package/src/api/subscriptions/jobs/SubscriptionJobs.ts +382 -0
  154. package/src/api/subscriptions/middleware/$requireLimit.ts +50 -0
  155. package/src/api/subscriptions/middleware/$requirePlan.ts +49 -0
  156. package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +110 -0
  157. package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +8 -0
  158. package/src/api/subscriptions/schemas/changePlanSchema.ts +9 -0
  159. package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +11 -0
  160. package/src/api/subscriptions/schemas/entitlementsSchema.ts +21 -0
  161. package/src/api/subscriptions/schemas/mrrSchema.ts +13 -0
  162. package/src/api/subscriptions/schemas/planDefinitionSchema.ts +71 -0
  163. package/src/api/subscriptions/schemas/planResourceSchema.ts +25 -0
  164. package/src/api/subscriptions/schemas/subscriptionEventResourceSchema.ts +8 -0
  165. package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +19 -0
  166. package/src/api/subscriptions/schemas/subscriptionResourceSchema.ts +6 -0
  167. package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +32 -0
  168. package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +23 -0
  169. package/src/api/subscriptions/services/BillingService.ts +437 -0
  170. package/src/api/subscriptions/services/SubscriptionConfig.ts +56 -0
  171. package/src/api/subscriptions/services/SubscriptionService.ts +867 -0
  172. package/src/api/subscriptions/services/UsageService.ts +118 -0
  173. package/src/api/users/__tests__/AdminUserController.spec.ts +80 -1
  174. package/src/api/users/__tests__/CredentialService.spec.ts +177 -0
  175. package/src/api/users/__tests__/EmailVerification.spec.ts +29 -18
  176. package/src/api/users/__tests__/PasswordReset.spec.ts +3 -0
  177. package/src/api/users/__tests__/RegistrationService.spec.ts +148 -1
  178. package/src/api/users/__tests__/SessionService.spec.ts +142 -1
  179. package/src/api/users/atoms/realmAuthSettingsAtom.ts +10 -1
  180. package/src/api/users/controllers/UserController.ts +3 -8
  181. package/src/api/users/notifications/UserNotifications.ts +23 -0
  182. package/src/api/users/schemas/loginSchema.ts +1 -1
  183. package/src/api/users/services/CredentialService.ts +51 -4
  184. package/src/api/users/services/RegistrationService.ts +38 -9
  185. package/src/api/users/services/SessionService.ts +62 -9
  186. package/src/api/users/services/UserService.ts +21 -12
  187. package/src/api/workflows/__tests__/$workflow.spec.ts +616 -0
  188. package/src/api/workflows/controllers/AdminWorkflowController.ts +191 -0
  189. package/src/api/workflows/entities/workflowExecutions.ts +74 -0
  190. package/src/api/workflows/entities/workflowStepExecutions.ts +74 -0
  191. package/src/api/workflows/entities/workflowStepLogs.ts +13 -0
  192. package/src/api/workflows/index.browser.ts +22 -0
  193. package/src/api/workflows/index.ts +124 -0
  194. package/src/api/workflows/jobs/WorkflowJobs.ts +77 -0
  195. package/src/api/workflows/primitives/$workflow.ts +202 -0
  196. package/src/api/workflows/providers/WorkflowProvider.ts +1284 -0
  197. package/src/api/workflows/schemas/workflowActivitySchema.ts +15 -0
  198. package/src/api/workflows/schemas/workflowConfigAtom.ts +51 -0
  199. package/src/api/workflows/schemas/workflowExecutionDetailSchema.ts +18 -0
  200. package/src/api/workflows/schemas/workflowExecutionQuerySchema.ts +26 -0
  201. package/src/api/workflows/schemas/workflowExecutionResourceSchema.ts +30 -0
  202. package/src/api/workflows/schemas/workflowRegistrationSchema.ts +26 -0
  203. package/src/api/workflows/schemas/workflowStatsSchema.ts +16 -0
  204. package/src/api/workflows/schemas/workflowStepExecutionResourceSchema.ts +15 -0
  205. package/src/api/workflows/services/WorkflowService.ts +382 -0
  206. package/src/cli/config/defineConfig.ts +17 -46
  207. package/src/cli/core/providers/ViteDevServerProvider.ts +45 -3
  208. package/src/cli/core/services/PackageManagerUtils.ts +3 -1
  209. package/src/cli/core/services/ProjectScaffolder.ts +5 -5
  210. package/src/cli/core/templates/agentMd.ts +14 -5
  211. package/src/cli/core/templates/webAppRouterTs.ts +5 -58
  212. package/src/cli/devtools/index.ts +21 -1
  213. package/src/cli/platform/index.ts +23 -2
  214. package/src/cli/vendor/__tests__/VendorService.spec.ts +283 -178
  215. package/src/cli/vendor/index.ts +20 -3
  216. package/src/cli/vendor/services/VendorService.ts +126 -27
  217. package/src/core/Alepha.ts +10 -0
  218. package/src/core/__tests__/TypeProvider.spec.ts +4 -2
  219. package/src/core/providers/SchemaValidator.ts +1 -1
  220. package/src/core/providers/TypeProvider.ts +46 -3
  221. package/src/logger/index.ts +6 -1
  222. package/src/orm/__tests__/enums.spec.ts +22 -29
  223. package/src/orm/__tests__/orm-showcase-tests.ts +430 -0
  224. package/src/orm/__tests__/orm-showcase.spec.ts +167 -0
  225. package/src/orm/core/providers/DatabaseTypeProvider.ts +0 -29
  226. package/src/orm/core/providers/DrizzleKitProvider.ts +56 -105
  227. package/src/orm/postgres/services/PostgresModelBuilder.ts +3 -6
  228. package/src/react/router/__tests__/$page.browser.spec.tsx +157 -0
  229. package/src/react/router/providers/ReactBrowserProvider.ts +39 -0
  230. package/src/react/router/providers/ReactBrowserRouterProvider.ts +22 -0
  231. package/src/security/__tests__/$secure-combinations.spec.ts +945 -0
  232. package/src/security/primitives/$secure.ts +28 -0
  233. package/tsconfig.base.json +0 -1
  234. package/dist/billing/index.d.ts.map +0 -1
  235. package/dist/billing/index.js.map +0 -1
  236. package/src/billing/__tests__/BillingService.spec.ts +0 -136
  237. /package/src/{billing → api/payments}/entities/paymentMethods.ts +0 -0
  238. /package/src/{billing → api/payments}/entities/refunds.ts +0 -0
  239. /package/src/{billing → api/payments}/schemas/intentSchemas.ts +0 -0
  240. /package/src/{billing → api/payments}/schemas/paymentMethodSchemas.ts +0 -0
  241. /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>;