alepha 0.21.2 → 0.23.0
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 +0 -1
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.d.ts +393 -403
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +25 -56
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.browser.js +31 -1
- package/dist/api/files/index.browser.js.map +1 -1
- package/dist/api/files/index.d.ts +313 -208
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +152 -42
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +2 -2
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +282 -285
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +39 -33
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +217 -222
- package/dist/api/keys/index.d.ts.map +1 -1
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +188 -195
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/oauth/index.d.ts +71 -76
- package/dist/api/oauth/index.d.ts.map +1 -1
- package/dist/api/oauth/index.js.map +1 -1
- package/dist/api/organizations/index.browser.js.map +1 -1
- package/dist/api/organizations/index.d.ts +104 -109
- package/dist/api/organizations/index.d.ts.map +1 -1
- package/dist/api/organizations/index.js.map +1 -1
- package/dist/api/parameters/index.browser.js +43 -16
- package/dist/api/parameters/index.browser.js.map +1 -1
- package/dist/api/parameters/index.d.ts +488 -344
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +175 -35
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/payments/index.d.ts +396 -402
- package/dist/api/payments/index.d.ts.map +1 -1
- package/dist/api/payments/index.js.map +1 -1
- package/dist/api/subscriptions/index.d.ts +644 -652
- package/dist/api/subscriptions/index.d.ts.map +1 -1
- package/dist/api/subscriptions/index.js +1 -1
- package/dist/api/subscriptions/index.js.map +1 -1
- package/dist/api/users/index.browser.js +7 -0
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +1106 -1005
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +307 -64
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.browser.js.map +1 -1
- package/dist/api/verifications/index.d.ts +137 -143
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/background/index.d.ts +95 -0
- package/dist/background/index.d.ts.map +1 -0
- package/dist/background/index.js +121 -0
- package/dist/background/index.js.map +1 -0
- package/dist/background/index.workerd.js +110 -0
- package/dist/background/index.workerd.js.map +1 -0
- package/dist/batch/index.d.ts +5 -7
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/bin/index.js.map +1 -1
- package/dist/bucket/index.d.ts +76 -54
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +58 -11
- package/dist/bucket/index.js.map +1 -1
- package/dist/bucket/index.workerd.js +200 -5
- package/dist/bucket/index.workerd.js.map +1 -1
- package/dist/cache/core/index.d.ts +7 -10
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/cache/database/index.d.ts +22 -26
- package/dist/cache/database/index.d.ts.map +1 -1
- package/dist/cache/database/index.js.map +1 -1
- package/dist/cache/redis/index.d.ts +4 -7
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/captcha/index.d.ts +3 -6
- package/dist/captcha/index.d.ts.map +1 -1
- package/dist/captcha/index.js.map +1 -1
- package/dist/cli/config/index.d.ts.map +1 -1
- package/dist/cli/config/index.js.map +1 -1
- package/dist/cli/core/index.d.ts +458 -249
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +372 -660
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.d.ts +3 -5
- package/dist/cli/devtools/index.d.ts.map +1 -1
- package/dist/cli/devtools/index.js.map +1 -1
- package/dist/cli/i18n/index.d.ts +20 -17
- package/dist/cli/i18n/index.d.ts.map +1 -1
- package/dist/cli/i18n/index.js +45 -11
- package/dist/cli/i18n/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +126 -1342
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +136 -2374
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/platform-lib/index.d.ts +1472 -0
- package/dist/cli/platform-lib/index.d.ts.map +1 -0
- package/dist/cli/platform-lib/index.js +2660 -0
- package/dist/cli/platform-lib/index.js.map +1 -0
- package/dist/cli/vendor/index.d.ts +17 -21
- package/dist/cli/vendor/index.d.ts.map +1 -1
- package/dist/cli/vendor/index.js.map +1 -1
- package/dist/command/index.d.ts +20 -19
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +39 -10
- package/dist/command/index.js.map +1 -1
- package/dist/{containers → container}/core/index.d.ts +13 -15
- package/dist/container/core/index.d.ts.map +1 -0
- package/dist/{containers → container}/core/index.js +23 -14
- package/dist/container/core/index.js.map +1 -0
- package/dist/{containers → container}/core/index.workerd.js +37 -22
- package/dist/container/core/index.workerd.js.map +1 -0
- package/dist/core/index.browser.js +27 -1
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +48 -24
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +27 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +27 -1
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +27 -1
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.browser.js.map +1 -1
- package/dist/crypto/index.d.ts +5 -8
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/crypto/index.js.map +1 -1
- package/dist/datetime/index.d.ts +3 -4
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/brevo/index.d.ts +2 -4
- package/dist/email/brevo/index.d.ts.map +1 -1
- package/dist/email/brevo/index.js.map +1 -1
- package/dist/email/cloudflare/index.d.ts +20 -7
- package/dist/email/cloudflare/index.d.ts.map +1 -1
- package/dist/email/cloudflare/index.js +46 -9
- package/dist/email/cloudflare/index.js.map +1 -1
- package/dist/email/core/index.d.ts +6 -9
- package/dist/email/core/index.d.ts.map +1 -1
- package/dist/email/core/index.js.map +1 -1
- package/dist/email/core/index.workerd.js.map +1 -1
- package/dist/email/smtp/index.d.ts +10 -13
- package/dist/email/smtp/index.d.ts.map +1 -1
- package/dist/email/smtp/index.js +107 -32
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/fake/index.d.ts +1 -2
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +9 -14
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js.map +1 -1
- package/dist/lock/redis/index.d.ts +2 -4
- package/dist/lock/redis/index.d.ts.map +1 -1
- package/dist/lock/redis/index.js.map +1 -1
- package/dist/logger/index.d.ts +105 -76
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +196 -174
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +25 -20
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +23 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +19 -1
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +76 -62
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +20 -2
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +28 -20
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +12 -15
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/core/index.js.map +1 -1
- package/dist/queue/core/index.workerd.js.map +1 -1
- package/dist/queue/redis/index.d.ts +3 -5
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/react/auth/index.browser.js +9 -2
- package/dist/react/auth/index.browser.js.map +1 -1
- package/dist/react/auth/index.d.ts +14 -9
- package/dist/react/auth/index.d.ts.map +1 -1
- package/dist/react/auth/index.js +9 -2
- package/dist/react/auth/index.js.map +1 -1
- package/dist/react/core/index.d.ts +7 -8
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +6 -3
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +2 -5
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +16 -15
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/head/index.browser.js.map +1 -1
- package/dist/react/head/index.d.ts +2 -4
- package/dist/react/head/index.d.ts.map +1 -1
- package/dist/react/head/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +90 -11
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/i18n/index.js +147 -11
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/intro/index.d.ts +1 -2
- package/dist/react/intro/index.d.ts.map +1 -1
- package/dist/react/intro/index.js +2 -2
- package/dist/react/intro/index.js.map +1 -1
- package/dist/react/router/index.browser.js +193 -24
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +434 -222
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +249 -35
- package/dist/react/router/index.js.map +1 -1
- package/dist/react/sitemap/index.browser.js +35 -0
- package/dist/react/sitemap/index.browser.js.map +1 -0
- package/dist/react/sitemap/index.d.ts +92 -0
- package/dist/react/sitemap/index.d.ts.map +1 -0
- package/dist/react/sitemap/index.js +131 -0
- package/dist/react/sitemap/index.js.map +1 -0
- package/dist/react/testing/index.d.ts +1 -2
- package/dist/react/testing/index.d.ts.map +1 -1
- package/dist/react/testing/index.js +16 -17
- package/dist/react/testing/index.js.map +1 -1
- package/dist/react/ui/index.d.ts +20 -25
- package/dist/react/ui/index.d.ts.map +1 -1
- package/dist/react/ui/index.js.map +1 -1
- package/dist/redis/index.bun.js.map +1 -1
- package/dist/redis/index.d.ts +17 -19
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +2 -4
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/retry/index.js.map +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +10 -13
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/index.workerd.js.map +1 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +45 -48
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.browser.js.map +1 -1
- package/dist/server/auth/index.d.ts +272 -173
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +1608 -15
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.browser.js.map +1 -1
- package/dist/server/cookies/index.d.ts +20 -7
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +22 -3
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.d.ts +106 -73
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +44 -0
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +11 -14
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/etag/index.d.ts +6 -9
- package/dist/server/etag/index.d.ts.map +1 -1
- package/dist/server/etag/index.js.map +1 -1
- package/dist/server/health/index.d.ts +18 -21
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/links/index.browser.js +2 -0
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +63 -67
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +2 -0
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +5 -7
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts +3 -5
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts +10 -13
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +3 -5
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +5 -8
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +3 -5
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.browser.js.map +1 -1
- package/dist/system/index.d.ts +2 -4
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/index.workerd.js.map +1 -1
- package/dist/topic/core/index.d.ts +4 -6
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/core/index.js.map +1 -1
- package/dist/topic/redis/index.d.ts +5 -8
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/topic/redis/index.js.map +1 -1
- package/package.json +59 -23
- package/src/api/audits/__tests__/AuditService.spec.ts +18 -110
- package/src/api/audits/controllers/AdminAuditController.ts +14 -0
- package/src/api/audits/services/AuditService.ts +21 -88
- package/src/api/files/__tests__/FileService.spec.ts +207 -2
- package/src/api/files/index.ts +3 -0
- package/src/api/files/schemas/fileCreatorSummarySchema.ts +22 -0
- package/src/api/files/schemas/fileResourceSchema.ts +10 -1
- package/src/api/files/services/FileService.ts +170 -72
- package/src/api/jobs/__tests__/$job.spec.ts +24 -1
- package/src/api/jobs/index.ts +4 -3
- package/src/api/jobs/primitives/$job.ts +7 -3
- package/src/api/jobs/providers/DirectJobDispatcher.ts +17 -36
- package/src/api/jobs/providers/JobProvider.ts +53 -24
- package/src/api/jobs/schemas/jobConfigAtom.ts +1 -1
- package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +4 -1
- package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +3 -1
- package/src/api/parameters/__tests__/$parameter.spec.ts +19 -2
- package/src/api/parameters/audits/ParameterAudits.ts +17 -0
- package/src/api/parameters/controllers/AdminParameterController.ts +95 -19
- package/src/api/parameters/index.ts +3 -0
- package/src/api/parameters/schemas/activateParameterBodySchema.ts +3 -3
- package/src/api/parameters/schemas/createParameterVersionBodySchema.ts +3 -2
- package/src/api/parameters/schemas/parameterCreatorSummarySchema.ts +25 -0
- package/src/api/parameters/schemas/parameterResponseSchema.ts +5 -0
- package/src/api/parameters/schemas/rollbackParameterBodySchema.ts +4 -2
- package/src/api/parameters/services/ParameterProvider.ts +69 -6
- package/src/api/subscriptions/jobs/SubscriptionJobs.ts +1 -1
- package/src/api/users/__tests__/AdminSessionController.spec.ts +37 -0
- package/src/api/users/audits/SessionAudits.ts +33 -0
- package/src/api/users/audits/UserAudits.ts +19 -43
- package/src/api/users/controllers/AdminUserController.ts +66 -1
- package/src/api/users/controllers/RealmController.ts +1 -0
- package/src/api/users/entities/sessions.ts +6 -0
- package/src/api/users/entities/users.ts +2 -0
- package/src/api/users/index.ts +9 -1
- package/src/api/users/primitives/$realm.ts +29 -0
- package/src/api/users/providers/RealmProvider.ts +15 -0
- package/src/api/users/schemas/realmConfigSchema.ts +14 -0
- package/src/api/users/schemas/sessionResourceSchema.ts +16 -0
- package/src/api/users/schemas/updateUserSchema.ts +1 -8
- package/src/api/users/schemas/userQuerySchema.ts +7 -0
- package/src/api/users/services/CredentialService.ts +15 -6
- package/src/api/users/services/IdentityService.ts +2 -1
- package/src/api/users/services/RegistrationService.ts +2 -1
- package/src/api/users/services/SessionCrudService.ts +19 -2
- package/src/api/users/services/SessionService.ts +39 -19
- package/src/api/users/services/UserService.ts +106 -8
- package/src/background/__tests__/BackgroundTaskProvider.spec.ts +96 -0
- package/src/background/index.ts +37 -0
- package/src/background/index.workerd.ts +28 -0
- package/src/background/providers/BackgroundTaskProvider.ts +70 -0
- package/src/background/providers/WorkerdBackgroundTaskProvider.ts +43 -0
- package/src/bucket/__tests__/$bucket.spec.ts +18 -0
- package/src/bucket/__tests__/LocalFileStorageProvider.spec.ts +5 -0
- package/src/bucket/__tests__/MemoryFileStorageProvider.spec.ts +5 -0
- package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +23 -4
- package/src/bucket/__tests__/shared.ts +30 -0
- package/src/bucket/index.ts +5 -5
- package/src/bucket/index.workerd.ts +11 -4
- package/src/bucket/primitives/$bucket.ts +27 -0
- package/src/bucket/providers/FileStorageProvider.ts +13 -0
- package/src/bucket/providers/LocalFileStorageProvider.ts +17 -1
- package/src/bucket/providers/MemoryFileStorageProvider.ts +7 -0
- package/src/bucket/providers/{CloudflareR2Provider.ts → R2FileStorageProvider.ts} +10 -1
- package/src/bucket/providers/{NodeS3BucketProvider.ts → S3FileStorageProvider.ts} +27 -5
- package/src/cli/core/__tests__/BuildDockerTask.spec.ts +25 -1
- package/src/cli/core/__tests__/init.spec.ts +0 -219
- package/src/cli/core/atoms/buildOptions.ts +0 -12
- package/src/cli/core/commands/__tests__/BuildCommand.spec.ts +43 -0
- package/src/cli/core/commands/build.ts +105 -37
- package/src/cli/core/commands/init.ts +0 -12
- package/src/cli/core/commands/pack.ts +133 -0
- package/src/cli/core/index.ts +3 -3
- package/src/cli/core/providers/ViteDevServerProvider.ts +40 -16
- package/src/cli/core/services/PackageManagerUtils.ts +0 -16
- package/src/cli/core/services/ProjectScaffolder.ts +29 -291
- package/src/cli/core/tasks/BuildCloudflareTask.ts +382 -56
- package/src/cli/core/tasks/BuildDockerTask.ts +33 -3
- package/src/cli/core/tasks/BuildPrerenderTask.ts +44 -7
- package/src/cli/core/tasks/BuildTask.ts +34 -0
- package/src/cli/core/templates/apiIndexTs.ts +1 -22
- package/src/cli/core/templates/mainCss.ts +0 -1
- package/src/cli/core/templates/webAppRouterTs.ts +0 -99
- package/src/cli/core/templates/webIndexTs.ts +1 -22
- package/src/cli/i18n/__tests__/I18nCheckService.spec.ts +48 -0
- package/src/cli/i18n/services/I18nCheckService.ts +65 -11
- package/src/cli/platform/__tests__/SecretsCommand.spec.ts +5 -3
- package/src/cli/platform/commands/SecretsCommand.ts +8 -6
- package/src/cli/platform/commands/platform.ts +192 -46
- package/src/cli/platform/index.ts +12 -52
- package/src/cli/{platform → platform-lib}/__tests__/CloudflareAdapter.spec.ts +426 -169
- package/src/cli/{platform → platform-lib}/__tests__/NamingService.spec.ts +91 -4
- package/src/cli/{platform → platform-lib}/__tests__/VercelAdapter.spec.ts +56 -85
- package/src/cli/{platform → platform-lib}/adapters/CloudflareAdapter.ts +519 -190
- package/src/cli/{platform → platform-lib}/adapters/PlatformAdapter.ts +62 -35
- package/src/cli/{platform → platform-lib}/adapters/VercelAdapter.ts +6 -10
- package/src/cli/{platform → platform-lib}/atoms/platformOptions.ts +34 -1
- package/src/cli/platform-lib/index.ts +67 -0
- package/src/cli/platform-lib/services/NamingService.ts +136 -0
- package/src/cli/{platform → platform-lib}/services/PlatformInspector.ts +60 -13
- package/src/cli/{platform → platform-lib}/services/PlatformOrchestrator.ts +54 -43
- package/src/cli/{platform → platform-lib}/services/WranglerApi.ts +4 -2
- package/src/command/__tests__/Runner.spec.ts +20 -0
- package/src/command/helpers/EnvUtils.ts +19 -3
- package/src/command/helpers/Runner.ts +12 -2
- package/src/command/providers/CliProvider.ts +34 -1
- package/src/{containers → container}/core/__tests__/$container.spec.ts +5 -5
- package/src/{containers → container}/core/index.ts +4 -4
- package/src/{containers → container}/core/index.workerd.ts +19 -3
- package/src/{containers → container}/core/primitives/$container.ts +1 -1
- package/src/{containers → container}/core/providers/CloudflareContainerProvider.ts +17 -19
- package/src/{containers → container}/core/providers/ContainerProvider.ts +16 -2
- package/src/{containers → container}/core/providers/MockContainerProvider.ts +1 -1
- package/src/core/Alepha.ts +49 -1
- package/src/core/__tests__/$env.spec.ts +42 -0
- package/src/core/__tests__/dump.spec.ts +47 -0
- package/src/email/cloudflare/__tests__/CloudflareEmailProvider.spec.ts +42 -10
- package/src/email/cloudflare/index.ts +14 -5
- package/src/email/cloudflare/providers/CloudflareEmailProvider.ts +54 -9
- package/src/logger/__tests__/Logger.spec.ts +55 -0
- package/src/logger/index.ts +13 -0
- package/src/logger/services/Logger.ts +31 -1
- package/src/mcp/__tests__/McpServerProvider.spec.ts +71 -0
- package/src/mcp/providers/McpServerProvider.ts +55 -0
- package/src/orm/__tests__/orm-showcase-tests.ts +27 -0
- package/src/orm/__tests__/orm-showcase.spec.ts +12 -0
- package/src/orm/core/interfaces/PgQuery.ts +4 -1
- package/src/orm/core/services/Repository.ts +27 -11
- package/src/react/auth/hooks/useAuth.ts +10 -5
- package/src/react/core/__tests__/useQuery.browser.spec.tsx +25 -0
- package/src/react/core/hooks/useAction.ts +14 -3
- package/src/react/core/hooks/useQuery.ts +24 -4
- package/src/react/form/__tests__/FormModel-submit-loading.spec.ts +71 -0
- package/src/react/form/__tests__/form-submitting-reactive.browser.spec.tsx +96 -0
- package/src/react/form/services/FormModel.ts +57 -39
- package/src/react/i18n/__tests__/I18nProvider.spec.ts +89 -0
- package/src/react/i18n/__tests__/locale-routing.spec.ts +107 -0
- package/src/react/i18n/components/Translate.tsx +47 -0
- package/src/react/i18n/index.ts +2 -0
- package/src/react/i18n/providers/I18nProvider.ts +171 -12
- package/src/react/intro/components/GettingStartedAdminSlide.tsx +2 -2
- package/src/react/router/__tests__/$page.spec.tsx +3 -2
- package/src/react/router/__tests__/RouterLocaleProvider.spec.ts +127 -0
- package/src/react/router/__tests__/page-can.spec.ts +18 -13
- package/src/react/router/hooks/useQueryParams.ts +114 -14
- package/src/react/router/index.browser.ts +4 -0
- package/src/react/router/index.shared.ts +1 -0
- package/src/react/router/index.ts +9 -0
- package/src/react/router/primitives/$page.ts +85 -4
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +18 -8
- package/src/react/router/providers/ReactPageProvider.ts +12 -1
- package/src/react/router/providers/ReactServerProvider.ts +96 -14
- package/src/react/router/providers/RootComponentsProvider.ts +13 -0
- package/src/react/router/providers/RouterLocaleProvider.ts +125 -0
- package/src/react/router/providers/__tests__/RootComponentsProvider.spec.ts +15 -0
- package/src/react/router/providers/__tests__/rootComponents.ssr.browser.spec.tsx +67 -0
- package/src/react/sitemap/__tests__/$sitemap.spec.ts +131 -0
- package/src/react/sitemap/index.browser.ts +21 -0
- package/src/react/sitemap/index.ts +25 -0
- package/src/react/sitemap/primitives/$sitemap.browser.ts +26 -0
- package/src/react/sitemap/primitives/$sitemap.ts +196 -0
- package/src/react/ui/services/SchemaControl.ts +3 -4
- package/src/server/auth/__tests__/appleClientSecret.spec.ts +34 -0
- package/src/server/auth/__tests__/authFederationClient.spec.ts +40 -0
- package/src/server/auth/__tests__/federationAssertion.spec.ts +146 -0
- package/src/server/auth/__tests__/federationRedirectReplay.spec.ts +44 -0
- package/src/server/auth/helpers/appleClientSecret.ts +24 -0
- package/src/server/auth/helpers/federationAssertion.ts +74 -0
- package/src/server/auth/helpers/jtiReplayGuard.ts +41 -0
- package/src/server/auth/helpers/safeRedirectPath.ts +19 -0
- package/src/server/auth/index.ts +4 -0
- package/src/server/auth/primitives/$authFederationBroker.ts +273 -0
- package/src/server/auth/primitives/$authFederationClient.ts +89 -0
- package/src/server/auth/providers/ServerAuthProvider.ts +18 -4
- package/src/server/cookies/__tests__/ServerCookiesProvider.spec.ts +70 -0
- package/src/server/cookies/providers/ServerCookiesProvider.ts +23 -3
- package/src/server/core/interfaces/ServerRequest.ts +8 -0
- package/src/server/core/primitives/$route.ts +27 -0
- package/src/server/core/providers/ServerMultipartProvider.ts +19 -0
- package/src/server/links/providers/LinkProvider.ts +10 -0
- package/dist/containers/core/index.d.ts.map +0 -1
- package/dist/containers/core/index.js.map +0 -1
- package/dist/containers/core/index.workerd.js.map +0 -1
- package/src/cli/core/tasks/BuildSitemapTask.ts +0 -130
- package/src/cli/core/templates/componentsJsonTs.ts +0 -39
- package/src/cli/core/templates/saasAdminLayoutTsx.ts +0 -77
- package/src/cli/core/templates/saasAdminPagesTsx.ts +0 -26
- package/src/cli/core/templates/saasAuthLayoutTsx.ts +0 -22
- package/src/cli/core/templates/saasAuthPagesTsx.ts +0 -62
- package/src/cli/core/templates/saasRealmProviderTs.ts +0 -52
- package/src/cli/platform/services/NamingService.ts +0 -54
- /package/dist/orm/core/{chunk-o8xxKEmq.js → chunk-B4FMCO8f.js} +0 -0
- /package/dist/react/testing/{chunk-6Ep1yQYe.js → chunk-BpyX8vjI.js} +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/GitHubSecretStore.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/PlatformCacheProvider.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/PlatformInspector.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/PlatformOrchestrator.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/SecretFilterService.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/detectResources.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/GitHubSecretStore.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/MemorySecretStore.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/PlatformCacheProvider.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/SecretStoreProvider.ts +0 -0
- /package/src/cli/{platform → platform-lib}/schemas/cloudflare.ts +0 -0
- /package/src/cli/{platform → platform-lib}/schemas/platform.ts +0 -0
- /package/src/cli/{platform → platform-lib}/schemas/vercel.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/CloudflareApi.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/SecretFilterService.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/VercelApi.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/VercelCli.ts +0 -0
- /package/src/{containers → container}/core/interfaces/ContainerOptions.ts +0 -0
- /package/src/{containers → container}/core/providers/NodeContainerProvider.ts +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { $inject, Alepha } from "alepha";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generic locale path-prefix mechanism for the router.
|
|
5
|
+
*
|
|
6
|
+
* This provider knows nothing about i18n — it only deals with URL path
|
|
7
|
+
* segments. It is configured by the i18n module (`I18nProvider`) when
|
|
8
|
+
* `routing: "prefix"` is enabled, which keeps the dependency one-directional
|
|
9
|
+
* (`i18n → router`) and avoids a module cycle.
|
|
10
|
+
*
|
|
11
|
+
* The default locale is served WITHOUT a prefix (`/about` = default,
|
|
12
|
+
* `/fr/about` = French). The active locale is derived from the current
|
|
13
|
+
* request/navigation and stored under `alepha.react.router.locale`, so every
|
|
14
|
+
* URL the router builds (`pathname()`) automatically carries the right prefix.
|
|
15
|
+
*/
|
|
16
|
+
export class RouterLocaleProvider {
|
|
17
|
+
protected readonly alepha = $inject(Alepha);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Whether locale path-prefixing is active. Off by default — opt-in via the
|
|
21
|
+
* i18n module.
|
|
22
|
+
*/
|
|
23
|
+
public enabled = false;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The default locale, served without a path prefix (e.g. `"en"` → `/about`).
|
|
27
|
+
*/
|
|
28
|
+
public defaultLocale = "";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* All known locales, including the default one.
|
|
32
|
+
*/
|
|
33
|
+
public locales: string[] = [];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Configure the provider. Called by the i18n module before the SSR routes
|
|
37
|
+
* are registered.
|
|
38
|
+
*/
|
|
39
|
+
public configure(options: {
|
|
40
|
+
enabled?: boolean;
|
|
41
|
+
defaultLocale?: string;
|
|
42
|
+
locales?: string[];
|
|
43
|
+
}): void {
|
|
44
|
+
if (options.enabled !== undefined) {
|
|
45
|
+
this.enabled = options.enabled;
|
|
46
|
+
}
|
|
47
|
+
if (options.defaultLocale !== undefined) {
|
|
48
|
+
this.defaultLocale = options.defaultLocale;
|
|
49
|
+
}
|
|
50
|
+
if (options.locales !== undefined) {
|
|
51
|
+
this.locales = options.locales;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Locales that carry a URL prefix — every known locale except the default.
|
|
57
|
+
*/
|
|
58
|
+
public get prefixedLocales(): string[] {
|
|
59
|
+
return this.locales.filter((locale) => locale !== this.defaultLocale);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Splits a leading locale segment off a pathname.
|
|
64
|
+
*
|
|
65
|
+
* - `/fr/about` → `{ locale: "fr", pathname: "/about" }` when `fr` is a
|
|
66
|
+
* prefixed locale.
|
|
67
|
+
* - `/about` → `{ locale: defaultLocale, pathname: "/about" }`.
|
|
68
|
+
*
|
|
69
|
+
* When prefixing is disabled the pathname is returned untouched.
|
|
70
|
+
*/
|
|
71
|
+
public detect(pathname: string): { locale: string; pathname: string } {
|
|
72
|
+
if (this.enabled) {
|
|
73
|
+
const first = pathname.split("/")[1];
|
|
74
|
+
if (first && this.prefixedLocales.includes(first)) {
|
|
75
|
+
const rest = pathname.slice(first.length + 1);
|
|
76
|
+
return { locale: first, pathname: this.normalize(rest) };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { locale: this.defaultLocale, pathname };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Prepends the locale prefix to a pathname when needed. The default locale
|
|
84
|
+
* (and any unknown/disabled case) returns the pathname unchanged.
|
|
85
|
+
*/
|
|
86
|
+
public withPrefix(pathname: string, locale: string = this.current): string {
|
|
87
|
+
if (
|
|
88
|
+
!this.enabled ||
|
|
89
|
+
!locale ||
|
|
90
|
+
locale === this.defaultLocale ||
|
|
91
|
+
!this.prefixedLocales.includes(locale)
|
|
92
|
+
) {
|
|
93
|
+
return pathname;
|
|
94
|
+
}
|
|
95
|
+
return `/${locale}${pathname === "/" ? "" : pathname}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The active locale, derived from the current request/navigation. Falls back
|
|
100
|
+
* to the default locale when nothing has been detected.
|
|
101
|
+
*/
|
|
102
|
+
public get current(): string {
|
|
103
|
+
return (
|
|
104
|
+
this.alepha.store.get("alepha.react.router.locale") || this.defaultLocale
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public set current(locale: string) {
|
|
109
|
+
this.alepha.store.set("alepha.react.router.locale", locale);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Normalizes a stripped pathname so it always starts with a single slash and
|
|
114
|
+
* carries no trailing slash (except the root `/`).
|
|
115
|
+
*/
|
|
116
|
+
protected normalize(pathname: string): string {
|
|
117
|
+
if (!pathname || pathname === "/") {
|
|
118
|
+
return "/";
|
|
119
|
+
}
|
|
120
|
+
const withLeading = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
121
|
+
return withLeading.length > 1 && withLeading.endsWith("/")
|
|
122
|
+
? withLeading.slice(0, -1)
|
|
123
|
+
: withLeading;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Alepha } from "alepha";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { RootComponentsProvider } from "../RootComponentsProvider.ts";
|
|
4
|
+
|
|
5
|
+
describe("RootComponentsProvider", () => {
|
|
6
|
+
it("starts empty and accepts pushed nodes", () => {
|
|
7
|
+
const alepha = Alepha.create();
|
|
8
|
+
const provider = alepha.inject(RootComponentsProvider);
|
|
9
|
+
expect(provider.rootComponents).toEqual([]);
|
|
10
|
+
provider.rootComponents.push("x" as any);
|
|
11
|
+
expect(alepha.inject(RootComponentsProvider).rootComponents).toHaveLength(
|
|
12
|
+
1,
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Alepha } from "alepha";
|
|
2
|
+
import { createElement } from "react";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import type { ReactRouterState } from "../ReactPageProvider.ts";
|
|
5
|
+
import { ReactPageProvider } from "../ReactPageProvider.ts";
|
|
6
|
+
import { RootComponentsProvider } from "../RootComponentsProvider.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Walk a React element tree (non-circular, element children only) and collect
|
|
10
|
+
* all "data-testid" values found in any element's props.
|
|
11
|
+
*/
|
|
12
|
+
function collectTestIds(node: any, seen = new Set<any>()): string[] {
|
|
13
|
+
if (!node || typeof node !== "object" || seen.has(node)) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
seen.add(node);
|
|
17
|
+
|
|
18
|
+
const ids: string[] = [];
|
|
19
|
+
|
|
20
|
+
if (node.props?.["data-testid"]) {
|
|
21
|
+
ids.push(node.props["data-testid"]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Children may be a single node or an array
|
|
25
|
+
const children = node.props?.children;
|
|
26
|
+
if (Array.isArray(children)) {
|
|
27
|
+
for (const child of children) {
|
|
28
|
+
ids.push(...collectTestIds(child, seen));
|
|
29
|
+
}
|
|
30
|
+
} else if (children) {
|
|
31
|
+
ids.push(...collectTestIds(children, seen));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Also walk positional children (args 2+ on createElement are spread as props.children
|
|
35
|
+
// but StrictMode wraps with a single child — unwrap one level manually)
|
|
36
|
+
return ids;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("rootComponents in ReactPageProvider.root", () => {
|
|
40
|
+
it("includes pushed components in the rendered root element tree", () => {
|
|
41
|
+
const alepha = Alepha.create();
|
|
42
|
+
alepha
|
|
43
|
+
.inject(RootComponentsProvider)
|
|
44
|
+
.rootComponents.push(
|
|
45
|
+
createElement("div", { "data-testid": "sigil-marker", key: "m" }),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const page = alepha.inject(ReactPageProvider);
|
|
49
|
+
|
|
50
|
+
const state: ReactRouterState = {
|
|
51
|
+
layers: [{ element: null } as any],
|
|
52
|
+
url: new URL("http://localhost/"),
|
|
53
|
+
onError: () => undefined as any,
|
|
54
|
+
params: {},
|
|
55
|
+
query: {},
|
|
56
|
+
meta: {},
|
|
57
|
+
head: {} as any,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const element: any = page.root(state);
|
|
61
|
+
|
|
62
|
+
// StrictMode wraps the root — unwrap one level
|
|
63
|
+
const inner = element?.props?.children ?? element;
|
|
64
|
+
const ids = collectTestIds(inner);
|
|
65
|
+
expect(ids).toContain("sigil-marker");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Alepha, t } from "alepha";
|
|
2
|
+
import { DateTimeProvider } from "alepha/datetime";
|
|
3
|
+
import { $page, AlephaReactRouter } from "alepha/react/router";
|
|
4
|
+
import { ServerRouterProvider } from "alepha/server";
|
|
5
|
+
import { describe, it } from "vitest";
|
|
6
|
+
import { AlephaReactSitemap } from "../index.ts";
|
|
7
|
+
import { $sitemap, type SitemapPrimitive } from "../primitives/$sitemap.ts";
|
|
8
|
+
|
|
9
|
+
describe("$sitemap", () => {
|
|
10
|
+
class App {
|
|
11
|
+
sitemap = $sitemap({ hostname: "https://example.com" });
|
|
12
|
+
|
|
13
|
+
home = $page({
|
|
14
|
+
path: "/",
|
|
15
|
+
static: true,
|
|
16
|
+
component: () => "home",
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
about = $page({
|
|
20
|
+
path: "/about",
|
|
21
|
+
static: true,
|
|
22
|
+
component: () => "about",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
notFound = $page({
|
|
26
|
+
path: "/*",
|
|
27
|
+
component: () => "404",
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
github404 = $page({
|
|
31
|
+
path: "/404",
|
|
32
|
+
static: true,
|
|
33
|
+
component: () => "404",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
blog = $page({
|
|
37
|
+
path: "/blog/:slug",
|
|
38
|
+
schema: { params: t.object({ slug: t.text() }) },
|
|
39
|
+
static: { entries: [{ params: { slug: "hello" } }] },
|
|
40
|
+
component: () => "post",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const start = async () => {
|
|
45
|
+
const alepha = Alepha.create()
|
|
46
|
+
.with(AlephaReactRouter)
|
|
47
|
+
.with(AlephaReactSitemap);
|
|
48
|
+
alepha.inject(App);
|
|
49
|
+
const router = alepha.inject(ServerRouterProvider);
|
|
50
|
+
await alepha.start();
|
|
51
|
+
return { alepha, router };
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const sitemapOf = (alepha: Alepha) =>
|
|
55
|
+
alepha.primitives("sitemap")[0] as SitemapPrimitive;
|
|
56
|
+
|
|
57
|
+
const findSitemapRoute = (router: ServerRouterProvider) =>
|
|
58
|
+
router.getRoutes().find((route) => route.path === "/sitemap.xml");
|
|
59
|
+
|
|
60
|
+
it("registers a static GET /sitemap.xml route", async ({ expect }) => {
|
|
61
|
+
const { router } = await start();
|
|
62
|
+
const route = findSitemapRoute(router);
|
|
63
|
+
expect(route).toBeDefined();
|
|
64
|
+
expect(route?.method).toBe("GET");
|
|
65
|
+
expect(route?.static).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("prerenders xml built from the app's pages", async ({ expect }) => {
|
|
69
|
+
const { alepha } = await start();
|
|
70
|
+
const { path, body } = sitemapOf(alepha).prerender();
|
|
71
|
+
|
|
72
|
+
expect(path).toBe("/sitemap.xml");
|
|
73
|
+
expect(body).toContain('<?xml version="1.0" encoding="UTF-8"?>');
|
|
74
|
+
expect(body).toContain("<loc>https://example.com/</loc>");
|
|
75
|
+
expect(body).toContain("<loc>https://example.com/about</loc>");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("serves application/xml at request time", async ({ expect }) => {
|
|
79
|
+
const { router } = await start();
|
|
80
|
+
const route = findSitemapRoute(router)!;
|
|
81
|
+
const headers: Record<string, string> = {};
|
|
82
|
+
const reply = {
|
|
83
|
+
headers,
|
|
84
|
+
setHeader(name: string, value: string) {
|
|
85
|
+
headers[name.toLowerCase()] = value;
|
|
86
|
+
return this;
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
const body = await route.handler.run({ reply } as any);
|
|
90
|
+
expect(headers["content-type"]).toBe("application/xml");
|
|
91
|
+
expect(String(body)).toContain("<urlset");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("expands parameterized pages via static.entries", async ({ expect }) => {
|
|
95
|
+
const { alepha } = await start();
|
|
96
|
+
const { body } = sitemapOf(alepha).prerender();
|
|
97
|
+
expect(body).toContain("<loc>https://example.com/blog/hello</loc>");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("excludes wildcard and 404 routes", async ({ expect }) => {
|
|
101
|
+
const { alepha } = await start();
|
|
102
|
+
const { body } = sitemapOf(alepha).prerender();
|
|
103
|
+
expect(body).not.toContain("/*");
|
|
104
|
+
expect(body).not.toContain("/404");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("falls back to PUBLIC_URL, then relative, when no hostname is given", async ({
|
|
108
|
+
expect,
|
|
109
|
+
}) => {
|
|
110
|
+
class RelApp {
|
|
111
|
+
sitemap = $sitemap();
|
|
112
|
+
home = $page({ path: "/", static: true, component: () => "home" });
|
|
113
|
+
}
|
|
114
|
+
const alepha = Alepha.create()
|
|
115
|
+
.with(AlephaReactRouter)
|
|
116
|
+
.with(AlephaReactSitemap);
|
|
117
|
+
alepha.inject(RelApp);
|
|
118
|
+
await alepha.start();
|
|
119
|
+
|
|
120
|
+
const { body } = sitemapOf(alepha).prerender();
|
|
121
|
+
expect(body).toContain("<loc>/</loc>");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("uses DateTimeProvider for lastmod (travel-able)", async ({ expect }) => {
|
|
125
|
+
const { alepha } = await start();
|
|
126
|
+
const dateTime = alepha.inject(DateTimeProvider);
|
|
127
|
+
const expected = dateTime.now().format("YYYY-MM-DD");
|
|
128
|
+
const { body } = sitemapOf(alepha).prerender();
|
|
129
|
+
expect(body).toContain(`<lastmod>${expected}</lastmod>`);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { $module } from "alepha";
|
|
2
|
+
import { $sitemap } from "./primitives/$sitemap.browser.ts";
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
5
|
+
|
|
6
|
+
export * from "./primitives/$sitemap.browser.ts";
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Sitemap generation for React applications (browser entry).
|
|
12
|
+
*
|
|
13
|
+
* The sitemap route only exists server-side, so the browser build ships a
|
|
14
|
+
* no-op {@link $sitemap}. See the server entry for the real implementation.
|
|
15
|
+
*
|
|
16
|
+
* @module alepha.react.sitemap
|
|
17
|
+
*/
|
|
18
|
+
export const AlephaReactSitemap = $module({
|
|
19
|
+
name: "alepha.react.sitemap",
|
|
20
|
+
primitives: [$sitemap],
|
|
21
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { $module } from "alepha";
|
|
2
|
+
import { AlephaDateTime } from "alepha/datetime";
|
|
3
|
+
import { AlephaServer } from "alepha/server";
|
|
4
|
+
import { $sitemap } from "./primitives/$sitemap.ts";
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
export * from "./primitives/$sitemap.ts";
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Sitemap generation for React applications.
|
|
14
|
+
*
|
|
15
|
+
* Exposes the {@link $sitemap} primitive, which serves a `sitemap.xml` built
|
|
16
|
+
* from the app's `$page` primitives — live at request time and prerendered to a
|
|
17
|
+
* static file at build time.
|
|
18
|
+
*
|
|
19
|
+
* @module alepha.react.sitemap
|
|
20
|
+
*/
|
|
21
|
+
export const AlephaReactSitemap = $module({
|
|
22
|
+
name: "alepha.react.sitemap",
|
|
23
|
+
imports: [AlephaServer, AlephaDateTime],
|
|
24
|
+
primitives: [$sitemap],
|
|
25
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createPrimitive, KIND, Primitive } from "alepha";
|
|
2
|
+
import type { SitemapPrimitiveOptions } from "./$sitemap.ts";
|
|
3
|
+
|
|
4
|
+
export type { SitemapPrimitiveOptions } from "./$sitemap.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Browser variant of {@link $sitemap}.
|
|
8
|
+
*
|
|
9
|
+
* The sitemap is a server-only route — there is nothing to register in the
|
|
10
|
+
* client bundle, so this is a no-op primitive. It exists only to keep
|
|
11
|
+
* `$sitemap()` valid as an isomorphic router field; the real implementation
|
|
12
|
+
* lives in `$sitemap.ts` (server entry).
|
|
13
|
+
*/
|
|
14
|
+
export const $sitemap = (
|
|
15
|
+
options: SitemapPrimitiveOptions = {},
|
|
16
|
+
): SitemapPrimitive => {
|
|
17
|
+
return createPrimitive(SitemapPrimitive, options);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export class SitemapPrimitive extends Primitive<SitemapPrimitiveOptions> {
|
|
21
|
+
protected onInit() {
|
|
22
|
+
// no-op in the browser
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
$sitemap[KIND] = SitemapPrimitive;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { $inject, createPrimitive, KIND, Primitive } from "alepha";
|
|
2
|
+
import { DateTimeProvider } from "alepha/datetime";
|
|
3
|
+
import { type ServerRequest, ServerRouterProvider } from "alepha/server";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Expose a `sitemap.xml` generated from the application's `$page` primitives.
|
|
7
|
+
*
|
|
8
|
+
* Registers a `GET /sitemap.xml` route that reads every registered page at
|
|
9
|
+
* request time and emits a standard XML sitemap. Marked `static` by default, so
|
|
10
|
+
* the build prerenders it to `dist/public/sitemap.xml` for static deployments —
|
|
11
|
+
* while SSR runtimes also serve it live.
|
|
12
|
+
*
|
|
13
|
+
* The hostname comes from `options.hostname`, falling back to `PUBLIC_URL`, then
|
|
14
|
+
* to `""` (relative URLs).
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { $sitemap } from "alepha/react/sitemap";
|
|
19
|
+
*
|
|
20
|
+
* class AppRouter {
|
|
21
|
+
* sitemap = $sitemap();
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export const $sitemap = (
|
|
26
|
+
options: SitemapPrimitiveOptions = {},
|
|
27
|
+
): SitemapPrimitive => {
|
|
28
|
+
return createPrimitive(SitemapPrimitive, options);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
export interface SitemapPrimitiveOptions {
|
|
34
|
+
/**
|
|
35
|
+
* Absolute base URL used to build `<loc>` entries (e.g. "https://alepha.dev").
|
|
36
|
+
*
|
|
37
|
+
* Defaults to `PUBLIC_URL`, then to `""` (relative URLs).
|
|
38
|
+
*/
|
|
39
|
+
hostname?: string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Route path the sitemap is served at.
|
|
43
|
+
*
|
|
44
|
+
* @default "/sitemap.xml"
|
|
45
|
+
*/
|
|
46
|
+
path?: string;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Prerender the sitemap to a static file at build time.
|
|
50
|
+
*
|
|
51
|
+
* @default true
|
|
52
|
+
*/
|
|
53
|
+
static?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
export class SitemapPrimitive extends Primitive<SitemapPrimitiveOptions> {
|
|
59
|
+
protected readonly router = $inject(ServerRouterProvider);
|
|
60
|
+
protected readonly dateTime = $inject(DateTimeProvider);
|
|
61
|
+
|
|
62
|
+
protected onInit() {
|
|
63
|
+
this.router.createRoute({
|
|
64
|
+
method: "GET",
|
|
65
|
+
path: this.options.path ?? "/sitemap.xml",
|
|
66
|
+
static: this.options.static ?? true,
|
|
67
|
+
silent: true,
|
|
68
|
+
handler: (request: ServerRequest) => {
|
|
69
|
+
request.reply.setHeader("content-type", "application/xml");
|
|
70
|
+
return this.buildSitemap();
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Render the sitemap to its path and body. Used by the build to snapshot the
|
|
77
|
+
* sitemap to a static file.
|
|
78
|
+
*/
|
|
79
|
+
public prerender(): { path: string; body: string } {
|
|
80
|
+
return {
|
|
81
|
+
path: this.options.path ?? "/sitemap.xml",
|
|
82
|
+
body: this.buildSitemap(),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Build the sitemap XML from the application's page primitives.
|
|
88
|
+
*/
|
|
89
|
+
protected buildSitemap(): string {
|
|
90
|
+
const hostname =
|
|
91
|
+
this.options.hostname ?? String(this.alepha.env.PUBLIC_URL ?? "");
|
|
92
|
+
const pages = this.getSitemapPages();
|
|
93
|
+
return this.generateSitemapFromPages(pages, hostname);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Select the pages that should appear in the sitemap.
|
|
98
|
+
*
|
|
99
|
+
* Excludes layout pages (with `children`), wildcard paths, and `/404`.
|
|
100
|
+
* Parameterized pages are included only when they declare `static.entries`.
|
|
101
|
+
*/
|
|
102
|
+
protected getSitemapPages(): any[] {
|
|
103
|
+
const pages = this.alepha.primitives("page") as any[];
|
|
104
|
+
return pages.filter((page) => {
|
|
105
|
+
const options = page.options;
|
|
106
|
+
const path: string = options.path ?? "";
|
|
107
|
+
if (options.children) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
if (path.includes("*")) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
if (path === "/404") {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if (!options.schema?.params) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
if (
|
|
120
|
+
options.static &&
|
|
121
|
+
typeof options.static === "object" &&
|
|
122
|
+
options.static.entries
|
|
123
|
+
) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
protected generateSitemapFromPages(pages: any[], baseUrl: string): string {
|
|
131
|
+
const urls: string[] = [];
|
|
132
|
+
const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
|
|
133
|
+
|
|
134
|
+
for (const page of pages) {
|
|
135
|
+
const options = page.options;
|
|
136
|
+
|
|
137
|
+
if (!options.schema?.params) {
|
|
138
|
+
const path = options.path || "";
|
|
139
|
+
const url = `${normalizedBaseUrl}${path === "" ? "/" : path}`;
|
|
140
|
+
urls.push(url);
|
|
141
|
+
} else if (
|
|
142
|
+
options.static &&
|
|
143
|
+
typeof options.static === "object" &&
|
|
144
|
+
options.static.entries
|
|
145
|
+
) {
|
|
146
|
+
for (const entry of options.static.entries) {
|
|
147
|
+
const path = this.buildPathFromParams(
|
|
148
|
+
options.path || "",
|
|
149
|
+
entry.params || {},
|
|
150
|
+
);
|
|
151
|
+
const url = `${normalizedBaseUrl}${path}`;
|
|
152
|
+
urls.push(url);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return this.buildSitemapXml(urls);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
protected buildPathFromParams(
|
|
161
|
+
pathPattern: string,
|
|
162
|
+
params: Record<string, any>,
|
|
163
|
+
): string {
|
|
164
|
+
let path = pathPattern;
|
|
165
|
+
for (const [key, value] of Object.entries(params)) {
|
|
166
|
+
path = path.replace(`:${key}`, String(value));
|
|
167
|
+
}
|
|
168
|
+
return path || "/";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
protected buildSitemapXml(urls: string[]): string {
|
|
172
|
+
const lastMod = this.dateTime.now().format("YYYY-MM-DD");
|
|
173
|
+
const urlEntries = urls
|
|
174
|
+
.map(
|
|
175
|
+
(url) =>
|
|
176
|
+
` <url>\n <loc>${this.escapeXml(url)}</loc>\n <lastmod>${lastMod}</lastmod>\n </url>`,
|
|
177
|
+
)
|
|
178
|
+
.join("\n");
|
|
179
|
+
|
|
180
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
181
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
182
|
+
${urlEntries}
|
|
183
|
+
</urlset>`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
protected escapeXml(str: string): string {
|
|
187
|
+
return str
|
|
188
|
+
.replace(/&/g, "&")
|
|
189
|
+
.replace(/</g, "<")
|
|
190
|
+
.replace(/>/g, ">")
|
|
191
|
+
.replace(/"/g, """)
|
|
192
|
+
.replace(/'/g, "'");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
$sitemap[KIND] = SitemapPrimitive;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { FormModel } from "alepha/react/form";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Schema-bound metadata read by `<Control>` (in `@alepha/ui
|
|
5
|
-
*
|
|
6
|
-
* schema option.
|
|
4
|
+
* Schema-bound metadata read by `<Control>` (in `@alepha/ui`) to configure
|
|
5
|
+
* how a field renders. Place under `$control` on any TypeBox schema option.
|
|
7
6
|
*
|
|
8
7
|
* Two forms:
|
|
9
8
|
*
|
|
@@ -201,7 +200,7 @@ export const resolveSchemaControl = (
|
|
|
201
200
|
declare module "typebox" {
|
|
202
201
|
interface TSchemaOptions {
|
|
203
202
|
/**
|
|
204
|
-
* UI metadata read by `<Control>` from `@alepha/ui
|
|
203
|
+
* UI metadata read by `<Control>` from `@alepha/ui`. See
|
|
205
204
|
* {@link SchemaControl}.
|
|
206
205
|
*/
|
|
207
206
|
$control?: SchemaControlOption;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decodeJwt,
|
|
3
|
+
decodeProtectedHeader,
|
|
4
|
+
exportPKCS8,
|
|
5
|
+
generateKeyPair,
|
|
6
|
+
} from "jose";
|
|
7
|
+
import { describe, expect, it } from "vitest";
|
|
8
|
+
import { signAppleClientSecret } from "../helpers/appleClientSecret.ts";
|
|
9
|
+
|
|
10
|
+
describe("apple client secret", () => {
|
|
11
|
+
it("signs an ES256 JWT with the Apple-required claims + kid header", async () => {
|
|
12
|
+
const { privateKey } = await generateKeyPair("ES256", {
|
|
13
|
+
crv: "P-256",
|
|
14
|
+
extractable: true,
|
|
15
|
+
});
|
|
16
|
+
const pem = await exportPKCS8(privateKey);
|
|
17
|
+
const secret = await signAppleClientSecret({
|
|
18
|
+
privateKeyPem: pem,
|
|
19
|
+
teamId: "TEAM123456",
|
|
20
|
+
serviceId: "club.alepha.signin",
|
|
21
|
+
keyId: "KEY1234567",
|
|
22
|
+
});
|
|
23
|
+
const claims = decodeJwt(secret);
|
|
24
|
+
expect(claims.iss).toBe("TEAM123456");
|
|
25
|
+
expect(claims.sub).toBe("club.alepha.signin");
|
|
26
|
+
expect(claims.aud).toBe("https://appleid.apple.com");
|
|
27
|
+
expect(typeof claims.exp).toBe("number");
|
|
28
|
+
|
|
29
|
+
// Apple requires ES256 + the Key ID in the protected header.
|
|
30
|
+
const header = decodeProtectedHeader(secret);
|
|
31
|
+
expect(header.alg).toBe("ES256");
|
|
32
|
+
expect(header.kid).toBe("KEY1234567");
|
|
33
|
+
});
|
|
34
|
+
});
|