alepha 0.19.3 → 0.19.5

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 (289) hide show
  1. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  2. package/dist/api/invitations/index.d.ts +790 -0
  3. package/dist/api/invitations/index.d.ts.map +1 -0
  4. package/dist/api/invitations/index.js +665 -0
  5. package/dist/api/invitations/index.js.map +1 -0
  6. package/dist/api/issues/index.d.ts +810 -0
  7. package/dist/api/issues/index.d.ts.map +1 -0
  8. package/dist/api/issues/index.js +447 -0
  9. package/dist/api/issues/index.js.map +1 -0
  10. package/dist/api/jobs/index.browser.js +8 -9
  11. package/dist/api/jobs/index.browser.js.map +1 -1
  12. package/dist/api/jobs/index.d.ts +99 -43
  13. package/dist/api/jobs/index.d.ts.map +1 -1
  14. package/dist/api/jobs/index.js +257 -40
  15. package/dist/api/jobs/index.js.map +1 -1
  16. package/dist/api/notifications/index.browser.js +0 -1
  17. package/dist/api/notifications/index.browser.js.map +1 -1
  18. package/dist/api/notifications/index.d.ts +3 -3
  19. package/dist/api/notifications/index.d.ts.map +1 -1
  20. package/dist/api/notifications/index.js +0 -1
  21. package/dist/api/notifications/index.js.map +1 -1
  22. package/dist/api/parameters/index.browser.js +112 -1
  23. package/dist/api/parameters/index.browser.js.map +1 -1
  24. package/dist/api/parameters/index.d.ts +90 -3
  25. package/dist/api/parameters/index.d.ts.map +1 -1
  26. package/dist/api/parameters/index.js +79 -12
  27. package/dist/api/parameters/index.js.map +1 -1
  28. package/dist/{billing → api/payments}/index.d.ts +67 -49
  29. package/dist/api/payments/index.d.ts.map +1 -0
  30. package/dist/{billing → api/payments}/index.js +108 -74
  31. package/dist/api/payments/index.js.map +1 -0
  32. package/dist/api/subscriptions/index.d.ts +1692 -0
  33. package/dist/api/subscriptions/index.d.ts.map +1 -0
  34. package/dist/api/subscriptions/index.js +1870 -0
  35. package/dist/api/subscriptions/index.js.map +1 -0
  36. package/dist/api/users/index.d.ts +24 -2
  37. package/dist/api/users/index.d.ts.map +1 -1
  38. package/dist/api/users/index.js +176 -36
  39. package/dist/api/users/index.js.map +1 -1
  40. package/dist/api/verifications/index.d.ts +13 -13
  41. package/dist/api/workflows/index.browser.js +246 -0
  42. package/dist/api/workflows/index.browser.js.map +1 -0
  43. package/dist/api/workflows/index.d.ts +1618 -0
  44. package/dist/api/workflows/index.d.ts.map +1 -0
  45. package/dist/api/workflows/index.js +1504 -0
  46. package/dist/api/workflows/index.js.map +1 -0
  47. package/dist/captcha/index.d.ts +142 -0
  48. package/dist/captcha/index.d.ts.map +1 -0
  49. package/dist/captcha/index.js +177 -0
  50. package/dist/captcha/index.js.map +1 -0
  51. package/dist/cli/core/index.d.ts +126 -30
  52. package/dist/cli/core/index.d.ts.map +1 -1
  53. package/dist/cli/core/index.js +106 -67
  54. package/dist/cli/core/index.js.map +1 -1
  55. package/dist/cli/platform/index.d.ts +84 -10
  56. package/dist/cli/platform/index.d.ts.map +1 -1
  57. package/dist/cli/platform/index.js +92 -4
  58. package/dist/cli/platform/index.js.map +1 -1
  59. package/dist/cli/vendor/index.d.ts +60 -10
  60. package/dist/cli/vendor/index.d.ts.map +1 -1
  61. package/dist/cli/vendor/index.js +177 -45
  62. package/dist/cli/vendor/index.js.map +1 -1
  63. package/dist/command/index.d.ts.map +1 -1
  64. package/dist/command/index.js +2 -3
  65. package/dist/command/index.js.map +1 -1
  66. package/dist/core/index.browser.js +21 -2
  67. package/dist/core/index.browser.js.map +1 -1
  68. package/dist/core/index.d.ts +33 -2
  69. package/dist/core/index.d.ts.map +1 -1
  70. package/dist/core/index.js +21 -2
  71. package/dist/core/index.js.map +1 -1
  72. package/dist/core/index.native.js +21 -2
  73. package/dist/core/index.native.js.map +1 -1
  74. package/dist/core/index.workerd.js +21 -2
  75. package/dist/core/index.workerd.js.map +1 -1
  76. package/dist/email/smtp/index.js +24 -8
  77. package/dist/email/smtp/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 +6 -23
  81. package/dist/orm/core/index.bun.js.map +1 -1
  82. package/dist/orm/core/index.d.ts +1 -13
  83. package/dist/orm/core/index.d.ts.map +1 -1
  84. package/dist/orm/core/index.js +6 -23
  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.map +1 -1
  89. package/dist/orm/postgres/index.js +3 -3
  90. package/dist/orm/postgres/index.js.map +1 -1
  91. package/dist/react/i18n/index.d.ts +1 -0
  92. package/dist/react/i18n/index.d.ts.map +1 -1
  93. package/dist/react/i18n/index.js +8 -4
  94. package/dist/react/i18n/index.js.map +1 -1
  95. package/dist/react/router/index.browser.js +25 -3
  96. package/dist/react/router/index.browser.js.map +1 -1
  97. package/dist/react/router/index.d.ts +16 -1
  98. package/dist/react/router/index.d.ts.map +1 -1
  99. package/dist/react/router/index.js +25 -3
  100. package/dist/react/router/index.js.map +1 -1
  101. package/dist/security/index.d.ts +28 -0
  102. package/dist/security/index.d.ts.map +1 -1
  103. package/dist/security/index.js +28 -0
  104. package/dist/security/index.js.map +1 -1
  105. package/dist/server/auth/index.d.ts +145 -2
  106. package/dist/server/auth/index.d.ts.map +1 -1
  107. package/dist/server/auth/index.js +364 -63
  108. package/dist/server/auth/index.js.map +1 -1
  109. package/dist/server/cookies/index.d.ts.map +1 -1
  110. package/dist/server/cookies/index.js.map +1 -1
  111. package/dist/websocket/index.d.ts.map +1 -1
  112. package/dist/websocket/index.js.map +1 -1
  113. package/package.json +47 -20
  114. package/src/api/invitations/__tests__/InvitationService.spec.ts +439 -0
  115. package/src/api/invitations/controllers/AdminInvitationController.ts +86 -0
  116. package/src/api/invitations/controllers/InvitationController.ts +84 -0
  117. package/src/api/invitations/entities/invitations.ts +33 -0
  118. package/src/api/invitations/index.ts +65 -0
  119. package/src/api/invitations/jobs/InvitationJobs.ts +37 -0
  120. package/src/api/invitations/providers/InvitationProvider.ts +45 -0
  121. package/src/api/invitations/schemas/createInvitationSchema.ts +12 -0
  122. package/src/api/invitations/schemas/invitationConfigAtom.ts +20 -0
  123. package/src/api/invitations/schemas/invitationQuerySchema.ts +15 -0
  124. package/src/api/invitations/schemas/invitationResourceSchema.ts +6 -0
  125. package/src/api/invitations/schemas/invitationWithResourceInfoSchema.ts +22 -0
  126. package/src/api/invitations/schemas/myInvitationsQuerySchema.ts +10 -0
  127. package/src/api/invitations/services/InvitationService.ts +556 -0
  128. package/src/api/issues/__tests__/IssueService.spec.ts +263 -0
  129. package/src/api/issues/controllers/AdminIssueController.ts +149 -0
  130. package/src/api/issues/controllers/IssueController.ts +44 -0
  131. package/src/api/issues/entities/issues.ts +49 -0
  132. package/src/api/issues/index.ts +53 -0
  133. package/src/api/issues/schemas/createIssueSchema.ts +13 -0
  134. package/src/api/issues/schemas/issueConfigAtom.ts +13 -0
  135. package/src/api/issues/schemas/issueQuerySchema.ts +18 -0
  136. package/src/api/issues/schemas/issueResourceSchema.ts +6 -0
  137. package/src/api/issues/schemas/myIssueQuerySchema.ts +10 -0
  138. package/src/api/issues/schemas/updateIssueSchema.ts +13 -0
  139. package/src/api/issues/services/IssueService.ts +264 -0
  140. package/src/api/jobs/__tests__/$job.spec.ts +876 -0
  141. package/src/api/jobs/controllers/AdminJobController.ts +44 -0
  142. package/src/api/jobs/entities/jobExecutionEntity.ts +0 -2
  143. package/src/api/jobs/index.ts +0 -3
  144. package/src/api/jobs/primitives/$job.ts +22 -11
  145. package/src/api/jobs/providers/JobProvider.ts +229 -19
  146. package/src/api/jobs/schemas/jobConfigAtom.ts +4 -0
  147. package/src/api/jobs/schemas/jobCronInfoSchema.ts +1 -0
  148. package/src/api/jobs/schemas/jobExecutionQuerySchema.ts +0 -1
  149. package/src/api/jobs/schemas/jobQueueDepthSchema.ts +1 -0
  150. package/src/api/jobs/schemas/jobRegistrationSchema.ts +1 -6
  151. package/src/api/jobs/services/JobService.ts +51 -12
  152. package/src/api/notifications/schemas/notificationQuerySchema.ts +0 -1
  153. package/src/api/parameters/__tests__/$parameter.spec.ts +327 -0
  154. package/src/api/parameters/controllers/AdminParameterController.ts +29 -3
  155. package/src/api/parameters/index.browser.ts +12 -0
  156. package/src/api/parameters/primitives/$parameter.ts +20 -3
  157. package/src/api/parameters/services/ParameterProvider.ts +48 -7
  158. package/src/{billing → api/payments}/__tests__/PaymentMethodService.spec.ts +32 -6
  159. package/src/api/payments/__tests__/PaymentService.spec.ts +279 -0
  160. package/src/{billing/controllers/AdminBillingController.ts → api/payments/controllers/AdminPaymentController.ts} +26 -21
  161. package/src/{billing/controllers/BillingController.ts → api/payments/controllers/PaymentController.ts} +23 -11
  162. package/src/{billing → api/payments}/entities/paymentIntents.ts +1 -0
  163. package/src/{billing/errors/BillingError.ts → api/payments/errors/PaymentError.ts} +1 -1
  164. package/src/{billing → api/payments}/index.ts +31 -25
  165. package/src/{billing/providers/MemoryBillingProvider.ts → api/payments/providers/MemoryPaymentProvider.ts} +4 -4
  166. package/src/{billing/providers/BillingProvider.ts → api/payments/providers/PaymentProvider.ts} +9 -2
  167. package/src/{billing → api/payments}/services/PaymentMethodService.ts +5 -5
  168. package/src/{billing/services/BillingService.ts → api/payments/services/PaymentService.ts} +94 -18
  169. package/src/api/subscriptions/__tests__/BillingService.spec.ts +218 -0
  170. package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +278 -0
  171. package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +212 -0
  172. package/src/api/subscriptions/controllers/SubscriptionController.ts +189 -0
  173. package/src/api/subscriptions/entities/subscriptionEvents.ts +54 -0
  174. package/src/api/subscriptions/entities/subscriptions.ts +68 -0
  175. package/src/api/subscriptions/index.ts +144 -0
  176. package/src/api/subscriptions/jobs/SubscriptionJobs.ts +382 -0
  177. package/src/api/subscriptions/middleware/$requireLimit.ts +50 -0
  178. package/src/api/subscriptions/middleware/$requirePlan.ts +49 -0
  179. package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +110 -0
  180. package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +8 -0
  181. package/src/api/subscriptions/schemas/changePlanSchema.ts +9 -0
  182. package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +11 -0
  183. package/src/api/subscriptions/schemas/entitlementsSchema.ts +21 -0
  184. package/src/api/subscriptions/schemas/mrrSchema.ts +13 -0
  185. package/src/api/subscriptions/schemas/planDefinitionSchema.ts +71 -0
  186. package/src/api/subscriptions/schemas/planResourceSchema.ts +25 -0
  187. package/src/api/subscriptions/schemas/subscriptionEventResourceSchema.ts +8 -0
  188. package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +19 -0
  189. package/src/api/subscriptions/schemas/subscriptionResourceSchema.ts +6 -0
  190. package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +32 -0
  191. package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +23 -0
  192. package/src/api/subscriptions/services/BillingService.ts +437 -0
  193. package/src/api/subscriptions/services/SubscriptionConfig.ts +56 -0
  194. package/src/api/subscriptions/services/SubscriptionService.ts +867 -0
  195. package/src/api/subscriptions/services/UsageService.ts +118 -0
  196. package/src/api/users/__tests__/AdminUserController.spec.ts +80 -1
  197. package/src/api/users/__tests__/CredentialService.spec.ts +177 -0
  198. package/src/api/users/__tests__/EmailVerification.spec.ts +29 -18
  199. package/src/api/users/__tests__/PasswordReset.spec.ts +3 -0
  200. package/src/api/users/__tests__/RegistrationService.spec.ts +148 -1
  201. package/src/api/users/__tests__/SessionService.spec.ts +142 -1
  202. package/src/api/users/atoms/realmAuthSettingsAtom.ts +10 -1
  203. package/src/api/users/controllers/UserController.ts +3 -8
  204. package/src/api/users/notifications/UserNotifications.ts +23 -0
  205. package/src/api/users/primitives/$realm.ts +24 -0
  206. package/src/api/users/schemas/loginSchema.ts +1 -1
  207. package/src/api/users/services/CredentialService.ts +57 -7
  208. package/src/api/users/services/RegistrationService.ts +50 -11
  209. package/src/api/users/services/SessionService.ts +64 -9
  210. package/src/api/users/services/UserService.ts +21 -12
  211. package/src/api/workflows/__tests__/$workflow.spec.ts +616 -0
  212. package/src/api/workflows/controllers/AdminWorkflowController.ts +191 -0
  213. package/src/api/workflows/entities/workflowExecutions.ts +74 -0
  214. package/src/api/workflows/entities/workflowStepExecutions.ts +74 -0
  215. package/src/api/workflows/entities/workflowStepLogs.ts +13 -0
  216. package/src/api/workflows/index.browser.ts +22 -0
  217. package/src/api/workflows/index.ts +124 -0
  218. package/src/api/workflows/jobs/WorkflowJobs.ts +77 -0
  219. package/src/api/workflows/primitives/$workflow.ts +202 -0
  220. package/src/api/workflows/providers/WorkflowProvider.ts +1284 -0
  221. package/src/api/workflows/schemas/workflowActivitySchema.ts +15 -0
  222. package/src/api/workflows/schemas/workflowConfigAtom.ts +51 -0
  223. package/src/api/workflows/schemas/workflowExecutionDetailSchema.ts +18 -0
  224. package/src/api/workflows/schemas/workflowExecutionQuerySchema.ts +26 -0
  225. package/src/api/workflows/schemas/workflowExecutionResourceSchema.ts +30 -0
  226. package/src/api/workflows/schemas/workflowRegistrationSchema.ts +26 -0
  227. package/src/api/workflows/schemas/workflowStatsSchema.ts +16 -0
  228. package/src/api/workflows/schemas/workflowStepExecutionResourceSchema.ts +15 -0
  229. package/src/api/workflows/services/WorkflowService.ts +382 -0
  230. package/src/captcha/__tests__/MemoryCaptchaProvider.spec.ts +74 -0
  231. package/src/captcha/index.ts +33 -0
  232. package/src/captcha/providers/CaptchaProvider.ts +17 -0
  233. package/src/captcha/providers/MemoryCaptchaProvider.ts +65 -0
  234. package/src/captcha/providers/TurnstileCaptchaProvider.ts +125 -0
  235. package/src/cli/core/atoms/buildOptions.ts +57 -0
  236. package/src/cli/core/commands/build.ts +2 -0
  237. package/src/cli/core/providers/ViteDevServerProvider.ts +1 -1
  238. package/src/cli/core/services/ViteUtils.ts +5 -2
  239. package/src/cli/core/tasks/BuildClientTask.ts +3 -1
  240. package/src/cli/core/tasks/BuildCloudflareTask.ts +4 -0
  241. package/src/cli/core/tasks/BuildPwaTask.ts +81 -0
  242. package/src/cli/core/templates/webAppRouterTs.ts +5 -58
  243. package/src/cli/platform/adapters/CloudflareAdapter.ts +24 -0
  244. package/src/cli/platform/atoms/platformOptions.ts +19 -3
  245. package/src/cli/platform/hooks/PlatformHook.ts +51 -0
  246. package/src/cli/platform/index.ts +1 -0
  247. package/src/cli/platform/services/CloudflareApi.ts +22 -1
  248. package/src/cli/platform/services/PlatformOrchestrator.ts +67 -2
  249. package/src/cli/vendor/__tests__/VendorService.spec.ts +322 -178
  250. package/src/cli/vendor/commands/VendorCommand.ts +41 -38
  251. package/src/cli/vendor/services/VendorService.ts +234 -31
  252. package/src/command/__tests__/CliProvider.spec.ts +45 -0
  253. package/src/command/providers/CliProvider.ts +3 -4
  254. package/src/core/__tests__/TypeProvider.spec.ts +4 -2
  255. package/src/core/providers/SchemaValidator.ts +1 -1
  256. package/src/core/providers/TypeProvider.ts +46 -3
  257. package/src/orm/__tests__/enums.spec.ts +22 -29
  258. package/src/orm/__tests__/orm-showcase-tests.ts +430 -0
  259. package/src/orm/__tests__/orm-showcase.spec.ts +167 -0
  260. package/src/orm/core/providers/DatabaseTypeProvider.ts +0 -29
  261. package/src/orm/core/services/Repository.ts +20 -6
  262. package/src/orm/postgres/services/PostgresModelBuilder.ts +3 -6
  263. package/src/react/i18n/__tests__/I18nProvider.spec.ts +83 -0
  264. package/src/react/i18n/providers/I18nProvider.ts +12 -10
  265. package/src/react/router/__tests__/$page.browser.spec.tsx +157 -0
  266. package/src/react/router/providers/ReactBrowserProvider.ts +39 -0
  267. package/src/react/router/providers/ReactBrowserRouterProvider.ts +22 -0
  268. package/src/security/__tests__/$secure-combinations.spec.ts +945 -0
  269. package/src/security/primitives/$issuer.ts +3 -1
  270. package/src/security/primitives/$secure.ts +28 -0
  271. package/src/server/auth/index.ts +7 -0
  272. package/src/server/auth/primitives/$auth.ts +37 -3
  273. package/src/server/auth/primitives/$authApple.ts +114 -4
  274. package/src/server/auth/primitives/$authFacebook.ts +98 -0
  275. package/src/server/auth/primitives/$authFranceConnect.ts +105 -0
  276. package/src/server/auth/primitives/$authGithub.ts +22 -16
  277. package/src/server/auth/primitives/$authMicrosoft.ts +88 -0
  278. package/src/server/auth/providers/ServerAuthProvider.ts +197 -72
  279. package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -0
  280. package/src/server/core/__tests__/ServerRouterProvider-errorHandler.spec.ts +1 -1
  281. package/src/websocket/providers/NodeWebSocketServerProvider.ts +3 -1
  282. package/dist/billing/index.d.ts.map +0 -1
  283. package/dist/billing/index.js.map +0 -1
  284. package/src/billing/__tests__/BillingService.spec.ts +0 -136
  285. /package/src/{billing → api/payments}/entities/paymentMethods.ts +0 -0
  286. /package/src/{billing → api/payments}/entities/refunds.ts +0 -0
  287. /package/src/{billing → api/payments}/schemas/intentSchemas.ts +0 -0
  288. /package/src/{billing → api/payments}/schemas/paymentMethodSchemas.ts +0 -0
  289. /package/src/{billing → api/payments}/schemas/refundSchemas.ts +0 -0
