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
|
@@ -83,29 +83,53 @@ describe("ServerRateLimitProvider", () => {
|
|
|
83
83
|
expect(result2.allowed).toBe(true);
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
-
it("should
|
|
86
|
+
it("should use req.ip for rate limiting (trust proxy handled by ServerRequestParser)", async () => {
|
|
87
87
|
const options = { max: 1, windowMs: 60000 };
|
|
88
88
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
req1
|
|
92
|
-
|
|
89
|
+
// Rate limit uses req.ip which is resolved by ServerRequestParser
|
|
90
|
+
// Trust proxy configuration is at server level via TRUST_PROXY env var
|
|
91
|
+
const req1 = createMockRequest("192.168.1.100");
|
|
93
92
|
const result1 = await provider.checkLimit(req1, options);
|
|
94
93
|
expect(result1.allowed).toBe(true);
|
|
95
94
|
|
|
96
|
-
//
|
|
97
|
-
const req2 = createMockRequest("
|
|
98
|
-
req2.headers["x-forwarded-for"] = "203.0.113.1, 192.168.1.1";
|
|
99
|
-
|
|
95
|
+
// Same IP should be blocked
|
|
96
|
+
const req2 = createMockRequest("192.168.1.100");
|
|
100
97
|
const result2 = await provider.checkLimit(req2, options);
|
|
101
98
|
expect(result2.allowed).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should handle truly concurrent requests atomically", async () => {
|
|
102
|
+
const options = { max: 3, windowMs: 60000 };
|
|
103
|
+
|
|
104
|
+
// Fire 5 requests concurrently from the same IP
|
|
105
|
+
const requests = Array.from({ length: 5 }, () =>
|
|
106
|
+
createMockRequest("10.0.0.1"),
|
|
107
|
+
);
|
|
108
|
+
const results = await Promise.all(
|
|
109
|
+
requests.map((req) => provider.checkLimit(req, options)),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Exactly 3 should be allowed, 2 should be blocked
|
|
113
|
+
const allowed = results.filter((r) => r.allowed).length;
|
|
114
|
+
const blocked = results.filter((r) => !r.allowed).length;
|
|
115
|
+
|
|
116
|
+
expect(allowed).toBe(3);
|
|
117
|
+
expect(blocked).toBe(2);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should return correct resetTime within the fixed window", async () => {
|
|
121
|
+
const windowMs = 60000;
|
|
122
|
+
const options = { max: 10, windowMs };
|
|
123
|
+
const req = createMockRequest();
|
|
124
|
+
|
|
125
|
+
const result = await provider.checkLimit(req, options);
|
|
102
126
|
|
|
103
|
-
//
|
|
104
|
-
const
|
|
105
|
-
|
|
127
|
+
// resetTime should be at the end of the current window
|
|
128
|
+
const now = Date.now();
|
|
129
|
+
const windowStart = Math.floor(now / windowMs) * windowMs;
|
|
130
|
+
const expectedResetTime = windowStart + windowMs;
|
|
106
131
|
|
|
107
|
-
|
|
108
|
-
expect(result3.allowed).toBe(true);
|
|
132
|
+
expect(result.resetTime).toBe(expectedResetTime);
|
|
109
133
|
});
|
|
110
134
|
});
|
|
111
135
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { $atom, $env, $hook, $inject, $use, type Static, t } from "alepha";
|
|
2
|
-
import {
|
|
2
|
+
import { CacheProvider } from "alepha/cache";
|
|
3
3
|
import { $logger } from "alepha/logger";
|
|
4
4
|
import {
|
|
5
5
|
HttpError,
|
|
@@ -74,14 +74,11 @@ export class ServerRateLimitProvider {
|
|
|
74
74
|
protected readonly log = $logger();
|
|
75
75
|
protected readonly serverRouterProvider = $inject(ServerRouterProvider);
|
|
76
76
|
protected readonly env = $env(envSchema);
|
|
77
|
-
|
|
78
|
-
protected readonly cache = $cache<RateLimitData>({
|
|
79
|
-
name: "server-rate-limit",
|
|
80
|
-
ttl: [this.env.RATE_LIMIT_WINDOW_MS, "milliseconds"],
|
|
81
|
-
});
|
|
82
|
-
|
|
77
|
+
protected readonly cacheProvider = $inject(CacheProvider);
|
|
83
78
|
protected readonly globalOptions = $use(rateLimitOptions);
|
|
84
79
|
|
|
80
|
+
protected static readonly CACHE_NAME = "rate-limit";
|
|
81
|
+
|
|
85
82
|
/**
|
|
86
83
|
* Registered rate limit configurations with their path patterns
|
|
87
84
|
*/
|
|
@@ -210,42 +207,30 @@ export class ServerRateLimitProvider {
|
|
|
210
207
|
): Promise<RateLimitResult> {
|
|
211
208
|
const windowMs = options.windowMs ?? this.env.RATE_LIMIT_WINDOW_MS;
|
|
212
209
|
const max = options.max ?? this.env.RATE_LIMIT_MAX_REQUESTS;
|
|
213
|
-
const
|
|
210
|
+
const baseKey = this.generateKey(req);
|
|
214
211
|
|
|
215
212
|
const now = Date.now();
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
213
|
+
// Fixed window: round down to nearest window boundary
|
|
214
|
+
const windowStart = Math.floor(now / windowMs) * windowMs;
|
|
215
|
+
const resetTime = windowStart + windowMs;
|
|
216
|
+
|
|
217
|
+
// Include window timestamp in key for automatic expiration of old windows
|
|
218
|
+
const key = `${baseKey}:${windowStart}`;
|
|
219
|
+
|
|
220
|
+
// Atomic increment - returns the new count after incrementing
|
|
221
|
+
const count = await this.cacheProvider.incr(
|
|
222
|
+
ServerRateLimitProvider.CACHE_NAME,
|
|
223
|
+
key,
|
|
224
|
+
1,
|
|
228
225
|
);
|
|
229
226
|
|
|
230
|
-
|
|
231
|
-
const
|
|
232
|
-
const remaining = Math.max(0, max - validHits.length);
|
|
233
|
-
const resetTime = Math.max(...validHits, windowStart) + windowMs;
|
|
234
|
-
|
|
235
|
-
// If allowed, record this request
|
|
236
|
-
if (allowed) {
|
|
237
|
-
validHits.push(now);
|
|
238
|
-
await this.cache.set(key, {
|
|
239
|
-
count: validHits.length,
|
|
240
|
-
windowStart: Math.min(currentData.windowStart, windowStart),
|
|
241
|
-
hits: validHits,
|
|
242
|
-
});
|
|
243
|
-
}
|
|
227
|
+
const allowed = count <= max;
|
|
228
|
+
const remaining = Math.max(0, max - count);
|
|
244
229
|
|
|
245
230
|
const result: RateLimitResult = {
|
|
246
231
|
allowed,
|
|
247
232
|
limit: max,
|
|
248
|
-
remaining
|
|
233
|
+
remaining,
|
|
249
234
|
resetTime,
|
|
250
235
|
};
|
|
251
236
|
|
|
@@ -257,26 +242,7 @@ export class ServerRateLimitProvider {
|
|
|
257
242
|
}
|
|
258
243
|
|
|
259
244
|
protected generateKey(req: ServerRequest): string {
|
|
260
|
-
//
|
|
261
|
-
|
|
262
|
-
return `ip:${ip}`;
|
|
245
|
+
// Use req.ip which is resolved by ServerRequestParser with proper trust proxy handling
|
|
246
|
+
return `ip:${req.ip || "unknown"}`;
|
|
263
247
|
}
|
|
264
|
-
|
|
265
|
-
protected getClientIP(req: ServerRequest): string {
|
|
266
|
-
// Check x-forwarded-for header first (for proxies/load balancers)
|
|
267
|
-
const forwarded = req.headers?.["x-forwarded-for"];
|
|
268
|
-
if (forwarded) {
|
|
269
|
-
// x-forwarded-for can contain multiple IPs, get the first one (original client)
|
|
270
|
-
const firstIp = forwarded.split(",")[0].trim();
|
|
271
|
-
if (firstIp) return firstIp;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return req.ip || "unknown";
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
interface RateLimitData {
|
|
279
|
-
count: number;
|
|
280
|
-
windowStart: number;
|
|
281
|
-
hits: number[];
|
|
282
248
|
}
|
|
@@ -11,9 +11,15 @@ export * from "./providers/ServerStaticProvider.ts";
|
|
|
11
11
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* | type | quality | stability |
|
|
15
|
+
* |------|---------|-----------|
|
|
16
|
+
* | backend | standard | stable |
|
|
17
|
+
*
|
|
18
|
+
* Static file serving.
|
|
19
|
+
*
|
|
20
|
+
* **Features:**
|
|
21
|
+
* - Serve static files from directory
|
|
15
22
|
*
|
|
16
|
-
* @see {@link ServerStaticProvider}
|
|
17
23
|
* @module alepha.server.static
|
|
18
24
|
*/
|
|
19
25
|
export const AlephaServerStatic = $module({
|
|
@@ -4,9 +4,9 @@ import { basename, isAbsolute, join } from "node:path";
|
|
|
4
4
|
import type { Readable as NodeStream } from "node:stream";
|
|
5
5
|
import { $hook, $inject, Alepha } from "alepha";
|
|
6
6
|
import { DateTimeProvider } from "alepha/datetime";
|
|
7
|
-
import { FileDetector } from "alepha/file";
|
|
8
7
|
import { $logger } from "alepha/logger";
|
|
9
8
|
import { type ServerHandler, ServerRouterProvider } from "alepha/server";
|
|
9
|
+
import { FileDetector } from "alepha/system";
|
|
10
10
|
import { $serve, type ServePrimitiveOptions } from "../primitives/$serve.ts";
|
|
11
11
|
|
|
12
12
|
export class ServerStaticProvider {
|
|
@@ -30,11 +30,16 @@ declare module "alepha/server" {
|
|
|
30
30
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* | type | quality | stability |
|
|
34
|
+
* |------|---------|-----------|
|
|
35
|
+
* | backend | standard | stable |
|
|
36
|
+
*
|
|
37
|
+
* Automatic API documentation generation.
|
|
38
|
+
*
|
|
39
|
+
* **Features:**
|
|
40
|
+
* - Swagger/OpenAPI configuration
|
|
41
|
+
* - Routes: `GET /swagger/ui`, `GET /swagger.json`
|
|
36
42
|
*
|
|
37
|
-
* @see {@link ServerSwaggerProvider}
|
|
38
43
|
* @module alepha.server.swagger
|
|
39
44
|
*/
|
|
40
45
|
export const AlephaServerSwagger = $module({
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
type TSchema,
|
|
13
13
|
t,
|
|
14
14
|
} from "alepha";
|
|
15
|
-
import { FileSystemProvider } from "alepha/file";
|
|
16
15
|
import { $logger } from "alepha/logger";
|
|
17
16
|
import { AlephaSecurity } from "alepha/security";
|
|
18
17
|
import {
|
|
@@ -23,6 +22,7 @@ import {
|
|
|
23
22
|
ServerRouterProvider,
|
|
24
23
|
} from "alepha/server";
|
|
25
24
|
import { ServerStaticProvider } from "alepha/server/static";
|
|
25
|
+
import { FileSystemProvider } from "alepha/system";
|
|
26
26
|
import {
|
|
27
27
|
$swagger,
|
|
28
28
|
type OpenApiDocument,
|
package/src/sms/index.ts
CHANGED
|
@@ -34,13 +34,17 @@ declare module "alepha" {
|
|
|
34
34
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
37
|
+
* | type | quality | stability |
|
|
38
|
+
* |------|---------|-----------|
|
|
39
|
+
* | backend | rare | stable |
|
|
38
40
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
41
|
+
* SMS delivery with multiple provider support.
|
|
42
|
+
*
|
|
43
|
+
* **Features:**
|
|
44
|
+
* - Send SMS with templates
|
|
45
|
+
* - Multiple recipients
|
|
46
|
+
* - Provider abstraction
|
|
42
47
|
*
|
|
43
|
-
* @see {@link SmsProvider}
|
|
44
48
|
* @module alepha.sms
|
|
45
49
|
*/
|
|
46
50
|
export const AlephaSms = $module({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Alepha } from "alepha";
|
|
2
|
-
import { FileSystemProvider, MemoryFileSystemProvider } from "alepha/
|
|
2
|
+
import { FileSystemProvider, MemoryFileSystemProvider } from "alepha/system";
|
|
3
3
|
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
4
4
|
import { SmsError } from "../errors/SmsError.ts";
|
|
5
5
|
import { LocalSmsProvider } from "./LocalSmsProvider.ts";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { $inject } from "alepha";
|
|
2
|
-
import { FileSystemProvider } from "alepha/file";
|
|
3
2
|
import { $logger } from "alepha/logger";
|
|
3
|
+
import { FileSystemProvider } from "alepha/system";
|
|
4
4
|
import { SmsError } from "../errors/SmsError.ts";
|
|
5
5
|
import type { SmsProvider, SmsSendOptions } from "./SmsProvider.ts";
|
|
6
6
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { $module } from "alepha";
|
|
2
|
+
import { FileSystemProvider } from "./providers/FileSystemProvider.ts";
|
|
3
|
+
import { MemoryFileSystemProvider } from "./providers/MemoryFileSystemProvider.ts";
|
|
4
|
+
import { MemoryShellProvider } from "./providers/MemoryShellProvider.ts";
|
|
5
|
+
import { ShellProvider } from "./providers/ShellProvider.ts";
|
|
6
|
+
import { FileDetector } from "./services/FileDetector.ts";
|
|
7
|
+
|
|
8
|
+
export * from "./errors/FileError.ts";
|
|
9
|
+
export * from "./providers/FileSystemProvider.ts";
|
|
10
|
+
export * from "./providers/MemoryFileSystemProvider.ts";
|
|
11
|
+
export * from "./providers/MemoryShellProvider.ts";
|
|
12
|
+
export * from "./providers/ShellProvider.ts";
|
|
13
|
+
export * from "./services/FileDetector.ts";
|
|
14
|
+
|
|
15
|
+
export const AlephaSystem = $module({
|
|
16
|
+
name: "alepha.system",
|
|
17
|
+
services: [
|
|
18
|
+
FileDetector,
|
|
19
|
+
FileSystemProvider,
|
|
20
|
+
MemoryFileSystemProvider,
|
|
21
|
+
ShellProvider,
|
|
22
|
+
MemoryShellProvider,
|
|
23
|
+
],
|
|
24
|
+
register: (alepha) =>
|
|
25
|
+
alepha
|
|
26
|
+
.with({
|
|
27
|
+
optional: true,
|
|
28
|
+
provide: FileSystemProvider,
|
|
29
|
+
use: MemoryFileSystemProvider,
|
|
30
|
+
})
|
|
31
|
+
.with({
|
|
32
|
+
optional: true,
|
|
33
|
+
provide: ShellProvider,
|
|
34
|
+
use: MemoryShellProvider,
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { $module } from "alepha";
|
|
2
|
+
import { FileSystemProvider } from "./providers/FileSystemProvider.ts";
|
|
3
|
+
import { MemoryFileSystemProvider } from "./providers/MemoryFileSystemProvider.ts";
|
|
4
|
+
import { MemoryShellProvider } from "./providers/MemoryShellProvider.ts";
|
|
5
|
+
import { NodeFileSystemProvider } from "./providers/NodeFileSystemProvider.ts";
|
|
6
|
+
import { NodeShellProvider } from "./providers/NodeShellProvider.ts";
|
|
7
|
+
import { ShellProvider } from "./providers/ShellProvider.ts";
|
|
8
|
+
import { FileDetector } from "./services/FileDetector.ts";
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
export * from "./errors/FileError.ts";
|
|
13
|
+
export * from "./providers/FileSystemProvider.ts";
|
|
14
|
+
export * from "./providers/MemoryFileSystemProvider.ts";
|
|
15
|
+
export * from "./providers/MemoryShellProvider.ts";
|
|
16
|
+
export * from "./providers/NodeFileSystemProvider.ts";
|
|
17
|
+
export * from "./providers/NodeShellProvider.ts";
|
|
18
|
+
export * from "./providers/ShellProvider.ts";
|
|
19
|
+
export * from "./services/FileDetector.ts";
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* | type | quality | stability |
|
|
25
|
+
* |------|---------|-----------|
|
|
26
|
+
* | tooling | standard | stable |
|
|
27
|
+
*
|
|
28
|
+
* System-level abstractions for portable code across runtimes.
|
|
29
|
+
*
|
|
30
|
+
* **Features:**
|
|
31
|
+
* - File system operations (read, write, exists, etc.)
|
|
32
|
+
* - Shell command execution
|
|
33
|
+
* - File type detection and MIME utilities
|
|
34
|
+
* - Memory implementations for testing
|
|
35
|
+
*
|
|
36
|
+
* @module alepha.system
|
|
37
|
+
*/
|
|
38
|
+
export const AlephaSystem = $module({
|
|
39
|
+
name: "alepha.system",
|
|
40
|
+
primitives: [],
|
|
41
|
+
services: [
|
|
42
|
+
FileDetector,
|
|
43
|
+
FileSystemProvider,
|
|
44
|
+
MemoryFileSystemProvider,
|
|
45
|
+
NodeFileSystemProvider,
|
|
46
|
+
ShellProvider,
|
|
47
|
+
MemoryShellProvider,
|
|
48
|
+
NodeShellProvider,
|
|
49
|
+
],
|
|
50
|
+
register: (alepha) =>
|
|
51
|
+
alepha
|
|
52
|
+
.with({
|
|
53
|
+
optional: true,
|
|
54
|
+
provide: FileSystemProvider,
|
|
55
|
+
use: NodeFileSystemProvider,
|
|
56
|
+
})
|
|
57
|
+
.with({
|
|
58
|
+
optional: true,
|
|
59
|
+
provide: ShellProvider,
|
|
60
|
+
use: alepha.isTest() ? MemoryShellProvider : NodeShellProvider,
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./index.browser.ts";
|
|
@@ -196,8 +196,16 @@ export interface CpOptions {
|
|
|
196
196
|
export interface MkdirOptions {
|
|
197
197
|
/**
|
|
198
198
|
* If true, creates parent directories as needed
|
|
199
|
+
*
|
|
200
|
+
* @default true
|
|
199
201
|
*/
|
|
200
202
|
recursive?: boolean;
|
|
203
|
+
/**
|
|
204
|
+
* If true, does not throw an error if the directory already exists
|
|
205
|
+
*
|
|
206
|
+
* @default true
|
|
207
|
+
*/
|
|
208
|
+
force?: boolean;
|
|
201
209
|
/**
|
|
202
210
|
* File mode (permission and sticky bits)
|
|
203
211
|
*/
|
|
@@ -306,4 +314,20 @@ export abstract class FileSystemProvider {
|
|
|
306
314
|
path: string,
|
|
307
315
|
data: Uint8Array | Buffer | string | FileLike,
|
|
308
316
|
): Promise<void>;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Reads the content of a file as a string.
|
|
320
|
+
*
|
|
321
|
+
* @param path - The file path to read
|
|
322
|
+
* @returns The file content as a string
|
|
323
|
+
*/
|
|
324
|
+
abstract readTextFile(path: string): Promise<string>;
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Reads the content of a file as JSON.
|
|
328
|
+
*
|
|
329
|
+
* @param path - The file path to read
|
|
330
|
+
* @returns The parsed JSON content
|
|
331
|
+
*/
|
|
332
|
+
abstract readJsonFile<T = unknown>(path: string): Promise<T>;
|
|
309
333
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { join as nodeJoin } from "node:path";
|
|
2
|
+
import { $inject, type FileLike, Json } from "alepha";
|
|
2
3
|
import type {
|
|
3
4
|
CpOptions,
|
|
4
5
|
CreateFileOptions,
|
|
@@ -51,6 +52,8 @@ export interface MemoryFileSystemProviderOptions {
|
|
|
51
52
|
* ```
|
|
52
53
|
*/
|
|
53
54
|
export class MemoryFileSystemProvider implements FileSystemProvider {
|
|
55
|
+
protected json = $inject(Json);
|
|
56
|
+
|
|
54
57
|
/**
|
|
55
58
|
* In-memory storage for files (path -> content)
|
|
56
59
|
*/
|
|
@@ -71,6 +74,16 @@ export class MemoryFileSystemProvider implements FileSystemProvider {
|
|
|
71
74
|
*/
|
|
72
75
|
public writeFileCalls: Array<{ path: string; data: string }> = [];
|
|
73
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Track readFile calls for test assertions
|
|
79
|
+
*/
|
|
80
|
+
public readFileCalls: Array<string> = [];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Track rm calls for test assertions
|
|
84
|
+
*/
|
|
85
|
+
public rmCalls: Array<{ path: string; options?: RmOptions }> = [];
|
|
86
|
+
|
|
74
87
|
/**
|
|
75
88
|
* Track join calls for test assertions
|
|
76
89
|
*/
|
|
@@ -99,17 +112,42 @@ export class MemoryFileSystemProvider implements FileSystemProvider {
|
|
|
99
112
|
|
|
100
113
|
/**
|
|
101
114
|
* Join path segments using forward slashes.
|
|
115
|
+
* Uses Node's path.join for proper normalization (handles .. and .)
|
|
102
116
|
*/
|
|
103
117
|
public join(...paths: string[]): string {
|
|
104
118
|
this.joinCalls.push(paths);
|
|
105
|
-
|
|
106
|
-
return paths.filter(Boolean).join("/").replace(/\/+/g, "/");
|
|
119
|
+
return nodeJoin(...paths);
|
|
107
120
|
}
|
|
108
121
|
|
|
109
122
|
/**
|
|
110
123
|
* Create a FileLike object from various sources.
|
|
111
124
|
*/
|
|
112
125
|
public createFile(options: CreateFileOptions): FileLike {
|
|
126
|
+
if ("path" in options) {
|
|
127
|
+
const filePath = options.path;
|
|
128
|
+
const buffer = this.files.get(filePath);
|
|
129
|
+
if (buffer === undefined) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`ENOENT: no such file or directory, open '${filePath}'`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
name: options.name ?? filePath.split("/").pop() ?? "file",
|
|
136
|
+
type: options.type ?? "application/octet-stream",
|
|
137
|
+
size: buffer.byteLength,
|
|
138
|
+
lastModified: Date.now(),
|
|
139
|
+
stream: () => {
|
|
140
|
+
throw new Error("Stream not implemented in MemoryFileSystemProvider");
|
|
141
|
+
},
|
|
142
|
+
arrayBuffer: async (): Promise<ArrayBuffer> =>
|
|
143
|
+
buffer.buffer.slice(
|
|
144
|
+
buffer.byteOffset,
|
|
145
|
+
buffer.byteOffset + buffer.byteLength,
|
|
146
|
+
) as ArrayBuffer,
|
|
147
|
+
text: async () => buffer.toString("utf-8"),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
113
151
|
if ("buffer" in options) {
|
|
114
152
|
const buffer = options.buffer;
|
|
115
153
|
return {
|
|
@@ -157,6 +195,8 @@ export class MemoryFileSystemProvider implements FileSystemProvider {
|
|
|
157
195
|
* Remove a file or directory from memory.
|
|
158
196
|
*/
|
|
159
197
|
public async rm(path: string, options?: RmOptions): Promise<void> {
|
|
198
|
+
this.rmCalls.push({ path, options });
|
|
199
|
+
|
|
160
200
|
const exists = this.files.has(path) || this.directories.has(path);
|
|
161
201
|
|
|
162
202
|
if (!exists && !options?.force) {
|
|
@@ -327,6 +367,8 @@ export class MemoryFileSystemProvider implements FileSystemProvider {
|
|
|
327
367
|
* Read a file from memory.
|
|
328
368
|
*/
|
|
329
369
|
public async readFile(path: string): Promise<Buffer> {
|
|
370
|
+
this.readFileCalls.push(path);
|
|
371
|
+
|
|
330
372
|
if (this.readFileError) {
|
|
331
373
|
throw this.readFileError;
|
|
332
374
|
}
|
|
@@ -338,6 +380,22 @@ export class MemoryFileSystemProvider implements FileSystemProvider {
|
|
|
338
380
|
return content;
|
|
339
381
|
}
|
|
340
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Read a file from memory as text.
|
|
385
|
+
*/
|
|
386
|
+
public async readTextFile(path: string): Promise<string> {
|
|
387
|
+
const buffer = await this.readFile(path);
|
|
388
|
+
return buffer.toString("utf-8");
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Read a file from memory as JSON.
|
|
393
|
+
*/
|
|
394
|
+
public async readJsonFile<T = unknown>(path: string): Promise<T> {
|
|
395
|
+
const text = await this.readTextFile(path);
|
|
396
|
+
return this.json.parse(text) as T;
|
|
397
|
+
}
|
|
398
|
+
|
|
341
399
|
/**
|
|
342
400
|
* Write a file to memory.
|
|
343
401
|
*/
|
|
@@ -378,12 +436,67 @@ export class MemoryFileSystemProvider implements FileSystemProvider {
|
|
|
378
436
|
this.directories.clear();
|
|
379
437
|
this.mkdirCalls = [];
|
|
380
438
|
this.writeFileCalls = [];
|
|
439
|
+
this.readFileCalls = [];
|
|
440
|
+
this.rmCalls = [];
|
|
381
441
|
this.joinCalls = [];
|
|
382
442
|
this.mkdirError = null;
|
|
383
443
|
this.writeFileError = null;
|
|
384
444
|
this.readFileError = null;
|
|
385
445
|
}
|
|
386
446
|
|
|
447
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
448
|
+
// Test assertion helpers
|
|
449
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Check if a file was written during the test.
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* ```typescript
|
|
456
|
+
* expect(fs.wasWritten("/project/tsconfig.json")).toBe(true);
|
|
457
|
+
* ```
|
|
458
|
+
*/
|
|
459
|
+
public wasWritten(path: string): boolean {
|
|
460
|
+
return this.writeFileCalls.some((call) => call.path === path);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Check if a file was written with content matching a pattern.
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* ```typescript
|
|
468
|
+
* expect(fs.wasWrittenMatching("/project/tsconfig.json", /extends/)).toBe(true);
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
471
|
+
public wasWrittenMatching(path: string, pattern: RegExp): boolean {
|
|
472
|
+
const call = this.writeFileCalls.find((c) => c.path === path);
|
|
473
|
+
return call ? pattern.test(call.data) : false;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Check if a file was read during the test.
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* ```typescript
|
|
481
|
+
* expect(fs.wasRead("/project/package.json")).toBe(true);
|
|
482
|
+
* ```
|
|
483
|
+
*/
|
|
484
|
+
public wasRead(path: string): boolean {
|
|
485
|
+
return this.readFileCalls.includes(path);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Check if a file was deleted during the test.
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* ```typescript
|
|
493
|
+
* expect(fs.wasDeleted("/project/old-file.txt")).toBe(true);
|
|
494
|
+
* ```
|
|
495
|
+
*/
|
|
496
|
+
public wasDeleted(path: string): boolean {
|
|
497
|
+
return this.rmCalls.some((call) => call.path === path);
|
|
498
|
+
}
|
|
499
|
+
|
|
387
500
|
/**
|
|
388
501
|
* Get the content of a file as a string (convenience method for testing).
|
|
389
502
|
*/
|