alepha 0.14.1 → 0.14.3
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.
- package/README.md +3 -3
- package/dist/api/audits/index.browser.js +5 -5
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.d.ts +784 -784
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +13 -13
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.browser.js +5 -5
- package/dist/api/files/index.browser.js.map +1 -1
- package/dist/api/files/index.d.ts +57 -57
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +71 -71
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +5 -5
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +165 -165
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +10 -10
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/notifications/index.browser.js +10 -10
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +583 -171
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +12 -12
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.browser.js +163 -10
- package/dist/api/parameters/index.browser.js.map +1 -1
- package/dist/api/parameters/index.d.ts +281 -276
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +196 -91
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.browser.js +19 -19
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +778 -764
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +831 -596
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.browser.js +6 -6
- package/dist/api/verifications/index.browser.js.map +1 -1
- package/dist/api/verifications/index.d.ts +125 -125
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +6 -6
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/bin/index.d.ts +1 -2
- package/dist/bin/index.js +0 -1
- package/dist/bin/index.js.map +1 -1
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cli/index.d.ts +249 -218
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +951 -821
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +40 -0
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +97 -17
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +14 -18
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +29 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +21 -24
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +21 -24
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.js.map +1 -1
- package/dist/fake/index.js +195 -168
- package/dist/fake/index.js.map +1 -1
- package/dist/file/index.d.ts +8 -0
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +3 -0
- package/dist/file/index.js.map +1 -1
- package/dist/lock/redis/index.js.map +1 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.browser.js +26 -5
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.d.ts +146 -121
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +49 -24
- package/dist/orm/index.js.map +1 -1
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.js.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +6 -6
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +29 -29
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +171 -155
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +0 -1
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/compress/index.js +2 -0
- package/dist/server/compress/index.js.map +1 -1
- package/dist/server/cookies/index.browser.js.map +1 -1
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +1 -1
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/health/index.d.ts +17 -17
- package/dist/server/helmet/index.js.map +1 -1
- package/dist/server/links/index.browser.js +22 -6
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +46 -44
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +24 -41
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/security/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +2 -1
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +8 -3
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.js.map +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +12 -4
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.js.map +1 -1
- package/package.json +7 -7
- package/src/api/audits/controllers/{AuditController.ts → AdminAuditController.ts} +5 -6
- package/src/api/audits/entities/audits.ts +5 -5
- package/src/api/audits/index.browser.ts +1 -1
- package/src/api/audits/index.ts +3 -3
- package/src/api/audits/primitives/$audit.spec.ts +276 -0
- package/src/api/audits/services/AuditService.spec.ts +495 -0
- package/src/api/files/__tests__/$bucket.spec.ts +91 -0
- package/src/api/files/controllers/AdminFileStatsController.spec.ts +166 -0
- package/src/api/files/controllers/{StorageStatsController.ts → AdminFileStatsController.ts} +2 -2
- package/src/api/files/controllers/FileController.spec.ts +558 -0
- package/src/api/files/controllers/FileController.ts +4 -5
- package/src/api/files/entities/files.ts +5 -5
- package/src/api/files/index.browser.ts +1 -1
- package/src/api/files/index.ts +4 -4
- package/src/api/files/jobs/FileJobs.spec.ts +52 -0
- package/src/api/files/services/FileService.spec.ts +109 -0
- package/src/api/jobs/__tests__/JobController.spec.ts +343 -0
- package/src/api/jobs/controllers/{JobController.ts → AdminJobController.ts} +2 -2
- package/src/api/jobs/entities/jobExecutions.ts +5 -5
- package/src/api/jobs/index.ts +3 -3
- package/src/api/jobs/primitives/$job.spec.ts +476 -0
- package/src/api/notifications/controllers/{NotificationController.ts → AdminNotificationController.ts} +4 -5
- package/src/api/notifications/entities/notifications.ts +5 -5
- package/src/api/notifications/index.browser.ts +1 -1
- package/src/api/notifications/index.ts +4 -4
- package/src/api/parameters/controllers/{ConfigController.ts → AdminConfigController.ts} +46 -107
- package/src/api/parameters/entities/parameters.ts +7 -17
- package/src/api/parameters/index.ts +3 -3
- package/src/api/parameters/primitives/$config.spec.ts +356 -0
- package/src/api/parameters/schemas/activateConfigBodySchema.ts +12 -0
- package/src/api/parameters/schemas/checkScheduledResponseSchema.ts +8 -0
- package/src/api/parameters/schemas/configCurrentResponseSchema.ts +13 -0
- package/src/api/parameters/schemas/configHistoryResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/configNameParamSchema.ts +10 -0
- package/src/api/parameters/schemas/configNamesResponseSchema.ts +8 -0
- package/src/api/parameters/schemas/configTreeNodeSchema.ts +13 -0
- package/src/api/parameters/schemas/configVersionParamSchema.ts +9 -0
- package/src/api/parameters/schemas/configVersionResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/configsByStatusResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/createConfigVersionBodySchema.ts +24 -0
- package/src/api/parameters/schemas/index.ts +15 -0
- package/src/api/parameters/schemas/parameterResponseSchema.ts +26 -0
- package/src/api/parameters/schemas/parameterStatusSchema.ts +13 -0
- package/src/api/parameters/schemas/rollbackConfigBodySchema.ts +15 -0
- package/src/api/parameters/schemas/statusParamSchema.ts +9 -0
- package/src/api/users/__tests__/EmailVerification.spec.ts +369 -0
- package/src/api/users/__tests__/PasswordReset.spec.ts +550 -0
- package/src/api/users/controllers/AdminIdentityController.spec.ts +365 -0
- package/src/api/users/controllers/{IdentityController.ts → AdminIdentityController.ts} +3 -4
- package/src/api/users/controllers/AdminSessionController.spec.ts +274 -0
- package/src/api/users/controllers/{SessionController.ts → AdminSessionController.ts} +3 -4
- package/src/api/users/controllers/AdminUserController.spec.ts +372 -0
- package/src/api/users/controllers/AdminUserController.ts +116 -0
- package/src/api/users/controllers/UserController.ts +4 -107
- package/src/api/users/controllers/UserRealmController.ts +3 -0
- package/src/api/users/entities/identities.ts +6 -6
- package/src/api/users/entities/sessions.ts +6 -6
- package/src/api/users/entities/users.ts +9 -9
- package/src/api/users/index.ts +13 -6
- package/src/api/users/primitives/$userRealm.ts +13 -8
- package/src/api/users/services/CredentialService.spec.ts +509 -0
- package/src/api/users/services/CredentialService.ts +46 -0
- package/src/api/users/services/IdentityService.ts +15 -0
- package/src/api/users/services/RegistrationService.spec.ts +630 -0
- package/src/api/users/services/RegistrationService.ts +18 -0
- package/src/api/users/services/SessionService.spec.ts +301 -0
- package/src/api/users/services/SessionService.ts +110 -1
- package/src/api/users/services/UserService.ts +67 -2
- package/src/api/verifications/__tests__/CodeVerification.spec.ts +318 -0
- package/src/api/verifications/__tests__/LinkVerification.spec.ts +279 -0
- package/src/api/verifications/entities/verifications.ts +6 -6
- package/src/api/verifications/jobs/VerificationJobs.spec.ts +50 -0
- package/src/batch/__tests__/startup-buffering.spec.ts +458 -0
- package/src/batch/primitives/$batch.spec.ts +766 -0
- package/src/batch/providers/BatchProvider.spec.ts +786 -0
- package/src/bin/index.ts +0 -1
- package/src/bucket/__tests__/shared.ts +194 -0
- package/src/bucket/primitives/$bucket.spec.ts +104 -0
- package/src/bucket/providers/FileStorageProvider.spec.ts +13 -0
- package/src/bucket/providers/LocalFileStorageProvider.spec.ts +77 -0
- package/src/bucket/providers/MemoryFileStorageProvider.spec.ts +82 -0
- package/src/cache/core/__tests__/shared.ts +377 -0
- package/src/cache/core/primitives/$cache.spec.ts +111 -0
- package/src/cache/redis/__tests__/cache-redis.spec.ts +70 -0
- package/src/cli/apps/AlephaCli.ts +54 -16
- package/src/cli/apps/AlephaPackageBuilderCli.ts +2 -1
- package/src/cli/assets/appRouterTs.ts +1 -1
- package/src/cli/commands/{ViteCommands.ts → build.ts} +2 -105
- package/src/cli/commands/clean.ts +14 -0
- package/src/cli/commands/{DrizzleCommands.ts → db.ts} +10 -117
- package/src/cli/commands/{DeployCommands.ts → deploy.ts} +1 -1
- package/src/cli/commands/dev.ts +69 -0
- package/src/cli/commands/format.ts +17 -0
- package/src/cli/commands/gen/changelog.spec.ts +315 -0
- package/src/cli/commands/{ChangelogCommands.ts → gen/changelog.ts} +16 -31
- package/src/cli/commands/gen/openapi.ts +71 -0
- package/src/cli/commands/gen.ts +18 -0
- package/src/cli/commands/{CoreCommands.ts → init.ts} +4 -40
- package/src/cli/commands/lint.ts +17 -0
- package/src/cli/commands/root.ts +41 -0
- package/src/cli/commands/run.ts +24 -0
- package/src/cli/commands/test.ts +42 -0
- package/src/cli/commands/typecheck.ts +24 -0
- package/src/cli/commands/{VerifyCommands.ts → verify.ts} +1 -13
- package/src/cli/defineConfig.ts +10 -1
- package/src/cli/index.ts +17 -7
- package/src/cli/services/AlephaCliUtils.ts +71 -32
- package/src/cli/services/GitMessageParser.ts +1 -1
- package/src/command/helpers/Asker.spec.ts +127 -0
- package/src/command/helpers/Runner.spec.ts +126 -0
- package/src/command/primitives/$command.spec.ts +1588 -0
- package/src/command/providers/CliProvider.ts +74 -24
- package/src/core/Alepha.ts +52 -4
- package/src/core/__tests__/Alepha-emit.spec.ts +22 -0
- package/src/core/__tests__/Alepha-graph.spec.ts +93 -0
- package/src/core/__tests__/Alepha-has.spec.ts +41 -0
- package/src/core/__tests__/Alepha-inject.spec.ts +93 -0
- package/src/core/__tests__/Alepha-register.spec.ts +81 -0
- package/src/core/__tests__/Alepha-start.spec.ts +176 -0
- package/src/core/__tests__/Alepha-with.spec.ts +14 -0
- package/src/core/__tests__/TypeBox-usecases.spec.ts +35 -0
- package/src/core/__tests__/TypeBoxLocale.spec.ts +15 -0
- package/src/core/__tests__/descriptor.spec.ts +34 -0
- package/src/core/__tests__/fixtures/A.ts +5 -0
- package/src/core/__tests__/pagination.spec.ts +77 -0
- package/src/core/helpers/jsonSchemaToTypeBox.ts +2 -2
- package/src/core/primitives/$atom.spec.ts +43 -0
- package/src/core/primitives/$hook.spec.ts +130 -0
- package/src/core/primitives/$inject.spec.ts +175 -0
- package/src/core/primitives/$module.spec.ts +115 -0
- package/src/core/providers/CodecManager.spec.ts +740 -0
- package/src/core/providers/EventManager.spec.ts +762 -0
- package/src/core/providers/EventManager.ts +4 -0
- package/src/core/providers/StateManager.spec.ts +365 -0
- package/src/core/providers/TypeProvider.spec.ts +1607 -0
- package/src/core/providers/TypeProvider.ts +20 -26
- package/src/datetime/primitives/$interval.spec.ts +103 -0
- package/src/datetime/providers/DateTimeProvider.spec.ts +86 -0
- package/src/email/primitives/$email.spec.ts +175 -0
- package/src/email/providers/LocalEmailProvider.spec.ts +341 -0
- package/src/fake/__tests__/keyName.example.ts +40 -0
- package/src/fake/__tests__/keyName.spec.ts +152 -0
- package/src/fake/__tests__/module.example.ts +32 -0
- package/src/fake/providers/FakeProvider.spec.ts +438 -0
- package/src/file/providers/FileSystemProvider.ts +8 -0
- package/src/file/providers/NodeFileSystemProvider.spec.ts +418 -0
- package/src/file/providers/NodeFileSystemProvider.ts +5 -0
- package/src/file/services/FileDetector.spec.ts +591 -0
- package/src/lock/core/__tests__/shared.ts +190 -0
- package/src/lock/core/providers/MemoryLockProvider.spec.ts +25 -0
- package/src/lock/redis/providers/RedisLockProvider.spec.ts +25 -0
- package/src/logger/__tests__/SimpleFormatterProvider.spec.ts +109 -0
- package/src/logger/primitives/$logger.spec.ts +108 -0
- package/src/logger/services/Logger.spec.ts +295 -0
- package/src/mcp/__tests__/errors.spec.ts +175 -0
- package/src/mcp/__tests__/integration.spec.ts +450 -0
- package/src/mcp/helpers/jsonrpc.spec.ts +380 -0
- package/src/mcp/primitives/$prompt.spec.ts +468 -0
- package/src/mcp/primitives/$resource.spec.ts +390 -0
- package/src/mcp/primitives/$tool.spec.ts +406 -0
- package/src/mcp/providers/McpServerProvider.spec.ts +797 -0
- package/src/orm/__tests__/$repository-crud.spec.ts +276 -0
- package/src/orm/__tests__/$repository-hooks.spec.ts +325 -0
- package/src/orm/__tests__/$repository-orderBy.spec.ts +128 -0
- package/src/orm/__tests__/$repository-pagination-sort.spec.ts +149 -0
- package/src/orm/__tests__/$repository-save.spec.ts +37 -0
- package/src/orm/__tests__/ModelBuilder-integration.spec.ts +490 -0
- package/src/orm/__tests__/ModelBuilder-types.spec.ts +186 -0
- package/src/orm/__tests__/PostgresProvider.spec.ts +46 -0
- package/src/orm/__tests__/delete-returning.spec.ts +256 -0
- package/src/orm/__tests__/deletedAt.spec.ts +80 -0
- package/src/orm/__tests__/enums.spec.ts +315 -0
- package/src/orm/__tests__/execute.spec.ts +72 -0
- package/src/orm/__tests__/fixtures/bigEntitySchema.ts +65 -0
- package/src/orm/__tests__/fixtures/userEntitySchema.ts +27 -0
- package/src/orm/__tests__/joins.spec.ts +1114 -0
- package/src/orm/__tests__/page.spec.ts +287 -0
- package/src/orm/__tests__/primaryKey.spec.ts +87 -0
- package/src/orm/__tests__/query-date-encoding.spec.ts +402 -0
- package/src/orm/__tests__/ref-auto-onDelete.spec.ts +156 -0
- package/src/orm/__tests__/references.spec.ts +102 -0
- package/src/orm/__tests__/security.spec.ts +710 -0
- package/src/orm/__tests__/sqlite.spec.ts +111 -0
- package/src/orm/__tests__/string-operators.spec.ts +429 -0
- package/src/orm/__tests__/timestamps.spec.ts +388 -0
- package/src/orm/__tests__/validation.spec.ts +183 -0
- package/src/orm/__tests__/version.spec.ts +64 -0
- package/src/orm/helpers/parseQueryString.spec.ts +196 -0
- package/src/orm/index.browser.ts +1 -1
- package/src/orm/index.ts +10 -6
- package/src/orm/primitives/$repository.spec.ts +137 -0
- package/src/orm/primitives/$sequence.spec.ts +29 -0
- package/src/orm/primitives/$transaction.spec.ts +82 -0
- package/src/orm/providers/{PostgresTypeProvider.ts → DatabaseTypeProvider.ts} +25 -3
- package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -3
- package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +1 -1
- package/src/orm/providers/drivers/DatabaseProvider.ts +1 -1
- package/src/orm/providers/drivers/NodePostgresProvider.ts +3 -3
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/PglitePostgresProvider.ts +2 -2
- package/src/orm/services/ModelBuilder.spec.ts +575 -0
- package/src/orm/services/Repository.spec.ts +137 -0
- package/src/queue/core/__tests__/shared.ts +143 -0
- package/src/queue/core/providers/MemoryQueueProvider.spec.ts +23 -0
- package/src/queue/core/providers/WorkerProvider.spec.ts +378 -0
- package/src/queue/redis/providers/RedisQueueProvider.spec.ts +23 -0
- package/src/redis/__tests__/redis.spec.ts +58 -0
- package/src/retry/primitives/$retry.spec.ts +234 -0
- package/src/retry/providers/RetryProvider.spec.ts +438 -0
- package/src/router/__tests__/match.spec.ts +252 -0
- package/src/router/providers/RouterProvider.spec.ts +197 -0
- package/src/scheduler/__tests__/$scheduler-cron.spec.ts +25 -0
- package/src/scheduler/__tests__/$scheduler-interval.spec.ts +25 -0
- package/src/scheduler/__tests__/shared.ts +77 -0
- package/src/security/__tests__/bug-1-wildcard-after-start.spec.ts +229 -0
- package/src/security/__tests__/bug-2-password-validation.spec.ts +245 -0
- package/src/security/__tests__/bug-3-regex-vulnerability.spec.ts +407 -0
- package/src/security/__tests__/bug-4-oauth2-validation.spec.ts +439 -0
- package/src/security/__tests__/multi-layer-permissions.spec.ts +522 -0
- package/src/security/primitives/$permission.spec.ts +30 -0
- package/src/security/primitives/$permission.ts +2 -2
- package/src/security/primitives/$realm.spec.ts +101 -0
- package/src/security/primitives/$role.spec.ts +52 -0
- package/src/security/primitives/$serviceAccount.spec.ts +61 -0
- package/src/security/providers/SecurityProvider.spec.ts +350 -0
- package/src/server/auth/providers/ServerAuthProvider.ts +0 -2
- package/src/server/cache/providers/ServerCacheProvider.spec.ts +942 -0
- package/src/server/compress/providers/ServerCompressProvider.spec.ts +31 -0
- package/src/server/compress/providers/ServerCompressProvider.ts +2 -0
- package/src/server/cookies/providers/ServerCookiesProvider.spec.ts +253 -0
- package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +334 -0
- package/src/server/core/__tests__/ServerRouterProvider-requestId.spec.ts +129 -0
- package/src/server/core/primitives/$action.spec.ts +191 -0
- package/src/server/core/primitives/$route.spec.ts +65 -0
- package/src/server/core/providers/ServerBodyParserProvider.spec.ts +93 -0
- package/src/server/core/providers/ServerLoggerProvider.spec.ts +100 -0
- package/src/server/core/providers/ServerProvider.ts +3 -1
- package/src/server/core/services/HttpClient.spec.ts +123 -0
- package/src/server/core/services/UserAgentParser.spec.ts +111 -0
- package/src/server/cors/providers/ServerCorsProvider.spec.ts +481 -0
- package/src/server/health/providers/ServerHealthProvider.spec.ts +22 -0
- package/src/server/helmet/providers/ServerHelmetProvider.spec.ts +105 -0
- package/src/server/links/__tests__/$action.spec.ts +238 -0
- package/src/server/links/__tests__/fixtures/CrudApp.ts +122 -0
- package/src/server/links/__tests__/requestId.spec.ts +120 -0
- package/src/server/links/primitives/$remote.spec.ts +228 -0
- package/src/server/links/providers/LinkProvider.spec.ts +54 -0
- package/src/server/links/providers/LinkProvider.ts +49 -3
- package/src/server/links/providers/ServerLinksProvider.ts +1 -53
- package/src/server/links/schemas/apiLinksResponseSchema.ts +7 -0
- package/src/server/metrics/providers/ServerMetricsProvider.spec.ts +25 -0
- package/src/server/multipart/providers/ServerMultipartProvider.spec.ts +528 -0
- package/src/server/proxy/primitives/$proxy.spec.ts +87 -0
- package/src/server/rate-limit/__tests__/ActionRateLimit.spec.ts +211 -0
- package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +344 -0
- package/src/server/security/__tests__/BasicAuth.spec.ts +684 -0
- package/src/server/security/__tests__/ServerSecurityProvider-realm.spec.ts +388 -0
- package/src/server/security/providers/ServerSecurityProvider.spec.ts +123 -0
- package/src/server/static/primitives/$serve.spec.ts +193 -0
- package/src/server/swagger/__tests__/ui.spec.ts +52 -0
- package/src/server/swagger/primitives/$swagger.spec.ts +193 -0
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +18 -8
- package/src/sms/primitives/$sms.spec.ts +165 -0
- package/src/sms/providers/LocalSmsProvider.spec.ts +224 -0
- package/src/sms/providers/MemorySmsProvider.spec.ts +193 -0
- package/src/thread/primitives/$thread.spec.ts +186 -0
- package/src/topic/core/__tests__/shared.ts +144 -0
- package/src/topic/core/providers/MemoryTopicProvider.spec.ts +23 -0
- package/src/topic/redis/providers/RedisTopicProvider.spec.ts +23 -0
- package/src/vite/plugins/viteAlephaDev.ts +16 -4
- package/src/vite/tasks/runAlepha.ts +7 -1
- package/src/websocket/__tests__/$websocket-new.spec.ts +195 -0
- package/src/websocket/primitives/$channel.spec.ts +30 -0
- package/src/cli/commands/BiomeCommands.ts +0 -29
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { Alepha, t } from "alepha";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { $entity, $repository } from "../index.ts";
|
|
4
|
+
import { pg } from "../providers/DatabaseTypeProvider.ts";
|
|
5
|
+
|
|
6
|
+
// Helper function to parse dates from various formats
|
|
7
|
+
const parseDate = (dateValue: any): Date => {
|
|
8
|
+
if (!dateValue) return new Date(0);
|
|
9
|
+
if (typeof dateValue === "string") return new Date(dateValue);
|
|
10
|
+
if (dateValue.toDate) return dateValue.toDate();
|
|
11
|
+
if (dateValue.toISOString) return new Date(dateValue.toISOString());
|
|
12
|
+
return new Date(dateValue);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Test entity with timestamp fields
|
|
16
|
+
const articleEntity = $entity({
|
|
17
|
+
name: "articles",
|
|
18
|
+
schema: t.object({
|
|
19
|
+
id: pg.primaryKey(),
|
|
20
|
+
title: t.string(),
|
|
21
|
+
content: t.text(),
|
|
22
|
+
status: t.string(),
|
|
23
|
+
createdAt: pg.createdAt(),
|
|
24
|
+
updatedAt: pg.updatedAt(),
|
|
25
|
+
}),
|
|
26
|
+
indexes: [
|
|
27
|
+
"createdAt",
|
|
28
|
+
"updatedAt",
|
|
29
|
+
{
|
|
30
|
+
columns: ["status", "createdAt"],
|
|
31
|
+
name: "status_created_idx",
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const userActivityEntity = $entity({
|
|
37
|
+
name: "user_activities",
|
|
38
|
+
schema: t.object({
|
|
39
|
+
id: pg.primaryKey(),
|
|
40
|
+
userId: t.integer(),
|
|
41
|
+
action: t.string(),
|
|
42
|
+
metadata: t.optional(t.record(t.string(), t.any())),
|
|
43
|
+
createdAt: pg.createdAt(),
|
|
44
|
+
updatedAt: t.optional(pg.updatedAt()),
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
class TimestampTestApp {
|
|
49
|
+
articles = $repository(articleEntity);
|
|
50
|
+
activities = $repository(userActivityEntity);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Shared test function that runs on both PostgreSQL and SQLite
|
|
55
|
+
*/
|
|
56
|
+
const testTimestamps = async (alepha: Alepha) => {
|
|
57
|
+
const app = alepha.inject(TimestampTestApp);
|
|
58
|
+
await alepha.start();
|
|
59
|
+
|
|
60
|
+
// Test 1: createdAt should be automatically set on insert
|
|
61
|
+
const beforeCreate = new Date();
|
|
62
|
+
|
|
63
|
+
const article1 = await app.articles.create({
|
|
64
|
+
title: "First Article",
|
|
65
|
+
content: "This is the first article content",
|
|
66
|
+
status: "draft",
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const afterCreate = new Date();
|
|
70
|
+
|
|
71
|
+
expect(article1.id).toBeDefined();
|
|
72
|
+
expect(article1.createdAt).toBeDefined();
|
|
73
|
+
expect(article1.updatedAt).toBeDefined();
|
|
74
|
+
|
|
75
|
+
// Parse the dates - they come as dayjs objects from DrizzleSchemaCodec
|
|
76
|
+
const createdDate = parseDate(article1.createdAt);
|
|
77
|
+
const updatedDate = parseDate(article1.updatedAt);
|
|
78
|
+
|
|
79
|
+
// createdAt should be between beforeCreate and afterCreate
|
|
80
|
+
expect(createdDate.getTime()).toBeGreaterThanOrEqual(
|
|
81
|
+
beforeCreate.getTime() - 1000,
|
|
82
|
+
); // Allow 1s margin
|
|
83
|
+
expect(createdDate.getTime()).toBeLessThanOrEqual(
|
|
84
|
+
afterCreate.getTime() + 1000,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// Initially, updatedAt should equal createdAt
|
|
88
|
+
expect(Math.abs(createdDate.getTime() - updatedDate.getTime())).toBeLessThan(
|
|
89
|
+
1000,
|
|
90
|
+
); // Within 1 second
|
|
91
|
+
|
|
92
|
+
// Test 2: Wait a bit and update the record
|
|
93
|
+
await new Promise((resolve) => setTimeout(resolve, 100)); // Wait 100ms
|
|
94
|
+
|
|
95
|
+
const beforeUpdate = new Date();
|
|
96
|
+
|
|
97
|
+
const updatedArticle = await app.articles.updateById(article1.id, {
|
|
98
|
+
status: "published",
|
|
99
|
+
title: "Updated First Article",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const afterUpdate = new Date();
|
|
103
|
+
|
|
104
|
+
expect(updatedArticle.title).toBe("Updated First Article");
|
|
105
|
+
expect(updatedArticle.status).toBe("published");
|
|
106
|
+
|
|
107
|
+
const newUpdatedDate = parseDate(updatedArticle.updatedAt);
|
|
108
|
+
const unchangedCreatedDate = parseDate(updatedArticle.createdAt);
|
|
109
|
+
|
|
110
|
+
// createdAt should NOT change
|
|
111
|
+
expect(unchangedCreatedDate.getTime()).toBe(createdDate.getTime());
|
|
112
|
+
|
|
113
|
+
// updatedAt should be updated and be later than createdAt
|
|
114
|
+
expect(newUpdatedDate.getTime()).toBeGreaterThan(createdDate.getTime());
|
|
115
|
+
expect(newUpdatedDate.getTime()).toBeGreaterThanOrEqual(
|
|
116
|
+
beforeUpdate.getTime() - 1000,
|
|
117
|
+
);
|
|
118
|
+
expect(newUpdatedDate.getTime()).toBeLessThanOrEqual(
|
|
119
|
+
afterUpdate.getTime() + 1000,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// Test 3: Create multiple articles and verify they have different timestamps
|
|
123
|
+
const articles = await Promise.all([
|
|
124
|
+
app.articles.create({
|
|
125
|
+
title: "Article 2",
|
|
126
|
+
content: "Content 2",
|
|
127
|
+
status: "draft",
|
|
128
|
+
}),
|
|
129
|
+
app.articles.create({
|
|
130
|
+
title: "Article 3",
|
|
131
|
+
content: "Content 3",
|
|
132
|
+
status: "published",
|
|
133
|
+
}),
|
|
134
|
+
app.articles.create({
|
|
135
|
+
title: "Article 4",
|
|
136
|
+
content: "Content 4",
|
|
137
|
+
status: "archived",
|
|
138
|
+
}),
|
|
139
|
+
]);
|
|
140
|
+
|
|
141
|
+
// All should have createdAt and updatedAt
|
|
142
|
+
for (const article of articles) {
|
|
143
|
+
expect(article.createdAt).toBeDefined();
|
|
144
|
+
expect(article.updatedAt).toBeDefined();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Test 4: Query using createdAt ordering
|
|
148
|
+
const sortedByCreated = await app.articles.findMany({
|
|
149
|
+
orderBy: { column: "createdAt", direction: "asc" },
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// First article should be article1 (created first)
|
|
153
|
+
expect(sortedByCreated[0].id).toBe(article1.id);
|
|
154
|
+
|
|
155
|
+
// Verify the order is correct
|
|
156
|
+
for (let i = 1; i < sortedByCreated.length; i++) {
|
|
157
|
+
const prevDate = parseDate(sortedByCreated[i - 1].createdAt);
|
|
158
|
+
const currDate = parseDate(sortedByCreated[i].createdAt);
|
|
159
|
+
expect(currDate.getTime()).toBeGreaterThanOrEqual(prevDate.getTime());
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Test 5: Query with createdAt filters
|
|
163
|
+
// Use the actual createdAt value from the first article (already a Dayjs object)
|
|
164
|
+
const recentArticles = await app.articles.findMany({
|
|
165
|
+
where: {
|
|
166
|
+
createdAt: {
|
|
167
|
+
gte: article1.createdAt as any, // Cast to any since it's actually compatible at runtime
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
expect(recentArticles.length).toBeGreaterThan(0);
|
|
173
|
+
expect(recentArticles.some((a) => a.id === article1.id)).toBe(true);
|
|
174
|
+
|
|
175
|
+
// Test 6: Test with user activities (optional updatedAt)
|
|
176
|
+
const activity = await app.activities.create({
|
|
177
|
+
userId: 1,
|
|
178
|
+
action: "login",
|
|
179
|
+
metadata: { ip: "192.168.1.1", browser: "Chrome" },
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(activity.createdAt).toBeDefined();
|
|
183
|
+
// updatedAt is optional, might be null or undefined initially
|
|
184
|
+
|
|
185
|
+
// Test 7: Batch operations should maintain timestamps
|
|
186
|
+
const batchArticles = await Promise.all([
|
|
187
|
+
app.articles.create({
|
|
188
|
+
title: "Batch 1",
|
|
189
|
+
content: "Batch content 1",
|
|
190
|
+
status: "draft",
|
|
191
|
+
}),
|
|
192
|
+
app.articles.create({
|
|
193
|
+
title: "Batch 2",
|
|
194
|
+
content: "Batch content 2",
|
|
195
|
+
status: "draft",
|
|
196
|
+
}),
|
|
197
|
+
]);
|
|
198
|
+
|
|
199
|
+
// Update them all at once
|
|
200
|
+
await new Promise((resolve) => setTimeout(resolve, 100)); // Wait 100ms
|
|
201
|
+
|
|
202
|
+
const batchUpdates = await Promise.all(
|
|
203
|
+
batchArticles.map((article) =>
|
|
204
|
+
app.articles.updateById(article.id, {
|
|
205
|
+
status: "published",
|
|
206
|
+
}),
|
|
207
|
+
),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// All should have updated timestamps
|
|
211
|
+
for (const updated of batchUpdates) {
|
|
212
|
+
const createdTime = parseDate(updated.createdAt).getTime();
|
|
213
|
+
const updatedTime = parseDate(updated.updatedAt).getTime();
|
|
214
|
+
expect(updatedTime).toBeGreaterThan(createdTime);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Test 8: Query using composite index with timestamps
|
|
218
|
+
const statusCreatedQuery = await app.articles.findMany({
|
|
219
|
+
where: {
|
|
220
|
+
status: { eq: "published" },
|
|
221
|
+
},
|
|
222
|
+
orderBy: "createdAt",
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
expect(statusCreatedQuery.length).toBeGreaterThan(0);
|
|
226
|
+
expect(statusCreatedQuery.every((a) => a.status === "published")).toBe(true);
|
|
227
|
+
|
|
228
|
+
// Clean up
|
|
229
|
+
await app.articles.clear({ force: true });
|
|
230
|
+
await app.activities.clear({ force: true });
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
describe("Timestamp Fields (createdAt/updatedAt)", () => {
|
|
234
|
+
it("should handle timestamps correctly in PostgreSQL", async () => {
|
|
235
|
+
await testTimestamps(Alepha.create());
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("should handle timestamps correctly in SQLite", async () => {
|
|
239
|
+
await testTimestamps(
|
|
240
|
+
Alepha.create({
|
|
241
|
+
env: {
|
|
242
|
+
DATABASE_URL: "sqlite://:memory:",
|
|
243
|
+
},
|
|
244
|
+
}),
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("should create proper indexes on timestamp fields", async () => {
|
|
249
|
+
const alepha = Alepha.create();
|
|
250
|
+
const app = alepha.inject(TimestampTestApp);
|
|
251
|
+
await alepha.start();
|
|
252
|
+
|
|
253
|
+
// Create some test data
|
|
254
|
+
const article = await app.articles.create({
|
|
255
|
+
title: "Index Test Article",
|
|
256
|
+
content: "Testing indexes on timestamp fields",
|
|
257
|
+
status: "draft",
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Query using the createdAt index - use range query instead of exact match
|
|
261
|
+
// due to potential microsecond precision differences
|
|
262
|
+
const createdTime = parseDate(article.createdAt);
|
|
263
|
+
const startTime = new Date(createdTime.getTime() - 1000); // 1 second before
|
|
264
|
+
const endTime = new Date(createdTime.getTime() + 1000); // 1 second after
|
|
265
|
+
|
|
266
|
+
const byCreated = await app.articles.findMany({
|
|
267
|
+
where: {
|
|
268
|
+
createdAt: {
|
|
269
|
+
gte: startTime.toISOString() as any,
|
|
270
|
+
lte: endTime.toISOString() as any,
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
expect(byCreated.length).toBeGreaterThan(0);
|
|
276
|
+
expect(byCreated.some((a) => a.id === article.id)).toBe(true);
|
|
277
|
+
|
|
278
|
+
// Query using the composite index
|
|
279
|
+
const byStatusAndCreated = await app.articles.findMany({
|
|
280
|
+
where: {
|
|
281
|
+
status: { eq: "draft" },
|
|
282
|
+
createdAt: { gte: startTime.toISOString() as any },
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
expect(byStatusAndCreated.length).toBeGreaterThan(0);
|
|
287
|
+
expect(byStatusAndCreated.some((a) => a.id === article.id)).toBe(true);
|
|
288
|
+
|
|
289
|
+
await app.articles.clear({ force: true });
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("should handle timestamps with different formats", async () => {
|
|
293
|
+
// Test entity with custom timestamp configuration
|
|
294
|
+
const eventEntity = $entity({
|
|
295
|
+
name: "events",
|
|
296
|
+
schema: t.object({
|
|
297
|
+
id: pg.primaryKey(),
|
|
298
|
+
name: t.string(),
|
|
299
|
+
startTime: pg.createdAt(),
|
|
300
|
+
endTime: t.optional(pg.updatedAt()),
|
|
301
|
+
scheduledAt: t.string(), // Regular string field for ISO date
|
|
302
|
+
}),
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
class EventApp {
|
|
306
|
+
events = $repository(eventEntity);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const alepha = Alepha.create();
|
|
310
|
+
const app = alepha.inject(EventApp);
|
|
311
|
+
await alepha.start();
|
|
312
|
+
|
|
313
|
+
const now = new Date();
|
|
314
|
+
const event = await app.events.create({
|
|
315
|
+
name: "Test Event",
|
|
316
|
+
scheduledAt: now.toISOString(),
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
expect(event.startTime).toBeDefined();
|
|
320
|
+
expect(event.scheduledAt).toBeDefined();
|
|
321
|
+
|
|
322
|
+
// startTime should be auto-generated
|
|
323
|
+
const startTime = parseDate(event.startTime);
|
|
324
|
+
expect(startTime).toBeInstanceOf(Date);
|
|
325
|
+
expect(startTime.getTime()).toBeGreaterThan(0);
|
|
326
|
+
|
|
327
|
+
// scheduledAt should match what we provided
|
|
328
|
+
expect(event.scheduledAt).toBe(now.toISOString());
|
|
329
|
+
|
|
330
|
+
await app.events.clear({ force: true });
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it("should handle bulk inserts with timestamps", async () => {
|
|
334
|
+
const alepha = Alepha.create();
|
|
335
|
+
const app = alepha.inject(TimestampTestApp);
|
|
336
|
+
await alepha.start();
|
|
337
|
+
|
|
338
|
+
const beforeBulk = new Date();
|
|
339
|
+
|
|
340
|
+
// Create multiple articles at once
|
|
341
|
+
const articles = await Promise.all(
|
|
342
|
+
Array.from({ length: 10 }, (_, i) =>
|
|
343
|
+
app.articles.create({
|
|
344
|
+
title: `Bulk Article ${i}`,
|
|
345
|
+
content: `Content for article ${i}`,
|
|
346
|
+
status: i % 2 === 0 ? "draft" : "published",
|
|
347
|
+
}),
|
|
348
|
+
),
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const afterBulk = new Date();
|
|
352
|
+
|
|
353
|
+
expect(articles).toHaveLength(10);
|
|
354
|
+
|
|
355
|
+
// All should have timestamps within the expected range
|
|
356
|
+
for (const article of articles) {
|
|
357
|
+
const created = parseDate(article.createdAt);
|
|
358
|
+
const updated = parseDate(article.updatedAt);
|
|
359
|
+
|
|
360
|
+
expect(created.getTime()).toBeGreaterThanOrEqual(
|
|
361
|
+
beforeBulk.getTime() - 1000,
|
|
362
|
+
);
|
|
363
|
+
expect(created.getTime()).toBeLessThanOrEqual(afterBulk.getTime() + 1000);
|
|
364
|
+
expect(Math.abs(created.getTime() - updated.getTime())).toBeLessThan(
|
|
365
|
+
1000,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Query and verify ordering
|
|
370
|
+
const ordered = await app.articles.findMany({
|
|
371
|
+
orderBy: [
|
|
372
|
+
{ column: "createdAt", direction: "asc" },
|
|
373
|
+
{ column: "id", direction: "asc" },
|
|
374
|
+
],
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
expect(ordered).toHaveLength(10);
|
|
378
|
+
|
|
379
|
+
// Verify chronological order
|
|
380
|
+
for (let i = 1; i < ordered.length; i++) {
|
|
381
|
+
const prev = parseDate(ordered[i - 1].createdAt);
|
|
382
|
+
const curr = parseDate(ordered[i].createdAt);
|
|
383
|
+
expect(curr.getTime()).toBeGreaterThanOrEqual(prev.getTime());
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
await app.articles.clear({ force: true });
|
|
387
|
+
});
|
|
388
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { Alepha, t } from "alepha";
|
|
2
|
+
import { test } from "vitest";
|
|
3
|
+
import { $entity, $repository, pg } from "../index.ts";
|
|
4
|
+
|
|
5
|
+
const TestEntity = $entity({
|
|
6
|
+
name: "test_validation",
|
|
7
|
+
schema: t.object({
|
|
8
|
+
id: pg.primaryKey(),
|
|
9
|
+
createdAt: pg.createdAt(),
|
|
10
|
+
updatedAt: pg.updatedAt(),
|
|
11
|
+
name: t.text(),
|
|
12
|
+
age: t.number(),
|
|
13
|
+
status: t.text(),
|
|
14
|
+
}),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
class App {
|
|
18
|
+
items = $repository(TestEntity);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test("between operator validation - requires exactly 2 values", async ({
|
|
22
|
+
expect,
|
|
23
|
+
}) => {
|
|
24
|
+
const alepha = Alepha.create();
|
|
25
|
+
const app = alepha.inject(App);
|
|
26
|
+
await alepha.start();
|
|
27
|
+
|
|
28
|
+
const repository = app.items;
|
|
29
|
+
|
|
30
|
+
// Insert test data
|
|
31
|
+
await repository.create({ name: "Item 1", age: 10, status: "active" });
|
|
32
|
+
await repository.create({ name: "Item 2", age: 20, status: "active" });
|
|
33
|
+
await repository.create({ name: "Item 3", age: 30, status: "active" });
|
|
34
|
+
|
|
35
|
+
// Test 1: Valid between with exactly 2 values should work
|
|
36
|
+
const result1 = await repository.findMany({
|
|
37
|
+
where: { age: { between: [15, 25] } },
|
|
38
|
+
});
|
|
39
|
+
expect(result1).toHaveLength(1);
|
|
40
|
+
expect(result1[0].name).toBe("Item 2");
|
|
41
|
+
|
|
42
|
+
// Test 2: Empty array should throw
|
|
43
|
+
await expect(async () => {
|
|
44
|
+
await repository.findMany({
|
|
45
|
+
where: { age: { between: [] as any } },
|
|
46
|
+
});
|
|
47
|
+
}).rejects.toThrow("between operator requires exactly 2 values [min, max]");
|
|
48
|
+
|
|
49
|
+
// Test 3: Single value should throw
|
|
50
|
+
await expect(async () => {
|
|
51
|
+
await repository.findMany({
|
|
52
|
+
where: { age: { between: [15] as any } },
|
|
53
|
+
});
|
|
54
|
+
}).rejects.toThrow("between operator requires exactly 2 values [min, max]");
|
|
55
|
+
|
|
56
|
+
// Test 4: More than 2 values should throw
|
|
57
|
+
await expect(async () => {
|
|
58
|
+
await repository.findMany({
|
|
59
|
+
where: { age: { between: [15, 25, 35] as any } },
|
|
60
|
+
});
|
|
61
|
+
}).rejects.toThrow("between operator requires exactly 2 values [min, max]");
|
|
62
|
+
|
|
63
|
+
// Test 5: Non-array should throw
|
|
64
|
+
await expect(async () => {
|
|
65
|
+
await repository.findMany({
|
|
66
|
+
where: { age: { between: 15 as any } },
|
|
67
|
+
});
|
|
68
|
+
}).rejects.toThrow("between operator requires exactly 2 values [min, max]");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("notBetween operator validation - requires exactly 2 values", async ({
|
|
72
|
+
expect,
|
|
73
|
+
}) => {
|
|
74
|
+
const alepha = Alepha.create();
|
|
75
|
+
const app = alepha.inject(App);
|
|
76
|
+
await alepha.start();
|
|
77
|
+
|
|
78
|
+
const repository = app.items;
|
|
79
|
+
|
|
80
|
+
// Insert test data
|
|
81
|
+
await repository.create({ name: "Item 1", age: 10, status: "active" });
|
|
82
|
+
await repository.create({ name: "Item 2", age: 20, status: "active" });
|
|
83
|
+
await repository.create({ name: "Item 3", age: 30, status: "active" });
|
|
84
|
+
|
|
85
|
+
// Test 1: Valid notBetween with exactly 2 values should work
|
|
86
|
+
const result1 = await repository.findMany({
|
|
87
|
+
where: { age: { notBetween: [15, 25] } },
|
|
88
|
+
orderBy: { column: "age", direction: "asc" },
|
|
89
|
+
});
|
|
90
|
+
expect(result1).toHaveLength(2);
|
|
91
|
+
expect(result1.map((r: any) => r.name)).toEqual(["Item 1", "Item 3"]);
|
|
92
|
+
|
|
93
|
+
// Test 2: Empty array should throw
|
|
94
|
+
await expect(async () => {
|
|
95
|
+
await repository.findMany({
|
|
96
|
+
where: { age: { notBetween: [] as any } },
|
|
97
|
+
});
|
|
98
|
+
}).rejects.toThrow(
|
|
99
|
+
"notBetween operator requires exactly 2 values [min, max]",
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Test 3: Single value should throw
|
|
103
|
+
await expect(async () => {
|
|
104
|
+
await repository.findMany({
|
|
105
|
+
where: { age: { notBetween: [15] as any } },
|
|
106
|
+
});
|
|
107
|
+
}).rejects.toThrow(
|
|
108
|
+
"notBetween operator requires exactly 2 values [min, max]",
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("inArray operator validation - requires at least one value", async ({
|
|
113
|
+
expect,
|
|
114
|
+
}) => {
|
|
115
|
+
const alepha = Alepha.create();
|
|
116
|
+
const app = alepha.inject(App);
|
|
117
|
+
await alepha.start();
|
|
118
|
+
|
|
119
|
+
const repository = app.items;
|
|
120
|
+
|
|
121
|
+
// Insert test data
|
|
122
|
+
await repository.create({ name: "Item 1", age: 10, status: "active" });
|
|
123
|
+
await repository.create({ name: "Item 2", age: 20, status: "pending" });
|
|
124
|
+
await repository.create({ name: "Item 3", age: 30, status: "inactive" });
|
|
125
|
+
|
|
126
|
+
// Test 1: Valid inArray should work
|
|
127
|
+
const result1 = await repository.findMany({
|
|
128
|
+
where: { status: { inArray: ["active", "pending"] } },
|
|
129
|
+
orderBy: { column: "age", direction: "asc" },
|
|
130
|
+
});
|
|
131
|
+
expect(result1).toHaveLength(2);
|
|
132
|
+
expect(result1.map((r: any) => r.status)).toEqual(["active", "pending"]);
|
|
133
|
+
|
|
134
|
+
// Test 2: Empty array should throw
|
|
135
|
+
await expect(async () => {
|
|
136
|
+
await repository.findMany({
|
|
137
|
+
where: { status: { inArray: [] } },
|
|
138
|
+
});
|
|
139
|
+
}).rejects.toThrow("inArray operator requires at least one value");
|
|
140
|
+
|
|
141
|
+
// Test 3: Non-array should throw
|
|
142
|
+
await expect(async () => {
|
|
143
|
+
await repository.findMany({
|
|
144
|
+
where: { status: { inArray: "active" as any } },
|
|
145
|
+
});
|
|
146
|
+
}).rejects.toThrow("inArray operator requires at least one value");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("notInArray operator validation - requires at least one value", async ({
|
|
150
|
+
expect,
|
|
151
|
+
}) => {
|
|
152
|
+
const alepha = Alepha.create();
|
|
153
|
+
const app = alepha.inject(App);
|
|
154
|
+
await alepha.start();
|
|
155
|
+
|
|
156
|
+
const repository = app.items;
|
|
157
|
+
|
|
158
|
+
// Insert test data
|
|
159
|
+
await repository.create({ name: "Item 1", age: 10, status: "active" });
|
|
160
|
+
await repository.create({ name: "Item 2", age: 20, status: "pending" });
|
|
161
|
+
await repository.create({ name: "Item 3", age: 30, status: "inactive" });
|
|
162
|
+
|
|
163
|
+
// Test 1: Valid notInArray should work
|
|
164
|
+
const result1 = await repository.findMany({
|
|
165
|
+
where: { status: { notInArray: ["active", "pending"] } },
|
|
166
|
+
});
|
|
167
|
+
expect(result1).toHaveLength(1);
|
|
168
|
+
expect(result1[0].status).toBe("inactive");
|
|
169
|
+
|
|
170
|
+
// Test 2: Empty array should throw
|
|
171
|
+
await expect(async () => {
|
|
172
|
+
await repository.findMany({
|
|
173
|
+
where: { status: { notInArray: [] } },
|
|
174
|
+
});
|
|
175
|
+
}).rejects.toThrow("notInArray operator requires at least one value");
|
|
176
|
+
|
|
177
|
+
// Test 3: Non-array should throw
|
|
178
|
+
await expect(async () => {
|
|
179
|
+
await repository.findMany({
|
|
180
|
+
where: { status: { notInArray: "active" as any } },
|
|
181
|
+
});
|
|
182
|
+
}).rejects.toThrow("notInArray operator requires at least one value");
|
|
183
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { $inject, Alepha, t } from "alepha";
|
|
2
|
+
import { DateTimeProvider } from "alepha/datetime";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { DbVersionMismatchError } from "../errors/DbVersionMismatchError.ts";
|
|
5
|
+
import { $entity, $repository, pg, type TransactionContext } from "../index.ts";
|
|
6
|
+
|
|
7
|
+
class A {
|
|
8
|
+
dt = $inject(DateTimeProvider);
|
|
9
|
+
|
|
10
|
+
repository = $repository(
|
|
11
|
+
$entity({
|
|
12
|
+
name: "a",
|
|
13
|
+
schema: t.object({
|
|
14
|
+
id: pg.primaryKey(),
|
|
15
|
+
counter: t.integer(),
|
|
16
|
+
__v: pg.version(),
|
|
17
|
+
}),
|
|
18
|
+
}),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
incFn = async (
|
|
22
|
+
id: number,
|
|
23
|
+
val: number,
|
|
24
|
+
waitMs = 0,
|
|
25
|
+
tx?: TransactionContext,
|
|
26
|
+
) => {
|
|
27
|
+
const { counter } = await this.repository.findById(id, {
|
|
28
|
+
tx,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (waitMs) {
|
|
32
|
+
await this.dt.wait(waitMs);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return await this.repository.updateById(
|
|
36
|
+
id,
|
|
37
|
+
{
|
|
38
|
+
counter: counter + val,
|
|
39
|
+
},
|
|
40
|
+
{ tx },
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe("version", () => {
|
|
46
|
+
it("should detect version mismatch on concurrent updates", async () => {
|
|
47
|
+
const alepha = Alepha.create();
|
|
48
|
+
const app = alepha.inject(A);
|
|
49
|
+
await alepha.start();
|
|
50
|
+
|
|
51
|
+
const { id } = await app.repository.create({ counter: 0 });
|
|
52
|
+
const r1 = await app.repository.findById(id);
|
|
53
|
+
const r2 = await app.repository.findById(id);
|
|
54
|
+
|
|
55
|
+
r1.counter += 1;
|
|
56
|
+
r2.counter += 1;
|
|
57
|
+
|
|
58
|
+
await app.repository.save(r1);
|
|
59
|
+
|
|
60
|
+
await expect(() => app.repository.save(r2)).rejects.toThrow(
|
|
61
|
+
new DbVersionMismatchError("a", id),
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
});
|