@@ -3,33 +3,33 @@ import { describe, expect, it } from "vitest";
3
3
  import { $entity, $repository, DrizzleKitProvider, db } from "../core/index.ts";
4
4
  import { AlephaOrmPostgres } from "../postgres/index.ts";
5
5
 
6
- // Test 1: Basic enum using t.enum (should map to TEXT column)
6
+ // Test 1: t.enum with mode: "text" (should map to TEXT column)
7
7
  const textEnumEntity = $entity({
8
8
  name: "text_enum_test",
9
9
  schema: t.object({
10
10
  id: db.primaryKey(),
11
- status: t.enum(["pending", "active", "archived"]),
12
- role: t.enum(["user", "admin", "moderator"]),
11
+ status: t.enum(["pending", "active", "archived"], { mode: "text" }),
12
+ role: t.enum(["user", "admin", "moderator"], { mode: "text" }),
13
13
  }),
14
14
  });
15
15
 
16
- // Test 2: Basic enum using db.enum (should create real PG ENUM type)
16
+ // Test 2: t.enum without mode (should create real PG ENUM type)
17
17
  const pgEnumEntity = $entity({
18
18
  name: "pg_enum_test",
19
19
  schema: t.object({
20
20
  id: db.primaryKey(),
21
- status: db.enum(["draft", "published", "deleted"]),
22
- priority: db.enum(["low", "medium", "high"]),
21
+ status: t.enum(["draft", "published", "deleted"]),
22
+ priority: t.enum(["low", "medium", "high"]),
23
23
  }),
24
24
  });
