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
|
@@ -1,35 +1,24 @@
|
|
|
1
1
|
import { $inject, t } from "alepha";
|
|
2
2
|
import { $action } from "alepha/server";
|
|
3
3
|
import type { ParameterStatus } from "../entities/parameters.ts";
|
|
4
|
+
import {
|
|
5
|
+
activateConfigBodySchema,
|
|
6
|
+
checkScheduledResponseSchema,
|
|
7
|
+
configCurrentResponseSchema,
|
|
8
|
+
configHistoryResponseSchema,
|
|
9
|
+
configNameParamSchema,
|
|
10
|
+
configNamesResponseSchema,
|
|
11
|
+
configsByStatusResponseSchema,
|
|
12
|
+
configTreeNodeSchema,
|
|
13
|
+
configVersionParamSchema,
|
|
14
|
+
configVersionResponseSchema,
|
|
15
|
+
createConfigVersionBodySchema,
|
|
16
|
+
parameterResponseSchema,
|
|
17
|
+
rollbackConfigBodySchema,
|
|
18
|
+
statusParamSchema,
|
|
19
|
+
} from "../schemas/index.ts";
|
|
4
20
|
import { ConfigStore } from "../services/ConfigStore.ts";
|
|
5
21
|
|
|
6
|
-
// Define response schemas inline to avoid complex entity schema issues
|
|
7
|
-
const parameterResponseSchema = t.object({
|
|
8
|
-
id: t.uuid(),
|
|
9
|
-
createdAt: t.datetime(),
|
|
10
|
-
updatedAt: t.datetime(),
|
|
11
|
-
name: t.text(),
|
|
12
|
-
content: t.json(),
|
|
13
|
-
schemaHash: t.text(),
|
|
14
|
-
status: t.enum(["expired", "current", "next", "future"]),
|
|
15
|
-
activationDate: t.datetime(),
|
|
16
|
-
expiredAt: t.optional(t.datetime()),
|
|
17
|
-
version: t.integer(),
|
|
18
|
-
changeDescription: t.optional(t.text()),
|
|
19
|
-
tags: t.optional(t.array(t.text())),
|
|
20
|
-
creatorId: t.optional(t.uuid()),
|
|
21
|
-
creatorName: t.optional(t.text()),
|
|
22
|
-
previousContent: t.optional(t.json()),
|
|
23
|
-
migrationLog: t.optional(t.text()),
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const treeNodeSchema: any = t.object({
|
|
27
|
-
name: t.text(),
|
|
28
|
-
path: t.text(),
|
|
29
|
-
isLeaf: t.boolean(),
|
|
30
|
-
children: t.array(t.any()),
|
|
31
|
-
});
|
|
32
|
-
|
|
33
22
|
/**
|
|
34
23
|
* REST API controller for versioned configuration management.
|
|
35
24
|
*
|
|
@@ -41,7 +30,9 @@ const treeNodeSchema: any = t.object({
|
|
|
41
30
|
* - Rolling back to previous versions
|
|
42
31
|
* - Activating scheduled versions immediately
|
|
43
32
|
*/
|
|
44
|
-
export class
|
|
33
|
+
export class AdminConfigController {
|
|
34
|
+
protected readonly url = "/configs";
|
|
35
|
+
protected readonly group = "admin:configs";
|
|
45
36
|
protected readonly store = $inject(ConfigStore);
|
|
46
37
|
|
|
47
38
|
/**
|
|
@@ -49,12 +40,13 @@ export class ConfigController {
|
|
|
49
40
|
* Useful for admin UI navigation.
|
|
50
41
|
*/
|
|
51
42
|
getConfigTree = $action({
|
|
43
|
+
group: this.group,
|
|
52
44
|
description:
|
|
53
45
|
"Get tree structure of all configuration names for navigation.",
|
|
54
46
|
path: "/configs/tree",
|
|
55
47
|
method: "GET",
|
|
56
48
|
schema: {
|
|
57
|
-
response: t.array(
|
|
49
|
+
response: t.array(configTreeNodeSchema),
|
|
58
50
|
},
|
|
59
51
|
handler: async () => {
|
|
60
52
|
return this.store.getConfigTree();
|
|
@@ -65,13 +57,12 @@ export class ConfigController {
|
|
|
65
57
|
* List all unique configuration names.
|
|
66
58
|
*/
|
|
67
59
|
listConfigNames = $action({
|
|
60
|
+
group: this.group,
|
|
68
61
|
description: "List all unique configuration names.",
|
|
69
62
|
path: "/configs",
|
|
70
63
|
method: "GET",
|
|
71
64
|
schema: {
|
|
72
|
-
response:
|
|
73
|
-
names: t.array(t.text()),
|
|
74
|
-
}),
|
|
65
|
+
response: configNamesResponseSchema,
|
|
75
66
|
},
|
|
76
67
|
handler: async () => {
|
|
77
68
|
const names = await this.store.getConfigNames();
|
|
@@ -83,16 +74,13 @@ export class ConfigController {
|
|
|
83
74
|
* Get configurations by status.
|
|
84
75
|
*/
|
|
85
76
|
getByStatus = $action({
|
|
77
|
+
group: this.group,
|
|
86
78
|
description: "Get all configurations with a specific status.",
|
|
87
79
|
path: "/configs/status/:status",
|
|
88
80
|
method: "GET",
|
|
89
81
|
schema: {
|
|
90
|
-
params:
|
|
91
|
-
|
|
92
|
-
}),
|
|
93
|
-
response: t.object({
|
|
94
|
-
configs: t.array(parameterResponseSchema),
|
|
95
|
-
}),
|
|
82
|
+
params: statusParamSchema,
|
|
83
|
+
response: configsByStatusResponseSchema,
|
|
96
84
|
},
|
|
97
85
|
handler: async ({ params }) => {
|
|
98
86
|
const configs = await this.store.getByStatus(
|
|
@@ -106,18 +94,13 @@ export class ConfigController {
|
|
|
106
94
|
* Get version history for a specific configuration.
|
|
107
95
|
*/
|
|
108
96
|
getHistory = $action({
|
|
97
|
+
group: this.group,
|
|
109
98
|
description: "Get all versions of a specific configuration.",
|
|
110
99
|
path: "/configs/:name/history",
|
|
111
100
|
method: "GET",
|
|
112
101
|
schema: {
|
|
113
|
-
params:
|
|
114
|
-
|
|
115
|
-
description: "Configuration name (e.g., app.features.flags)",
|
|
116
|
-
}),
|
|
117
|
-
}),
|
|
118
|
-
response: t.object({
|
|
119
|
-
versions: t.array(parameterResponseSchema),
|
|
120
|
-
}),
|
|
102
|
+
params: configNameParamSchema,
|
|
103
|
+
response: configHistoryResponseSchema,
|
|
121
104
|
},
|
|
122
105
|
handler: async ({ params }) => {
|
|
123
106
|
const versions = await this.store.getHistory(params.name);
|
|
@@ -131,22 +114,13 @@ export class ConfigController {
|
|
|
131
114
|
* even if no versions exist in the database yet.
|
|
132
115
|
*/
|
|
133
116
|
getCurrent = $action({
|
|
117
|
+
group: this.group,
|
|
134
118
|
description: "Get current and next scheduled values for a configuration.",
|
|
135
119
|
path: "/configs/:name",
|
|
136
120
|
method: "GET",
|
|
137
121
|
schema: {
|
|
138
|
-
params:
|
|
139
|
-
|
|
140
|
-
description: "Configuration name (e.g., app.features.flags)",
|
|
141
|
-
}),
|
|
142
|
-
}),
|
|
143
|
-
response: t.object({
|
|
144
|
-
current: t.optional(parameterResponseSchema),
|
|
145
|
-
next: t.optional(parameterResponseSchema),
|
|
146
|
-
defaultValue: t.optional(t.json()),
|
|
147
|
-
currentValue: t.optional(t.json()),
|
|
148
|
-
schema: t.optional(t.json()),
|
|
149
|
-
}),
|
|
122
|
+
params: configNameParamSchema,
|
|
123
|
+
response: configCurrentResponseSchema,
|
|
150
124
|
},
|
|
151
125
|
handler: async ({ params }) => {
|
|
152
126
|
const result = await this.store.getCurrentWithDefault(params.name);
|
|
@@ -164,17 +138,13 @@ export class ConfigController {
|
|
|
164
138
|
* Get a specific version of a configuration.
|
|
165
139
|
*/
|
|
166
140
|
getVersion = $action({
|
|
141
|
+
group: this.group,
|
|
167
142
|
description: "Get a specific version of a configuration.",
|
|
168
143
|
path: "/configs/:name/versions/:version",
|
|
169
144
|
method: "GET",
|
|
170
145
|
schema: {
|
|
171
|
-
params:
|
|
172
|
-
|
|
173
|
-
version: t.integer(),
|
|
174
|
-
}),
|
|
175
|
-
response: t.object({
|
|
176
|
-
config: t.optional(parameterResponseSchema),
|
|
177
|
-
}),
|
|
146
|
+
params: configVersionParamSchema,
|
|
147
|
+
response: configVersionResponseSchema,
|
|
178
148
|
},
|
|
179
149
|
handler: async ({ params }) => {
|
|
180
150
|
const config = await this.store.getVersion(params.name, params.version);
|
|
@@ -186,31 +156,14 @@ export class ConfigController {
|
|
|
186
156
|
* Create a new configuration version.
|
|
187
157
|
*/
|
|
188
158
|
createVersion = $action({
|
|
159
|
+
group: this.group,
|
|
189
160
|
description:
|
|
190
161
|
"Create a new version of a configuration (immediate or scheduled).",
|
|
191
162
|
path: "/configs/:name",
|
|
192
163
|
method: "POST",
|
|
193
164
|
schema: {
|
|
194
|
-
params:
|
|
195
|
-
|
|
196
|
-
description: "Configuration name (e.g., app.features.flags)",
|
|
197
|
-
}),
|
|
198
|
-
}),
|
|
199
|
-
body: t.object({
|
|
200
|
-
content: t.json({ description: "New configuration content" }),
|
|
201
|
-
schemaHash: t.text({
|
|
202
|
-
description: "Hash of the schema for migration detection",
|
|
203
|
-
}),
|
|
204
|
-
activationDate: t.optional(
|
|
205
|
-
t.datetime({ description: "When to activate (default: now)" }),
|
|
206
|
-
),
|
|
207
|
-
changeDescription: t.optional(
|
|
208
|
-
t.text({ description: "Description of changes" }),
|
|
209
|
-
),
|
|
210
|
-
tags: t.optional(t.array(t.text())),
|
|
211
|
-
creatorId: t.optional(t.uuid()),
|
|
212
|
-
creatorName: t.optional(t.text()),
|
|
213
|
-
}),
|
|
165
|
+
params: configNameParamSchema,
|
|
166
|
+
body: createConfigVersionBodySchema,
|
|
214
167
|
response: parameterResponseSchema,
|
|
215
168
|
},
|
|
216
169
|
handler: async ({ params, body }) => {
|
|
@@ -230,22 +183,14 @@ export class ConfigController {
|
|
|
230
183
|
* Rollback to a previous version.
|
|
231
184
|
*/
|
|
232
185
|
rollback = $action({
|
|
186
|
+
group: this.group,
|
|
233
187
|
description:
|
|
234
188
|
"Rollback a configuration to a previous version (creates new version with old content).",
|
|
235
189
|
path: "/configs/:name/rollback",
|
|
236
190
|
method: "POST",
|
|
237
191
|
schema: {
|
|
238
|
-
params:
|
|
239
|
-
|
|
240
|
-
}),
|
|
241
|
-
body: t.object({
|
|
242
|
-
targetVersion: t.integer({
|
|
243
|
-
description: "Version number to rollback to",
|
|
244
|
-
}),
|
|
245
|
-
changeDescription: t.optional(t.text()),
|
|
246
|
-
creatorId: t.optional(t.uuid()),
|
|
247
|
-
creatorName: t.optional(t.text()),
|
|
248
|
-
}),
|
|
192
|
+
params: configNameParamSchema,
|
|
193
|
+
body: rollbackConfigBodySchema,
|
|
249
194
|
response: parameterResponseSchema,
|
|
250
195
|
},
|
|
251
196
|
handler: async ({ params, body }) => {
|
|
@@ -261,18 +206,13 @@ export class ConfigController {
|
|
|
261
206
|
* Activate a scheduled version immediately.
|
|
262
207
|
*/
|
|
263
208
|
activateNow = $action({
|
|
209
|
+
group: this.group,
|
|
264
210
|
description: "Activate a future/next configuration version immediately.",
|
|
265
211
|
path: "/configs/:name/activate",
|
|
266
212
|
method: "POST",
|
|
267
213
|
schema: {
|
|
268
|
-
params:
|
|
269
|
-
|
|
270
|
-
}),
|
|
271
|
-
body: t.object({
|
|
272
|
-
version: t.integer({ description: "Version number to activate" }),
|
|
273
|
-
creatorId: t.optional(t.uuid()),
|
|
274
|
-
creatorName: t.optional(t.text()),
|
|
275
|
-
}),
|
|
214
|
+
params: configNameParamSchema,
|
|
215
|
+
body: activateConfigBodySchema,
|
|
276
216
|
response: parameterResponseSchema,
|
|
277
217
|
},
|
|
278
218
|
handler: async ({ params, body }) => {
|
|
@@ -307,14 +247,13 @@ export class ConfigController {
|
|
|
307
247
|
* Normally called by a scheduler, but exposed for manual triggering.
|
|
308
248
|
*/
|
|
309
249
|
checkScheduled = $action({
|
|
250
|
+
group: this.group,
|
|
310
251
|
description:
|
|
311
252
|
"Manually trigger activation check for all scheduled configurations.",
|
|
312
253
|
path: "/configs/activate-scheduled",
|
|
313
254
|
method: "POST",
|
|
314
255
|
schema: {
|
|
315
|
-
response:
|
|
316
|
-
message: t.text(),
|
|
317
|
-
}),
|
|
256
|
+
response: checkScheduledResponseSchema,
|
|
318
257
|
},
|
|
319
258
|
handler: async () => {
|
|
320
259
|
await this.store.activateScheduledConfigs();
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import { type Static, t } from "alepha";
|
|
2
|
-
import { $entity,
|
|
2
|
+
import { $entity, db } from "alepha/orm";
|
|
3
|
+
import { parameterStatusSchema } from "../schemas/index.ts";
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
* Parameter status values.
|
|
6
|
-
*
|
|
7
|
-
* - EXPIRED: Past version, no longer active
|
|
8
|
-
* - CURRENT: Currently active version
|
|
9
|
-
* - NEXT: Scheduled to become active (closest future date)
|
|
10
|
-
* - FUTURE: Scheduled for activation after NEXT
|
|
11
|
-
*/
|
|
12
|
-
export type ParameterStatus = "expired" | "current" | "next" | "future";
|
|
5
|
+
export type { ParameterStatus } from "../schemas/index.ts";
|
|
13
6
|
|
|
14
7
|
/**
|
|
15
8
|
* Configuration parameter entity for versioned configuration management.
|
|
@@ -23,9 +16,9 @@ export type ParameterStatus = "expired" | "current" | "next" | "future";
|
|
|
23
16
|
export const parameters = $entity({
|
|
24
17
|
name: "parameters",
|
|
25
18
|
schema: t.object({
|
|
26
|
-
id:
|
|
27
|
-
createdAt:
|
|
28
|
-
updatedAt:
|
|
19
|
+
id: db.primaryKey(t.uuid()),
|
|
20
|
+
createdAt: db.createdAt(),
|
|
21
|
+
updatedAt: db.updatedAt(),
|
|
29
22
|
|
|
30
23
|
/**
|
|
31
24
|
* Configuration name using dot notation for tree hierarchy.
|
|
@@ -47,10 +40,7 @@ export const parameters = $entity({
|
|
|
47
40
|
/**
|
|
48
41
|
* Current status of this parameter version.
|
|
49
42
|
*/
|
|
50
|
-
status:
|
|
51
|
-
t.enum(["expired", "current", "next", "future"]),
|
|
52
|
-
"future",
|
|
53
|
-
),
|
|
43
|
+
status: db.default(parameterStatusSchema, "future"),
|
|
54
44
|
|
|
55
45
|
/**
|
|
56
46
|
* When this version should become active.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { $module } from "alepha";
|
|
2
|
-
import {
|
|
2
|
+
import { AdminConfigController } from "./controllers/AdminConfigController.ts";
|
|
3
3
|
import { ConfigActivationScheduler } from "./schedulers/ConfigActivationScheduler.ts";
|
|
4
4
|
import { ConfigStore } from "./services/ConfigStore.ts";
|
|
5
5
|
|
|
6
6
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
7
7
|
|
|
8
8
|
// Controller exports
|
|
9
|
-
export * from "./controllers/
|
|
9
|
+
export * from "./controllers/AdminConfigController.ts";
|
|
10
10
|
// Entity exports
|
|
11
11
|
export * from "./entities/parameters.ts";
|
|
12
12
|
// Primitive exports
|
|
@@ -56,5 +56,5 @@ export * from "./services/ConfigStore.ts";
|
|
|
56
56
|
*/
|
|
57
57
|
export const AlephaApiParameters = $module({
|
|
58
58
|
name: "alepha.api.parameters",
|
|
59
|
-
services: [ConfigStore,
|
|
59
|
+
services: [ConfigStore, AdminConfigController, ConfigActivationScheduler],
|
|
60
60
|
});
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { Alepha, t } from "alepha";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { $config, AlephaApiParameters, ConfigStore } from "../index.ts";
|
|
4
|
+
|
|
5
|
+
const featureSchema = t.object({
|
|
6
|
+
enableBeta: t.boolean(),
|
|
7
|
+
maxUploadSize: t.number(),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe("$config", () => {
|
|
11
|
+
it("should initialize with default value", async () => {
|
|
12
|
+
class AppConfig {
|
|
13
|
+
features = $config({
|
|
14
|
+
name: "app.features.flags",
|
|
15
|
+
schema: featureSchema,
|
|
16
|
+
default: { enableBeta: false, maxUploadSize: 10485760 },
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const alepha = Alepha.create();
|
|
21
|
+
alepha.with(AlephaApiParameters);
|
|
22
|
+
alepha.with(AppConfig);
|
|
23
|
+
await alepha.start();
|
|
24
|
+
|
|
25
|
+
const config = alepha.inject(AppConfig);
|
|
26
|
+
|
|
27
|
+
expect(config.features.current).toEqual({
|
|
28
|
+
enableBeta: false,
|
|
29
|
+
maxUploadSize: 10485760,
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should set and persist configuration", async () => {
|
|
34
|
+
class AppConfig {
|
|
35
|
+
features = $config({
|
|
36
|
+
name: "app.features.flags",
|
|
37
|
+
schema: featureSchema,
|
|
38
|
+
default: { enableBeta: false, maxUploadSize: 10485760 },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const alepha = Alepha.create();
|
|
43
|
+
alepha.with(AlephaApiParameters);
|
|
44
|
+
alepha.with(AppConfig);
|
|
45
|
+
await alepha.start();
|
|
46
|
+
|
|
47
|
+
const config = alepha.inject(AppConfig);
|
|
48
|
+
|
|
49
|
+
await config.features.set({
|
|
50
|
+
enableBeta: true,
|
|
51
|
+
maxUploadSize: 20971520,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(config.features.current).toEqual({
|
|
55
|
+
enableBeta: true,
|
|
56
|
+
maxUploadSize: 20971520,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Verify persisted to database
|
|
60
|
+
const store = alepha.inject(ConfigStore);
|
|
61
|
+
const history = await store.getHistory("app.features.flags");
|
|
62
|
+
expect(history.length).toBe(1);
|
|
63
|
+
expect(history[0].status).toBe("current");
|
|
64
|
+
expect(history[0].content).toEqual({
|
|
65
|
+
enableBeta: true,
|
|
66
|
+
maxUploadSize: 20971520,
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should load from database on start", async () => {
|
|
71
|
+
// Use a single Alepha instance, seed data via store before registering config
|
|
72
|
+
class AppConfig {
|
|
73
|
+
features = $config({
|
|
74
|
+
name: "app.features.load",
|
|
75
|
+
schema: featureSchema,
|
|
76
|
+
default: { enableBeta: false, maxUploadSize: 10485760 },
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const alepha = Alepha.create();
|
|
81
|
+
alepha.with(AlephaApiParameters);
|
|
82
|
+
alepha.with(AppConfig);
|
|
83
|
+
await alepha.start();
|
|
84
|
+
|
|
85
|
+
// First, seed the database with a value different from default
|
|
86
|
+
const store = alepha.inject(ConfigStore);
|
|
87
|
+
await store.save(
|
|
88
|
+
"app.features.load",
|
|
89
|
+
{ enableBeta: true, maxUploadSize: 5242880 },
|
|
90
|
+
"test-hash",
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Manually reload the config primitive
|
|
94
|
+
const config = alepha.inject(AppConfig);
|
|
95
|
+
await config.features.reload();
|
|
96
|
+
|
|
97
|
+
// Should have loaded from database, not default
|
|
98
|
+
expect(config.features.current).toEqual({
|
|
99
|
+
enableBeta: true,
|
|
100
|
+
maxUploadSize: 5242880,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should maintain version history", async () => {
|
|
105
|
+
class AppConfig {
|
|
106
|
+
features = $config({
|
|
107
|
+
name: "app.features.history",
|
|
108
|
+
schema: featureSchema,
|
|
109
|
+
default: { enableBeta: false, maxUploadSize: 10485760 },
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const alepha = Alepha.create();
|
|
114
|
+
alepha.with(AlephaApiParameters);
|
|
115
|
+
alepha.with(AppConfig);
|
|
116
|
+
await alepha.start();
|
|
117
|
+
|
|
118
|
+
const config = alepha.inject(AppConfig);
|
|
119
|
+
|
|
120
|
+
// Make multiple changes
|
|
121
|
+
await config.features.set({ enableBeta: true, maxUploadSize: 1 });
|
|
122
|
+
await config.features.set({ enableBeta: false, maxUploadSize: 2 });
|
|
123
|
+
await config.features.set({ enableBeta: true, maxUploadSize: 3 });
|
|
124
|
+
|
|
125
|
+
const history = await config.features.getHistory();
|
|
126
|
+
expect(history.length).toBe(3);
|
|
127
|
+
expect(history[0].version).toBe(3);
|
|
128
|
+
expect(history[1].version).toBe(2);
|
|
129
|
+
expect(history[2].version).toBe(1);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should support rollback to previous version", async () => {
|
|
133
|
+
class AppConfig {
|
|
134
|
+
features = $config({
|
|
135
|
+
name: "app.features.rollback",
|
|
136
|
+
schema: featureSchema,
|
|
137
|
+
default: { enableBeta: false, maxUploadSize: 10485760 },
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const alepha = Alepha.create();
|
|
142
|
+
alepha.with(AlephaApiParameters);
|
|
143
|
+
alepha.with(AppConfig);
|
|
144
|
+
await alepha.start();
|
|
145
|
+
|
|
146
|
+
const config = alepha.inject(AppConfig);
|
|
147
|
+
|
|
148
|
+
await config.features.set({ enableBeta: true, maxUploadSize: 100 });
|
|
149
|
+
await config.features.set({ enableBeta: false, maxUploadSize: 200 });
|
|
150
|
+
await config.features.set({ enableBeta: true, maxUploadSize: 300 });
|
|
151
|
+
|
|
152
|
+
// Rollback to version 1
|
|
153
|
+
await config.features.rollback(1);
|
|
154
|
+
|
|
155
|
+
expect(config.features.current).toEqual({
|
|
156
|
+
enableBeta: true,
|
|
157
|
+
maxUploadSize: 100,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Should have created a new version (4)
|
|
161
|
+
const history = await config.features.getHistory();
|
|
162
|
+
expect(history.length).toBe(4);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should get specific field value", async () => {
|
|
166
|
+
class AppConfig {
|
|
167
|
+
features = $config({
|
|
168
|
+
name: "app.features.field",
|
|
169
|
+
schema: featureSchema,
|
|
170
|
+
default: { enableBeta: false, maxUploadSize: 10485760 },
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const alepha = Alepha.create();
|
|
175
|
+
alepha.with(AlephaApiParameters);
|
|
176
|
+
alepha.with(AppConfig);
|
|
177
|
+
await alepha.start();
|
|
178
|
+
|
|
179
|
+
const config = alepha.inject(AppConfig);
|
|
180
|
+
|
|
181
|
+
expect(config.features.get("enableBeta")).toBe(false);
|
|
182
|
+
expect(config.features.get("maxUploadSize")).toBe(10485760);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("should support change description", async () => {
|
|
186
|
+
class AppConfig {
|
|
187
|
+
features = $config({
|
|
188
|
+
name: "app.features.description",
|
|
189
|
+
schema: featureSchema,
|
|
190
|
+
default: { enableBeta: false, maxUploadSize: 10485760 },
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const alepha = Alepha.create();
|
|
195
|
+
alepha.with(AlephaApiParameters);
|
|
196
|
+
alepha.with(AppConfig);
|
|
197
|
+
await alepha.start();
|
|
198
|
+
|
|
199
|
+
const config = alepha.inject(AppConfig);
|
|
200
|
+
|
|
201
|
+
await config.features.set(
|
|
202
|
+
{ enableBeta: true, maxUploadSize: 20971520 },
|
|
203
|
+
{ changeDescription: "Enable beta features for testing" },
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const history = await config.features.getHistory();
|
|
207
|
+
expect(history[0].changeDescription).toBe(
|
|
208
|
+
"Enable beta features for testing",
|
|
209
|
+
);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("should support user tracking", async () => {
|
|
213
|
+
class AppConfig {
|
|
214
|
+
features = $config({
|
|
215
|
+
name: "app.features.user",
|
|
216
|
+
schema: featureSchema,
|
|
217
|
+
default: { enableBeta: false, maxUploadSize: 10485760 },
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const alepha = Alepha.create();
|
|
222
|
+
alepha.with(AlephaApiParameters);
|
|
223
|
+
alepha.with(AppConfig);
|
|
224
|
+
await alepha.start();
|
|
225
|
+
|
|
226
|
+
const config = alepha.inject(AppConfig);
|
|
227
|
+
|
|
228
|
+
const userId = "550e8400-e29b-41d4-a716-446655440000";
|
|
229
|
+
await config.features.set(
|
|
230
|
+
{ enableBeta: true, maxUploadSize: 20971520 },
|
|
231
|
+
{
|
|
232
|
+
user: {
|
|
233
|
+
id: userId,
|
|
234
|
+
email: "admin@example.com",
|
|
235
|
+
name: "Admin User",
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
const history = await config.features.getHistory();
|
|
241
|
+
expect(history[0].creatorId).toBe(userId);
|
|
242
|
+
expect(history[0].creatorName).toBe("Admin User");
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe("ConfigStore", () => {
|
|
247
|
+
it("should build config tree from names", async () => {
|
|
248
|
+
const alepha = Alepha.create();
|
|
249
|
+
alepha.with(AlephaApiParameters);
|
|
250
|
+
await alepha.start();
|
|
251
|
+
|
|
252
|
+
const store = alepha.inject(ConfigStore);
|
|
253
|
+
|
|
254
|
+
await store.save("app.features.flags", { enabled: true }, "hash1");
|
|
255
|
+
await store.save("app.features.limits", { max: 100 }, "hash2");
|
|
256
|
+
await store.save("app.pricing.tiers", { basic: 10 }, "hash3");
|
|
257
|
+
await store.save("system.logging", { level: "info" }, "hash4");
|
|
258
|
+
|
|
259
|
+
const tree = await store.getConfigTree();
|
|
260
|
+
|
|
261
|
+
expect(tree.length).toBe(2); // app, system
|
|
262
|
+
expect(tree[0].name).toBe("app");
|
|
263
|
+
expect(tree[0].isLeaf).toBe(false);
|
|
264
|
+
expect(tree[0].children.length).toBe(2); // features, pricing
|
|
265
|
+
|
|
266
|
+
const features = tree[0].children.find((c) => c.name === "features");
|
|
267
|
+
expect(features?.children.length).toBe(2); // flags, limits
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("should manage status transitions correctly", async () => {
|
|
271
|
+
const alepha = Alepha.create();
|
|
272
|
+
alepha.with(AlephaApiParameters);
|
|
273
|
+
await alepha.start();
|
|
274
|
+
|
|
275
|
+
const store = alepha.inject(ConfigStore);
|
|
276
|
+
|
|
277
|
+
// Create first version (current)
|
|
278
|
+
const v1 = await store.save("test.status.config", { value: 1 }, "hash");
|
|
279
|
+
expect(v1.status).toBe("current");
|
|
280
|
+
|
|
281
|
+
// Create second version (becomes current, first becomes expired)
|
|
282
|
+
const v2 = await store.save("test.status.config", { value: 2 }, "hash");
|
|
283
|
+
expect(v2.status).toBe("current");
|
|
284
|
+
|
|
285
|
+
const history = await store.getHistory("test.status.config");
|
|
286
|
+
const v1Updated = history.find((v) => v.version === 1);
|
|
287
|
+
expect(v1Updated?.status).toBe("expired");
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it("should get configs by status", async () => {
|
|
291
|
+
const alepha = Alepha.create();
|
|
292
|
+
alepha.with(AlephaApiParameters);
|
|
293
|
+
await alepha.start();
|
|
294
|
+
|
|
295
|
+
const store = alepha.inject(ConfigStore);
|
|
296
|
+
|
|
297
|
+
await store.save("status.a", { a: 1 }, "h1");
|
|
298
|
+
await store.save("status.b", { b: 1 }, "h2");
|
|
299
|
+
await store.save("status.a", { a: 2 }, "h1");
|
|
300
|
+
|
|
301
|
+
const current = await store.getByStatus("current");
|
|
302
|
+
const expired = await store.getByStatus("expired");
|
|
303
|
+
|
|
304
|
+
expect(current.length).toBe(2);
|
|
305
|
+
expect(expired.length).toBe(1);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it("should detect schema migration", async () => {
|
|
309
|
+
const alepha = Alepha.create();
|
|
310
|
+
alepha.with(AlephaApiParameters);
|
|
311
|
+
await alepha.start();
|
|
312
|
+
|
|
313
|
+
const store = alepha.inject(ConfigStore);
|
|
314
|
+
|
|
315
|
+
await store.save("migration.config", { v: 1 }, "hash-v1");
|
|
316
|
+
const v2 = await store.save(
|
|
317
|
+
"migration.config",
|
|
318
|
+
{ v: 2, extra: true },
|
|
319
|
+
"hash-v2",
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
expect(v2.migrationLog).toContain("Schema changed");
|
|
323
|
+
expect(v2.migrationLog).toContain("hash-v1");
|
|
324
|
+
expect(v2.migrationLog).toContain("hash-v2");
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it("should store previous content for rollback", async () => {
|
|
328
|
+
const alepha = Alepha.create();
|
|
329
|
+
alepha.with(AlephaApiParameters);
|
|
330
|
+
await alepha.start();
|
|
331
|
+
|
|
332
|
+
const store = alepha.inject(ConfigStore);
|
|
333
|
+
|
|
334
|
+
await store.save("previous.config", { old: true }, "hash");
|
|
335
|
+
const v2 = await store.save("previous.config", { new: true }, "hash");
|
|
336
|
+
|
|
337
|
+
expect(v2.previousContent).toEqual({ old: true });
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it("should get all config names", async () => {
|
|
341
|
+
const alepha = Alepha.create();
|
|
342
|
+
alepha.with(AlephaApiParameters);
|
|
343
|
+
await alepha.start();
|
|
344
|
+
|
|
345
|
+
const store = alepha.inject(ConfigStore);
|
|
346
|
+
|
|
347
|
+
await store.save("names.alpha", { a: 1 }, "h1");
|
|
348
|
+
await store.save("names.beta", { b: 1 }, "h2");
|
|
349
|
+
await store.save("names.alpha", { a: 2 }, "h1"); // Second version
|
|
350
|
+
|
|
351
|
+
const names = await store.getConfigNames();
|
|
352
|
+
|
|
353
|
+
expect(names).toContain("names.alpha");
|
|
354
|
+
expect(names).toContain("names.beta");
|
|
355
|
+
});
|
|
356
|
+
});
|