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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/react/core/components/ClientOnly.tsx","../../../src/react/core/components/ErrorBoundary.tsx","../../../src/react/core/contexts/AlephaContext.ts","../../../src/react/core/contexts/AlephaProvider.tsx","../../../src/react/core/hooks/useAlepha.ts","../../../src/react/core/hooks/useInject.ts","../../../src/react/core/hooks/useAction.ts","../../../src/react/core/hooks/useClient.ts","../../../src/react/core/hooks/useEvents.ts","../../../src/react/core/hooks/useQuery.ts","../../../src/react/core/hooks/useStore.ts","../../../src/react/core/index.ts"],"sourcesContent":["import {\n type PropsWithChildren,\n type ReactNode,\n useEffect,\n useState,\n} from \"react\";\n\nexport interface ClientOnlyProps {\n fallback?: ReactNode;\n disabled?: boolean;\n}\n\n/**\n * A small utility component that renders its children only on the client side.\n *\n * Optionally, you can provide a fallback React node that will be rendered.\n *\n * You should use this component when\n * - you have code that relies on browser-specific APIs\n * - you want to avoid server-side rendering for a specific part of your application\n * - you want to prevent pre-rendering of a component\n *\n * @example\n * ```tsx\n * import { ClientOnly } from \"alepha/react\";\n *\n * const MyComponent = () => {\n * // Avoids SSR issues with Date API\n * return (\n * <ClientOnly>\n * {new Date().toLocaleTimeString()}\n * </ClientOnly>\n * );\n * }\n * ```\n */\nconst ClientOnly = (props: PropsWithChildren<ClientOnlyProps>) => {\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => setMounted(true), []);\n\n if (props.disabled) {\n return props.children;\n }\n\n return mounted ? props.children : props.fallback;\n};\n\nexport default ClientOnly;\n","import React, {\n type ErrorInfo,\n type PropsWithChildren,\n type ReactNode,\n} from \"react\";\n\n/**\n * Props for the ErrorBoundary component.\n */\nexport interface ErrorBoundaryProps {\n /**\n * Fallback React node to render when an error is caught.\n * If not provided, a default error message will be shown.\n */\n fallback: (error: Error, reset: () => void) => ReactNode;\n\n /**\n * Optional callback that receives the error and error info.\n * Use this to log errors to a monitoring service.\n */\n onError?: (error: Error, info: ErrorInfo) => void;\n}\n\n/**\n * State of the ErrorBoundary component.\n */\ninterface ErrorBoundaryState {\n error?: Error;\n}\n\n/**\n * A reusable error boundary for catching rendering errors in any part of the React component tree.\n *\n * It's already included in the Alepha React framework when using page or layout components.\n */\nexport class ErrorBoundary extends React.Component<\n PropsWithChildren<ErrorBoundaryProps>,\n ErrorBoundaryState\n> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = {};\n }\n\n /**\n * Update state so the next render shows the fallback UI.\n */\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return {\n error,\n };\n }\n\n /**\n * Lifecycle method called when an error is caught.\n * You can log the error or perform side effects here.\n */\n componentDidCatch(error: Error, info: ErrorInfo): void {\n if (this.props.onError) {\n this.props.onError(error, info);\n }\n }\n\n render(): ReactNode {\n if (this.state.error) {\n const reset = () => {\n this.setState({ error: undefined });\n };\n return this.props.fallback(this.state.error, reset);\n }\n\n return this.props.children;\n }\n}\n\nexport default ErrorBoundary;\n","import type { Alepha } from \"alepha\";\nimport { createContext } from \"react\";\n\n/**\n * React context to provide the Alepha instance throughout the component tree.\n */\nexport const AlephaContext = createContext<Alepha | undefined>(undefined);\n","import { Alepha } from \"alepha\";\nimport { type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { AlephaContext } from \"./AlephaContext.ts\";\n\nexport interface AlephaProviderProps {\n children: ReactNode;\n onError: (error: Error) => ReactNode;\n onLoading: () => ReactNode;\n}\n\n/**\n * AlephaProvider component to initialize and provide Alepha instance to the app.\n *\n * This isn't recommended for apps using `alepha/react/router`, as Router will handle this for you.\n */\nexport const AlephaProvider = (props: AlephaProviderProps) => {\n const alepha = useMemo(() => Alepha.create(), []);\n\n const [started, setStarted] = useState(false);\n const [error, setError] = useState<Error | undefined>();\n\n useEffect(() => {\n alepha\n .start()\n .then(() => setStarted(true))\n .catch((err) => setError(err));\n }, [alepha]);\n\n if (error) {\n return props.onError(error);\n }\n\n if (!started) {\n return props.onLoading();\n }\n\n return (\n <AlephaContext.Provider value={alepha}>\n {props.children}\n </AlephaContext.Provider>\n );\n};\n","import { type Alepha, AlephaError } from \"alepha\";\nimport { useContext } from \"react\";\nimport { AlephaContext } from \"../contexts/AlephaContext.ts\";\n\n/**\n * Main Alepha hook.\n *\n * It provides access to the Alepha instance within a React component.\n *\n * With Alepha, you can access the core functionalities of the framework:\n *\n * - alepha.state() for state management\n * - alepha.inject() for dependency injection\n * - alepha.events.emit() for event handling\n * etc...\n */\nexport const useAlepha = (): Alepha => {\n const alepha = useContext(AlephaContext);\n if (!alepha) {\n throw new AlephaError(\n \"Hook 'useAlepha()' must be used within an AlephaContext.Provider\",\n );\n }\n\n return alepha;\n};\n","import type { Service } from \"alepha\";\nimport { useMemo } from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\n\n/**\n * Hook to inject a service instance.\n * It's a wrapper of `useAlepha().inject(service)` with a memoization.\n */\nexport const useInject = <T extends object>(service: Service<T>): T => {\n const alepha = useAlepha();\n return useMemo(() => alepha.inject(service), []);\n};\n","import type { Async } from \"alepha\";\nimport {\n DateTimeProvider,\n type DurationLike,\n type Interval,\n type Timeout,\n} from \"alepha/datetime\";\nimport {\n type DependencyList,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\nimport { useInject } from \"./useInject.ts\";\n\n/**\n * Hook for handling async actions with automatic error handling and event emission.\n *\n * By default, prevents concurrent executions - if an action is running and you call it again,\n * the second call will be ignored. Use `debounce` option to delay execution instead.\n *\n * Emits lifecycle events:\n * - `react:action:begin` - When action starts\n * - `react:action:success` - When action completes successfully\n * - `react:action:error` - When action throws an error\n * - `react:action:end` - Always emitted at the end\n *\n * @example Basic usage\n * ```tsx\n * const action = useAction({\n * handler: async (data) => {\n * await api.save(data);\n * }\n * }, []);\n *\n * <button onClick={() => action.run(data)} disabled={action.loading}>\n * Save\n * </button>\n * ```\n *\n * @example With debounce (search input)\n * ```tsx\n * const search = useAction({\n * handler: async (query: string) => {\n * await api.search(query);\n * },\n * debounce: 300 // Wait 300ms after last call\n * }, []);\n *\n * <input onChange={(e) => search.run(e.target.value)} />\n * ```\n *\n * @example Run on component mount\n * ```tsx\n * const fetchData = useAction({\n * handler: async () => {\n * const data = await api.getData();\n * return data;\n * },\n * runOnInit: true // Runs once when component mounts\n * }, []);\n * ```\n *\n * @example Run periodically (polling)\n * ```tsx\n * const pollStatus = useAction({\n * handler: async () => {\n * const status = await api.getStatus();\n * return status;\n * },\n * runEvery: 5000 // Run every 5 seconds\n * }, []);\n *\n * // Or with duration tuple\n * const pollStatus = useAction({\n * handler: async () => {\n * const status = await api.getStatus();\n * return status;\n * },\n * runEvery: [30, 'seconds'] // Run every 30 seconds\n * }, []);\n * ```\n *\n * @example With AbortController\n * ```tsx\n * const fetch = useAction({\n * handler: async (url, { signal }) => {\n * const response = await fetch(url, { signal });\n * return response.json();\n * }\n * }, []);\n * // Automatically cancelled on unmount or when new request starts\n * ```\n *\n * @example With error handling\n * ```tsx\n * const deleteAction = useAction({\n * handler: async (id: string) => {\n * await api.delete(id);\n * },\n * onError: (error) => {\n * if (error.code === 'NOT_FOUND') {\n * // Custom error handling\n * }\n * }\n * }, []);\n *\n * {deleteAction.error && <div>Error: {deleteAction.error.message}</div>}\n * ```\n *\n * @example Global error handling\n * ```tsx\n * // In your root app setup\n * alepha.events.on(\"react:action:error\", ({ error }) => {\n * toast.danger(error.message);\n * Sentry.captureException(error);\n * });\n * ```\n */\nexport function useAction<Args extends any[], Result = void>(\n options: UseActionOptions<Args, Result>,\n deps: DependencyList,\n): UseActionReturn<Args, Result> {\n const alepha = useAlepha();\n const dateTimeProvider = useInject(DateTimeProvider);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>();\n const [result, setResult] = useState<Result | undefined>();\n const isExecutingRef = useRef(false);\n const debounceTimerRef = useRef<Timeout | undefined>(undefined);\n const abortControllerRef = useRef<AbortController | undefined>(undefined);\n const isMountedRef = useRef(true);\n const intervalRef = useRef<Interval | undefined>(undefined);\n\n // Track mount state — must set true in body for React StrictMode double-invoke\n useEffect(() => {\n isMountedRef.current = true;\n\n return () => {\n isMountedRef.current = false;\n\n // clear debounce timer\n if (debounceTimerRef.current) {\n dateTimeProvider.clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = undefined;\n }\n\n // clear interval\n if (intervalRef.current) {\n dateTimeProvider.clearInterval(intervalRef.current);\n intervalRef.current = undefined;\n }\n\n // abort in-flight request\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = undefined;\n }\n };\n }, []);\n\n const executeAction = useCallback(\n async (...args: Args): Promise<Result | undefined> => {\n // Prevent concurrent executions\n if (isExecutingRef.current) {\n return;\n }\n\n // Abort previous request if still running\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n\n // Create new AbortController for this request\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n isExecutingRef.current = true;\n setLoading(true);\n setError(undefined);\n\n try {\n await alepha.events.emit(\"react:action:begin\", {\n type: \"custom\",\n id: options.id,\n });\n // Pass abort signal as last argument to handler\n const result = await options.handler(...args, {\n signal: abortController.signal,\n } as any);\n\n // Only update state if still mounted and not aborted\n if (!isMountedRef.current || abortController.signal.aborted) {\n return;\n }\n\n setResult(result as Result);\n\n await alepha.events.emit(\"react:action:success\", {\n type: \"custom\",\n id: options.id,\n });\n\n if (options.onSuccess) {\n await options.onSuccess(result);\n }\n\n return result;\n } catch (err) {\n // Ignore abort errors\n if (err instanceof Error && err.name === \"AbortError\") {\n return;\n }\n\n // Only update state if still mounted\n if (!isMountedRef.current) {\n return;\n }\n\n const error = err as Error;\n setError(error);\n\n await alepha.events.emit(\"react:action:error\", {\n type: \"custom\",\n id: options.id,\n error,\n });\n\n if (options.onError) {\n await options.onError(error);\n } else {\n // Re-throw if no custom error handler\n throw error;\n }\n } finally {\n isExecutingRef.current = false;\n if (isMountedRef.current) {\n setLoading(false);\n }\n\n await alepha.events.emit(\"react:action:end\", {\n type: \"custom\",\n id: options.id,\n });\n\n // Clean up abort controller\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = undefined;\n }\n }\n },\n [...deps, options.id, options.onError, options.onSuccess],\n );\n\n const handler = useCallback(\n async (...args: Args): Promise<Result | undefined> => {\n if (options.debounce) {\n // clear existing timer\n if (debounceTimerRef.current) {\n dateTimeProvider.clearTimeout(debounceTimerRef.current);\n }\n\n // Set new timer\n return new Promise((resolve) => {\n debounceTimerRef.current = dateTimeProvider.createTimeout(\n async () => {\n const result = await executeAction(...args);\n resolve(result);\n },\n options.debounce ?? 0,\n );\n });\n }\n\n return executeAction(...args);\n },\n [executeAction, options.debounce],\n );\n\n const cancel = useCallback(() => {\n // clear debounce timer\n if (debounceTimerRef.current) {\n dateTimeProvider.clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = undefined;\n }\n\n // abort in-flight request\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = undefined;\n }\n\n // reset state\n if (isMountedRef.current) {\n isExecutingRef.current = false;\n setLoading(false);\n }\n }, []);\n\n // Run action on mount if runOnInit is true\n useEffect(() => {\n if (options.runOnInit) {\n handler(...([] as any));\n }\n }, deps);\n\n // Run action periodically if runEvery is specified\n useEffect(() => {\n if (!options.runEvery) {\n return;\n }\n\n // Set up interval\n intervalRef.current = dateTimeProvider.createInterval(\n () => handler(...([] as any)),\n options.runEvery,\n true,\n );\n\n // cleanup on unmount or when runEvery changes\n return () => {\n if (intervalRef.current) {\n dateTimeProvider.clearInterval(intervalRef.current);\n intervalRef.current = undefined;\n }\n };\n }, [handler, options.runEvery]);\n\n return {\n run: handler,\n loading,\n error,\n cancel,\n result,\n };\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Context object passed as the last argument to action handlers.\n * Contains an AbortSignal that can be used to cancel the request.\n */\nexport interface ActionContext {\n /**\n * AbortSignal that can be passed to fetch or other async operations.\n * The signal will be aborted when:\n * - The component unmounts\n * - A new action is triggered (cancels previous)\n * - The cancel() method is called\n *\n * @example\n * ```tsx\n * const action = useAction({\n * handler: async (url, { signal }) => {\n * const response = await fetch(url, { signal });\n * return response.json();\n * }\n * }, []);\n * ```\n */\n signal: AbortSignal;\n}\n\nexport interface UseActionOptions<Args extends any[] = any[], Result = any> {\n /**\n * The async action handler function.\n * Receives the action arguments plus an ActionContext as the last parameter.\n */\n handler: (...args: [...Args, ActionContext]) => Async<Result>;\n\n /**\n * Custom error handler. If provided, prevents default error re-throw.\n */\n onError?: (error: Error) => void | Promise<void>;\n\n /**\n * Custom success handler.\n */\n onSuccess?: (result: Result) => void | Promise<void>;\n\n /**\n * Optional identifier for this action (useful for debugging/analytics)\n */\n id?: string;\n\n name?: string;\n\n /**\n * Debounce delay in milliseconds. If specified, the action will only execute\n * after the specified delay has passed since the last call. Useful for search inputs\n * or other high-frequency events.\n *\n * @example\n * ```tsx\n * // Execute search 300ms after user stops typing\n * const search = useAction({ handler: search, debounce: 300 }, [])\n * ```\n */\n debounce?: number;\n\n /**\n * If true, the action will be executed once when the component mounts.\n *\n * @example\n * ```tsx\n * const fetchData = useAction({\n * handler: async () => await api.getData(),\n * runOnInit: true\n * }, []);\n * ```\n */\n runOnInit?: boolean;\n\n /**\n * If specified, the action will be executed periodically at the given interval.\n * The interval is specified as a DurationLike value (number in ms, Duration object, or [number, unit] tuple).\n *\n * @example\n * ```tsx\n * // Run every 5 seconds\n * const poll = useAction({\n * handler: async () => await api.poll(),\n * runEvery: 5000\n * }, []);\n * ```\n *\n * @example\n * ```tsx\n * // Run every 1 minute\n * const poll = useAction({\n * handler: async () => await api.poll(),\n * runEvery: [1, 'minute']\n * }, []);\n * ```\n */\n runEvery?: DurationLike;\n}\n\nexport interface UseActionReturn<Args extends any[], Result> {\n /**\n * Execute the action with the provided arguments.\n *\n * @example\n * ```tsx\n * const action = useAction({ handler: async (data) => { ... } }, []);\n * action.run(data);\n * ```\n */\n run: (...args: Args) => Promise<Result | undefined>;\n\n /**\n * Loading state - true when action is executing.\n */\n loading: boolean;\n\n /**\n * Error state - contains error if action failed, undefined otherwise.\n */\n error?: Error;\n\n /**\n * Cancel any pending debounced action or abort the current in-flight request.\n *\n * @example\n * ```tsx\n * const action = useAction({ ... }, []);\n *\n * <button onClick={action.cancel} disabled={!action.loading}>\n * Cancel\n * </button>\n * ```\n */\n cancel: () => void;\n\n /**\n * The result data from the last successful action execution.\n */\n result?: Result;\n}\n","import {\n type ClientScope,\n type HttpVirtualClient,\n LinkProvider,\n} from \"alepha/server/links\";\nimport { useMemo } from \"react\";\nimport { useInject } from \"./useInject.ts\";\n\n/**\n * Hook to get a virtual client for the specified scope.\n *\n * It's the React-hook version of `$client()`, from `AlephaServerLinks` module.\n */\nexport const useClient = <T extends object>(\n scope?: ClientScope,\n): HttpVirtualClient<T> => {\n const linkProvider = useInject(LinkProvider);\n\n return useMemo(() => {\n return linkProvider.client<T>(scope);\n }, [scope]);\n};\n","import type { Async, Hook, Hooks } from \"alepha\";\nimport { type DependencyList, useEffect } from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\n\n/**\n * Allow subscribing to multiple Alepha events. See {@link Hooks} for available events.\n *\n * useEvents is fully typed to ensure correct event callback signatures.\n *\n * @example\n * ```tsx\n * useEvents(\n * {\n * \"react:transition:begin\": (ev) => {\n * console.log(\"Transition began to:\", ev.to);\n * },\n * \"react:transition:error\": {\n * priority: \"first\",\n * callback: (ev) => {\n * console.error(\"Transition error:\", ev.error);\n * },\n * },\n * },\n * [],\n * );\n * ```\n */\nexport const useEvents = (opts: UseEvents, deps: DependencyList) => {\n const alepha = useAlepha();\n\n useEffect(() => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n const subs: Function[] = [];\n for (const [name, hook] of Object.entries(opts)) {\n subs.push(alepha.events.on(name as any, hook as any));\n }\n\n return () => {\n for (const clear of subs) {\n clear();\n }\n };\n }, deps);\n};\n\ntype UseEvents = {\n [T in keyof Hooks]?: Hook<T> | ((payload: Hooks[T]) => Async<void>);\n};\n","import type { Async } from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { type DependencyList, useCallback, useState } from \"react\";\nimport type { ActionContext } from \"./useAction.ts\";\nimport { useAction } from \"./useAction.ts\";\n\n/**\n * Hook for declarative data fetching with automatic execution and refetch.\n *\n * Thin wrapper over {@link useAction}: it pre-applies `runOnInit: true`,\n * exposes the last result as `data`, and provides a stable `refetch()` to\n * re-run the query on demand. For optimistic mutations and side-effects,\n * use {@link useAction} directly — `useQuery` is for the read path.\n *\n * Caching, request deduplication, and AbortSignal cancellation come from\n * `useAction` + `HttpClient`. There is no separate cache layer — pass\n * `localCache` to your `HttpClient.fetch()`/`fetchAction()` call inside\n * the query handler if you want per-call caching.\n *\n * @example Basic\n * ```tsx\n * const client = useInject(HttpClient);\n * const { data, loading, error, refetch } = useQuery({\n * handler: async ({ signal }) => {\n * const res = await client.fetch(\"/api/users\", { request: { signal } });\n * return res.data;\n * },\n * }, []);\n * ```\n *\n * @example Re-fetch when a dep changes\n * ```tsx\n * const { data } = useQuery({\n * handler: async () => api.getUser(userId),\n * }, [userId]);\n * ```\n *\n * @example Polling\n * ```tsx\n * const { data } = useQuery({\n * handler: async () => api.getStatus(),\n * runEvery: [5, \"seconds\"],\n * }, []);\n * ```\n */\nexport function useQuery<Result>(\n options: UseQueryOptions<Result>,\n deps: DependencyList,\n): UseQueryReturn<Result> {\n const [data, setData] = useState<Result | undefined>(options.initialData);\n\n const action = useAction<[], Result>(\n {\n id: options.id,\n handler: options.handler,\n runOnInit: options.enabled !== false,\n runEvery: options.runEvery,\n debounce: options.debounce,\n onError: options.onError,\n onSuccess: async (result) => {\n setData(result);\n if (options.onSuccess) {\n await options.onSuccess(result);\n }\n },\n },\n deps,\n );\n\n const refetch = useCallback(() => action.run(), [action.run]);\n\n return {\n data,\n loading: action.loading,\n error: action.error,\n refetch,\n cancel: action.cancel,\n };\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface UseQueryOptions<Result> {\n /**\n * Async query handler. Receives an {@link ActionContext} with an\n * AbortSignal that fires on unmount, dependency change, or `cancel()`.\n */\n handler: (context: ActionContext) => Async<Result>;\n\n /**\n * Optional identifier (used in lifecycle events for debugging/analytics).\n */\n id?: string;\n\n /**\n * If `false`, skip automatic execution on mount and dep change. Use\n * `refetch()` to trigger manually. Defaults to `true`.\n */\n enabled?: boolean;\n\n /**\n * Initial value for `data` before the first successful fetch.\n */\n initialData?: Result;\n\n /**\n * Re-run periodically. See {@link useAction} `runEvery`.\n */\n runEvery?: DurationLike;\n\n /**\n * Debounce delay in milliseconds. See {@link useAction} `debounce`.\n */\n debounce?: number;\n\n /**\n * Called on success with the resolved value.\n */\n onSuccess?: (result: Result) => void | Promise<void>;\n\n /**\n * Custom error handler. If provided, prevents default error re-throw.\n */\n onError?: (error: Error) => void | Promise<void>;\n}\n\nexport interface UseQueryReturn<Result> {\n /**\n * The last successful result. `undefined` until the first fetch resolves\n * (or the value of `initialData` if provided).\n */\n data: Result | undefined;\n\n /**\n * Loading state — `true` while a fetch is in flight.\n */\n loading: boolean;\n\n /**\n * Error from the last failed fetch, if any.\n */\n error?: Error;\n\n /**\n * Re-run the query. The previous in-flight request, if any, is aborted.\n */\n refetch: () => Promise<Result | undefined>;\n\n /**\n * Abort the in-flight request without scheduling another.\n */\n cancel: () => void;\n}\n","import type { State, Static, TAtomObject } from \"alepha\";\nimport { Atom } from \"alepha\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\n\n/**\n * Hook to access and mutate the Alepha state.\n */\nfunction useStore<T extends TAtomObject>(\n target: Atom<T>,\n defaultValue?: Static<T>,\n): UseStoreReturn<Static<T>>;\nfunction useStore<Key extends keyof State>(\n target: Key,\n defaultValue?: State[Key],\n): UseStoreReturn<State[Key]>;\nfunction useStore(target: any, defaultValue?: any): any {\n const alepha = useAlepha();\n\n useMemo(() => {\n if (defaultValue != null && alepha.store.get(target) == null) {\n alepha.store.set(target, defaultValue);\n }\n }, [defaultValue]);\n\n const [state, setState] = useState(alepha.store.get(target));\n\n useEffect(() => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n const key = target instanceof Atom ? target.key : target;\n\n return alepha.events.on(\"state:mutate\", (ev) => {\n if (ev.key === key) {\n setState(ev.value);\n }\n });\n }, []);\n\n return [\n state,\n (value: any) => {\n alepha.store.set(target, value);\n },\n ] as const;\n}\n\nexport type UseStoreReturn<T> = [T, (value: T) => void];\n\nexport { useStore };\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type * from \"./components/ClientOnly.tsx\";\nexport { default as ClientOnly } from \"./components/ClientOnly.tsx\";\nexport type * from \"./components/ErrorBoundary.tsx\";\nexport { default as ErrorBoundary } from \"./components/ErrorBoundary.tsx\";\nexport * from \"./contexts/AlephaContext.ts\";\nexport * from \"./contexts/AlephaProvider.tsx\";\nexport * from \"./hooks/useAction.ts\";\nexport * from \"./hooks/useAlepha.ts\";\nexport * from \"./hooks/useClient.ts\";\nexport * from \"./hooks/useEvents.ts\";\nexport * from \"./hooks/useInject.ts\";\nexport * from \"./hooks/useQuery.ts\";\nexport * from \"./hooks/useStore.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n /**\n * Fires when a user action is starting.\n * Action can be a form submission, a route transition, or a custom action.\n */\n \"react:action:begin\": {\n type: string;\n id?: string;\n };\n /**\n * Fires when a user action has succeeded.\n * Action can be a form submission, a route transition, or a custom action.\n */\n \"react:action:success\": {\n type: string;\n id?: string;\n };\n /**\n * Fires when a user action has failed.\n * Action can be a form submission, a route transition, or a custom action.\n */\n \"react:action:error\": {\n type: string;\n id?: string;\n error: Error;\n };\n /**\n * Fires when a user action has completed, regardless of success or failure.\n * Action can be a form submission, a route transition, or a custom action.\n */\n \"react:action:end\": {\n type: string;\n id?: string;\n };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Full-stack React framework with server-side rendering.\n *\n * **Features:**\n * - React page routes with type-safe params\n * - Async action handler with loading/error/cancel states\n * - Type-safe HTTP client access\n * - Dependency injection in components\n * - Global state management\n * - Router navigation methods\n * - Current route state access\n * - Check if path is active\n * - URL query parameters\n * - Access route schema\n * - Subscribe to Alepha events\n * - Type-safe form handling with validation\n * - Error handling wrapper component\n * - Client-side only rendering component\n * - Server-side rendering with hydration\n * - Automatic code splitting\n * - Event system for action tracking\n *\n * @module alepha.react\n */\nexport const AlephaReact = $module({\n name: \"alepha.react.core\",\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,MAAM,cAAc,UAA8C;CAChE,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAE7C,gBAAgB,WAAW,KAAK,EAAE,EAAE,CAAC;CAErC,IAAI,MAAM,UACR,OAAO,MAAM;CAGf,OAAO,UAAU,MAAM,WAAW,MAAM;;;;;;;;;ACV1C,IAAa,gBAAb,cAAmC,MAAM,UAGvC;CACA,YAAY,OAA2B;EACrC,MAAM,MAAM;EACZ,KAAK,QAAQ,EAAE;;;;;CAMjB,OAAO,yBAAyB,OAAkC;EAChE,OAAO,EACL,OACD;;;;;;CAOH,kBAAkB,OAAc,MAAuB;EACrD,IAAI,KAAK,MAAM,SACb,KAAK,MAAM,QAAQ,OAAO,KAAK;;CAInC,SAAoB;EAClB,IAAI,KAAK,MAAM,OAAO;GACpB,MAAM,cAAc;IAClB,KAAK,SAAS,EAAE,OAAO,KAAA,GAAW,CAAC;;GAErC,OAAO,KAAK,MAAM,SAAS,KAAK,MAAM,OAAO,MAAM;;EAGrD,OAAO,KAAK,MAAM;;;;;;;;ACjEtB,MAAa,gBAAgB,cAAkC,KAAA,EAAU;;;;;;;;ACSzE,MAAa,kBAAkB,UAA+B;CAC5D,MAAM,SAAS,cAAc,OAAO,QAAQ,EAAE,EAAE,CAAC;CAEjD,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAC7C,MAAM,CAAC,OAAO,YAAY,UAA6B;CAEvD,gBAAgB;EACd,OACG,OAAO,CACP,WAAW,WAAW,KAAK,CAAC,CAC5B,OAAO,QAAQ,SAAS,IAAI,CAAC;IAC/B,CAAC,OAAO,CAAC;CAEZ,IAAI,OACF,OAAO,MAAM,QAAQ,MAAM;CAG7B,IAAI,CAAC,SACH,OAAO,MAAM,WAAW;CAG1B,OACE,oBAAC,cAAc,UAAf;EAAwB,OAAO;YAC5B,MAAM;EACgB,CAAA;;;;;;;;;;;;;;;;ACvB7B,MAAa,kBAA0B;CACrC,MAAM,SAAS,WAAW,cAAc;CACxC,IAAI,CAAC,QACH,MAAM,IAAI,YACR,mEACD;CAGH,OAAO;;;;;;;;AChBT,MAAa,aAA+B,YAA2B;CACrE,MAAM,SAAS,WAAW;CAC1B,OAAO,cAAc,OAAO,OAAO,QAAQ,EAAE,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC+GlD,SAAgB,UACd,SACA,MAC+B;CAC/B,MAAM,SAAS,WAAW;CAC1B,MAAM,mBAAmB,UAAU,iBAAiB;CACpD,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAC7C,MAAM,CAAC,OAAO,YAAY,UAA6B;CACvD,MAAM,CAAC,QAAQ,aAAa,UAA8B;CAC1D,MAAM,iBAAiB,OAAO,MAAM;CACpC,MAAM,mBAAmB,OAA4B,KAAA,EAAU;CAC/D,MAAM,qBAAqB,OAAoC,KAAA,EAAU;CACzE,MAAM,eAAe,OAAO,KAAK;CACjC,MAAM,cAAc,OAA6B,KAAA,EAAU;CAG3D,gBAAgB;EACd,aAAa,UAAU;EAEvB,aAAa;GACX,aAAa,UAAU;GAGvB,IAAI,iBAAiB,SAAS;IAC5B,iBAAiB,aAAa,iBAAiB,QAAQ;IACvD,iBAAiB,UAAU,KAAA;;GAI7B,IAAI,YAAY,SAAS;IACvB,iBAAiB,cAAc,YAAY,QAAQ;IACnD,YAAY,UAAU,KAAA;;GAIxB,IAAI,mBAAmB,SAAS;IAC9B,mBAAmB,QAAQ,OAAO;IAClC,mBAAmB,UAAU,KAAA;;;IAGhC,EAAE,CAAC;CAEN,MAAM,gBAAgB,YACpB,OAAO,GAAG,SAA4C;EAEpD,IAAI,eAAe,SACjB;EAIF,IAAI,mBAAmB,SACrB,mBAAmB,QAAQ,OAAO;EAIpC,MAAM,kBAAkB,IAAI,iBAAiB;EAC7C,mBAAmB,UAAU;EAE7B,eAAe,UAAU;EACzB,WAAW,KAAK;EAChB,SAAS,KAAA,EAAU;EAEnB,IAAI;GACF,MAAM,OAAO,OAAO,KAAK,sBAAsB;IAC7C,MAAM;IACN,IAAI,QAAQ;IACb,CAAC;GAEF,MAAM,SAAS,MAAM,QAAQ,QAAQ,GAAG,MAAM,EAC5C,QAAQ,gBAAgB,QACzB,CAAQ;GAGT,IAAI,CAAC,aAAa,WAAW,gBAAgB,OAAO,SAClD;GAGF,UAAU,OAAiB;GAE3B,MAAM,OAAO,OAAO,KAAK,wBAAwB;IAC/C,MAAM;IACN,IAAI,QAAQ;IACb,CAAC;GAEF,IAAI,QAAQ,WACV,MAAM,QAAQ,UAAU,OAAO;GAGjC,OAAO;WACA,KAAK;GAEZ,IAAI,eAAe,SAAS,IAAI,SAAS,cACvC;GAIF,IAAI,CAAC,aAAa,SAChB;GAGF,MAAM,QAAQ;GACd,SAAS,MAAM;GAEf,MAAM,OAAO,OAAO,KAAK,sBAAsB;IAC7C,MAAM;IACN,IAAI,QAAQ;IACZ;IACD,CAAC;GAEF,IAAI,QAAQ,SACV,MAAM,QAAQ,QAAQ,MAAM;QAG5B,MAAM;YAEA;GACR,eAAe,UAAU;GACzB,IAAI,aAAa,SACf,WAAW,MAAM;GAGnB,MAAM,OAAO,OAAO,KAAK,oBAAoB;IAC3C,MAAM;IACN,IAAI,QAAQ;IACb,CAAC;GAGF,IAAI,mBAAmB,YAAY,iBACjC,mBAAmB,UAAU,KAAA;;IAInC;EAAC,GAAG;EAAM,QAAQ;EAAI,QAAQ;EAAS,QAAQ;EAAU,CAC1D;CAED,MAAM,UAAU,YACd,OAAO,GAAG,SAA4C;EACpD,IAAI,QAAQ,UAAU;GAEpB,IAAI,iBAAiB,SACnB,iBAAiB,aAAa,iBAAiB,QAAQ;GAIzD,OAAO,IAAI,SAAS,YAAY;IAC9B,iBAAiB,UAAU,iBAAiB,cAC1C,YAAY;KAEV,QAAQ,MADa,cAAc,GAAG,KAAK,CAC5B;OAEjB,QAAQ,YAAY,EACrB;KACD;;EAGJ,OAAO,cAAc,GAAG,KAAK;IAE/B,CAAC,eAAe,QAAQ,SAAS,CAClC;CAED,MAAM,SAAS,kBAAkB;EAE/B,IAAI,iBAAiB,SAAS;GAC5B,iBAAiB,aAAa,iBAAiB,QAAQ;GACvD,iBAAiB,UAAU,KAAA;;EAI7B,IAAI,mBAAmB,SAAS;GAC9B,mBAAmB,QAAQ,OAAO;GAClC,mBAAmB,UAAU,KAAA;;EAI/B,IAAI,aAAa,SAAS;GACxB,eAAe,UAAU;GACzB,WAAW,MAAM;;IAElB,EAAE,CAAC;CAGN,gBAAgB;EACd,IAAI,QAAQ,WACV,QAAQ,GAAI,EAAE,CAAS;IAExB,KAAK;CAGR,gBAAgB;EACd,IAAI,CAAC,QAAQ,UACX;EAIF,YAAY,UAAU,iBAAiB,qBAC/B,QAAQ,GAAI,EAAE,CAAS,EAC7B,QAAQ,UACR,KACD;EAGD,aAAa;GACX,IAAI,YAAY,SAAS;IACvB,iBAAiB,cAAc,YAAY,QAAQ;IACnD,YAAY,UAAU,KAAA;;;IAGzB,CAAC,SAAS,QAAQ,SAAS,CAAC;CAE/B,OAAO;EACL,KAAK;EACL;EACA;EACA;EACA;EACD;;;;;;;;;ACnUH,MAAa,aACX,UACyB;CACzB,MAAM,eAAe,UAAU,aAAa;CAE5C,OAAO,cAAc;EACnB,OAAO,aAAa,OAAU,MAAM;IACnC,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;ACOb,MAAa,aAAa,MAAiB,SAAyB;CAClE,MAAM,SAAS,WAAW;CAE1B,gBAAgB;EACd,IAAI,CAAC,OAAO,WAAW,EACrB;EAGF,MAAM,OAAmB,EAAE;EAC3B,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,KAAK,EAC7C,KAAK,KAAK,OAAO,OAAO,GAAG,MAAa,KAAY,CAAC;EAGvD,aAAa;GACX,KAAK,MAAM,SAAS,MAClB,OAAO;;IAGV,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAV,SAAgB,SACd,SACA,MACwB;CACxB,MAAM,CAAC,MAAM,WAAW,SAA6B,QAAQ,YAAY;CAEzE,MAAM,SAAS,UACb;EACE,IAAI,QAAQ;EACZ,SAAS,QAAQ;EACjB,WAAW,QAAQ,YAAY;EAC/B,UAAU,QAAQ;EAClB,UAAU,QAAQ;EAClB,SAAS,QAAQ;EACjB,WAAW,OAAO,WAAW;GAC3B,QAAQ,OAAO;GACf,IAAI,QAAQ,WACV,MAAM,QAAQ,UAAU,OAAO;;EAGpC,EACD,KACD;CAED,MAAM,UAAU,kBAAkB,OAAO,KAAK,EAAE,CAAC,OAAO,IAAI,CAAC;CAE7D,OAAO;EACL;EACA,SAAS,OAAO;EAChB,OAAO,OAAO;EACd;EACA,QAAQ,OAAO;EAChB;;;;AC7DH,SAAS,SAAS,QAAa,cAAyB;CACtD,MAAM,SAAS,WAAW;CAE1B,cAAc;EACZ,IAAI,gBAAgB,QAAQ,OAAO,MAAM,IAAI,OAAO,IAAI,MACtD,OAAO,MAAM,IAAI,QAAQ,aAAa;IAEvC,CAAC,aAAa,CAAC;CAElB,MAAM,CAAC,OAAO,YAAY,SAAS,OAAO,MAAM,IAAI,OAAO,CAAC;CAE5D,gBAAgB;EACd,IAAI,CAAC,OAAO,WAAW,EACrB;EAGF,MAAM,MAAM,kBAAkB,OAAO,OAAO,MAAM;EAElD,OAAO,OAAO,OAAO,GAAG,iBAAiB,OAAO;GAC9C,IAAI,GAAG,QAAQ,KACb,SAAS,GAAG,MAAM;IAEpB;IACD,EAAE,CAAC;CAEN,OAAO,CACL,QACC,UAAe;EACd,OAAO,MAAM,IAAI,QAAQ,MAAM;GAElC;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACsCH,MAAa,cAAc,QAAQ,EACjC,MAAM,qBACP,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/react/core/components/ClientOnly.tsx","../../../src/react/core/components/ErrorBoundary.tsx","../../../src/react/core/contexts/AlephaContext.ts","../../../src/react/core/contexts/AlephaProvider.tsx","../../../src/react/core/hooks/useAlepha.ts","../../../src/react/core/hooks/useInject.ts","../../../src/react/core/hooks/useAction.ts","../../../src/react/core/hooks/useClient.ts","../../../src/react/core/hooks/useEvents.ts","../../../src/react/core/hooks/useQuery.ts","../../../src/react/core/hooks/useStore.ts","../../../src/react/core/index.ts"],"sourcesContent":["import {\n type PropsWithChildren,\n type ReactNode,\n useEffect,\n useState,\n} from \"react\";\n\nexport interface ClientOnlyProps {\n fallback?: ReactNode;\n disabled?: boolean;\n}\n\n/**\n * A small utility component that renders its children only on the client side.\n *\n * Optionally, you can provide a fallback React node that will be rendered.\n *\n * You should use this component when\n * - you have code that relies on browser-specific APIs\n * - you want to avoid server-side rendering for a specific part of your application\n * - you want to prevent pre-rendering of a component\n *\n * @example\n * ```tsx\n * import { ClientOnly } from \"alepha/react\";\n *\n * const MyComponent = () => {\n * // Avoids SSR issues with Date API\n * return (\n * <ClientOnly>\n * {new Date().toLocaleTimeString()}\n * </ClientOnly>\n * );\n * }\n * ```\n */\nconst ClientOnly = (props: PropsWithChildren<ClientOnlyProps>) => {\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => setMounted(true), []);\n\n if (props.disabled) {\n return props.children;\n }\n\n return mounted ? props.children : props.fallback;\n};\n\nexport default ClientOnly;\n","import React, {\n type ErrorInfo,\n type PropsWithChildren,\n type ReactNode,\n} from \"react\";\n\n/**\n * Props for the ErrorBoundary component.\n */\nexport interface ErrorBoundaryProps {\n /**\n * Fallback React node to render when an error is caught.\n * If not provided, a default error message will be shown.\n */\n fallback: (error: Error, reset: () => void) => ReactNode;\n\n /**\n * Optional callback that receives the error and error info.\n * Use this to log errors to a monitoring service.\n */\n onError?: (error: Error, info: ErrorInfo) => void;\n}\n\n/**\n * State of the ErrorBoundary component.\n */\ninterface ErrorBoundaryState {\n error?: Error;\n}\n\n/**\n * A reusable error boundary for catching rendering errors in any part of the React component tree.\n *\n * It's already included in the Alepha React framework when using page or layout components.\n */\nexport class ErrorBoundary extends React.Component<\n PropsWithChildren<ErrorBoundaryProps>,\n ErrorBoundaryState\n> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = {};\n }\n\n /**\n * Update state so the next render shows the fallback UI.\n */\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return {\n error,\n };\n }\n\n /**\n * Lifecycle method called when an error is caught.\n * You can log the error or perform side effects here.\n */\n componentDidCatch(error: Error, info: ErrorInfo): void {\n if (this.props.onError) {\n this.props.onError(error, info);\n }\n }\n\n render(): ReactNode {\n if (this.state.error) {\n const reset = () => {\n this.setState({ error: undefined });\n };\n return this.props.fallback(this.state.error, reset);\n }\n\n return this.props.children;\n }\n}\n\nexport default ErrorBoundary;\n","import type { Alepha } from \"alepha\";\nimport { createContext } from \"react\";\n\n/**\n * React context to provide the Alepha instance throughout the component tree.\n */\nexport const AlephaContext = createContext<Alepha | undefined>(undefined);\n","import { Alepha } from \"alepha\";\nimport { type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { AlephaContext } from \"./AlephaContext.ts\";\n\nexport interface AlephaProviderProps {\n children: ReactNode;\n onError: (error: Error) => ReactNode;\n onLoading: () => ReactNode;\n}\n\n/**\n * AlephaProvider component to initialize and provide Alepha instance to the app.\n *\n * This isn't recommended for apps using `alepha/react/router`, as Router will handle this for you.\n */\nexport const AlephaProvider = (props: AlephaProviderProps) => {\n const alepha = useMemo(() => Alepha.create(), []);\n\n const [started, setStarted] = useState(false);\n const [error, setError] = useState<Error | undefined>();\n\n useEffect(() => {\n alepha\n .start()\n .then(() => setStarted(true))\n .catch((err) => setError(err));\n }, [alepha]);\n\n if (error) {\n return props.onError(error);\n }\n\n if (!started) {\n return props.onLoading();\n }\n\n return (\n <AlephaContext.Provider value={alepha}>\n {props.children}\n </AlephaContext.Provider>\n );\n};\n","import { type Alepha, AlephaError } from \"alepha\";\nimport { useContext } from \"react\";\nimport { AlephaContext } from \"../contexts/AlephaContext.ts\";\n\n/**\n * Main Alepha hook.\n *\n * It provides access to the Alepha instance within a React component.\n *\n * With Alepha, you can access the core functionalities of the framework:\n *\n * - alepha.state() for state management\n * - alepha.inject() for dependency injection\n * - alepha.events.emit() for event handling\n * etc...\n */\nexport const useAlepha = (): Alepha => {\n const alepha = useContext(AlephaContext);\n if (!alepha) {\n throw new AlephaError(\n \"Hook 'useAlepha()' must be used within an AlephaContext.Provider\",\n );\n }\n\n return alepha;\n};\n","import type { Service } from \"alepha\";\nimport { useMemo } from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\n\n/**\n * Hook to inject a service instance.\n * It's a wrapper of `useAlepha().inject(service)` with a memoization.\n */\nexport const useInject = <T extends object>(service: Service<T>): T => {\n const alepha = useAlepha();\n return useMemo(() => alepha.inject(service), []);\n};\n","import type { Async } from \"alepha\";\nimport {\n DateTimeProvider,\n type DurationLike,\n type Interval,\n type Timeout,\n} from \"alepha/datetime\";\nimport {\n type DependencyList,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\nimport { useInject } from \"./useInject.ts\";\n\n/**\n * Hook for handling async actions with automatic error handling and event emission.\n *\n * By default, prevents concurrent executions - if an action is running and you call it again,\n * the second call will be ignored. Use `debounce` option to delay execution instead.\n *\n * Emits lifecycle events:\n * - `react:action:begin` - When action starts\n * - `react:action:success` - When action completes successfully\n * - `react:action:error` - When action throws an error\n * - `react:action:end` - Always emitted at the end\n *\n * @example Basic usage\n * ```tsx\n * const action = useAction({\n * handler: async (data) => {\n * await api.save(data);\n * }\n * }, []);\n *\n * <button onClick={() => action.run(data)} disabled={action.loading}>\n * Save\n * </button>\n * ```\n *\n * @example With debounce (search input)\n * ```tsx\n * const search = useAction({\n * handler: async (query: string) => {\n * await api.search(query);\n * },\n * debounce: 300 // Wait 300ms after last call\n * }, []);\n *\n * <input onChange={(e) => search.run(e.target.value)} />\n * ```\n *\n * @example Run on component mount\n * ```tsx\n * const fetchData = useAction({\n * handler: async () => {\n * const data = await api.getData();\n * return data;\n * },\n * runOnInit: true // Runs once when component mounts\n * }, []);\n * ```\n *\n * @example Run periodically (polling)\n * ```tsx\n * const pollStatus = useAction({\n * handler: async () => {\n * const status = await api.getStatus();\n * return status;\n * },\n * runEvery: 5000 // Run every 5 seconds\n * }, []);\n *\n * // Or with duration tuple\n * const pollStatus = useAction({\n * handler: async () => {\n * const status = await api.getStatus();\n * return status;\n * },\n * runEvery: [30, 'seconds'] // Run every 30 seconds\n * }, []);\n * ```\n *\n * @example With AbortController\n * ```tsx\n * const fetch = useAction({\n * handler: async (url, { signal }) => {\n * const response = await fetch(url, { signal });\n * return response.json();\n * }\n * }, []);\n * // Automatically cancelled on unmount or when new request starts\n * ```\n *\n * @example With error handling\n * ```tsx\n * const deleteAction = useAction({\n * handler: async (id: string) => {\n * await api.delete(id);\n * },\n * onError: (error) => {\n * if (error.code === 'NOT_FOUND') {\n * // Custom error handling\n * }\n * }\n * }, []);\n *\n * {deleteAction.error && <div>Error: {deleteAction.error.message}</div>}\n * ```\n *\n * @example Global error handling\n * ```tsx\n * // In your root app setup\n * alepha.events.on(\"react:action:error\", ({ error }) => {\n * toast.danger(error.message);\n * Sentry.captureException(error);\n * });\n * ```\n */\nexport function useAction<Args extends any[], Result = void>(\n options: UseActionOptions<Args, Result>,\n deps: DependencyList,\n): UseActionReturn<Args, Result> {\n const alepha = useAlepha();\n const dateTimeProvider = useInject(DateTimeProvider);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>();\n const [result, setResult] = useState<Result | undefined>();\n const isExecutingRef = useRef(false);\n const debounceTimerRef = useRef<Timeout | undefined>(undefined);\n const abortControllerRef = useRef<AbortController | undefined>(undefined);\n const isMountedRef = useRef(true);\n const intervalRef = useRef<Interval | undefined>(undefined);\n\n // Track mount state — must set true in body for React StrictMode double-invoke\n useEffect(() => {\n isMountedRef.current = true;\n\n return () => {\n isMountedRef.current = false;\n\n // clear debounce timer\n if (debounceTimerRef.current) {\n dateTimeProvider.clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = undefined;\n }\n\n // clear interval\n if (intervalRef.current) {\n dateTimeProvider.clearInterval(intervalRef.current);\n intervalRef.current = undefined;\n }\n\n // abort in-flight request\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = undefined;\n }\n\n // Release the concurrency guard: we've just abandoned the in-flight run\n // (its result is discarded by the aborted-signal check), so the *next*\n // lifecycle must be free to start. Without this, a remount that reuses\n // the same refs — React StrictMode's double-invoke, or a Suspense/router\n // remount on client-side navigation — would hit `if (isExecutingRef\n // .current) return` and never re-run, leaving a `useQuery` stuck on its\n // loading skeleton forever (the original request 200s but is dropped).\n isExecutingRef.current = false;\n };\n }, []);\n\n const executeAction = useCallback(\n async (...args: Args): Promise<Result | undefined> => {\n // Prevent concurrent executions\n if (isExecutingRef.current) {\n return;\n }\n\n // Abort previous request if still running\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n\n // Create new AbortController for this request\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n isExecutingRef.current = true;\n setLoading(true);\n setError(undefined);\n\n try {\n await alepha.events.emit(\"react:action:begin\", {\n type: \"custom\",\n id: options.id,\n });\n // Pass abort signal as last argument to handler\n const result = await options.handler(...args, {\n signal: abortController.signal,\n } as any);\n\n // Only update state if still mounted and not aborted\n if (!isMountedRef.current || abortController.signal.aborted) {\n return;\n }\n\n setResult(result as Result);\n\n await alepha.events.emit(\"react:action:success\", {\n type: \"custom\",\n id: options.id,\n });\n\n if (options.onSuccess) {\n await options.onSuccess(result);\n }\n\n return result;\n } catch (err) {\n // Ignore abort errors\n if (err instanceof Error && err.name === \"AbortError\") {\n return;\n }\n\n // Only update state if still mounted\n if (!isMountedRef.current) {\n return;\n }\n\n const error = err as Error;\n setError(error);\n\n await alepha.events.emit(\"react:action:error\", {\n type: \"custom\",\n id: options.id,\n error,\n });\n\n if (options.onError) {\n await options.onError(error);\n }\n // Without a custom `onError`, the error is NOT re-thrown: it is captured\n // in `error` state and emitted as `react:action:error` (a mounted\n // <ActionErrorToaster /> surfaces it as a toast). This keeps\n // fire-and-forget `action.run()` calls from producing unhandled\n // promise rejections.\n } finally {\n isExecutingRef.current = false;\n if (isMountedRef.current) {\n setLoading(false);\n }\n\n await alepha.events.emit(\"react:action:end\", {\n type: \"custom\",\n id: options.id,\n });\n\n // Clean up abort controller\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = undefined;\n }\n }\n },\n [...deps, options.id, options.onError, options.onSuccess],\n );\n\n const handler = useCallback(\n async (...args: Args): Promise<Result | undefined> => {\n if (options.debounce) {\n // clear existing timer\n if (debounceTimerRef.current) {\n dateTimeProvider.clearTimeout(debounceTimerRef.current);\n }\n\n // Set new timer\n return new Promise((resolve) => {\n debounceTimerRef.current = dateTimeProvider.createTimeout(\n async () => {\n const result = await executeAction(...args);\n resolve(result);\n },\n options.debounce ?? 0,\n );\n });\n }\n\n return executeAction(...args);\n },\n [executeAction, options.debounce],\n );\n\n const cancel = useCallback(() => {\n // clear debounce timer\n if (debounceTimerRef.current) {\n dateTimeProvider.clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = undefined;\n }\n\n // abort in-flight request\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = undefined;\n }\n\n // reset state\n if (isMountedRef.current) {\n isExecutingRef.current = false;\n setLoading(false);\n }\n }, []);\n\n // Run action on mount if runOnInit is true\n useEffect(() => {\n if (options.runOnInit) {\n handler(...([] as any));\n }\n }, deps);\n\n // Run action periodically if runEvery is specified\n useEffect(() => {\n if (!options.runEvery) {\n return;\n }\n\n // Set up interval\n intervalRef.current = dateTimeProvider.createInterval(\n () => handler(...([] as any)),\n options.runEvery,\n true,\n );\n\n // cleanup on unmount or when runEvery changes\n return () => {\n if (intervalRef.current) {\n dateTimeProvider.clearInterval(intervalRef.current);\n intervalRef.current = undefined;\n }\n };\n }, [handler, options.runEvery]);\n\n return {\n run: handler,\n loading,\n error,\n cancel,\n result,\n };\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Context object passed as the last argument to action handlers.\n * Contains an AbortSignal that can be used to cancel the request.\n */\nexport interface ActionContext {\n /**\n * AbortSignal that can be passed to fetch or other async operations.\n * The signal will be aborted when:\n * - The component unmounts\n * - A new action is triggered (cancels previous)\n * - The cancel() method is called\n *\n * @example\n * ```tsx\n * const action = useAction({\n * handler: async (url, { signal }) => {\n * const response = await fetch(url, { signal });\n * return response.json();\n * }\n * }, []);\n * ```\n */\n signal: AbortSignal;\n}\n\nexport interface UseActionOptions<Args extends any[] = any[], Result = any> {\n /**\n * The async action handler function.\n * Receives the action arguments plus an ActionContext as the last parameter.\n */\n handler: (...args: [...Args, ActionContext]) => Async<Result>;\n\n /**\n * Custom error handler. If provided, prevents default error re-throw.\n */\n onError?: (error: Error) => void | Promise<void>;\n\n /**\n * Custom success handler.\n */\n onSuccess?: (result: Result) => void | Promise<void>;\n\n /**\n * Optional identifier for this action (useful for debugging/analytics)\n */\n id?: string;\n\n name?: string;\n\n /**\n * Debounce delay in milliseconds. If specified, the action will only execute\n * after the specified delay has passed since the last call. Useful for search inputs\n * or other high-frequency events.\n *\n * @example\n * ```tsx\n * // Execute search 300ms after user stops typing\n * const search = useAction({ handler: search, debounce: 300 }, [])\n * ```\n */\n debounce?: number;\n\n /**\n * If true, the action will be executed once when the component mounts.\n *\n * @example\n * ```tsx\n * const fetchData = useAction({\n * handler: async () => await api.getData(),\n * runOnInit: true\n * }, []);\n * ```\n */\n runOnInit?: boolean;\n\n /**\n * If specified, the action will be executed periodically at the given interval.\n * The interval is specified as a DurationLike value (number in ms, Duration object, or [number, unit] tuple).\n *\n * @example\n * ```tsx\n * // Run every 5 seconds\n * const poll = useAction({\n * handler: async () => await api.poll(),\n * runEvery: 5000\n * }, []);\n * ```\n *\n * @example\n * ```tsx\n * // Run every 1 minute\n * const poll = useAction({\n * handler: async () => await api.poll(),\n * runEvery: [1, 'minute']\n * }, []);\n * ```\n */\n runEvery?: DurationLike;\n}\n\nexport interface UseActionReturn<Args extends any[], Result> {\n /**\n * Execute the action with the provided arguments.\n *\n * @example\n * ```tsx\n * const action = useAction({ handler: async (data) => { ... } }, []);\n * action.run(data);\n * ```\n */\n run: (...args: Args) => Promise<Result | undefined>;\n\n /**\n * Loading state - true when action is executing.\n */\n loading: boolean;\n\n /**\n * Error state - contains error if action failed, undefined otherwise.\n */\n error?: Error;\n\n /**\n * Cancel any pending debounced action or abort the current in-flight request.\n *\n * @example\n * ```tsx\n * const action = useAction({ ... }, []);\n *\n * <button onClick={action.cancel} disabled={!action.loading}>\n * Cancel\n * </button>\n * ```\n */\n cancel: () => void;\n\n /**\n * The result data from the last successful action execution.\n */\n result?: Result;\n}\n","import {\n type ClientScope,\n type HttpVirtualClient,\n LinkProvider,\n} from \"alepha/server/links\";\nimport { useMemo } from \"react\";\nimport { useInject } from \"./useInject.ts\";\n\n/**\n * Hook to get a virtual client for the specified scope.\n *\n * It's the React-hook version of `$client()`, from `AlephaServerLinks` module.\n */\nexport const useClient = <T extends object>(\n scope?: ClientScope,\n): HttpVirtualClient<T> => {\n const linkProvider = useInject(LinkProvider);\n\n return useMemo(() => {\n return linkProvider.client<T>(scope);\n }, [scope]);\n};\n","import type { Async, Hook, Hooks } from \"alepha\";\nimport { type DependencyList, useEffect } from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\n\n/**\n * Allow subscribing to multiple Alepha events. See {@link Hooks} for available events.\n *\n * useEvents is fully typed to ensure correct event callback signatures.\n *\n * @example\n * ```tsx\n * useEvents(\n * {\n * \"react:transition:begin\": (ev) => {\n * console.log(\"Transition began to:\", ev.to);\n * },\n * \"react:transition:error\": {\n * priority: \"first\",\n * callback: (ev) => {\n * console.error(\"Transition error:\", ev.error);\n * },\n * },\n * },\n * [],\n * );\n * ```\n */\nexport const useEvents = (opts: UseEvents, deps: DependencyList) => {\n const alepha = useAlepha();\n\n useEffect(() => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n const subs: Function[] = [];\n for (const [name, hook] of Object.entries(opts)) {\n subs.push(alepha.events.on(name as any, hook as any));\n }\n\n return () => {\n for (const clear of subs) {\n clear();\n }\n };\n }, deps);\n};\n\ntype UseEvents = {\n [T in keyof Hooks]?: Hook<T> | ((payload: Hooks[T]) => Async<void>);\n};\n","import type { Async } from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { type DependencyList, useCallback, useRef, useState } from \"react\";\nimport type { ActionContext } from \"./useAction.ts\";\nimport { useAction } from \"./useAction.ts\";\n\n/**\n * Hook for declarative data fetching with automatic execution and refetch.\n *\n * Thin wrapper over {@link useAction}: it pre-applies `runOnInit: true`,\n * exposes the last result as `data`, and provides a stable `refetch()` to\n * re-run the query on demand. For optimistic mutations and side-effects,\n * use {@link useAction} directly — `useQuery` is for the read path.\n *\n * Caching, request deduplication, and AbortSignal cancellation come from\n * `useAction` + `HttpClient`. There is no separate cache layer — pass\n * `localCache` to your `HttpClient.fetch()`/`fetchAction()` call inside\n * the query handler if you want per-call caching.\n *\n * @example Basic\n * ```tsx\n * const client = useInject(HttpClient);\n * const { data, loading, error, refetch } = useQuery({\n * handler: async ({ signal }) => {\n * const res = await client.fetch(\"/api/users\", { request: { signal } });\n * return res.data;\n * },\n * }, []);\n * ```\n *\n * @example Re-fetch when a dep changes\n * ```tsx\n * const { data } = useQuery({\n * handler: async () => api.getUser(userId),\n * }, [userId]);\n * ```\n *\n * @example Polling\n * ```tsx\n * const { data } = useQuery({\n * handler: async () => api.getStatus(),\n * runEvery: [5, \"seconds\"],\n * }, []);\n * ```\n */\nexport function useQuery<Result>(\n options: UseQueryOptions<Result>,\n deps: DependencyList,\n): UseQueryReturn<Result> {\n const enabled = options.enabled !== false;\n const [data, setData] = useState<Result | undefined>(options.initialData);\n // Tracks whether the auto-run has completed at least once, so we can keep\n // `loading` true on the very first render — before `runOnInit`'s effect\n // fires — instead of briefly reporting `loading: false` with no data.\n const settledRef = useRef(false);\n\n const action = useAction<[], Result>(\n {\n id: options.id,\n handler: options.handler,\n runOnInit: enabled,\n runEvery: options.runEvery,\n debounce: options.debounce,\n onError: options.onError,\n onSuccess: async (result) => {\n settledRef.current = true;\n setData(result);\n if (options.onSuccess) {\n await options.onSuccess(result);\n }\n },\n },\n deps,\n );\n\n const refetch = useCallback(() => action.run(), [action.run]);\n\n // `loading` is true while a fetch is in flight, AND during the initial gap\n // between first render and the auto-run effect — so consumers can render a\n // skeleton immediately instead of flashing an empty/not-found state. Only\n // applies to enabled queries with no `initialData` seed; an error settles\n // it (the error path keeps the caller's `onError` re-throw semantics).\n const loading =\n action.loading ||\n (enabled &&\n options.initialData === undefined &&\n !settledRef.current &&\n action.error === undefined);\n\n return {\n data,\n loading,\n error: action.error,\n refetch,\n cancel: action.cancel,\n };\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface UseQueryOptions<Result> {\n /**\n * Async query handler. Receives an {@link ActionContext} with an\n * AbortSignal that fires on unmount, dependency change, or `cancel()`.\n */\n handler: (context: ActionContext) => Async<Result>;\n\n /**\n * Optional identifier (used in lifecycle events for debugging/analytics).\n */\n id?: string;\n\n /**\n * If `false`, skip automatic execution on mount and dep change. Use\n * `refetch()` to trigger manually. Defaults to `true`.\n */\n enabled?: boolean;\n\n /**\n * Initial value for `data` before the first successful fetch.\n */\n initialData?: Result;\n\n /**\n * Re-run periodically. See {@link useAction} `runEvery`.\n */\n runEvery?: DurationLike;\n\n /**\n * Debounce delay in milliseconds. See {@link useAction} `debounce`.\n */\n debounce?: number;\n\n /**\n * Called on success with the resolved value.\n */\n onSuccess?: (result: Result) => void | Promise<void>;\n\n /**\n * Custom error handler. If provided, prevents default error re-throw.\n */\n onError?: (error: Error) => void | Promise<void>;\n}\n\nexport interface UseQueryReturn<Result> {\n /**\n * The last successful result. `undefined` until the first fetch resolves\n * (or the value of `initialData` if provided).\n */\n data: Result | undefined;\n\n /**\n * Loading state — `true` while a fetch is in flight, and also from the\n * first render until the initial auto-run settles (for enabled queries\n * with no `initialData`), so a skeleton can render without a flash.\n */\n loading: boolean;\n\n /**\n * Error from the last failed fetch, if any.\n */\n error?: Error;\n\n /**\n * Re-run the query. The previous in-flight request, if any, is aborted.\n */\n refetch: () => Promise<Result | undefined>;\n\n /**\n * Abort the in-flight request without scheduling another.\n */\n cancel: () => void;\n}\n","import type { State, Static, TAtomObject } from \"alepha\";\nimport { Atom } from \"alepha\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\n\n/**\n * Hook to access and mutate the Alepha state.\n */\nfunction useStore<T extends TAtomObject>(\n target: Atom<T>,\n defaultValue?: Static<T>,\n): UseStoreReturn<Static<T>>;\nfunction useStore<Key extends keyof State>(\n target: Key,\n defaultValue?: State[Key],\n): UseStoreReturn<State[Key]>;\nfunction useStore(target: any, defaultValue?: any): any {\n const alepha = useAlepha();\n\n useMemo(() => {\n if (defaultValue != null && alepha.store.get(target) == null) {\n alepha.store.set(target, defaultValue);\n }\n }, [defaultValue]);\n\n const [state, setState] = useState(alepha.store.get(target));\n\n useEffect(() => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n const key = target instanceof Atom ? target.key : target;\n\n return alepha.events.on(\"state:mutate\", (ev) => {\n if (ev.key === key) {\n setState(ev.value);\n }\n });\n }, []);\n\n return [\n state,\n (value: any) => {\n alepha.store.set(target, value);\n },\n ] as const;\n}\n\nexport type UseStoreReturn<T> = [T, (value: T) => void];\n\nexport { useStore };\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type * from \"./components/ClientOnly.tsx\";\nexport { default as ClientOnly } from \"./components/ClientOnly.tsx\";\nexport type * from \"./components/ErrorBoundary.tsx\";\nexport { default as ErrorBoundary } from \"./components/ErrorBoundary.tsx\";\nexport * from \"./contexts/AlephaContext.ts\";\nexport * from \"./contexts/AlephaProvider.tsx\";\nexport * from \"./hooks/useAction.ts\";\nexport * from \"./hooks/useAlepha.ts\";\nexport * from \"./hooks/useClient.ts\";\nexport * from \"./hooks/useEvents.ts\";\nexport * from \"./hooks/useInject.ts\";\nexport * from \"./hooks/useQuery.ts\";\nexport * from \"./hooks/useStore.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n /**\n * Fires when a user action is starting.\n * Action can be a form submission, a route transition, or a custom action.\n */\n \"react:action:begin\": {\n type: string;\n id?: string;\n };\n /**\n * Fires when a user action has succeeded.\n * Action can be a form submission, a route transition, or a custom action.\n */\n \"react:action:success\": {\n type: string;\n id?: string;\n };\n /**\n * Fires when a user action has failed.\n * Action can be a form submission, a route transition, or a custom action.\n */\n \"react:action:error\": {\n type: string;\n id?: string;\n error: Error;\n };\n /**\n * Fires when a user action has completed, regardless of success or failure.\n * Action can be a form submission, a route transition, or a custom action.\n */\n \"react:action:end\": {\n type: string;\n id?: string;\n };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Full-stack React framework with server-side rendering.\n *\n * **Features:**\n * - React page routes with type-safe params\n * - Async action handler with loading/error/cancel states\n * - Type-safe HTTP client access\n * - Dependency injection in components\n * - Global state management\n * - Router navigation methods\n * - Current route state access\n * - Check if path is active\n * - URL query parameters\n * - Access route schema\n * - Subscribe to Alepha events\n * - Type-safe form handling with validation\n * - Error handling wrapper component\n * - Client-side only rendering component\n * - Server-side rendering with hydration\n * - Automatic code splitting\n * - Event system for action tracking\n *\n * @module alepha.react\n */\nexport const AlephaReact = $module({\n name: \"alepha.react.core\",\n});\n"],"mappings":"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"}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import * as _$alepha from "alepha";
|
|
2
1
|
import { Alepha, Static, TArray, TObject, TSchema, TypeBoxError } from "alepha";
|
|
3
2
|
import { InputHTMLAttributes, ReactNode } from "react";
|
|
4
|
-
import * as _$alepha_logger0 from "alepha/logger";
|
|
5
3
|
|
|
6
4
|
//#region ../../src/react/form/services/FormModel.d.ts
|
|
7
5
|
/**
|
|
@@ -15,13 +13,12 @@ import * as _$alepha_logger0 from "alepha/logger";
|
|
|
15
13
|
declare class FormModel<T extends TObject> {
|
|
16
14
|
readonly id: string;
|
|
17
15
|
readonly options: FormCtrlOptions<T>;
|
|
18
|
-
protected readonly log:
|
|
16
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
19
17
|
protected readonly alepha: Alepha;
|
|
20
18
|
protected readonly values: Record<string, any>;
|
|
21
19
|
protected readonly initialValues: Record<string, any>;
|
|
22
20
|
protected submitInProgress: boolean;
|
|
23
21
|
input: SchemaToInput<T>;
|
|
24
|
-
get submitting(): boolean;
|
|
25
22
|
constructor(id: string, options: FormCtrlOptions<T>);
|
|
26
23
|
/**
|
|
27
24
|
* Extract default values from a TypeBox schema.
|
|
@@ -314,7 +311,7 @@ declare module "alepha" {
|
|
|
314
311
|
*
|
|
315
312
|
* @module alepha.react.form
|
|
316
313
|
*/
|
|
317
|
-
declare const AlephaReactForm:
|
|
314
|
+
declare const AlephaReactForm: import("alepha").Service<import("alepha").Module>;
|
|
318
315
|
//#endregion
|
|
319
316
|
export { AlephaReactForm, ArrayInputField, BaseInputField, FieldConstraints, FieldMeta, FormCtrlOptions, FormEventLike, FormModel, FormState, FormValidationError, IconHint, InputField, InputHTMLAttributesLike, ObjectInputField, ParseFieldOptions, SchemaToInput, UseFormQuerySyncOptions, UseFormStateReturn, parseField, prettyName, useFieldValue, useForm, useFormQuerySync, useFormState, useFormValues };
|
|
320
317
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/react/form/services/FormModel.ts","../../../src/react/form/components/FormState.tsx","../../../src/react/form/errors/FormValidationError.ts","../../../src/react/form/hooks/useFieldValue.ts","../../../src/react/form/hooks/useForm.ts","../../../src/react/form/hooks/useFormQuerySync.ts","../../../src/react/form/hooks/useFormState.ts","../../../src/react/form/hooks/useFormValues.ts","../../../src/react/form/services/parseField.ts","../../../src/react/form/services/prettyName.ts","../../../src/react/form/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/react/form/services/FormModel.ts","../../../src/react/form/components/FormState.tsx","../../../src/react/form/errors/FormValidationError.ts","../../../src/react/form/hooks/useFieldValue.ts","../../../src/react/form/hooks/useForm.ts","../../../src/react/form/hooks/useFormQuerySync.ts","../../../src/react/form/hooks/useFormState.ts","../../../src/react/form/hooks/useFormValues.ts","../../../src/react/form/services/parseField.ts","../../../src/react/form/services/prettyName.ts","../../../src/react/form/index.ts"],"mappings":"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"}
|
package/dist/react/form/index.js
CHANGED
|
@@ -127,9 +127,6 @@ var FormModel = class {
|
|
|
127
127
|
initialValues = {};
|
|
128
128
|
submitInProgress = false;
|
|
129
129
|
input;
|
|
130
|
-
get submitting() {
|
|
131
|
-
return this.submitInProgress;
|
|
132
|
-
}
|
|
133
130
|
constructor(id, options) {
|
|
134
131
|
this.id = id;
|
|
135
132
|
this.options = options;
|
|
@@ -219,8 +216,8 @@ var FormModel = class {
|
|
|
219
216
|
await this.alepha.events.emit("react:action:begin", {
|
|
220
217
|
type: "form",
|
|
221
218
|
id: this.id
|
|
222
|
-
});
|
|
223
|
-
await this.alepha.events.emit("form:submit:begin", { id: this.id });
|
|
219
|
+
}, { catch: true });
|
|
220
|
+
await this.alepha.events.emit("form:submit:begin", { id: this.id }, { catch: true });
|
|
224
221
|
this.submitInProgress = true;
|
|
225
222
|
const options = this.options;
|
|
226
223
|
try {
|
|
@@ -230,31 +227,35 @@ var FormModel = class {
|
|
|
230
227
|
await this.alepha.events.emit("react:action:success", {
|
|
231
228
|
type: "form",
|
|
232
229
|
id: this.id
|
|
233
|
-
});
|
|
230
|
+
}, { catch: true });
|
|
234
231
|
await this.alepha.events.emit("form:submit:success", {
|
|
235
232
|
id: this.id,
|
|
236
233
|
values
|
|
237
|
-
});
|
|
234
|
+
}, { catch: true });
|
|
238
235
|
} catch (error) {
|
|
239
236
|
this.log.error("Form submission error:", error);
|
|
240
|
-
|
|
237
|
+
try {
|
|
238
|
+
options.onError?.(error);
|
|
239
|
+
} catch (handlerError) {
|
|
240
|
+
this.log.error("Form onError handler threw:", handlerError);
|
|
241
|
+
}
|
|
241
242
|
await this.alepha.events.emit("react:action:error", {
|
|
242
243
|
type: "form",
|
|
243
244
|
id: this.id,
|
|
244
245
|
error
|
|
245
|
-
});
|
|
246
|
+
}, { catch: true });
|
|
246
247
|
await this.alepha.events.emit("form:submit:error", {
|
|
247
248
|
error,
|
|
248
249
|
id: this.id
|
|
249
|
-
});
|
|
250
|
+
}, { catch: true });
|
|
250
251
|
} finally {
|
|
251
252
|
this.submitInProgress = false;
|
|
253
|
+
await this.alepha.events.emit("react:action:end", {
|
|
254
|
+
type: "form",
|
|
255
|
+
id: this.id
|
|
256
|
+
}, { catch: true });
|
|
257
|
+
await this.alepha.events.emit("form:submit:end", { id: this.id }, { catch: true });
|
|
252
258
|
}
|
|
253
|
-
await this.alepha.events.emit("react:action:end", {
|
|
254
|
-
type: "form",
|
|
255
|
-
id: this.id
|
|
256
|
-
});
|
|
257
|
-
await this.alepha.events.emit("form:submit:end", { id: this.id });
|
|
258
259
|
};
|
|
259
260
|
/**
|
|
260
261
|
* Restructures flat keys like "address.city" into nested objects like { address: { city: ... } }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/react/form/hooks/useFormState.ts","../../../src/react/form/components/FormState.tsx","../../../src/react/form/errors/FormValidationError.ts","../../../src/react/form/hooks/useFieldValue.ts","../../../src/react/form/services/FormModel.ts","../../../src/react/form/hooks/useForm.ts","../../../src/react/form/hooks/useFormQuerySync.ts","../../../src/react/form/hooks/useFormValues.ts","../../../src/react/form/services/prettyName.ts","../../../src/react/form/services/parseField.ts","../../../src/react/form/index.ts"],"sourcesContent":["import { type TObject, TypeBoxError } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\nexport interface UseFormStateReturn {\n loading: boolean;\n dirty: boolean;\n values?: Record<string, any>;\n error?: Error;\n}\n\nexport const useFormState = <\n T extends TObject,\n Keys extends keyof UseFormStateReturn,\n>(\n target: FormModel<T> | { form: FormModel<T>; path: string },\n _events: Keys[] = [\"loading\", \"dirty\", \"error\"] as Keys[],\n): Pick<UseFormStateReturn, Keys> => {\n const alepha = useAlepha();\n const events = _events as string[];\n\n const [dirty, setDirty] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n const [values, setValues] = useState<Record<string, any> | undefined>(\n undefined,\n );\n\n const form = \"form\" in target ? target.form : target;\n const path = \"path\" in target ? target.path : undefined;\n\n const hasValues = events.includes(\"values\");\n const hasErrors = events.includes(\"error\");\n const hasDirty = events.includes(\"dirty\");\n const hasLoading = events.includes(\"loading\");\n\n useEffect(() => {\n const listeners: Function[] = [];\n\n if (hasErrors || hasValues || hasDirty) {\n listeners.push(\n alepha.events.on(\"form:change\", (event) => {\n if (event.id === form.id) {\n if (!path || event.path === path) {\n // `initial: true` marks a programmatic reset (e.g. parent\n // re-rendering with fresh data via `setInitialValues`). Treat\n // it like a reset: clear dirty, don't mark dirty.\n if (hasDirty) {\n if (event.initial) {\n setDirty(false);\n } else {\n setDirty(true);\n }\n }\n if (hasErrors) {\n setError(undefined);\n }\n }\n if (hasValues) {\n setValues(form.currentValues);\n }\n }\n }),\n );\n }\n\n if (hasLoading) {\n listeners.push(\n alepha.events.on(\"form:submit:begin\", (event) => {\n if (event.id === form.id) {\n setLoading(true);\n }\n }),\n alepha.events.on(\"form:submit:end\", (event) => {\n if (event.id === form.id) {\n setLoading(false);\n }\n }),\n );\n }\n\n if (hasValues || hasDirty) {\n listeners.push(\n alepha.events.on(\"form:submit:success\", (event) => {\n if (event.id === form.id) {\n if (hasValues) {\n setValues(event.values);\n }\n if (hasDirty) {\n setDirty(false);\n }\n }\n }),\n );\n }\n\n if (hasDirty) {\n listeners.push(\n alepha.events.on(\"form:reset\", (event) => {\n if (event.id === form.id) {\n setDirty(false);\n }\n }),\n );\n }\n\n if (hasErrors) {\n listeners.push(\n alepha.events.on(\"form:submit:error\", (event) => {\n if (event.id === form.id) {\n if (\n !path ||\n (event.error instanceof TypeBoxError &&\n event.error.value.path === path)\n ) {\n setError(event.error);\n }\n }\n }),\n );\n }\n\n return () => {\n for (const unsub of listeners) {\n unsub();\n }\n };\n }, []);\n\n return {\n dirty,\n loading,\n error,\n values,\n } as Pick<UseFormStateReturn, Keys>;\n};\n","import type { TObject } from \"alepha\";\nimport type { ReactNode } from \"react\";\nimport { useFormState } from \"../hooks/useFormState.ts\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\nconst FormState = <T extends TObject>(props: {\n form: FormModel<T>;\n children: (state: { loading: boolean; dirty: boolean }) => ReactNode;\n}) => {\n const formState = useFormState(props.form);\n return props.children({\n loading: formState.loading,\n dirty: formState.dirty,\n });\n};\n\nexport default FormState;\n","import { TypeBoxError } from \"alepha\";\n\nexport class FormValidationError extends TypeBoxError {\n readonly name = \"ValidationError\";\n\n constructor(options: {\n message: string;\n path: string;\n }) {\n super({\n message: options.message,\n instancePath: options.path,\n schemaPath: \"\",\n keyword: \"not\",\n params: {},\n });\n }\n}\n","import { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { BaseInputField } from \"../services/FormModel.ts\";\n\n/**\n * Hook to subscribe to a single form field's value.\n * Only re-renders when this specific field changes.\n *\n * @returns A tuple of [value, setValue] similar to useState.\n */\nexport const useFieldValue = (\n input: BaseInputField,\n): [any, (value: any) => void] => {\n const alepha = useAlepha();\n const [value, setValue] = useState(input?.initialValue);\n\n useEffect(() => {\n if (!input?.form || !alepha.isBrowser()) {\n return;\n }\n\n return alepha.events.on(\"form:change\", (ev) => {\n if (ev.id === input.form.id && ev.path === input.path) {\n setValue(ev.value);\n }\n });\n }, []);\n\n const setFieldValue = (newValue: any) => {\n input.set(newValue);\n };\n\n return [value, setFieldValue];\n};\n","import type { TArray } from \"alepha\";\nimport {\n $inject,\n Alepha,\n type Static,\n type TObject,\n type TSchema,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { InputHTMLAttributes } from \"react\";\n\n/**\n * FormModel is a dynamic form handler that generates form inputs based on a provided TypeBox schema.\n * It manages form state, handles input changes, and processes form submissions with validation.\n *\n * It means to be injected and used within React components to provide a structured way to create and manage forms.\n *\n * @see {@link useForm}\n */\nexport class FormModel<T extends TObject> {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly values: Record<string, any> = {};\n protected readonly initialValues: Record<string, any> = {};\n protected submitInProgress = false;\n\n public input: SchemaToInput<T>;\n\n public get submitting(): boolean {\n return this.submitInProgress;\n }\n\n constructor(\n public readonly id: string,\n public readonly options: FormCtrlOptions<T>,\n ) {\n this.options = options;\n\n // Initialize with schema defaults first, then override with initialValues\n const schemaDefaults = this.extractSchemaDefaults(options.schema);\n if (Object.keys(schemaDefaults).length > 0) {\n Object.assign(this.values, schemaDefaults);\n }\n\n if (options.initialValues) {\n // Decode against a partial of the schema so callers can supply incomplete\n // initial values (the form's whole job is to collect them). Codecs still\n // run on whatever's provided; missing fields stay undefined and only the\n // full schema is enforced at submit time.\n const decoded = this.alepha.codec.decode(\n t.partial(options.schema),\n options.initialValues,\n ) as Record<string, any>;\n Object.assign(this.values, decoded);\n }\n\n this.initialValues = { ...this.values };\n\n this.input = this.createProxyFromSchema(options, options.schema, {\n store: this.values,\n parent: \"\",\n });\n }\n\n /**\n * Extract default values from a TypeBox schema.\n * Recursively handles nested objects.\n */\n protected extractSchemaDefaults(\n schema: TObject,\n prefix: string = \"\",\n ): Record<string, any> {\n const defaults: Record<string, any> = {};\n\n if (!schema.properties) {\n return defaults;\n }\n\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n\n if (\"default\" in propSchema && propSchema.default !== undefined) {\n defaults[fullKey] = propSchema.default;\n } else if (\n propSchema &&\n \"type\" in propSchema &&\n propSchema.type === \"object\" &&\n \"properties\" in propSchema\n ) {\n // Recursively extract defaults from nested objects\n const nestedDefaults = this.extractSchemaDefaults(\n propSchema as TObject,\n fullKey,\n );\n Object.assign(defaults, nestedDefaults);\n }\n }\n\n return defaults;\n }\n\n public get currentValues(): Record<string, any> {\n return this.restructureValues(this.values);\n }\n\n public get props() {\n return {\n id: this.id,\n noValidate: true,\n onSubmit: (ev?: FormEventLike) => {\n ev?.preventDefault?.();\n this.submit();\n },\n onReset: (event: FormEventLike) => this.reset(event),\n };\n }\n\n public readonly setInitialValues = (values: Record<string, any>) => {\n // Same partial-decode rationale as the constructor — initial values may be\n // incomplete; full schema is enforced only at submit time.\n const decoded = this.alepha.codec.decode(\n t.partial(this.options.schema),\n values,\n ) as Record<string, any>;\n\n // Snapshot the OLD keys before we wipe — without this, fields that\n // had a value but are absent from the new initialValues never emit\n // form:change and useFieldValue subscribers keep showing the stale\n // value. Mirrors the union-of-keys pattern in reset() below.\n const oldKeys = new Set(Object.keys(this.values));\n\n for (const key in this.initialValues) {\n delete (this.initialValues as Record<string, any>)[key];\n }\n Object.assign(this.initialValues, decoded);\n\n for (const key in this.values) {\n delete this.values[key];\n }\n Object.assign(this.values, { ...this.initialValues });\n\n const keys = new Set<string>([...oldKeys, ...Object.keys(this.values)]);\n for (const key of keys) {\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path, value: this.values[key], initial: true },\n { catch: true },\n );\n }\n };\n\n public readonly reset = (event?: FormEventLike) => {\n event?.preventDefault?.();\n // Snapshot all keys that need notification — both keys present\n // before reset (so subscribers learn the cleared value) and keys\n // restored from initialValues. Without the union, fields that were\n // typed but absent from initialValues stay visually stale.\n const keys = new Set<string>([\n ...Object.keys(this.values),\n ...Object.keys(this.initialValues),\n ]);\n for (const key in this.values) {\n delete this.values[key];\n }\n Object.assign(this.values, { ...this.initialValues });\n for (const key of keys) {\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path, value: this.values[key] },\n { catch: true },\n );\n }\n this.alepha.events.emit(\"form:reset\", { id: this.id }, { catch: true });\n this.options.onReset?.();\n };\n\n public readonly submit = async () => {\n if (this.submitInProgress) {\n this.log.warn(\n \"Form submission already in progress, ignoring duplicate submit.\",\n );\n return;\n }\n\n // emit both action and form events\n await this.alepha.events.emit(\"react:action:begin\", {\n type: \"form\",\n id: this.id,\n });\n await this.alepha.events.emit(\"form:submit:begin\", {\n id: this.id,\n });\n\n this.submitInProgress = true;\n\n const options = this.options;\n\n try {\n let values: Record<string, any> = this.restructureValues(this.values);\n\n if (t.schema.isSchema(options.schema)) {\n values = this.alepha.codec.decode(options.schema, values) as Record<\n string,\n any\n >;\n }\n\n await options.handler(values as any);\n\n await this.alepha.events.emit(\"react:action:success\", {\n type: \"form\",\n id: this.id,\n });\n await this.alepha.events.emit(\"form:submit:success\", {\n id: this.id,\n values,\n });\n } catch (error) {\n this.log.error(\"Form submission error:\", error);\n\n options.onError?.(error as Error);\n\n await this.alepha.events.emit(\"react:action:error\", {\n type: \"form\",\n id: this.id,\n error: error as Error,\n });\n await this.alepha.events.emit(\"form:submit:error\", {\n error: error as Error,\n id: this.id,\n });\n } finally {\n this.submitInProgress = false;\n }\n\n await this.alepha.events.emit(\"react:action:end\", {\n type: \"form\",\n id: this.id,\n });\n await this.alepha.events.emit(\"form:submit:end\", {\n id: this.id,\n });\n };\n\n /**\n * Restructures flat keys like \"address.city\" into nested objects like { address: { city: ... } }\n * Values are already typed from onChange, so no conversion is needed.\n */\n protected restructureValues(store: Record<string, any>): Record<string, any> {\n const values: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(store)) {\n if (key.includes(\".\")) {\n // nested object: restructure flat key to nested structure\n this.restructureNestedValue(values, key, value);\n } else {\n // value is already typed, just copy it\n values[key] = value;\n }\n }\n\n return values;\n }\n\n /**\n * Helper to restructure a flat key like \"address.city\" into nested object structure.\n * The value is already typed, so we just assign it to the nested path.\n */\n protected restructureNestedValue(\n values: Record<string, any>,\n key: string,\n value: any,\n ) {\n const pathSegments = key.split(\".\");\n const finalPropertyKey = pathSegments.pop();\n if (!finalPropertyKey) {\n return;\n }\n\n let currentObjectLevel = values;\n\n // traverse/create the nested structure\n for (const segment of pathSegments) {\n currentObjectLevel[segment] ??= {};\n currentObjectLevel = currentObjectLevel[segment];\n }\n\n // value is already typed from onChange, just assign it\n currentObjectLevel[finalPropertyKey] = value;\n }\n\n protected createProxyFromSchema<T extends TObject>(\n options: FormCtrlOptions<T>,\n schema: TSchema,\n context: {\n parent: string;\n store: Record<string, any>;\n },\n ): SchemaToInput<T> {\n const parent = context.parent || \"\";\n return new Proxy<SchemaToInput<T>>({} as SchemaToInput<T>, {\n get: (_, prop: string) => {\n if (!options.schema || !t.schema.isObject(schema)) {\n return {};\n }\n\n if (prop in schema.properties) {\n // // it's a nested object, create another proxy\n // if (t.schema.isObject(schema.properties[prop])) {\n // return this.createProxyFromSchema(\n // options,\n // schema.properties[prop],\n // {\n // parent: parent ? `${parent}.${prop}` : prop,\n // store: context.store,\n // },\n // );\n // }\n\n return this.createInputFromSchema<T>(\n prop as keyof Static<T> & string,\n options,\n schema,\n schema.required?.includes(prop as string) || false,\n context,\n );\n }\n },\n });\n }\n\n protected createInputFromSchema<T extends TObject>(\n name: keyof Static<T> & string,\n options: FormCtrlOptions<T>,\n schema: TObject,\n required: boolean,\n context: {\n parent: string;\n store: Record<string, any>;\n },\n ): BaseInputField {\n const parent = context.parent || \"\";\n const field = schema.properties?.[name];\n if (!field) {\n return {\n path: \"\",\n required,\n initialValue: undefined,\n props: {} as InputHTMLAttributes<unknown>,\n schema: schema,\n set: () => {},\n form: this,\n };\n }\n\n const isRequired = schema.required?.includes(name) ?? false;\n const key = parent ? `${parent}.${name}` : name;\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n\n const set = (value: any) => {\n const typedValue = this.getValueFromInput(value, field);\n context.store[key] = typedValue;\n if (options.onChange) {\n options.onChange(key, typedValue, context.store);\n }\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path: path, value: typedValue },\n { catch: true },\n );\n };\n\n const attr: InputHTMLAttributesLike = {\n name: key,\n };\n\n // Use the form's runtime id (always set — comes from `useId()` when\n // no explicit `options.id` was provided). This guarantees stable\n // per-field DOM ids without forcing callers to pass `id`.\n attr.id = `${this.id}-${key}`;\n (attr as any)[\"data-testid\"] = attr.id;\n\n if (t.schema.isString(field)) {\n if (field.maxLength != null) {\n attr.maxLength = Number(field.maxLength);\n }\n\n if (field.minLength != null) {\n attr.minLength = Number(field.minLength);\n }\n }\n\n if (isRequired) {\n attr.required = true;\n }\n\n if (\"description\" in field && typeof field.description === \"string\") {\n attr[\"aria-label\"] = field.description;\n }\n\n if (t.schema.isInteger(field) || t.schema.isNumber(field)) {\n attr.type = \"number\";\n } else if (name === \"password\") {\n attr.type = \"password\";\n } else if (name === \"email\") {\n attr.type = \"email\";\n } else if (name === \"url\") {\n attr.type = \"url\";\n } else if (t.schema.isString(field)) {\n if (field.format === \"binary\") {\n attr.type = \"file\";\n } else if (field.format === \"date\") {\n attr.type = \"date\";\n } else if (field.format === \"time\") {\n attr.type = \"time\";\n } else if (field.format === \"date-time\") {\n attr.type = \"datetime-local\";\n } else {\n attr.type = \"text\";\n }\n } else if (t.schema.isBoolean(field)) {\n attr.type = \"checkbox\";\n }\n\n if (options.onCreateField) {\n const customAttr = options.onCreateField(name, field);\n Object.assign(attr, customAttr);\n }\n\n // if type = object, add items: { [key: string]: InputField }\n if (t.schema.isObject(field)) {\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n items: this.createProxyFromSchema(options, field, {\n parent: key,\n store: context.store,\n }),\n } as ObjectInputField<any>;\n }\n\n // if type = array, add items: InputField[]\n if (t.schema.isArray(field)) {\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n items: [], // <- will be populated dynamically in the UI\n } as ArrayInputField<any>;\n }\n\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n };\n }\n\n /**\n * Convert an input value to the correct type based on the schema.\n * Handles raw DOM values (strings, booleans from checkboxes, Files, etc.)\n */\n protected getValueFromInput(input: any, schema: TSchema): any {\n // Treat null/undefined as \"unset\" for every schema. Without this the\n // string branch below stringifies them to \"null\"/\"undefined\" (and\n // the date branches throw on `new Date(undefined)`), which then\n // round-trips into controlled inputs as literal text — most\n // visible after the Clear (X) affordance in Control sets\n // value=undefined and the input promptly displays \"undefined\".\n if (input === null || input === undefined) {\n return undefined;\n }\n if (input instanceof File) {\n // for file inputs, return the File object directly\n if (t.schema.isString(schema) && schema.format === \"binary\") {\n return input;\n }\n // for now, ignore other formats\n return null;\n }\n\n if (t.schema.isBoolean(schema)) {\n // Handle string representations from Select components (Yes/No dropdown)\n if (input === \"true\") return true;\n if (input === \"false\") return false;\n if (input === \"\" || input === null || input === undefined)\n return undefined;\n // Handle actual boolean values\n return !!input;\n }\n\n if (t.schema.isNumber(schema)) {\n const num = Number(input);\n return Number.isNaN(num) ? null : num;\n }\n\n if (t.schema.isString(schema)) {\n if (schema.format === \"date\") {\n return new Date(input).toISOString().slice(0, 10); // For date input\n }\n if (schema.format === \"time\") {\n return new Date(`1970-01-01T${input}`).toISOString().slice(11, 16); // For time input\n }\n if (schema.format === \"date-time\") {\n return new Date(input).toISOString(); // For datetime-local input\n }\n return String(input);\n }\n\n return input; // fallback for other types\n }\n}\n\nexport type SchemaToInput<T extends TObject> = {\n [K in keyof T[\"properties\"]]: InputField<T[\"properties\"][K]>;\n};\n\nexport interface FormEventLike {\n preventDefault?: () => void;\n stopPropagation?: () => void;\n}\n\nexport type InputField<T extends TSchema> = T extends TObject\n ? ObjectInputField<T>\n : T extends TArray<infer U>\n ? ArrayInputField<U>\n : BaseInputField;\n\nexport interface BaseInputField {\n path: string;\n required: boolean;\n initialValue: any;\n props: InputHTMLAttributesLike;\n schema: TSchema;\n set: (value: any) => void;\n form: FormModel<any>;\n items?: any;\n}\n\nexport interface ObjectInputField<T extends TObject> extends BaseInputField {\n items: SchemaToInput<T>;\n}\n\nexport interface ArrayInputField<T extends TSchema> extends BaseInputField {\n items: Array<InputField<T>>;\n}\n\nexport type InputHTMLAttributesLike = Pick<\n InputHTMLAttributes<unknown>,\n | \"id\"\n | \"name\"\n | \"type\"\n | \"value\"\n | \"required\"\n | \"maxLength\"\n | \"minLength\"\n | \"aria-label\"\n> & {\n value?: any;\n};\n\nexport type FormCtrlOptions<T extends TObject> = {\n /**\n * The schema defining the structure and validation rules for the form.\n * This should be a TypeBox schema object.\n */\n schema: T;\n\n /**\n * Callback function to handle form submission.\n * This function will receive the parsed and validated form values.\n */\n handler: (values: Static<T>) => unknown;\n\n /**\n * Optional initial values for the form fields.\n * This can be used to pre-populate the form with existing data.\n */\n initialValues?: Partial<Static<T>>;\n\n /**\n * Optional function to create custom field attributes.\n * This can be used to add custom validation, styles, or other attributes.\n */\n onCreateField?: (\n name: keyof Static<T> & string,\n schema: TSchema,\n ) => InputHTMLAttributes<unknown>;\n\n /**\n * If defined, this will generate a unique ID for each field, prefixed with this string.\n *\n * > \"username\" with id=\"form-123\" will become \"form-123-username\".\n *\n * If omitted, IDs will not be generated.\n */\n id?: string;\n\n onError?: (error: Error) => void;\n\n onChange?: (key: string, value: any, store: Record<string, any>) => void;\n\n onReset?: () => void;\n};\n","import type { TObject } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useId, useMemo, useRef } from \"react\";\nimport { type FormCtrlOptions, FormModel } from \"../services/FormModel.ts\";\n\n/**\n * Custom hook to create a form with validation and field management.\n * This hook uses TypeBox schemas to define the structure and validation rules for the form.\n * It provides a way to handle form submission, field creation, and value management.\n *\n * @example\n * ```tsx\n * import { t } from \"alepha\";\n *\n * const form = useForm({\n * schema: t.object({\n * username: t.text(),\n * password: t.text(),\n * }),\n * handler: (values) => {\n * console.log(\"Form submitted with values:\", values);\n * },\n * });\n *\n * return (\n * <form {...form.props}>\n * <input {...form.input.username.props} />\n * <input {...form.input.password.props} />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * ```\n */\n/**\n * Shallow / structural equality for form initial-value objects. Form values\n * are plain data (strings, numbers, arrays, plain objects) so JSON.stringify\n * is both fast enough and exact. Wrapped in try/catch to fall back to\n * reference equality if anything exotic sneaks in (e.g. circular refs).\n */\nconst stableEqual = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n if (a == null || b == null) return false;\n try {\n return JSON.stringify(a) === JSON.stringify(b);\n } catch {\n return false;\n }\n};\n\nexport const useForm = <T extends TObject>(\n options: FormCtrlOptions<T>,\n deps: any[] = [],\n): FormModel<T> => {\n const alepha = useAlepha();\n const formId = useId();\n const initialValuesRef = useRef(options.initialValues);\n\n const form = useMemo(() => {\n return alepha.inject(FormModel<T>, {\n lifetime: \"transient\",\n args: [options.id || formId, options],\n });\n }, deps);\n\n useEffect(() => {\n // Reference inequality alone is unsafe: callers commonly build the\n // `initialValues` object inline at the top of their render, which yields\n // a fresh reference every commit. Reinitializing on every render wipes\n // user-typed values via `setInitialValues` while leaving the DOM stale,\n // producing the \"I filled the form but submit says it's empty\" bug.\n //\n // Deep-equality (JSON-compare) catches the common case where the inline\n // literal is rebuilt with the same content. Callers who legitimately\n // need to swap initial values mid-edit (e.g. editing quest A → quest B)\n // still see a different JSON shape and trigger the reinit.\n if (stableEqual(initialValuesRef.current, options.initialValues)) {\n return;\n }\n initialValuesRef.current = options.initialValues;\n if (options.initialValues) {\n form.setInitialValues(options.initialValues as Record<string, any>);\n }\n }, [options.initialValues]);\n\n return form;\n};\n","import type { TObject } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useRouter, useRouterState } from \"alepha/react/router\";\nimport { useEffect, useRef } from \"react\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\nexport interface UseFormQuerySyncOptions<TKey extends string> {\n /**\n * Form field keys to mirror to/from the URL query. Fields not listed\n * here stay local to the form and never leak into the address bar.\n */\n keys: readonly TKey[];\n}\n\n/**\n * Two-way bind a `useForm` instance to the URL query params, keyed\n * per-field (so the URL stays human-readable: `?status=new&zone=ops`).\n *\n * Direction 1 — URL → form:\n * - On mount AND every time one of the watched query keys changes\n * (typically via browser back/forward or external `router.push`),\n * call `form.input[key].set(value)` for each listed key. Missing /\n * empty params clear the field (set to `undefined`).\n *\n * Direction 2 — form → URL:\n * - Subscribe to the form's `form:change` event for the listed keys.\n * Each emission writes the current values for all listed keys to the\n * URL via `router.setQueryParams` (replace-state, no history spam).\n * Empty values (`undefined` / `\"\"`) are stripped from the URL so the\n * address bar stays clean.\n *\n * Replaces ad-hoc `localStorage` filter persistence: the URL becomes\n * the canonical store, so the view is shareable via copy-paste and\n * navigable via back/forward.\n *\n * @example\n * const form = useForm({ schema: t.object({ status: t.string(), q: t.string() }) });\n * useFormQuerySync(form, { keys: [\"status\", \"q\"] });\n */\nexport const useFormQuerySync = <T extends TObject, TKey extends string>(\n form: FormModel<T>,\n options: UseFormQuerySyncOptions<TKey>,\n): void => {\n const alepha = useAlepha();\n const router = useRouter();\n const state = useRouterState();\n const keys = options.keys;\n\n // URL → form. Recompute a stable signature of the watched slice of\n // router.query so the effect fires precisely when it changes.\n const querySig = keys\n .map((k) => {\n const raw = state.query?.[k];\n return typeof raw === \"string\" ? raw : \"\";\n })\n .join(\"\u0000\");\n\n // First-mount semantics differ from later URL changes: when the URL\n // is empty AND the form has an initial value (e.g. status: \"new\"),\n // preserve that initial value — and write it back to the URL so the\n // URL becomes the source of truth from then on. Without this guard\n // the mount would clear the form's defaults, defeating the whole\n // \"default lane\" pattern.\n const initialized = useRef(false);\n useEffect(() => {\n const isFirstRun = !initialized.current;\n initialized.current = true;\n const values = (form as unknown as { currentValues: Record<string, any> })\n .currentValues;\n const missingInitials: Record<string, string> = {};\n for (const key of keys) {\n const raw = state.query?.[key];\n const next = typeof raw === \"string\" && raw !== \"\" ? raw : undefined;\n const current = values[key];\n if (\n isFirstRun &&\n next === undefined &&\n current != null &&\n current !== \"\"\n ) {\n // URL missing this key on first mount but the form has a\n // default — keep the default, queue it to be written below.\n missingInitials[key] = String(current);\n continue;\n }\n // Skip when already in sync — avoids bouncing form:change ↔\n // setQueryParams and re-triggering subscribers.\n if ((current ?? undefined) !== next) {\n const input = (form.input as Record<string, any>)[key];\n if (input && typeof input.set === \"function\") {\n input.set(next);\n }\n }\n }\n if (isFirstRun && Object.keys(missingInitials).length > 0) {\n // Merge with whatever URL params ARE present so we don't clobber\n // unrelated keys the page also uses.\n const merged: Record<string, string> = {};\n for (const key of keys) {\n const raw = state.query?.[key];\n if (typeof raw === \"string\" && raw !== \"\") {\n merged[key] = raw;\n }\n }\n for (const [k, v] of Object.entries(missingInitials)) {\n merged[k] = v;\n }\n router.setQueryParams(merged);\n }\n // querySig is the meaningful trigger; form/keys/router are stable.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [querySig]);\n\n // form → URL. Listen for changes on the watched keys only.\n useEffect(() => {\n if (!alepha.isBrowser()) return;\n const off = alepha.events.on(\"form:change\", (e: any) => {\n if (e.id !== form.id) return;\n if (!keys.includes(e.path)) return;\n const next: Record<string, string> = {};\n const values = (form as unknown as { currentValues: Record<string, any> })\n .currentValues;\n for (const key of keys) {\n const value = values[key];\n if (value != null && value !== \"\") {\n next[key] = String(value);\n }\n }\n // Replace-state (default) — filters shouldn't pollute history.\n router.setQueryParams(next);\n });\n return off;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [alepha, form, router]);\n};\n","import type { TObject } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\n/**\n * Hook to subscribe to all form values.\n * Re-renders on every field change — use only when needed (debug panels, live previews).\n */\nexport const useFormValues = <T extends TObject>(\n form: FormModel<T>,\n): Record<string, any> => {\n const alepha = useAlepha();\n const [values, setValues] = useState<Record<string, any>>(form.currentValues);\n\n useEffect(() => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n return alepha.events.on(\"form:change\", (ev) => {\n if (ev.id === form.id) {\n setValues(form.currentValues);\n }\n });\n }, []);\n\n return values;\n};\n","/**\n * Converts a path or identifier string into a pretty display name.\n * For paths like \"/contacts/0/name\", extracts just the field name \"Name\".\n * Handles camelCase and snake_case conversion to Title Case.\n *\n * @example\n * prettyName(\"/userName\") // \"User Name\"\n * prettyName(\"/contacts/0/email\") // \"Email\"\n * prettyName(\"/address/streetName\") // \"Street Name\"\n * prettyName(\"first_name\") // \"First Name\"\n */\nexport const prettyName = (name: string): string => {\n const segments = name.split(\"/\").filter((s) => s && !/^\\d+$/.test(s));\n const fieldName = segments[segments.length - 1] || name.replaceAll(\"/\", \"\");\n return fieldName\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n};\n","import { type TSchema, TypeBoxError } from \"alepha\";\nimport type { BaseInputField } from \"./FormModel.ts\";\nimport { prettyName } from \"./prettyName.ts\";\n\n/**\n * Semantic icon hint derived from schema metadata. UI layers map this\n * to their own icon set — this module is headless and ships no JSX.\n */\nexport type IconHint =\n | \"email\"\n | \"password\"\n | \"phone\"\n | \"url\"\n | \"number\"\n | \"calendar\"\n | \"clock\"\n | \"list\"\n | \"text\"\n | \"user\"\n | \"file\"\n | \"switch\";\n\nexport interface FieldConstraints {\n minLength?: number;\n maxLength?: number;\n minimum?: number;\n maximum?: number;\n pattern?: string;\n}\n\nexport interface FieldMeta {\n id?: string;\n label: string;\n description?: string;\n error?: string;\n required: boolean;\n type?: string;\n format?: string;\n isEnum: boolean;\n isArray: boolean;\n isObject: boolean;\n isArrayOfObjects: boolean;\n enum?: readonly unknown[];\n iconHint?: IconHint;\n constraints: FieldConstraints;\n testId?: string;\n schema: TSchema;\n /**\n * Raw `$control` value from the schema, untyped here. The UI layer\n * (`alepha/react/ui`) provides the strict {@link SchemaControl} type and\n * a `resolveSchemaControl` helper to evaluate the function form.\n */\n control?: unknown;\n}\n\nexport interface ParseFieldOptions {\n label?: string;\n description?: string;\n error?: Error;\n}\n\n/**\n * Derives a {@link FieldMeta} from an `InputField` (from `useForm`) plus\n * optional overrides. Pure — no React, no JSX, no UI library coupling.\n *\n * UI components consume this metadata to render labels, descriptions,\n * error messages, icons, and validation constraints.\n */\nexport const parseField = (\n input: BaseInputField,\n options: ParseFieldOptions = {},\n): FieldMeta => {\n const schema = input.schema as TSchema & {\n type?: string;\n format?: string;\n title?: string;\n description?: string;\n enum?: readonly unknown[];\n minLength?: number;\n maxLength?: number;\n minimum?: number;\n maximum?: number;\n pattern?: string;\n properties?: unknown;\n items?: { properties?: unknown };\n $control?: unknown;\n };\n\n const label =\n options.label ??\n (typeof schema.title === \"string\" ? schema.title : undefined) ??\n prettyName(input.path);\n\n const description =\n options.description ??\n (typeof schema.description === \"string\" ? schema.description : undefined);\n\n const error =\n options.error instanceof TypeBoxError\n ? (options.error as TypeBoxError).value?.message\n : undefined;\n\n const type = schema.type;\n const format = typeof schema.format === \"string\" ? schema.format : undefined;\n const isEnum = Array.isArray(schema.enum);\n const isArray = type === \"array\";\n const isObject = type === \"object\" && Boolean(schema.properties);\n const isArrayOfObjects =\n isArray && Boolean(schema.items && (schema.items as any).properties);\n\n const name = input.props.name;\n const iconHint = inferIconHint({ type, format, name, isEnum, isArray });\n\n const constraints: FieldConstraints = {};\n if (typeof schema.minLength === \"number\")\n constraints.minLength = schema.minLength;\n if (typeof schema.maxLength === \"number\")\n constraints.maxLength = schema.maxLength;\n if (typeof schema.minimum === \"number\") constraints.minimum = schema.minimum;\n if (typeof schema.maximum === \"number\") constraints.maximum = schema.maximum;\n if (typeof schema.pattern === \"string\") constraints.pattern = schema.pattern;\n\n return {\n id: input.props.id,\n label,\n description,\n error,\n required: input.required,\n type,\n format,\n isEnum,\n isArray,\n isObject,\n isArrayOfObjects,\n enum: schema.enum,\n iconHint,\n constraints,\n testId: (input.props as Record<string, unknown>)[\"data-testid\"] as\n | string\n | undefined,\n schema: input.schema,\n control: schema.$control,\n };\n};\n\nconst inferIconHint = (params: {\n type?: string;\n format?: string;\n name?: string;\n isEnum: boolean;\n isArray: boolean;\n}): IconHint | undefined => {\n const { type, format, name, isEnum, isArray } = params;\n\n if (format === \"email\") return \"email\";\n if (format === \"url\" || format === \"uri\") return \"url\";\n if (format === \"tel\" || format === \"phone\") return \"phone\";\n if (format === \"date\" || format === \"date-time\") return \"calendar\";\n if (format === \"time\") return \"clock\";\n\n if (name?.toLowerCase().includes(\"password\")) return \"password\";\n if (name?.toLowerCase().includes(\"email\")) return \"email\";\n if (name?.toLowerCase().includes(\"phone\")) return \"phone\";\n\n if (type === \"boolean\") return \"switch\";\n if (type === \"number\" || type === \"integer\") return \"number\";\n if (isEnum || isArray) return \"list\";\n if (type === \"string\") return \"text\";\n\n return undefined;\n};\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport { default as FormState } from \"./components/FormState.tsx\";\nexport * from \"./errors/FormValidationError.ts\";\nexport * from \"./hooks/useFieldValue.ts\";\nexport * from \"./hooks/useForm.ts\";\nexport * from \"./hooks/useFormQuerySync.ts\";\nexport * from \"./hooks/useFormState.ts\";\nexport * from \"./hooks/useFormValues.ts\";\nexport * from \"./services/FormModel.ts\";\nexport * from \"./services/parseField.ts\";\nexport * from \"./services/prettyName.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n \"form:change\": {\n id: string;\n path: string;\n value: any;\n /**\n * Programmatic reset (e.g. `setInitialValues` after the parent updates\n * its state). Subscribers tracking dirty state should ignore these.\n */\n initial?: boolean;\n };\n \"form:submit:begin\": { id: string };\n \"form:submit:success\": { id: string; values: Record<string, any> };\n \"form:submit:error\": { id: string; error: Error };\n \"form:submit:end\": { id: string };\n \"form:reset\": { id: string };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Type-safe forms with validation.\n *\n * **Features:**\n * - Form state management\n * - TypeBox schema validation\n * - Field-level error handling\n * - Submit handling with loading state\n * - Form reset\n *\n * @module alepha.react.form\n */\nexport const AlephaReactForm = $module({\n name: \"alepha.react.form\",\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/react/form/hooks/useFormState.ts","../../../src/react/form/components/FormState.tsx","../../../src/react/form/errors/FormValidationError.ts","../../../src/react/form/hooks/useFieldValue.ts","../../../src/react/form/services/FormModel.ts","../../../src/react/form/hooks/useForm.ts","../../../src/react/form/hooks/useFormQuerySync.ts","../../../src/react/form/hooks/useFormValues.ts","../../../src/react/form/services/prettyName.ts","../../../src/react/form/services/parseField.ts","../../../src/react/form/index.ts"],"sourcesContent":["import { type TObject, TypeBoxError } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\nexport interface UseFormStateReturn {\n loading: boolean;\n dirty: boolean;\n values?: Record<string, any>;\n error?: Error;\n}\n\nexport const useFormState = <\n T extends TObject,\n Keys extends keyof UseFormStateReturn,\n>(\n target: FormModel<T> | { form: FormModel<T>; path: string },\n _events: Keys[] = [\"loading\", \"dirty\", \"error\"] as Keys[],\n): Pick<UseFormStateReturn, Keys> => {\n const alepha = useAlepha();\n const events = _events as string[];\n\n const [dirty, setDirty] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n const [values, setValues] = useState<Record<string, any> | undefined>(\n undefined,\n );\n\n const form = \"form\" in target ? target.form : target;\n const path = \"path\" in target ? target.path : undefined;\n\n const hasValues = events.includes(\"values\");\n const hasErrors = events.includes(\"error\");\n const hasDirty = events.includes(\"dirty\");\n const hasLoading = events.includes(\"loading\");\n\n useEffect(() => {\n const listeners: Function[] = [];\n\n if (hasErrors || hasValues || hasDirty) {\n listeners.push(\n alepha.events.on(\"form:change\", (event) => {\n if (event.id === form.id) {\n if (!path || event.path === path) {\n // `initial: true` marks a programmatic reset (e.g. parent\n // re-rendering with fresh data via `setInitialValues`). Treat\n // it like a reset: clear dirty, don't mark dirty.\n if (hasDirty) {\n if (event.initial) {\n setDirty(false);\n } else {\n setDirty(true);\n }\n }\n if (hasErrors) {\n setError(undefined);\n }\n }\n if (hasValues) {\n setValues(form.currentValues);\n }\n }\n }),\n );\n }\n\n if (hasLoading) {\n listeners.push(\n alepha.events.on(\"form:submit:begin\", (event) => {\n if (event.id === form.id) {\n setLoading(true);\n }\n }),\n alepha.events.on(\"form:submit:end\", (event) => {\n if (event.id === form.id) {\n setLoading(false);\n }\n }),\n );\n }\n\n if (hasValues || hasDirty) {\n listeners.push(\n alepha.events.on(\"form:submit:success\", (event) => {\n if (event.id === form.id) {\n if (hasValues) {\n setValues(event.values);\n }\n if (hasDirty) {\n setDirty(false);\n }\n }\n }),\n );\n }\n\n if (hasDirty) {\n listeners.push(\n alepha.events.on(\"form:reset\", (event) => {\n if (event.id === form.id) {\n setDirty(false);\n }\n }),\n );\n }\n\n if (hasErrors) {\n listeners.push(\n alepha.events.on(\"form:submit:error\", (event) => {\n if (event.id === form.id) {\n if (\n !path ||\n (event.error instanceof TypeBoxError &&\n event.error.value.path === path)\n ) {\n setError(event.error);\n }\n }\n }),\n );\n }\n\n return () => {\n for (const unsub of listeners) {\n unsub();\n }\n };\n }, []);\n\n return {\n dirty,\n loading,\n error,\n values,\n } as Pick<UseFormStateReturn, Keys>;\n};\n","import type { TObject } from \"alepha\";\nimport type { ReactNode } from \"react\";\nimport { useFormState } from \"../hooks/useFormState.ts\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\nconst FormState = <T extends TObject>(props: {\n form: FormModel<T>;\n children: (state: { loading: boolean; dirty: boolean }) => ReactNode;\n}) => {\n const formState = useFormState(props.form);\n return props.children({\n loading: formState.loading,\n dirty: formState.dirty,\n });\n};\n\nexport default FormState;\n","import { TypeBoxError } from \"alepha\";\n\nexport class FormValidationError extends TypeBoxError {\n readonly name = \"ValidationError\";\n\n constructor(options: {\n message: string;\n path: string;\n }) {\n super({\n message: options.message,\n instancePath: options.path,\n schemaPath: \"\",\n keyword: \"not\",\n params: {},\n });\n }\n}\n","import { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { BaseInputField } from \"../services/FormModel.ts\";\n\n/**\n * Hook to subscribe to a single form field's value.\n * Only re-renders when this specific field changes.\n *\n * @returns A tuple of [value, setValue] similar to useState.\n */\nexport const useFieldValue = (\n input: BaseInputField,\n): [any, (value: any) => void] => {\n const alepha = useAlepha();\n const [value, setValue] = useState(input?.initialValue);\n\n useEffect(() => {\n if (!input?.form || !alepha.isBrowser()) {\n return;\n }\n\n return alepha.events.on(\"form:change\", (ev) => {\n if (ev.id === input.form.id && ev.path === input.path) {\n setValue(ev.value);\n }\n });\n }, []);\n\n const setFieldValue = (newValue: any) => {\n input.set(newValue);\n };\n\n return [value, setFieldValue];\n};\n","import type { TArray } from \"alepha\";\nimport {\n $inject,\n Alepha,\n type Static,\n type TObject,\n type TSchema,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { InputHTMLAttributes } from \"react\";\n\n/**\n * FormModel is a dynamic form handler that generates form inputs based on a provided TypeBox schema.\n * It manages form state, handles input changes, and processes form submissions with validation.\n *\n * It means to be injected and used within React components to provide a structured way to create and manage forms.\n *\n * @see {@link useForm}\n */\nexport class FormModel<T extends TObject> {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly values: Record<string, any> = {};\n protected readonly initialValues: Record<string, any> = {};\n protected submitInProgress = false;\n\n public input: SchemaToInput<T>;\n\n constructor(\n public readonly id: string,\n public readonly options: FormCtrlOptions<T>,\n ) {\n this.options = options;\n\n // Initialize with schema defaults first, then override with initialValues\n const schemaDefaults = this.extractSchemaDefaults(options.schema);\n if (Object.keys(schemaDefaults).length > 0) {\n Object.assign(this.values, schemaDefaults);\n }\n\n if (options.initialValues) {\n // Decode against a partial of the schema so callers can supply incomplete\n // initial values (the form's whole job is to collect them). Codecs still\n // run on whatever's provided; missing fields stay undefined and only the\n // full schema is enforced at submit time.\n const decoded = this.alepha.codec.decode(\n t.partial(options.schema),\n options.initialValues,\n ) as Record<string, any>;\n Object.assign(this.values, decoded);\n }\n\n this.initialValues = { ...this.values };\n\n this.input = this.createProxyFromSchema(options, options.schema, {\n store: this.values,\n parent: \"\",\n });\n }\n\n /**\n * Extract default values from a TypeBox schema.\n * Recursively handles nested objects.\n */\n protected extractSchemaDefaults(\n schema: TObject,\n prefix: string = \"\",\n ): Record<string, any> {\n const defaults: Record<string, any> = {};\n\n if (!schema.properties) {\n return defaults;\n }\n\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n\n if (\"default\" in propSchema && propSchema.default !== undefined) {\n defaults[fullKey] = propSchema.default;\n } else if (\n propSchema &&\n \"type\" in propSchema &&\n propSchema.type === \"object\" &&\n \"properties\" in propSchema\n ) {\n // Recursively extract defaults from nested objects\n const nestedDefaults = this.extractSchemaDefaults(\n propSchema as TObject,\n fullKey,\n );\n Object.assign(defaults, nestedDefaults);\n }\n }\n\n return defaults;\n }\n\n public get currentValues(): Record<string, any> {\n return this.restructureValues(this.values);\n }\n\n public get props() {\n return {\n id: this.id,\n noValidate: true,\n onSubmit: (ev?: FormEventLike) => {\n ev?.preventDefault?.();\n this.submit();\n },\n onReset: (event: FormEventLike) => this.reset(event),\n };\n }\n\n public readonly setInitialValues = (values: Record<string, any>) => {\n // Same partial-decode rationale as the constructor — initial values may be\n // incomplete; full schema is enforced only at submit time.\n const decoded = this.alepha.codec.decode(\n t.partial(this.options.schema),\n values,\n ) as Record<string, any>;\n\n // Snapshot the OLD keys before we wipe — without this, fields that\n // had a value but are absent from the new initialValues never emit\n // form:change and useFieldValue subscribers keep showing the stale\n // value. Mirrors the union-of-keys pattern in reset() below.\n const oldKeys = new Set(Object.keys(this.values));\n\n for (const key in this.initialValues) {\n delete (this.initialValues as Record<string, any>)[key];\n }\n Object.assign(this.initialValues, decoded);\n\n for (const key in this.values) {\n delete this.values[key];\n }\n Object.assign(this.values, { ...this.initialValues });\n\n const keys = new Set<string>([...oldKeys, ...Object.keys(this.values)]);\n for (const key of keys) {\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path, value: this.values[key], initial: true },\n { catch: true },\n );\n }\n };\n\n public readonly reset = (event?: FormEventLike) => {\n event?.preventDefault?.();\n // Snapshot all keys that need notification — both keys present\n // before reset (so subscribers learn the cleared value) and keys\n // restored from initialValues. Without the union, fields that were\n // typed but absent from initialValues stay visually stale.\n const keys = new Set<string>([\n ...Object.keys(this.values),\n ...Object.keys(this.initialValues),\n ]);\n for (const key in this.values) {\n delete this.values[key];\n }\n Object.assign(this.values, { ...this.initialValues });\n for (const key of keys) {\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path, value: this.values[key] },\n { catch: true },\n );\n }\n this.alepha.events.emit(\"form:reset\", { id: this.id }, { catch: true });\n this.options.onReset?.();\n };\n\n public readonly submit = async () => {\n if (this.submitInProgress) {\n this.log.warn(\n \"Form submission already in progress, ignoring duplicate submit.\",\n );\n return;\n }\n\n // Lifecycle events are best-effort NOTIFICATIONS (loading spinners, toasts,\n // analytics). A misbehaving listener must never break form state or reject\n // submit() — hence `{ catch: true }` on every emit below. Without it, a\n // throwing `react:action:error`/`form:submit:error` listener would skip the\n // `form:submit:end` \"loading off\" signal and leave submit buttons stuck in\n // their loading state forever.\n await this.alepha.events.emit(\n \"react:action:begin\",\n { type: \"form\", id: this.id },\n { catch: true },\n );\n await this.alepha.events.emit(\n \"form:submit:begin\",\n { id: this.id },\n { catch: true },\n );\n\n this.submitInProgress = true;\n\n const options = this.options;\n\n try {\n let values: Record<string, any> = this.restructureValues(this.values);\n\n if (t.schema.isSchema(options.schema)) {\n values = this.alepha.codec.decode(options.schema, values) as Record<\n string,\n any\n >;\n }\n\n await options.handler(values as any);\n\n await this.alepha.events.emit(\n \"react:action:success\",\n { type: \"form\", id: this.id },\n { catch: true },\n );\n await this.alepha.events.emit(\n \"form:submit:success\",\n { id: this.id, values },\n { catch: true },\n );\n } catch (error) {\n this.log.error(\"Form submission error:\", error);\n\n // A throwing onError callback must not abort the lifecycle either.\n try {\n options.onError?.(error as Error);\n } catch (handlerError) {\n this.log.error(\"Form onError handler threw:\", handlerError);\n }\n\n await this.alepha.events.emit(\n \"react:action:error\",\n { type: \"form\", id: this.id, error: error as Error },\n { catch: true },\n );\n await this.alepha.events.emit(\n \"form:submit:error\",\n { error: error as Error, id: this.id },\n { catch: true },\n );\n } finally {\n this.submitInProgress = false;\n\n // The \"loading off\" signals live in `finally` so they ALWAYS fire,\n // even if something above threw — guaranteeing the begin/end pairing\n // that drives submit-button loading state.\n await this.alepha.events.emit(\n \"react:action:end\",\n { type: \"form\", id: this.id },\n { catch: true },\n );\n await this.alepha.events.emit(\n \"form:submit:end\",\n { id: this.id },\n { catch: true },\n );\n }\n };\n\n /**\n * Restructures flat keys like \"address.city\" into nested objects like { address: { city: ... } }\n * Values are already typed from onChange, so no conversion is needed.\n */\n protected restructureValues(store: Record<string, any>): Record<string, any> {\n const values: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(store)) {\n if (key.includes(\".\")) {\n // nested object: restructure flat key to nested structure\n this.restructureNestedValue(values, key, value);\n } else {\n // value is already typed, just copy it\n values[key] = value;\n }\n }\n\n return values;\n }\n\n /**\n * Helper to restructure a flat key like \"address.city\" into nested object structure.\n * The value is already typed, so we just assign it to the nested path.\n */\n protected restructureNestedValue(\n values: Record<string, any>,\n key: string,\n value: any,\n ) {\n const pathSegments = key.split(\".\");\n const finalPropertyKey = pathSegments.pop();\n if (!finalPropertyKey) {\n return;\n }\n\n let currentObjectLevel = values;\n\n // traverse/create the nested structure\n for (const segment of pathSegments) {\n currentObjectLevel[segment] ??= {};\n currentObjectLevel = currentObjectLevel[segment];\n }\n\n // value is already typed from onChange, just assign it\n currentObjectLevel[finalPropertyKey] = value;\n }\n\n protected createProxyFromSchema<T extends TObject>(\n options: FormCtrlOptions<T>,\n schema: TSchema,\n context: {\n parent: string;\n store: Record<string, any>;\n },\n ): SchemaToInput<T> {\n const parent = context.parent || \"\";\n return new Proxy<SchemaToInput<T>>({} as SchemaToInput<T>, {\n get: (_, prop: string) => {\n if (!options.schema || !t.schema.isObject(schema)) {\n return {};\n }\n\n if (prop in schema.properties) {\n // // it's a nested object, create another proxy\n // if (t.schema.isObject(schema.properties[prop])) {\n // return this.createProxyFromSchema(\n // options,\n // schema.properties[prop],\n // {\n // parent: parent ? `${parent}.${prop}` : prop,\n // store: context.store,\n // },\n // );\n // }\n\n return this.createInputFromSchema<T>(\n prop as keyof Static<T> & string,\n options,\n schema,\n schema.required?.includes(prop as string) || false,\n context,\n );\n }\n },\n });\n }\n\n protected createInputFromSchema<T extends TObject>(\n name: keyof Static<T> & string,\n options: FormCtrlOptions<T>,\n schema: TObject,\n required: boolean,\n context: {\n parent: string;\n store: Record<string, any>;\n },\n ): BaseInputField {\n const parent = context.parent || \"\";\n const field = schema.properties?.[name];\n if (!field) {\n return {\n path: \"\",\n required,\n initialValue: undefined,\n props: {} as InputHTMLAttributes<unknown>,\n schema: schema,\n set: () => {},\n form: this,\n };\n }\n\n const isRequired = schema.required?.includes(name) ?? false;\n const key = parent ? `${parent}.${name}` : name;\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n\n const set = (value: any) => {\n const typedValue = this.getValueFromInput(value, field);\n context.store[key] = typedValue;\n if (options.onChange) {\n options.onChange(key, typedValue, context.store);\n }\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path: path, value: typedValue },\n { catch: true },\n );\n };\n\n const attr: InputHTMLAttributesLike = {\n name: key,\n };\n\n // Use the form's runtime id (always set — comes from `useId()` when\n // no explicit `options.id` was provided). This guarantees stable\n // per-field DOM ids without forcing callers to pass `id`.\n attr.id = `${this.id}-${key}`;\n (attr as any)[\"data-testid\"] = attr.id;\n\n if (t.schema.isString(field)) {\n if (field.maxLength != null) {\n attr.maxLength = Number(field.maxLength);\n }\n\n if (field.minLength != null) {\n attr.minLength = Number(field.minLength);\n }\n }\n\n if (isRequired) {\n attr.required = true;\n }\n\n if (\"description\" in field && typeof field.description === \"string\") {\n attr[\"aria-label\"] = field.description;\n }\n\n if (t.schema.isInteger(field) || t.schema.isNumber(field)) {\n attr.type = \"number\";\n } else if (name === \"password\") {\n attr.type = \"password\";\n } else if (name === \"email\") {\n attr.type = \"email\";\n } else if (name === \"url\") {\n attr.type = \"url\";\n } else if (t.schema.isString(field)) {\n if (field.format === \"binary\") {\n attr.type = \"file\";\n } else if (field.format === \"date\") {\n attr.type = \"date\";\n } else if (field.format === \"time\") {\n attr.type = \"time\";\n } else if (field.format === \"date-time\") {\n attr.type = \"datetime-local\";\n } else {\n attr.type = \"text\";\n }\n } else if (t.schema.isBoolean(field)) {\n attr.type = \"checkbox\";\n }\n\n if (options.onCreateField) {\n const customAttr = options.onCreateField(name, field);\n Object.assign(attr, customAttr);\n }\n\n // if type = object, add items: { [key: string]: InputField }\n if (t.schema.isObject(field)) {\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n items: this.createProxyFromSchema(options, field, {\n parent: key,\n store: context.store,\n }),\n } as ObjectInputField<any>;\n }\n\n // if type = array, add items: InputField[]\n if (t.schema.isArray(field)) {\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n items: [], // <- will be populated dynamically in the UI\n } as ArrayInputField<any>;\n }\n\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n };\n }\n\n /**\n * Convert an input value to the correct type based on the schema.\n * Handles raw DOM values (strings, booleans from checkboxes, Files, etc.)\n */\n protected getValueFromInput(input: any, schema: TSchema): any {\n // Treat null/undefined as \"unset\" for every schema. Without this the\n // string branch below stringifies them to \"null\"/\"undefined\" (and\n // the date branches throw on `new Date(undefined)`), which then\n // round-trips into controlled inputs as literal text — most\n // visible after the Clear (X) affordance in Control sets\n // value=undefined and the input promptly displays \"undefined\".\n if (input === null || input === undefined) {\n return undefined;\n }\n if (input instanceof File) {\n // for file inputs, return the File object directly\n if (t.schema.isString(schema) && schema.format === \"binary\") {\n return input;\n }\n // for now, ignore other formats\n return null;\n }\n\n if (t.schema.isBoolean(schema)) {\n // Handle string representations from Select components (Yes/No dropdown)\n if (input === \"true\") return true;\n if (input === \"false\") return false;\n if (input === \"\" || input === null || input === undefined)\n return undefined;\n // Handle actual boolean values\n return !!input;\n }\n\n if (t.schema.isNumber(schema)) {\n const num = Number(input);\n return Number.isNaN(num) ? null : num;\n }\n\n if (t.schema.isString(schema)) {\n if (schema.format === \"date\") {\n return new Date(input).toISOString().slice(0, 10); // For date input\n }\n if (schema.format === \"time\") {\n return new Date(`1970-01-01T${input}`).toISOString().slice(11, 16); // For time input\n }\n if (schema.format === \"date-time\") {\n return new Date(input).toISOString(); // For datetime-local input\n }\n return String(input);\n }\n\n return input; // fallback for other types\n }\n}\n\nexport type SchemaToInput<T extends TObject> = {\n [K in keyof T[\"properties\"]]: InputField<T[\"properties\"][K]>;\n};\n\nexport interface FormEventLike {\n preventDefault?: () => void;\n stopPropagation?: () => void;\n}\n\nexport type InputField<T extends TSchema> = T extends TObject\n ? ObjectInputField<T>\n : T extends TArray<infer U>\n ? ArrayInputField<U>\n : BaseInputField;\n\nexport interface BaseInputField {\n path: string;\n required: boolean;\n initialValue: any;\n props: InputHTMLAttributesLike;\n schema: TSchema;\n set: (value: any) => void;\n form: FormModel<any>;\n items?: any;\n}\n\nexport interface ObjectInputField<T extends TObject> extends BaseInputField {\n items: SchemaToInput<T>;\n}\n\nexport interface ArrayInputField<T extends TSchema> extends BaseInputField {\n items: Array<InputField<T>>;\n}\n\nexport type InputHTMLAttributesLike = Pick<\n InputHTMLAttributes<unknown>,\n | \"id\"\n | \"name\"\n | \"type\"\n | \"value\"\n | \"required\"\n | \"maxLength\"\n | \"minLength\"\n | \"aria-label\"\n> & {\n value?: any;\n};\n\nexport type FormCtrlOptions<T extends TObject> = {\n /**\n * The schema defining the structure and validation rules for the form.\n * This should be a TypeBox schema object.\n */\n schema: T;\n\n /**\n * Callback function to handle form submission.\n * This function will receive the parsed and validated form values.\n */\n handler: (values: Static<T>) => unknown;\n\n /**\n * Optional initial values for the form fields.\n * This can be used to pre-populate the form with existing data.\n */\n initialValues?: Partial<Static<T>>;\n\n /**\n * Optional function to create custom field attributes.\n * This can be used to add custom validation, styles, or other attributes.\n */\n onCreateField?: (\n name: keyof Static<T> & string,\n schema: TSchema,\n ) => InputHTMLAttributes<unknown>;\n\n /**\n * If defined, this will generate a unique ID for each field, prefixed with this string.\n *\n * > \"username\" with id=\"form-123\" will become \"form-123-username\".\n *\n * If omitted, IDs will not be generated.\n */\n id?: string;\n\n onError?: (error: Error) => void;\n\n onChange?: (key: string, value: any, store: Record<string, any>) => void;\n\n onReset?: () => void;\n};\n","import type { TObject } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useId, useMemo, useRef } from \"react\";\nimport { type FormCtrlOptions, FormModel } from \"../services/FormModel.ts\";\n\n/**\n * Custom hook to create a form with validation and field management.\n * This hook uses TypeBox schemas to define the structure and validation rules for the form.\n * It provides a way to handle form submission, field creation, and value management.\n *\n * @example\n * ```tsx\n * import { t } from \"alepha\";\n *\n * const form = useForm({\n * schema: t.object({\n * username: t.text(),\n * password: t.text(),\n * }),\n * handler: (values) => {\n * console.log(\"Form submitted with values:\", values);\n * },\n * });\n *\n * return (\n * <form {...form.props}>\n * <input {...form.input.username.props} />\n * <input {...form.input.password.props} />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * ```\n */\n/**\n * Shallow / structural equality for form initial-value objects. Form values\n * are plain data (strings, numbers, arrays, plain objects) so JSON.stringify\n * is both fast enough and exact. Wrapped in try/catch to fall back to\n * reference equality if anything exotic sneaks in (e.g. circular refs).\n */\nconst stableEqual = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n if (a == null || b == null) return false;\n try {\n return JSON.stringify(a) === JSON.stringify(b);\n } catch {\n return false;\n }\n};\n\nexport const useForm = <T extends TObject>(\n options: FormCtrlOptions<T>,\n deps: any[] = [],\n): FormModel<T> => {\n const alepha = useAlepha();\n const formId = useId();\n const initialValuesRef = useRef(options.initialValues);\n\n const form = useMemo(() => {\n return alepha.inject(FormModel<T>, {\n lifetime: \"transient\",\n args: [options.id || formId, options],\n });\n }, deps);\n\n useEffect(() => {\n // Reference inequality alone is unsafe: callers commonly build the\n // `initialValues` object inline at the top of their render, which yields\n // a fresh reference every commit. Reinitializing on every render wipes\n // user-typed values via `setInitialValues` while leaving the DOM stale,\n // producing the \"I filled the form but submit says it's empty\" bug.\n //\n // Deep-equality (JSON-compare) catches the common case where the inline\n // literal is rebuilt with the same content. Callers who legitimately\n // need to swap initial values mid-edit (e.g. editing quest A → quest B)\n // still see a different JSON shape and trigger the reinit.\n if (stableEqual(initialValuesRef.current, options.initialValues)) {\n return;\n }\n initialValuesRef.current = options.initialValues;\n if (options.initialValues) {\n form.setInitialValues(options.initialValues as Record<string, any>);\n }\n }, [options.initialValues]);\n\n return form;\n};\n","import type { TObject } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useRouter, useRouterState } from \"alepha/react/router\";\nimport { useEffect, useRef } from \"react\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\nexport interface UseFormQuerySyncOptions<TKey extends string> {\n /**\n * Form field keys to mirror to/from the URL query. Fields not listed\n * here stay local to the form and never leak into the address bar.\n */\n keys: readonly TKey[];\n}\n\n/**\n * Two-way bind a `useForm` instance to the URL query params, keyed\n * per-field (so the URL stays human-readable: `?status=new&zone=ops`).\n *\n * Direction 1 — URL → form:\n * - On mount AND every time one of the watched query keys changes\n * (typically via browser back/forward or external `router.push`),\n * call `form.input[key].set(value)` for each listed key. Missing /\n * empty params clear the field (set to `undefined`).\n *\n * Direction 2 — form → URL:\n * - Subscribe to the form's `form:change` event for the listed keys.\n * Each emission writes the current values for all listed keys to the\n * URL via `router.setQueryParams` (replace-state, no history spam).\n * Empty values (`undefined` / `\"\"`) are stripped from the URL so the\n * address bar stays clean.\n *\n * Replaces ad-hoc `localStorage` filter persistence: the URL becomes\n * the canonical store, so the view is shareable via copy-paste and\n * navigable via back/forward.\n *\n * @example\n * const form = useForm({ schema: t.object({ status: t.string(), q: t.string() }) });\n * useFormQuerySync(form, { keys: [\"status\", \"q\"] });\n */\nexport const useFormQuerySync = <T extends TObject, TKey extends string>(\n form: FormModel<T>,\n options: UseFormQuerySyncOptions<TKey>,\n): void => {\n const alepha = useAlepha();\n const router = useRouter();\n const state = useRouterState();\n const keys = options.keys;\n\n // URL → form. Recompute a stable signature of the watched slice of\n // router.query so the effect fires precisely when it changes.\n const querySig = keys\n .map((k) => {\n const raw = state.query?.[k];\n return typeof raw === \"string\" ? raw : \"\";\n })\n .join(\"\u0000\");\n\n // First-mount semantics differ from later URL changes: when the URL\n // is empty AND the form has an initial value (e.g. status: \"new\"),\n // preserve that initial value — and write it back to the URL so the\n // URL becomes the source of truth from then on. Without this guard\n // the mount would clear the form's defaults, defeating the whole\n // \"default lane\" pattern.\n const initialized = useRef(false);\n useEffect(() => {\n const isFirstRun = !initialized.current;\n initialized.current = true;\n const values = (form as unknown as { currentValues: Record<string, any> })\n .currentValues;\n const missingInitials: Record<string, string> = {};\n for (const key of keys) {\n const raw = state.query?.[key];\n const next = typeof raw === \"string\" && raw !== \"\" ? raw : undefined;\n const current = values[key];\n if (\n isFirstRun &&\n next === undefined &&\n current != null &&\n current !== \"\"\n ) {\n // URL missing this key on first mount but the form has a\n // default — keep the default, queue it to be written below.\n missingInitials[key] = String(current);\n continue;\n }\n // Skip when already in sync — avoids bouncing form:change ↔\n // setQueryParams and re-triggering subscribers.\n if ((current ?? undefined) !== next) {\n const input = (form.input as Record<string, any>)[key];\n if (input && typeof input.set === \"function\") {\n input.set(next);\n }\n }\n }\n if (isFirstRun && Object.keys(missingInitials).length > 0) {\n // Merge with whatever URL params ARE present so we don't clobber\n // unrelated keys the page also uses.\n const merged: Record<string, string> = {};\n for (const key of keys) {\n const raw = state.query?.[key];\n if (typeof raw === \"string\" && raw !== \"\") {\n merged[key] = raw;\n }\n }\n for (const [k, v] of Object.entries(missingInitials)) {\n merged[k] = v;\n }\n router.setQueryParams(merged);\n }\n // querySig is the meaningful trigger; form/keys/router are stable.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [querySig]);\n\n // form → URL. Listen for changes on the watched keys only.\n useEffect(() => {\n if (!alepha.isBrowser()) return;\n const off = alepha.events.on(\"form:change\", (e: any) => {\n if (e.id !== form.id) return;\n if (!keys.includes(e.path)) return;\n const next: Record<string, string> = {};\n const values = (form as unknown as { currentValues: Record<string, any> })\n .currentValues;\n for (const key of keys) {\n const value = values[key];\n if (value != null && value !== \"\") {\n next[key] = String(value);\n }\n }\n // Replace-state (default) — filters shouldn't pollute history.\n router.setQueryParams(next);\n });\n return off;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [alepha, form, router]);\n};\n","import type { TObject } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\n/**\n * Hook to subscribe to all form values.\n * Re-renders on every field change — use only when needed (debug panels, live previews).\n */\nexport const useFormValues = <T extends TObject>(\n form: FormModel<T>,\n): Record<string, any> => {\n const alepha = useAlepha();\n const [values, setValues] = useState<Record<string, any>>(form.currentValues);\n\n useEffect(() => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n return alepha.events.on(\"form:change\", (ev) => {\n if (ev.id === form.id) {\n setValues(form.currentValues);\n }\n });\n }, []);\n\n return values;\n};\n","/**\n * Converts a path or identifier string into a pretty display name.\n * For paths like \"/contacts/0/name\", extracts just the field name \"Name\".\n * Handles camelCase and snake_case conversion to Title Case.\n *\n * @example\n * prettyName(\"/userName\") // \"User Name\"\n * prettyName(\"/contacts/0/email\") // \"Email\"\n * prettyName(\"/address/streetName\") // \"Street Name\"\n * prettyName(\"first_name\") // \"First Name\"\n */\nexport const prettyName = (name: string): string => {\n const segments = name.split(\"/\").filter((s) => s && !/^\\d+$/.test(s));\n const fieldName = segments[segments.length - 1] || name.replaceAll(\"/\", \"\");\n return fieldName\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n};\n","import { type TSchema, TypeBoxError } from \"alepha\";\nimport type { BaseInputField } from \"./FormModel.ts\";\nimport { prettyName } from \"./prettyName.ts\";\n\n/**\n * Semantic icon hint derived from schema metadata. UI layers map this\n * to their own icon set — this module is headless and ships no JSX.\n */\nexport type IconHint =\n | \"email\"\n | \"password\"\n | \"phone\"\n | \"url\"\n | \"number\"\n | \"calendar\"\n | \"clock\"\n | \"list\"\n | \"text\"\n | \"user\"\n | \"file\"\n | \"switch\";\n\nexport interface FieldConstraints {\n minLength?: number;\n maxLength?: number;\n minimum?: number;\n maximum?: number;\n pattern?: string;\n}\n\nexport interface FieldMeta {\n id?: string;\n label: string;\n description?: string;\n error?: string;\n required: boolean;\n type?: string;\n format?: string;\n isEnum: boolean;\n isArray: boolean;\n isObject: boolean;\n isArrayOfObjects: boolean;\n enum?: readonly unknown[];\n iconHint?: IconHint;\n constraints: FieldConstraints;\n testId?: string;\n schema: TSchema;\n /**\n * Raw `$control` value from the schema, untyped here. The UI layer\n * (`alepha/react/ui`) provides the strict {@link SchemaControl} type and\n * a `resolveSchemaControl` helper to evaluate the function form.\n */\n control?: unknown;\n}\n\nexport interface ParseFieldOptions {\n label?: string;\n description?: string;\n error?: Error;\n}\n\n/**\n * Derives a {@link FieldMeta} from an `InputField` (from `useForm`) plus\n * optional overrides. Pure — no React, no JSX, no UI library coupling.\n *\n * UI components consume this metadata to render labels, descriptions,\n * error messages, icons, and validation constraints.\n */\nexport const parseField = (\n input: BaseInputField,\n options: ParseFieldOptions = {},\n): FieldMeta => {\n const schema = input.schema as TSchema & {\n type?: string;\n format?: string;\n title?: string;\n description?: string;\n enum?: readonly unknown[];\n minLength?: number;\n maxLength?: number;\n minimum?: number;\n maximum?: number;\n pattern?: string;\n properties?: unknown;\n items?: { properties?: unknown };\n $control?: unknown;\n };\n\n const label =\n options.label ??\n (typeof schema.title === \"string\" ? schema.title : undefined) ??\n prettyName(input.path);\n\n const description =\n options.description ??\n (typeof schema.description === \"string\" ? schema.description : undefined);\n\n const error =\n options.error instanceof TypeBoxError\n ? (options.error as TypeBoxError).value?.message\n : undefined;\n\n const type = schema.type;\n const format = typeof schema.format === \"string\" ? schema.format : undefined;\n const isEnum = Array.isArray(schema.enum);\n const isArray = type === \"array\";\n const isObject = type === \"object\" && Boolean(schema.properties);\n const isArrayOfObjects =\n isArray && Boolean(schema.items && (schema.items as any).properties);\n\n const name = input.props.name;\n const iconHint = inferIconHint({ type, format, name, isEnum, isArray });\n\n const constraints: FieldConstraints = {};\n if (typeof schema.minLength === \"number\")\n constraints.minLength = schema.minLength;\n if (typeof schema.maxLength === \"number\")\n constraints.maxLength = schema.maxLength;\n if (typeof schema.minimum === \"number\") constraints.minimum = schema.minimum;\n if (typeof schema.maximum === \"number\") constraints.maximum = schema.maximum;\n if (typeof schema.pattern === \"string\") constraints.pattern = schema.pattern;\n\n return {\n id: input.props.id,\n label,\n description,\n error,\n required: input.required,\n type,\n format,\n isEnum,\n isArray,\n isObject,\n isArrayOfObjects,\n enum: schema.enum,\n iconHint,\n constraints,\n testId: (input.props as Record<string, unknown>)[\"data-testid\"] as\n | string\n | undefined,\n schema: input.schema,\n control: schema.$control,\n };\n};\n\nconst inferIconHint = (params: {\n type?: string;\n format?: string;\n name?: string;\n isEnum: boolean;\n isArray: boolean;\n}): IconHint | undefined => {\n const { type, format, name, isEnum, isArray } = params;\n\n if (format === \"email\") return \"email\";\n if (format === \"url\" || format === \"uri\") return \"url\";\n if (format === \"tel\" || format === \"phone\") return \"phone\";\n if (format === \"date\" || format === \"date-time\") return \"calendar\";\n if (format === \"time\") return \"clock\";\n\n if (name?.toLowerCase().includes(\"password\")) return \"password\";\n if (name?.toLowerCase().includes(\"email\")) return \"email\";\n if (name?.toLowerCase().includes(\"phone\")) return \"phone\";\n\n if (type === \"boolean\") return \"switch\";\n if (type === \"number\" || type === \"integer\") return \"number\";\n if (isEnum || isArray) return \"list\";\n if (type === \"string\") return \"text\";\n\n return undefined;\n};\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport { default as FormState } from \"./components/FormState.tsx\";\nexport * from \"./errors/FormValidationError.ts\";\nexport * from \"./hooks/useFieldValue.ts\";\nexport * from \"./hooks/useForm.ts\";\nexport * from \"./hooks/useFormQuerySync.ts\";\nexport * from \"./hooks/useFormState.ts\";\nexport * from \"./hooks/useFormValues.ts\";\nexport * from \"./services/FormModel.ts\";\nexport * from \"./services/parseField.ts\";\nexport * from \"./services/prettyName.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n \"form:change\": {\n id: string;\n path: string;\n value: any;\n /**\n * Programmatic reset (e.g. `setInitialValues` after the parent updates\n * its state). Subscribers tracking dirty state should ignore these.\n */\n initial?: boolean;\n };\n \"form:submit:begin\": { id: string };\n \"form:submit:success\": { id: string; values: Record<string, any> };\n \"form:submit:error\": { id: string; error: Error };\n \"form:submit:end\": { id: string };\n \"form:reset\": { id: string };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Type-safe forms with validation.\n *\n * **Features:**\n * - Form state management\n * - TypeBox schema validation\n * - Field-level error handling\n * - Submit handling with loading state\n * - Form reset\n *\n * @module alepha.react.form\n */\nexport const AlephaReactForm = $module({\n name: \"alepha.react.form\",\n});\n"],"mappings":"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"}
|