25
25
 
26
- // Test 3: Mixed t.enum and db.enum in the same table
26
+ // Test 3: Mixed text and pg enum in the same table
27
27
  const mixedEnumEntity = $entity({
28
28
  name: "mixed_enum_test",
29
29
  schema: t.object({
30
30
  id: db.primaryKey(),
31
- textStatus: t.enum(["open", "closed"]),
32
- pgStatus: db.enum(["new", "in_progress", "done"]),
31
+ textStatus: t.enum(["open", "closed"], { mode: "text" }),
32
+ pgStatus: t.enum(["new", "in_progress", "done"]),
33
33
  }),
34
34
  });
35
35
 
@@ -38,7 +38,7 @@ const sharedEnumEntity1 = $entity({
38
38
  name: "shared_enum_test_1",
39
39
  schema: t.object({
40
40
  id: db.primaryKey(),
41
- status: db.enum(["enabled", "disabled"], { name: "shared_status_enum" }),
41
+ status: t.enum(["enabled", "disabled"], { name: "shared_status_enum" }),
42
42
  }),
43
43
  });
44
44
 
@@ -46,7 +46,7 @@ const sharedEnumEntity2 = $entity({
46
46
  name: "shared_enum_test_2",
47
47
  schema: t.object({
48
48
  id: db.primaryKey(),
49
- status: db.enum(["enabled", "disabled"], { name: "shared_status_enum" }),
49
+ status: t.enum(["enabled", "disabled"], { name: "shared_status_enum" }),
50
50
  }),
51
51
  });
