alepha 0.15.1 → 0.15.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 +68 -80
- package/dist/api/audits/index.d.ts +10 -33
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +10 -33
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +10 -3
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +10 -3
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +162 -155
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +10 -3
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +413 -0
- package/dist/api/keys/index.d.ts.map +1 -0
- package/dist/api/keys/index.js +476 -0
- package/dist/api/keys/index.js.map +1 -0
- package/dist/api/notifications/index.d.ts +10 -4
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +10 -4
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +43 -50
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +30 -37
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.d.ts +1081 -760
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +2539 -218
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +138 -132
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +12 -4
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.d.ts +20 -40
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +31 -44
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +440 -8
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +1861 -12
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/core/index.d.ts +179 -7
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +213 -7
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/redis/index.d.ts +1 -0
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js +4 -0
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/cli/index.d.ts +638 -5645
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2550 -368
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +203 -45
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +2060 -71
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +70 -40
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +34 -13
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +90 -40
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +70 -40
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts +15 -0
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +15 -0
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +323 -20
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +1857 -7
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +90 -8
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +91 -20
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +11 -4
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +11 -4
- package/dist/lock/core/index.js.map +1 -1
- package/dist/logger/index.d.ts +17 -66
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +14 -63
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +10 -30
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +12 -35
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.browser.js +3 -3
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.bun.js +39 -20
- package/dist/orm/index.bun.js.map +1 -1
- package/dist/orm/index.d.ts +517 -540
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +58 -71
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +18 -10
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/core/index.js +14 -6
- package/dist/queue/core/index.js.map +1 -1
- package/dist/react/auth/index.browser.js +108 -0
- package/dist/react/auth/index.browser.js.map +1 -0
- package/dist/react/auth/index.d.ts +100 -0
- package/dist/react/auth/index.d.ts.map +1 -0
- package/dist/react/auth/index.js +145 -0
- package/dist/react/auth/index.js.map +1 -0
- package/dist/react/core/index.d.ts +469 -0
- package/dist/react/core/index.d.ts.map +1 -0
- package/dist/react/core/index.js +464 -0
- package/dist/react/core/index.js.map +1 -0
- package/dist/react/form/index.d.ts +232 -0
- package/dist/react/form/index.d.ts.map +1 -0
- package/dist/react/form/index.js +432 -0
- package/dist/react/form/index.js.map +1 -0
- package/dist/react/head/index.browser.js +423 -0
- package/dist/react/head/index.browser.js.map +1 -0
- package/dist/react/head/index.d.ts +288 -0
- package/dist/react/head/index.d.ts.map +1 -0
- package/dist/react/head/index.js +465 -0
- package/dist/react/head/index.js.map +1 -0
- package/dist/react/i18n/index.d.ts +175 -0
- package/dist/react/i18n/index.d.ts.map +1 -0
- package/dist/react/i18n/index.js +224 -0
- package/dist/react/i18n/index.js.map +1 -0
- package/dist/react/router/index.browser.js +1974 -0
- package/dist/react/router/index.browser.js.map +1 -0
- package/dist/react/router/index.d.ts +1956 -0
- package/dist/react/router/index.d.ts.map +1 -0
- package/dist/react/router/index.js +4722 -0
- package/dist/react/router/index.js.map +1 -0
- package/dist/react/websocket/index.d.ts +117 -0
- package/dist/react/websocket/index.d.ts.map +1 -0
- package/dist/react/websocket/index.js +107 -0
- package/dist/react/websocket/index.js.map +1 -0
- package/dist/redis/index.bun.js +4 -0
- package/dist/redis/index.bun.js.map +1 -1
- package/dist/redis/index.d.ts +41 -44
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +16 -25
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +11 -2
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/retry/index.js +11 -2
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +11 -2
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +11 -2
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +140 -49
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +164 -32
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +12 -7
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +12 -7
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +7 -22
- package/dist/server/cache/index.d.ts.map +1 -1
- package/dist/server/cache/index.js +7 -22
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts +10 -2
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/compress/index.js +10 -2
- package/dist/server/compress/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +40 -16
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +7 -5
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +124 -23
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +231 -14
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +13 -23
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/cors/index.js +7 -21
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/health/index.d.ts +8 -2
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/health/index.js +8 -2
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/helmet/index.d.ts +11 -3
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/helmet/index.js +11 -3
- package/dist/server/helmet/index.js.map +1 -1
- package/dist/server/links/index.d.ts +11 -6
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +11 -6
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +10 -3
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js +10 -3
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts +9 -3
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/multipart/index.js +9 -3
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts +8 -2
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/proxy/index.js +8 -2
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts +30 -35
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.js +18 -55
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +137 -4
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/static/index.js +1853 -5
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +309 -6
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +1854 -6
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +309 -7
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +1856 -7
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.browser.js +1218 -0
- package/dist/system/index.browser.js.map +1 -0
- package/dist/{file → system}/index.d.ts +343 -16
- package/dist/system/index.d.ts.map +1 -0
- package/dist/{file → system}/index.js +419 -22
- package/dist/system/index.js.map +1 -0
- package/dist/thread/index.d.ts +11 -2
- package/dist/thread/index.d.ts.map +1 -1
- package/dist/thread/index.js +11 -2
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.d.ts +12 -5
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/core/index.js +12 -5
- package/dist/topic/core/index.js.map +1 -1
- package/dist/vite/index.d.ts +5 -6272
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +23 -10
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts +12 -8
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +12 -8
- package/dist/websocket/index.js.map +1 -1
- package/package.json +82 -11
- package/src/api/audits/index.ts +10 -33
- package/src/api/files/__tests__/$bucket.spec.ts +1 -1
- package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
- package/src/api/files/controllers/FileController.spec.ts +1 -1
- package/src/api/files/index.ts +10 -3
- package/src/api/files/jobs/FileJobs.spec.ts +1 -1
- package/src/api/files/services/FileService.spec.ts +1 -1
- package/src/api/jobs/index.ts +10 -3
- package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
- package/src/api/keys/controllers/ApiKeyController.ts +103 -0
- package/src/api/keys/entities/apiKeyEntity.ts +41 -0
- package/src/api/keys/index.ts +49 -0
- package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
- package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
- package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
- package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
- package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
- package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
- package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
- package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
- package/src/api/keys/services/ApiKeyService.ts +306 -0
- package/src/api/logs/TODO.md +55 -0
- package/src/api/notifications/index.ts +10 -4
- package/src/api/parameters/index.ts +9 -30
- package/src/api/parameters/primitives/$config.ts +12 -4
- package/src/api/parameters/services/ConfigStore.ts +9 -3
- package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
- package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
- package/src/api/users/index.ts +14 -3
- package/src/api/users/primitives/$realm.ts +33 -5
- package/src/api/users/providers/RealmProvider.ts +1 -12
- package/src/api/users/services/SessionService.ts +1 -1
- package/src/api/verifications/controllers/VerificationController.ts +2 -0
- package/src/api/verifications/index.ts +10 -4
- package/src/batch/index.ts +9 -36
- package/src/batch/primitives/$batch.ts +0 -8
- package/src/batch/providers/BatchProvider.ts +29 -2
- package/src/bucket/__tests__/shared.ts +1 -1
- package/src/bucket/index.ts +13 -6
- package/src/bucket/primitives/$bucket.ts +1 -1
- package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
- package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
- package/src/cache/core/__tests__/shared.ts +30 -0
- package/src/cache/core/index.ts +11 -6
- package/src/cache/core/primitives/$cache.spec.ts +5 -0
- package/src/cache/core/providers/CacheProvider.ts +17 -0
- package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
- package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
- package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
- package/src/cli/apps/AlephaCli.ts +1 -14
- package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -1
- package/src/cli/atoms/buildOptions.ts +99 -9
- package/src/cli/commands/build.ts +150 -37
- package/src/cli/commands/db.ts +22 -18
- package/src/cli/commands/deploy.ts +1 -1
- package/src/cli/commands/dev.ts +1 -20
- package/src/cli/commands/gen/env.ts +5 -2
- package/src/cli/commands/gen/openapi.ts +5 -2
- package/src/cli/commands/init.spec.ts +588 -0
- package/src/cli/commands/init.ts +115 -58
- package/src/cli/commands/lint.ts +7 -1
- package/src/cli/commands/typecheck.ts +11 -0
- package/src/cli/providers/AppEntryProvider.ts +1 -1
- package/src/cli/providers/ViteBuildProvider.ts +8 -50
- package/src/cli/providers/ViteDevServerProvider.ts +35 -16
- package/src/cli/services/AlephaCliUtils.ts +52 -121
- package/src/cli/services/PackageManagerUtils.ts +129 -11
- package/src/cli/services/ProjectScaffolder.spec.ts +97 -0
- package/src/cli/services/ProjectScaffolder.ts +148 -81
- package/src/cli/services/ViteUtils.ts +82 -0
- package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +37 -24
- package/src/cli/templates/apiAppSecurityTs.ts +11 -0
- package/src/cli/templates/apiIndexTs.ts +30 -0
- package/src/cli/templates/gitignore.ts +39 -0
- package/src/cli/{assets → templates}/mainCss.ts +11 -2
- package/src/cli/templates/mainServerTs.ts +33 -0
- package/src/cli/templates/webAppRouterTs.ts +74 -0
- package/src/cli/templates/webHelloComponentTsx.ts +30 -0
- package/src/command/helpers/Runner.spec.ts +139 -0
- package/src/command/helpers/Runner.ts +7 -22
- package/src/command/index.ts +12 -4
- package/src/command/providers/CliProvider.spec.ts +1392 -0
- package/src/command/providers/CliProvider.ts +320 -47
- package/src/core/Alepha.ts +34 -27
- package/src/core/__tests__/Alepha-start.spec.ts +4 -4
- package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
- package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
- package/src/core/index.shared.ts +1 -0
- package/src/core/index.ts +20 -0
- package/src/core/providers/EventManager.spec.ts +0 -71
- package/src/core/providers/EventManager.ts +3 -15
- package/src/core/providers/Json.ts +2 -14
- package/src/datetime/index.ts +15 -0
- package/src/email/index.ts +10 -5
- package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
- package/src/email/providers/LocalEmailProvider.ts +1 -1
- package/src/fake/__tests__/keyName.example.ts +1 -1
- package/src/fake/__tests__/keyName.spec.ts +5 -5
- package/src/fake/index.ts +9 -6
- package/src/fake/providers/FakeProvider.spec.ts +258 -40
- package/src/fake/providers/FakeProvider.ts +133 -19
- package/src/lock/core/index.ts +11 -4
- package/src/logger/index.ts +17 -66
- package/src/mcp/index.ts +10 -27
- package/src/mcp/transports/SseMcpTransport.ts +0 -11
- package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
- package/src/orm/index.browser.ts +2 -2
- package/src/orm/index.bun.ts +5 -3
- package/src/orm/index.ts +23 -53
- package/src/orm/providers/drivers/BunSqliteProvider.ts +5 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +57 -30
- package/src/orm/providers/drivers/DatabaseProvider.ts +9 -1
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +4 -1
- package/src/orm/services/Repository.ts +7 -3
- package/src/queue/core/index.ts +14 -6
- package/src/react/auth/__tests__/$auth.spec.ts +202 -0
- package/src/react/auth/hooks/useAuth.ts +32 -0
- package/src/react/auth/index.browser.ts +13 -0
- package/src/react/auth/index.shared.ts +2 -0
- package/src/react/auth/index.ts +48 -0
- package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
- package/src/react/auth/services/ReactAuth.ts +135 -0
- package/src/react/core/__tests__/Router.spec.tsx +169 -0
- package/src/react/core/components/ClientOnly.tsx +49 -0
- package/src/react/core/components/ErrorBoundary.tsx +73 -0
- package/src/react/core/contexts/AlephaContext.ts +7 -0
- package/src/react/core/contexts/AlephaProvider.tsx +42 -0
- package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
- package/src/react/core/hooks/useAction.ts +480 -0
- package/src/react/core/hooks/useAlepha.ts +26 -0
- package/src/react/core/hooks/useClient.ts +17 -0
- package/src/react/core/hooks/useEvents.ts +51 -0
- package/src/react/core/hooks/useInject.ts +12 -0
- package/src/react/core/hooks/useStore.ts +52 -0
- package/src/react/core/index.ts +90 -0
- package/src/react/form/components/FormState.tsx +17 -0
- package/src/react/form/errors/FormValidationError.ts +18 -0
- package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
- package/src/react/form/hooks/useForm.ts +47 -0
- package/src/react/form/hooks/useFormState.ts +130 -0
- package/src/react/form/index.ts +44 -0
- package/src/react/form/services/FormModel.ts +614 -0
- package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
- package/src/react/head/helpers/SeoExpander.ts +142 -0
- package/src/react/head/hooks/useHead.spec.tsx +288 -0
- package/src/react/head/hooks/useHead.ts +62 -0
- package/src/react/head/index.browser.ts +26 -0
- package/src/react/head/index.ts +44 -0
- package/src/react/head/interfaces/Head.ts +105 -0
- package/src/react/head/primitives/$head.ts +25 -0
- package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
- package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
- package/src/react/head/providers/HeadProvider.ts +168 -0
- package/src/react/head/providers/ServerHeadProvider.ts +31 -0
- package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
- package/src/react/i18n/components/Localize.spec.tsx +357 -0
- package/src/react/i18n/components/Localize.tsx +35 -0
- package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
- package/src/react/i18n/hooks/useI18n.ts +18 -0
- package/src/react/i18n/index.ts +41 -0
- package/src/react/i18n/primitives/$dictionary.ts +69 -0
- package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
- package/src/react/i18n/providers/I18nProvider.ts +278 -0
- package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
- package/src/react/router/__tests__/page-head.spec.ts +48 -0
- package/src/react/router/__tests__/seo-head.spec.ts +125 -0
- package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
- package/src/react/router/components/ErrorViewer.tsx +872 -0
- package/src/react/router/components/Link.tsx +23 -0
- package/src/react/router/components/NestedView.tsx +223 -0
- package/src/react/router/components/NotFound.tsx +30 -0
- package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
- package/src/react/router/contexts/RouterLayerContext.ts +12 -0
- package/src/react/router/errors/Redirection.ts +28 -0
- package/src/react/router/hooks/useActive.ts +52 -0
- package/src/react/router/hooks/useQueryParams.ts +63 -0
- package/src/react/router/hooks/useRouter.ts +20 -0
- package/src/react/router/hooks/useRouterState.ts +11 -0
- package/src/react/router/index.browser.ts +45 -0
- package/src/react/router/index.shared.ts +19 -0
- package/src/react/router/index.ts +146 -0
- package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
- package/src/react/router/primitives/$page.spec.tsx +676 -0
- package/src/react/router/primitives/$page.ts +489 -0
- package/src/react/router/providers/ReactBrowserProvider.ts +312 -0
- package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
- package/src/react/router/providers/ReactPageProvider.ts +726 -0
- package/src/react/router/providers/ReactPreloadProvider.spec.ts +142 -0
- package/src/react/router/providers/ReactPreloadProvider.ts +85 -0
- package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
- package/src/react/router/providers/ReactServerProvider.ts +487 -0
- package/src/react/router/providers/ReactServerTemplateProvider.spec.ts +210 -0
- package/src/react/router/providers/ReactServerTemplateProvider.ts +542 -0
- package/src/react/router/providers/SSRManifestProvider.ts +334 -0
- package/src/react/router/services/ReactPageServerService.ts +48 -0
- package/src/react/router/services/ReactPageService.ts +27 -0
- package/src/react/router/services/ReactRouter.ts +262 -0
- package/src/react/websocket/hooks/useRoom.tsx +242 -0
- package/src/react/websocket/index.ts +7 -0
- package/src/redis/__tests__/redis.spec.ts +13 -0
- package/src/redis/index.ts +9 -25
- package/src/redis/providers/BunRedisProvider.ts +9 -0
- package/src/redis/providers/NodeRedisProvider.ts +8 -0
- package/src/redis/providers/RedisProvider.ts +16 -0
- package/src/retry/index.ts +11 -2
- package/src/router/index.ts +15 -0
- package/src/scheduler/index.ts +11 -2
- package/src/security/__tests__/BasicAuth.spec.ts +2 -0
- package/src/security/__tests__/ServerSecurityProvider.spec.ts +90 -5
- package/src/security/index.ts +15 -10
- package/src/security/interfaces/IssuerResolver.ts +27 -0
- package/src/security/primitives/$issuer.ts +55 -0
- package/src/security/providers/SecurityProvider.ts +179 -0
- package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
- package/src/security/providers/ServerSecurityProvider.ts +63 -41
- package/src/server/auth/index.ts +12 -7
- package/src/server/cache/index.ts +7 -22
- package/src/server/compress/index.ts +10 -2
- package/src/server/cookies/index.ts +7 -5
- package/src/server/cookies/primitives/$cookie.ts +33 -11
- package/src/server/core/index.ts +16 -6
- package/src/server/core/interfaces/ServerRequest.ts +83 -1
- package/src/server/core/primitives/$action.spec.ts +1 -1
- package/src/server/core/primitives/$action.ts +8 -3
- package/src/server/core/providers/NodeHttpServerProvider.spec.ts +9 -3
- package/src/server/core/providers/NodeHttpServerProvider.ts +9 -3
- package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
- package/src/server/core/services/ServerRequestParser.ts +306 -13
- package/src/server/cors/index.ts +7 -21
- package/src/server/cors/primitives/$cors.ts +6 -2
- package/src/server/health/index.ts +8 -2
- package/src/server/helmet/index.ts +11 -3
- package/src/server/links/index.ts +11 -6
- package/src/server/metrics/index.ts +10 -3
- package/src/server/multipart/index.ts +9 -3
- package/src/server/proxy/index.ts +8 -2
- package/src/server/rate-limit/index.ts +21 -25
- package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
- package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
- package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
- package/src/server/static/index.ts +8 -2
- package/src/server/static/providers/ServerStaticProvider.ts +1 -1
- package/src/server/swagger/index.ts +9 -4
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
- package/src/sms/index.ts +9 -5
- package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
- package/src/sms/providers/LocalSmsProvider.ts +1 -1
- package/src/system/index.browser.ts +36 -0
- package/src/system/index.ts +62 -0
- package/src/system/index.workerd.ts +1 -0
- package/src/{file → system}/providers/FileSystemProvider.ts +24 -0
- package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
- package/src/system/providers/MemoryShellProvider.ts +164 -0
- package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
- package/src/{file → system}/providers/NodeFileSystemProvider.ts +47 -2
- package/src/system/providers/NodeShellProvider.ts +184 -0
- package/src/system/providers/ShellProvider.ts +74 -0
- package/src/{file → system}/services/FileDetector.spec.ts +2 -2
- package/src/thread/index.ts +11 -2
- package/src/topic/core/index.ts +12 -5
- package/src/vite/tasks/buildClient.ts +2 -7
- package/src/vite/tasks/buildServer.ts +19 -13
- package/src/vite/tasks/generateCloudflare.ts +10 -7
- package/src/vite/tasks/generateDocker.ts +4 -0
- package/src/websocket/index.ts +12 -8
- package/dist/file/index.d.ts.map +0 -1
- package/dist/file/index.js.map +0 -1
- package/src/cli/assets/apiIndexTs.ts +0 -16
- package/src/cli/assets/mainServerTs.ts +0 -24
- package/src/cli/assets/webAppRouterTs.ts +0 -16
- package/src/cli/assets/webHelloComponentTsx.ts +0 -20
- package/src/cli/providers/ViteTemplateProvider.ts +0 -27
- package/src/file/index.ts +0 -43
- /package/src/cli/{assets → templates}/apiHelloControllerTs.ts +0 -0
- /package/src/cli/{assets → templates}/biomeJson.ts +0 -0
- /package/src/cli/{assets → templates}/dummySpecTs.ts +0 -0
- /package/src/cli/{assets → templates}/editorconfig.ts +0 -0
- /package/src/cli/{assets → templates}/mainBrowserTs.ts +0 -0
- /package/src/cli/{assets → templates}/tsconfigJson.ts +0 -0
- /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
- /package/src/{file → system}/errors/FileError.ts +0 -0
- /package/src/{file → system}/services/FileDetector.ts +0 -0
|
@@ -0,0 +1,1392 @@
|
|
|
1
|
+
import { Alepha, t } from "alepha";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { $command } from "../primitives/$command.ts";
|
|
4
|
+
import { CliProvider } from "./CliProvider.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Test subclass that exposes protected methods for unit testing.
|
|
8
|
+
*/
|
|
9
|
+
class TestCliProvider extends CliProvider {
|
|
10
|
+
public testParseFlags = this.parseFlags.bind(this);
|
|
11
|
+
public testParseCommandArgs = this.parseCommandArgs.bind(this);
|
|
12
|
+
public testParseArgumentValue = this.parseArgumentValue.bind(this);
|
|
13
|
+
public testParseModeFlag = this.parseModeFlag.bind(this);
|
|
14
|
+
public testResolveCommand = this.resolveCommand.bind(this);
|
|
15
|
+
public testRemoveConsumedArgs = this.removeConsumedArgs.bind(this);
|
|
16
|
+
public testGetFlagConsumedIndices = this.getFlagConsumedIndices.bind(this);
|
|
17
|
+
public testGenerateArgsUsage = this.generateArgsUsage.bind(this);
|
|
18
|
+
public testGenerateColoredArgsUsage =
|
|
19
|
+
this.generateColoredArgsUsage.bind(this);
|
|
20
|
+
public testGetTypeName = this.getTypeName.bind(this);
|
|
21
|
+
public testGetTopLevelCommands = this.getTopLevelCommands.bind(this);
|
|
22
|
+
public testFindParentCommand = this.findParentCommand.bind(this);
|
|
23
|
+
public testGetCommandPath = this.getCommandPath.bind(this);
|
|
24
|
+
public testFindCommand = this.findCommand.bind(this);
|
|
25
|
+
public testFindPreHooks = this.findPreHooks.bind(this);
|
|
26
|
+
public testFindPostHooks = this.findPostHooks.bind(this);
|
|
27
|
+
public testGetEnumValues = this.getEnumValues.bind(this);
|
|
28
|
+
public testFormatFlagDescription = this.formatFlagDescription.bind(this);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Extract flag definitions from a command's flags schema (for testing printHelp logic).
|
|
32
|
+
*/
|
|
33
|
+
public testExtractFlagDefs(flagsSchema: any) {
|
|
34
|
+
return Object.entries(flagsSchema.properties).map(([key, value]) => ({
|
|
35
|
+
key,
|
|
36
|
+
schema: value,
|
|
37
|
+
aliases: [
|
|
38
|
+
key,
|
|
39
|
+
...((value as any).aliases ??
|
|
40
|
+
((value as any).alias ? [(value as any).alias] : [])),
|
|
41
|
+
],
|
|
42
|
+
description: (value as any).description,
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Format aliases array into flag string (e.g., "-t, --target").
|
|
48
|
+
* Sorts by length (shorter first).
|
|
49
|
+
*/
|
|
50
|
+
public testFormatFlagStr(aliases: string[]): string {
|
|
51
|
+
return aliases
|
|
52
|
+
.slice()
|
|
53
|
+
.sort((a, b) => a.length - b.length)
|
|
54
|
+
.map((a) => (a.length === 1 ? `-${a}` : `--${a}`))
|
|
55
|
+
.join(", ");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
describe("CliProvider", () => {
|
|
60
|
+
const createTestCli = () => {
|
|
61
|
+
const alepha = Alepha.create();
|
|
62
|
+
return alepha.inject(TestCliProvider);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
66
|
+
// parseFlags
|
|
67
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
describe("parseFlags", () => {
|
|
70
|
+
it("should parse boolean flags", () => {
|
|
71
|
+
const cli = createTestCli();
|
|
72
|
+
const flagDefs = [
|
|
73
|
+
{ key: "verbose", aliases: ["v", "verbose"], schema: t.boolean() },
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const result = cli.testParseFlags(["--verbose"], flagDefs);
|
|
77
|
+
expect(result.verbose).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should parse short boolean flags", () => {
|
|
81
|
+
const cli = createTestCli();
|
|
82
|
+
const flagDefs = [
|
|
83
|
+
{ key: "verbose", aliases: ["v", "verbose"], schema: t.boolean() },
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const result = cli.testParseFlags(["-v"], flagDefs);
|
|
87
|
+
expect(result.verbose).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("should parse string flags with = syntax", () => {
|
|
91
|
+
const cli = createTestCli();
|
|
92
|
+
const flagDefs = [
|
|
93
|
+
{ key: "name", aliases: ["n", "name"], schema: t.string() },
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
const result = cli.testParseFlags(["--name=hello"], flagDefs);
|
|
97
|
+
expect(result.name).toBe("hello");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("should parse string flags with space syntax", () => {
|
|
101
|
+
const cli = createTestCli();
|
|
102
|
+
const flagDefs = [
|
|
103
|
+
{ key: "name", aliases: ["n", "name"], schema: t.string() },
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const result = cli.testParseFlags(["--name", "hello"], flagDefs);
|
|
107
|
+
expect(result.name).toBe("hello");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("should parse JSON object flags", () => {
|
|
111
|
+
const cli = createTestCli();
|
|
112
|
+
const flagDefs = [
|
|
113
|
+
{ key: "config", aliases: ["config"], schema: t.object({}) },
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
const result = cli.testParseFlags(
|
|
117
|
+
["--config", '{"key":"value"}'],
|
|
118
|
+
flagDefs,
|
|
119
|
+
);
|
|
120
|
+
expect(result.config).toEqual({ key: "value" });
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should parse JSON array flags", () => {
|
|
124
|
+
const cli = createTestCli();
|
|
125
|
+
const flagDefs = [
|
|
126
|
+
{ key: "items", aliases: ["items"], schema: t.array(t.string()) },
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
const result = cli.testParseFlags(["--items", '["a","b"]'], flagDefs);
|
|
130
|
+
expect(result.items).toEqual(["a", "b"]);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should throw on unknown flags", () => {
|
|
134
|
+
const cli = createTestCli();
|
|
135
|
+
const flagDefs = [
|
|
136
|
+
{ key: "known", aliases: ["known"], schema: t.boolean() },
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
expect(() =>
|
|
140
|
+
cli.testParseFlags(["--unknown", "--known"], flagDefs),
|
|
141
|
+
).toThrow("Unknown flag: --unknown");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("should throw on missing required value", () => {
|
|
145
|
+
const cli = createTestCli();
|
|
146
|
+
const flagDefs = [{ key: "name", aliases: ["name"], schema: t.string() }];
|
|
147
|
+
|
|
148
|
+
expect(() => cli.testParseFlags(["--name"], flagDefs)).toThrow(
|
|
149
|
+
"requires a value",
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should throw on invalid JSON", () => {
|
|
154
|
+
const cli = createTestCli();
|
|
155
|
+
const flagDefs = [
|
|
156
|
+
{ key: "config", aliases: ["config"], schema: t.object({}) },
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
expect(() =>
|
|
160
|
+
cli.testParseFlags(["--config", "{invalid}"], flagDefs),
|
|
161
|
+
).toThrow("Invalid JSON");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should parse union(boolean, text) flag without value as true", () => {
|
|
165
|
+
const cli = createTestCli();
|
|
166
|
+
const flagDefs = [
|
|
167
|
+
{
|
|
168
|
+
key: "image",
|
|
169
|
+
aliases: ["i", "image"],
|
|
170
|
+
schema: t.union([t.boolean(), t.text()]),
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const result = cli.testParseFlags(["-i"], flagDefs);
|
|
175
|
+
expect(result.image).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("should parse union(boolean, text) flag with = value as string", () => {
|
|
179
|
+
const cli = createTestCli();
|
|
180
|
+
const flagDefs = [
|
|
181
|
+
{
|
|
182
|
+
key: "image",
|
|
183
|
+
aliases: ["i", "image"],
|
|
184
|
+
schema: t.union([t.boolean(), t.text()]),
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
const result = cli.testParseFlags(["-i=1.3.4"], flagDefs);
|
|
189
|
+
expect(result.image).toBe("1.3.4");
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("should parse union(boolean, text) flag with space value as string", () => {
|
|
193
|
+
const cli = createTestCli();
|
|
194
|
+
const flagDefs = [
|
|
195
|
+
{
|
|
196
|
+
key: "image",
|
|
197
|
+
aliases: ["i", "image"],
|
|
198
|
+
schema: t.union([t.boolean(), t.text()]),
|
|
199
|
+
},
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
const result = cli.testParseFlags(["-i", "1.3.4"], flagDefs);
|
|
203
|
+
expect(result.image).toBe("1.3.4");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("should parse union(boolean, text) flag without value when followed by another flag", () => {
|
|
207
|
+
const cli = createTestCli();
|
|
208
|
+
const flagDefs = [
|
|
209
|
+
{
|
|
210
|
+
key: "image",
|
|
211
|
+
aliases: ["i", "image"],
|
|
212
|
+
schema: t.union([t.boolean(), t.text()]),
|
|
213
|
+
},
|
|
214
|
+
{ key: "verbose", aliases: ["v", "verbose"], schema: t.boolean() },
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
const result = cli.testParseFlags(["-i", "-v"], flagDefs);
|
|
218
|
+
expect(result.image).toBe(true);
|
|
219
|
+
expect(result.verbose).toBe(true);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("should parse union(boolean, text) flag with long form without value", () => {
|
|
223
|
+
const cli = createTestCli();
|
|
224
|
+
const flagDefs = [
|
|
225
|
+
{
|
|
226
|
+
key: "image",
|
|
227
|
+
aliases: ["i", "image"],
|
|
228
|
+
schema: t.union([t.boolean(), t.text()]),
|
|
229
|
+
},
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
const result = cli.testParseFlags(["--image"], flagDefs);
|
|
233
|
+
expect(result.image).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("should parse union(boolean, text) flag at end of argv without value", () => {
|
|
237
|
+
const cli = createTestCli();
|
|
238
|
+
const flagDefs = [
|
|
239
|
+
{
|
|
240
|
+
key: "image",
|
|
241
|
+
aliases: ["i", "image"],
|
|
242
|
+
schema: t.union([t.boolean(), t.text()]),
|
|
243
|
+
},
|
|
244
|
+
{ key: "verbose", aliases: ["v", "verbose"], schema: t.boolean() },
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
const result = cli.testParseFlags(["-v", "-i"], flagDefs);
|
|
248
|
+
expect(result.verbose).toBe(true);
|
|
249
|
+
expect(result.image).toBe(true);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("should parse union(boolean, text) with empty = value as empty string", () => {
|
|
253
|
+
const cli = createTestCli();
|
|
254
|
+
const flagDefs = [
|
|
255
|
+
{
|
|
256
|
+
key: "image",
|
|
257
|
+
aliases: ["i", "image"],
|
|
258
|
+
schema: t.union([t.boolean(), t.text()]),
|
|
259
|
+
},
|
|
260
|
+
];
|
|
261
|
+
|
|
262
|
+
// Note: --image= results in empty string value
|
|
263
|
+
const result = cli.testParseFlags(["--image="], flagDefs);
|
|
264
|
+
// Empty string is falsy, so it goes through the "no value" path → true
|
|
265
|
+
// This is expected behavior - use --image="" if you need empty string
|
|
266
|
+
expect(result.image).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("should parse union(boolean, text) with value containing special chars", () => {
|
|
270
|
+
const cli = createTestCli();
|
|
271
|
+
const flagDefs = [
|
|
272
|
+
{
|
|
273
|
+
key: "image",
|
|
274
|
+
aliases: ["i", "image"],
|
|
275
|
+
schema: t.union([t.boolean(), t.text()]),
|
|
276
|
+
},
|
|
277
|
+
];
|
|
278
|
+
|
|
279
|
+
const result = cli.testParseFlags(
|
|
280
|
+
["--image=my-org/my-app:v1.2.3-beta"],
|
|
281
|
+
flagDefs,
|
|
282
|
+
);
|
|
283
|
+
expect(result.image).toBe("my-org/my-app:v1.2.3-beta");
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
288
|
+
// parseArgumentValue
|
|
289
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
describe("parseArgumentValue", () => {
|
|
292
|
+
it("should parse string values", () => {
|
|
293
|
+
const cli = createTestCli();
|
|
294
|
+
expect(cli.testParseArgumentValue("hello", t.string())).toBe("hello");
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("should parse number values", () => {
|
|
298
|
+
const cli = createTestCli();
|
|
299
|
+
expect(cli.testParseArgumentValue("42", t.number())).toBe(42);
|
|
300
|
+
expect(cli.testParseArgumentValue("3.14", t.number())).toBe(3.14);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("should throw on invalid number", () => {
|
|
304
|
+
const cli = createTestCli();
|
|
305
|
+
expect(() => cli.testParseArgumentValue("abc", t.number())).toThrow(
|
|
306
|
+
"Expected number",
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("should parse integer values", () => {
|
|
311
|
+
const cli = createTestCli();
|
|
312
|
+
expect(cli.testParseArgumentValue("42", t.integer())).toBe(42);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("should throw on non-integer for integer schema", () => {
|
|
316
|
+
const cli = createTestCli();
|
|
317
|
+
expect(() => cli.testParseArgumentValue("3.14", t.integer())).toThrow(
|
|
318
|
+
"Expected integer",
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("should parse boolean true values", () => {
|
|
323
|
+
const cli = createTestCli();
|
|
324
|
+
expect(cli.testParseArgumentValue("true", t.boolean())).toBe(true);
|
|
325
|
+
expect(cli.testParseArgumentValue("1", t.boolean())).toBe(true);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("should parse boolean false values", () => {
|
|
329
|
+
const cli = createTestCli();
|
|
330
|
+
expect(cli.testParseArgumentValue("false", t.boolean())).toBe(false);
|
|
331
|
+
expect(cli.testParseArgumentValue("0", t.boolean())).toBe(false);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it("should throw on invalid boolean", () => {
|
|
335
|
+
const cli = createTestCli();
|
|
336
|
+
expect(() => cli.testParseArgumentValue("yes", t.boolean())).toThrow(
|
|
337
|
+
"Expected boolean",
|
|
338
|
+
);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
343
|
+
// parseCommandArgs
|
|
344
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
describe("parseCommandArgs", () => {
|
|
347
|
+
it("should return undefined when no schema", () => {
|
|
348
|
+
const cli = createTestCli();
|
|
349
|
+
expect(
|
|
350
|
+
cli.testParseCommandArgs(["arg"], undefined, true),
|
|
351
|
+
).toBeUndefined();
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("should parse optional args when present", () => {
|
|
355
|
+
const cli = createTestCli();
|
|
356
|
+
const schema = t.optional(t.string());
|
|
357
|
+
expect(cli.testParseCommandArgs(["hello"], schema, true)).toBe("hello");
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("should return undefined for optional args when missing", () => {
|
|
361
|
+
const cli = createTestCli();
|
|
362
|
+
const schema = t.optional(t.string());
|
|
363
|
+
expect(cli.testParseCommandArgs([], schema, true)).toBeUndefined();
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it("should parse required args", () => {
|
|
367
|
+
const cli = createTestCli();
|
|
368
|
+
const schema = t.string();
|
|
369
|
+
expect(cli.testParseCommandArgs(["hello"], schema, true)).toBe("hello");
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("should throw on missing required args", () => {
|
|
373
|
+
const cli = createTestCli();
|
|
374
|
+
const schema = t.string();
|
|
375
|
+
expect(() => cli.testParseCommandArgs([], schema, true)).toThrow(
|
|
376
|
+
"Missing required argument",
|
|
377
|
+
);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("should parse tuple args", () => {
|
|
381
|
+
const cli = createTestCli();
|
|
382
|
+
const schema = t.tuple([t.string(), t.number()]);
|
|
383
|
+
const result = cli.testParseCommandArgs(["hello", "42"], schema, true);
|
|
384
|
+
expect(result).toEqual(["hello", 42]);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it("should handle optional tuple items", () => {
|
|
388
|
+
const cli = createTestCli();
|
|
389
|
+
const schema = t.tuple([t.string(), t.optional(t.number())]);
|
|
390
|
+
const result = cli.testParseCommandArgs(["hello"], schema, true);
|
|
391
|
+
expect(result).toEqual(["hello", undefined]);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("should skip flags when parsing args", () => {
|
|
395
|
+
const cli = createTestCli();
|
|
396
|
+
const schema = t.string();
|
|
397
|
+
const flags = t.object({ verbose: t.optional(t.boolean()) });
|
|
398
|
+
const result = cli.testParseCommandArgs(
|
|
399
|
+
["--verbose", "hello"],
|
|
400
|
+
schema,
|
|
401
|
+
true,
|
|
402
|
+
flags,
|
|
403
|
+
);
|
|
404
|
+
expect(result).toBe("hello");
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it("should skip flag values when parsing args", () => {
|
|
408
|
+
const cli = createTestCli();
|
|
409
|
+
const schema = t.string();
|
|
410
|
+
const flags = t.object({ name: t.optional(t.string()) });
|
|
411
|
+
const result = cli.testParseCommandArgs(
|
|
412
|
+
["--name", "ignored", "actual"],
|
|
413
|
+
schema,
|
|
414
|
+
true,
|
|
415
|
+
flags,
|
|
416
|
+
);
|
|
417
|
+
expect(result).toBe("actual");
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
422
|
+
// parseModeFlag
|
|
423
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
424
|
+
|
|
425
|
+
describe("parseModeFlag", () => {
|
|
426
|
+
it("should parse --mode=value", () => {
|
|
427
|
+
const cli = createTestCli();
|
|
428
|
+
expect(cli.testParseModeFlag(["--mode=production"])).toBe("production");
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it("should parse -m=value", () => {
|
|
432
|
+
const cli = createTestCli();
|
|
433
|
+
expect(cli.testParseModeFlag(["-m=staging"])).toBe("staging");
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it("should parse --mode value", () => {
|
|
437
|
+
const cli = createTestCli();
|
|
438
|
+
expect(cli.testParseModeFlag(["--mode", "production"])).toBe(
|
|
439
|
+
"production",
|
|
440
|
+
);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it("should parse -m value", () => {
|
|
444
|
+
const cli = createTestCli();
|
|
445
|
+
expect(cli.testParseModeFlag(["-m", "staging"])).toBe("staging");
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it("should return undefined when no mode flag", () => {
|
|
449
|
+
const cli = createTestCli();
|
|
450
|
+
expect(cli.testParseModeFlag(["--other", "value"])).toBeUndefined();
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it("should throw when --mode has no value", () => {
|
|
454
|
+
const cli = createTestCli();
|
|
455
|
+
expect(() => cli.testParseModeFlag(["--mode"])).toThrow(
|
|
456
|
+
"requires a value",
|
|
457
|
+
);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it("should throw when --mode followed by another flag", () => {
|
|
461
|
+
const cli = createTestCli();
|
|
462
|
+
expect(() => cli.testParseModeFlag(["--mode", "--other"])).toThrow(
|
|
463
|
+
"requires a value",
|
|
464
|
+
);
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
469
|
+
// resolveCommand
|
|
470
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
471
|
+
|
|
472
|
+
describe("resolveCommand", () => {
|
|
473
|
+
it("should return undefined for empty args", () => {
|
|
474
|
+
const cli = createTestCli();
|
|
475
|
+
const result = cli.testResolveCommand([]);
|
|
476
|
+
expect(result.command).toBeUndefined();
|
|
477
|
+
expect(result.consumedArgs).toEqual([]);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it("should find command by name", () => {
|
|
481
|
+
class TestCommands {
|
|
482
|
+
build = $command({
|
|
483
|
+
name: "build",
|
|
484
|
+
flags: t.object({}),
|
|
485
|
+
handler: async () => {},
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
490
|
+
const cli = alepha.inject(TestCliProvider);
|
|
491
|
+
|
|
492
|
+
const result = cli.testResolveCommand(["build"]);
|
|
493
|
+
expect(result.command?.name).toBe("build");
|
|
494
|
+
expect(result.consumedArgs).toEqual(["build"]);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it("should find command by colon notation", () => {
|
|
498
|
+
class TestCommands {
|
|
499
|
+
deployVercel = $command({
|
|
500
|
+
name: "deploy:vercel",
|
|
501
|
+
flags: t.object({}),
|
|
502
|
+
handler: async () => {},
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
507
|
+
const cli = alepha.inject(TestCliProvider);
|
|
508
|
+
|
|
509
|
+
const result = cli.testResolveCommand(["deploy:vercel"]);
|
|
510
|
+
expect(result.command?.name).toBe("deploy:vercel");
|
|
511
|
+
expect(result.consumedArgs).toEqual(["deploy:vercel"]);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it("should return undefined for unknown command", () => {
|
|
515
|
+
const cli = createTestCli();
|
|
516
|
+
const result = cli.testResolveCommand(["unknown"]);
|
|
517
|
+
expect(result.command).toBeUndefined();
|
|
518
|
+
expect(result.consumedArgs).toEqual([]);
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
523
|
+
// removeConsumedArgs
|
|
524
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
525
|
+
|
|
526
|
+
describe("removeConsumedArgs", () => {
|
|
527
|
+
it("should remove consumed command args", () => {
|
|
528
|
+
const cli = createTestCli();
|
|
529
|
+
const result = cli.testRemoveConsumedArgs(
|
|
530
|
+
["build", "--verbose", "extra"],
|
|
531
|
+
["build"],
|
|
532
|
+
);
|
|
533
|
+
expect(result).toEqual(["--verbose", "extra"]);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
it("should preserve flags", () => {
|
|
537
|
+
const cli = createTestCli();
|
|
538
|
+
const result = cli.testRemoveConsumedArgs(
|
|
539
|
+
["--flag", "build", "arg"],
|
|
540
|
+
["build"],
|
|
541
|
+
);
|
|
542
|
+
expect(result).toEqual(["--flag", "arg"]);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it("should remove multiple consumed args", () => {
|
|
546
|
+
const cli = createTestCli();
|
|
547
|
+
const result = cli.testRemoveConsumedArgs(
|
|
548
|
+
["deploy", "vercel", "--prod"],
|
|
549
|
+
["deploy", "vercel"],
|
|
550
|
+
);
|
|
551
|
+
expect(result).toEqual(["--prod"]);
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
556
|
+
// getFlagConsumedIndices
|
|
557
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
558
|
+
|
|
559
|
+
describe("getFlagConsumedIndices", () => {
|
|
560
|
+
it("should mark flag indices as consumed", () => {
|
|
561
|
+
const cli = createTestCli();
|
|
562
|
+
const flagDefs = [
|
|
563
|
+
{ key: "verbose", aliases: ["v", "verbose"], schema: t.boolean() },
|
|
564
|
+
];
|
|
565
|
+
|
|
566
|
+
const result = cli.testGetFlagConsumedIndices(["--verbose"], flagDefs);
|
|
567
|
+
expect(result.has(0)).toBe(true);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it("should mark flag value indices as consumed", () => {
|
|
571
|
+
const cli = createTestCli();
|
|
572
|
+
const flagDefs = [{ key: "name", aliases: ["name"], schema: t.string() }];
|
|
573
|
+
|
|
574
|
+
const result = cli.testGetFlagConsumedIndices(
|
|
575
|
+
["--name", "value", "arg"],
|
|
576
|
+
flagDefs,
|
|
577
|
+
);
|
|
578
|
+
expect(result.has(0)).toBe(true); // --name
|
|
579
|
+
expect(result.has(1)).toBe(true); // value
|
|
580
|
+
expect(result.has(2)).toBe(false); // arg
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it("should not consume next arg for --flag=value syntax", () => {
|
|
584
|
+
const cli = createTestCli();
|
|
585
|
+
const flagDefs = [{ key: "name", aliases: ["name"], schema: t.string() }];
|
|
586
|
+
|
|
587
|
+
const result = cli.testGetFlagConsumedIndices(
|
|
588
|
+
["--name=value", "arg"],
|
|
589
|
+
flagDefs,
|
|
590
|
+
);
|
|
591
|
+
expect(result.has(0)).toBe(true); // --name=value
|
|
592
|
+
expect(result.has(1)).toBe(false); // arg
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it("should consume value for union(boolean, text) when value is provided", () => {
|
|
596
|
+
const cli = createTestCli();
|
|
597
|
+
const flagDefs = [
|
|
598
|
+
{
|
|
599
|
+
key: "image",
|
|
600
|
+
aliases: ["i", "image"],
|
|
601
|
+
schema: t.union([t.boolean(), t.text()]),
|
|
602
|
+
},
|
|
603
|
+
];
|
|
604
|
+
|
|
605
|
+
const result = cli.testGetFlagConsumedIndices(
|
|
606
|
+
["-i", "1.3.4", "arg"],
|
|
607
|
+
flagDefs,
|
|
608
|
+
);
|
|
609
|
+
expect(result.has(0)).toBe(true); // -i
|
|
610
|
+
expect(result.has(1)).toBe(true); // 1.3.4 (consumed as value)
|
|
611
|
+
expect(result.has(2)).toBe(false); // arg
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it("should not consume next arg for union(boolean, text) when next is a flag", () => {
|
|
615
|
+
const cli = createTestCli();
|
|
616
|
+
const flagDefs = [
|
|
617
|
+
{
|
|
618
|
+
key: "image",
|
|
619
|
+
aliases: ["i", "image"],
|
|
620
|
+
schema: t.union([t.boolean(), t.text()]),
|
|
621
|
+
},
|
|
622
|
+
{ key: "verbose", aliases: ["v", "verbose"], schema: t.boolean() },
|
|
623
|
+
];
|
|
624
|
+
|
|
625
|
+
const result = cli.testGetFlagConsumedIndices(["-i", "-v"], flagDefs);
|
|
626
|
+
expect(result.has(0)).toBe(true); // -i
|
|
627
|
+
expect(result.has(1)).toBe(true); // -v (not consumed by -i, but by itself)
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
it("should not consume next arg for union(boolean, text) at end of argv", () => {
|
|
631
|
+
const cli = createTestCli();
|
|
632
|
+
const flagDefs = [
|
|
633
|
+
{
|
|
634
|
+
key: "image",
|
|
635
|
+
aliases: ["i", "image"],
|
|
636
|
+
schema: t.union([t.boolean(), t.text()]),
|
|
637
|
+
},
|
|
638
|
+
];
|
|
639
|
+
|
|
640
|
+
const result = cli.testGetFlagConsumedIndices(["-i"], flagDefs);
|
|
641
|
+
expect(result.has(0)).toBe(true); // -i
|
|
642
|
+
expect(result.size).toBe(1);
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
647
|
+
// generateArgsUsage
|
|
648
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
649
|
+
|
|
650
|
+
describe("generateArgsUsage", () => {
|
|
651
|
+
it("should return empty string for no schema", () => {
|
|
652
|
+
const cli = createTestCli();
|
|
653
|
+
expect(cli.testGenerateArgsUsage(undefined)).toBe("");
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
it("should generate optional arg usage", () => {
|
|
657
|
+
const cli = createTestCli();
|
|
658
|
+
const schema = t.optional(t.string({ title: "path" }));
|
|
659
|
+
expect(cli.testGenerateArgsUsage(schema)).toBe(" [path]");
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
it("should generate required arg usage", () => {
|
|
663
|
+
const cli = createTestCli();
|
|
664
|
+
const schema = t.string({ title: "path" });
|
|
665
|
+
expect(cli.testGenerateArgsUsage(schema)).toBe(" <path>");
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it("should generate tuple arg usage", () => {
|
|
669
|
+
const cli = createTestCli();
|
|
670
|
+
const schema = t.tuple([t.string(), t.optional(t.number())]);
|
|
671
|
+
const result = cli.testGenerateArgsUsage(schema);
|
|
672
|
+
expect(result).toContain("<arg1>");
|
|
673
|
+
expect(result).toContain("[arg2");
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it("should include type for numbers", () => {
|
|
677
|
+
const cli = createTestCli();
|
|
678
|
+
const schema = t.number({ title: "count" });
|
|
679
|
+
expect(cli.testGenerateArgsUsage(schema)).toBe(" <count: number>");
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it("should include type for integers", () => {
|
|
683
|
+
const cli = createTestCli();
|
|
684
|
+
const schema = t.integer({ title: "port" });
|
|
685
|
+
expect(cli.testGenerateArgsUsage(schema)).toBe(" <port: integer>");
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
it("should include type for booleans", () => {
|
|
689
|
+
const cli = createTestCli();
|
|
690
|
+
const schema = t.boolean({ title: "force" });
|
|
691
|
+
expect(cli.testGenerateArgsUsage(schema)).toBe(" <force: boolean>");
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
696
|
+
// getTypeName
|
|
697
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
698
|
+
|
|
699
|
+
describe("getTypeName", () => {
|
|
700
|
+
it("should return empty for string", () => {
|
|
701
|
+
const cli = createTestCli();
|
|
702
|
+
expect(cli.testGetTypeName(t.string())).toBe("");
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
it("should return ': number' for number", () => {
|
|
706
|
+
const cli = createTestCli();
|
|
707
|
+
expect(cli.testGetTypeName(t.number())).toBe(": number");
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
it("should return ': integer' for integer", () => {
|
|
711
|
+
const cli = createTestCli();
|
|
712
|
+
expect(cli.testGetTypeName(t.integer())).toBe(": integer");
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
it("should return ': boolean' for boolean", () => {
|
|
716
|
+
const cli = createTestCli();
|
|
717
|
+
expect(cli.testGetTypeName(t.boolean())).toBe(": boolean");
|
|
718
|
+
});
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
722
|
+
// generateColoredArgsUsage
|
|
723
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
724
|
+
|
|
725
|
+
describe("generateColoredArgsUsage", () => {
|
|
726
|
+
it("should return empty string for no schema", () => {
|
|
727
|
+
const cli = createTestCli();
|
|
728
|
+
expect(cli.testGenerateColoredArgsUsage(undefined)).toBe("");
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it("should generate colored optional arg usage", () => {
|
|
732
|
+
const cli = createTestCli();
|
|
733
|
+
const schema = t.optional(t.string({ title: "path" }));
|
|
734
|
+
const result = cli.testGenerateColoredArgsUsage(schema);
|
|
735
|
+
expect(result).toContain("[path]");
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
it("should generate colored required arg usage", () => {
|
|
739
|
+
const cli = createTestCli();
|
|
740
|
+
const schema = t.string({ title: "path" });
|
|
741
|
+
const result = cli.testGenerateColoredArgsUsage(schema);
|
|
742
|
+
expect(result).toContain("<path>");
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
it("should generate colored tuple arg usage", () => {
|
|
746
|
+
const cli = createTestCli();
|
|
747
|
+
const schema = t.tuple([t.string(), t.optional(t.number())]);
|
|
748
|
+
const result = cli.testGenerateColoredArgsUsage(schema);
|
|
749
|
+
expect(result).toContain("<arg1>");
|
|
750
|
+
expect(result).toContain("[arg2");
|
|
751
|
+
});
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
755
|
+
// findCommand
|
|
756
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
757
|
+
|
|
758
|
+
describe("findCommand", () => {
|
|
759
|
+
it("should return undefined for unknown command", () => {
|
|
760
|
+
const cli = createTestCli();
|
|
761
|
+
expect(cli.testFindCommand("unknown")).toBeUndefined();
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
it("should find command by name", () => {
|
|
765
|
+
class TestCommands {
|
|
766
|
+
test = $command({
|
|
767
|
+
name: "test",
|
|
768
|
+
flags: t.object({}),
|
|
769
|
+
handler: async () => {},
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
774
|
+
const cli = alepha.inject(TestCliProvider);
|
|
775
|
+
|
|
776
|
+
expect(cli.testFindCommand("test")?.name).toBe("test");
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
it("should find command by alias", () => {
|
|
780
|
+
class TestCommands {
|
|
781
|
+
test = $command({
|
|
782
|
+
name: "test",
|
|
783
|
+
aliases: ["t"],
|
|
784
|
+
flags: t.object({}),
|
|
785
|
+
handler: async () => {},
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
790
|
+
const cli = alepha.inject(TestCliProvider);
|
|
791
|
+
|
|
792
|
+
expect(cli.testFindCommand("t")?.name).toBe("test");
|
|
793
|
+
});
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
797
|
+
// findPreHooks / findPostHooks
|
|
798
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
799
|
+
|
|
800
|
+
describe("findPreHooks", () => {
|
|
801
|
+
it("should return empty array when no hooks", () => {
|
|
802
|
+
const cli = createTestCli();
|
|
803
|
+
expect(cli.testFindPreHooks("build")).toEqual([]);
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
it("should find pre-hooks for command", () => {
|
|
807
|
+
class TestCommands {
|
|
808
|
+
preBuild = $command({
|
|
809
|
+
name: "prebuild",
|
|
810
|
+
flags: t.object({}),
|
|
811
|
+
handler: async () => {},
|
|
812
|
+
});
|
|
813
|
+
build = $command({
|
|
814
|
+
name: "build",
|
|
815
|
+
flags: t.object({}),
|
|
816
|
+
handler: async () => {},
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
821
|
+
const cli = alepha.inject(TestCliProvider);
|
|
822
|
+
|
|
823
|
+
expect(cli.testFindPreHooks("build").length).toBe(1);
|
|
824
|
+
});
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
describe("findPostHooks", () => {
|
|
828
|
+
it("should return empty array when no hooks", () => {
|
|
829
|
+
const cli = createTestCli();
|
|
830
|
+
expect(cli.testFindPostHooks("build")).toEqual([]);
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
it("should find post-hooks for command", () => {
|
|
834
|
+
class TestCommands {
|
|
835
|
+
build = $command({
|
|
836
|
+
name: "build",
|
|
837
|
+
flags: t.object({}),
|
|
838
|
+
handler: async () => {},
|
|
839
|
+
});
|
|
840
|
+
postBuild = $command({
|
|
841
|
+
name: "postbuild",
|
|
842
|
+
flags: t.object({}),
|
|
843
|
+
handler: async () => {},
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
848
|
+
const cli = alepha.inject(TestCliProvider);
|
|
849
|
+
|
|
850
|
+
expect(cli.testFindPostHooks("build").length).toBe(1);
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
855
|
+
// getTopLevelCommands
|
|
856
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
857
|
+
|
|
858
|
+
describe("getTopLevelCommands", () => {
|
|
859
|
+
it("should return all commands when no children", () => {
|
|
860
|
+
class TestCommands {
|
|
861
|
+
build = $command({
|
|
862
|
+
name: "build",
|
|
863
|
+
flags: t.object({}),
|
|
864
|
+
handler: async () => {},
|
|
865
|
+
});
|
|
866
|
+
test = $command({
|
|
867
|
+
name: "test",
|
|
868
|
+
flags: t.object({}),
|
|
869
|
+
handler: async () => {},
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
874
|
+
const cli = alepha.inject(TestCliProvider);
|
|
875
|
+
|
|
876
|
+
const topLevel = cli.testGetTopLevelCommands();
|
|
877
|
+
expect(topLevel.length).toBe(2);
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
it("should exclude child commands", () => {
|
|
881
|
+
class TestCommands {
|
|
882
|
+
deployVercel = $command({
|
|
883
|
+
name: "vercel",
|
|
884
|
+
flags: t.object({}),
|
|
885
|
+
handler: async () => {},
|
|
886
|
+
});
|
|
887
|
+
deploy = $command({
|
|
888
|
+
name: "deploy",
|
|
889
|
+
flags: t.object({}),
|
|
890
|
+
children: [this.deployVercel],
|
|
891
|
+
handler: async () => {},
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
896
|
+
const cli = alepha.inject(TestCliProvider);
|
|
897
|
+
|
|
898
|
+
const topLevel = cli.testGetTopLevelCommands();
|
|
899
|
+
expect(topLevel.length).toBe(1);
|
|
900
|
+
expect(topLevel[0].name).toBe("deploy");
|
|
901
|
+
});
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
905
|
+
// findParentCommand
|
|
906
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
907
|
+
|
|
908
|
+
describe("findParentCommand", () => {
|
|
909
|
+
it("should return undefined for top-level command", () => {
|
|
910
|
+
class TestCommands {
|
|
911
|
+
build = $command({
|
|
912
|
+
name: "build",
|
|
913
|
+
flags: t.object({}),
|
|
914
|
+
handler: async () => {},
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
919
|
+
const cli = alepha.inject(TestCliProvider);
|
|
920
|
+
const cmd = cli.testFindCommand("build")!;
|
|
921
|
+
|
|
922
|
+
expect(cli.testFindParentCommand(cmd)).toBeUndefined();
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
it("should find parent of child command", () => {
|
|
926
|
+
class TestCommands {
|
|
927
|
+
deployVercel = $command({
|
|
928
|
+
name: "vercel",
|
|
929
|
+
flags: t.object({}),
|
|
930
|
+
handler: async () => {},
|
|
931
|
+
});
|
|
932
|
+
deploy = $command({
|
|
933
|
+
name: "deploy",
|
|
934
|
+
flags: t.object({}),
|
|
935
|
+
children: [this.deployVercel],
|
|
936
|
+
handler: async () => {},
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
941
|
+
const cli = alepha.inject(TestCliProvider);
|
|
942
|
+
const child = cli.testFindCommand("vercel")!;
|
|
943
|
+
|
|
944
|
+
expect(cli.testFindParentCommand(child)?.name).toBe("deploy");
|
|
945
|
+
});
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
949
|
+
// getCommandPath
|
|
950
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
951
|
+
|
|
952
|
+
describe("getCommandPath", () => {
|
|
953
|
+
it("should return simple name for top-level command", () => {
|
|
954
|
+
class TestCommands {
|
|
955
|
+
build = $command({
|
|
956
|
+
name: "build",
|
|
957
|
+
flags: t.object({}),
|
|
958
|
+
handler: async () => {},
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
963
|
+
const cli = alepha.inject(TestCliProvider);
|
|
964
|
+
const cmd = cli.testFindCommand("build")!;
|
|
965
|
+
|
|
966
|
+
expect(cli.testGetCommandPath(cmd)).toBe("build");
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
it("should return full path for nested command", () => {
|
|
970
|
+
class TestCommands {
|
|
971
|
+
deployVercel = $command({
|
|
972
|
+
name: "vercel",
|
|
973
|
+
flags: t.object({}),
|
|
974
|
+
handler: async () => {},
|
|
975
|
+
});
|
|
976
|
+
deploy = $command({
|
|
977
|
+
name: "deploy",
|
|
978
|
+
flags: t.object({}),
|
|
979
|
+
children: [this.deployVercel],
|
|
980
|
+
handler: async () => {},
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
985
|
+
const cli = alepha.inject(TestCliProvider);
|
|
986
|
+
const child = cli.testFindCommand("vercel")!;
|
|
987
|
+
|
|
988
|
+
expect(cli.testGetCommandPath(child)).toBe("deploy vercel");
|
|
989
|
+
});
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
993
|
+
// printHelp
|
|
994
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
995
|
+
|
|
996
|
+
describe("printHelp", () => {
|
|
997
|
+
it("should print general help when no command provided", () => {
|
|
998
|
+
class TestCommands {
|
|
999
|
+
build = $command({
|
|
1000
|
+
name: "build",
|
|
1001
|
+
description: "Build the project",
|
|
1002
|
+
flags: t.object({}),
|
|
1003
|
+
handler: async () => {},
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1008
|
+
const cli = alepha.inject(TestCliProvider);
|
|
1009
|
+
|
|
1010
|
+
// Just verify it doesn't throw
|
|
1011
|
+
expect(() => cli.printHelp()).not.toThrow();
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
it("should print command-specific help", () => {
|
|
1015
|
+
class TestCommands {
|
|
1016
|
+
build = $command({
|
|
1017
|
+
name: "build",
|
|
1018
|
+
description: "Build the project",
|
|
1019
|
+
flags: t.object({
|
|
1020
|
+
watch: t.optional(t.boolean({ description: "Watch for changes" })),
|
|
1021
|
+
}),
|
|
1022
|
+
handler: async () => {},
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1027
|
+
const cli = alepha.inject(TestCliProvider);
|
|
1028
|
+
const cmd = cli.testFindCommand("build")!;
|
|
1029
|
+
|
|
1030
|
+
expect(() => cli.printHelp(cmd)).not.toThrow();
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
it("should print help for command with children", () => {
|
|
1034
|
+
class TestCommands {
|
|
1035
|
+
deployVercel = $command({
|
|
1036
|
+
name: "vercel",
|
|
1037
|
+
description: "Deploy to Vercel",
|
|
1038
|
+
flags: t.object({}),
|
|
1039
|
+
handler: async () => {},
|
|
1040
|
+
});
|
|
1041
|
+
deploy = $command({
|
|
1042
|
+
name: "deploy",
|
|
1043
|
+
description: "Deploy commands",
|
|
1044
|
+
flags: t.object({}),
|
|
1045
|
+
children: [this.deployVercel],
|
|
1046
|
+
handler: async () => {},
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1051
|
+
const cli = alepha.inject(TestCliProvider);
|
|
1052
|
+
const cmd = cli.testFindCommand("deploy")!;
|
|
1053
|
+
|
|
1054
|
+
expect(() => cli.printHelp(cmd)).not.toThrow();
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
it("should print help for command with env vars", () => {
|
|
1058
|
+
class TestCommands {
|
|
1059
|
+
deploy = $command({
|
|
1060
|
+
name: "deploy",
|
|
1061
|
+
description: "Deploy to production",
|
|
1062
|
+
flags: t.object({}),
|
|
1063
|
+
env: t.object({
|
|
1064
|
+
API_KEY: t.string({ description: "API key for deployment" }),
|
|
1065
|
+
REGION: t.optional(t.string({ description: "Target region" })),
|
|
1066
|
+
}),
|
|
1067
|
+
handler: async () => {},
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1072
|
+
const cli = alepha.inject(TestCliProvider);
|
|
1073
|
+
const cmd = cli.testFindCommand("deploy")!;
|
|
1074
|
+
|
|
1075
|
+
expect(() => cli.printHelp(cmd)).not.toThrow();
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
it("should print help for command with mode option", () => {
|
|
1079
|
+
class TestCommands {
|
|
1080
|
+
build = $command({
|
|
1081
|
+
name: "build",
|
|
1082
|
+
description: "Build for environment",
|
|
1083
|
+
flags: t.object({}),
|
|
1084
|
+
mode: "development",
|
|
1085
|
+
handler: async () => {},
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1090
|
+
const cli = alepha.inject(TestCliProvider);
|
|
1091
|
+
const cmd = cli.testFindCommand("build")!;
|
|
1092
|
+
|
|
1093
|
+
expect(() => cli.printHelp(cmd)).not.toThrow();
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
it("should print help for command with args", () => {
|
|
1097
|
+
class TestCommands {
|
|
1098
|
+
greet = $command({
|
|
1099
|
+
name: "greet",
|
|
1100
|
+
description: "Greet someone",
|
|
1101
|
+
flags: t.object({}),
|
|
1102
|
+
args: t.string({ title: "name" }),
|
|
1103
|
+
handler: async () => {},
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1108
|
+
const cli = alepha.inject(TestCliProvider);
|
|
1109
|
+
const cmd = cli.testFindCommand("greet")!;
|
|
1110
|
+
|
|
1111
|
+
expect(() => cli.printHelp(cmd)).not.toThrow();
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
it("should hide commands with hide option in general help", () => {
|
|
1115
|
+
class TestCommands {
|
|
1116
|
+
visible = $command({
|
|
1117
|
+
name: "visible",
|
|
1118
|
+
description: "Visible command",
|
|
1119
|
+
flags: t.object({}),
|
|
1120
|
+
handler: async () => {},
|
|
1121
|
+
});
|
|
1122
|
+
hidden = $command({
|
|
1123
|
+
name: "hidden",
|
|
1124
|
+
description: "Hidden command",
|
|
1125
|
+
hide: true,
|
|
1126
|
+
flags: t.object({}),
|
|
1127
|
+
handler: async () => {},
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1132
|
+
const cli = alepha.inject(TestCliProvider);
|
|
1133
|
+
|
|
1134
|
+
// Just verify it doesn't throw - hidden commands filtered in getTopLevelCommands
|
|
1135
|
+
expect(() => cli.printHelp()).not.toThrow();
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
it("should include flag aliases in help output", () => {
|
|
1139
|
+
const cli = createTestCli();
|
|
1140
|
+
|
|
1141
|
+
const flagsSchema = t.object({
|
|
1142
|
+
target: t.optional(
|
|
1143
|
+
t.enum(["bare", "docker", "vercel"], {
|
|
1144
|
+
aliases: ["t"],
|
|
1145
|
+
description: "Deployment target",
|
|
1146
|
+
}),
|
|
1147
|
+
),
|
|
1148
|
+
runtime: t.optional(
|
|
1149
|
+
t.enum(["node", "bun"], {
|
|
1150
|
+
alias: "r", // singular alias
|
|
1151
|
+
description: "JavaScript runtime",
|
|
1152
|
+
}),
|
|
1153
|
+
),
|
|
1154
|
+
verbose: t.optional(
|
|
1155
|
+
t.boolean({
|
|
1156
|
+
description: "Verbose output",
|
|
1157
|
+
}),
|
|
1158
|
+
),
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
const flagDefs = cli.testExtractFlagDefs(flagsSchema);
|
|
1162
|
+
|
|
1163
|
+
// target should have key + aliases array
|
|
1164
|
+
const targetFlag = flagDefs.find((f) => f.key === "target");
|
|
1165
|
+
expect(targetFlag?.aliases).toContain("target");
|
|
1166
|
+
expect(targetFlag?.aliases).toContain("t");
|
|
1167
|
+
expect(cli.testFormatFlagStr(targetFlag!.aliases)).toBe("-t, --target");
|
|
1168
|
+
|
|
1169
|
+
// runtime should have key + alias (singular)
|
|
1170
|
+
const runtimeFlag = flagDefs.find((f) => f.key === "runtime");
|
|
1171
|
+
expect(runtimeFlag?.aliases).toContain("runtime");
|
|
1172
|
+
expect(runtimeFlag?.aliases).toContain("r");
|
|
1173
|
+
expect(cli.testFormatFlagStr(runtimeFlag!.aliases)).toBe("-r, --runtime");
|
|
1174
|
+
|
|
1175
|
+
// verbose should only have key (no aliases defined)
|
|
1176
|
+
const verboseFlag = flagDefs.find((f) => f.key === "verbose");
|
|
1177
|
+
expect(verboseFlag?.aliases).toEqual(["verbose"]);
|
|
1178
|
+
expect(cli.testFormatFlagStr(verboseFlag!.aliases)).toBe("--verbose");
|
|
1179
|
+
});
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1182
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1183
|
+
// getEnumValues
|
|
1184
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1185
|
+
|
|
1186
|
+
describe("getEnumValues", () => {
|
|
1187
|
+
it("should extract values from t.enum schema", () => {
|
|
1188
|
+
const cli = createTestCli();
|
|
1189
|
+
const schema = t.enum(["yarn", "npm", "pnpm", "bun"]);
|
|
1190
|
+
|
|
1191
|
+
const values = cli.testGetEnumValues(schema);
|
|
1192
|
+
|
|
1193
|
+
expect(values).toEqual(["yarn", "npm", "pnpm", "bun"]);
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
it("should return undefined for non-enum schemas", () => {
|
|
1197
|
+
const cli = createTestCli();
|
|
1198
|
+
|
|
1199
|
+
expect(cli.testGetEnumValues(t.string())).toBeUndefined();
|
|
1200
|
+
expect(cli.testGetEnumValues(t.boolean())).toBeUndefined();
|
|
1201
|
+
expect(cli.testGetEnumValues(t.number())).toBeUndefined();
|
|
1202
|
+
expect(cli.testGetEnumValues(t.object({}))).toBeUndefined();
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
it("should return undefined for empty or undefined schema", () => {
|
|
1206
|
+
const cli = createTestCli();
|
|
1207
|
+
|
|
1208
|
+
expect(cli.testGetEnumValues(undefined as any)).toBeUndefined();
|
|
1209
|
+
});
|
|
1210
|
+
});
|
|
1211
|
+
|
|
1212
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1213
|
+
// formatFlagDescription
|
|
1214
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1215
|
+
|
|
1216
|
+
describe("formatFlagDescription", () => {
|
|
1217
|
+
it("should append enum values to description", () => {
|
|
1218
|
+
const cli = createTestCli();
|
|
1219
|
+
const schema = t.enum(["yarn", "npm", "pnpm", "bun"]);
|
|
1220
|
+
|
|
1221
|
+
const result = cli.testFormatFlagDescription(
|
|
1222
|
+
"Package manager to use",
|
|
1223
|
+
schema,
|
|
1224
|
+
);
|
|
1225
|
+
|
|
1226
|
+
expect(result).toContain("Package manager to use");
|
|
1227
|
+
expect(result).toContain("yarn");
|
|
1228
|
+
expect(result).toContain("npm");
|
|
1229
|
+
expect(result).toContain("pnpm");
|
|
1230
|
+
expect(result).toContain("bun");
|
|
1231
|
+
});
|
|
1232
|
+
|
|
1233
|
+
it("should return only enum values when description is empty", () => {
|
|
1234
|
+
const cli = createTestCli();
|
|
1235
|
+
const schema = t.enum(["a", "b", "c"]);
|
|
1236
|
+
|
|
1237
|
+
const result = cli.testFormatFlagDescription(undefined, schema);
|
|
1238
|
+
|
|
1239
|
+
expect(result).toContain("a");
|
|
1240
|
+
expect(result).toContain("b");
|
|
1241
|
+
expect(result).toContain("c");
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
it("should return original description for non-enum schemas", () => {
|
|
1245
|
+
const cli = createTestCli();
|
|
1246
|
+
|
|
1247
|
+
expect(cli.testFormatFlagDescription("A string flag", t.string())).toBe(
|
|
1248
|
+
"A string flag",
|
|
1249
|
+
);
|
|
1250
|
+
expect(cli.testFormatFlagDescription("A boolean flag", t.boolean())).toBe(
|
|
1251
|
+
"A boolean flag",
|
|
1252
|
+
);
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
it("should return empty string when no description and no enum", () => {
|
|
1256
|
+
const cli = createTestCli();
|
|
1257
|
+
|
|
1258
|
+
expect(cli.testFormatFlagDescription(undefined, t.string())).toBe("");
|
|
1259
|
+
});
|
|
1260
|
+
});
|
|
1261
|
+
|
|
1262
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1263
|
+
// commands getter
|
|
1264
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1265
|
+
|
|
1266
|
+
describe("commands", () => {
|
|
1267
|
+
it("should return all registered commands", () => {
|
|
1268
|
+
class TestCommands {
|
|
1269
|
+
build = $command({
|
|
1270
|
+
name: "build",
|
|
1271
|
+
flags: t.object({}),
|
|
1272
|
+
handler: async () => {},
|
|
1273
|
+
});
|
|
1274
|
+
test = $command({
|
|
1275
|
+
name: "test",
|
|
1276
|
+
flags: t.object({}),
|
|
1277
|
+
handler: async () => {},
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1282
|
+
const cli = alepha.inject(TestCliProvider);
|
|
1283
|
+
|
|
1284
|
+
expect(cli.commands.length).toBe(2);
|
|
1285
|
+
expect(cli.commands.map((c) => c.name).sort()).toEqual(["build", "test"]);
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
it("should return empty array when no commands", () => {
|
|
1289
|
+
const cli = createTestCli();
|
|
1290
|
+
expect(cli.commands).toEqual([]);
|
|
1291
|
+
});
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1295
|
+
// run (test helper)
|
|
1296
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1297
|
+
|
|
1298
|
+
describe("run", () => {
|
|
1299
|
+
it("should execute command with string args", async () => {
|
|
1300
|
+
let capturedFlags: Record<string, unknown> = {};
|
|
1301
|
+
|
|
1302
|
+
class TestCommands {
|
|
1303
|
+
build = $command({
|
|
1304
|
+
name: "build",
|
|
1305
|
+
flags: t.object({
|
|
1306
|
+
watch: t.optional(t.boolean()),
|
|
1307
|
+
}),
|
|
1308
|
+
handler: async ({ flags }) => {
|
|
1309
|
+
capturedFlags = flags;
|
|
1310
|
+
},
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1315
|
+
const cli = alepha.inject(CliProvider);
|
|
1316
|
+
const cmd = alepha.inject(TestCommands);
|
|
1317
|
+
|
|
1318
|
+
await cli.run(cmd.build, "--watch");
|
|
1319
|
+
|
|
1320
|
+
expect(capturedFlags.watch).toBe(true);
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
it("should execute command with array args", async () => {
|
|
1324
|
+
let capturedFlags: Record<string, unknown> = {};
|
|
1325
|
+
|
|
1326
|
+
class TestCommands {
|
|
1327
|
+
greet = $command({
|
|
1328
|
+
name: "greet",
|
|
1329
|
+
flags: t.object({
|
|
1330
|
+
name: t.optional(t.string()),
|
|
1331
|
+
}),
|
|
1332
|
+
handler: async ({ flags }) => {
|
|
1333
|
+
capturedFlags = flags;
|
|
1334
|
+
},
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1339
|
+
const cli = alepha.inject(CliProvider);
|
|
1340
|
+
const cmd = alepha.inject(TestCommands);
|
|
1341
|
+
|
|
1342
|
+
await cli.run(cmd.greet, ["--name", "World"]);
|
|
1343
|
+
|
|
1344
|
+
expect(capturedFlags.name).toBe("World");
|
|
1345
|
+
});
|
|
1346
|
+
|
|
1347
|
+
it("should execute command with options object", async () => {
|
|
1348
|
+
let capturedRoot = "";
|
|
1349
|
+
|
|
1350
|
+
class TestCommands {
|
|
1351
|
+
init = $command({
|
|
1352
|
+
name: "init",
|
|
1353
|
+
flags: t.object({}),
|
|
1354
|
+
handler: async ({ root }) => {
|
|
1355
|
+
capturedRoot = root;
|
|
1356
|
+
},
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1361
|
+
const cli = alepha.inject(CliProvider);
|
|
1362
|
+
const cmd = alepha.inject(TestCommands);
|
|
1363
|
+
|
|
1364
|
+
await cli.run(cmd.init, { root: "/custom/path" });
|
|
1365
|
+
|
|
1366
|
+
expect(capturedRoot).toBe("/custom/path");
|
|
1367
|
+
});
|
|
1368
|
+
|
|
1369
|
+
it("should parse command args", async () => {
|
|
1370
|
+
let capturedArgs: unknown;
|
|
1371
|
+
|
|
1372
|
+
class TestCommands {
|
|
1373
|
+
greet = $command({
|
|
1374
|
+
name: "greet",
|
|
1375
|
+
flags: t.object({}),
|
|
1376
|
+
args: t.string(),
|
|
1377
|
+
handler: async ({ args }) => {
|
|
1378
|
+
capturedArgs = args;
|
|
1379
|
+
},
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
const alepha = Alepha.create().with(TestCommands);
|
|
1384
|
+
const cli = alepha.inject(CliProvider);
|
|
1385
|
+
const cmd = alepha.inject(TestCommands);
|
|
1386
|
+
|
|
1387
|
+
await cli.run(cmd.greet, "World");
|
|
1388
|
+
|
|
1389
|
+
expect(capturedArgs).toBe("World");
|
|
1390
|
+
});
|
|
1391
|
+
});
|
|
1392
|
+
});
|