52
52
 
@@ -55,7 +55,7 @@ const conflictEnumEntity1 = $entity({
55
55
  name: "conflict_enum_test_1",
56
56
  schema: t.object({
57
57
  id: db.primaryKey(),
58
- status: db.enum(["a", "b", "c"], { name: "conflict_status_enum" }),
58
+ status: t.enum(["a", "b", "c"], { name: "conflict_status_enum" }),
59
59
  }),
60
60
  });
61
61
 
@@ -63,12 +63,12 @@ const conflictEnumEntity2 = $entity({
63
63
  name: "conflict_enum_test_2",
64
64
  schema: t.object({
65
65
  id: db.primaryKey(),
66
- status: db.enum(["a", "b", "d"], { name: "conflict_status_enum" }), // Different value!
66
+ status: t.enum(["a", "b", "d"], { name: "conflict_status_enum" }), // Different value!
67
67
  }),
68
68
  });
69
69
 
70
- describe("enums - t.enum (TEXT column)", () => {
71
- it("should create TEXT columns for t.enum fields", async () => {
70
+ describe("enums - t.enum with mode: 'text' (TEXT column)", () => {
71
+ it("should create TEXT columns for t.enum fields with mode: 'text'", async () => {
72
72
  const alepha = Alepha.create().with(AlephaOrmPostgres);
73
73
 
74
74
  class App {
@@ -87,12 +87,12 @@ describe("enums - t.enum (TEXT column)", () => {
87
87
 
88
88
  expect(sql).toContainEqual(expect.stringContaining("CREATE TABLE"));
89
89
  expect(sql).toContainEqual(expect.stringContaining("text_enum_test"));
90
- // t.enum should map to TEXT, not a real ENUM type
90
+ // mode: "text" should map to TEXT, not a real ENUM type
91
91
  expect(sql.some((s) => s.includes('"status" text'))).toBe(true);
92
92
  expect(sql.some((s) => s.includes('"role" text'))).toBe(true);
93
93
  });
94
94
 
95
- it("should allow inserting and querying with t.enum values", async () => {
95
+ it("should allow inserting and querying with text enum values", async () => {
96
96
  const alepha = Alepha.create().with(AlephaOrmPostgres);
97
97
 
98
98
  class App {
@@ -117,8 +117,8 @@ describe("enums - t.enum (TEXT column)", () => {
117
117
  });
118
118
  });
119
119
 
120
- describe("enums - db.enum (real PG ENUM type)", () => {
121
- it("should create real PostgreSQL ENUM types for db.enum fields", async () => {
120
+ describe("enums - t.enum (real PG ENUM type)", () => {
121
+ it("should create real PostgreSQL ENUM types for t.enum fields", async () => {
122
122
  const alepha = Alepha.create().with(AlephaOrmPostgres);
123
123
 
124
124
  class App {
@@ -137,13 +137,13 @@ describe("enums - db.enum (real PG ENUM type)", () => {
137
137
 
138
138
  expect(sql).toContainEqual(expect.stringContaining("CREATE TABLE"));
139
139
  expect(sql).toContainEqual(expect.stringContaining("pg_enum_test"));
140
- // db.enum should create real ENUM types
140
+ // t.enum without mode should create real ENUM types
141
141
  expect(
142
142
  sql.some((s) => s.includes("CREATE TYPE") || s.includes("DO $$ BEGIN")),
143
143
  ).toBe(true);
144
144
  });
145
145
 
146
- it("should allow inserting and querying with db.enum values", async () => {
146
+ it("should allow inserting and querying with pg enum values", async () => {
147
147
  const alepha = Alepha.create().with(AlephaOrmPostgres);
148
148
 
149
149
  class App {
@@ -168,7 +168,7 @@ describe("enums - db.enum (real PG ENUM type)", () => {
168
168
  });
169
169
  });
170
170
 
171
- describe("enums - mixed t.enum and db.enum in same table", () => {
171
+ describe("enums - mixed text and pg enum in same table", () => {
172
172
  it("should handle both TEXT and PG ENUM types in the same table", async () => {
173
173
  const alepha = Alepha.create().with(AlephaOrmPostgres);
174
174
 
@@ -299,13 +299,6 @@ describe("enums - conflict detection with different values", () => {
299
299
  it("should throw error when same enum name has different values", async () => {
300
300
  const alepha = Alepha.create().with(AlephaOrmPostgres);
301
301
 
302
- const load = async () => {
303
- return alepha.with(() => ({
304
- r1: $repository(conflictEnumEntity1),
305
- r2: $repository(conflictEnumEntity2),
306
- }));
307
- };
308
-
309
302
  expect(() =>
310
303
  alepha.with(() => ({
311
304
  r1: $repository(conflictEnumEntity1),
@@ -0,0 +1,430 @@
1
+ import type { Alepha } from "alepha";
2
+ import { t } from "alepha";
3
+ import { expect } from "vitest";
4
+ import { $entity, $repository, db } from "../core/index.ts";
5
+
6
+ // ============================================================================
7
+ // Entity definitions — two entities with a foreign key relationship
8
+ // ============================================================================
9
+
10
+ const teams = $entity({
11
+ name: "teams",
12
+ schema: t.object({
13
+ id: db.primaryKey(),
14
+ name: t.text(),
15
+ country: t.text(),
16
+ }),
17
+ });
18
+
19
+ const players = $entity({
20
+ name: "players",
21
+ schema: t.object({
22
+ id: db.primaryKey(),
23
+ teamId: db.ref(t.optional(t.integer()), () => teams.cols.id),
24
+ name: t.text(),
25
+ position: t.text(),
26
+ goals: db.default(t.integer(), 0),
27
+ }),
28
+ });
29
+
30
+ class App {
31
+ teams = $repository(teams);
32
+ players = $repository(players);
33
+ }
34
+
35
+ // ============================================================================
36
+ // Shared setup
37
+ // ============================================================================
38
+
39
+ async function seed(app: App) {
40
+ const barca = await app.teams.create({
41
+ name: "FC Barcelona",
42
+ country: "Spain",
43
+ });
44
+ const psg = await app.teams.create({
45
+ name: "Paris Saint-Germain",
46
+ country: "France",
47
+ });
48
+ const real = await app.teams.create({
49
+ name: "Real Madrid",
50
+ country: "Spain",
51
+ });
52
+
53
+ const messi = await app.players.create({
54
+ teamId: barca.id,
55
+ name: "Messi",
56
+ position: "Forward",
57
+ goals: 672,
58
+ });
59
+ const pedri = await app.players.create({
60
+ teamId: barca.id,
61
+ name: "Pedri",
62
+ position: "Midfielder",
63
+ goals: 18,
64
+ });
65
+ const mbappe = await app.players.create({
66
+ teamId: psg.id,
67
+ name: "Mbappé",
68
+ position: "Forward",
69
+ goals: 256,
70
+ });
71
+ const modric = await app.players.create({
72
+ teamId: real.id,
73
+ name: "Modrić",
74
+ position: "Midfielder",
75
+ goals: 39,
76
+ });
77
+ const freeAgent = await app.players.create({
78
+ name: "Free Agent",
79
+ position: "Goalkeeper",
80
+ goals: 0,
81
+ });
82
+
83
+ return {
84
+ teams: { barca, psg, real },
85
+ players: { messi, pedri, mbappe, modric, freeAgent },
86
+ };
87
+ }
88
+
89
+ // ============================================================================
90
+ // 1. Basic left join — player with their team
91
+ // ============================================================================
92
+
93
+ export const testLeftJoinPlayerWithTeam = async (alepha: Alepha) => {
94
+ const app = alepha.inject(App);
95
+ await alepha.start();
96
+ const { players: p } = await seed(app);
97
+
98
+ const result = await app.players.getOne({
99
+ where: { id: { eq: p.messi.id } },
100
+ with: {
101
+ team: {
102
+ join: teams,
103
+ on: ["teamId", teams.cols.id],
104
+ },
105
+ },
106
+ });
107
+
108
+ expect(result.name).toBe("Messi");
109
+ expect(result.team).toBeDefined();
110
+ expect(result.team.name).toBe("FC Barcelona");
111
+ expect(result.team.country).toBe("Spain");
112
+ };
113
+
114
+ // ============================================================================
115
+ // 2. Left join returns undefined when FK is null
116
+ // ============================================================================
117
+
118
+ export const testLeftJoinReturnsUndefinedWhenFkNull = async (
119
+ alepha: Alepha,
120
+ ) => {
121
+ const app = alepha.inject(App);
122
+ await alepha.start();
123
+ const { players: p } = await seed(app);
124
+
125
+ const result = await app.players.getOne({
126
+ where: { id: { eq: p.freeAgent.id } },
127
+ with: {
128
+ team: {
129
+ join: teams,
130
+ on: ["teamId", teams.cols.id],
131
+ },
132
+ },
133
+ });
134
+
135
+ expect(result.name).toBe("Free Agent");
136
+ expect(result.team).toBeUndefined();
137
+ };
138
+
139
+ // ============================================================================
140
+ // 3. Inner join excludes rows without match
141
+ // ============================================================================
142
+
143
+ export const testInnerJoinExcludesNulls = async (alepha: Alepha) => {
144
+ const app = alepha.inject(App);
145
+ await alepha.start();
146
+ await seed(app);
147
+
148
+ const results = await app.players.findMany({
149
+ with: {
150
+ team: {
151
+ type: "inner",
152
+ join: teams,
153
+ on: ["teamId", teams.cols.id],
154
+ },
155
+ },
156
+ });
157
+
158
+ // Free Agent has no team → excluded by inner join
159
+ expect(results.length).toBe(4);
160
+ expect(results.every((r) => r.team !== null && r.team !== undefined)).toBe(
161
+ true,
162
+ );
163
+ expect(results.find((r) => r.name === "Free Agent")).toBeUndefined();
164
+ };
165
+
166
+ // ============================================================================
167
+ // 4. findMany with join + filter on joined table
168
+ // ============================================================================
169
+
170
+ export const testFilterOnJoinedTable = async (alepha: Alepha) => {
171
+ const app = alepha.inject(App);
172
+ await alepha.start();
173
+ await seed(app);
174
+
175
+ const spanishPlayers = await app.players.findMany({
176
+ with: {
177
+ team: {
178
+ join: teams,
179
+ on: ["teamId", teams.cols.id],
180
+ },
181
+ },
182
+ where: {
183
+ team: {
184
+ country: { eq: "Spain" },
185
+ },
186
+ },
187
+ });
188
+
189
+ expect(spanishPlayers.length).toBe(3); // Messi, Pedri (Barça), Modrić (Real)
190
+ expect(spanishPlayers.every((p) => p.team.country === "Spain")).toBe(true);
191
+ };
192
+
193
+ // ============================================================================
194
+ // 5. Combined filters — base table + joined table
195
+ // ============================================================================
196
+
197
+ export const testCombinedFilters = async (alepha: Alepha) => {
198
+ const app = alepha.inject(App);
199
+ await alepha.start();
200
+ await seed(app);
201
+
202
+ const results = await app.players.findMany({
203
+ with: {
204
+ team: {
205
+ join: teams,
206
+ on: ["teamId", teams.cols.id],
207
+ },
208
+ },
209
+ where: {
210
+ and: [
211
+ { position: { eq: "Forward" } },
212
+ { team: { country: { eq: "Spain" } } },
213
+ ],
214
+ },
215
+ });
216
+
217
+ expect(results.length).toBe(1);
218
+ expect(results[0].name).toBe("Messi");
219
+ };
220
+
221
+ // ============================================================================
222
+ // 6. findMany with join + orderBy + limit
223
+ // ============================================================================
224
+
225
+ export const testJoinWithOrderByAndLimit = async (alepha: Alepha) => {
226
+ const app = alepha.inject(App);
227
+ await alepha.start();
228
+ await seed(app);
229
+
230
+ const topScorers = await app.players.findMany({
231
+ with: {
232
+ team: {
233
+ join: teams,
234
+ on: ["teamId", teams.cols.id],
235
+ },
236
+ },
237
+ orderBy: { column: "goals", direction: "desc" },
238
+ limit: 2,
239
+ });
240
+
241
+ expect(topScorers.length).toBe(2);
242
+ expect(topScorers[0].name).toBe("Messi");
243
+ expect(topScorers[1].name).toBe("Mbappé");
244
+ expect(topScorers[0].team).toBeDefined();
245
+ expect(topScorers[1].team).toBeDefined();
246
+ };
247
+
248
+ // ============================================================================
249
+ // 7. Pagination with joins
250
+ // ============================================================================
251
+
252
+ export const testPaginationWithJoins = async (alepha: Alepha) => {
253
+ const app = alepha.inject(App);
254
+ await alepha.start();
255
+ await seed(app);
256
+
257
+ const page = await app.players.paginate(
258
+ { page: 0, size: 2 },
259
+ {
260
+ with: {
261
+ team: {
262
+ join: teams,
263
+ on: ["teamId", teams.cols.id],
264
+ },
265
+ },
266
+ orderBy: { column: "name", direction: "asc" },
267
+ },
268
+ );
269
+
270
+ expect(page.content.length).toBe(2);
271
+ expect(page.page.isFirst).toBe(true);
272
+ expect(page.page.isLast).toBe(false);
273
+ // Each result should include the joined team
274
+ for (const player of page.content) {
275
+ // Some players may have no team (left join)
276
+ if (player.teamId) {
277
+ expect(player.team).toBeDefined();
278
+ }
279
+ }
280
+ };
281
+
282
+ // ============================================================================
283
+ // 8. OR filter across base + joined table
284
+ // ============================================================================
285
+
286
+ export const testOrFilterAcrossTables = async (alepha: Alepha) => {
287
+ const app = alepha.inject(App);
288
+ await alepha.start();
289
+ await seed(app);
290
+
291
+ const results = await app.players.findMany({
292
+ with: {
293
+ team: {
294
+ join: teams,
295
+ on: ["teamId", teams.cols.id],
296
+ },
297
+ },
298
+ where: {
299
+ or: [{ goals: { gte: 200 } }, { team: { country: { eq: "France" } } }],
300
+ },
301
+ orderBy: { column: "goals", direction: "desc" },
302
+ });
303
+
304
+ // Messi (672), Mbappé (256 + France) — Mbappé matches both conditions
305
+ expect(results.length).toBe(2);
306
+ expect(results.map((r) => r.name)).toEqual(["Messi", "Mbappé"]);
307
+ };
308
+
309
+ // ============================================================================
310
+ // 9. getOne with join throws when not found
311
+ // ============================================================================
312
+
313
+ export const testGetOneWithJoinThrowsWhenNotFound = async (alepha: Alepha) => {
314
+ const app = alepha.inject(App);
315
+ await alepha.start();
316
+ await seed(app);
317
+
318
+ await expect(
319
+ app.players.getOne({
320
+ where: { name: { eq: "Neymar" } },
321
+ with: {
322
+ team: {
323
+ join: teams,
324
+ on: ["teamId", teams.cols.id],
325
+ },
326
+ },
327
+ }),
328
+ ).rejects.toThrow();
329
+ };
330
+
331
+ // ============================================================================
332
+ // 10. findOne with join — returns undefined instead of throwing
333
+ // ============================================================================
334
+
335
+ export const testFindOneWithJoinReturnsUndefined = async (alepha: Alepha) => {
336
+ const app = alepha.inject(App);
337
+ await alepha.start();
338
+ await seed(app);
339
+
340
+ const result = await app.players.findOne({
341
+ where: { name: { eq: "Neymar" } },
342
+ with: {
343
+ team: {
344
+ join: teams,
345
+ on: ["teamId", teams.cols.id],
346
+ },
347
+ },
348
+ });
349
+
350
+ expect(result).toBeUndefined();
351
+ };
352
+
353
+ // ============================================================================
354
+ // 11. Empty result set with joins
355
+ // ============================================================================
356
+
357
+ export const testEmptyResultWithJoins = async (alepha: Alepha) => {
358
+ const app = alepha.inject(App);
359
+ await alepha.start();
360
+ await seed(app);
361
+
362
+ const results = await app.players.findMany({
363
+ where: { position: { eq: "Defender" } },
364
+ with: {
365
+ team: {
366
+ join: teams,
367
+ on: ["teamId", teams.cols.id],
368
+ },
369
+ },
370
+ });
371
+
372
+ expect(results.length).toBe(0);
373
+ };
374
+
375
+ // ============================================================================
376
+ // 12. Multiple operators on joined table filter
377
+ // ============================================================================
378
+
379
+ export const testMultipleOperatorsOnJoinedFilter = async (alepha: Alepha) => {
380
+ const app = alepha.inject(App);
381
+ await alepha.start();
382
+ await seed(app);
383
+
384
+ const results = await app.players.findMany({
385
+ with: {
386
+ team: {
387
+ join: teams,
388
+ on: ["teamId", teams.cols.id],
389
+ },
390
+ },
391
+ where: {
392
+ team: {
393
+ name: { contains: "paris" }, // case-insensitive
394
+ },
395
+ },
396
+ });
397
+
398
+ expect(results.length).toBe(1);
399
+ expect(results[0].name).toBe("Mbappé");
400
+ expect(results[0].team.name).toBe("Paris Saint-Germain");
401
+ };
402
+
403
+ // ============================================================================
404
+ // 13. Join with offset (pagination without paginate)
405
+ // ============================================================================
406
+
407
+ export const testJoinWithOffset = async (alepha: Alepha) => {
408
+ const app = alepha.inject(App);
409
+ await alepha.start();
410
+ await seed(app);
411
+
412
+ const results = await app.players.findMany({
413
+ with: {
414
+ team: {
415
+ type: "inner",
416
+ join: teams,
417
+ on: ["teamId", teams.cols.id],
418
+ },
419
+ },
420
+ orderBy: { column: "name", direction: "asc" },
421
+ offset: 1,
422
+ limit: 2,
423
+ });
424
+
425
+ // Inner join excludes Free Agent → sorted: Mbappé, Messi, Modrić, Pedri
426
+ // offset 1 + limit 2 → Messi, Modrić
427
+ expect(results.length).toBe(2);
428
+ expect(results[0].name).toBe("Messi");
429
+ expect(results[1].name).toBe("Modrić");
430
+ };