alepha 0.15.3 → 0.15.5
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 +26 -11
- package/dist/api/audits/index.d.ts +335 -335
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +11 -3
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +3 -3
- package/dist/api/files/index.js +4 -3
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +198 -155
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +103 -5
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +198 -198
- package/dist/api/keys/index.d.ts.map +1 -1
- package/dist/api/keys/index.js +3 -3
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.browser.js +1 -0
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +3 -3
- package/dist/api/notifications/index.js +4 -3
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +263 -263
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +41 -30
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.d.ts +383 -77
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +284 -72
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +131 -131
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +3 -3
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.d.ts +3 -3
- package/dist/batch/index.js +3 -3
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +3 -3
- package/dist/bucket/index.js +6 -6
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/core/index.d.ts +3 -3
- package/dist/cache/core/index.js +3 -3
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cli/index.d.ts +5612 -20
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +122 -91
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +11 -4
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +8 -6
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +4 -8
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts +3 -3
- package/dist/datetime/index.js +3 -3
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +16 -16
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +10562 -10
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +3 -3
- package/dist/fake/index.js +3 -3
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +9 -4
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +12 -4
- package/dist/lock/core/index.js.map +1 -1
- package/dist/logger/index.d.ts +3 -3
- package/dist/logger/index.js +6 -3
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +3 -3
- package/dist/mcp/index.js +3 -3
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.d.ts +12 -12
- package/dist/orm/index.js +4 -4
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +3 -3
- package/dist/queue/core/index.js +3 -3
- package/dist/queue/core/index.js.map +1 -1
- package/dist/react/auth/index.browser.js +2 -1
- package/dist/react/auth/index.browser.js.map +1 -1
- package/dist/react/auth/index.d.ts +3 -3
- package/dist/react/auth/index.js +5 -4
- package/dist/react/auth/index.js.map +1 -1
- package/dist/react/core/index.d.ts +6 -6
- package/dist/react/core/index.js +3 -3
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +3 -3
- package/dist/react/form/index.js +3 -3
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/head/index.d.ts +3 -3
- package/dist/react/head/index.js +3 -3
- package/dist/react/head/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +3 -3
- package/dist/react/i18n/index.js +3 -3
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/intro/index.css +337 -0
- package/dist/react/intro/index.css.map +1 -0
- package/dist/react/intro/index.d.ts +10 -0
- package/dist/react/intro/index.d.ts.map +1 -0
- package/dist/react/intro/index.js +222 -0
- package/dist/react/intro/index.js.map +1 -0
- package/dist/react/router/index.browser.js +2 -2
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +11 -1
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +21 -11
- package/dist/react/router/index.js.map +1 -1
- package/dist/redis/index.d.ts +22 -22
- package/dist/redis/index.js +3 -3
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +3 -3
- package/dist/retry/index.js +3 -3
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +16 -4
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +45 -7
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +3 -3
- package/dist/security/index.js +5 -5
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +3 -3
- package/dist/server/auth/index.js +3 -3
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +3 -3
- package/dist/server/cache/index.js +3 -3
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts +3 -3
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/compress/index.js +4 -3
- package/dist/server/compress/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +3 -3
- package/dist/server/cookies/index.js +3 -3
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +14 -25
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +13 -29
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +3 -3
- package/dist/server/cors/index.js +3 -3
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/health/index.d.ts +20 -20
- package/dist/server/health/index.js +3 -3
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/helmet/index.d.ts +3 -3
- package/dist/server/helmet/index.js +3 -3
- package/dist/server/helmet/index.js.map +1 -1
- package/dist/server/links/index.d.ts +42 -42
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +4 -4
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +3 -3
- package/dist/server/metrics/index.js +3 -3
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts +3 -3
- package/dist/server/multipart/index.js +3 -3
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts +3 -3
- package/dist/server/proxy/index.js +3 -3
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts +3 -3
- package/dist/server/rate-limit/index.js +3 -3
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +3 -3
- package/dist/server/static/index.js +6 -6
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +3 -3
- package/dist/server/swagger/index.js +6 -6
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +3 -3
- package/dist/sms/index.js +6 -6
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.d.ts +3 -3
- package/dist/system/index.js +3 -3
- package/dist/system/index.js.map +1 -1
- package/dist/thread/index.d.ts +3 -3
- package/dist/thread/index.js +3 -3
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.d.ts +3 -3
- package/dist/topic/core/index.js +3 -3
- package/dist/topic/core/index.js.map +1 -1
- package/dist/vite/index.d.ts +6286 -4
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +28 -2
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts +37 -37
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +3 -3
- package/dist/websocket/index.js.map +1 -1
- package/package.json +12 -4
- package/src/api/audits/controllers/AdminAuditController.ts +8 -0
- package/src/api/audits/index.ts +3 -3
- package/src/api/files/controllers/AdminFileStatsController.ts +1 -0
- package/src/api/files/index.ts +3 -3
- package/src/api/jobs/controllers/AdminJobController.ts +18 -2
- package/src/api/jobs/index.ts +4 -3
- package/src/api/jobs/services/JobAudits.spec.ts +89 -0
- package/src/api/jobs/services/JobAudits.ts +101 -0
- package/src/api/keys/index.ts +3 -3
- package/src/api/notifications/controllers/AdminNotificationController.ts +1 -0
- package/src/api/notifications/index.ts +3 -3
- package/src/api/parameters/controllers/AdminConfigController.ts +10 -0
- package/src/api/parameters/index.ts +5 -3
- package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1 -1
- package/src/api/users/__tests__/ApiKeys.spec.ts +1 -1
- package/src/api/users/__tests__/EmailVerification.spec.ts +16 -1
- package/src/api/users/__tests__/PasswordReset.spec.ts +11 -0
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +10 -0
- package/src/api/users/controllers/AdminIdentityController.ts +3 -0
- package/src/api/users/controllers/AdminSessionController.ts +3 -0
- package/src/api/users/controllers/AdminUserController.ts +5 -0
- package/src/api/users/index.ts +8 -9
- package/src/api/users/primitives/$realm.ts +117 -19
- package/src/api/users/providers/RealmProvider.ts +15 -7
- package/src/api/users/services/CredentialService.spec.ts +11 -0
- package/src/api/users/services/CredentialService.ts +47 -24
- package/src/api/users/services/IdentityService.ts +12 -4
- package/src/api/users/services/RegistrationService.spec.ts +11 -0
- package/src/api/users/services/RegistrationService.ts +33 -12
- package/src/api/users/services/SessionService.ts +83 -12
- package/src/api/users/services/UserAudits.ts +47 -0
- package/src/api/users/services/UserFiles.ts +19 -0
- package/src/api/users/services/UserJobs.spec.ts +107 -0
- package/src/api/users/services/UserJobs.ts +62 -0
- package/src/api/users/services/UserParameters.ts +23 -0
- package/src/api/users/services/UserService.ts +34 -17
- package/src/api/verifications/index.ts +3 -3
- package/src/batch/index.ts +3 -3
- package/src/bucket/index.ts +3 -3
- package/src/cache/core/index.ts +3 -3
- package/src/cli/commands/build.ts +1 -0
- package/src/cli/commands/db.ts +9 -0
- package/src/cli/commands/init.spec.ts +2 -17
- package/src/cli/commands/init.ts +37 -1
- package/src/cli/providers/ViteDevServerProvider.ts +36 -2
- package/src/cli/services/AlephaCliUtils.ts +17 -0
- package/src/cli/services/PackageManagerUtils.ts +15 -1
- package/src/cli/services/ProjectScaffolder.ts +8 -13
- package/src/cli/templates/agentMd.ts +2 -25
- package/src/cli/templates/apiAppSecurityTs.ts +37 -2
- package/src/cli/templates/mainCss.ts +2 -32
- package/src/cli/templates/webAppRouterTs.ts +5 -5
- package/src/cli/templates/webHomeComponentTsx.ts +10 -0
- package/src/command/helpers/Runner.ts +14 -1
- package/src/command/index.ts +3 -3
- package/src/core/helpers/primitive.ts +0 -5
- package/src/core/index.ts +3 -3
- package/src/datetime/index.ts +3 -3
- package/src/email/index.ts +3 -3
- package/src/email/index.workerd.ts +36 -0
- package/src/email/providers/LocalEmailProvider.ts +2 -2
- package/src/email/providers/WorkermailerEmailProvider.ts +221 -0
- package/src/fake/index.ts +3 -3
- package/src/lock/core/index.ts +3 -3
- package/src/lock/core/primitives/$lock.ts +13 -1
- package/src/logger/index.ts +3 -3
- package/src/logger/providers/PrettyFormatterProvider.ts +7 -0
- package/src/mcp/index.ts +3 -3
- package/src/orm/index.ts +3 -3
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
- package/src/queue/core/index.ts +3 -3
- package/src/react/auth/index.ts +3 -3
- package/src/react/auth/services/ReactAuth.ts +3 -1
- package/src/react/core/index.ts +3 -3
- package/src/react/form/index.ts +3 -3
- package/src/react/head/index.ts +3 -3
- package/src/react/i18n/index.ts +3 -3
- package/src/react/intro/components/GettingStarted.css +334 -0
- package/src/react/intro/components/GettingStarted.tsx +276 -0
- package/src/react/intro/index.ts +1 -0
- package/src/react/router/atoms/ssrManifestAtom.ts +7 -0
- package/src/react/router/index.browser.ts +2 -0
- package/src/react/router/index.ts +2 -0
- package/src/react/router/providers/ReactServerProvider.ts +14 -4
- package/src/react/router/providers/SSRManifestProvider.ts +7 -0
- package/src/redis/index.ts +3 -3
- package/src/retry/index.ts +3 -3
- package/src/router/index.ts +3 -3
- package/src/scheduler/index.ts +3 -3
- package/src/scheduler/index.workerd.ts +43 -0
- package/src/scheduler/providers/CronProvider.ts +53 -6
- package/src/scheduler/providers/WorkerdCronProvider.ts +102 -0
- package/src/security/index.ts +3 -3
- package/src/security/providers/JwtProvider.ts +2 -2
- package/src/server/auth/index.ts +3 -3
- package/src/server/cache/index.ts +3 -3
- package/src/server/compress/index.ts +3 -3
- package/src/server/compress/providers/ServerCompressProvider.ts +6 -0
- package/src/server/cookies/index.ts +3 -3
- package/src/server/core/index.ts +3 -3
- package/src/server/core/primitives/$action.spec.ts +3 -2
- package/src/server/core/primitives/$action.ts +6 -2
- package/src/server/core/providers/NodeHttpServerProvider.ts +2 -15
- package/src/server/core/providers/ServerProvider.ts +4 -2
- package/src/server/core/providers/ServerRouterProvider.ts +5 -27
- package/src/server/cors/index.ts +3 -3
- package/src/server/health/index.ts +3 -3
- package/src/server/helmet/index.ts +3 -3
- package/src/server/links/index.ts +3 -3
- package/src/server/links/providers/ServerLinksProvider.spec.ts +332 -0
- package/src/server/links/providers/ServerLinksProvider.ts +1 -1
- package/src/server/metrics/index.ts +3 -3
- package/src/server/multipart/index.ts +3 -3
- package/src/server/proxy/index.ts +3 -3
- package/src/server/rate-limit/index.ts +3 -3
- package/src/server/static/index.ts +3 -3
- package/src/server/swagger/index.ts +3 -3
- package/src/sms/index.ts +3 -3
- package/src/system/index.ts +3 -3
- package/src/thread/index.ts +3 -3
- package/src/topic/core/index.ts +3 -3
- package/src/vite/tasks/generateCloudflare.ts +38 -2
- package/src/websocket/index.ts +3 -3
- package/src/cli/templates/webHelloComponentTsx.ts +0 -30
- /package/src/api/users/{notifications → services}/UserNotifications.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["INTENT_TTL_MINUTES","nodeJoin","fsRm","fsCp","fsMkdir","fsReadFile","fsWriteFile"],"sources":["../../../src/api/users/schemas/identityQuerySchema.ts","../../../src/api/users/entities/users.ts","../../../src/api/users/entities/identities.ts","../../../src/api/users/schemas/identityResourceSchema.ts","../../../src/api/users/atoms/realmAuthSettingsAtom.ts","../../../src/api/users/entities/sessions.ts","../../../src/api/users/providers/RealmProvider.ts","../../../src/api/users/services/IdentityService.ts","../../../src/api/users/controllers/AdminIdentityController.ts","../../../src/api/users/schemas/sessionQuerySchema.ts","../../../src/api/users/schemas/sessionResourceSchema.ts","../../../src/api/users/services/SessionCrudService.ts","../../../src/api/users/controllers/AdminSessionController.ts","../../../src/api/users/schemas/createUserSchema.ts","../../../src/api/users/schemas/updateUserSchema.ts","../../../src/api/users/schemas/userQuerySchema.ts","../../../src/api/users/schemas/userResourceSchema.ts","../../../src/api/users/notifications/UserNotifications.ts","../../../src/api/users/services/UserService.ts","../../../src/api/users/controllers/AdminUserController.ts","../../../src/api/users/schemas/realmConfigSchema.ts","../../../src/api/users/controllers/RealmController.ts","../../../src/api/users/schemas/completePasswordResetRequestSchema.ts","../../../src/api/users/schemas/completeRegistrationRequestSchema.ts","../../../src/api/users/schemas/passwordResetIntentResponseSchema.ts","../../../src/api/users/schemas/registerQuerySchema.ts","../../../src/api/users/schemas/registerRequestSchema.ts","../../../src/api/users/schemas/registrationIntentResponseSchema.ts","../../../src/api/users/services/CredentialService.ts","../../../src/api/users/services/RegistrationService.ts","../../../src/api/users/controllers/UserController.ts","../../../src/system/providers/FileSystemProvider.ts","../../../src/system/providers/MemoryFileSystemProvider.ts","../../../src/system/providers/MemoryShellProvider.ts","../../../src/system/services/FileDetector.ts","../../../src/system/providers/NodeFileSystemProvider.ts","../../../src/system/providers/NodeShellProvider.ts","../../../src/system/providers/ShellProvider.ts","../../../src/system/index.ts","../../../src/api/users/services/SessionService.ts","../../../src/api/keys/schemas/adminApiKeyQuerySchema.ts","../../../src/api/keys/schemas/adminApiKeyResourceSchema.ts","../../../src/api/keys/entities/apiKeyEntity.ts","../../../src/api/keys/services/ApiKeyService.ts","../../../src/api/keys/controllers/AdminApiKeyController.ts","../../../src/api/keys/schemas/createApiKeyBodySchema.ts","../../../src/api/keys/schemas/createApiKeyResponseSchema.ts","../../../src/api/keys/schemas/listApiKeyResponseSchema.ts","../../../src/api/keys/schemas/revokeApiKeyParamsSchema.ts","../../../src/api/keys/schemas/revokeApiKeyResponseSchema.ts","../../../src/api/keys/controllers/ApiKeyController.ts","../../../src/api/keys/index.ts","../../../src/api/users/primitives/$realm.ts","../../../src/api/users/schemas/loginSchema.ts","../../../src/api/users/schemas/registerSchema.ts","../../../src/api/users/schemas/resetPasswordSchema.ts","../../../src/api/users/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const identityQuerySchema = t.extend(pageQuerySchema, {\n userId: t.optional(t.uuid()),\n provider: t.optional(t.string()),\n});\n\nexport type IdentityQuery = Static<typeof identityQuerySchema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const DEFAULT_USER_REALM_NAME = \"default\";\n\nexport const users = $entity({\n name: \"users\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n\n realm: db.default(t.text(), DEFAULT_USER_REALM_NAME),\n\n username: t.optional(\n t.shortText({\n minLength: 3,\n maxLength: 50,\n // pattern is handled at the realm settings level\n }),\n ),\n\n email: t.optional(t.string({ format: \"email\" })),\n\n phoneNumber: t.optional(t.e164()),\n\n roles: db.default(t.array(t.string()), []),\n firstName: t.optional(t.string()),\n lastName: t.optional(t.string()),\n picture: t.optional(t.string()),\n enabled: db.default(t.boolean(), true),\n\n emailVerified: db.default(t.boolean(), false),\n }),\n indexes: [\n { columns: [\"realm\", \"username\"], unique: true },\n { columns: [\"realm\", \"email\"], unique: true },\n { columns: [\"realm\", \"phoneNumber\"], unique: true },\n ],\n});\n\nexport type UserEntity = Static<typeof users.schema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\nimport { users } from \"./users.ts\";\n\nexport const identities = $entity({\n name: \"identities\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n userId: db.ref(t.uuid(), () => users.cols.id),\n password: t.optional(t.text()),\n provider: t.text(),\n providerUserId: t.optional(t.text()),\n providerData: t.optional(t.json()),\n }),\n});\n\nexport type IdentityEntity = Static<typeof identities.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { identities } from \"../entities/identities.ts\";\n\nexport const identityResourceSchema = t.omit(identities.schema, [\"password\"]);\n\nexport type IdentityResource = Static<typeof identityResourceSchema>;\n","import { $atom, type Static, t } from \"alepha\";\n\nexport const realmAuthSettingsAtom = $atom({\n name: \"alepha.api.users.realmAuthSettings\",\n schema: t.object({\n // Branding and display settings\n displayName: t.optional(\n t.string({\n description:\n \"Display name shown on auth pages (e.g., 'Customer Portal')\",\n }),\n ),\n description: t.optional(\n t.string({\n description: \"Description shown on auth pages\",\n }),\n ),\n logoUrl: t.optional(\n t.string({\n description: \"Logo URL for auth pages\",\n }),\n ),\n\n // Auth settings\n registrationAllowed: t.boolean({\n description: \"Enable user self-registration\",\n }),\n emailEnabled: t.boolean({\n description: \"Enable email address as a login/registration credential\",\n }),\n emailRequired: t.boolean({\n description: \"Require email address for user accounts\",\n }),\n usernameEnabled: t.boolean({\n description: \"Enable username as a login/registration credential\",\n }),\n usernameRequired: t.boolean({\n description: \"Require username for user accounts\",\n }),\n usernameRegExp: t.string({\n description:\n \"Regular expression that usernames must match (if username is enabled)\",\n }),\n phoneEnabled: t.boolean({\n description: \"Enable phone number as a login/registration credential\",\n }),\n phoneRequired: t.boolean({\n description: \"Require phone number for user accounts\",\n }),\n verifyEmailRequired: t.boolean({\n description: \"Require email verification for user accounts\",\n }),\n verifyPhoneRequired: t.boolean({\n description: \"Require phone verification for user accounts\",\n }),\n firstNameLastNameEnabled: t.boolean({\n description: \"Enable first and last name for user accounts\",\n }),\n firstNameLastNameRequired: t.boolean({\n description: \"Require first and last name for user accounts\",\n }),\n resetPasswordAllowed: t.boolean({\n description: \"Enable forgot password functionality\",\n }),\n passwordPolicy: t.object({\n minLength: t.integer({\n description: \"Minimum password length\",\n default: 8,\n minimum: 1,\n }),\n requireUppercase: t.boolean({\n description: \"Require at least one uppercase letter\",\n }),\n requireLowercase: t.boolean({\n description: \"Require at least one lowercase letter\",\n }),\n requireNumbers: t.boolean({\n description: \"Require at least one number\",\n }),\n requireSpecialCharacters: t.boolean({\n description: \"Require at least one special character\",\n }),\n }),\n }),\n default: {\n // for a fresh hello world setup, we accept registration and email login\n registrationAllowed: true,\n emailEnabled: true,\n emailRequired: true,\n usernameEnabled: false,\n usernameRequired: false,\n usernameRegExp: \"^[a-zA-Z0-9_]{3,30}$\",\n phoneEnabled: false,\n phoneRequired: false,\n verifyEmailRequired: false,\n verifyPhoneRequired: false,\n resetPasswordAllowed: false,\n firstNameLastNameEnabled: false,\n firstNameLastNameRequired: false,\n // TODO: not implemented yet\n passwordPolicy: {\n minLength: 8,\n requireUppercase: true,\n requireLowercase: true,\n requireNumbers: true,\n requireSpecialCharacters: false,\n },\n },\n});\n\nexport type RealmAuthSettings = Static<typeof realmAuthSettingsAtom.schema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\nimport { users } from \"./users.ts\";\n\nexport const sessions = $entity({\n name: \"sessions\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n refreshToken: t.uuid(),\n userId: db.ref(t.uuid(), () => users.cols.id),\n expiresAt: t.datetime(),\n ip: t.optional(t.text()),\n userAgent: t.optional(\n t.object({\n os: t.text(),\n browser: t.text(),\n device: t.enum([\"MOBILE\", \"DESKTOP\", \"TABLET\"]),\n }),\n ),\n }),\n});\n\nexport type SessionEntity = Static<typeof sessions.schema>;\n","import { $inject, Alepha, AlephaError } from \"alepha\";\nimport { $bucket } from \"alepha/bucket\";\nimport { $repository, type Repository } from \"alepha/orm\";\nimport {\n type RealmAuthSettings,\n realmAuthSettingsAtom,\n} from \"../atoms/realmAuthSettingsAtom.ts\";\nimport { identities } from \"../entities/identities.ts\";\nimport { sessions } from \"../entities/sessions.ts\";\nimport { DEFAULT_USER_REALM_NAME, users } from \"../entities/users.ts\";\nimport type { RealmOptions } from \"../primitives/$realm.ts\";\n\nexport interface RealmRepositories {\n identities: Repository<typeof identities.schema>;\n sessions: Repository<typeof sessions.schema>;\n users: Repository<typeof users.schema>;\n}\n\nexport interface Realm {\n name: string;\n repositories: RealmRepositories;\n settings: RealmAuthSettings;\n}\n\nexport class RealmProvider {\n protected readonly alepha = $inject(Alepha);\n // Default repositories using $repository() for eager initialization\n protected readonly defaultIdentities = $repository(identities);\n protected readonly defaultSessions = $repository(sessions);\n protected readonly defaultUsers = $repository(users);\n\n protected realms = new Map<string, Realm>();\n\n public avatars = $bucket({\n maxSize: 5 * 1024 * 1024, // 5 MB\n mimeTypes: [\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"],\n });\n\n public register(realmName: string, realmOptions: RealmOptions = {}) {\n this.realms.set(realmName, {\n name: realmName,\n repositories: {\n identities: realmOptions.entities?.identities ?? this.defaultIdentities,\n sessions: realmOptions.entities?.sessions ?? this.defaultSessions,\n users: realmOptions.entities?.users ?? this.defaultUsers,\n },\n // TODO: Remove deep merge when alepha supports it natively\n settings: {\n ...realmAuthSettingsAtom.options.default,\n ...realmOptions.settings,\n passwordPolicy: {\n ...realmAuthSettingsAtom.options.default.passwordPolicy,\n ...realmOptions.settings?.passwordPolicy,\n },\n },\n });\n return this.getRealm(realmName);\n }\n\n /**\n * Gets a registered realm by name, auto-creating default if needed.\n */\n public getRealm(realmName = DEFAULT_USER_REALM_NAME): Realm {\n let realm = this.realms.get(realmName);\n\n if (!realm) {\n // Auto-register default realm for backward compatibility\n const realms = Array.from(this.realms.values());\n const firstRealm = realms[0];\n if (realmName === DEFAULT_USER_REALM_NAME && firstRealm) {\n realm = firstRealm;\n } else if (this.alepha.isTest()) {\n realm = this.register(realmName); // Auto-create default realm in tests\n } else {\n throw new AlephaError(\n `Missing realm '${realmName}', please declare $realm in your application.`,\n );\n }\n }\n\n return realm;\n }\n\n public identityRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof identities.schema> {\n return this.getRealm(realmName).repositories.identities;\n }\n\n public sessionRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof sessions.schema> {\n return this.getRealm(realmName).repositories.sessions;\n }\n\n public userRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof users.schema> {\n return this.getRealm(realmName).repositories.users;\n }\n}\n","import { $inject } from \"alepha\";\nimport { AuditService } from \"alepha/api/audits\";\nimport { $logger } from \"alepha/logger\";\nimport type { Page } from \"alepha/orm\";\nimport type { IdentityEntity } from \"../entities/identities.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { IdentityQuery } from \"../schemas/identityQuerySchema.ts\";\n\nexport class IdentityService {\n protected readonly log = $logger();\n protected readonly realmProvider = $inject(RealmProvider);\n protected readonly auditService = $inject(AuditService);\n\n public identities(userRealmName?: string) {\n return this.realmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Find identities with pagination and filtering.\n */\n public async findIdentities(\n q: IdentityQuery = {},\n userRealmName?: string,\n ): Promise<Page<IdentityEntity>> {\n this.log.trace(\"Finding identities\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.identities(userRealmName).createQueryWhere();\n\n if (q.userId) {\n where.userId = { eq: q.userId };\n }\n\n if (q.provider) {\n where.provider = { like: q.provider };\n }\n\n const result = await this.identities(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Identities found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get an identity by ID.\n */\n public async getIdentityById(\n id: string,\n userRealmName?: string,\n ): Promise<IdentityEntity> {\n this.log.trace(\"Getting identity by ID\", { id, userRealmName });\n const identity = await this.identities(userRealmName).findById(id);\n this.log.debug(\"Identity retrieved\", {\n id,\n provider: identity.provider,\n userId: identity.userId,\n });\n return identity;\n }\n\n /**\n * Delete an identity by ID.\n */\n public async deleteIdentity(\n id: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Deleting identity\", { id, userRealmName });\n\n // Verify identity exists\n const identity = await this.getIdentityById(id, userRealmName);\n\n await this.identities(userRealmName).deleteById(id);\n this.log.info(\"Identity deleted\", {\n id,\n provider: identity.provider,\n userId: identity.userId,\n });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n await this.auditService.recordUser(\"update\", {\n userRealm: realm.name,\n resourceId: identity.userId,\n description: `Identity provider disconnected: ${identity.provider}`,\n metadata: {\n identityId: id,\n provider: identity.provider,\n userId: identity.userId,\n },\n });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { identityQuerySchema } from \"../schemas/identityQuerySchema.ts\";\nimport { identityResourceSchema } from \"../schemas/identityResourceSchema.ts\";\nimport { IdentityService } from \"../services/IdentityService.ts\";\n\nexport class AdminIdentityController {\n protected readonly url = \"/identities\";\n protected readonly group = \"admin:identities\";\n protected readonly identityService = $inject(IdentityService);\n\n /**\n * Find identities with pagination and filtering.\n */\n public readonly findIdentities = $action({\n path: this.url,\n group: this.group,\n description: \"Find identities with pagination and filtering\",\n schema: {\n query: t.extend(identityQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: t.page(identityResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.identityService.findIdentities(q, userRealmName);\n },\n });\n\n /**\n * Get an identity by ID.\n */\n public readonly getIdentity = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Get an identity by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: identityResourceSchema,\n },\n handler: ({ params, query }) =>\n this.identityService.getIdentityById(params.id, query.userRealmName),\n });\n\n /**\n * Delete an identity.\n */\n public readonly deleteIdentity = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Delete an identity\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.identityService.deleteIdentity(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const sessionQuerySchema = t.extend(pageQuerySchema, {\n userId: t.optional(t.uuid()),\n});\n\nexport type SessionQuery = Static<typeof sessionQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const sessionResourceSchema = t.object({\n id: t.uuid(),\n version: t.number(),\n createdAt: t.datetime(),\n updatedAt: t.datetime(),\n refreshToken: t.uuid(),\n userId: t.uuid(),\n expiresAt: t.datetime(),\n ip: t.optional(t.string()),\n userAgent: t.optional(\n t.object({\n os: t.string(),\n browser: t.string(),\n device: t.enum([\"MOBILE\", \"DESKTOP\", \"TABLET\"]),\n }),\n ),\n});\n\nexport type SessionResource = Static<typeof sessionResourceSchema>;\n","import { $inject } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { Page } from \"alepha/orm\";\nimport type { SessionEntity } from \"../entities/sessions.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { SessionQuery } from \"../schemas/sessionQuerySchema.ts\";\n\nexport class SessionCrudService {\n protected readonly log = $logger();\n protected readonly realmProvider = $inject(RealmProvider);\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.sessionRepository(userRealmName);\n }\n\n /**\n * Find sessions with pagination and filtering.\n */\n public async findSessions(\n q: SessionQuery = {},\n userRealmName?: string,\n ): Promise<Page<SessionEntity>> {\n this.log.trace(\"Finding sessions\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.sessions(userRealmName).createQueryWhere();\n\n if (q.userId) {\n where.userId = { eq: q.userId };\n }\n\n const result = await this.sessions(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Sessions found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get a session by ID.\n */\n public async getSessionById(\n id: string,\n userRealmName?: string,\n ): Promise<SessionEntity> {\n this.log.trace(\"Getting session by ID\", { id, userRealmName });\n const session = await this.sessions(userRealmName).findById(id);\n this.log.debug(\"Session retrieved\", { id, userId: session.userId });\n return session;\n }\n\n /**\n * Delete a session by ID.\n */\n public async deleteSession(\n id: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Deleting session\", { id, userRealmName });\n\n // Verify session exists\n await this.getSessionById(id, userRealmName);\n\n await this.sessions(userRealmName).deleteById(id);\n this.log.info(\"Session deleted\", { id });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { sessionQuerySchema } from \"../schemas/sessionQuerySchema.ts\";\nimport { sessionResourceSchema } from \"../schemas/sessionResourceSchema.ts\";\nimport { SessionCrudService } from \"../services/SessionCrudService.ts\";\n\nexport class AdminSessionController {\n protected readonly url = \"/sessions\";\n protected readonly group = \"admin:sessions\";\n protected readonly sessionService = $inject(SessionCrudService);\n\n /**\n * Find sessions with pagination and filtering.\n */\n public readonly findSessions = $action({\n path: this.url,\n group: this.group,\n description: \"Find sessions with pagination and filtering\",\n schema: {\n query: t.extend(sessionQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: t.page(sessionResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.sessionService.findSessions(q, userRealmName);\n },\n });\n\n /**\n * Get a session by ID.\n */\n public readonly getSession = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Get a session by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: sessionResourceSchema,\n },\n handler: ({ params, query }) =>\n this.sessionService.getSessionById(params.id, query.userRealmName),\n });\n\n /**\n * Delete a session.\n */\n public readonly deleteSession = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Delete a session\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.sessionService.deleteSession(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n}\n","import { type Static, t } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const createUserSchema = t.omit(users.insertSchema, [\"realm\"]);\n\nexport type CreateUser = Static<typeof createUserSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const updateUserSchema = t.partial(\n t.omit(users.insertSchema, [\n \"id\",\n \"version\",\n \"createdAt\",\n \"updatedAt\",\n \"username\",\n \"emailVerified\",\n ]),\n);\n\nexport type UpdateUser = Static<typeof updateUserSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const userQuerySchema = t.extend(pageQuerySchema, {\n email: t.optional(t.string()),\n enabled: t.optional(t.boolean()),\n emailVerified: t.optional(t.boolean()),\n roles: t.optional(t.array(t.string())),\n query: t.optional(t.text()),\n});\n\nexport type UserQuery = Static<typeof userQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const userResourceSchema = users.schema;\n\nexport type UserResource = Static<typeof userResourceSchema>;\n","import { t } from \"alepha\";\nimport { $notification } from \"alepha/api/notifications\";\n\nexport class UserNotifications {\n // Code-based notifications (preferred)\n public readonly passwordReset = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a verification code to reset their password.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Reset your password\",\n body: (it) => `\n\t\t\t<h1>Reset Your Password</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>We received a request to reset your password. Use the code below to verify your identity:</p>\n\t\t\t<p style=\"margin: 30px 0; text-align: center;\">\n\t\t\t\t<span style=\"font-size: 32px; font-weight: bold; letter-spacing: 8px; font-family: monospace; background-color: #f5f5f5; padding: 16px 24px; border-radius: 8px; display: inline-block;\">\n\t\t\t\t\t${it.code}\n\t\t\t\t</span>\n\t\t\t</p>\n\t\t\t<p>This code will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly emailVerification = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a verification code to verify their email address.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Verify your email address\",\n body: (it) => `\n\t\t\t<h1>Verify Your Email Address</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>Thanks for signing up! Use the code below to verify your email address:</p>\n\t\t\t<p style=\"margin: 30px 0; text-align: center;\">\n\t\t\t\t<span style=\"font-size: 32px; font-weight: bold; letter-spacing: 8px; font-family: monospace; background-color: #f5f5f5; padding: 16px 24px; border-radius: 8px; display: inline-block;\">\n\t\t\t\t\t${it.code}\n\t\t\t\t</span>\n\t\t\t</p>\n\t\t\t<p>This code will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you did not create an account, please ignore this email.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly phoneVerification = $notification({\n category: \"security\",\n description:\n \"SMS sent to users with a verification code to verify their phone number.\",\n critical: true,\n sensitive: true,\n sms: {\n message: (it) =>\n `Your verification code is: ${it.code}. This code expires in ${it.expiresInMinutes} minutes.`,\n },\n schema: t.object({\n phoneNumber: t.string(),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n // Link-based notifications (alternative)\n public readonly passwordResetLink = $notification({\n category: \"security\",\n description: \"Email sent to users with a link to reset their password.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Reset your password\",\n body: (it) => `\n\t\t\t<h1>Reset Your Password</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>We received a request to reset your password. Click the link below to create a new password:</p>\n\t\t\t<p style=\"margin: 30px 0;\">\n\t\t\t\t<a href=\"${it.resetUrl}\" style=\"background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;\">\n\t\t\t\t\tReset Password\n\t\t\t\t</a>\n\t\t\t</p>\n\t\t\t<p>Or copy and paste this link into your browser:</p>\n\t\t\t<p style=\"word-break: break-all; color: #666;\">${it.resetUrl}</p>\n\t\t\t<p>This link will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n resetUrl: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly emailVerificationLink = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a link to verify their email address.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Verify your email address\",\n body: (it) => `\n\t\t\t<h1>Verify Your Email Address</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>Thanks for signing up! Click the button below to verify your email address:</p>\n\t\t\t<p style=\"margin: 30px 0;\">\n\t\t\t\t<a href=\"${it.verifyUrl}\" style=\"background-color: #28a745; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;\">\n\t\t\t\t\tVerify Email\n\t\t\t\t</a>\n\t\t\t</p>\n\t\t\t<p>Or copy and paste this link into your browser:</p>\n\t\t\t<p style=\"word-break: break-all; color: #666;\">${it.verifyUrl}</p>\n\t\t\t<p>This link will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you did not create an account, please ignore this email.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n verifyUrl: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n}\n","import { $inject } from \"alepha\";\nimport { AuditService } from \"alepha/api/audits\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $logger } from \"alepha/logger\";\nimport { type Page, parseQueryString } from \"alepha/orm\";\nimport { BadRequestError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { CreateUser } from \"../schemas/createUserSchema.ts\";\nimport type { UpdateUser } from \"../schemas/updateUserSchema.ts\";\nimport type { UserQuery } from \"../schemas/userQuerySchema.ts\";\n\nexport class UserService {\n protected readonly log = $logger();\n protected readonly verificationController = $client<VerificationController>();\n protected readonly userNotifications = $inject(UserNotifications);\n protected readonly realmProvider = $inject(RealmProvider);\n protected readonly auditService = $inject(AuditService);\n\n public users(userRealmName?: string) {\n return this.realmProvider.userRepository(userRealmName);\n }\n\n /**\n * Request email verification for a user.\n * @param email - The email address to verify.\n * @param userRealmName - Optional realm name.\n * @param method - The verification method: \"code\" (default) or \"link\".\n * @param verifyUrl - Base URL for verification link (required when method is \"link\").\n */\n public async requestEmailVerification(\n email: string,\n userRealmName?: string,\n method: \"code\" | \"link\" = \"code\",\n verifyUrl?: string,\n ): Promise<boolean> {\n this.log.trace(\"Requesting email verification\", {\n email,\n userRealmName,\n method,\n });\n\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n if (!user) {\n this.log.debug(\"Email verification requested for non-existent user\", {\n email,\n });\n return true;\n }\n\n if (user.emailVerified) {\n this.log.debug(\"Email verification requested for already verified user\", {\n email,\n userId: user.id,\n });\n return true;\n }\n\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: method },\n body: { target: email },\n });\n\n if (method === \"link\") {\n // Build verification URL with token\n const url = new URL(verifyUrl || \"/verify-email\", \"http://localhost\");\n url.searchParams.set(\"email\", email);\n url.searchParams.set(\"token\", verification.token);\n const fullVerifyUrl = verifyUrl\n ? `${verifyUrl}${url.search}`\n : url.pathname + url.search;\n\n await this.userNotifications.emailVerificationLink.push({\n contact: email,\n variables: {\n email,\n verifyUrl: fullVerifyUrl,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n this.log.debug(\"Email verification link sent\", {\n email,\n userId: user.id,\n });\n } else {\n await this.userNotifications.emailVerification.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n this.log.debug(\"Email verification code sent\", {\n email,\n userId: user.id,\n });\n }\n } catch (error) {\n // Silent fail for security\n this.log.warn(\"Failed to send email verification\", { email, error });\n }\n\n return true;\n }\n\n /**\n * Verify a user's email using a valid verification token.\n * Supports both code (6-digit) and link (UUID) verification tokens.\n */\n public async verifyEmail(\n email: string,\n token: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Verifying email\", { email, userRealmName });\n\n // Detect verification type based on token format\n // Codes are 6-digit numbers, links are UUIDs\n const isCode = /^\\d{6}$/.test(token);\n const type = isCode ? \"code\" : \"link\";\n\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type },\n body: { target: email, token },\n })\n .catch(() => {\n this.log.warn(\"Invalid email verification token\", { email, type });\n throw new BadRequestError(\"Invalid or expired verification token\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Email verification token already used\", { email });\n throw new BadRequestError(\"Invalid or expired verification token\");\n }\n\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\n\n await this.users(userRealmName).updateById(user.id, {\n emailVerified: true,\n });\n\n this.log.info(\"Email verified\", { email, userId: user.id, type });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n await this.auditService.recordUser(\"update\", {\n userId: user.id,\n userEmail: email,\n userRealm: realm.name,\n resourceId: user.id,\n description: \"Email verified\",\n metadata: { email, verificationType: type },\n });\n }\n\n /**\n * Check if an email is verified.\n */\n public async isEmailVerified(\n email: string,\n userRealmName?: string,\n ): Promise<boolean> {\n this.log.trace(\"Checking if email is verified\", { email, userRealmName });\n\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n return user?.emailVerified ?? false;\n }\n\n /**\n * Find users with pagination and filtering.\n */\n public async findUsers(\n q: UserQuery = {},\n userRealmName?: string,\n ): Promise<Page<UserEntity>> {\n this.log.trace(\"Finding users\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.users(userRealmName).createQueryWhere();\n\n if (q.email) {\n where.email = { like: q.email };\n }\n\n if (q.enabled !== undefined) {\n where.enabled = { eq: q.enabled };\n }\n\n if (q.emailVerified !== undefined) {\n where.emailVerified = { eq: q.emailVerified };\n }\n\n if (q.roles) {\n where.roles = { arrayContains: q.roles };\n }\n\n if (q.query) {\n Object.assign(where, parseQueryString(q.query));\n }\n\n const result = await this.users(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Users found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get a user by ID.\n */\n public async getUserById(\n id: string,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Getting user by ID\", { id, userRealmName });\n return await this.users(userRealmName).findById(id);\n }\n\n /**\n * Create a new user.\n */\n public async createUser(\n data: CreateUser,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Creating user\", {\n username: data.username,\n email: data.email,\n userRealmName,\n });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n // TODO: one query instead of 3\n\n // Check for existing user based on provided unique fields\n if (data.username) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { username: { eq: data.username } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Username already taken\", { username: data.username });\n throw new BadRequestError(\"User with this username already exists\");\n }\n }\n\n if (data.email) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: data.email } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Email already taken\", { email: data.email });\n throw new BadRequestError(\"User with this email already exists\");\n }\n }\n\n if (data.phoneNumber) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { phoneNumber: { eq: data.phoneNumber } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Phone number already taken\", {\n phoneNumber: data.phoneNumber,\n });\n throw new BadRequestError(\"User with this phone number already exists\");\n }\n }\n\n const user = await this.users(userRealmName).create({\n ...data,\n roles: data.roles ?? [\"user\"], // TODO: Default roles from realm settings\n realm: realm.name,\n });\n\n this.log.info(\"User created\", {\n userId: user.id,\n username: user.username,\n email: user.email,\n });\n\n await this.auditService.recordUser(\"create\", {\n userRealm: realm.name,\n resourceId: user.id,\n description: \"User created\",\n metadata: {\n username: user.username,\n email: user.email,\n roles: user.roles,\n },\n });\n\n return user;\n }\n\n /**\n * Update an existing user.\n */\n public async updateUser(\n id: string,\n data: UpdateUser,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Updating user\", { id, userRealmName });\n const before = await this.getUserById(id, userRealmName);\n\n const user = await this.users(userRealmName).updateById(id, data);\n this.log.debug(\"User updated\", { userId: id });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n // Build changes object showing what was updated\n const changes: Record<string, { from: unknown; to: unknown }> = {};\n for (const key of Object.keys(data) as (keyof UpdateUser)[]) {\n if (data[key] !== undefined && before[key] !== data[key]) {\n changes[key] = { from: before[key], to: data[key] };\n }\n }\n\n // Detect role changes for special handling\n const isRoleChange =\n data.roles !== undefined &&\n JSON.stringify(before.roles) !== JSON.stringify(data.roles);\n\n await this.auditService.recordUser(\n isRoleChange ? \"role_change\" : \"update\",\n {\n userRealm: realm.name,\n resourceId: user.id,\n description: isRoleChange\n ? \"User roles changed\"\n : `User updated: ${Object.keys(changes).join(\", \")}`,\n metadata: { changes },\n },\n );\n\n return user;\n }\n\n /**\n * Delete a user by ID.\n */\n public async deleteUser(id: string, userRealmName?: string): Promise<void> {\n this.log.trace(\"Deleting user\", { id, userRealmName });\n const user = await this.getUserById(id, userRealmName);\n\n await this.users(userRealmName).deleteById(id);\n this.log.info(\"User deleted\", { userId: id });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n await this.auditService.recordUser(\"delete\", {\n userRealm: realm.name,\n resourceId: id,\n severity: \"warning\",\n description: \"User deleted\",\n metadata: {\n username: user.username,\n email: user.email,\n },\n });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { createUserSchema } from \"../schemas/createUserSchema.ts\";\nimport { updateUserSchema } from \"../schemas/updateUserSchema.ts\";\nimport { userQuerySchema } from \"../schemas/userQuerySchema.ts\";\nimport { userResourceSchema } from \"../schemas/userResourceSchema.ts\";\nimport { UserService } from \"../services/UserService.ts\";\n\nexport class AdminUserController {\n protected readonly url = \"/users\";\n protected readonly group = \"admin:users\";\n protected readonly userService = $inject(UserService);\n\n /**\n * Find users with pagination and filtering.\n */\n public readonly findUsers = $action({\n path: this.url,\n group: this.group,\n description: \"Find users with pagination and filtering\",\n schema: {\n query: t.extend(userQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: t.page(userResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.userService.findUsers(q, userRealmName);\n },\n });\n\n /**\n * Get a user by ID.\n */\n public readonly getUser = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Get a user by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: userResourceSchema,\n },\n handler: ({ params, query }) =>\n this.userService.getUserById(params.id, query.userRealmName),\n });\n\n /**\n * Create a new user.\n */\n public readonly createUser = $action({\n method: \"POST\",\n path: this.url,\n group: this.group,\n description: \"Create a new user\",\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: createUserSchema,\n response: userResourceSchema,\n },\n handler: ({ body, query }) =>\n this.userService.createUser(body, query.userRealmName),\n });\n\n /**\n * Update a user.\n */\n public readonly updateUser = $action({\n method: \"PATCH\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Update a user\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: updateUserSchema,\n response: userResourceSchema,\n },\n handler: ({ params, body, query }) =>\n this.userService.updateUser(params.id, body, query.userRealmName),\n });\n\n /**\n * Delete a user.\n */\n public readonly deleteUser = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Delete a user\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.userService.deleteUser(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n}\n","import { type Static, t } from \"alepha\";\nimport { authenticationProviderSchema } from \"alepha/server/auth\";\nimport { realmAuthSettingsAtom } from \"../atoms/realmAuthSettingsAtom.ts\";\n\nexport const realmConfigSchema = t.object({\n settings: realmAuthSettingsAtom.schema,\n realmName: t.string(),\n authenticationMethods: t.array(authenticationProviderSchema),\n});\n\nexport type RealmConfig = Static<typeof realmConfigSchema>;\n","import { $inject, t } from \"alepha\";\nimport { $action } from \"alepha/server\";\nimport { ServerAuthProvider } from \"alepha/server/auth\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport { realmConfigSchema } from \"../schemas/realmConfigSchema.ts\";\n\n/**\n * Controller for exposing realm configuration.\n * Uses $route instead of $action to keep endpoints hidden from API documentation.\n */\nexport class RealmController {\n protected readonly url = \"/realms\";\n protected readonly group = \"realms\";\n protected readonly realmProvider = $inject(RealmProvider);\n protected readonly serverAuthProvider = $inject(ServerAuthProvider);\n\n /**\n * Get realm configuration settings.\n * This endpoint is not exposed in the API documentation.\n */\n public readonly getRealmConfig = $action({\n group: this.group,\n method: \"GET\",\n path: `${this.url}/config`,\n secure: false,\n cache: {\n etag: true,\n control: {\n maxAge: [24, \"hours\"],\n },\n },\n schema: {\n query: t.object({\n realmName: t.optional(t.string()),\n }),\n response: realmConfigSchema,\n },\n handler: ({ query }) => {\n const { name: realmName, settings } = this.realmProvider.getRealm(\n query.realmName,\n );\n\n const authenticationMethods =\n this.serverAuthProvider.getAuthenticationProviders({\n realmName,\n });\n\n return {\n settings,\n realmName,\n authenticationMethods,\n };\n },\n });\n\n public readonly checkUsernameAvailability = $action({\n group: this.group,\n path: `${this.url}/check-username`,\n secure: false,\n schema: {\n query: t.object({\n realmName: t.optional(t.text()),\n }),\n body: t.object({\n username: t.text(),\n }),\n response: t.object({\n available: t.boolean(),\n }),\n },\n handler: async ({ query, body }) => {\n const realmName = query.realmName;\n const userRepository = this.realmProvider.userRepository(realmName);\n\n const existingUser = await userRepository\n .findOne({ where: { username: { eq: body.username } } })\n .catch(() => undefined);\n\n return {\n available: !existingUser,\n };\n },\n });\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Request schema for completing a password reset.\n *\n * Requires the intent ID from Phase 1, the verification code,\n * and the new password.\n */\nexport const completePasswordResetRequestSchema = t.object({\n intentId: t.uuid({\n description: \"The intent ID from createPasswordResetIntent\",\n }),\n code: t.string({\n description: \"6-digit verification code sent via email\",\n }),\n newPassword: t.string({\n minLength: 8,\n description: \"New password (minimum 8 characters)\",\n }),\n});\n\nexport type CompletePasswordResetRequest = Static<\n typeof completePasswordResetRequestSchema\n>;\n","import { type Static, t } from \"alepha\";\n\nexport const completeRegistrationRequestSchema = t.object({\n intentId: t.uuid({\n description: \"The registration intent ID from the first phase\",\n }),\n emailCode: t.optional(\n t.string({\n description: \"Email verification code (if email verification required)\",\n }),\n ),\n phoneCode: t.optional(\n t.string({\n description: \"Phone verification code (if phone verification required)\",\n }),\n ),\n captchaToken: t.optional(\n t.string({\n description: \"Captcha token (if captcha required)\",\n }),\n ),\n});\n\nexport type CompleteRegistrationRequest = Static<\n typeof completeRegistrationRequestSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Response schema for password reset intent creation.\n *\n * Contains the intent ID needed for Phase 2 completion,\n * along with expiration time.\n */\nexport const passwordResetIntentResponseSchema = t.object({\n intentId: t.uuid({\n description: \"Unique identifier for this password reset intent\",\n }),\n expiresAt: t.datetime({\n description: \"ISO timestamp when this intent expires\",\n }),\n});\n\nexport type PasswordResetIntentResponse = Static<\n typeof passwordResetIntentResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Schema for user registration query parameters.\n * Allows specifying a custom user realm.\n */\nexport const registerQuerySchema = t.object({\n userRealmName: t.optional(\n t.text({\n description:\n \"The user realm to register the user in (defaults to 'default')\",\n }),\n ),\n});\n\nexport type RegisterQuery = Static<typeof registerQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Schema for user registration request body.\n * Password is always required, other fields depend on realm settings.\n */\nexport const registerRequestSchema = t.object({\n // Password is always required\n password: t.string({\n minLength: 8,\n description: \"Password for the account\",\n }),\n\n // Identity fields (requirements depend on realm settings)\n username: t.optional(\n t.string({\n minLength: 3,\n description: \"Unique username for the account\",\n }),\n ),\n\n // Optional contact fields\n email: t.optional(\n t.string({\n format: \"email\",\n description: \"User's email address\",\n }),\n ),\n phoneNumber: t.optional(\n t.string({\n description: \"User's phone number\",\n }),\n ),\n\n // Optional user profile fields\n firstName: t.optional(\n t.string({\n description: \"User's first name\",\n }),\n ),\n lastName: t.optional(\n t.string({\n description: \"User's last name\",\n }),\n ),\n picture: t.optional(\n t.string({\n description: \"User's profile picture URL\",\n }),\n ),\n});\n\nexport type RegisterRequest = Static<typeof registerRequestSchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const registrationIntentResponseSchema = t.object({\n intentId: t.uuid({\n description: \"Unique identifier for the registration intent\",\n }),\n expectCaptcha: t.boolean({\n description: \"Whether captcha verification is required\",\n }),\n expectEmailVerification: t.boolean({\n description: \"Whether email verification is required\",\n }),\n expectPhoneVerification: t.boolean({\n description: \"Whether phone verification is required\",\n }),\n expiresAt: t.datetime({\n description: \"When the registration intent expires\",\n }),\n});\n\nexport type RegistrationIntentResponse = Static<\n typeof registrationIntentResponseSchema\n>;\n","import { randomUUID } from \"node:crypto\";\nimport { $inject } from \"alepha\";\nimport { AuditService } from \"alepha/api/audits\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $cache } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { CryptoProvider } from \"alepha/security\";\nimport { BadRequestError, HttpError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { CompletePasswordResetRequest } from \"../schemas/completePasswordResetRequestSchema.ts\";\nimport type { PasswordResetIntentResponse } from \"../schemas/passwordResetIntentResponseSchema.ts\";\n\n/**\n * Intent stored in cache during the password reset flow.\n */\ninterface PasswordResetIntent {\n email: string;\n userId: string;\n identityId: string;\n realmName?: string;\n expiresAt: string;\n}\n\nconst INTENT_TTL_MINUTES = 10;\n\nexport class CredentialService {\n protected readonly log = $logger();\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly verificationController = $client<VerificationController>();\n protected readonly userNotifications = $inject(UserNotifications);\n protected readonly realmProvider = $inject(RealmProvider);\n protected readonly auditService = $inject(AuditService);\n\n protected readonly intentCache = $cache<PasswordResetIntent>({\n name: \"password-reset-intents\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n public users(userRealmName?: string) {\n return this.realmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.realmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Phase 1: Create a password reset intent.\n *\n * Validates the email, checks for existing user with credentials,\n * sends verification code, and stores the intent in cache.\n *\n * @param email - User's email address\n * @param userRealmName - Optional realm name\n * @returns Intent response with intentId and expiration (always returns for security)\n */\n public async createPasswordResetIntent(\n email: string,\n userRealmName?: string,\n ): Promise<PasswordResetIntentResponse> {\n this.log.trace(\"Creating password reset intent\", { email, userRealmName });\n\n // Generate intent ID and expiration upfront for consistent response\n const intentId = randomUUID();\n const expiresAt = this.dateTimeProvider\n .now()\n .add(INTENT_TTL_MINUTES, \"minutes\")\n .toISOString();\n\n // Find user by email (silent fail for security)\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n if (!user) {\n // Silent fail - don't reveal that email doesn't exist\n this.log.debug(\"Password reset requested for non-existent email\", {\n email,\n });\n return { intentId, expiresAt };\n }\n\n // Find the credentials identity for this user\n const identity = await this.identities(userRealmName)\n .findOne({\n where: {\n userId: { eq: user.id },\n provider: { eq: \"credentials\" },\n },\n })\n .catch(() => undefined);\n\n if (!identity) {\n // User doesn't have credentials identity (maybe OAuth only)\n this.log.debug(\"Password reset requested for user without credentials\", {\n userId: user.id,\n });\n return { intentId, expiresAt };\n }\n\n // Create verification using verification controller\n // This handles: token generation, expiration, rate limiting, cooldown\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: email },\n });\n\n // Send password reset notification with the code\n await this.userNotifications.passwordReset.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n // Store intent in cache\n const intent: PasswordResetIntent = {\n email,\n userId: user.id,\n identityId: identity.id,\n realmName: userRealmName,\n expiresAt,\n };\n\n await this.intentCache.set(intentId, intent);\n\n this.log.info(\"Password reset intent created\", {\n intentId,\n userId: user.id,\n email,\n });\n } catch (error) {\n // If rate limit or cooldown hit, still return success for security\n this.log.warn(\"Failed to create password reset verification\", error);\n }\n\n return { intentId, expiresAt };\n }\n\n /**\n * Phase 2: Complete password reset using an intent.\n *\n * Validates the verification code, updates the password,\n * and invalidates all existing sessions.\n *\n * @param body - Request body with intentId, code, and newPassword\n */\n public async completePasswordReset(\n body: CompletePasswordResetRequest,\n ): Promise<void> {\n this.log.trace(\"Completing password reset\", { intentId: body.intentId });\n\n // Fetch intent from cache\n const intent = await this.intentCache.get(body.intentId);\n if (!intent) {\n this.log.warn(\"Invalid or expired password reset intent\", {\n intentId: body.intentId,\n });\n throw new HttpError({\n status: 410,\n message: \"Invalid or expired password reset intent\",\n });\n }\n\n // Verify code using verification controller\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: intent.email, token: body.code },\n })\n .catch(() => {\n this.log.warn(\"Invalid verification code for password reset\", {\n intentId: body.intentId,\n email: intent.email,\n });\n throw new BadRequestError(\"Invalid or expired verification code\");\n });\n\n // If already verified, this is a code reuse attempt\n if (result.alreadyVerified) {\n this.log.warn(\"Verification code reuse attempt\", {\n intentId: body.intentId,\n email: intent.email,\n });\n throw new BadRequestError(\"Verification code has already been used\");\n }\n\n // Atomically delete cache key to prevent replay\n await this.intentCache.invalidate(body.intentId);\n\n // Hash the new password\n const hashedPassword = await this.cryptoProvider.hashPassword(\n body.newPassword,\n );\n\n // Update the identity with new password\n await this.identities(intent.realmName).updateById(intent.identityId, {\n password: hashedPassword,\n });\n\n // Invalidate all existing sessions for this user\n await this.sessions(intent.realmName).deleteMany({\n userId: { eq: intent.userId },\n });\n\n this.log.info(\"Password reset completed\", {\n userId: intent.userId,\n email: intent.email,\n });\n\n const realm = this.realmProvider.getRealm(intent.realmName);\n\n // Audit: password reset\n await this.auditService.recordUser(\"update\", {\n userId: intent.userId,\n userEmail: intent.email,\n userRealm: realm.name,\n resourceId: intent.userId,\n description: \"Password reset completed\",\n metadata: { email: intent.email },\n });\n\n // Audit: sessions invalidated (security event)\n await this.auditService.record(\"security\", \"sessions_invalidated\", {\n userId: intent.userId,\n userEmail: intent.email,\n userRealm: realm.name,\n resourceId: intent.userId,\n severity: \"warning\",\n description: \"All sessions invalidated after password reset\",\n });\n }\n\n // Legacy methods kept for backward compatibility\n\n /**\n * @deprecated Use createPasswordResetIntent instead\n */\n public async requestPasswordReset(\n email: string,\n userRealmName?: string,\n ): Promise<boolean> {\n await this.createPasswordResetIntent(email, userRealmName);\n return true;\n }\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public async validateResetToken(\n email: string,\n token: string,\n _userRealmName?: string,\n ): Promise<string> {\n // Verify using verification controller\n const isValid = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token },\n })\n .catch(() => undefined);\n\n if (!isValid?.ok) {\n throw new BadRequestError(\"Invalid or expired reset token\");\n }\n\n return email;\n }\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public async resetPassword(\n email: string,\n token: string,\n newPassword: string,\n userRealmName?: string,\n ): Promise<void> {\n // Verify token using verification controller\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token },\n })\n .catch(() => {\n throw new BadRequestError(\"Invalid or expired reset token\");\n });\n\n // If already verified, this is a token reuse attempt\n if (result.alreadyVerified) {\n throw new BadRequestError(\"Invalid or expired reset token\");\n }\n\n // Find user and identity\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\n\n const identity = await this.identities(userRealmName).findOne({\n where: {\n userId: { eq: user.id },\n provider: { eq: \"credentials\" },\n },\n });\n\n // Hash the new password\n const hashedPassword = await this.cryptoProvider.hashPassword(newPassword);\n\n // Update the identity with new password\n await this.identities(userRealmName).updateById(identity.id, {\n password: hashedPassword,\n });\n\n // Invalidate all existing sessions for this user\n await this.sessions(userRealmName).deleteMany({\n userId: { eq: user.id },\n });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n // Audit: password reset (legacy method)\n await this.auditService.recordUser(\"update\", {\n userId: user.id,\n userEmail: email,\n userRealm: realm.name,\n resourceId: user.id,\n description: \"Password reset completed (legacy)\",\n metadata: { email },\n });\n\n // Audit: sessions invalidated\n await this.auditService.record(\"security\", \"sessions_invalidated\", {\n userId: user.id,\n userEmail: email,\n userRealm: realm.name,\n resourceId: user.id,\n severity: \"warning\",\n description: \"All sessions invalidated after password reset\",\n });\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { $inject } from \"alepha\";\nimport { AuditService } from \"alepha/api/audits\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $cache } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { CryptoProvider } from \"alepha/security\";\nimport { BadRequestError, ConflictError, HttpError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { CompleteRegistrationRequest } from \"../schemas/completeRegistrationRequestSchema.ts\";\nimport type { RegisterRequest } from \"../schemas/registerRequestSchema.ts\";\nimport type { RegistrationIntentResponse } from \"../schemas/registrationIntentResponseSchema.ts\";\n\n/**\n * Intent stored in cache during the registration flow.\n */\ninterface RegistrationIntent {\n data: {\n username?: string;\n email?: string;\n phoneNumber?: string;\n firstName?: string;\n lastName?: string;\n picture?: string;\n passwordHash: string;\n };\n requirements: {\n email: boolean;\n phone: boolean;\n captcha: boolean;\n };\n realmName?: string;\n expiresAt: string;\n}\n\nconst INTENT_TTL_MINUTES = 10;\n\nexport class RegistrationService {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly verificationController = $client<VerificationController>();\n protected readonly userNotifications = $inject(UserNotifications);\n protected readonly realmProvider = $inject(RealmProvider);\n protected readonly auditService = $inject(AuditService);\n\n protected readonly intentCache = $cache<RegistrationIntent>({\n name: \"registration-intents\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n /**\n * Phase 1: Create a registration intent.\n *\n * Validates the registration data, checks for existing users,\n * creates verification sessions, and stores the intent in cache.\n */\n public async createRegistrationIntent(\n body: RegisterRequest,\n userRealmName?: string,\n ): Promise<RegistrationIntentResponse> {\n this.log.trace(\"Creating registration intent\", {\n email: body.email,\n username: body.username,\n userRealmName,\n });\n\n const realmSettings = this.realmProvider.getRealm(userRealmName).settings;\n\n // Check if registration is allowed\n if (realmSettings?.registrationAllowed === false) {\n this.log.warn(\"Registration not allowed for realm\", { userRealmName });\n throw new BadRequestError(\"Registration is not allowed\");\n }\n\n // Validate required fields based on settings\n if (realmSettings?.usernameRequired && !body.username) {\n this.log.debug(\"Registration rejected: username required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Username is required\");\n }\n\n if (body.username) {\n const usernameRegExp = realmSettings?.usernameRegExp;\n if (usernameRegExp) {\n const regex = new RegExp(usernameRegExp);\n if (!regex.test(body.username)) {\n this.log.debug(\"Registration rejected: username regex mismatch\", {\n userRealmName,\n username: body.username,\n });\n throw new BadRequestError(\n \"Username does not meet the required format\",\n );\n }\n }\n }\n\n if (realmSettings?.emailRequired !== false && !body.email) {\n this.log.debug(\"Registration rejected: email required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Email is required\");\n }\n\n if (realmSettings?.phoneRequired && !body.phoneNumber) {\n this.log.debug(\"Registration rejected: phone required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Phone number is required\");\n }\n\n // Check for existing users (username, email, phone)\n await this.checkUserAvailability(body, userRealmName);\n\n // Hash the password\n const passwordHash = await this.cryptoProvider.hashPassword(body.password);\n\n // Determine requirements based on realm settings\n const requirements = {\n email: realmSettings?.verifyEmailRequired === true && !!body.email,\n phone: realmSettings?.verifyPhoneRequired === true && !!body.phoneNumber,\n captcha: false, // Always false for now\n };\n\n // Create verification sessions and send codes\n if (requirements.email && body.email) {\n await this.sendEmailVerification(body.email);\n }\n\n if (requirements.phone && body.phoneNumber) {\n await this.sendPhoneVerification(body.phoneNumber);\n }\n\n // Generate intent ID and expiration\n const intentId = randomUUID();\n const expiresAt = this.dateTimeProvider\n .now()\n .add(INTENT_TTL_MINUTES, \"minutes\")\n .toISOString();\n\n // Store intent in cache\n const intent: RegistrationIntent = {\n data: {\n username: body.username,\n email: body.email,\n phoneNumber: body.phoneNumber,\n firstName: body.firstName,\n lastName: body.lastName,\n picture: body.picture,\n passwordHash,\n },\n requirements,\n realmName: userRealmName,\n expiresAt,\n };\n\n await this.intentCache.set(intentId, intent);\n\n this.log.info(\"Registration intent created\", {\n intentId,\n email: body.email,\n username: body.username,\n requiresEmailVerification: requirements.email,\n requiresPhoneVerification: requirements.phone,\n });\n\n return {\n intentId,\n expectCaptcha: requirements.captcha,\n expectEmailVerification: requirements.email,\n expectPhoneVerification: requirements.phone,\n expiresAt,\n };\n }\n\n /**\n * Phase 2: Complete registration using an intent.\n *\n * Validates all requirements (verification codes, captcha),\n * creates the user and credentials, and returns the user.\n */\n public async completeRegistration(\n body: CompleteRegistrationRequest,\n ): Promise<UserEntity> {\n this.log.trace(\"Completing registration\", { intentId: body.intentId });\n\n // Fetch intent from cache\n const intent = await this.intentCache.get(body.intentId);\n if (!intent) {\n this.log.warn(\"Invalid or expired registration intent\", {\n intentId: body.intentId,\n });\n throw new HttpError({\n status: 410,\n message: \"Invalid or expired registration intent\",\n });\n }\n\n const userRealmName = intent.realmName;\n const userRepository = this.realmProvider.userRepository(userRealmName);\n const identityRepository =\n this.realmProvider.identityRepository(userRealmName);\n\n // Validate email verification if required\n if (intent.requirements.email) {\n if (!body.emailCode) {\n this.log.debug(\"Registration completion missing email code\", {\n intentId: body.intentId,\n });\n throw new BadRequestError(\"Email verification code is required\");\n }\n\n if (!intent.data.email) {\n throw new BadRequestError(\"Email is missing from registration intent\");\n }\n\n await this.verifyEmailCode(intent.data.email, body.emailCode);\n }\n\n // Validate phone verification if required\n if (intent.requirements.phone) {\n if (!body.phoneCode) {\n this.log.debug(\"Registration completion missing phone code\", {\n intentId: body.intentId,\n });\n throw new BadRequestError(\"Phone verification code is required\");\n }\n\n if (!intent.data.phoneNumber) {\n throw new BadRequestError(\n \"Phone number is missing from registration intent\",\n );\n }\n\n await this.verifyPhoneCode(intent.data.phoneNumber, body.phoneCode);\n }\n\n // Validate captcha if required (placeholder for future implementation)\n if (intent.requirements.captcha) {\n if (!body.captchaToken) {\n throw new BadRequestError(\"Captcha verification is required\");\n }\n // TODO: Validate captcha token\n }\n\n // Final availability check (race condition guard)\n await this.checkUserAvailability(\n {\n username: intent.data.username,\n email: intent.data.email,\n phoneNumber: intent.data.phoneNumber,\n },\n userRealmName,\n );\n\n // Atomically delete cache key to prevent replay\n await this.intentCache.invalidate(body.intentId);\n\n // Create the user\n const user = await userRepository.create({\n realm: userRealmName,\n username: intent.data.username,\n email: intent.data.email,\n phoneNumber: intent.data.phoneNumber,\n firstName: intent.data.firstName,\n lastName: intent.data.lastName,\n picture: intent.data.picture,\n roles: [\"user\"],\n enabled: true,\n emailVerified: intent.requirements.email, // Marked as verified if we verified during registration\n });\n\n // Create credentials identity\n await identityRepository.create({\n userId: user.id,\n provider: \"credentials\",\n password: intent.data.passwordHash,\n });\n\n this.log.info(\"User registered successfully\", {\n userId: user.id,\n email: user.email,\n username: user.username,\n });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n await this.auditService.recordUser(\"create\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: \"User registered\",\n metadata: {\n username: user.username,\n email: user.email,\n emailVerified: user.emailVerified,\n registrationMethod: \"credentials\",\n },\n });\n\n return user;\n }\n\n /**\n * Check if username, email, and phone are available.\n */\n protected async checkUserAvailability(\n body: Pick<RegisterRequest, \"username\" | \"email\" | \"phoneNumber\">,\n userRealmName?: string,\n ): Promise<void> {\n const userRepository = this.realmProvider.userRepository(userRealmName);\n\n if (body.username) {\n const existingUser = await userRepository\n .findOne({ where: { username: { eq: body.username } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Username already taken\", { username: body.username });\n throw new ConflictError(\"User with this username already exists\");\n }\n }\n\n if (body.email) {\n const existingUser = await userRepository\n .findOne({ where: { email: { eq: body.email } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Email already taken\", { email: body.email });\n throw new ConflictError(\"User with this email already exists\");\n }\n }\n\n if (body.phoneNumber) {\n const existingUser = await userRepository\n .findOne({ where: { phoneNumber: { eq: body.phoneNumber } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Phone number already taken\", {\n phoneNumber: body.phoneNumber,\n });\n throw new ConflictError(\"User with this phone number already exists\");\n }\n }\n }\n\n /**\n * Send email verification code.\n */\n protected async sendEmailVerification(email: string): Promise<void> {\n this.log.debug(\"Sending email verification code\", { email });\n\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: email },\n });\n\n await this.userNotifications.emailVerification.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n this.log.debug(\"Email verification code sent\", { email });\n }\n\n /**\n * Send phone verification code.\n */\n protected async sendPhoneVerification(phoneNumber: string): Promise<void> {\n this.log.debug(\"Sending phone verification code\", { phoneNumber });\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: phoneNumber },\n });\n\n await this.userNotifications.phoneVerification.push({\n contact: phoneNumber,\n variables: {\n phoneNumber,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n this.log.debug(\"Phone verification code sent\", { phoneNumber });\n } catch (error) {\n // Silent fail - verification service may have rate limiting\n this.log.warn(\"Failed to send phone verification code\", {\n phoneNumber,\n error,\n });\n }\n }\n\n /**\n * Verify email code using verification service.\n */\n protected async verifyEmailCode(email: string, code: string): Promise<void> {\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token: code },\n })\n .catch(() => {\n this.log.warn(\"Invalid email verification code\", { email });\n throw new BadRequestError(\"Invalid or expired email verification code\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Email verification code already used\", { email });\n throw new BadRequestError(\n \"Email verification code has already been used\",\n );\n }\n }\n\n /**\n * Verify phone code using verification service.\n */\n protected async verifyPhoneCode(\n phoneNumber: string,\n code: string,\n ): Promise<void> {\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: phoneNumber, token: code },\n })\n .catch(() => {\n this.log.warn(\"Invalid phone verification code\", { phoneNumber });\n throw new BadRequestError(\"Invalid or expired phone verification code\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Phone verification code already used\", { phoneNumber });\n throw new BadRequestError(\n \"Phone verification code has already been used\",\n );\n }\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { completePasswordResetRequestSchema } from \"../schemas/completePasswordResetRequestSchema.ts\";\nimport { completeRegistrationRequestSchema } from \"../schemas/completeRegistrationRequestSchema.ts\";\nimport { passwordResetIntentResponseSchema } from \"../schemas/passwordResetIntentResponseSchema.ts\";\nimport { registerQuerySchema } from \"../schemas/registerQuerySchema.ts\";\nimport { registerRequestSchema } from \"../schemas/registerRequestSchema.ts\";\nimport { registrationIntentResponseSchema } from \"../schemas/registrationIntentResponseSchema.ts\";\nimport { userResourceSchema } from \"../schemas/userResourceSchema.ts\";\nimport { CredentialService } from \"../services/CredentialService.ts\";\nimport { RegistrationService } from \"../services/RegistrationService.ts\";\nimport { UserService } from \"../services/UserService.ts\";\n\nexport class UserController {\n protected readonly url = \"/users\";\n protected readonly group = \"users\";\n protected readonly credentialService = $inject(CredentialService);\n protected readonly userService = $inject(UserService);\n protected readonly registrationService = $inject(RegistrationService);\n\n /**\n * Phase 1: Create a registration intent.\n * Validates data, creates verification sessions, and stores intent in cache.\n */\n public readonly createRegistrationIntent = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/register`,\n secure: false,\n schema: {\n body: registerRequestSchema,\n query: registerQuerySchema,\n response: registrationIntentResponseSchema,\n },\n handler: ({ body, query }) =>\n this.registrationService.createRegistrationIntent(\n body,\n query.userRealmName,\n ),\n });\n\n /**\n * Phase 2: Complete registration using an intent.\n * Validates verification codes and creates the user.\n */\n public readonly createUserFromIntent = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/register/complete`,\n secure: false,\n schema: {\n body: completeRegistrationRequestSchema,\n response: userResourceSchema,\n },\n handler: ({ body }) => this.registrationService.completeRegistration(body),\n });\n\n /**\n * Phase 1: Create a password reset intent.\n * Validates email, sends verification code, and stores intent in cache.\n */\n public readonly createPasswordResetIntent = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/password-reset`,\n secure: false,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: passwordResetIntentResponseSchema,\n },\n handler: ({ body, query }) =>\n this.credentialService.createPasswordResetIntent(\n body.email,\n query.userRealmName,\n ),\n });\n\n /**\n * Phase 2: Complete password reset using an intent.\n * Validates verification code, updates password, and invalidates sessions.\n */\n public readonly completePasswordReset = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/password-reset/complete`,\n secure: false,\n schema: {\n body: completePasswordResetRequestSchema,\n response: okSchema,\n },\n handler: async ({ body }) => {\n await this.credentialService.completePasswordReset(body);\n return { ok: true };\n },\n });\n\n // Legacy endpoints for backward compatibility\n\n /**\n * @deprecated Use createPasswordResetIntent instead\n */\n public requestPasswordReset = $action({\n path: \"/users/password-reset/request\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.credentialService.requestPasswordReset(\n body.email,\n query.userRealmName,\n );\n\n return {\n success: true,\n message:\n \"If an account exists with this email, a password reset code has been sent.\",\n };\n },\n });\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public validateResetToken = $action({\n path: \"/users/password-reset/validate\",\n group: this.group,\n schema: {\n query: t.object({\n email: t.email(),\n token: t.string(),\n userRealmName: t.optional(t.string()),\n }),\n response: t.object({\n valid: t.boolean(),\n email: t.optional(t.email()),\n }),\n },\n handler: async ({ query }) => {\n try {\n const email = await this.credentialService.validateResetToken(\n query.email,\n query.token,\n query.userRealmName,\n );\n return {\n valid: true,\n email,\n };\n } catch {\n return {\n valid: false,\n };\n }\n },\n });\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public resetPassword = $action({\n path: \"/users/password-reset/reset\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n token: t.string(),\n newPassword: t.string({ minLength: 8 }),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.credentialService.resetPassword(\n body.email,\n body.token,\n body.newPassword,\n query.userRealmName,\n );\n\n return {\n success: true,\n message: \"Password has been reset successfully. Please log in.\",\n };\n },\n });\n\n /**\n * Request email verification.\n * Generates a verification token using verification service and sends an email to the user.\n * @param method - The verification method: \"code\" (default) sends a 6-digit code, \"link\" sends a clickable verification link.\n * @param verifyUrl - Required when method is \"link\". The base URL for the verification link. Token and email will be appended as query params.\n */\n public requestEmailVerification = $action({\n path: \"/users/email-verification/request\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n method: t.optional(\n t.enum([\"code\", \"link\"], {\n default: \"code\",\n description:\n 'Verification method: \"code\" sends a 6-digit code, \"link\" sends a clickable verification link.',\n }),\n ),\n verifyUrl: t.optional(\n t.string({\n description:\n 'Base URL for verification link. Required when method is \"link\". Token and email will be appended as query params.',\n }),\n ),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n const method = query.method ?? \"code\";\n await this.userService.requestEmailVerification(\n body.email,\n query.userRealmName,\n method,\n query.verifyUrl,\n );\n\n return {\n success: true,\n message:\n method === \"link\"\n ? \"If an account exists with this email, a verification link has been sent.\"\n : \"If an account exists with this email, a verification code has been sent.\",\n };\n },\n });\n\n /**\n * Verify email with a valid token.\n * Updates the user's emailVerified status.\n */\n public verifyEmail = $action({\n path: \"/users/email-verification/verify\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n token: t.string(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.userService.verifyEmail(\n body.email,\n body.token,\n query.userRealmName,\n );\n\n return {\n success: true,\n message: \"Email has been verified successfully.\",\n };\n },\n });\n\n /**\n * Check if an email is verified.\n */\n public checkEmailVerification = $action({\n path: \"/users/email-verification/check\",\n group: this.group,\n schema: {\n query: t.object({\n email: t.email(),\n userRealmName: t.optional(t.string()),\n }),\n response: t.object({\n verified: t.boolean(),\n }),\n },\n handler: async ({ query }) => {\n const verified = await this.userService.isEmailVerified(\n query.email,\n query.userRealmName,\n );\n\n return {\n verified,\n };\n },\n });\n}\n","import type { FileLike, StreamLike } from \"alepha\";\n\n/**\n * Options for creating a file from a URL\n */\nexport interface CreateFileFromUrlOptions {\n /**\n * The URL to load the file from (file://, http://, or https://)\n */\n url: string;\n /**\n * The MIME type of the file (optional, will be detected from filename if not provided)\n */\n type?: string;\n /**\n * The name of the file (optional, will be extracted from URL if not provided)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a path (URL with file:// scheme)\n */\nexport interface CreateFileFromPathOptions {\n /**\n * The path to the file on the local filesystem\n */\n path: string;\n /**\n * The MIME type of the file (optional, will be detected from filename if not provided)\n */\n type?: string;\n /**\n * The name of the file (optional, will be extracted from URL if not provided)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a Buffer\n */\nexport interface CreateFileFromBufferOptions {\n /**\n * The Buffer containing the file data\n */\n buffer: Buffer;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a stream\n */\nexport interface CreateFileFromStreamOptions {\n /**\n * The readable stream containing the file data\n */\n stream: StreamLike;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n /**\n * The size of the file in bytes (optional)\n */\n size?: number;\n}\n\n/**\n * Options for creating a file from text content\n */\nexport interface CreateFileFromTextOptions {\n /**\n * The text content to create the file from\n */\n text: string;\n /**\n * The MIME type of the file (default: text/plain)\n */\n type?: string;\n /**\n * The name of the file (default: \"file.txt\")\n */\n name?: string;\n}\n\nexport interface CreateFileFromResponseOptions {\n /**\n * The Response object containing the file data\n */\n response: Response;\n /**\n * Override the name (optional, uses filename from Content-Disposition header if not provided)\n */\n name?: string;\n /**\n * Override the MIME type (optional, uses file.type if not provided)\n */\n type?: string;\n}\n\n/**\n * Options for creating a file from a Web File object\n */\nexport interface CreateFileFromWebFileOptions {\n /**\n * The Web File object\n */\n file: File;\n /**\n * Override the MIME type (optional, uses file.type if not provided)\n */\n type?: string;\n /**\n * Override the name (optional, uses file.name if not provided)\n */\n name?: string;\n /**\n * Override the size (optional, uses file.size if not provided)\n */\n size?: number;\n}\n\n/**\n * Options for creating a file from an ArrayBuffer\n */\nexport interface CreateFileFromArrayBufferOptions {\n /**\n * The ArrayBuffer containing the file data\n */\n arrayBuffer: ArrayBuffer;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n}\n\n/**\n * Union type for all createFile options\n */\nexport type CreateFileOptions =\n | CreateFileFromUrlOptions\n | CreateFileFromPathOptions\n | CreateFileFromBufferOptions\n | CreateFileFromStreamOptions\n | CreateFileFromTextOptions\n | CreateFileFromWebFileOptions\n | CreateFileFromResponseOptions\n | CreateFileFromArrayBufferOptions;\n\n/**\n * Options for rm (remove) operation\n */\nexport interface RmOptions {\n /**\n * If true, removes directories and their contents recursively\n */\n recursive?: boolean;\n /**\n * If true, no error will be thrown if the path does not exist\n */\n force?: boolean;\n}\n\n/**\n * Options for cp (copy) operation\n */\nexport interface CpOptions {\n /**\n * If true, copy directories recursively\n */\n recursive?: boolean;\n /**\n * If true, overwrite existing destination\n */\n force?: boolean;\n}\n\n/**\n * Options for mkdir operation\n */\nexport interface MkdirOptions {\n /**\n * If true, creates parent directories as needed\n *\n * @default true\n */\n recursive?: boolean;\n /**\n * If true, does not throw an error if the directory already exists\n *\n * @default true\n */\n force?: boolean;\n /**\n * File mode (permission and sticky bits)\n */\n mode?: number;\n}\n\n/**\n * Options for ls (list) operation\n */\nexport interface LsOptions {\n /**\n * If true, list contents of directories recursively\n */\n recursive?: boolean;\n /**\n * If true, include hidden files (starting with .)\n */\n hidden?: boolean;\n}\n\n/**\n * FileSystem interface providing utilities for working with files.\n */\nexport abstract class FileSystemProvider {\n /**\n * Joins multiple path segments into a single path.\n *\n * @param paths - The path segments to join\n * @returns The joined path\n */\n abstract join(...paths: string[]): string;\n\n /**\n * Creates a FileLike object from various sources.\n *\n * @param options - Options for creating the file\n * @returns A FileLike object\n */\n abstract createFile(options: CreateFileOptions): FileLike;\n\n /**\n * Removes a file or directory.\n *\n * @param path - The path to remove\n * @param options - Remove options\n */\n abstract rm(path: string, options?: RmOptions): Promise<void>;\n\n /**\n * Copies a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n * @param options - Copy options\n */\n abstract cp(src: string, dest: string, options?: CpOptions): Promise<void>;\n\n /**\n * Moves/renames a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n */\n abstract mv(src: string, dest: string): Promise<void>;\n\n /**\n * Creates a directory.\n *\n * @param path - The directory path to create\n * @param options - Mkdir options\n */\n abstract mkdir(path: string, options?: MkdirOptions): Promise<void>;\n\n /**\n * Lists files in a directory.\n *\n * @param path - The directory path to list\n * @param options - List options\n * @returns Array of filenames\n */\n abstract ls(path: string, options?: LsOptions): Promise<string[]>;\n\n /**\n * Checks if a file or directory exists.\n *\n * @param path - The path to check\n * @returns True if the path exists, false otherwise\n */\n abstract exists(path: string): Promise<boolean>;\n\n /**\n * Reads the content of a file.\n *\n * @param path - The file path to read\n * @returns The file content as a Buffer\n */\n abstract readFile(path: string): Promise<Buffer>;\n\n /**\n * Writes data to a file.\n *\n * @param path - The file path to write to\n * @param data - The data to write (Buffer or string)\n */\n abstract writeFile(\n path: string,\n data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void>;\n\n /**\n * Reads the content of a file as a string.\n *\n * @param path - The file path to read\n * @returns The file content as a string\n */\n abstract readTextFile(path: string): Promise<string>;\n\n /**\n * Reads the content of a file as JSON.\n *\n * @param path - The file path to read\n * @returns The parsed JSON content\n */\n abstract readJsonFile<T = unknown>(path: string): Promise<T>;\n}\n","import { join as nodeJoin } from \"node:path\";\nimport { $inject, type FileLike, Json } from \"alepha\";\nimport type {\n CpOptions,\n CreateFileOptions,\n FileSystemProvider,\n LsOptions,\n MkdirOptions,\n RmOptions,\n} from \"./FileSystemProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface MemoryFileSystemProviderOptions {\n /**\n * Error to throw on mkdir operations (for testing error handling)\n */\n mkdirError?: Error | null;\n /**\n * Error to throw on writeFile operations (for testing error handling)\n */\n writeFileError?: Error | null;\n /**\n * Error to throw on readFile operations (for testing error handling)\n */\n readFileError?: Error | null;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * In-memory implementation of FileSystemProvider for testing.\n *\n * This provider stores all files and directories in memory, making it ideal for\n * unit tests that need to verify file operations without touching the real file system.\n *\n * @example\n * ```typescript\n * // In tests, substitute the real FileSystemProvider with MemoryFileSystemProvider\n * const alepha = Alepha.create().with({\n * provide: FileSystemProvider,\n * use: MemoryFileSystemProvider,\n * });\n *\n * // Run code that uses FileSystemProvider\n * const service = alepha.inject(MyService);\n * await service.saveFile(\"test.txt\", \"Hello World\");\n *\n * // Verify the file was written\n * const memoryFs = alepha.inject(MemoryFileSystemProvider);\n * expect(memoryFs.files.get(\"test.txt\")?.toString()).toBe(\"Hello World\");\n * ```\n */\nexport class MemoryFileSystemProvider implements FileSystemProvider {\n protected json = $inject(Json);\n\n /**\n * In-memory storage for files (path -> content)\n */\n public files = new Map<string, Buffer>();\n\n /**\n * In-memory storage for directories\n */\n public directories = new Set<string>();\n\n /**\n * Track mkdir calls for test assertions\n */\n public mkdirCalls: Array<{ path: string; options?: MkdirOptions }> = [];\n\n /**\n * Track writeFile calls for test assertions\n */\n public writeFileCalls: Array<{ path: string; data: string }> = [];\n\n /**\n * Track readFile calls for test assertions\n */\n public readFileCalls: Array<string> = [];\n\n /**\n * Track rm calls for test assertions\n */\n public rmCalls: Array<{ path: string; options?: RmOptions }> = [];\n\n /**\n * Track join calls for test assertions\n */\n public joinCalls: Array<string[]> = [];\n\n /**\n * Error to throw on mkdir (for testing error handling)\n */\n public mkdirError: Error | null = null;\n\n /**\n * Error to throw on writeFile (for testing error handling)\n */\n public writeFileError: Error | null = null;\n\n /**\n * Error to throw on readFile (for testing error handling)\n */\n public readFileError: Error | null = null;\n\n constructor(options: MemoryFileSystemProviderOptions = {}) {\n this.mkdirError = options.mkdirError ?? null;\n this.writeFileError = options.writeFileError ?? null;\n this.readFileError = options.readFileError ?? null;\n }\n\n /**\n * Join path segments using forward slashes.\n * Uses Node's path.join for proper normalization (handles .. and .)\n */\n public join(...paths: string[]): string {\n this.joinCalls.push(paths);\n return nodeJoin(...paths);\n }\n\n /**\n * Create a FileLike object from various sources.\n */\n public createFile(options: CreateFileOptions): FileLike {\n if (\"path\" in options) {\n const filePath = options.path;\n const buffer = this.files.get(filePath);\n if (buffer === undefined) {\n throw new Error(\n `ENOENT: no such file or directory, open '${filePath}'`,\n );\n }\n return {\n name: options.name ?? filePath.split(\"/\").pop() ?? \"file\",\n type: options.type ?? \"application/octet-stream\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Stream not implemented in MemoryFileSystemProvider\");\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => buffer.toString(\"utf-8\"),\n };\n }\n\n if (\"buffer\" in options) {\n const buffer = options.buffer;\n return {\n name: options.name ?? \"file\",\n type: options.type ?? \"application/octet-stream\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Stream not implemented in MemoryFileSystemProvider\");\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => buffer.toString(\"utf-8\"),\n };\n }\n\n if (\"text\" in options) {\n const buffer = Buffer.from(options.text, \"utf-8\");\n return {\n name: options.name ?? \"file.txt\",\n type: options.type ?? \"text/plain\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Stream not implemented in MemoryFileSystemProvider\");\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => options.text,\n };\n }\n\n throw new Error(\n \"MemoryFileSystemProvider.createFile: unsupported options. Only buffer and text are supported.\",\n );\n }\n\n /**\n * Remove a file or directory from memory.\n */\n public async rm(path: string, options?: RmOptions): Promise<void> {\n this.rmCalls.push({ path, options });\n\n const exists = this.files.has(path) || this.directories.has(path);\n\n if (!exists && !options?.force) {\n throw new Error(`ENOENT: no such file or directory, rm '${path}'`);\n }\n\n if (this.directories.has(path)) {\n if (options?.recursive) {\n // Remove directory and all contents\n this.directories.delete(path);\n for (const filePath of this.files.keys()) {\n if (filePath.startsWith(`${path}/`)) {\n this.files.delete(filePath);\n }\n }\n for (const dirPath of this.directories) {\n if (dirPath.startsWith(`${path}/`)) {\n this.directories.delete(dirPath);\n }\n }\n } else {\n throw new Error(\n `EISDIR: illegal operation on a directory, rm '${path}'`,\n );\n }\n } else {\n this.files.delete(path);\n }\n }\n\n /**\n * Copy a file or directory in memory.\n */\n public async cp(\n src: string,\n dest: string,\n options?: CpOptions,\n ): Promise<void> {\n if (this.directories.has(src)) {\n if (!options?.recursive) {\n throw new Error(\n `Cannot copy directory without recursive option: ${src}`,\n );\n }\n // Copy directory and contents\n this.directories.add(dest);\n for (const [filePath, content] of this.files) {\n if (filePath.startsWith(`${src}/`)) {\n const newPath = filePath.replace(src, dest);\n this.files.set(newPath, Buffer.from(content));\n }\n }\n } else if (this.files.has(src)) {\n const content = this.files.get(src)!;\n this.files.set(dest, Buffer.from(content));\n } else {\n throw new Error(`ENOENT: no such file or directory, cp '${src}'`);\n }\n }\n\n /**\n * Move/rename a file or directory in memory.\n */\n public async mv(src: string, dest: string): Promise<void> {\n if (this.directories.has(src)) {\n // Move directory and contents\n this.directories.delete(src);\n this.directories.add(dest);\n for (const [filePath, content] of this.files) {\n if (filePath.startsWith(`${src}/`)) {\n const newPath = filePath.replace(src, dest);\n this.files.delete(filePath);\n this.files.set(newPath, content);\n }\n }\n } else if (this.files.has(src)) {\n const content = this.files.get(src)!;\n this.files.delete(src);\n this.files.set(dest, content);\n } else {\n throw new Error(`ENOENT: no such file or directory, mv '${src}'`);\n }\n }\n\n /**\n * Create a directory in memory.\n */\n public async mkdir(path: string, options?: MkdirOptions): Promise<void> {\n this.mkdirCalls.push({ path, options });\n\n if (this.mkdirError) {\n throw this.mkdirError;\n }\n\n if (this.directories.has(path) && !options?.recursive) {\n throw new Error(`EEXIST: file already exists, mkdir '${path}'`);\n }\n\n this.directories.add(path);\n\n // If recursive, create parent directories\n if (options?.recursive) {\n const parts = path.split(\"/\").filter(Boolean);\n let current = \"\";\n for (const part of parts) {\n current = current ? `${current}/${part}` : part;\n this.directories.add(current);\n }\n }\n }\n\n /**\n * List files in a directory.\n */\n public async ls(path: string, options?: LsOptions): Promise<string[]> {\n const normalizedPath = path.replace(/\\/$/, \"\");\n const entries = new Set<string>();\n\n // Find files in the directory\n for (const filePath of this.files.keys()) {\n if (filePath.startsWith(`${normalizedPath}/`)) {\n const relativePath = filePath.slice(normalizedPath.length + 1);\n const parts = relativePath.split(\"/\");\n\n if (options?.recursive) {\n entries.add(relativePath);\n } else {\n entries.add(parts[0]);\n }\n }\n }\n\n // Find subdirectories\n for (const dirPath of this.directories) {\n if (\n dirPath.startsWith(`${normalizedPath}/`) &&\n dirPath !== normalizedPath\n ) {\n const relativePath = dirPath.slice(normalizedPath.length + 1);\n const parts = relativePath.split(\"/\");\n\n if (options?.recursive) {\n entries.add(relativePath);\n } else if (parts.length === 1) {\n entries.add(parts[0]);\n }\n }\n }\n\n let result = Array.from(entries);\n\n // Filter hidden files unless requested\n if (!options?.hidden) {\n result = result.filter((entry) => !entry.startsWith(\".\"));\n }\n\n return result.sort();\n }\n\n /**\n * Check if a file or directory exists in memory.\n */\n public async exists(path: string): Promise<boolean> {\n return this.files.has(path) || this.directories.has(path);\n }\n\n /**\n * Read a file from memory.\n */\n public async readFile(path: string): Promise<Buffer> {\n this.readFileCalls.push(path);\n\n if (this.readFileError) {\n throw this.readFileError;\n }\n\n const content = this.files.get(path);\n if (!content) {\n throw new Error(`ENOENT: no such file or directory, open '${path}'`);\n }\n return content;\n }\n\n /**\n * Read a file from memory as text.\n */\n public async readTextFile(path: string): Promise<string> {\n const buffer = await this.readFile(path);\n return buffer.toString(\"utf-8\");\n }\n\n /**\n * Read a file from memory as JSON.\n */\n public async readJsonFile<T = unknown>(path: string): Promise<T> {\n const text = await this.readTextFile(path);\n return this.json.parse(text) as T;\n }\n\n /**\n * Write a file to memory.\n */\n public async writeFile(\n path: string,\n data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void> {\n const dataStr =\n typeof data === \"string\"\n ? data\n : data instanceof Buffer || data instanceof Uint8Array\n ? data.toString(\"utf-8\")\n : await data.text();\n\n this.writeFileCalls.push({ path, data: dataStr });\n\n if (this.writeFileError) {\n throw this.writeFileError;\n }\n\n const buffer =\n typeof data === \"string\"\n ? Buffer.from(data, \"utf-8\")\n : data instanceof Buffer\n ? data\n : data instanceof Uint8Array\n ? Buffer.from(data)\n : Buffer.from(await data.text(), \"utf-8\");\n\n this.files.set(path, buffer);\n }\n\n /**\n * Reset all in-memory state (useful between tests).\n */\n public reset(): void {\n this.files.clear();\n this.directories.clear();\n this.mkdirCalls = [];\n this.writeFileCalls = [];\n this.readFileCalls = [];\n this.rmCalls = [];\n this.joinCalls = [];\n this.mkdirError = null;\n this.writeFileError = null;\n this.readFileError = null;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Test assertion helpers\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Check if a file was written during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasWritten(\"/project/tsconfig.json\")).toBe(true);\n * ```\n */\n public wasWritten(path: string): boolean {\n return this.writeFileCalls.some((call) => call.path === path);\n }\n\n /**\n * Check if a file was written with content matching a pattern.\n *\n * @example\n * ```typescript\n * expect(fs.wasWrittenMatching(\"/project/tsconfig.json\", /extends/)).toBe(true);\n * ```\n */\n public wasWrittenMatching(path: string, pattern: RegExp): boolean {\n const call = this.writeFileCalls.find((c) => c.path === path);\n return call ? pattern.test(call.data) : false;\n }\n\n /**\n * Check if a file was read during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasRead(\"/project/package.json\")).toBe(true);\n * ```\n */\n public wasRead(path: string): boolean {\n return this.readFileCalls.includes(path);\n }\n\n /**\n * Check if a file was deleted during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasDeleted(\"/project/old-file.txt\")).toBe(true);\n * ```\n */\n public wasDeleted(path: string): boolean {\n return this.rmCalls.some((call) => call.path === path);\n }\n\n /**\n * Get the content of a file as a string (convenience method for testing).\n */\n public getFileContent(path: string): string | undefined {\n return this.files.get(path)?.toString(\"utf-8\");\n }\n}\n","import type { ShellProvider, ShellRunOptions } from \"./ShellProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface MemoryShellCall {\n command: string;\n options: ShellRunOptions;\n}\n\nexport interface MemoryShellProviderOptions {\n /**\n * Simulated outputs for specific commands.\n * Key is the command string, value is the stdout to return.\n */\n outputs?: Record<string, string>;\n\n /**\n * Commands that should throw an error.\n * Key is the command string, value is the error message.\n */\n errors?: Record<string, string>;\n\n /**\n * Commands that are considered \"installed\" in the system PATH.\n */\n installedCommands?: string[];\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * In-memory implementation of ShellProvider for testing.\n *\n * Records all commands that would be executed without actually running them.\n * Can be configured to return specific outputs or throw errors for testing.\n *\n * @example\n * ```typescript\n * // In tests, substitute the real ShellProvider with MemoryShellProvider\n * const alepha = Alepha.create().with({\n * provide: ShellProvider,\n * use: MemoryShellProvider,\n * });\n *\n * // Configure mock behavior\n * const shell = alepha.inject(MemoryShellProvider);\n * shell.configure({\n * outputs: { \"echo hello\": \"hello\\n\" },\n * errors: { \"failing-cmd\": \"Command failed\" },\n * });\n *\n * // Or use the fluent API\n * shell.outputs.set(\"another-cmd\", \"output\");\n * shell.errors.set(\"another-error\", \"Error message\");\n *\n * // Run code that uses ShellProvider\n * const service = alepha.inject(MyService);\n * await service.doSomething();\n *\n * // Verify commands were called\n * expect(shell.calls).toHaveLength(2);\n * expect(shell.calls[0].command).toBe(\"yarn install\");\n * ```\n */\nexport class MemoryShellProvider implements ShellProvider {\n /**\n * All recorded shell calls.\n */\n public calls: MemoryShellCall[] = [];\n\n /**\n * Simulated outputs for specific commands.\n */\n public outputs = new Map<string, string>();\n\n /**\n * Commands that should throw an error.\n */\n public errors = new Map<string, string>();\n\n /**\n * Commands considered installed in the system PATH.\n */\n public installedCommands = new Set<string>();\n\n /**\n * Configure the mock with predefined outputs, errors, and installed commands.\n */\n public configure(options: MemoryShellProviderOptions): this {\n if (options.outputs) {\n for (const [cmd, output] of Object.entries(options.outputs)) {\n this.outputs.set(cmd, output);\n }\n }\n if (options.errors) {\n for (const [cmd, error] of Object.entries(options.errors)) {\n this.errors.set(cmd, error);\n }\n }\n if (options.installedCommands) {\n for (const cmd of options.installedCommands) {\n this.installedCommands.add(cmd);\n }\n }\n return this;\n }\n\n /**\n * Record command and return simulated output.\n */\n public async run(\n command: string,\n options: ShellRunOptions = {},\n ): Promise<string> {\n this.calls.push({ command, options });\n\n // Check for configured error\n const errorMsg = this.errors.get(command);\n if (errorMsg) {\n throw new Error(errorMsg);\n }\n\n // Return configured output or empty string\n return this.outputs.get(command) ?? \"\";\n }\n\n /**\n * Check if a specific command was called.\n */\n public wasCalled(command: string): boolean {\n return this.calls.some((call) => call.command === command);\n }\n\n /**\n * Check if a command matching a pattern was called.\n */\n public wasCalledMatching(pattern: RegExp): boolean {\n return this.calls.some((call) => pattern.test(call.command));\n }\n\n /**\n * Get all calls matching a pattern.\n */\n public getCallsMatching(pattern: RegExp): MemoryShellCall[] {\n return this.calls.filter((call) => pattern.test(call.command));\n }\n\n /**\n * Check if a command is installed.\n */\n public async isInstalled(command: string): Promise<boolean> {\n return this.installedCommands.has(command);\n }\n\n /**\n * Reset all recorded state.\n */\n public reset(): void {\n this.calls = [];\n this.outputs.clear();\n this.errors.clear();\n this.installedCommands.clear();\n }\n}\n","import { Readable } from \"node:stream\";\n\nexport interface FileTypeResult {\n /**\n * The detected MIME type\n */\n mimeType: string;\n /**\n * The detected file extension\n */\n extension: string;\n /**\n * Whether the file type was verified by magic bytes\n */\n verified: boolean;\n /**\n * The stream (potentially wrapped to allow re-reading)\n */\n stream: Readable;\n}\n\n/**\n * Service for detecting file types and getting content types.\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n *\n * // Get content type from filename\n * const mimeType = detector.getContentType(\"image.png\"); // \"image/png\"\n *\n * // Detect file type by magic bytes\n * const stream = createReadStream('image.png');\n * const result = await detector.detectFileType(stream, 'image.png');\n * console.log(result.mimeType); // 'image/png'\n * console.log(result.verified); // true if magic bytes match\n * ```\n */\nexport class FileDetector {\n /**\n * Magic byte signatures for common file formats.\n * Each signature is represented as an array of bytes or null (wildcard).\n */\n protected static readonly MAGIC_BYTES: Record<\n string,\n { signature: (number | null)[]; mimeType: string }[]\n > = {\n // Images\n png: [\n {\n signature: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],\n mimeType: \"image/png\",\n },\n ],\n jpg: [\n { signature: [0xff, 0xd8, 0xff, 0xe0], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe1], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe2], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe3], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe8], mimeType: \"image/jpeg\" },\n ],\n jpeg: [\n { signature: [0xff, 0xd8, 0xff, 0xe0], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe1], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe2], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe3], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe8], mimeType: \"image/jpeg\" },\n ],\n gif: [\n {\n signature: [0x47, 0x49, 0x46, 0x38, 0x37, 0x61],\n mimeType: \"image/gif\",\n }, // GIF87a\n {\n signature: [0x47, 0x49, 0x46, 0x38, 0x39, 0x61],\n mimeType: \"image/gif\",\n }, // GIF89a\n ],\n webp: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x57,\n 0x45,\n 0x42,\n 0x50,\n ],\n mimeType: \"image/webp\",\n },\n ],\n bmp: [{ signature: [0x42, 0x4d], mimeType: \"image/bmp\" }],\n ico: [{ signature: [0x00, 0x00, 0x01, 0x00], mimeType: \"image/x-icon\" }],\n tiff: [\n { signature: [0x49, 0x49, 0x2a, 0x00], mimeType: \"image/tiff\" }, // Little-endian\n { signature: [0x4d, 0x4d, 0x00, 0x2a], mimeType: \"image/tiff\" }, // Big-endian\n ],\n tif: [\n { signature: [0x49, 0x49, 0x2a, 0x00], mimeType: \"image/tiff\" },\n { signature: [0x4d, 0x4d, 0x00, 0x2a], mimeType: \"image/tiff\" },\n ],\n\n // Documents\n pdf: [\n {\n signature: [0x25, 0x50, 0x44, 0x46, 0x2d],\n mimeType: \"application/pdf\",\n },\n ], // %PDF-\n zip: [\n { signature: [0x50, 0x4b, 0x03, 0x04], mimeType: \"application/zip\" },\n { signature: [0x50, 0x4b, 0x05, 0x06], mimeType: \"application/zip\" },\n { signature: [0x50, 0x4b, 0x07, 0x08], mimeType: \"application/zip\" },\n ],\n\n // Archives\n rar: [\n {\n signature: [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07],\n mimeType: \"application/vnd.rar\",\n },\n ],\n \"7z\": [\n {\n signature: [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c],\n mimeType: \"application/x-7z-compressed\",\n },\n ],\n tar: [\n {\n signature: [0x75, 0x73, 0x74, 0x61, 0x72],\n mimeType: \"application/x-tar\",\n },\n ],\n gz: [{ signature: [0x1f, 0x8b], mimeType: \"application/gzip\" }],\n tgz: [{ signature: [0x1f, 0x8b], mimeType: \"application/gzip\" }],\n\n // Audio\n mp3: [\n { signature: [0xff, 0xfb], mimeType: \"audio/mpeg\" },\n { signature: [0xff, 0xf3], mimeType: \"audio/mpeg\" },\n { signature: [0xff, 0xf2], mimeType: \"audio/mpeg\" },\n { signature: [0x49, 0x44, 0x33], mimeType: \"audio/mpeg\" }, // ID3\n ],\n wav: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x57,\n 0x41,\n 0x56,\n 0x45,\n ],\n mimeType: \"audio/wav\",\n },\n ],\n ogg: [{ signature: [0x4f, 0x67, 0x67, 0x53], mimeType: \"audio/ogg\" }],\n flac: [{ signature: [0x66, 0x4c, 0x61, 0x43], mimeType: \"audio/flac\" }], // fLaC\n\n // Video\n mp4: [\n {\n signature: [null, null, null, null, 0x66, 0x74, 0x79, 0x70],\n mimeType: \"video/mp4\",\n }, // ftyp\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x69,\n 0x73,\n 0x6f,\n 0x6d,\n ],\n mimeType: \"video/mp4\",\n }, // ftypisom\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x6d,\n 0x70,\n 0x34,\n 0x32,\n ],\n mimeType: \"video/mp4\",\n }, // ftypmp42\n ],\n webm: [{ signature: [0x1a, 0x45, 0xdf, 0xa3], mimeType: \"video/webm\" }],\n avi: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x41,\n 0x56,\n 0x49,\n 0x20,\n ],\n mimeType: \"video/x-msvideo\",\n },\n ],\n mov: [\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x71,\n 0x74,\n 0x20,\n 0x20,\n ],\n mimeType: \"video/quicktime\",\n },\n ],\n mkv: [\n { signature: [0x1a, 0x45, 0xdf, 0xa3], mimeType: \"video/x-matroska\" },\n ],\n\n // Office (DOCX, XLSX, PPTX are all ZIP-based)\n docx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n },\n ],\n xlsx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n },\n ],\n pptx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n },\n ],\n doc: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/msword\",\n },\n ],\n xls: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/vnd.ms-excel\",\n },\n ],\n ppt: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/vnd.ms-powerpoint\",\n },\n ],\n };\n\n /**\n * All possible format signatures for checking against actual file content\n */\n protected static readonly ALL_SIGNATURES = Object.entries(\n FileDetector.MAGIC_BYTES,\n ).flatMap(([ext, signatures]) => signatures.map((sig) => ({ ext, ...sig })));\n\n /**\n * MIME type map for file extensions.\n *\n * Can be used to get the content type of file based on its extension.\n * Feel free to add more mime types in your project!\n */\n public static readonly mimeMap: Record<string, string> = {\n // Documents\n json: \"application/json\",\n txt: \"text/plain\",\n html: \"text/html\",\n htm: \"text/html\",\n xml: \"application/xml\",\n csv: \"text/csv\",\n pdf: \"application/pdf\",\n md: \"text/markdown\",\n markdown: \"text/markdown\",\n rtf: \"application/rtf\",\n\n // Styles and scripts\n css: \"text/css\",\n js: \"application/javascript\",\n mjs: \"application/javascript\",\n ts: \"application/typescript\",\n jsx: \"text/jsx\",\n tsx: \"text/tsx\",\n\n // Archives\n zip: \"application/zip\",\n rar: \"application/vnd.rar\",\n \"7z\": \"application/x-7z-compressed\",\n tar: \"application/x-tar\",\n gz: \"application/gzip\",\n tgz: \"application/gzip\",\n\n // Images\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n bmp: \"image/bmp\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n\n // Audio\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n ogg: \"audio/ogg\",\n m4a: \"audio/mp4\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n\n // Video\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n avi: \"video/x-msvideo\",\n mov: \"video/quicktime\",\n wmv: \"video/x-ms-wmv\",\n flv: \"video/x-flv\",\n mkv: \"video/x-matroska\",\n\n // Office\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n\n // Fonts\n woff: \"font/woff\",\n woff2: \"font/woff2\",\n ttf: \"font/ttf\",\n otf: \"font/otf\",\n eot: \"application/vnd.ms-fontobject\",\n };\n\n /**\n * Reverse MIME type map for looking up extensions from MIME types.\n * Prefers shorter, more common extensions when multiple exist.\n */\n protected static readonly reverseMimeMap: Record<string, string> = (() => {\n const reverse: Record<string, string> = {};\n // Process in order so common extensions come first\n for (const [ext, mimeType] of Object.entries(FileDetector.mimeMap)) {\n // Only set if not already set (prefer first/shorter extension)\n if (!reverse[mimeType]) {\n reverse[mimeType] = ext;\n }\n }\n return reverse;\n })();\n\n /**\n * Returns the file extension for a given MIME type.\n *\n * @param mimeType - The MIME type to look up\n * @returns The file extension (without dot), or \"bin\" if not found\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const ext = detector.getExtensionFromMimeType(\"image/png\"); // \"png\"\n * const ext2 = detector.getExtensionFromMimeType(\"application/octet-stream\"); // \"bin\"\n * ```\n */\n getExtensionFromMimeType(mimeType: string): string {\n return FileDetector.reverseMimeMap[mimeType] || \"bin\";\n }\n /**\n * Returns the content type of file based on its filename.\n *\n * @param filename - The filename to check\n * @returns The MIME type\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const mimeType = detector.getContentType(\"image.png\"); // \"image/png\"\n * ```\n */\n getContentType(filename: string): string {\n const ext = filename.toLowerCase().split(\".\").pop() || \"\";\n return FileDetector.mimeMap[ext] || \"application/octet-stream\";\n }\n\n /**\n * Detects the file type by checking magic bytes against the stream content.\n *\n * @param stream - The readable stream to check\n * @param filename - The filename (used to get the extension)\n * @returns File type information including MIME type, extension, and verification status\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const stream = createReadStream('image.png');\n * const result = await detector.detectFileType(stream, 'image.png');\n * console.log(result.mimeType); // 'image/png'\n * console.log(result.verified); // true if magic bytes match\n * ```\n */\n async detectFileType(\n stream: Readable,\n filename: string,\n ): Promise<FileTypeResult> {\n // Get the expected MIME type from the filename extension\n const expectedMimeType = this.getContentType(filename);\n\n // Extract extension - only if filename contains a dot\n const lastDotIndex = filename.lastIndexOf(\".\");\n const ext =\n lastDotIndex > 0\n ? filename.substring(lastDotIndex + 1).toLowerCase()\n : \"\";\n\n // Read the first 16 bytes (enough for most magic byte checks)\n const { buffer, stream: newStream } = await this.peekBytes(stream, 16);\n\n // First, check if the extension's expected signature matches\n const expectedSignatures = FileDetector.MAGIC_BYTES[ext];\n if (expectedSignatures) {\n for (const { signature, mimeType } of expectedSignatures) {\n if (this.matchesSignature(buffer, signature)) {\n return {\n mimeType,\n extension: ext,\n verified: true,\n stream: newStream,\n };\n }\n }\n }\n\n // If the expected signature didn't match, try all other signatures\n for (const {\n ext: detectedExt,\n signature,\n mimeType,\n } of FileDetector.ALL_SIGNATURES) {\n if (detectedExt !== ext && this.matchesSignature(buffer, signature)) {\n return {\n mimeType,\n extension: detectedExt,\n verified: true,\n stream: newStream,\n };\n }\n }\n\n // If no magic bytes matched, fall back to extension-based detection\n // or return binary if extension is not recognized\n return {\n mimeType: expectedMimeType,\n extension: ext,\n verified: false,\n stream: newStream,\n };\n }\n\n /**\n * Reads all bytes from a stream and returns the first N bytes along with a new stream containing all data.\n * This approach reads the entire stream upfront to avoid complex async handling issues.\n *\n * @protected\n */\n protected async peekBytes(\n stream: Readable,\n numBytes: number,\n ): Promise<{ buffer: Buffer; stream: Readable }> {\n const chunks: Buffer[] = [];\n\n // Read the entire stream\n for await (const chunk of stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n\n const allData = Buffer.concat(chunks);\n const buffer = allData.subarray(0, numBytes);\n\n // Create a new stream with all the data\n const newStream = Readable.from(allData);\n\n return { buffer, stream: newStream };\n }\n\n /**\n * Checks if a buffer matches a magic byte signature.\n *\n * @protected\n */\n protected matchesSignature(\n buffer: Buffer,\n signature: (number | null)[],\n ): boolean {\n if (buffer.length < signature.length) {\n return false;\n }\n\n for (let i = 0; i < signature.length; i++) {\n if (signature[i] !== null && buffer[i] !== signature[i]) {\n return false;\n }\n }\n\n return true;\n }\n}\n","import { createReadStream } from \"node:fs\";\nimport {\n access,\n copyFile,\n cp as fsCp,\n mkdir as fsMkdir,\n readFile as fsReadFile,\n rm as fsRm,\n writeFile as fsWriteFile,\n readdir,\n rename,\n stat,\n} from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { PassThrough, Readable } from \"node:stream\";\nimport type { ReadableStream as NodeWebStream } from \"node:stream/web\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n $inject,\n AlephaError,\n type FileLike,\n isFileLike,\n Json,\n type StreamLike,\n} from \"alepha\";\nimport { FileDetector } from \"../services/FileDetector.ts\";\nimport type {\n CpOptions,\n CreateFileOptions,\n FileSystemProvider,\n LsOptions,\n MkdirOptions,\n RmOptions,\n} from \"./FileSystemProvider.ts\";\n\n/**\n * Node.js implementation of FileSystem interface.\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Create from URL\n * const file1 = fs.createFile({ url: \"file:///path/to/file.png\" });\n *\n * // Create from Buffer\n * const file2 = fs.createFile({ buffer: Buffer.from(\"hello\"), name: \"hello.txt\" });\n *\n * // Create from text\n * const file3 = fs.createFile({ text: \"Hello, world!\", name: \"greeting.txt\" });\n *\n * // File operations\n * await fs.mkdir(\"/tmp/mydir\", { recursive: true });\n * await fs.cp(\"/src/file.txt\", \"/dest/file.txt\");\n * await fs.mv(\"/old/path.txt\", \"/new/path.txt\");\n * const files = await fs.ls(\"/tmp\");\n * await fs.rm(\"/tmp/file.txt\");\n * ```\n */\nexport class NodeFileSystemProvider implements FileSystemProvider {\n protected detector = $inject(FileDetector);\n protected json = $inject(Json);\n\n public join(...paths: string[]): string {\n return join(...paths);\n }\n\n /**\n * Creates a FileLike object from various sources.\n *\n * @param options - Options for creating the file\n * @returns A FileLike object\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // From URL\n * const file1 = fs.createFile({ url: \"https://example.com/image.png\" });\n *\n * // From Buffer\n * const file2 = fs.createFile({\n * buffer: Buffer.from(\"hello\"),\n * name: \"hello.txt\",\n * type: \"text/plain\"\n * });\n *\n * // From text\n * const file3 = fs.createFile({ text: \"Hello!\", name: \"greeting.txt\" });\n *\n * // From stream with detection\n * const stream = createReadStream(\"/path/to/file.png\");\n * const file4 = fs.createFile({ stream, name: \"image.png\" });\n * ```\n */\n createFile(options: CreateFileOptions): FileLike {\n if (\"path\" in options) {\n const path = options.path;\n const filename = path.split(\"/\").pop() || \"file\";\n return this.createFileFromUrl(`file://${path}`, {\n type: options.type,\n name: options.name || filename,\n });\n }\n\n // Handle URL\n if (\"url\" in options) {\n return this.createFileFromUrl(options.url, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"response\" in options) {\n if (!options.response.body) {\n throw new AlephaError(\"Response has no body stream\");\n }\n const res = options.response;\n // guess size from content-length header if available\n const sizeHeader = res.headers.get(\"content-length\");\n const size = sizeHeader ? parseInt(sizeHeader, 10) : undefined;\n // guess name from content-disposition header if available\n let name = options.name;\n const contentDisposition = res.headers.get(\"content-disposition\");\n if (contentDisposition && !name) {\n const match = contentDisposition.match(/filename=\"?([^\"]+)\"?/);\n if (match) {\n name = match[1];\n }\n }\n // guess type from content-type header if available\n const type = options.type || res.headers.get(\"content-type\") || undefined;\n return this.createFileFromStream(options.response.body, {\n type,\n name,\n size,\n });\n }\n\n // Handle Web File\n if (\"file\" in options) {\n return this.createFileFromWebFile(options.file, {\n type: options.type,\n name: options.name,\n size: options.size,\n });\n }\n\n // Handle Buffer\n if (\"buffer\" in options) {\n return this.createFileFromBuffer(options.buffer, {\n type: options.type,\n name: options.name,\n });\n }\n\n // Handle ArrayBuffer\n if (\"arrayBuffer\" in options) {\n return this.createFileFromBuffer(Buffer.from(options.arrayBuffer), {\n type: options.type,\n name: options.name,\n });\n }\n\n // Handle text\n if (\"text\" in options) {\n return this.createFileFromBuffer(Buffer.from(options.text, \"utf-8\"), {\n type: options.type || \"text/plain\",\n name: options.name || \"file.txt\",\n });\n }\n\n // Handle stream\n if (\"stream\" in options) {\n return this.createFileFromStream(options.stream, {\n type: options.type,\n name: options.name,\n size: options.size,\n });\n }\n\n throw new AlephaError(\n \"Invalid createFile options: no valid source provided\",\n );\n }\n\n /**\n * Removes a file or directory.\n *\n * @param path - The path to remove\n * @param options - Remove options\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Remove a file\n * await fs.rm(\"/tmp/file.txt\");\n *\n * // Remove a directory recursively\n * await fs.rm(\"/tmp/mydir\", { recursive: true });\n *\n * // Remove with force (no error if doesn't exist)\n * await fs.rm(\"/tmp/maybe-exists.txt\", { force: true });\n * ```\n */\n async rm(path: string, options?: RmOptions): Promise<void> {\n await fsRm(path, options);\n }\n\n /**\n * Copies a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n * @param options - Copy options\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Copy a file\n * await fs.cp(\"/src/file.txt\", \"/dest/file.txt\");\n *\n * // Copy a directory recursively\n * await fs.cp(\"/src/dir\", \"/dest/dir\", { recursive: true });\n *\n * // Copy with force (overwrite existing)\n * await fs.cp(\"/src/file.txt\", \"/dest/file.txt\", { force: true });\n * ```\n */\n async cp(src: string, dest: string, options?: CpOptions): Promise<void> {\n // Check if source is a directory\n const srcStat = await stat(src);\n\n if (srcStat.isDirectory()) {\n if (!options?.recursive) {\n throw new Error(\n `Cannot copy directory without recursive option: ${src}`,\n );\n }\n // Use Node.js cp function for recursive directory copy\n await fsCp(src, dest, {\n recursive: true,\n force: options?.force ?? false,\n });\n } else {\n // For files, use copyFile\n await copyFile(src, dest);\n }\n }\n\n /**\n * Moves/renames a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Move/rename a file\n * await fs.mv(\"/old/path.txt\", \"/new/path.txt\");\n *\n * // Move a directory\n * await fs.mv(\"/old/dir\", \"/new/dir\");\n * ```\n */\n async mv(src: string, dest: string): Promise<void> {\n await rename(src, dest);\n }\n\n /**\n * Creates a directory.\n *\n * @param path - The directory path to create\n * @param options - Mkdir options\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Create a directory\n * await fs.mkdir(\"/tmp/mydir\");\n *\n * // Create nested directories\n * await fs.mkdir(\"/tmp/path/to/dir\", { recursive: true });\n *\n * // Create with specific permissions\n * await fs.mkdir(\"/tmp/mydir\", { mode: 0o755 });\n * ```\n */\n async mkdir(path: string, options: MkdirOptions = {}): Promise<void> {\n const p = fsMkdir(path, {\n recursive: options.recursive ?? true,\n mode: options.mode,\n });\n\n if (options.force === false) {\n await p;\n } else {\n await p.catch(() => {});\n }\n }\n\n /**\n * Lists files in a directory.\n *\n * @param path - The directory path to list\n * @param options - List options\n * @returns Array of filenames\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // List files in a directory\n * const files = await fs.ls(\"/tmp\");\n * console.log(files); // [\"file1.txt\", \"file2.txt\", \"subdir\"]\n *\n * // List with hidden files\n * const allFiles = await fs.ls(\"/tmp\", { hidden: true });\n *\n * // List recursively\n * const allFilesRecursive = await fs.ls(\"/tmp\", { recursive: true });\n * ```\n */\n async ls(path: string, options?: LsOptions): Promise<string[]> {\n const entries = await readdir(path);\n\n // Filter out hidden files if not requested\n const filteredEntries = options?.hidden\n ? entries\n : entries.filter((e) => !e.startsWith(\".\"));\n\n // If recursive, get all nested files\n if (options?.recursive) {\n const allFiles: string[] = [];\n\n for (const entry of filteredEntries) {\n const fullPath = join(path, entry);\n const entryStat = await stat(fullPath);\n\n if (entryStat.isDirectory()) {\n // Add directory entry\n allFiles.push(entry);\n // Recursively get files from subdirectory\n const subFiles = await this.ls(fullPath, options);\n allFiles.push(...subFiles.map((f) => join(entry, f)));\n } else {\n allFiles.push(entry);\n }\n }\n\n return allFiles;\n }\n\n return filteredEntries;\n }\n\n /**\n * Checks if a file or directory exists.\n *\n * @param path - The path to check\n * @returns True if the path exists, false otherwise\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * if (await fs.exists(\"/tmp/file.txt\")) {\n * console.log(\"File exists\");\n * }\n * ```\n */\n async exists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Reads the content of a file.\n *\n * @param path - The file path to read\n * @returns The file content as a Buffer\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * const buffer = await fs.readFile(\"/tmp/file.txt\");\n * console.log(buffer.toString(\"utf-8\"));\n * ```\n */\n async readFile(path: string): Promise<Buffer> {\n return await fsReadFile(path);\n }\n\n /**\n * Writes data to a file.\n *\n * @param path - The file path to write to\n * @param data - The data to write (Buffer or string)\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Write string\n * await fs.writeFile(\"/tmp/file.txt\", \"Hello, world!\");\n *\n * // Write Buffer\n * await fs.writeFile(\"/tmp/file.bin\", Buffer.from([0x01, 0x02, 0x03]));\n * ```\n */\n async writeFile(\n path: string,\n data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void> {\n if (isFileLike(data)) {\n await fsWriteFile(path, Readable.from(data.stream()));\n return;\n }\n await fsWriteFile(path, data);\n }\n\n /**\n * Reads the content of a file as a string.\n *\n * @param path - The file path to read\n * @returns The file content as a string\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n * const content = await fs.readTextFile(\"/tmp/file.txt\");\n * ```\n */\n async readTextFile(path: string): Promise<string> {\n const buffer = await this.readFile(path);\n return buffer.toString(\"utf-8\");\n }\n\n /**\n * Reads the content of a file as JSON.\n *\n * @param path - The file path to read\n * @returns The parsed JSON content\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n * const config = await fs.readJsonFile<{ name: string }>(\"/tmp/config.json\");\n * ```\n */\n async readJsonFile<T = unknown>(path: string): Promise<T> {\n const text = await this.readTextFile(path);\n return this.json.parse(text) as T;\n }\n\n /**\n * Creates a FileLike object from a Web File.\n *\n * @protected\n */\n protected createFileFromWebFile(\n source: File,\n options: {\n type?: string;\n name?: string;\n size?: number;\n } = {},\n ): FileLike {\n const name = options.name ?? source.name;\n return {\n name,\n type: options.type ?? (source.type || this.detector.getContentType(name)),\n size: options.size ?? source.size ?? 0,\n lastModified: source.lastModified || Date.now(),\n stream: () => source.stream(),\n arrayBuffer: async (): Promise<ArrayBuffer> => {\n return await source.arrayBuffer();\n },\n text: async (): Promise<string> => {\n return await source.text();\n },\n };\n }\n\n /**\n * Creates a FileLike object from a Buffer.\n *\n * @protected\n */\n protected createFileFromBuffer(\n source: Buffer,\n options: {\n type?: string;\n name?: string;\n } = {},\n ): FileLike {\n const name: string = options.name ?? \"file\";\n return {\n name,\n type: options.type ?? this.detector.getContentType(options.name ?? name),\n size: source.byteLength,\n lastModified: Date.now(),\n stream: (): Readable => Readable.from(source),\n arrayBuffer: async (): Promise<ArrayBuffer> => {\n return this.bufferToArrayBuffer(source);\n },\n text: async (): Promise<string> => {\n return source.toString(\"utf-8\");\n },\n };\n }\n\n /**\n * Creates a FileLike object from a stream.\n *\n * @protected\n */\n protected createFileFromStream(\n source: StreamLike,\n options: {\n type?: string;\n name?: string;\n size?: number;\n } = {},\n ): FileLike & { _buffer: null | Buffer } {\n let buffer: Buffer | null = null;\n\n return {\n name: options.name ?? \"file\",\n type:\n options.type ?? this.detector.getContentType(options.name ?? \"file\"),\n size: options.size ?? 0,\n lastModified: Date.now(),\n stream: () => source,\n _buffer: null as Buffer | null,\n arrayBuffer: async () => {\n buffer ??= await this.streamToBuffer(source);\n return this.bufferToArrayBuffer(buffer);\n },\n text: async () => {\n buffer ??= await this.streamToBuffer(source);\n return buffer.toString(\"utf-8\");\n },\n };\n }\n\n /**\n * Creates a FileLike object from a URL.\n *\n * @protected\n */\n protected createFileFromUrl(\n url: string,\n options: {\n type?: string;\n name?: string;\n } = {},\n ): FileLike {\n const parsedUrl = new URL(url);\n const filename =\n options.name || parsedUrl.pathname.split(\"/\").pop() || \"file\";\n let buffer: Buffer | null = null;\n\n return {\n name: filename,\n type: options.type ?? this.detector.getContentType(filename),\n size: 0, // Unknown size until loaded\n lastModified: Date.now(),\n stream: () => this.createStreamFromUrl(url),\n arrayBuffer: async () => {\n buffer ??= await this.loadFromUrl(url);\n return this.bufferToArrayBuffer(buffer);\n },\n text: async () => {\n buffer ??= await this.loadFromUrl(url);\n return buffer.toString(\"utf-8\");\n },\n filepath: url,\n };\n }\n\n /**\n * Gets a streaming response from a URL.\n *\n * @protected\n */\n protected getStreamingResponse(url: string): Readable {\n const stream = new PassThrough();\n\n fetch(url)\n .then((res) =>\n Readable.fromWeb(res.body as unknown as NodeWebStream).pipe(stream),\n )\n .catch((err) => stream.destroy(err));\n\n return stream;\n }\n\n /**\n * Loads data from a URL.\n *\n * @protected\n */\n protected async loadFromUrl(url: string): Promise<Buffer> {\n const parsedUrl = new URL(url);\n\n if (parsedUrl.protocol === \"file:\") {\n // Handle file:// URLs\n const filePath = fileURLToPath(url);\n return await fsReadFile(filePath);\n } else if (\n parsedUrl.protocol === \"http:\" ||\n parsedUrl.protocol === \"https:\"\n ) {\n // Handle HTTP/HTTPS URLs\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch ${url}: ${response.status} ${response.statusText}`,\n );\n }\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n } else {\n throw new Error(`Unsupported protocol: ${parsedUrl.protocol}`);\n }\n }\n\n /**\n * Creates a stream from a URL.\n *\n * @protected\n */\n protected createStreamFromUrl(url: string): Readable {\n const parsedUrl = new URL(url);\n\n if (parsedUrl.protocol === \"file:\") {\n // For file:// URLs, create a stream that reads the file\n return createReadStream(fileURLToPath(url));\n } else if (\n parsedUrl.protocol === \"http:\" ||\n parsedUrl.protocol === \"https:\"\n ) {\n // For HTTP/HTTPS URLs, create a stream that fetches the content\n return this.getStreamingResponse(url);\n } else {\n throw new AlephaError(`Unsupported protocol: ${parsedUrl.protocol}`);\n }\n }\n\n /**\n * Converts a stream-like object to a Buffer.\n *\n * @protected\n */\n protected async streamToBuffer(streamLike: StreamLike): Promise<Buffer> {\n const stream =\n streamLike instanceof Readable\n ? streamLike\n : Readable.fromWeb(streamLike as NodeWebStream);\n\n return new Promise<Buffer>((resolve, reject) => {\n const buffer: any[] = [];\n stream.on(\"data\", (chunk) => buffer.push(Buffer.from(chunk)));\n stream.on(\"end\", () => resolve(Buffer.concat(buffer)));\n stream.on(\"error\", (err) =>\n reject(new AlephaError(\"Error converting stream\", { cause: err })),\n );\n });\n }\n\n /**\n * Converts a Node.js Buffer to an ArrayBuffer.\n *\n * @protected\n */\n protected bufferToArrayBuffer(buffer: Buffer): ArrayBuffer {\n return buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer;\n }\n}\n","import { exec, spawn } from \"node:child_process\";\nimport { $inject, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { FileSystemProvider } from \"./FileSystemProvider.ts\";\nimport type { ShellProvider, ShellRunOptions } from \"./ShellProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Node.js implementation of ShellProvider.\n *\n * Executes shell commands using Node.js child_process module.\n * Supports binary resolution from node_modules/.bin for local packages.\n */\nexport class NodeShellProvider implements ShellProvider {\n protected readonly log = $logger();\n protected readonly fs = $inject(FileSystemProvider);\n\n /**\n * Run a shell command or binary.\n */\n public async run(\n command: string,\n options: ShellRunOptions = {},\n ): Promise<string> {\n const { resolve = false, capture = false, root, env } = options;\n const cwd = root ?? process.cwd();\n\n this.log.debug(`Shell: ${command}`, { cwd, resolve, capture });\n\n let executable: string;\n let args: string[];\n\n if (resolve) {\n const [bin, ...rest] = command.split(\" \");\n executable = await this.resolveExecutable(bin, cwd);\n args = rest;\n } else {\n [executable, ...args] = command.split(\" \");\n }\n\n if (capture) {\n return this.execCapture(command, { cwd, env });\n }\n\n return this.execInherit(executable, args, { cwd, env });\n }\n\n /**\n * Execute command with inherited stdio (streams to terminal).\n */\n protected async execInherit(\n executable: string,\n args: string[],\n options: { cwd: string; env?: Record<string, string> },\n ): Promise<string> {\n const proc = spawn(executable, args, {\n stdio: \"inherit\",\n cwd: options.cwd,\n env: {\n ...process.env,\n ...options.env,\n },\n });\n\n return new Promise<string>((resolve, reject) => {\n proc.on(\"exit\", (code) => {\n if (code === 0 || code === null) {\n resolve(\"\");\n } else {\n reject(new AlephaError(`Command exited with code ${code}`));\n }\n });\n proc.on(\"error\", reject);\n });\n }\n\n /**\n * Execute command and capture stdout.\n */\n protected execCapture(\n command: string,\n options: { cwd: string; env?: Record<string, string> },\n ): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n exec(\n command,\n {\n cwd: options.cwd,\n env: {\n ...process.env,\n LOG_FORMAT: \"pretty\",\n ...options.env,\n },\n },\n (err, stdout) => {\n if (err) {\n (err as any).stdout = stdout;\n reject(err);\n } else {\n resolve(stdout);\n }\n },\n );\n });\n }\n\n /**\n * Resolve executable path from node_modules/.bin.\n *\n * Search order:\n * 1. Local: node_modules/.bin/\n * 2. Pnpm nested: node_modules/alepha/node_modules/.bin/\n * 3. Monorepo: Walk up to 3 parent directories\n */\n protected async resolveExecutable(\n name: string,\n root: string,\n ): Promise<string> {\n const suffix = process.platform === \"win32\" ? \".cmd\" : \"\";\n\n // 1. Local node_modules\n let execPath = await this.findExecutable(\n root,\n `node_modules/.bin/${name}${suffix}`,\n );\n\n // 2. Pnpm nested (alepha's own node_modules)\n if (!execPath) {\n execPath = await this.findExecutable(\n root,\n `node_modules/alepha/node_modules/.bin/${name}${suffix}`,\n );\n }\n\n // 3. Monorepo: check parent directories (up to 3 levels)\n if (!execPath) {\n let parentDir = this.fs.join(root, \"..\");\n for (let i = 0; i < 3; i++) {\n execPath = await this.findExecutable(\n parentDir,\n `node_modules/.bin/${name}${suffix}`,\n );\n if (execPath) break;\n parentDir = this.fs.join(parentDir, \"..\");\n }\n }\n\n if (!execPath) {\n throw new AlephaError(\n `Could not find executable for '${name}'. Make sure the package is installed.`,\n );\n }\n\n return execPath;\n }\n\n /**\n * Check if executable exists at path.\n */\n protected async findExecutable(\n root: string,\n relativePath: string,\n ): Promise<string | undefined> {\n const fullPath = this.fs.join(root, relativePath);\n if (await this.fs.exists(fullPath)) {\n return fullPath;\n }\n return undefined;\n }\n\n /**\n * Check if a command is installed and available in the system PATH.\n */\n public isInstalled(command: string): Promise<boolean> {\n return new Promise((resolve) => {\n const check =\n process.platform === \"win32\"\n ? `where ${command}`\n : `command -v ${command}`;\n exec(check, (error) => resolve(!error));\n });\n }\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ShellRunOptions {\n /**\n * Working directory for the command.\n */\n root?: string;\n\n /**\n * Additional environment variables.\n */\n env?: Record<string, string>;\n\n /**\n * Resolve the executable from node_modules/.bin.\n * Supports local project, pnpm nested, and monorepo structures.\n * @default false\n */\n resolve?: boolean;\n\n /**\n * Capture stdout instead of inheriting stdio.\n * When true, returns stdout as string.\n * When false, streams output to terminal.\n * @default false\n */\n capture?: boolean;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Abstract provider for executing shell commands and binaries.\n *\n * Implementations:\n * - `NodeShellProvider` - Real shell execution using Node.js child_process\n * - `MemoryShellProvider` - In-memory mock for testing\n *\n * @example\n * ```typescript\n * class MyService {\n * protected readonly shell = $inject(ShellProvider);\n *\n * async build() {\n * // Run shell command directly\n * await this.shell.run(\"yarn install\");\n *\n * // Run local binary with resolution\n * await this.shell.run(\"vite build\", { resolve: true });\n *\n * // Capture output\n * const output = await this.shell.run(\"echo hello\", { capture: true });\n * }\n * }\n * ```\n */\nexport abstract class ShellProvider {\n /**\n * Run a shell command or binary.\n *\n * @param command - The command to run\n * @param options - Execution options\n * @returns stdout if capture is true, empty string otherwise\n */\n abstract run(command: string, options?: ShellRunOptions): Promise<string>;\n\n /**\n * Check if a command is installed and available in the system PATH.\n *\n * @param command - The command name to check\n * @returns true if the command is available\n */\n abstract isInstalled(command: string): Promise<boolean>;\n}\n","import { $module } from \"alepha\";\nimport { FileSystemProvider } from \"./providers/FileSystemProvider.ts\";\nimport { MemoryFileSystemProvider } from \"./providers/MemoryFileSystemProvider.ts\";\nimport { MemoryShellProvider } from \"./providers/MemoryShellProvider.ts\";\nimport { NodeFileSystemProvider } from \"./providers/NodeFileSystemProvider.ts\";\nimport { NodeShellProvider } from \"./providers/NodeShellProvider.ts\";\nimport { ShellProvider } from \"./providers/ShellProvider.ts\";\nimport { FileDetector } from \"./services/FileDetector.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/FileError.ts\";\nexport * from \"./providers/FileSystemProvider.ts\";\nexport * from \"./providers/MemoryFileSystemProvider.ts\";\nexport * from \"./providers/MemoryShellProvider.ts\";\nexport * from \"./providers/NodeFileSystemProvider.ts\";\nexport * from \"./providers/NodeShellProvider.ts\";\nexport * from \"./providers/ShellProvider.ts\";\nexport * from \"./services/FileDetector.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | type | quality | stability |\n * |------|---------|-----------|\n * | tooling | standard | stable |\n *\n * System-level abstractions for portable code across runtimes.\n *\n * **Features:**\n * - File system operations (read, write, exists, etc.)\n * - Shell command execution\n * - File type detection and MIME utilities\n * - Memory implementations for testing\n *\n * @module alepha.system\n */\nexport const AlephaSystem = $module({\n name: \"alepha.system\",\n primitives: [],\n services: [\n FileDetector,\n FileSystemProvider,\n MemoryFileSystemProvider,\n NodeFileSystemProvider,\n ShellProvider,\n MemoryShellProvider,\n NodeShellProvider,\n ],\n register: (alepha) =>\n alepha\n .with({\n optional: true,\n provide: FileSystemProvider,\n use: NodeFileSystemProvider,\n })\n .with({\n optional: true,\n provide: ShellProvider,\n use: alepha.isTest() ? MemoryShellProvider : NodeShellProvider,\n }),\n});\n","import { randomInt } from \"node:crypto\";\nimport { $inject, Alepha } from \"alepha\";\nimport { AuditService } from \"alepha/api/audits\";\nimport type { FileController } from \"alepha/api/files\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport {\n CryptoProvider,\n InvalidCredentialsError,\n type UserAccount,\n} from \"alepha/security\";\nimport { type ServerRequest, UnauthorizedError } from \"alepha/server\";\nimport type { OAuth2Profile } from \"alepha/server/auth\";\nimport { $client } from \"alepha/server/links\";\nimport { FileSystemProvider } from \"alepha/system\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\n\nexport class SessionService {\n protected readonly alepha = $inject(Alepha);\n protected readonly fsp = $inject(FileSystemProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly log = $logger();\n protected readonly realmProvider = $inject(RealmProvider);\n protected readonly fileController = $client<FileController>();\n protected readonly auditService = $inject(AuditService);\n\n public users(userRealmName?: string) {\n return this.realmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.realmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Random delay to prevent timing attacks (50-200ms)\n * Uses cryptographically secure random number generation\n */\n protected randomDelay(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, randomInt(50, 201)));\n }\n\n /**\n * Validate user credentials and return the user if valid.\n */\n public async login(\n provider: string,\n username: string,\n password: string,\n userRealmName?: string,\n ): Promise<UserEntity> {\n const { settings, name } = this.realmProvider.getRealm(userRealmName);\n const isEmail = username.includes(\"@\");\n const isPhone = /^[+\\d][\\d\\s()-]+$/.test(username);\n const isUsername = !isEmail && !isPhone;\n const identities = this.identities(userRealmName);\n const users = this.users(userRealmName);\n\n await this.randomDelay();\n\n try {\n const where = users.createQueryWhere();\n\n where.realm = name;\n\n if (settings.usernameEnabled !== false && isUsername) {\n // validate username format if regex is provided\n if (settings.usernameRegExp) {\n const regex = new RegExp(settings.usernameRegExp);\n if (!regex.test(username)) {\n this.log.warn(\"Username does not match required format\", {\n provider,\n username,\n realm: name,\n });\n\n await this.auditService.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"Username does not match required format\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n }\n where.username = username;\n } else if (settings.emailEnabled !== false && isEmail) {\n where.email = username;\n } else if (settings.phoneEnabled === true && isPhone) {\n where.phoneNumber = username;\n } else {\n this.log.warn(\"Invalid login identifier format\", {\n provider,\n username,\n realm: name,\n });\n\n await this.auditService.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"Invalid login identifier format\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n\n const user = await users.findOne({ where }).catch(() => undefined);\n if (!user) {\n this.log.warn(\"User not found during login attempt\", {\n provider,\n username,\n realm: name,\n });\n\n await this.auditService.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"User not found\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n\n const identity = await identities.findOne({\n where: {\n provider: { eq: provider },\n userId: { eq: user.id },\n },\n });\n\n const storedPassword = identity.password;\n if (!storedPassword) {\n this.log.error(\"Identity has no password configured\", {\n provider,\n username,\n identityId: identity.id,\n realm: name,\n });\n throw new InvalidCredentialsError();\n }\n\n const valid = await this.cryptoProvider.verifyPassword(\n password,\n storedPassword,\n );\n\n if (!valid) {\n this.log.warn(\"Invalid password during login attempt\", {\n provider,\n username,\n realm: name,\n });\n\n await this.auditService.recordAuth(\"login_failed\", {\n userRealm: name,\n resourceId: user.id,\n description: \"Invalid password\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n\n await this.auditService.recordAuth(\"login\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: name,\n resourceId: user.id,\n description: `User logged in via ${provider}`,\n metadata: { provider, username },\n });\n\n return user;\n } catch (error) {\n if (error instanceof InvalidCredentialsError) {\n // TODO: store failed login attempts (with request data) and lock account after threshold\n throw error;\n }\n\n this.log.warn(\"Error during login attempt\", error);\n\n throw new InvalidCredentialsError();\n }\n }\n\n public async createSession(\n user: UserAccount,\n expiresIn: number,\n userRealmName?: string,\n ) {\n this.log.trace(\"Creating session\", { userId: user.id, expiresIn });\n\n const request = this.alepha.context.get<ServerRequest>(\"request\");\n const refreshToken = this.cryptoProvider.randomUUID();\n\n const expiresAt = this.dateTimeProvider\n .now()\n .add(expiresIn, \"seconds\")\n .toISOString();\n\n const session = await this.sessions(userRealmName).create({\n userId: user.id,\n expiresAt,\n ip: request?.ip,\n userAgent: request?.userAgent,\n refreshToken,\n });\n\n this.log.info(\"Session created\", {\n sessionId: session.id,\n userId: user.id,\n ip: request?.ip,\n });\n\n return {\n refreshToken,\n sessionId: session.id,\n };\n }\n\n public async refreshSession(refreshToken: string, userRealmName?: string) {\n this.log.trace(\"Refreshing session\");\n\n const session = await this.sessions(userRealmName).findOne({\n where: {\n refreshToken: { eq: refreshToken },\n },\n });\n\n const now = this.dateTimeProvider.now();\n const expiresAt = this.dateTimeProvider.of(session.expiresAt);\n\n if (this.dateTimeProvider.of(session.expiresAt) < now) {\n this.log.debug(\"Session expired during refresh\", {\n sessionId: session.id,\n userId: session.userId,\n });\n await this.sessions(userRealmName).deleteById(refreshToken);\n throw new UnauthorizedError(\"Session expired\");\n }\n\n const user = await this.users(userRealmName).findOne({\n where: {\n id: { eq: session.userId },\n },\n });\n\n this.log.debug(\"Session refreshed\", {\n sessionId: session.id,\n userId: session.userId,\n });\n\n return {\n user,\n expiresIn: expiresAt.unix() - now.unix(),\n sessionId: session.id,\n };\n }\n\n public async deleteSession(refreshToken: string, userRealmName?: string) {\n this.log.trace(\"Deleting session\");\n\n // Get session info before deletion for audit\n const session = await this.sessions(userRealmName)\n .findOne({\n where: { refreshToken: { eq: refreshToken } },\n })\n .catch(() => undefined);\n\n await this.sessions(userRealmName).deleteOne({\n refreshToken,\n });\n this.log.debug(\"Session deleted\");\n\n if (session) {\n const { name } = this.realmProvider.getRealm(userRealmName);\n\n await this.auditService.recordAuth(\"logout\", {\n userId: session.userId,\n userRealm: name,\n sessionId: session.id,\n description: \"User logged out\",\n });\n }\n }\n\n public async link(\n provider: string,\n profile: OAuth2Profile,\n userRealmName?: string,\n ) {\n this.log.trace(\"Linking OAuth2 profile\", {\n provider,\n profileSub: profile.sub,\n email: profile.email,\n });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n const identities = this.identities(userRealmName);\n const users = this.users(userRealmName);\n\n const identity = await identities\n .findOne({\n where: {\n provider,\n providerUserId: profile.sub,\n },\n })\n .catch(() => undefined);\n\n // existing identity found, return associated user\n if (identity) {\n this.log.debug(\"Existing identity found\", {\n provider,\n identityId: identity.id,\n userId: identity.userId,\n });\n\n const user = await users.findById(identity.userId);\n\n await this.auditService.recordAuth(\"login\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: `User logged in via OAuth2 (${provider})`,\n metadata: { provider, providerUserId: profile.sub },\n });\n\n return user;\n }\n\n if (!profile.email) {\n this.log.debug(\"OAuth2 profile has no email, returning profile as-is\", {\n provider,\n profileSub: profile.sub,\n });\n return {\n id: profile.sub,\n ...profile,\n };\n }\n\n const existing = await users\n .findOne({\n where: {\n email: profile.email,\n },\n })\n .catch(() => undefined);\n\n if (existing) {\n this.log.debug(\"Linking OAuth2 profile to existing user by email\", {\n provider,\n profileSub: profile.sub,\n userId: existing.id,\n email: profile.email,\n });\n await identities.create({\n provider,\n providerUserId: profile.sub,\n userId: existing.id,\n });\n\n await this.auditService.recordAuth(\"login\", {\n userId: existing.id,\n userEmail: existing.email ?? undefined,\n userRealm: realm.name,\n resourceId: existing.id,\n description: `OAuth2 identity linked to existing user (${provider})`,\n metadata: { provider, providerUserId: profile.sub, linked: true },\n });\n\n return existing;\n }\n\n // TODO: check usernames for uniqueness, add suffix if needed (e.g. john.doe1)\n // TODO: username must match a-zA-Z0-9._-\n\n const user = await users.create({\n realm: realm.name,\n username: profile.email.split(\"@\")[0],\n email: profile.email,\n // we trust the OAuth2 provider\n emailVerified: true,\n roles: [\"user\"], // TODO: make default roles configurable via realm settings\n });\n\n if (profile.picture) {\n this.log.debug(\"Fetching user profile picture from OAuth2 provider\", {\n provider,\n url: profile.picture,\n });\n try {\n const response = await fetch(profile.picture);\n const file = this.fsp.createFile({\n response,\n });\n if (response.ok && response.body) {\n const fileEntity = await this.fileController.uploadFile(\n {\n body: { file },\n },\n {\n user,\n },\n );\n await users.updateById(user.id, { picture: fileEntity.id });\n }\n } catch (error) {\n this.log.warn(\"Failed to fetch user profile picture\", error);\n }\n }\n\n await this.identities(userRealmName).create({\n provider,\n providerUserId: profile.sub,\n userId: user.id,\n });\n\n this.log.info(\"New user created via OAuth2 link\", {\n provider,\n userId: user.id,\n email: user.email,\n username: user.username,\n });\n\n // Audit: user created via OAuth\n await this.auditService.recordUser(\"create\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: `User created via OAuth2 (${provider})`,\n metadata: {\n provider,\n providerUserId: profile.sub,\n username: user.username,\n email: user.email,\n },\n });\n\n // Audit: login event\n await this.auditService.recordAuth(\"login\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: `First login via OAuth2 (${provider})`,\n metadata: { provider, providerUserId: profile.sub, firstLogin: true },\n });\n\n return user;\n }\n}\n","import { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const adminApiKeyQuerySchema = t.extend(pageQuerySchema, {\n userId: t.optional(t.uuid()),\n includeRevoked: t.optional(t.boolean()),\n});\n","import { t } from \"alepha\";\n\nexport const adminApiKeyResourceSchema = t.object({\n id: t.uuid(),\n userId: t.uuid(),\n name: t.string(),\n description: t.optional(t.string()),\n tokenPrefix: t.string(),\n tokenSuffix: t.string(),\n roles: t.array(t.string()),\n createdAt: t.datetime(),\n lastUsedAt: t.optional(t.datetime()),\n lastUsedIp: t.optional(t.string()),\n expiresAt: t.optional(t.datetime()),\n revokedAt: t.optional(t.datetime()),\n usageCount: t.integer(),\n});\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const apiKeyEntity = $entity({\n name: \"api_keys\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n\n // Owner\n userId: t.uuid(),\n\n // Key metadata\n name: t.text({ maxLength: 100 }),\n description: t.optional(t.text({ maxLength: 500 })),\n\n // Token (hashed) - internal, not user input\n tokenHash: t.string({ maxLength: 256 }),\n tokenPrefix: t.string({ maxLength: 10 }),\n tokenSuffix: t.string({ maxLength: 8 }),\n\n // Roles (snapshot from user at creation)\n roles: db.default(t.array(t.string()), []),\n\n // Tracking\n lastUsedAt: t.optional(t.datetime()),\n lastUsedIp: t.optional(t.string({ maxLength: 45 })),\n usageCount: db.default(t.integer(), 0),\n\n // Lifecycle\n expiresAt: t.optional(t.datetime()),\n revokedAt: t.optional(t.datetime()),\n }),\n indexes: [\n { columns: [\"userId\", \"name\"], unique: true },\n { columns: [\"tokenHash\"], unique: true },\n ],\n});\n\nexport type ApiKeyEntity = Static<typeof apiKeyEntity.schema>;\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { $inject, Alepha } from \"alepha\";\nimport { $cache } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository, sql } from \"alepha/orm\";\nimport type { IssuerResolver, UserInfo } from \"alepha/security\";\nimport {\n ForbiddenError,\n NotFoundError,\n type ServerRequest,\n} from \"alepha/server\";\nimport { type ApiKeyEntity, apiKeyEntity } from \"../entities/apiKeyEntity.ts\";\n\nexport class ApiKeyService {\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n protected readonly repo = $repository(apiKeyEntity);\n\n /**\n * Cache validated API keys for 15 minutes.\n */\n protected readonly validationCache = $cache<ApiKeyEntity | null, [string]>({\n name: \"api-key-validation\",\n ttl: [15, \"minutes\"],\n });\n\n // -------------------------------------------------------------------------\n // Resolver\n // -------------------------------------------------------------------------\n\n /**\n * Create an issuer resolver for API key authentication.\n * Lower priority means it runs before JWT resolver.\n *\n * @param options.priority - Priority of this resolver (default: 50, JWT is 100)\n * @param options.prefix - API key prefix to match in Bearer header (default: \"ak\")\n */\n public createResolver(\n options: { priority?: number; prefix?: string } = {},\n ): IssuerResolver {\n const { priority = 50, prefix = \"ak\" } = options;\n const prefixPattern = `${prefix}_`;\n\n return {\n priority,\n onRequest: async (req: ServerRequest) => {\n // Try query param first\n const url = typeof req.url === \"string\" ? new URL(req.url) : req.url;\n let token = url.searchParams.get(\"api_key\");\n\n // Try Bearer header - only if token starts with expected prefix\n if (!token) {\n const auth = req.headers.authorization;\n if (auth?.startsWith(\"Bearer \")) {\n const bearerToken = auth.slice(7);\n if (bearerToken.startsWith(prefixPattern)) {\n token = bearerToken;\n }\n }\n }\n\n if (!token) {\n return null;\n }\n\n return this.validate(token);\n },\n };\n }\n\n // -------------------------------------------------------------------------\n // CRUD\n // -------------------------------------------------------------------------\n\n /**\n * Create a new API key for a user.\n * Returns both the API key entity and the plain token (which is only available once).\n */\n public async create(options: {\n userId: string;\n name: string;\n roles: string[];\n description?: string;\n expiresAt?: Date;\n prefix?: string;\n }): Promise<{ apiKey: ApiKeyEntity; token: string }> {\n const prefix = options.prefix ?? \"ak\";\n const random = randomBytes(24).toString(\"base64url\");\n const token = `${prefix}_${random}`;\n const hash = this.hashToken(token);\n const suffix = token.slice(-8);\n\n const apiKey = await this.repo.create({\n userId: options.userId,\n name: options.name,\n description: options.description,\n tokenHash: hash,\n tokenPrefix: prefix,\n tokenSuffix: suffix,\n roles: options.roles,\n expiresAt: options.expiresAt?.toISOString(),\n });\n\n this.log.info(\"API key created\", {\n apiKeyId: apiKey.id,\n userId: options.userId,\n name: options.name,\n });\n\n return { apiKey, token };\n }\n\n /**\n * List all non-revoked API keys for a user.\n */\n public async list(userId: string): Promise<ApiKeyEntity[]> {\n return this.repo.findMany({\n where: {\n userId: { eq: userId },\n revokedAt: { isNull: true },\n },\n orderBy: { column: \"createdAt\", direction: \"desc\" },\n });\n }\n\n // -------------------------------------------------------------------------\n // Admin Operations\n // -------------------------------------------------------------------------\n\n /**\n * Find all API keys with optional filtering (admin only).\n */\n public async findAll(query: {\n userId?: string;\n includeRevoked?: boolean;\n page?: number;\n size?: number;\n sort?: string;\n }) {\n query.sort ??= \"-createdAt\";\n\n const where = this.repo.createQueryWhere();\n\n if (query.userId) {\n where.userId = { eq: query.userId };\n }\n\n if (!query.includeRevoked) {\n where.revokedAt = { isNull: true };\n }\n\n return this.repo.paginate(query, { where }, { count: true });\n }\n\n /**\n * Get an API key by ID (admin only).\n */\n public async getById(id: string): Promise<ApiKeyEntity> {\n const apiKey = await this.repo.findById(id).catch(() => null);\n\n if (!apiKey) {\n throw new NotFoundError(\"API key not found\");\n }\n\n return apiKey;\n }\n\n /**\n * Revoke any API key (admin only).\n */\n public async revokeByAdmin(id: string): Promise<void> {\n const apiKey = await this.repo.findById(id).catch(() => null);\n\n if (!apiKey) {\n throw new NotFoundError(\"API key not found\");\n }\n\n if (apiKey.revokedAt) {\n return; // Already revoked\n }\n\n // Invalidate cache\n await this.validationCache.invalidate(apiKey.tokenHash);\n\n await this.repo.updateById(id, {\n revokedAt: this.dateTimeProvider.now().toISOString(),\n });\n\n this.log.info(\"API key revoked by admin\", {\n apiKeyId: id,\n userId: apiKey.userId,\n });\n }\n\n // -------------------------------------------------------------------------\n // User Operations\n // -------------------------------------------------------------------------\n\n /**\n * Revoke an API key. Only the owner can revoke their own keys.\n */\n public async revoke(id: string, userId: string): Promise<void> {\n const apiKey = await this.repo.findById(id).catch(() => null);\n\n if (!apiKey) {\n throw new NotFoundError(\"API key not found\");\n }\n\n if (apiKey.userId !== userId) {\n throw new ForbiddenError(\"Not your API key\");\n }\n\n await this.validationCache.invalidate(apiKey.tokenHash);\n\n await this.repo.updateById(id, {\n revokedAt: this.dateTimeProvider.now().toISOString(),\n });\n\n this.log.info(\"API key revoked\", {\n apiKeyId: id,\n userId,\n });\n }\n\n // -------------------------------------------------------------------------\n // Validation\n // -------------------------------------------------------------------------\n\n /**\n * Validate an API key token and return user info if valid.\n */\n public async validate(token: string): Promise<UserInfo | null> {\n // Quick check for API key format\n if (!token.includes(\"_\")) {\n return null;\n }\n\n const hash = this.hashToken(token);\n\n // Try cache first\n let apiKey = await this.validationCache.get(hash);\n\n // If not in cache, look up in database\n if (apiKey === undefined) {\n apiKey = await this.repo\n .findOne({\n where: { tokenHash: { eq: hash } },\n })\n .catch(() => null);\n\n // Store in cache (even if null, to prevent repeated lookups)\n if (apiKey) {\n await this.validationCache.set(hash, apiKey);\n }\n }\n\n if (!apiKey) {\n return null;\n }\n\n // Check revocation\n if (apiKey.revokedAt) {\n return null;\n }\n\n // Check expiration\n if (\n apiKey.expiresAt &&\n this.dateTimeProvider.now().isAfter(apiKey.expiresAt)\n ) {\n return null;\n }\n\n // Update usage stats (fire and forget)\n this.updateUsage(apiKey.id).catch((error) => {\n this.log.warn(\"Failed to update API key usage\", { error });\n });\n\n return {\n id: apiKey.userId,\n roles: apiKey.roles,\n };\n }\n\n /**\n * Update usage statistics for an API key.\n */\n protected async updateUsage(id: string): Promise<void> {\n const request = this.alepha.context.get<ServerRequest>(\"request\");\n\n await this.repo.updateById(id, {\n lastUsedAt: this.dateTimeProvider.now().toISOString(),\n lastUsedIp: request?.ip,\n usageCount: sql`${this.repo.table.usageCount} + 1`,\n });\n }\n\n /**\n * Hash a token using SHA-256.\n */\n protected hashToken(token: string): string {\n return createHash(\"sha256\").update(token).digest(\"hex\");\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { adminApiKeyQuerySchema } from \"../schemas/adminApiKeyQuerySchema.ts\";\nimport { adminApiKeyResourceSchema } from \"../schemas/adminApiKeyResourceSchema.ts\";\nimport { ApiKeyService } from \"../services/ApiKeyService.ts\";\n\n/**\n * REST API controller for admin API key management.\n * Admins can list, view, and revoke any API key.\n */\nexport class AdminApiKeyController {\n protected readonly url = \"/admin/api-keys\";\n protected readonly group = \"admin:api-keys\";\n protected readonly apiKeyService = $inject(ApiKeyService);\n\n /**\n * Find all API keys with optional filtering.\n */\n public readonly findApiKeys = $action({\n path: this.url,\n group: this.group,\n secure: true,\n description: \"Find API keys with pagination and filtering\",\n schema: {\n query: adminApiKeyQuerySchema,\n response: t.page(adminApiKeyResourceSchema),\n },\n handler: ({ query }) => {\n const { userId, includeRevoked, ...pagination } = query;\n return this.apiKeyService.findAll({\n userId,\n includeRevoked,\n ...pagination,\n });\n },\n });\n\n /**\n * Get an API key by ID.\n */\n public readonly getApiKey = $action({\n path: `${this.url}/:id`,\n group: this.group,\n secure: true,\n description: \"Get an API key by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: adminApiKeyResourceSchema,\n },\n handler: ({ params }) => this.apiKeyService.getById(params.id),\n });\n\n /**\n * Revoke any API key.\n */\n public readonly revokeApiKey = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n secure: true,\n description: \"Revoke an API key\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: okSchema,\n },\n handler: async ({ params }) => {\n await this.apiKeyService.revokeByAdmin(params.id);\n return { ok: true, id: params.id };\n },\n });\n}\n","import { t } from \"alepha\";\n\nexport const createApiKeyBodySchema = t.object({\n name: t.text({ minLength: 1, maxLength: 100 }),\n description: t.optional(t.text({ maxLength: 500 })),\n expiresAt: t.optional(t.datetime()),\n});\n","import { t } from \"alepha\";\n\nexport const createApiKeyResponseSchema = t.object({\n id: t.uuid(),\n name: t.string(),\n token: t.string(),\n tokenSuffix: t.string(),\n roles: t.array(t.string()),\n createdAt: t.datetime(),\n expiresAt: t.optional(t.datetime()),\n});\n","import { t } from \"alepha\";\n\nexport const listApiKeyItemSchema = t.object({\n id: t.uuid(),\n name: t.string(),\n tokenPrefix: t.string(),\n tokenSuffix: t.string(),\n roles: t.array(t.string()),\n createdAt: t.datetime(),\n lastUsedAt: t.optional(t.datetime()),\n expiresAt: t.optional(t.datetime()),\n usageCount: t.integer(),\n});\n\nexport const listApiKeyResponseSchema = t.array(listApiKeyItemSchema);\n","import { t } from \"alepha\";\n\nexport const revokeApiKeyParamsSchema = t.object({\n id: t.uuid(),\n});\n","import { t } from \"alepha\";\n\nexport const revokeApiKeyResponseSchema = t.object({\n ok: t.boolean(),\n});\n","import { $inject } from \"alepha\";\nimport { $action } from \"alepha/server\";\nimport { createApiKeyBodySchema } from \"../schemas/createApiKeyBodySchema.ts\";\nimport { createApiKeyResponseSchema } from \"../schemas/createApiKeyResponseSchema.ts\";\nimport { listApiKeyResponseSchema } from \"../schemas/listApiKeyResponseSchema.ts\";\nimport { revokeApiKeyParamsSchema } from \"../schemas/revokeApiKeyParamsSchema.ts\";\nimport { revokeApiKeyResponseSchema } from \"../schemas/revokeApiKeyResponseSchema.ts\";\nimport { ApiKeyService } from \"../services/ApiKeyService.ts\";\n\n/**\n * REST API controller for user's own API key management.\n * Users can create, list, and revoke their own API keys.\n */\nexport class ApiKeyController {\n protected readonly url = \"/api-keys\";\n protected readonly group = \"api-keys\";\n protected readonly apiKeyService = $inject(ApiKeyService);\n\n /**\n * Create a new API key for the authenticated user.\n * The token is only returned once upon creation.\n */\n public readonly createApiKey = $action({\n method: \"POST\",\n path: this.url,\n group: this.group,\n description: \"Create a new API key\",\n secure: true,\n schema: {\n body: createApiKeyBodySchema,\n response: createApiKeyResponseSchema,\n },\n handler: async (request) => {\n const { apiKey, token } = await this.apiKeyService.create({\n userId: request.user.id,\n name: request.body.name,\n description: request.body.description,\n roles: request.user.roles ?? [],\n expiresAt: request.body.expiresAt\n ? new Date(request.body.expiresAt)\n : undefined,\n });\n\n return {\n id: apiKey.id,\n name: apiKey.name,\n token,\n tokenSuffix: apiKey.tokenSuffix,\n roles: apiKey.roles,\n createdAt: apiKey.createdAt,\n expiresAt: apiKey.expiresAt,\n };\n },\n });\n\n /**\n * List all active API keys for the authenticated user.\n * Does not return the actual tokens.\n */\n public readonly listApiKeys = $action({\n path: this.url,\n group: this.group,\n description: \"List your API keys\",\n secure: true,\n schema: {\n response: listApiKeyResponseSchema,\n },\n handler: async (request) => {\n const apiKeys = await this.apiKeyService.list(request.user.id);\n\n return apiKeys.map((apiKey) => ({\n id: apiKey.id,\n name: apiKey.name,\n tokenPrefix: apiKey.tokenPrefix,\n tokenSuffix: apiKey.tokenSuffix,\n roles: apiKey.roles,\n createdAt: apiKey.createdAt,\n lastUsedAt: apiKey.lastUsedAt,\n expiresAt: apiKey.expiresAt,\n usageCount: apiKey.usageCount,\n }));\n },\n });\n\n /**\n * Revoke an API key. Only the owner can revoke their own keys.\n */\n public readonly revokeApiKey = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Revoke an API key\",\n secure: true,\n schema: {\n params: revokeApiKeyParamsSchema,\n response: revokeApiKeyResponseSchema,\n },\n handler: async (request) => {\n await this.apiKeyService.revoke(request.params.id, request.user.id);\n return { ok: true };\n },\n });\n}\n","import { $module } from \"alepha\";\nimport { AdminApiKeyController } from \"./controllers/AdminApiKeyController.ts\";\nimport { ApiKeyController } from \"./controllers/ApiKeyController.ts\";\nimport { ApiKeyService } from \"./services/ApiKeyService.ts\";\n\nexport * from \"./controllers/AdminApiKeyController.ts\";\nexport * from \"./controllers/ApiKeyController.ts\";\nexport * from \"./entities/apiKeyEntity.ts\";\nexport * from \"./schemas/adminApiKeyQuerySchema.ts\";\nexport * from \"./schemas/adminApiKeyResourceSchema.ts\";\nexport * from \"./schemas/createApiKeyBodySchema.ts\";\nexport * from \"./schemas/createApiKeyResponseSchema.ts\";\nexport * from \"./schemas/listApiKeyResponseSchema.ts\";\nexport * from \"./schemas/revokeApiKeyParamsSchema.ts\";\nexport * from \"./schemas/revokeApiKeyResponseSchema.ts\";\nexport * from \"./services/ApiKeyService.ts\";\n\n/**\n * | type | quality | stability |\n * |------|---------|--------------|\n * | backend | good | experimental |\n *\n * API key management module for programmatic access.\n *\n * **Features:**\n * - Create API keys with role snapshots\n * - List and revoke API keys\n * - 15-minute validation caching\n * - Query param (?api_key=) and Bearer header support\n *\n * **Integration:**\n * To enable API key authentication for an issuer, register the resolver:\n *\n * ```ts\n * class MyApp {\n * apiKeyService = $inject(ApiKeyService);\n * issuer = $issuer({\n * secret: env.APP_SECRET,\n * resolvers: [this.apiKeyService.createResolver()],\n * });\n * }\n * ```\n *\n * @module alepha.api.keys\n */\nexport const AlephaApiKeys = $module({\n name: \"alepha.api.keys\",\n services: [ApiKeyService, ApiKeyController, AdminApiKeyController],\n});\n","import { $context } from \"alepha\";\nimport { AlephaApiAudits } from \"alepha/api/audits\";\nimport { AlephaApiFiles } from \"alepha/api/files\";\nimport { AlephaApiJobs } from \"alepha/api/jobs\";\nimport { AlephaApiKeys, ApiKeyService } from \"alepha/api/keys\";\nimport type { Repository } from \"alepha/orm\";\nimport {\n $issuer,\n type IssuerPrimitive,\n type IssuerPrimitiveOptions,\n type IssuerResolver,\n SecurityProvider,\n} from \"alepha/security\";\nimport {\n $authCredentials,\n $authGithub,\n $authGoogle,\n type AuthPrimitive,\n type Credentials,\n type LinkAccountOptions,\n type WithLinkFn,\n type WithLoginFn,\n} from \"alepha/server/auth\";\nimport type { RealmAuthSettings } from \"../atoms/realmAuthSettingsAtom.ts\";\nimport type { identities } from \"../entities/identities.ts\";\nimport type { sessions } from \"../entities/sessions.ts\";\nimport { DEFAULT_USER_REALM_NAME, type users } from \"../entities/users.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport { SessionService } from \"../services/SessionService.ts\";\n\nexport type RealmPrimitive = IssuerPrimitive & WithLinkFn & WithLoginFn;\n\n/**\n * Already configured realm for user management.\n *\n * Realm contains two roles: `admin` and `user`.\n *\n * - `admin`: Has full access to all resources and permissions.\n * - `user`: Has access to their own resources and permissions, but cannot access admin-level resources.\n *\n * Realm uses session management for handling user sessions.\n *\n * Environment Variables:\n * - `APP_SECRET`: Secret key for signing tokens (if not provided in options).\n */\n\nexport const $realm = (options: RealmOptions = {}): RealmPrimitive => {\n const { alepha } = $context();\n const sessionService = alepha.inject(SessionService);\n const securityProvider = alepha.inject(SecurityProvider);\n const realmProvider = alepha.inject(RealmProvider);\n\n const name = options.issuer?.name ?? DEFAULT_USER_REALM_NAME;\n\n options.settings ??= {};\n\n if (options.settings.emailRequired) {\n options.settings.emailEnabled = true;\n }\n\n if (options.settings.usernameRequired) {\n options.settings.usernameEnabled = true;\n }\n\n if (options.settings.phoneRequired) {\n options.settings.phoneEnabled = true;\n }\n\n const realmRegistration = realmProvider.register(name, options);\n\n // For now, all modules are enabled\n alepha.with(AlephaApiFiles);\n alepha.with(AlephaApiAudits);\n alepha.with(AlephaApiJobs);\n\n // Collect custom resolvers that will be registered during $issuer.onInit()\n // This ensures they are registered AFTER the realm is created (not on the default test realm)\n const customResolvers: IssuerResolver[] = [\n ...(options.issuer?.resolvers ?? []),\n ];\n\n // Enable API key authentication - must be added to customResolvers before $issuer() call\n if (options.apiKeys) {\n alepha.with(AlephaApiKeys);\n const apiKeyService = alepha.inject(ApiKeyService);\n customResolvers.push(apiKeyService.createResolver());\n }\n\n const realm: RealmPrimitive = $issuer({\n ...options.issuer,\n name,\n secret: options.secret ?? securityProvider.secretKey,\n resolvers: customResolvers,\n roles: options.issuer?.roles ?? [\n {\n name: \"admin\",\n permissions: [\n {\n name: \"*\",\n },\n ],\n },\n {\n name: \"user\",\n permissions: [\n {\n name: \"*\",\n ownership: true,\n exclude: [\"admin:*\"],\n },\n ],\n },\n ],\n settings: {\n accessToken: {\n expiration: [15, \"minutes\"],\n },\n refreshToken: {\n expiration: [30, \"days\"],\n },\n onCreateSession: async (user, config) => {\n return sessionService.createSession(user, config.expiresIn);\n },\n onRefreshSession: async (refreshToken) => {\n return sessionService.refreshSession(refreshToken);\n },\n onDeleteSession: async (refreshToken) => {\n await sessionService.deleteSession(refreshToken);\n },\n ...options.issuer?.settings,\n },\n });\n\n realm.link = (name: string) => {\n return (ctx: LinkAccountOptions) =>\n sessionService.link(name, ctx.user, realm.name);\n };\n\n realm.login = (name: string) => {\n return (credentials: Credentials) => {\n return sessionService.login(\n name,\n credentials.username,\n credentials.password,\n realm.name,\n );\n };\n };\n\n const identities = options.identities ?? {\n credentials: true,\n };\n\n if (identities) {\n const auth: Record<string, AuthPrimitive> = {};\n if (identities.credentials) {\n auth.credentials = $authCredentials(realm);\n } else {\n // if credentials auth is disabled, disable registration as well\n realmRegistration.settings.registrationAllowed = false;\n }\n\n if (identities.google) {\n auth.google = $authGoogle(realm);\n }\n\n if (identities.github) {\n auth.github = $authGithub(realm);\n }\n\n alepha.with(() => auth);\n }\n\n return realm;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RealmOptions {\n /**\n * Secret key for signing tokens.\n *\n * If not provided, the secret from the SecurityProvider will be used (usually from the APP_SECRET environment variable).\n */\n secret?: string;\n\n /**\n * Issuer configuration options.\n *\n * It's already pre-configured for user management with admin and user roles.\n */\n issuer?: Partial<IssuerPrimitiveOptions>;\n\n /**\n * Override entities.\n */\n entities?: {\n users?: Repository<typeof users.schema>;\n identities?: Repository<typeof identities.schema>;\n sessions?: Repository<typeof sessions.schema>;\n };\n\n settings?: Partial<RealmAuthSettings>;\n\n identities?: {\n credentials?: true;\n google?: true;\n github?: true;\n };\n\n /**\n * Enable API key authentication.\n *\n * When enabled, users can create API keys to access protected endpoints\n * without using JWT tokens. API keys are useful for:\n * - Programmatic access (CLI tools, scripts)\n * - Long-lived authentication tokens\n * - Third-party integrations\n *\n * API keys can be passed via:\n * - Query parameter: `?api_key=ak_xxx`\n * - Bearer header: `Authorization: Bearer ak_xxx`\n */\n apiKeys?: boolean;\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const loginSchema = t.object({\n username: t.text({\n minLength: 3,\n maxLength: 100,\n description: \"Username or email address for login\",\n }),\n password: t.text({\n minLength: 6,\n description: \"User password\",\n }),\n});\n\nexport type LoginInput = Static<typeof loginSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const registerSchema = t.object({\n username: t.string({\n minLength: 3,\n maxLength: 20,\n pattern: /^[a-zA-Z0-9_]+$/,\n description: \"Username for the new account\",\n }),\n email: t.email({\n description: \"Email address for the new account\",\n }),\n password: t.string({\n minLength: 8,\n description: \"Password for the new account\",\n }),\n confirmPassword: t.string({\n minLength: 8,\n description: \"Confirmation of the password\",\n }),\n firstName: t.optional(\n t.string({\n maxLength: 100,\n description: \"User's first name\",\n }),\n ),\n lastName: t.optional(\n t.string({\n maxLength: 100,\n description: \"User's last name\",\n }),\n ),\n});\n\nexport type RegisterInput = Static<typeof registerSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const resetPasswordRequestSchema = t.object({\n email: t.email({\n description: \"Email address to send password reset link\",\n }),\n});\n\nexport const resetPasswordSchema = t.object({\n token: t.string({\n description: \"Password reset token from email\",\n }),\n password: t.string({\n minLength: 8,\n description: \"New password\",\n }),\n confirmPassword: t.string({\n minLength: 8,\n description: \"Confirmation of the new password\",\n }),\n});\n\nexport type ResetPasswordRequest = Static<typeof resetPasswordRequestSchema>;\nexport type ResetPasswordInput = Static<typeof resetPasswordSchema>;\n","import { $module } from \"alepha\";\nimport { AlephaApiNotifications } from \"alepha/api/notifications\";\nimport { AlephaApiVerification } from \"alepha/api/verifications\";\nimport { AlephaEmail } from \"alepha/email\";\nimport { AlephaServerCompress } from \"alepha/server/compress\";\nimport { AlephaServerHelmet } from \"alepha/server/helmet\";\nimport { AdminIdentityController } from \"./controllers/AdminIdentityController.ts\";\nimport { AdminSessionController } from \"./controllers/AdminSessionController.ts\";\nimport { AdminUserController } from \"./controllers/AdminUserController.ts\";\nimport { RealmController } from \"./controllers/RealmController.ts\";\nimport { UserController } from \"./controllers/UserController.ts\";\nimport { UserNotifications } from \"./notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"./providers/RealmProvider.ts\";\nimport { CredentialService } from \"./services/CredentialService.ts\";\nimport { IdentityService } from \"./services/IdentityService.ts\";\nimport { RegistrationService } from \"./services/RegistrationService.ts\";\nimport { SessionCrudService } from \"./services/SessionCrudService.ts\";\nimport { SessionService } from \"./services/SessionService.ts\";\nimport { UserService } from \"./services/UserService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./atoms/realmAuthSettingsAtom.ts\";\nexport * from \"./controllers/AdminIdentityController.ts\";\nexport * from \"./controllers/AdminSessionController.ts\";\nexport * from \"./controllers/AdminUserController.ts\";\nexport * from \"./controllers/RealmController.ts\";\nexport * from \"./controllers/UserController.ts\";\nexport * from \"./entities/identities.ts\";\nexport * from \"./entities/sessions.ts\";\nexport * from \"./entities/users.ts\";\nexport * from \"./primitives/$realm.ts\";\nexport * from \"./providers/RealmProvider.ts\";\nexport * from \"./schemas/completePasswordResetRequestSchema.ts\";\nexport * from \"./schemas/completeRegistrationRequestSchema.ts\";\nexport * from \"./schemas/createUserSchema.ts\";\nexport * from \"./schemas/identityQuerySchema.ts\";\nexport * from \"./schemas/identityResourceSchema.ts\";\nexport * from \"./schemas/loginSchema.ts\";\nexport * from \"./schemas/passwordResetIntentResponseSchema.ts\";\nexport * from \"./schemas/realmConfigSchema.ts\";\nexport * from \"./schemas/registerSchema.ts\";\nexport * from \"./schemas/registrationIntentResponseSchema.ts\";\nexport * from \"./schemas/resetPasswordSchema.ts\";\nexport * from \"./schemas/sessionQuerySchema.ts\";\nexport * from \"./schemas/sessionResourceSchema.ts\";\nexport * from \"./schemas/updateUserSchema.ts\";\nexport * from \"./schemas/userQuerySchema.ts\";\nexport * from \"./schemas/userResourceSchema.ts\";\nexport * from \"./services/CredentialService.ts\";\nexport * from \"./services/IdentityService.ts\";\nexport * from \"./services/RegistrationService.ts\";\nexport * from \"./services/SessionCrudService.ts\";\nexport * from \"./services/SessionService.ts\";\nexport * from \"./services/UserService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | type | quality | stability |\n * |------|---------|-----------|\n * | backend | epic | stable |\n *\n * Complete user management with multi-realm support for multi-tenant applications.\n *\n * **Features:**\n * - User registration, login, and profile management\n * - Password reset workflows\n * - Email verification\n * - Session management with multiple devices\n * - Identity management (social logins, SSO)\n * - Multi-realm support for tenant isolation\n * - Credential management\n * - Entities: `users`, `identities`, `sessions`\n *\n * @module alepha.api.users\n */\nexport const AlephaApiUsers = $module({\n name: \"alepha.api.users\",\n services: [\n AlephaApiVerification,\n AlephaApiNotifications,\n AlephaServerHelmet,\n AlephaServerCompress,\n AlephaEmail,\n RealmProvider,\n SessionService,\n SessionCrudService,\n CredentialService,\n RegistrationService,\n UserService,\n IdentityService,\n UserController,\n AdminUserController,\n AdminSessionController,\n AdminIdentityController,\n RealmController,\n UserNotifications,\n ],\n});\n"],"mappings":"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IAAE,UAAU;IAAc;GAC1D;EACD,KAAK,CACH;GACE,WAAW;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACD,UAAU;GACX,CACF;EACD,KAAK,CAAC;GAAE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GAAE,UAAU;GAAa,CAAC;EACrE,MAAM,CAAC;GAAE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GAAE,UAAU;GAAc,CAAC;EAGvE,KAAK;GACH;IACE,WAAW;KAAC;KAAM;KAAM;KAAM;KAAM;KAAM;KAAM;KAAM;KAAK;IAC3D,UAAU;IACX;GACD;IACE,WAAW;KACT;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACD;IACD,UAAU;IACX;GACD;IACE,WAAW;KACT;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACD;IACD,UAAU;IACX;GACF;EACD,MAAM,CAAC;GAAE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GAAE,UAAU;GAAc,CAAC;EACvE,KAAK,CACH;GACE,WAAW;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACD,UAAU;GACX,CACF;EACD,KAAK,CACH;GACE,WAAW;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACD,UAAU;GACX,CACF;EACD,KAAK,CACH;GAAE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GAAE,UAAU;GAAoB,CACtE;EAGD,MAAM,CACJ;GACE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GACnC,UACE;GACH,CACF;EACD,MAAM,CACJ;GACE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GACnC,UACE;GACH,CACF;EACD,MAAM,CACJ;GACE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GACnC,UACE;GACH,CACF;EACD,KAAK,CACH;GACE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAM;IAAM;IAAM;IAAM;IAAK;GAC3D,UAAU;GACX,CACF;EACD,KAAK,CACH;GACE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAM;IAAM;IAAM;IAAM;IAAK;GAC3D,UAAU;GACX,CACF;EACD,KAAK,CACH;GACE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAM;IAAM;IAAM;IAAM;IAAK;GAC3D,UAAU;GACX,CACF;EACF;;;;CAKD,OAA0B,iBAAiB,OAAO,QAChD,aAAa,YACd,CAAC,SAAS,CAAC,KAAK,gBAAgB,WAAW,KAAK,SAAS;EAAE;EAAK,GAAG;EAAK,EAAE,CAAC;;;;;;;CAQ5E,OAAuB,UAAkC;EAEvD,MAAM;EACN,KAAK;EACL,MAAM;EACN,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,IAAI;EACJ,UAAU;EACV,KAAK;EAGL,KAAK;EACL,IAAI;EACJ,KAAK;EACL,IAAI;EACJ,KAAK;EACL,KAAK;EAGL,KAAK;EACL,KAAK;EACL,MAAM;EACN,KAAK;EACL,IAAI;EACJ,KAAK;EAGL,KAAK;EACL,KAAK;EACL,MAAM;EACN,KAAK;EACL,MAAM;EACN,KAAK;EACL,KAAK;EACL,KAAK;EACL,MAAM;EACN,KAAK;EAGL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,MAAM;EAGN,KAAK;EACL,MAAM;EACN,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EAGL,KAAK;EACL,MAAM;EACN,KAAK;EACL,MAAM;EACN,KAAK;EACL,MAAM;EAGN,MAAM;EACN,OAAO;EACP,KAAK;EACL,KAAK;EACL,KAAK;EACN;;;;;CAMD,OAA0B,wBAAgD;EACxE,MAAM,UAAkC,EAAE;AAE1C,OAAK,MAAM,CAAC,KAAK,aAAa,OAAO,QAAQ,aAAa,QAAQ,CAEhE,KAAI,CAAC,QAAQ,UACX,SAAQ,YAAY;AAGxB,SAAO;KACL;;;;;;;;;;;;;;CAeJ,yBAAyB,UAA0B;AACjD,SAAO,aAAa,eAAe,aAAa;;;;;;;;;;;;;;CAclD,eAAe,UAA0B;EACvC,MAAM,MAAM,SAAS,aAAa,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI;AACvD,SAAO,aAAa,QAAQ,QAAQ;;;;;;;;;;;;;;;;;;CAmBtC,MAAM,eACJ,QACA,UACyB;EAEzB,MAAM,mBAAmB,KAAK,eAAe,SAAS;EAGtD,MAAM,eAAe,SAAS,YAAY,IAAI;EAC9C,MAAM,MACJ,eAAe,IACX,SAAS,UAAU,eAAe,EAAE,CAAC,aAAa,GAClD;EAGN,MAAM,EAAE,QAAQ,QAAQ,cAAc,MAAM,KAAK,UAAU,QAAQ,GAAG;EAGtE,MAAM,qBAAqB,aAAa,YAAY;AACpD,MAAI,oBACF;QAAK,MAAM,EAAE,WAAW,cAAc,mBACpC,KAAI,KAAK,iBAAiB,QAAQ,UAAU,CAC1C,QAAO;IACL;IACA,WAAW;IACX,UAAU;IACV,QAAQ;IACT;;AAMP,OAAK,MAAM,EACT,KAAK,aACL,WACA,cACG,aAAa,eAChB,KAAI,gBAAgB,OAAO,KAAK,iBAAiB,QAAQ,UAAU,CACjE,QAAO;GACL;GACA,WAAW;GACX,UAAU;GACV,QAAQ;GACT;AAML,SAAO;GACL,UAAU;GACV,WAAW;GACX,UAAU;GACV,QAAQ;GACT;;;;;;;;CASH,MAAgB,UACd,QACA,UAC+C;EAC/C,MAAM,SAAmB,EAAE;AAG3B,aAAW,MAAM,SAAS,OACxB,QAAO,KAAK,OAAO,SAAS,MAAM,GAAG,QAAQ,OAAO,KAAK,MAAM,CAAC;EAGlE,MAAM,UAAU,OAAO,OAAO,OAAO;AAMrC,SAAO;GAAE,QALM,QAAQ,SAAS,GAAG,SAAS;GAK3B,QAFC,SAAS,KAAK,QAAQ;GAEJ;;;;;;;CAQtC,AAAU,iBACR,QACA,WACS;AACT,MAAI,OAAO,SAAS,UAAU,OAC5B,QAAO;AAGT,OAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,IACpC,KAAI,UAAU,OAAO,QAAQ,OAAO,OAAO,UAAU,GACnD,QAAO;AAIX,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9eX,IAAa,yBAAb,MAAkE;CAChE,AAAU,WAAW,QAAQ,aAAa;CAC1C,AAAU,OAAO,QAAQ,KAAK;CAE9B,AAAO,KAAK,GAAG,OAAyB;AACtC,SAAO,KAAK,GAAG,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BvB,WAAW,SAAsC;AAC/C,MAAI,UAAU,SAAS;GACrB,MAAM,OAAO,QAAQ;GACrB,MAAM,WAAW,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI;AAC1C,UAAO,KAAK,kBAAkB,UAAU,QAAQ;IAC9C,MAAM,QAAQ;IACd,MAAM,QAAQ,QAAQ;IACvB,CAAC;;AAIJ,MAAI,SAAS,QACX,QAAO,KAAK,kBAAkB,QAAQ,KAAK;GACzC,MAAM,QAAQ;GACd,MAAM,QAAQ;GACf,CAAC;AAGJ,MAAI,cAAc,SAAS;AACzB,OAAI,CAAC,QAAQ,SAAS,KACpB,OAAM,IAAI,YAAY,8BAA8B;GAEtD,MAAM,MAAM,QAAQ;GAEpB,MAAM,aAAa,IAAI,QAAQ,IAAI,iBAAiB;GACpD,MAAM,OAAO,aAAa,SAAS,YAAY,GAAG,GAAG;GAErD,IAAI,OAAO,QAAQ;GACnB,MAAM,qBAAqB,IAAI,QAAQ,IAAI,sBAAsB;AACjE,OAAI,sBAAsB,CAAC,MAAM;IAC/B,MAAM,QAAQ,mBAAmB,MAAM,uBAAuB;AAC9D,QAAI,MACF,QAAO,MAAM;;GAIjB,MAAM,OAAO,QAAQ,QAAQ,IAAI,QAAQ,IAAI,eAAe,IAAI;AAChE,UAAO,KAAK,qBAAqB,QAAQ,SAAS,MAAM;IACtD;IACA;IACA;IACD,CAAC;;AAIJ,MAAI,UAAU,QACZ,QAAO,KAAK,sBAAsB,QAAQ,MAAM;GAC9C,MAAM,QAAQ;GACd,MAAM,QAAQ;GACd,MAAM,QAAQ;GACf,CAAC;AAIJ,MAAI,YAAY,QACd,QAAO,KAAK,qBAAqB,QAAQ,QAAQ;GAC/C,MAAM,QAAQ;GACd,MAAM,QAAQ;GACf,CAAC;AAIJ,MAAI,iBAAiB,QACnB,QAAO,KAAK,qBAAqB,OAAO,KAAK,QAAQ,YAAY,EAAE;GACjE,MAAM,QAAQ;GACd,MAAM,QAAQ;GACf,CAAC;AAIJ,MAAI,UAAU,QACZ,QAAO,KAAK,qBAAqB,OAAO,KAAK,QAAQ,MAAM,QAAQ,EAAE;GACnE,MAAM,QAAQ,QAAQ;GACtB,MAAM,QAAQ,QAAQ;GACvB,CAAC;AAIJ,MAAI,YAAY,QACd,QAAO,KAAK,qBAAqB,QAAQ,QAAQ;GAC/C,MAAM,QAAQ;GACd,MAAM,QAAQ;GACd,MAAM,QAAQ;GACf,CAAC;AAGJ,QAAM,IAAI,YACR,uDACD;;;;;;;;;;;;;;;;;;;;;;CAuBH,MAAM,GAAG,MAAc,SAAoC;AACzD,QAAMC,GAAK,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;CAwB3B,MAAM,GAAG,KAAa,MAAc,SAAoC;AAItE,OAFgB,MAAM,KAAK,IAAI,EAEnB,aAAa,EAAE;AACzB,OAAI,CAAC,SAAS,UACZ,OAAM,IAAI,MACR,mDAAmD,MACpD;AAGH,SAAMC,GAAK,KAAK,MAAM;IACpB,WAAW;IACX,OAAO,SAAS,SAAS;IAC1B,CAAC;QAGF,OAAM,SAAS,KAAK,KAAK;;;;;;;;;;;;;;;;;;;CAqB7B,MAAM,GAAG,KAAa,MAA6B;AACjD,QAAM,OAAO,KAAK,KAAK;;;;;;;;;;;;;;;;;;;;;;CAuBzB,MAAM,MAAM,MAAc,UAAwB,EAAE,EAAiB;EACnE,MAAM,IAAIC,MAAQ,MAAM;GACtB,WAAW,QAAQ,aAAa;GAChC,MAAM,QAAQ;GACf,CAAC;AAEF,MAAI,QAAQ,UAAU,MACpB,OAAM;MAEN,OAAM,EAAE,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;CA0B3B,MAAM,GAAG,MAAc,SAAwC;EAC7D,MAAM,UAAU,MAAM,QAAQ,KAAK;EAGnC,MAAM,kBAAkB,SAAS,SAC7B,UACA,QAAQ,QAAQ,MAAM,CAAC,EAAE,WAAW,IAAI,CAAC;AAG7C,MAAI,SAAS,WAAW;GACtB,MAAM,WAAqB,EAAE;AAE7B,QAAK,MAAM,SAAS,iBAAiB;IACnC,MAAM,WAAW,KAAK,MAAM,MAAM;AAGlC,SAFkB,MAAM,KAAK,SAAS,EAExB,aAAa,EAAE;AAE3B,cAAS,KAAK,MAAM;KAEpB,MAAM,WAAW,MAAM,KAAK,GAAG,UAAU,QAAQ;AACjD,cAAS,KAAK,GAAG,SAAS,KAAK,MAAM,KAAK,OAAO,EAAE,CAAC,CAAC;UAErD,UAAS,KAAK,MAAM;;AAIxB,UAAO;;AAGT,SAAO;;;;;;;;;;;;;;;;;CAkBT,MAAM,OAAO,MAAgC;AAC3C,MAAI;AACF,SAAM,OAAO,KAAK;AAClB,UAAO;UACD;AACN,UAAO;;;;;;;;;;;;;;;;;CAkBX,MAAM,SAAS,MAA+B;AAC5C,SAAO,MAAMC,SAAW,KAAK;;;;;;;;;;;;;;;;;;;CAoB/B,MAAM,UACJ,MACA,MACe;AACf,MAAI,WAAW,KAAK,EAAE;AACpB,SAAMC,UAAY,MAAM,SAAS,KAAK,KAAK,QAAQ,CAAC,CAAC;AACrD;;AAEF,QAAMA,UAAY,MAAM,KAAK;;;;;;;;;;;;;;CAe/B,MAAM,aAAa,MAA+B;AAEhD,UADe,MAAM,KAAK,SAAS,KAAK,EAC1B,SAAS,QAAQ;;;;;;;;;;;;;;CAejC,MAAM,aAA0B,MAA0B;EACxD,MAAM,OAAO,MAAM,KAAK,aAAa,KAAK;AAC1C,SAAO,KAAK,KAAK,MAAM,KAAK;;;;;;;CAQ9B,AAAU,sBACR,QACA,UAII,EAAE,EACI;EACV,MAAM,OAAO,QAAQ,QAAQ,OAAO;AACpC,SAAO;GACL;GACA,MAAM,QAAQ,SAAS,OAAO,QAAQ,KAAK,SAAS,eAAe,KAAK;GACxE,MAAM,QAAQ,QAAQ,OAAO,QAAQ;GACrC,cAAc,OAAO,gBAAgB,KAAK,KAAK;GAC/C,cAAc,OAAO,QAAQ;GAC7B,aAAa,YAAkC;AAC7C,WAAO,MAAM,OAAO,aAAa;;GAEnC,MAAM,YAA6B;AACjC,WAAO,MAAM,OAAO,MAAM;;GAE7B;;;;;;;CAQH,AAAU,qBACR,QACA,UAGI,EAAE,EACI;EACV,MAAM,OAAe,QAAQ,QAAQ;AACrC,SAAO;GACL;GACA,MAAM,QAAQ,QAAQ,KAAK,SAAS,eAAe,QAAQ,QAAQ,KAAK;GACxE,MAAM,OAAO;GACb,cAAc,KAAK,KAAK;GACxB,cAAwB,SAAS,KAAK,OAAO;GAC7C,aAAa,YAAkC;AAC7C,WAAO,KAAK,oBAAoB,OAAO;;GAEzC,MAAM,YAA6B;AACjC,WAAO,OAAO,SAAS,QAAQ;;GAElC;;;;;;;CAQH,AAAU,qBACR,QACA,UAII,EAAE,EACiC;EACvC,IAAI,SAAwB;AAE5B,SAAO;GACL,MAAM,QAAQ,QAAQ;GACtB,MACE,QAAQ,QAAQ,KAAK,SAAS,eAAe,QAAQ,QAAQ,OAAO;GACtE,MAAM,QAAQ,QAAQ;GACtB,cAAc,KAAK,KAAK;GACxB,cAAc;GACd,SAAS;GACT,aAAa,YAAY;AACvB,eAAW,MAAM,KAAK,eAAe,OAAO;AAC5C,WAAO,KAAK,oBAAoB,OAAO;;GAEzC,MAAM,YAAY;AAChB,eAAW,MAAM,KAAK,eAAe,OAAO;AAC5C,WAAO,OAAO,SAAS,QAAQ;;GAElC;;;;;;;CAQH,AAAU,kBACR,KACA,UAGI,EAAE,EACI;EACV,MAAM,YAAY,IAAI,IAAI,IAAI;EAC9B,MAAM,WACJ,QAAQ,QAAQ,UAAU,SAAS,MAAM,IAAI,CAAC,KAAK,IAAI;EACzD,IAAI,SAAwB;AAE5B,SAAO;GACL,MAAM;GACN,MAAM,QAAQ,QAAQ,KAAK,SAAS,eAAe,SAAS;GAC5D,MAAM;GACN,cAAc,KAAK,KAAK;GACxB,cAAc,KAAK,oBAAoB,IAAI;GAC3C,aAAa,YAAY;AACvB,eAAW,MAAM,KAAK,YAAY,IAAI;AACtC,WAAO,KAAK,oBAAoB,OAAO;;GAEzC,MAAM,YAAY;AAChB,eAAW,MAAM,KAAK,YAAY,IAAI;AACtC,WAAO,OAAO,SAAS,QAAQ;;GAEjC,UAAU;GACX;;;;;;;CAQH,AAAU,qBAAqB,KAAuB;EACpD,MAAM,SAAS,IAAI,aAAa;AAEhC,QAAM,IAAI,CACP,MAAM,QACL,SAAS,QAAQ,IAAI,KAAiC,CAAC,KAAK,OAAO,CACpE,CACA,OAAO,QAAQ,OAAO,QAAQ,IAAI,CAAC;AAEtC,SAAO;;;;;;;CAQT,MAAgB,YAAY,KAA8B;EACxD,MAAM,YAAY,IAAI,IAAI,IAAI;AAE9B,MAAI,UAAU,aAAa,QAGzB,QAAO,MAAMD,SADI,cAAc,IAAI,CACF;WAEjC,UAAU,aAAa,WACvB,UAAU,aAAa,UACvB;GAEA,MAAM,WAAW,MAAM,MAAM,IAAI;AACjC,OAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MACR,mBAAmB,IAAI,IAAI,SAAS,OAAO,GAAG,SAAS,aACxD;GAEH,MAAM,cAAc,MAAM,SAAS,aAAa;AAChD,UAAO,OAAO,KAAK,YAAY;QAE/B,OAAM,IAAI,MAAM,yBAAyB,UAAU,WAAW;;;;;;;CASlE,AAAU,oBAAoB,KAAuB;EACnD,MAAM,YAAY,IAAI,IAAI,IAAI;AAE9B,MAAI,UAAU,aAAa,QAEzB,QAAO,iBAAiB,cAAc,IAAI,CAAC;WAE3C,UAAU,aAAa,WACvB,UAAU,aAAa,SAGvB,QAAO,KAAK,qBAAqB,IAAI;MAErC,OAAM,IAAI,YAAY,yBAAyB,UAAU,WAAW;;;;;;;CASxE,MAAgB,eAAe,YAAyC;EACtE,MAAM,SACJ,sBAAsB,WAClB,aACA,SAAS,QAAQ,WAA4B;AAEnD,SAAO,IAAI,SAAiB,SAAS,WAAW;GAC9C,MAAM,SAAgB,EAAE;AACxB,UAAO,GAAG,SAAS,UAAU,OAAO,KAAK,OAAO,KAAK,MAAM,CAAC,CAAC;AAC7D,UAAO,GAAG,aAAa,QAAQ,OAAO,OAAO,OAAO,CAAC,CAAC;AACtD,UAAO,GAAG,UAAU,QAClB,OAAO,IAAI,YAAY,2BAA2B,EAAE,OAAO,KAAK,CAAC,CAAC,CACnE;IACD;;;;;;;CAQJ,AAAU,oBAAoB,QAA6B;AACzD,SAAO,OAAO,OAAO,MACnB,OAAO,YACP,OAAO,aAAa,OAAO,WAC5B;;;;;;;;;;;;ACpqBL,IAAa,oBAAb,MAAwD;CACtD,AAAmB,MAAM,SAAS;CAClC,AAAmB,KAAK,QAAQ,mBAAmB;;;;CAKnD,MAAa,IACX,SACA,UAA2B,EAAE,EACZ;EACjB,MAAM,EAAE,UAAU,OAAO,UAAU,OAAO,MAAM,QAAQ;EACxD,MAAM,MAAM,QAAQ,QAAQ,KAAK;AAEjC,OAAK,IAAI,MAAM,UAAU,WAAW;GAAE;GAAK;GAAS;GAAS,CAAC;EAE9D,IAAI;EACJ,IAAI;AAEJ,MAAI,SAAS;GACX,MAAM,CAAC,KAAK,GAAG,QAAQ,QAAQ,MAAM,IAAI;AACzC,gBAAa,MAAM,KAAK,kBAAkB,KAAK,IAAI;AACnD,UAAO;QAEP,EAAC,eAAe,QAAQ,QAAQ,MAAM,IAAI;AAG5C,MAAI,QACF,QAAO,KAAK,YAAY,SAAS;GAAE;GAAK;GAAK,CAAC;AAGhD,SAAO,KAAK,YAAY,YAAY,MAAM;GAAE;GAAK;GAAK,CAAC;;;;;CAMzD,MAAgB,YACd,YACA,MACA,SACiB;EACjB,MAAM,OAAO,MAAM,YAAY,MAAM;GACnC,OAAO;GACP,KAAK,QAAQ;GACb,KAAK;IACH,GAAG,QAAQ;IACX,GAAG,QAAQ;IACZ;GACF,CAAC;AAEF,SAAO,IAAI,SAAiB,SAAS,WAAW;AAC9C,QAAK,GAAG,SAAS,SAAS;AACxB,QAAI,SAAS,KAAK,SAAS,KACzB,SAAQ,GAAG;QAEX,QAAO,IAAI,YAAY,4BAA4B,OAAO,CAAC;KAE7D;AACF,QAAK,GAAG,SAAS,OAAO;IACxB;;;;;CAMJ,AAAU,YACR,SACA,SACiB;AACjB,SAAO,IAAI,SAAiB,SAAS,WAAW;AAC9C,QACE,SACA;IACE,KAAK,QAAQ;IACb,KAAK;KACH,GAAG,QAAQ;KACX,YAAY;KACZ,GAAG,QAAQ;KACZ;IACF,GACA,KAAK,WAAW;AACf,QAAI,KAAK;AACP,KAAC,IAAY,SAAS;AACtB,YAAO,IAAI;UAEX,SAAQ,OAAO;KAGpB;IACD;;;;;;;;;;CAWJ,MAAgB,kBACd,MACA,MACiB;EACjB,MAAM,SAAS,QAAQ,aAAa,UAAU,SAAS;EAGvD,IAAI,WAAW,MAAM,KAAK,eACxB,MACA,qBAAqB,OAAO,SAC7B;AAGD,MAAI,CAAC,SACH,YAAW,MAAM,KAAK,eACpB,MACA,yCAAyC,OAAO,SACjD;AAIH,MAAI,CAAC,UAAU;GACb,IAAI,YAAY,KAAK,GAAG,KAAK,MAAM,KAAK;AACxC,QAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,eAAW,MAAM,KAAK,eACpB,WACA,qBAAqB,OAAO,SAC7B;AACD,QAAI,SAAU;AACd,gBAAY,KAAK,GAAG,KAAK,WAAW,KAAK;;;AAI7C,MAAI,CAAC,SACH,OAAM,IAAI,YACR,kCAAkC,KAAK,wCACxC;AAGH,SAAO;;;;;CAMT,MAAgB,eACd,MACA,cAC6B;EAC7B,MAAM,WAAW,KAAK,GAAG,KAAK,MAAM,aAAa;AACjD,MAAI,MAAM,KAAK,GAAG,OAAO,SAAS,CAChC,QAAO;;;;;CAQX,AAAO,YAAY,SAAmC;AACpD,SAAO,IAAI,SAAS,YAAY;AAK9B,QAHE,QAAQ,aAAa,UACjB,SAAS,YACT,cAAc,YACP,UAAU,QAAQ,CAAC,MAAM,CAAC;IACvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7HN,IAAsB,gBAAtB,MAAoC;;;;;;;;;;;;;;;;;;;ACnBpC,MAAa,eAAe,QAAQ;CAClC,MAAM;CACN,YAAY,EAAE;CACd,UAAU;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,WAAW,WACT,OACG,KAAK;EACJ,UAAU;EACV,SAAS;EACT,KAAK;EACN,CAAC,CACD,KAAK;EACJ,UAAU;EACV,SAAS;EACT,KAAK,OAAO,QAAQ,GAAG,sBAAsB;EAC9C,CAAC;CACP,CAAC;;;;AC3CF,IAAa,iBAAb,MAA4B;CAC1B,AAAmB,SAAS,QAAQ,OAAO;CAC3C,AAAmB,MAAM,QAAQ,mBAAmB;CACpD,AAAmB,mBAAmB,QAAQ,iBAAiB;CAC/D,AAAmB,iBAAiB,QAAQ,eAAe;CAC3D,AAAmB,MAAM,SAAS;CAClC,AAAmB,gBAAgB,QAAQ,cAAc;CACzD,AAAmB,iBAAiB,SAAyB;CAC7D,AAAmB,eAAe,QAAQ,aAAa;CAEvD,AAAO,MAAM,eAAwB;AACnC,SAAO,KAAK,cAAc,eAAe,cAAc;;CAGzD,AAAO,SAAS,eAAwB;AACtC,SAAO,KAAK,cAAc,kBAAkB,cAAc;;CAG5D,AAAO,WAAW,eAAwB;AACxC,SAAO,KAAK,cAAc,mBAAmB,cAAc;;;;;;CAO7D,AAAU,cAA6B;AACrC,SAAO,IAAI,SAAS,YAAY,WAAW,SAAS,UAAU,IAAI,IAAI,CAAC,CAAC;;;;;CAM1E,MAAa,MACX,UACA,UACA,UACA,eACqB;EACrB,MAAM,EAAE,UAAU,SAAS,KAAK,cAAc,SAAS,cAAc;EACrE,MAAM,UAAU,SAAS,SAAS,IAAI;EACtC,MAAM,UAAU,oBAAoB,KAAK,SAAS;EAClD,MAAM,aAAa,CAAC,WAAW,CAAC;EAChC,MAAM,aAAa,KAAK,WAAW,cAAc;EACjD,MAAM,QAAQ,KAAK,MAAM,cAAc;AAEvC,QAAM,KAAK,aAAa;AAExB,MAAI;GACF,MAAM,QAAQ,MAAM,kBAAkB;AAEtC,SAAM,QAAQ;AAEd,OAAI,SAAS,oBAAoB,SAAS,YAAY;AAEpD,QAAI,SAAS,gBAEX;SAAI,CADU,IAAI,OAAO,SAAS,eAAe,CACtC,KAAK,SAAS,EAAE;AACzB,WAAK,IAAI,KAAK,2CAA2C;OACvD;OACA;OACA,OAAO;OACR,CAAC;AAEF,YAAM,KAAK,aAAa,WAAW,gBAAgB;OACjD,WAAW;OACX,aAAa;OACb,UAAU;QAAE;QAAU;QAAU;OACjC,CAAC;AAEF,YAAM,IAAI,yBAAyB;;;AAGvC,UAAM,WAAW;cACR,SAAS,iBAAiB,SAAS,QAC5C,OAAM,QAAQ;YACL,SAAS,iBAAiB,QAAQ,QAC3C,OAAM,cAAc;QACf;AACL,SAAK,IAAI,KAAK,mCAAmC;KAC/C;KACA;KACA,OAAO;KACR,CAAC;AAEF,UAAM,KAAK,aAAa,WAAW,gBAAgB;KACjD,WAAW;KACX,aAAa;KACb,UAAU;MAAE;MAAU;MAAU;KACjC,CAAC;AAEF,UAAM,IAAI,yBAAyB;;GAGrC,MAAM,OAAO,MAAM,MAAM,QAAQ,EAAE,OAAO,CAAC,CAAC,YAAY,OAAU;AAClE,OAAI,CAAC,MAAM;AACT,SAAK,IAAI,KAAK,uCAAuC;KACnD;KACA;KACA,OAAO;KACR,CAAC;AAEF,UAAM,KAAK,aAAa,WAAW,gBAAgB;KACjD,WAAW;KACX,aAAa;KACb,UAAU;MAAE;MAAU;MAAU;KACjC,CAAC;AAEF,UAAM,IAAI,yBAAyB;;GAGrC,MAAM,WAAW,MAAM,WAAW,QAAQ,EACxC,OAAO;IACL,UAAU,EAAE,IAAI,UAAU;IAC1B,QAAQ,EAAE,IAAI,KAAK,IAAI;IACxB,EACF,CAAC;GAEF,MAAM,iBAAiB,SAAS;AAChC,OAAI,CAAC,gBAAgB;AACnB,SAAK,IAAI,MAAM,uCAAuC;KACpD;KACA;KACA,YAAY,SAAS;KACrB,OAAO;KACR,CAAC;AACF,UAAM,IAAI,yBAAyB;;AAQrC,OAAI,CALU,MAAM,KAAK,eAAe,eACtC,UACA,eACD,EAEW;AACV,SAAK,IAAI,KAAK,yCAAyC;KACrD;KACA;KACA,OAAO;KACR,CAAC;AAEF,UAAM,KAAK,aAAa,WAAW,gBAAgB;KACjD,WAAW;KACX,YAAY,KAAK;KACjB,aAAa;KACb,UAAU;MAAE;MAAU;MAAU;KACjC,CAAC;AAEF,UAAM,IAAI,yBAAyB;;AAGrC,SAAM,KAAK,aAAa,WAAW,SAAS;IAC1C,QAAQ,KAAK;IACb,WAAW,KAAK,SAAS;IACzB,WAAW;IACX,YAAY,KAAK;IACjB,aAAa,sBAAsB;IACnC,UAAU;KAAE;KAAU;KAAU;IACjC,CAAC;AAEF,UAAO;WACA,OAAO;AACd,OAAI,iBAAiB,wBAEnB,OAAM;AAGR,QAAK,IAAI,KAAK,8BAA8B,MAAM;AAElD,SAAM,IAAI,yBAAyB;;;CAIvC,MAAa,cACX,MACA,WACA,eACA;AACA,OAAK,IAAI,MAAM,oBAAoB;GAAE,QAAQ,KAAK;GAAI;GAAW,CAAC;EAElE,MAAM,UAAU,KAAK,OAAO,QAAQ,IAAmB,UAAU;EACjE,MAAM,eAAe,KAAK,eAAe,YAAY;EAErD,MAAM,YAAY,KAAK,iBACpB,KAAK,CACL,IAAI,WAAW,UAAU,CACzB,aAAa;EAEhB,MAAM,UAAU,MAAM,KAAK,SAAS,cAAc,CAAC,OAAO;GACxD,QAAQ,KAAK;GACb;GACA,IAAI,SAAS;GACb,WAAW,SAAS;GACpB;GACD,CAAC;AAEF,OAAK,IAAI,KAAK,mBAAmB;GAC/B,WAAW,QAAQ;GACnB,QAAQ,KAAK;GACb,IAAI,SAAS;GACd,CAAC;AAEF,SAAO;GACL;GACA,WAAW,QAAQ;GACpB;;CAGH,MAAa,eAAe,cAAsB,eAAwB;AACxE,OAAK,IAAI,MAAM,qBAAqB;EAEpC,MAAM,UAAU,MAAM,KAAK,SAAS,cAAc,CAAC,QAAQ,EACzD,OAAO,EACL,cAAc,EAAE,IAAI,cAAc,EACnC,EACF,CAAC;EAEF,MAAM,MAAM,KAAK,iBAAiB,KAAK;EACvC,MAAM,YAAY,KAAK,iBAAiB,GAAG,QAAQ,UAAU;AAE7D,MAAI,KAAK,iBAAiB,GAAG,QAAQ,UAAU,GAAG,KAAK;AACrD,QAAK,IAAI,MAAM,kCAAkC;IAC/C,WAAW,QAAQ;IACnB,QAAQ,QAAQ;IACjB,CAAC;AACF,SAAM,KAAK,SAAS,cAAc,CAAC,WAAW,aAAa;AAC3D,SAAM,IAAI,kBAAkB,kBAAkB;;EAGhD,MAAM,OAAO,MAAM,KAAK,MAAM,cAAc,CAAC,QAAQ,EACnD,OAAO,EACL,IAAI,EAAE,IAAI,QAAQ,QAAQ,EAC3B,EACF,CAAC;AAEF,OAAK,IAAI,MAAM,qBAAqB;GAClC,WAAW,QAAQ;GACnB,QAAQ,QAAQ;GACjB,CAAC;AAEF,SAAO;GACL;GACA,WAAW,UAAU,MAAM,GAAG,IAAI,MAAM;GACxC,WAAW,QAAQ;GACpB;;CAGH,MAAa,cAAc,cAAsB,eAAwB;AACvE,OAAK,IAAI,MAAM,mBAAmB;EAGlC,MAAM,UAAU,MAAM,KAAK,SAAS,cAAc,CAC/C,QAAQ,EACP,OAAO,EAAE,cAAc,EAAE,IAAI,cAAc,EAAE,EAC9C,CAAC,CACD,YAAY,OAAU;AAEzB,QAAM,KAAK,SAAS,cAAc,CAAC,UAAU,EAC3C,cACD,CAAC;AACF,OAAK,IAAI,MAAM,kBAAkB;AAEjC,MAAI,SAAS;GACX,MAAM,EAAE,SAAS,KAAK,cAAc,SAAS,cAAc;AAE3D,SAAM,KAAK,aAAa,WAAW,UAAU;IAC3C,QAAQ,QAAQ;IAChB,WAAW;IACX,WAAW,QAAQ;IACnB,aAAa;IACd,CAAC;;;CAIN,MAAa,KACX,UACA,SACA,eACA;AACA,OAAK,IAAI,MAAM,0BAA0B;GACvC;GACA,YAAY,QAAQ;GACpB,OAAO,QAAQ;GAChB,CAAC;EAEF,MAAM,QAAQ,KAAK,cAAc,SAAS,cAAc;EACxD,MAAM,aAAa,KAAK,WAAW,cAAc;EACjD,MAAM,QAAQ,KAAK,MAAM,cAAc;EAEvC,MAAM,WAAW,MAAM,WACpB,QAAQ,EACP,OAAO;GACL;GACA,gBAAgB,QAAQ;GACzB,EACF,CAAC,CACD,YAAY,OAAU;AAGzB,MAAI,UAAU;AACZ,QAAK,IAAI,MAAM,2BAA2B;IACxC;IACA,YAAY,SAAS;IACrB,QAAQ,SAAS;IAClB,CAAC;GAEF,MAAM,OAAO,MAAM,MAAM,SAAS,SAAS,OAAO;AAElD,SAAM,KAAK,aAAa,WAAW,SAAS;IAC1C,QAAQ,KAAK;IACb,WAAW,KAAK,SAAS;IACzB,WAAW,MAAM;IACjB,YAAY,KAAK;IACjB,aAAa,8BAA8B,SAAS;IACpD,UAAU;KAAE;KAAU,gBAAgB,QAAQ;KAAK;IACpD,CAAC;AAEF,UAAO;;AAGT,MAAI,CAAC,QAAQ,OAAO;AAClB,QAAK,IAAI,MAAM,wDAAwD;IACrE;IACA,YAAY,QAAQ;IACrB,CAAC;AACF,UAAO;IACL,IAAI,QAAQ;IACZ,GAAG;IACJ;;EAGH,MAAM,WAAW,MAAM,MACpB,QAAQ,EACP,OAAO,EACL,OAAO,QAAQ,OAChB,EACF,CAAC,CACD,YAAY,OAAU;AAEzB,MAAI,UAAU;AACZ,QAAK,IAAI,MAAM,oDAAoD;IACjE;IACA,YAAY,QAAQ;IACpB,QAAQ,SAAS;IACjB,OAAO,QAAQ;IAChB,CAAC;AACF,SAAM,WAAW,OAAO;IACtB;IACA,gBAAgB,QAAQ;IACxB,QAAQ,SAAS;IAClB,CAAC;AAEF,SAAM,KAAK,aAAa,WAAW,SAAS;IAC1C,QAAQ,SAAS;IACjB,WAAW,SAAS,SAAS;IAC7B,WAAW,MAAM;IACjB,YAAY,SAAS;IACrB,aAAa,4CAA4C,SAAS;IAClE,UAAU;KAAE;KAAU,gBAAgB,QAAQ;KAAK,QAAQ;KAAM;IAClE,CAAC;AAEF,UAAO;;EAMT,MAAM,OAAO,MAAM,MAAM,OAAO;GAC9B,OAAO,MAAM;GACb,UAAU,QAAQ,MAAM,MAAM,IAAI,CAAC;GACnC,OAAO,QAAQ;GAEf,eAAe;GACf,OAAO,CAAC,OAAO;GAChB,CAAC;AAEF,MAAI,QAAQ,SAAS;AACnB,QAAK,IAAI,MAAM,sDAAsD;IACnE;IACA,KAAK,QAAQ;IACd,CAAC;AACF,OAAI;IACF,MAAM,WAAW,MAAM,MAAM,QAAQ,QAAQ;IAC7C,MAAM,OAAO,KAAK,IAAI,WAAW,EAC/B,UACD,CAAC;AACF,QAAI,SAAS,MAAM,SAAS,MAAM;KAChC,MAAM,aAAa,MAAM,KAAK,eAAe,WAC3C,EACE,MAAM,EAAE,MAAM,EACf,EACD,EACE,MACD,CACF;AACD,WAAM,MAAM,WAAW,KAAK,IAAI,EAAE,SAAS,WAAW,IAAI,CAAC;;YAEtD,OAAO;AACd,SAAK,IAAI,KAAK,wCAAwC,MAAM;;;AAIhE,QAAM,KAAK,WAAW,cAAc,CAAC,OAAO;GAC1C;GACA,gBAAgB,QAAQ;GACxB,QAAQ,KAAK;GACd,CAAC;AAEF,OAAK,IAAI,KAAK,oCAAoC;GAChD;GACA,QAAQ,KAAK;GACb,OAAO,KAAK;GACZ,UAAU,KAAK;GAChB,CAAC;AAGF,QAAM,KAAK,aAAa,WAAW,UAAU;GAC3C,QAAQ,KAAK;GACb,WAAW,KAAK,SAAS;GACzB,WAAW,MAAM;GACjB,YAAY,KAAK;GACjB,aAAa,4BAA4B,SAAS;GAClD,UAAU;IACR;IACA,gBAAgB,QAAQ;IACxB,UAAU,KAAK;IACf,OAAO,KAAK;IACb;GACF,CAAC;AAGF,QAAM,KAAK,aAAa,WAAW,SAAS;GAC1C,QAAQ,KAAK;GACb,WAAW,KAAK,SAAS;GACzB,WAAW,MAAM;GACjB,YAAY,KAAK;GACjB,aAAa,2BAA2B,SAAS;GACjD,UAAU;IAAE;IAAU,gBAAgB,QAAQ;IAAK,YAAY;IAAM;GACtE,CAAC;AAEF,SAAO;;;;;;ACvcX,MAAa,yBAAyB,EAAE,OAAO,iBAAiB;CAC9D,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC;CAC5B,gBAAgB,EAAE,SAAS,EAAE,SAAS,CAAC;CACxC,CAAC;;;;ACJF,MAAa,4BAA4B,EAAE,OAAO;CAChD,IAAI,EAAE,MAAM;CACZ,QAAQ,EAAE,MAAM;CAChB,MAAM,EAAE,QAAQ;CAChB,aAAa,EAAE,SAAS,EAAE,QAAQ,CAAC;CACnC,aAAa,EAAE,QAAQ;CACvB,aAAa,EAAE,QAAQ;CACvB,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC1B,WAAW,EAAE,UAAU;CACvB,YAAY,EAAE,SAAS,EAAE,UAAU,CAAC;CACpC,YAAY,EAAE,SAAS,EAAE,QAAQ,CAAC;CAClC,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC;CACnC,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC;CACnC,YAAY,EAAE,SAAS;CACxB,CAAC;;;;ACbF,MAAa,eAAe,QAAQ;CAClC,MAAM;CACN,QAAQ,EAAE,OAAO;EACf,IAAI,GAAG,WAAW,EAAE,MAAM,CAAC;EAC3B,WAAW,GAAG,WAAW;EACzB,WAAW,GAAG,WAAW;EAGzB,QAAQ,EAAE,MAAM;EAGhB,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,CAAC;EAChC,aAAa,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,KAAK,CAAC,CAAC;EAGnD,WAAW,EAAE,OAAO,EAAE,WAAW,KAAK,CAAC;EACvC,aAAa,EAAE,OAAO,EAAE,WAAW,IAAI,CAAC;EACxC,aAAa,EAAE,OAAO,EAAE,WAAW,GAAG,CAAC;EAGvC,OAAO,GAAG,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC;EAG1C,YAAY,EAAE,SAAS,EAAE,UAAU,CAAC;EACpC,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,IAAI,CAAC,CAAC;EACnD,YAAY,GAAG,QAAQ,EAAE,SAAS,EAAE,EAAE;EAGtC,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC;EACnC,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC;EACpC,CAAC;CACF,SAAS,CACP;EAAE,SAAS,CAAC,UAAU,OAAO;EAAE,QAAQ;EAAM,EAC7C;EAAE,SAAS,CAAC,YAAY;EAAE,QAAQ;EAAM,CACzC;CACF,CAAC;;;;ACxBF,IAAa,gBAAb,MAA2B;CACzB,AAAmB,SAAS,QAAQ,OAAO;CAC3C,AAAmB,mBAAmB,QAAQ,iBAAiB;CAC/D,AAAmB,MAAM,SAAS;CAClC,AAAmB,OAAO,YAAY,aAAa;;;;CAKnD,AAAmB,kBAAkB,OAAsC;EACzE,MAAM;EACN,KAAK,CAAC,IAAI,UAAU;EACrB,CAAC;;;;;;;;CAaF,AAAO,eACL,UAAkD,EAAE,EACpC;EAChB,MAAM,EAAE,WAAW,IAAI,SAAS,SAAS;EACzC,MAAM,gBAAgB,GAAG,OAAO;AAEhC,SAAO;GACL;GACA,WAAW,OAAO,QAAuB;IAGvC,IAAI,SADQ,OAAO,IAAI,QAAQ,WAAW,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,KACjD,aAAa,IAAI,UAAU;AAG3C,QAAI,CAAC,OAAO;KACV,MAAM,OAAO,IAAI,QAAQ;AACzB,SAAI,MAAM,WAAW,UAAU,EAAE;MAC/B,MAAM,cAAc,KAAK,MAAM,EAAE;AACjC,UAAI,YAAY,WAAW,cAAc,CACvC,SAAQ;;;AAKd,QAAI,CAAC,MACH,QAAO;AAGT,WAAO,KAAK,SAAS,MAAM;;GAE9B;;;;;;CAWH,MAAa,OAAO,SAOiC;EACnD,MAAM,SAAS,QAAQ,UAAU;EAEjC,MAAM,QAAQ,GAAG,OAAO,GADT,YAAY,GAAG,CAAC,SAAS,YAAY;EAEpD,MAAM,OAAO,KAAK,UAAU,MAAM;EAClC,MAAM,SAAS,MAAM,MAAM,GAAG;EAE9B,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO;GACpC,QAAQ,QAAQ;GAChB,MAAM,QAAQ;GACd,aAAa,QAAQ;GACrB,WAAW;GACX,aAAa;GACb,aAAa;GACb,OAAO,QAAQ;GACf,WAAW,QAAQ,WAAW,aAAa;GAC5C,CAAC;AAEF,OAAK,IAAI,KAAK,mBAAmB;GAC/B,UAAU,OAAO;GACjB,QAAQ,QAAQ;GAChB,MAAM,QAAQ;GACf,CAAC;AAEF,SAAO;GAAE;GAAQ;GAAO;;;;;CAM1B,MAAa,KAAK,QAAyC;AACzD,SAAO,KAAK,KAAK,SAAS;GACxB,OAAO;IACL,QAAQ,EAAE,IAAI,QAAQ;IACtB,WAAW,EAAE,QAAQ,MAAM;IAC5B;GACD,SAAS;IAAE,QAAQ;IAAa,WAAW;IAAQ;GACpD,CAAC;;;;;CAUJ,MAAa,QAAQ,OAMlB;AACD,QAAM,SAAS;EAEf,MAAM,QAAQ,KAAK,KAAK,kBAAkB;AAE1C,MAAI,MAAM,OACR,OAAM,SAAS,EAAE,IAAI,MAAM,QAAQ;AAGrC,MAAI,CAAC,MAAM,eACT,OAAM,YAAY,EAAE,QAAQ,MAAM;AAGpC,SAAO,KAAK,KAAK,SAAS,OAAO,EAAE,OAAO,EAAE,EAAE,OAAO,MAAM,CAAC;;;;;CAM9D,MAAa,QAAQ,IAAmC;EACtD,MAAM,SAAS,MAAM,KAAK,KAAK,SAAS,GAAG,CAAC,YAAY,KAAK;AAE7D,MAAI,CAAC,OACH,OAAM,IAAI,cAAc,oBAAoB;AAG9C,SAAO;;;;;CAMT,MAAa,cAAc,IAA2B;EACpD,MAAM,SAAS,MAAM,KAAK,KAAK,SAAS,GAAG,CAAC,YAAY,KAAK;AAE7D,MAAI,CAAC,OACH,OAAM,IAAI,cAAc,oBAAoB;AAG9C,MAAI,OAAO,UACT;AAIF,QAAM,KAAK,gBAAgB,WAAW,OAAO,UAAU;AAEvD,QAAM,KAAK,KAAK,WAAW,IAAI,EAC7B,WAAW,KAAK,iBAAiB,KAAK,CAAC,aAAa,EACrD,CAAC;AAEF,OAAK,IAAI,KAAK,4BAA4B;GACxC,UAAU;GACV,QAAQ,OAAO;GAChB,CAAC;;;;;CAUJ,MAAa,OAAO,IAAY,QAA+B;EAC7D,MAAM,SAAS,MAAM,KAAK,KAAK,SAAS,GAAG,CAAC,YAAY,KAAK;AAE7D,MAAI,CAAC,OACH,OAAM,IAAI,cAAc,oBAAoB;AAG9C,MAAI,OAAO,WAAW,OACpB,OAAM,IAAI,eAAe,mBAAmB;AAG9C,QAAM,KAAK,gBAAgB,WAAW,OAAO,UAAU;AAEvD,QAAM,KAAK,KAAK,WAAW,IAAI,EAC7B,WAAW,KAAK,iBAAiB,KAAK,CAAC,aAAa,EACrD,CAAC;AAEF,OAAK,IAAI,KAAK,mBAAmB;GAC/B,UAAU;GACV;GACD,CAAC;;;;;CAUJ,MAAa,SAAS,OAAyC;AAE7D,MAAI,CAAC,MAAM,SAAS,IAAI,CACtB,QAAO;EAGT,MAAM,OAAO,KAAK,UAAU,MAAM;EAGlC,IAAI,SAAS,MAAM,KAAK,gBAAgB,IAAI,KAAK;AAGjD,MAAI,WAAW,QAAW;AACxB,YAAS,MAAM,KAAK,KACjB,QAAQ,EACP,OAAO,EAAE,WAAW,EAAE,IAAI,MAAM,EAAE,EACnC,CAAC,CACD,YAAY,KAAK;AAGpB,OAAI,OACF,OAAM,KAAK,gBAAgB,IAAI,MAAM,OAAO;;AAIhD,MAAI,CAAC,OACH,QAAO;AAIT,MAAI,OAAO,UACT,QAAO;AAIT,MACE,OAAO,aACP,KAAK,iBAAiB,KAAK,CAAC,QAAQ,OAAO,UAAU,CAErD,QAAO;AAIT,OAAK,YAAY,OAAO,GAAG,CAAC,OAAO,UAAU;AAC3C,QAAK,IAAI,KAAK,kCAAkC,EAAE,OAAO,CAAC;IAC1D;AAEF,SAAO;GACL,IAAI,OAAO;GACX,OAAO,OAAO;GACf;;;;;CAMH,MAAgB,YAAY,IAA2B;EACrD,MAAM,UAAU,KAAK,OAAO,QAAQ,IAAmB,UAAU;AAEjE,QAAM,KAAK,KAAK,WAAW,IAAI;GAC7B,YAAY,KAAK,iBAAiB,KAAK,CAAC,aAAa;GACrD,YAAY,SAAS;GACrB,YAAY,GAAG,GAAG,KAAK,KAAK,MAAM,WAAW;GAC9C,CAAC;;;;;CAMJ,AAAU,UAAU,OAAuB;AACzC,SAAO,WAAW,SAAS,CAAC,OAAO,MAAM,CAAC,OAAO,MAAM;;;;;;;;;;ACrS3D,IAAa,wBAAb,MAAmC;CACjC,AAAmB,MAAM;CACzB,AAAmB,QAAQ;CAC3B,AAAmB,gBAAgB,QAAQ,cAAc;;;;CAKzD,AAAgB,cAAc,QAAQ;EACpC,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,QAAQ;EACR,aAAa;EACb,QAAQ;GACN,OAAO;GACP,UAAU,EAAE,KAAK,0BAA0B;GAC5C;EACD,UAAU,EAAE,YAAY;GACtB,MAAM,EAAE,QAAQ,gBAAgB,GAAG,eAAe;AAClD,UAAO,KAAK,cAAc,QAAQ;IAChC;IACA;IACA,GAAG;IACJ,CAAC;;EAEL,CAAC;;;;CAKF,AAAgB,YAAY,QAAQ;EAClC,MAAM,GAAG,KAAK,IAAI;EAClB,OAAO,KAAK;EACZ,QAAQ;EACR,aAAa;EACb,QAAQ;GACN,QAAQ,EAAE,OAAO,EACf,IAAI,EAAE,MAAM,EACb,CAAC;GACF,UAAU;GACX;EACD,UAAU,EAAE,aAAa,KAAK,cAAc,QAAQ,OAAO,GAAG;EAC/D,CAAC;;;;CAKF,AAAgB,eAAe,QAAQ;EACrC,QAAQ;EACR,MAAM,GAAG,KAAK,IAAI;EAClB,OAAO,KAAK;EACZ,QAAQ;EACR,aAAa;EACb,QAAQ;GACN,QAAQ,EAAE,OAAO,EACf,IAAI,EAAE,MAAM,EACb,CAAC;GACF,UAAU;GACX;EACD,SAAS,OAAO,EAAE,aAAa;AAC7B,SAAM,KAAK,cAAc,cAAc,OAAO,GAAG;AACjD,UAAO;IAAE,IAAI;IAAM,IAAI,OAAO;IAAI;;EAErC,CAAC;;;;;ACvEJ,MAAa,yBAAyB,EAAE,OAAO;CAC7C,MAAM,EAAE,KAAK;EAAE,WAAW;EAAG,WAAW;EAAK,CAAC;CAC9C,aAAa,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,KAAK,CAAC,CAAC;CACnD,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC;CACpC,CAAC;;;;ACJF,MAAa,6BAA6B,EAAE,OAAO;CACjD,IAAI,EAAE,MAAM;CACZ,MAAM,EAAE,QAAQ;CAChB,OAAO,EAAE,QAAQ;CACjB,aAAa,EAAE,QAAQ;CACvB,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC1B,WAAW,EAAE,UAAU;CACvB,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC;CACpC,CAAC;;;;ACRF,MAAa,uBAAuB,EAAE,OAAO;CAC3C,IAAI,EAAE,MAAM;CACZ,MAAM,EAAE,QAAQ;CAChB,aAAa,EAAE,QAAQ;CACvB,aAAa,EAAE,QAAQ;CACvB,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC1B,WAAW,EAAE,UAAU;CACvB,YAAY,EAAE,SAAS,EAAE,UAAU,CAAC;CACpC,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC;CACnC,YAAY,EAAE,SAAS;CACxB,CAAC;AAEF,MAAa,2BAA2B,EAAE,MAAM,qBAAqB;;;;ACZrE,MAAa,2BAA2B,EAAE,OAAO,EAC/C,IAAI,EAAE,MAAM,EACb,CAAC;;;;ACFF,MAAa,6BAA6B,EAAE,OAAO,EACjD,IAAI,EAAE,SAAS,EAChB,CAAC;;;;;;;;ACSF,IAAa,mBAAb,MAA8B;CAC5B,AAAmB,MAAM;CACzB,AAAmB,QAAQ;CAC3B,AAAmB,gBAAgB,QAAQ,cAAc;;;;;CAMzD,AAAgB,eAAe,QAAQ;EACrC,QAAQ;EACR,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,aAAa;EACb,QAAQ;EACR,QAAQ;GACN,MAAM;GACN,UAAU;GACX;EACD,SAAS,OAAO,YAAY;GAC1B,MAAM,EAAE,QAAQ,UAAU,MAAM,KAAK,cAAc,OAAO;IACxD,QAAQ,QAAQ,KAAK;IACrB,MAAM,QAAQ,KAAK;IACnB,aAAa,QAAQ,KAAK;IAC1B,OAAO,QAAQ,KAAK,SAAS,EAAE;IAC/B,WAAW,QAAQ,KAAK,YACpB,IAAI,KAAK,QAAQ,KAAK,UAAU,GAChC;IACL,CAAC;AAEF,UAAO;IACL,IAAI,OAAO;IACX,MAAM,OAAO;IACb;IACA,aAAa,OAAO;IACpB,OAAO,OAAO;IACd,WAAW,OAAO;IAClB,WAAW,OAAO;IACnB;;EAEJ,CAAC;;;;;CAMF,AAAgB,cAAc,QAAQ;EACpC,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,aAAa;EACb,QAAQ;EACR,QAAQ,EACN,UAAU,0BACX;EACD,SAAS,OAAO,YAAY;AAG1B,WAFgB,MAAM,KAAK,cAAc,KAAK,QAAQ,KAAK,GAAG,EAE/C,KAAK,YAAY;IAC9B,IAAI,OAAO;IACX,MAAM,OAAO;IACb,aAAa,OAAO;IACpB,aAAa,OAAO;IACpB,OAAO,OAAO;IACd,WAAW,OAAO;IAClB,YAAY,OAAO;IACnB,WAAW,OAAO;IAClB,YAAY,OAAO;IACpB,EAAE;;EAEN,CAAC;;;;CAKF,AAAgB,eAAe,QAAQ;EACrC,QAAQ;EACR,MAAM,GAAG,KAAK,IAAI;EAClB,OAAO,KAAK;EACZ,aAAa;EACb,QAAQ;EACR,QAAQ;GACN,QAAQ;GACR,UAAU;GACX;EACD,SAAS,OAAO,YAAY;AAC1B,SAAM,KAAK,cAAc,OAAO,QAAQ,OAAO,IAAI,QAAQ,KAAK,GAAG;AACnE,UAAO,EAAE,IAAI,MAAM;;EAEtB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxDJ,MAAa,gBAAgB,QAAQ;CACnC,MAAM;CACN,UAAU;EAAC;EAAe;EAAkB;EAAsB;CACnE,CAAC;;;;;;;;;;;;;;;;;ACFF,MAAa,UAAU,UAAwB,EAAE,KAAqB;CACpE,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,iBAAiB,OAAO,OAAO,eAAe;CACpD,MAAM,mBAAmB,OAAO,OAAO,iBAAiB;CACxD,MAAM,gBAAgB,OAAO,OAAO,cAAc;CAElD,MAAM,OAAO,QAAQ,QAAQ,QAAQ;AAErC,SAAQ,aAAa,EAAE;AAEvB,KAAI,QAAQ,SAAS,cACnB,SAAQ,SAAS,eAAe;AAGlC,KAAI,QAAQ,SAAS,iBACnB,SAAQ,SAAS,kBAAkB;AAGrC,KAAI,QAAQ,SAAS,cACnB,SAAQ,SAAS,eAAe;CAGlC,MAAM,oBAAoB,cAAc,SAAS,MAAM,QAAQ;AAG/D,QAAO,KAAK,eAAe;AAC3B,QAAO,KAAK,gBAAgB;AAC5B,QAAO,KAAK,cAAc;CAI1B,MAAM,kBAAoC,CACxC,GAAI,QAAQ,QAAQ,aAAa,EAAE,CACpC;AAGD,KAAI,QAAQ,SAAS;AACnB,SAAO,KAAK,cAAc;EAC1B,MAAM,gBAAgB,OAAO,OAAO,cAAc;AAClD,kBAAgB,KAAK,cAAc,gBAAgB,CAAC;;CAGtD,MAAM,QAAwB,QAAQ;EACpC,GAAG,QAAQ;EACX;EACA,QAAQ,QAAQ,UAAU,iBAAiB;EAC3C,WAAW;EACX,OAAO,QAAQ,QAAQ,SAAS,CAC9B;GACE,MAAM;GACN,aAAa,CACX,EACE,MAAM,KACP,CACF;GACF,EACD;GACE,MAAM;GACN,aAAa,CACX;IACE,MAAM;IACN,WAAW;IACX,SAAS,CAAC,UAAU;IACrB,CACF;GACF,CACF;EACD,UAAU;GACR,aAAa,EACX,YAAY,CAAC,IAAI,UAAU,EAC5B;GACD,cAAc,EACZ,YAAY,CAAC,IAAI,OAAO,EACzB;GACD,iBAAiB,OAAO,MAAM,WAAW;AACvC,WAAO,eAAe,cAAc,MAAM,OAAO,UAAU;;GAE7D,kBAAkB,OAAO,iBAAiB;AACxC,WAAO,eAAe,eAAe,aAAa;;GAEpD,iBAAiB,OAAO,iBAAiB;AACvC,UAAM,eAAe,cAAc,aAAa;;GAElD,GAAG,QAAQ,QAAQ;GACpB;EACF,CAAC;AAEF,OAAM,QAAQ,SAAiB;AAC7B,UAAQ,QACN,eAAe,KAAK,MAAM,IAAI,MAAM,MAAM,KAAK;;AAGnD,OAAM,SAAS,SAAiB;AAC9B,UAAQ,gBAA6B;AACnC,UAAO,eAAe,MACpB,MACA,YAAY,UACZ,YAAY,UACZ,MAAM,KACP;;;CAIL,MAAM,aAAa,QAAQ,cAAc,EACvC,aAAa,MACd;AAED,KAAI,YAAY;EACd,MAAM,OAAsC,EAAE;AAC9C,MAAI,WAAW,YACb,MAAK,cAAc,iBAAiB,MAAM;MAG1C,mBAAkB,SAAS,sBAAsB;AAGnD,MAAI,WAAW,OACb,MAAK,SAAS,YAAY,MAAM;AAGlC,MAAI,WAAW,OACb,MAAK,SAAS,YAAY,MAAM;AAGlC,SAAO,WAAW,KAAK;;AAGzB,QAAO;;;;;AC1KT,MAAa,cAAc,EAAE,OAAO;CAClC,UAAU,EAAE,KAAK;EACf,WAAW;EACX,WAAW;EACX,aAAa;EACd,CAAC;CACF,UAAU,EAAE,KAAK;EACf,WAAW;EACX,aAAa;EACd,CAAC;CACH,CAAC;;;;ACVF,MAAa,iBAAiB,EAAE,OAAO;CACrC,UAAU,EAAE,OAAO;EACjB,WAAW;EACX,WAAW;EACX,SAAS;EACT,aAAa;EACd,CAAC;CACF,OAAO,EAAE,MAAM,EACb,aAAa,qCACd,CAAC;CACF,UAAU,EAAE,OAAO;EACjB,WAAW;EACX,aAAa;EACd,CAAC;CACF,iBAAiB,EAAE,OAAO;EACxB,WAAW;EACX,aAAa;EACd,CAAC;CACF,WAAW,EAAE,SACX,EAAE,OAAO;EACP,WAAW;EACX,aAAa;EACd,CAAC,CACH;CACD,UAAU,EAAE,SACV,EAAE,OAAO;EACP,WAAW;EACX,aAAa;EACd,CAAC,CACH;CACF,CAAC;;;;AC9BF,MAAa,6BAA6B,EAAE,OAAO,EACjD,OAAO,EAAE,MAAM,EACb,aAAa,6CACd,CAAC,EACH,CAAC;AAEF,MAAa,sBAAsB,EAAE,OAAO;CAC1C,OAAO,EAAE,OAAO,EACd,aAAa,mCACd,CAAC;CACF,UAAU,EAAE,OAAO;EACjB,WAAW;EACX,aAAa;EACd,CAAC;CACF,iBAAiB,EAAE,OAAO;EACxB,WAAW;EACX,aAAa;EACd,CAAC;CACH,CAAC;;;;;;;;;;;;;;;;;;;;;;;ACwDF,MAAa,iBAAiB,QAAQ;CACpC,MAAM;CACN,UAAU;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["INTENT_TTL_MINUTES","nodeJoin","fsRm","fsCp","fsMkdir","fsReadFile","fsWriteFile"],"sources":["../../../src/api/users/schemas/identityQuerySchema.ts","../../../src/api/users/entities/users.ts","../../../src/api/users/entities/identities.ts","../../../src/api/users/schemas/identityResourceSchema.ts","../../../src/api/users/atoms/realmAuthSettingsAtom.ts","../../../src/api/users/entities/sessions.ts","../../../src/api/users/providers/RealmProvider.ts","../../../src/api/users/services/UserAudits.ts","../../../src/api/users/services/IdentityService.ts","../../../src/api/users/controllers/AdminIdentityController.ts","../../../src/api/users/schemas/sessionQuerySchema.ts","../../../src/api/users/schemas/sessionResourceSchema.ts","../../../src/api/users/services/SessionCrudService.ts","../../../src/api/users/controllers/AdminSessionController.ts","../../../src/api/users/schemas/createUserSchema.ts","../../../src/api/users/schemas/updateUserSchema.ts","../../../src/api/users/schemas/userQuerySchema.ts","../../../src/api/users/schemas/userResourceSchema.ts","../../../src/api/users/services/UserNotifications.ts","../../../src/api/users/services/UserService.ts","../../../src/api/users/controllers/AdminUserController.ts","../../../src/api/users/schemas/realmConfigSchema.ts","../../../src/api/users/controllers/RealmController.ts","../../../src/api/users/schemas/completePasswordResetRequestSchema.ts","../../../src/api/users/schemas/completeRegistrationRequestSchema.ts","../../../src/api/users/schemas/passwordResetIntentResponseSchema.ts","../../../src/api/users/schemas/registerQuerySchema.ts","../../../src/api/users/schemas/registerRequestSchema.ts","../../../src/api/users/schemas/registrationIntentResponseSchema.ts","../../../src/api/users/services/CredentialService.ts","../../../src/api/users/services/RegistrationService.ts","../../../src/api/users/controllers/UserController.ts","../../../src/system/providers/FileSystemProvider.ts","../../../src/system/providers/MemoryFileSystemProvider.ts","../../../src/system/providers/MemoryShellProvider.ts","../../../src/system/services/FileDetector.ts","../../../src/system/providers/NodeFileSystemProvider.ts","../../../src/system/providers/NodeShellProvider.ts","../../../src/system/providers/ShellProvider.ts","../../../src/system/index.ts","../../../src/api/users/services/SessionService.ts","../../../src/api/keys/schemas/adminApiKeyQuerySchema.ts","../../../src/api/keys/schemas/adminApiKeyResourceSchema.ts","../../../src/api/keys/entities/apiKeyEntity.ts","../../../src/api/keys/services/ApiKeyService.ts","../../../src/api/keys/controllers/AdminApiKeyController.ts","../../../src/api/keys/schemas/createApiKeyBodySchema.ts","../../../src/api/keys/schemas/createApiKeyResponseSchema.ts","../../../src/api/keys/schemas/listApiKeyResponseSchema.ts","../../../src/api/keys/schemas/revokeApiKeyParamsSchema.ts","../../../src/api/keys/schemas/revokeApiKeyResponseSchema.ts","../../../src/api/keys/controllers/ApiKeyController.ts","../../../src/api/keys/index.ts","../../../src/api/users/services/UserFiles.ts","../../../src/api/users/services/UserJobs.ts","../../../src/api/users/services/UserParameters.ts","../../../src/api/users/primitives/$realm.ts","../../../src/api/users/schemas/loginSchema.ts","../../../src/api/users/schemas/registerSchema.ts","../../../src/api/users/schemas/resetPasswordSchema.ts","../../../src/api/users/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const identityQuerySchema = t.extend(pageQuerySchema, {\n userId: t.optional(t.uuid()),\n provider: t.optional(t.string()),\n});\n\nexport type IdentityQuery = Static<typeof identityQuerySchema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const DEFAULT_USER_REALM_NAME = \"default\";\n\nexport const users = $entity({\n name: \"users\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n\n realm: db.default(t.text(), DEFAULT_USER_REALM_NAME),\n\n username: t.optional(\n t.shortText({\n minLength: 3,\n maxLength: 50,\n // pattern is handled at the realm settings level\n }),\n ),\n\n email: t.optional(t.string({ format: \"email\" })),\n\n phoneNumber: t.optional(t.e164()),\n\n roles: db.default(t.array(t.string()), []),\n firstName: t.optional(t.string()),\n lastName: t.optional(t.string()),\n picture: t.optional(t.string()),\n enabled: db.default(t.boolean(), true),\n\n emailVerified: db.default(t.boolean(), false),\n }),\n indexes: [\n { columns: [\"realm\", \"username\"], unique: true },\n { columns: [\"realm\", \"email\"], unique: true },\n { columns: [\"realm\", \"phoneNumber\"], unique: true },\n ],\n});\n\nexport type UserEntity = Static<typeof users.schema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\nimport { users } from \"./users.ts\";\n\nexport const identities = $entity({\n name: \"identities\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n userId: db.ref(t.uuid(), () => users.cols.id),\n password: t.optional(t.text()),\n provider: t.text(),\n providerUserId: t.optional(t.text()),\n providerData: t.optional(t.json()),\n }),\n});\n\nexport type IdentityEntity = Static<typeof identities.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { identities } from \"../entities/identities.ts\";\n\nexport const identityResourceSchema = t.omit(identities.schema, [\"password\"]);\n\nexport type IdentityResource = Static<typeof identityResourceSchema>;\n","import { $atom, type Static, t } from \"alepha\";\n\nexport const realmAuthSettingsAtom = $atom({\n name: \"alepha.api.users.realmAuthSettings\",\n schema: t.object({\n // Branding and display settings\n displayName: t.optional(\n t.string({\n description:\n \"Display name shown on auth pages (e.g., 'Customer Portal')\",\n }),\n ),\n description: t.optional(\n t.string({\n description: \"Description shown on auth pages\",\n }),\n ),\n logoUrl: t.optional(\n t.string({\n description: \"Logo URL for auth pages\",\n }),\n ),\n\n // Auth settings\n registrationAllowed: t.boolean({\n description: \"Enable user self-registration\",\n }),\n emailEnabled: t.boolean({\n description: \"Enable email address as a login/registration credential\",\n }),\n emailRequired: t.boolean({\n description: \"Require email address for user accounts\",\n }),\n usernameEnabled: t.boolean({\n description: \"Enable username as a login/registration credential\",\n }),\n usernameRequired: t.boolean({\n description: \"Require username for user accounts\",\n }),\n usernameRegExp: t.string({\n description:\n \"Regular expression that usernames must match (if username is enabled)\",\n }),\n phoneEnabled: t.boolean({\n description: \"Enable phone number as a login/registration credential\",\n }),\n phoneRequired: t.boolean({\n description: \"Require phone number for user accounts\",\n }),\n verifyEmailRequired: t.boolean({\n description: \"Require email verification for user accounts\",\n }),\n verifyPhoneRequired: t.boolean({\n description: \"Require phone verification for user accounts\",\n }),\n firstNameLastNameEnabled: t.boolean({\n description: \"Enable first and last name for user accounts\",\n }),\n firstNameLastNameRequired: t.boolean({\n description: \"Require first and last name for user accounts\",\n }),\n resetPasswordAllowed: t.boolean({\n description: \"Enable forgot password functionality\",\n }),\n adminEmails: t.array(t.email(), {\n description:\n \"List of email addresses that are automatically promoted to admin role on login\",\n }),\n adminUsernames: t.array(t.text(), {\n description:\n \"List of usernames that are automatically promoted to admin role on login\",\n }),\n passwordPolicy: t.object({\n minLength: t.integer({\n description: \"Minimum password length\",\n default: 8,\n minimum: 1,\n }),\n requireUppercase: t.boolean({\n description: \"Require at least one uppercase letter\",\n }),\n requireLowercase: t.boolean({\n description: \"Require at least one lowercase letter\",\n }),\n requireNumbers: t.boolean({\n description: \"Require at least one number\",\n }),\n requireSpecialCharacters: t.boolean({\n description: \"Require at least one special character\",\n }),\n }),\n }),\n default: {\n // for a fresh hello world setup, we accept registration and email login\n registrationAllowed: true,\n emailEnabled: true,\n emailRequired: true,\n usernameEnabled: false,\n usernameRequired: false,\n usernameRegExp: \"^[a-zA-Z0-9_]{3,30}$\",\n phoneEnabled: false,\n phoneRequired: false,\n verifyEmailRequired: false,\n verifyPhoneRequired: false,\n resetPasswordAllowed: false,\n firstNameLastNameEnabled: false,\n firstNameLastNameRequired: false,\n adminEmails: [],\n adminUsernames: [],\n // TODO: not implemented yet\n passwordPolicy: {\n minLength: 8,\n requireUppercase: true,\n requireLowercase: true,\n requireNumbers: true,\n requireSpecialCharacters: false,\n },\n },\n});\n\nexport type RealmAuthSettings = Static<typeof realmAuthSettingsAtom.schema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\nimport { users } from \"./users.ts\";\n\nexport const sessions = $entity({\n name: \"sessions\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n refreshToken: t.uuid(),\n userId: db.ref(t.uuid(), () => users.cols.id),\n expiresAt: t.datetime(),\n ip: t.optional(t.text()),\n userAgent: t.optional(\n t.object({\n os: t.text(),\n browser: t.text(),\n device: t.enum([\"MOBILE\", \"DESKTOP\", \"TABLET\"]),\n }),\n ),\n }),\n});\n\nexport type SessionEntity = Static<typeof sessions.schema>;\n","import { $inject, Alepha, AlephaError } from \"alepha\";\nimport { $repository, type Repository } from \"alepha/orm\";\nimport {\n type RealmAuthSettings,\n realmAuthSettingsAtom,\n} from \"../atoms/realmAuthSettingsAtom.ts\";\nimport { identities } from \"../entities/identities.ts\";\nimport { sessions } from \"../entities/sessions.ts\";\nimport { DEFAULT_USER_REALM_NAME, users } from \"../entities/users.ts\";\nimport type { RealmFeatures, RealmOptions } from \"../primitives/$realm.ts\";\n\nexport interface RealmRepositories {\n identities: Repository<typeof identities.schema>;\n sessions: Repository<typeof sessions.schema>;\n users: Repository<typeof users.schema>;\n}\n\nexport interface Realm {\n name: string;\n repositories: RealmRepositories;\n settings: RealmAuthSettings;\n features: RealmFeatures;\n}\n\nexport class RealmProvider {\n protected readonly alepha = $inject(Alepha);\n // Default repositories using $repository() for eager initialization\n protected readonly defaultIdentities = $repository(identities);\n protected readonly defaultSessions = $repository(sessions);\n protected readonly defaultUsers = $repository(users);\n\n protected realms = new Map<string, Realm>();\n\n public register(realmName: string, realmOptions: RealmOptions = {}) {\n // Merge features with defaults\n const features: RealmFeatures = {\n jobs: false,\n notifications: false,\n apiKeys: false,\n parameters: false,\n files: false,\n audits: false,\n organizations: false,\n ...realmOptions.features,\n };\n\n this.realms.set(realmName, {\n name: realmName,\n repositories: {\n identities: realmOptions.entities?.identities ?? this.defaultIdentities,\n sessions: realmOptions.entities?.sessions ?? this.defaultSessions,\n users: realmOptions.entities?.users ?? this.defaultUsers,\n },\n // TODO: Remove deep merge when alepha supports it natively\n settings: {\n ...realmAuthSettingsAtom.options.default,\n ...realmOptions.settings,\n passwordPolicy: {\n ...realmAuthSettingsAtom.options.default.passwordPolicy,\n ...realmOptions.settings?.passwordPolicy,\n },\n },\n features,\n });\n return this.getRealm(realmName);\n }\n\n /**\n * Gets a registered realm by name, auto-creating default if needed.\n */\n public getRealm(realmName = DEFAULT_USER_REALM_NAME): Realm {\n let realm = this.realms.get(realmName);\n\n if (!realm) {\n // Auto-register default realm for backward compatibility\n const realms = Array.from(this.realms.values());\n const firstRealm = realms[0];\n if (realmName === DEFAULT_USER_REALM_NAME && firstRealm) {\n realm = firstRealm;\n } else if (this.alepha.isTest()) {\n realm = this.register(realmName); // Auto-create default realm in tests\n } else {\n throw new AlephaError(\n `Missing realm '${realmName}', please declare $realm in your application.`,\n );\n }\n }\n\n return realm;\n }\n\n public identityRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof identities.schema> {\n return this.getRealm(realmName).repositories.identities;\n }\n\n public sessionRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof sessions.schema> {\n return this.getRealm(realmName).repositories.sessions;\n }\n\n public userRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof users.schema> {\n return this.getRealm(realmName).repositories.users;\n }\n}\n","import { $inject } from \"alepha\";\nimport { AuditService, type CreateAudit } from \"alepha/api/audits\";\n\ntype AuditContext = Omit<CreateAudit, \"type\" | \"action\">;\n\n/**\n * User-specific audit wrapper service.\n *\n * This service wraps the core AuditService to provide user-related audit logging.\n * It is lazy-loaded when the `audits` feature is enabled in the realm.\n */\nexport class UserAudits {\n protected readonly auditService = $inject(AuditService);\n\n /**\n * Record a user-related audit event.\n */\n public recordUser(\n action:\n | \"create\"\n | \"update\"\n | \"delete\"\n | \"role_change\"\n | \"enable\"\n | \"disable\",\n context: AuditContext,\n ) {\n return this.auditService.recordUser(action, context);\n }\n\n /**\n * Record an authentication-related audit event.\n */\n public recordAuth(\n action: \"login\" | \"logout\" | \"login_failed\" | \"token_refresh\",\n context: AuditContext,\n ) {\n return this.auditService.recordAuth(action, context);\n }\n\n /**\n * Record a generic audit event.\n */\n public record(category: string, action: string, context: AuditContext) {\n return this.auditService.record(category, action, context);\n }\n}\n","import { $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { Page } from \"alepha/orm\";\nimport type { IdentityEntity } from \"../entities/identities.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { IdentityQuery } from \"../schemas/identityQuerySchema.ts\";\nimport { UserAudits } from \"./UserAudits.ts\";\n\nexport class IdentityService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly realmProvider = $inject(RealmProvider);\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n public identities(userRealmName?: string) {\n return this.realmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Find identities with pagination and filtering.\n */\n public async findIdentities(\n q: IdentityQuery = {},\n userRealmName?: string,\n ): Promise<Page<IdentityEntity>> {\n this.log.trace(\"Finding identities\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.identities(userRealmName).createQueryWhere();\n\n if (q.userId) {\n where.userId = { eq: q.userId };\n }\n\n if (q.provider) {\n where.provider = { like: q.provider };\n }\n\n const result = await this.identities(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Identities found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get an identity by ID.\n */\n public async getIdentityById(\n id: string,\n userRealmName?: string,\n ): Promise<IdentityEntity> {\n this.log.trace(\"Getting identity by ID\", { id, userRealmName });\n const identity = await this.identities(userRealmName).findById(id);\n this.log.debug(\"Identity retrieved\", {\n id,\n provider: identity.provider,\n userId: identity.userId,\n });\n return identity;\n }\n\n /**\n * Delete an identity by ID.\n */\n public async deleteIdentity(\n id: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Deleting identity\", { id, userRealmName });\n\n // Verify identity exists\n const identity = await this.getIdentityById(id, userRealmName);\n\n await this.identities(userRealmName).deleteById(id);\n this.log.info(\"Identity deleted\", {\n id,\n provider: identity.provider,\n userId: identity.userId,\n });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.recordUser(\"update\", {\n userRealm: realm.name,\n resourceId: identity.userId,\n description: `Identity provider disconnected: ${identity.provider}`,\n metadata: {\n identityId: id,\n provider: identity.provider,\n userId: identity.userId,\n },\n });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { identityQuerySchema } from \"../schemas/identityQuerySchema.ts\";\nimport { identityResourceSchema } from \"../schemas/identityResourceSchema.ts\";\nimport { IdentityService } from \"../services/IdentityService.ts\";\n\nexport class AdminIdentityController {\n protected readonly url = \"/identities\";\n protected readonly group = \"admin:identities\";\n protected readonly identityService = $inject(IdentityService);\n\n /**\n * Find identities with pagination and filtering.\n */\n public readonly findIdentities = $action({\n path: this.url,\n group: this.group,\n secure: true,\n description: \"Find identities with pagination and filtering\",\n schema: {\n query: t.extend(identityQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: t.page(identityResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.identityService.findIdentities(q, userRealmName);\n },\n });\n\n /**\n * Get an identity by ID.\n */\n public readonly getIdentity = $action({\n path: `${this.url}/:id`,\n group: this.group,\n secure: true,\n description: \"Get an identity by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: identityResourceSchema,\n },\n handler: ({ params, query }) =>\n this.identityService.getIdentityById(params.id, query.userRealmName),\n });\n\n /**\n * Delete an identity.\n */\n public readonly deleteIdentity = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n secure: true,\n description: \"Delete an identity\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.identityService.deleteIdentity(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const sessionQuerySchema = t.extend(pageQuerySchema, {\n userId: t.optional(t.uuid()),\n});\n\nexport type SessionQuery = Static<typeof sessionQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const sessionResourceSchema = t.object({\n id: t.uuid(),\n version: t.number(),\n createdAt: t.datetime(),\n updatedAt: t.datetime(),\n refreshToken: t.uuid(),\n userId: t.uuid(),\n expiresAt: t.datetime(),\n ip: t.optional(t.string()),\n userAgent: t.optional(\n t.object({\n os: t.string(),\n browser: t.string(),\n device: t.enum([\"MOBILE\", \"DESKTOP\", \"TABLET\"]),\n }),\n ),\n});\n\nexport type SessionResource = Static<typeof sessionResourceSchema>;\n","import { $inject } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { Page } from \"alepha/orm\";\nimport type { SessionEntity } from \"../entities/sessions.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { SessionQuery } from \"../schemas/sessionQuerySchema.ts\";\n\nexport class SessionCrudService {\n protected readonly log = $logger();\n protected readonly realmProvider = $inject(RealmProvider);\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.sessionRepository(userRealmName);\n }\n\n /**\n * Find sessions with pagination and filtering.\n */\n public async findSessions(\n q: SessionQuery = {},\n userRealmName?: string,\n ): Promise<Page<SessionEntity>> {\n this.log.trace(\"Finding sessions\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.sessions(userRealmName).createQueryWhere();\n\n if (q.userId) {\n where.userId = { eq: q.userId };\n }\n\n const result = await this.sessions(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Sessions found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get a session by ID.\n */\n public async getSessionById(\n id: string,\n userRealmName?: string,\n ): Promise<SessionEntity> {\n this.log.trace(\"Getting session by ID\", { id, userRealmName });\n const session = await this.sessions(userRealmName).findById(id);\n this.log.debug(\"Session retrieved\", { id, userId: session.userId });\n return session;\n }\n\n /**\n * Delete a session by ID.\n */\n public async deleteSession(\n id: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Deleting session\", { id, userRealmName });\n\n // Verify session exists\n await this.getSessionById(id, userRealmName);\n\n await this.sessions(userRealmName).deleteById(id);\n this.log.info(\"Session deleted\", { id });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { sessionQuerySchema } from \"../schemas/sessionQuerySchema.ts\";\nimport { sessionResourceSchema } from \"../schemas/sessionResourceSchema.ts\";\nimport { SessionCrudService } from \"../services/SessionCrudService.ts\";\n\nexport class AdminSessionController {\n protected readonly url = \"/sessions\";\n protected readonly group = \"admin:sessions\";\n protected readonly sessionService = $inject(SessionCrudService);\n\n /**\n * Find sessions with pagination and filtering.\n */\n public readonly findSessions = $action({\n path: this.url,\n group: this.group,\n secure: true,\n description: \"Find sessions with pagination and filtering\",\n schema: {\n query: t.extend(sessionQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: t.page(sessionResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.sessionService.findSessions(q, userRealmName);\n },\n });\n\n /**\n * Get a session by ID.\n */\n public readonly getSession = $action({\n path: `${this.url}/:id`,\n group: this.group,\n secure: true,\n description: \"Get a session by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: sessionResourceSchema,\n },\n handler: ({ params, query }) =>\n this.sessionService.getSessionById(params.id, query.userRealmName),\n });\n\n /**\n * Delete a session.\n */\n public readonly deleteSession = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n secure: true,\n description: \"Delete a session\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.sessionService.deleteSession(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n}\n","import { type Static, t } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const createUserSchema = t.omit(users.insertSchema, [\"realm\"]);\n\nexport type CreateUser = Static<typeof createUserSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const updateUserSchema = t.partial(\n t.omit(users.insertSchema, [\n \"id\",\n \"version\",\n \"createdAt\",\n \"updatedAt\",\n \"username\",\n \"emailVerified\",\n ]),\n);\n\nexport type UpdateUser = Static<typeof updateUserSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const userQuerySchema = t.extend(pageQuerySchema, {\n email: t.optional(t.string()),\n enabled: t.optional(t.boolean()),\n emailVerified: t.optional(t.boolean()),\n roles: t.optional(t.array(t.string())),\n query: t.optional(t.text()),\n});\n\nexport type UserQuery = Static<typeof userQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const userResourceSchema = users.schema;\n\nexport type UserResource = Static<typeof userResourceSchema>;\n","import { t } from \"alepha\";\nimport { $notification } from \"alepha/api/notifications\";\n\nexport class UserNotifications {\n // Code-based notifications (preferred)\n public readonly passwordReset = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a verification code to reset their password.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Reset your password\",\n body: (it) => `\n\t\t\t<h1>Reset Your Password</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>We received a request to reset your password. Use the code below to verify your identity:</p>\n\t\t\t<p style=\"margin: 30px 0; text-align: center;\">\n\t\t\t\t<span style=\"font-size: 32px; font-weight: bold; letter-spacing: 8px; font-family: monospace; background-color: #f5f5f5; padding: 16px 24px; border-radius: 8px; display: inline-block;\">\n\t\t\t\t\t${it.code}\n\t\t\t\t</span>\n\t\t\t</p>\n\t\t\t<p>This code will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly emailVerification = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a verification code to verify their email address.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Verify your email address\",\n body: (it) => `\n\t\t\t<h1>Verify Your Email Address</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>Thanks for signing up! Use the code below to verify your email address:</p>\n\t\t\t<p style=\"margin: 30px 0; text-align: center;\">\n\t\t\t\t<span style=\"font-size: 32px; font-weight: bold; letter-spacing: 8px; font-family: monospace; background-color: #f5f5f5; padding: 16px 24px; border-radius: 8px; display: inline-block;\">\n\t\t\t\t\t${it.code}\n\t\t\t\t</span>\n\t\t\t</p>\n\t\t\t<p>This code will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you did not create an account, please ignore this email.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly phoneVerification = $notification({\n category: \"security\",\n description:\n \"SMS sent to users with a verification code to verify their phone number.\",\n critical: true,\n sensitive: true,\n sms: {\n message: (it) =>\n `Your verification code is: ${it.code}. This code expires in ${it.expiresInMinutes} minutes.`,\n },\n schema: t.object({\n phoneNumber: t.string(),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n // Link-based notifications (alternative)\n public readonly passwordResetLink = $notification({\n category: \"security\",\n description: \"Email sent to users with a link to reset their password.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Reset your password\",\n body: (it) => `\n\t\t\t<h1>Reset Your Password</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>We received a request to reset your password. Click the link below to create a new password:</p>\n\t\t\t<p style=\"margin: 30px 0;\">\n\t\t\t\t<a href=\"${it.resetUrl}\" style=\"background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;\">\n\t\t\t\t\tReset Password\n\t\t\t\t</a>\n\t\t\t</p>\n\t\t\t<p>Or copy and paste this link into your browser:</p>\n\t\t\t<p style=\"word-break: break-all; color: #666;\">${it.resetUrl}</p>\n\t\t\t<p>This link will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n resetUrl: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly emailVerificationLink = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a link to verify their email address.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Verify your email address\",\n body: (it) => `\n\t\t\t<h1>Verify Your Email Address</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>Thanks for signing up! Click the button below to verify your email address:</p>\n\t\t\t<p style=\"margin: 30px 0;\">\n\t\t\t\t<a href=\"${it.verifyUrl}\" style=\"background-color: #28a745; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;\">\n\t\t\t\t\tVerify Email\n\t\t\t\t</a>\n\t\t\t</p>\n\t\t\t<p>Or copy and paste this link into your browser:</p>\n\t\t\t<p style=\"word-break: break-all; color: #666;\">${it.verifyUrl}</p>\n\t\t\t<p>This link will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you did not create an account, please ignore this email.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n verifyUrl: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n}\n","import { $inject, Alepha } from \"alepha\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $logger } from \"alepha/logger\";\nimport { type Page, parseQueryString } from \"alepha/orm\";\nimport { BadRequestError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { CreateUser } from \"../schemas/createUserSchema.ts\";\nimport type { UpdateUser } from \"../schemas/updateUserSchema.ts\";\nimport type { UserQuery } from \"../schemas/userQuerySchema.ts\";\nimport { UserAudits } from \"./UserAudits.ts\";\nimport { UserNotifications } from \"./UserNotifications.ts\";\n\nexport class UserService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly verificationController = $client<VerificationController>();\n protected readonly realmProvider = $inject(RealmProvider);\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n protected userNotifications(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.notifications) {\n return this.alepha.inject(UserNotifications);\n }\n return undefined;\n }\n\n public users(userRealmName?: string) {\n return this.realmProvider.userRepository(userRealmName);\n }\n\n /**\n * Request email verification for a user.\n * @param email - The email address to verify.\n * @param userRealmName - Optional realm name.\n * @param method - The verification method: \"code\" (default) or \"link\".\n * @param verifyUrl - Base URL for verification link (required when method is \"link\").\n */\n public async requestEmailVerification(\n email: string,\n userRealmName?: string,\n method: \"code\" | \"link\" = \"code\",\n verifyUrl?: string,\n ): Promise<boolean> {\n this.log.trace(\"Requesting email verification\", {\n email,\n userRealmName,\n method,\n });\n\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n if (!user) {\n this.log.debug(\"Email verification requested for non-existent user\", {\n email,\n });\n return true;\n }\n\n if (user.emailVerified) {\n this.log.debug(\"Email verification requested for already verified user\", {\n email,\n userId: user.id,\n });\n return true;\n }\n\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: method },\n body: { target: email },\n });\n\n if (method === \"link\") {\n // Build verification URL with token\n const url = new URL(verifyUrl || \"/verify-email\", \"http://localhost\");\n url.searchParams.set(\"email\", email);\n url.searchParams.set(\"token\", verification.token);\n const fullVerifyUrl = verifyUrl\n ? `${verifyUrl}${url.search}`\n : url.pathname + url.search;\n\n await this.userNotifications(userRealmName)?.emailVerificationLink.push(\n {\n contact: email,\n variables: {\n email,\n verifyUrl: fullVerifyUrl,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n },\n );\n\n this.log.debug(\"Email verification link sent\", {\n email,\n userId: user.id,\n });\n } else {\n await this.userNotifications(userRealmName)?.emailVerification.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n this.log.debug(\"Email verification code sent\", {\n email,\n userId: user.id,\n });\n }\n } catch (error) {\n // Silent fail for security\n this.log.warn(\"Failed to send email verification\", { email, error });\n }\n\n return true;\n }\n\n /**\n * Verify a user's email using a valid verification token.\n * Supports both code (6-digit) and link (UUID) verification tokens.\n */\n public async verifyEmail(\n email: string,\n token: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Verifying email\", { email, userRealmName });\n\n // Detect verification type based on token format\n // Codes are 6-digit numbers, links are UUIDs\n const isCode = /^\\d{6}$/.test(token);\n const type = isCode ? \"code\" : \"link\";\n\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type },\n body: { target: email, token },\n })\n .catch(() => {\n this.log.warn(\"Invalid email verification token\", { email, type });\n throw new BadRequestError(\"Invalid or expired verification token\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Email verification token already used\", { email });\n throw new BadRequestError(\"Invalid or expired verification token\");\n }\n\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\n\n await this.users(userRealmName).updateById(user.id, {\n emailVerified: true,\n });\n\n this.log.info(\"Email verified\", { email, userId: user.id, type });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.recordUser(\"update\", {\n userId: user.id,\n userEmail: email,\n userRealm: realm.name,\n resourceId: user.id,\n description: \"Email verified\",\n metadata: { email, verificationType: type },\n });\n }\n\n /**\n * Check if an email is verified.\n */\n public async isEmailVerified(\n email: string,\n userRealmName?: string,\n ): Promise<boolean> {\n this.log.trace(\"Checking if email is verified\", { email, userRealmName });\n\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n return user?.emailVerified ?? false;\n }\n\n /**\n * Find users with pagination and filtering.\n */\n public async findUsers(\n q: UserQuery = {},\n userRealmName?: string,\n ): Promise<Page<UserEntity>> {\n this.log.trace(\"Finding users\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.users(userRealmName).createQueryWhere();\n\n if (q.email) {\n where.email = { like: q.email };\n }\n\n if (q.enabled !== undefined) {\n where.enabled = { eq: q.enabled };\n }\n\n if (q.emailVerified !== undefined) {\n where.emailVerified = { eq: q.emailVerified };\n }\n\n if (q.roles) {\n where.roles = { arrayContains: q.roles };\n }\n\n if (q.query) {\n Object.assign(where, parseQueryString(q.query));\n }\n\n const result = await this.users(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Users found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get a user by ID.\n */\n public async getUserById(\n id: string,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Getting user by ID\", { id, userRealmName });\n return await this.users(userRealmName).findById(id);\n }\n\n /**\n * Create a new user.\n */\n public async createUser(\n data: CreateUser,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Creating user\", {\n username: data.username,\n email: data.email,\n userRealmName,\n });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n // TODO: one query instead of 3\n\n // Check for existing user based on provided unique fields\n if (data.username) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { username: { eq: data.username } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Username already taken\", { username: data.username });\n throw new BadRequestError(\"User with this username already exists\");\n }\n }\n\n if (data.email) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: data.email } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Email already taken\", { email: data.email });\n throw new BadRequestError(\"User with this email already exists\");\n }\n }\n\n if (data.phoneNumber) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { phoneNumber: { eq: data.phoneNumber } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Phone number already taken\", {\n phoneNumber: data.phoneNumber,\n });\n throw new BadRequestError(\"User with this phone number already exists\");\n }\n }\n\n const user = await this.users(userRealmName).create({\n ...data,\n roles: data.roles ?? [\"user\"], // TODO: Default roles from realm settings\n realm: realm.name,\n });\n\n this.log.info(\"User created\", {\n userId: user.id,\n username: user.username,\n email: user.email,\n });\n\n await this.userAudits(userRealmName)?.recordUser(\"create\", {\n userRealm: realm.name,\n resourceId: user.id,\n description: \"User created\",\n metadata: {\n username: user.username,\n email: user.email,\n roles: user.roles,\n },\n });\n\n return user;\n }\n\n /**\n * Update an existing user.\n */\n public async updateUser(\n id: string,\n data: UpdateUser,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Updating user\", { id, userRealmName });\n const before = await this.getUserById(id, userRealmName);\n\n const user = await this.users(userRealmName).updateById(id, data);\n this.log.debug(\"User updated\", { userId: id });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n // Build changes object showing what was updated\n const changes: Record<string, { from: unknown; to: unknown }> = {};\n for (const key of Object.keys(data) as (keyof UpdateUser)[]) {\n if (data[key] !== undefined && before[key] !== data[key]) {\n changes[key] = { from: before[key], to: data[key] };\n }\n }\n\n // Detect role changes for special handling\n const isRoleChange =\n data.roles !== undefined &&\n JSON.stringify(before.roles) !== JSON.stringify(data.roles);\n\n await this.userAudits(userRealmName)?.recordUser(\n isRoleChange ? \"role_change\" : \"update\",\n {\n userRealm: realm.name,\n resourceId: user.id,\n description: isRoleChange\n ? \"User roles changed\"\n : `User updated: ${Object.keys(changes).join(\", \")}`,\n metadata: { changes },\n },\n );\n\n return user;\n }\n\n /**\n * Delete a user by ID.\n */\n public async deleteUser(id: string, userRealmName?: string): Promise<void> {\n this.log.trace(\"Deleting user\", { id, userRealmName });\n const user = await this.getUserById(id, userRealmName);\n\n await this.users(userRealmName).deleteById(id);\n this.log.info(\"User deleted\", { userId: id });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.recordUser(\"delete\", {\n userRealm: realm.name,\n resourceId: id,\n severity: \"warning\",\n description: \"User deleted\",\n metadata: {\n username: user.username,\n email: user.email,\n },\n });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { createUserSchema } from \"../schemas/createUserSchema.ts\";\nimport { updateUserSchema } from \"../schemas/updateUserSchema.ts\";\nimport { userQuerySchema } from \"../schemas/userQuerySchema.ts\";\nimport { userResourceSchema } from \"../schemas/userResourceSchema.ts\";\nimport { UserService } from \"../services/UserService.ts\";\n\nexport class AdminUserController {\n protected readonly url = \"/users\";\n protected readonly group = \"admin:users\";\n protected readonly userService = $inject(UserService);\n\n /**\n * Find users with pagination and filtering.\n */\n public readonly findUsers = $action({\n path: this.url,\n group: this.group,\n secure: true,\n description: \"Find users with pagination and filtering\",\n schema: {\n query: t.extend(userQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: t.page(userResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.userService.findUsers(q, userRealmName);\n },\n });\n\n /**\n * Get a user by ID.\n */\n public readonly getUser = $action({\n path: `${this.url}/:id`,\n group: this.group,\n secure: true,\n description: \"Get a user by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: userResourceSchema,\n },\n handler: ({ params, query }) =>\n this.userService.getUserById(params.id, query.userRealmName),\n });\n\n /**\n * Create a new user.\n */\n public readonly createUser = $action({\n method: \"POST\",\n path: this.url,\n group: this.group,\n secure: true,\n description: \"Create a new user\",\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: createUserSchema,\n response: userResourceSchema,\n },\n handler: ({ body, query }) =>\n this.userService.createUser(body, query.userRealmName),\n });\n\n /**\n * Update a user.\n */\n public readonly updateUser = $action({\n method: \"PATCH\",\n path: `${this.url}/:id`,\n group: this.group,\n secure: true,\n description: \"Update a user\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: updateUserSchema,\n response: userResourceSchema,\n },\n handler: ({ params, body, query }) =>\n this.userService.updateUser(params.id, body, query.userRealmName),\n });\n\n /**\n * Delete a user.\n */\n public readonly deleteUser = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n secure: true,\n description: \"Delete a user\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.userService.deleteUser(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n}\n","import { type Static, t } from \"alepha\";\nimport { authenticationProviderSchema } from \"alepha/server/auth\";\nimport { realmAuthSettingsAtom } from \"../atoms/realmAuthSettingsAtom.ts\";\n\nexport const realmConfigSchema = t.object({\n settings: realmAuthSettingsAtom.schema,\n realmName: t.string(),\n authenticationMethods: t.array(authenticationProviderSchema),\n});\n\nexport type RealmConfig = Static<typeof realmConfigSchema>;\n","import { $inject, t } from \"alepha\";\nimport { $action } from \"alepha/server\";\nimport { ServerAuthProvider } from \"alepha/server/auth\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport { realmConfigSchema } from \"../schemas/realmConfigSchema.ts\";\n\n/**\n * Controller for exposing realm configuration.\n * Uses $route instead of $action to keep endpoints hidden from API documentation.\n */\nexport class RealmController {\n protected readonly url = \"/realms\";\n protected readonly group = \"realms\";\n protected readonly realmProvider = $inject(RealmProvider);\n protected readonly serverAuthProvider = $inject(ServerAuthProvider);\n\n /**\n * Get realm configuration settings.\n * This endpoint is not exposed in the API documentation.\n */\n public readonly getRealmConfig = $action({\n group: this.group,\n method: \"GET\",\n path: `${this.url}/config`,\n secure: false,\n cache: {\n etag: true,\n control: {\n maxAge: [24, \"hours\"],\n },\n },\n schema: {\n query: t.object({\n realmName: t.optional(t.string()),\n }),\n response: realmConfigSchema,\n },\n handler: ({ query }) => {\n const { name: realmName, settings } = this.realmProvider.getRealm(\n query.realmName,\n );\n\n const authenticationMethods =\n this.serverAuthProvider.getAuthenticationProviders({\n realmName,\n });\n\n return {\n settings,\n realmName,\n authenticationMethods,\n };\n },\n });\n\n public readonly checkUsernameAvailability = $action({\n group: this.group,\n path: `${this.url}/check-username`,\n secure: false,\n schema: {\n query: t.object({\n realmName: t.optional(t.text()),\n }),\n body: t.object({\n username: t.text(),\n }),\n response: t.object({\n available: t.boolean(),\n }),\n },\n handler: async ({ query, body }) => {\n const realmName = query.realmName;\n const userRepository = this.realmProvider.userRepository(realmName);\n\n const existingUser = await userRepository\n .findOne({ where: { username: { eq: body.username } } })\n .catch(() => undefined);\n\n return {\n available: !existingUser,\n };\n },\n });\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Request schema for completing a password reset.\n *\n * Requires the intent ID from Phase 1, the verification code,\n * and the new password.\n */\nexport const completePasswordResetRequestSchema = t.object({\n intentId: t.uuid({\n description: \"The intent ID from createPasswordResetIntent\",\n }),\n code: t.string({\n description: \"6-digit verification code sent via email\",\n }),\n newPassword: t.string({\n minLength: 8,\n description: \"New password (minimum 8 characters)\",\n }),\n});\n\nexport type CompletePasswordResetRequest = Static<\n typeof completePasswordResetRequestSchema\n>;\n","import { type Static, t } from \"alepha\";\n\nexport const completeRegistrationRequestSchema = t.object({\n intentId: t.uuid({\n description: \"The registration intent ID from the first phase\",\n }),\n emailCode: t.optional(\n t.string({\n description: \"Email verification code (if email verification required)\",\n }),\n ),\n phoneCode: t.optional(\n t.string({\n description: \"Phone verification code (if phone verification required)\",\n }),\n ),\n captchaToken: t.optional(\n t.string({\n description: \"Captcha token (if captcha required)\",\n }),\n ),\n});\n\nexport type CompleteRegistrationRequest = Static<\n typeof completeRegistrationRequestSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Response schema for password reset intent creation.\n *\n * Contains the intent ID needed for Phase 2 completion,\n * along with expiration time.\n */\nexport const passwordResetIntentResponseSchema = t.object({\n intentId: t.uuid({\n description: \"Unique identifier for this password reset intent\",\n }),\n expiresAt: t.datetime({\n description: \"ISO timestamp when this intent expires\",\n }),\n});\n\nexport type PasswordResetIntentResponse = Static<\n typeof passwordResetIntentResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Schema for user registration query parameters.\n * Allows specifying a custom user realm.\n */\nexport const registerQuerySchema = t.object({\n userRealmName: t.optional(\n t.text({\n description:\n \"The user realm to register the user in (defaults to 'default')\",\n }),\n ),\n});\n\nexport type RegisterQuery = Static<typeof registerQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Schema for user registration request body.\n * Password is always required, other fields depend on realm settings.\n */\nexport const registerRequestSchema = t.object({\n // Password is always required\n password: t.string({\n minLength: 8,\n description: \"Password for the account\",\n }),\n\n // Identity fields (requirements depend on realm settings)\n username: t.optional(\n t.string({\n minLength: 3,\n description: \"Unique username for the account\",\n }),\n ),\n\n // Optional contact fields\n email: t.optional(\n t.string({\n format: \"email\",\n description: \"User's email address\",\n }),\n ),\n phoneNumber: t.optional(\n t.string({\n description: \"User's phone number\",\n }),\n ),\n\n // Optional user profile fields\n firstName: t.optional(\n t.string({\n description: \"User's first name\",\n }),\n ),\n lastName: t.optional(\n t.string({\n description: \"User's last name\",\n }),\n ),\n picture: t.optional(\n t.string({\n description: \"User's profile picture URL\",\n }),\n ),\n});\n\nexport type RegisterRequest = Static<typeof registerRequestSchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const registrationIntentResponseSchema = t.object({\n intentId: t.uuid({\n description: \"Unique identifier for the registration intent\",\n }),\n expectCaptcha: t.boolean({\n description: \"Whether captcha verification is required\",\n }),\n expectEmailVerification: t.boolean({\n description: \"Whether email verification is required\",\n }),\n expectPhoneVerification: t.boolean({\n description: \"Whether phone verification is required\",\n }),\n expiresAt: t.datetime({\n description: \"When the registration intent expires\",\n }),\n});\n\nexport type RegistrationIntentResponse = Static<\n typeof registrationIntentResponseSchema\n>;\n","import { randomUUID } from \"node:crypto\";\nimport { $inject, Alepha } from \"alepha\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $cache } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { CryptoProvider } from \"alepha/security\";\nimport { BadRequestError, HttpError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { CompletePasswordResetRequest } from \"../schemas/completePasswordResetRequestSchema.ts\";\nimport type { PasswordResetIntentResponse } from \"../schemas/passwordResetIntentResponseSchema.ts\";\nimport { UserAudits } from \"./UserAudits.ts\";\nimport { UserNotifications } from \"./UserNotifications.ts\";\n\n/**\n * Intent stored in cache during the password reset flow.\n */\ninterface PasswordResetIntent {\n email: string;\n userId: string;\n identityId: string;\n realmName?: string;\n expiresAt: string;\n}\n\nconst INTENT_TTL_MINUTES = 10;\n\nexport class CredentialService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly verificationController = $client<VerificationController>();\n protected readonly realmProvider = $inject(RealmProvider);\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n protected userNotifications(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.notifications) {\n return this.alepha.inject(UserNotifications);\n }\n return undefined;\n }\n\n protected readonly intentCache = $cache<PasswordResetIntent>({\n name: \"password-reset-intents\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n public users(userRealmName?: string) {\n return this.realmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.realmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Phase 1: Create a password reset intent.\n *\n * Validates the email, checks for existing user with credentials,\n * sends verification code, and stores the intent in cache.\n *\n * @param email - User's email address\n * @param userRealmName - Optional realm name\n * @returns Intent response with intentId and expiration (always returns for security)\n */\n public async createPasswordResetIntent(\n email: string,\n userRealmName?: string,\n ): Promise<PasswordResetIntentResponse> {\n this.log.trace(\"Creating password reset intent\", { email, userRealmName });\n\n // Generate intent ID and expiration upfront for consistent response\n const intentId = randomUUID();\n const expiresAt = this.dateTimeProvider\n .now()\n .add(INTENT_TTL_MINUTES, \"minutes\")\n .toISOString();\n\n // Find user by email (silent fail for security)\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n if (!user) {\n // Silent fail - don't reveal that email doesn't exist\n this.log.debug(\"Password reset requested for non-existent email\", {\n email,\n });\n return { intentId, expiresAt };\n }\n\n // Find the credentials identity for this user\n const identity = await this.identities(userRealmName)\n .findOne({\n where: {\n userId: { eq: user.id },\n provider: { eq: \"credentials\" },\n },\n })\n .catch(() => undefined);\n\n if (!identity) {\n // User doesn't have credentials identity (maybe OAuth only)\n this.log.debug(\"Password reset requested for user without credentials\", {\n userId: user.id,\n });\n return { intentId, expiresAt };\n }\n\n // Create verification using verification controller\n // This handles: token generation, expiration, rate limiting, cooldown\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: email },\n });\n\n // Send password reset notification with the code\n await this.userNotifications(userRealmName)?.passwordReset.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n // Store intent in cache\n const intent: PasswordResetIntent = {\n email,\n userId: user.id,\n identityId: identity.id,\n realmName: userRealmName,\n expiresAt,\n };\n\n await this.intentCache.set(intentId, intent);\n\n this.log.info(\"Password reset intent created\", {\n intentId,\n userId: user.id,\n email,\n });\n } catch (error) {\n // If rate limit or cooldown hit, still return success for security\n this.log.warn(\"Failed to create password reset verification\", error);\n }\n\n return { intentId, expiresAt };\n }\n\n /**\n * Phase 2: Complete password reset using an intent.\n *\n * Validates the verification code, updates the password,\n * and invalidates all existing sessions.\n *\n * @param body - Request body with intentId, code, and newPassword\n */\n public async completePasswordReset(\n body: CompletePasswordResetRequest,\n ): Promise<void> {\n this.log.trace(\"Completing password reset\", { intentId: body.intentId });\n\n // Fetch intent from cache\n const intent = await this.intentCache.get(body.intentId);\n if (!intent) {\n this.log.warn(\"Invalid or expired password reset intent\", {\n intentId: body.intentId,\n });\n throw new HttpError({\n status: 410,\n message: \"Invalid or expired password reset intent\",\n });\n }\n\n // Verify code using verification controller\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: intent.email, token: body.code },\n })\n .catch(() => {\n this.log.warn(\"Invalid verification code for password reset\", {\n intentId: body.intentId,\n email: intent.email,\n });\n throw new BadRequestError(\"Invalid or expired verification code\");\n });\n\n // If already verified, this is a code reuse attempt\n if (result.alreadyVerified) {\n this.log.warn(\"Verification code reuse attempt\", {\n intentId: body.intentId,\n email: intent.email,\n });\n throw new BadRequestError(\"Verification code has already been used\");\n }\n\n // Atomically delete cache key to prevent replay\n await this.intentCache.invalidate(body.intentId);\n\n // Hash the new password\n const hashedPassword = await this.cryptoProvider.hashPassword(\n body.newPassword,\n );\n\n // Update the identity with new password\n await this.identities(intent.realmName).updateById(intent.identityId, {\n password: hashedPassword,\n });\n\n // Invalidate all existing sessions for this user\n await this.sessions(intent.realmName).deleteMany({\n userId: { eq: intent.userId },\n });\n\n this.log.info(\"Password reset completed\", {\n userId: intent.userId,\n email: intent.email,\n });\n\n const realm = this.realmProvider.getRealm(intent.realmName);\n\n // Audit: password reset\n await this.userAudits(intent.realmName)?.recordUser(\"update\", {\n userId: intent.userId,\n userEmail: intent.email,\n userRealm: realm.name,\n resourceId: intent.userId,\n description: \"Password reset completed\",\n metadata: { email: intent.email },\n });\n\n // Audit: sessions invalidated (security event)\n await this.userAudits(intent.realmName)?.record(\n \"security\",\n \"sessions_invalidated\",\n {\n userId: intent.userId,\n userEmail: intent.email,\n userRealm: realm.name,\n resourceId: intent.userId,\n severity: \"warning\",\n description: \"All sessions invalidated after password reset\",\n },\n );\n }\n\n // Legacy methods kept for backward compatibility\n\n /**\n * @deprecated Use createPasswordResetIntent instead\n */\n public async requestPasswordReset(\n email: string,\n userRealmName?: string,\n ): Promise<boolean> {\n await this.createPasswordResetIntent(email, userRealmName);\n return true;\n }\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public async validateResetToken(\n email: string,\n token: string,\n _userRealmName?: string,\n ): Promise<string> {\n // Verify using verification controller\n const isValid = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token },\n })\n .catch(() => undefined);\n\n if (!isValid?.ok) {\n throw new BadRequestError(\"Invalid or expired reset token\");\n }\n\n return email;\n }\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public async resetPassword(\n email: string,\n token: string,\n newPassword: string,\n userRealmName?: string,\n ): Promise<void> {\n // Verify token using verification controller\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token },\n })\n .catch(() => {\n throw new BadRequestError(\"Invalid or expired reset token\");\n });\n\n // If already verified, this is a token reuse attempt\n if (result.alreadyVerified) {\n throw new BadRequestError(\"Invalid or expired reset token\");\n }\n\n // Find user and identity\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\n\n const identity = await this.identities(userRealmName).findOne({\n where: {\n userId: { eq: user.id },\n provider: { eq: \"credentials\" },\n },\n });\n\n // Hash the new password\n const hashedPassword = await this.cryptoProvider.hashPassword(newPassword);\n\n // Update the identity with new password\n await this.identities(userRealmName).updateById(identity.id, {\n password: hashedPassword,\n });\n\n // Invalidate all existing sessions for this user\n await this.sessions(userRealmName).deleteMany({\n userId: { eq: user.id },\n });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n // Audit: password reset (legacy method)\n await this.userAudits(userRealmName)?.recordUser(\"update\", {\n userId: user.id,\n userEmail: email,\n userRealm: realm.name,\n resourceId: user.id,\n description: \"Password reset completed (legacy)\",\n metadata: { email },\n });\n\n // Audit: sessions invalidated\n await this.userAudits(userRealmName)?.record(\n \"security\",\n \"sessions_invalidated\",\n {\n userId: user.id,\n userEmail: email,\n userRealm: realm.name,\n resourceId: user.id,\n severity: \"warning\",\n description: \"All sessions invalidated after password reset\",\n },\n );\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { $inject, Alepha } from \"alepha\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $cache } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { CryptoProvider } from \"alepha/security\";\nimport { BadRequestError, ConflictError, HttpError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { CompleteRegistrationRequest } from \"../schemas/completeRegistrationRequestSchema.ts\";\nimport type { RegisterRequest } from \"../schemas/registerRequestSchema.ts\";\nimport type { RegistrationIntentResponse } from \"../schemas/registrationIntentResponseSchema.ts\";\nimport { UserAudits } from \"./UserAudits.ts\";\nimport { UserNotifications } from \"./UserNotifications.ts\";\n\n/**\n * Intent stored in cache during the registration flow.\n */\ninterface RegistrationIntent {\n data: {\n username?: string;\n email?: string;\n phoneNumber?: string;\n firstName?: string;\n lastName?: string;\n picture?: string;\n passwordHash: string;\n };\n requirements: {\n email: boolean;\n phone: boolean;\n captcha: boolean;\n };\n realmName?: string;\n expiresAt: string;\n}\n\nconst INTENT_TTL_MINUTES = 10;\n\nexport class RegistrationService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly verificationController = $client<VerificationController>();\n protected readonly realmProvider = $inject(RealmProvider);\n\n protected readonly intentCache = $cache<RegistrationIntent>({\n name: \"registration-intents\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n protected userNotifications(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.notifications) {\n return this.alepha.inject(UserNotifications);\n }\n return undefined;\n }\n\n /**\n * Phase 1: Create a registration intent.\n *\n * Validates the registration data, checks for existing users,\n * creates verification sessions, and stores the intent in cache.\n */\n public async createRegistrationIntent(\n body: RegisterRequest,\n userRealmName?: string,\n ): Promise<RegistrationIntentResponse> {\n this.log.trace(\"Creating registration intent\", {\n email: body.email,\n username: body.username,\n userRealmName,\n });\n\n const realmSettings = this.realmProvider.getRealm(userRealmName).settings;\n\n // Check if registration is allowed\n if (realmSettings?.registrationAllowed === false) {\n this.log.warn(\"Registration not allowed for realm\", { userRealmName });\n throw new BadRequestError(\"Registration is not allowed\");\n }\n\n // Validate required fields based on settings\n if (realmSettings?.usernameRequired && !body.username) {\n this.log.debug(\"Registration rejected: username required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Username is required\");\n }\n\n if (body.username) {\n const usernameRegExp = realmSettings?.usernameRegExp;\n if (usernameRegExp) {\n const regex = new RegExp(usernameRegExp);\n if (!regex.test(body.username)) {\n this.log.debug(\"Registration rejected: username regex mismatch\", {\n userRealmName,\n username: body.username,\n });\n throw new BadRequestError(\n \"Username does not meet the required format\",\n );\n }\n }\n }\n\n if (realmSettings?.emailRequired !== false && !body.email) {\n this.log.debug(\"Registration rejected: email required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Email is required\");\n }\n\n if (realmSettings?.phoneRequired && !body.phoneNumber) {\n this.log.debug(\"Registration rejected: phone required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Phone number is required\");\n }\n\n // Check for existing users (username, email, phone)\n await this.checkUserAvailability(body, userRealmName);\n\n // Hash the password\n const passwordHash = await this.cryptoProvider.hashPassword(body.password);\n\n // Determine requirements based on realm settings\n const requirements = {\n email: realmSettings?.verifyEmailRequired === true && !!body.email,\n phone: realmSettings?.verifyPhoneRequired === true && !!body.phoneNumber,\n captcha: false, // Always false for now\n };\n\n // Create verification sessions and send codes\n if (requirements.email && body.email) {\n await this.sendEmailVerification(body.email, userRealmName);\n }\n\n if (requirements.phone && body.phoneNumber) {\n await this.sendPhoneVerification(body.phoneNumber, userRealmName);\n }\n\n // Generate intent ID and expiration\n const intentId = randomUUID();\n const expiresAt = this.dateTimeProvider\n .now()\n .add(INTENT_TTL_MINUTES, \"minutes\")\n .toISOString();\n\n // Store intent in cache\n const intent: RegistrationIntent = {\n data: {\n username: body.username,\n email: body.email,\n phoneNumber: body.phoneNumber,\n firstName: body.firstName,\n lastName: body.lastName,\n picture: body.picture,\n passwordHash,\n },\n requirements,\n realmName: userRealmName,\n expiresAt,\n };\n\n await this.intentCache.set(intentId, intent);\n\n this.log.info(\"Registration intent created\", {\n intentId,\n email: body.email,\n username: body.username,\n requiresEmailVerification: requirements.email,\n requiresPhoneVerification: requirements.phone,\n });\n\n return {\n intentId,\n expectCaptcha: requirements.captcha,\n expectEmailVerification: requirements.email,\n expectPhoneVerification: requirements.phone,\n expiresAt,\n };\n }\n\n /**\n * Phase 2: Complete registration using an intent.\n *\n * Validates all requirements (verification codes, captcha),\n * creates the user and credentials, and returns the user.\n */\n public async completeRegistration(\n body: CompleteRegistrationRequest,\n ): Promise<UserEntity> {\n this.log.trace(\"Completing registration\", { intentId: body.intentId });\n\n // Fetch intent from cache\n const intent = await this.intentCache.get(body.intentId);\n if (!intent) {\n this.log.warn(\"Invalid or expired registration intent\", {\n intentId: body.intentId,\n });\n throw new HttpError({\n status: 410,\n message: \"Invalid or expired registration intent\",\n });\n }\n\n const userRealmName = intent.realmName;\n const userRepository = this.realmProvider.userRepository(userRealmName);\n const identityRepository =\n this.realmProvider.identityRepository(userRealmName);\n\n // Validate email verification if required\n if (intent.requirements.email) {\n if (!body.emailCode) {\n this.log.debug(\"Registration completion missing email code\", {\n intentId: body.intentId,\n });\n throw new BadRequestError(\"Email verification code is required\");\n }\n\n if (!intent.data.email) {\n throw new BadRequestError(\"Email is missing from registration intent\");\n }\n\n await this.verifyEmailCode(intent.data.email, body.emailCode);\n }\n\n // Validate phone verification if required\n if (intent.requirements.phone) {\n if (!body.phoneCode) {\n this.log.debug(\"Registration completion missing phone code\", {\n intentId: body.intentId,\n });\n throw new BadRequestError(\"Phone verification code is required\");\n }\n\n if (!intent.data.phoneNumber) {\n throw new BadRequestError(\n \"Phone number is missing from registration intent\",\n );\n }\n\n await this.verifyPhoneCode(intent.data.phoneNumber, body.phoneCode);\n }\n\n // Validate captcha if required (placeholder for future implementation)\n if (intent.requirements.captcha) {\n if (!body.captchaToken) {\n throw new BadRequestError(\"Captcha verification is required\");\n }\n // TODO: Validate captcha token\n }\n\n // Final availability check (race condition guard)\n await this.checkUserAvailability(\n {\n username: intent.data.username,\n email: intent.data.email,\n phoneNumber: intent.data.phoneNumber,\n },\n userRealmName,\n );\n\n // Atomically delete cache key to prevent replay\n await this.intentCache.invalidate(body.intentId);\n\n // Create the user\n const user = await userRepository.create({\n realm: userRealmName,\n username: intent.data.username,\n email: intent.data.email,\n phoneNumber: intent.data.phoneNumber,\n firstName: intent.data.firstName,\n lastName: intent.data.lastName,\n picture: intent.data.picture,\n roles: [\"user\"],\n enabled: true,\n emailVerified: intent.requirements.email, // Marked as verified if we verified during registration\n });\n\n // Create credentials identity\n await identityRepository.create({\n userId: user.id,\n provider: \"credentials\",\n password: intent.data.passwordHash,\n });\n\n this.log.info(\"User registered successfully\", {\n userId: user.id,\n email: user.email,\n username: user.username,\n });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.recordUser(\"create\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: \"User registered\",\n metadata: {\n username: user.username,\n email: user.email,\n emailVerified: user.emailVerified,\n registrationMethod: \"credentials\",\n },\n });\n\n return user;\n }\n\n /**\n * Check if username, email, and phone are available.\n */\n protected async checkUserAvailability(\n body: Pick<RegisterRequest, \"username\" | \"email\" | \"phoneNumber\">,\n userRealmName?: string,\n ): Promise<void> {\n const userRepository = this.realmProvider.userRepository(userRealmName);\n\n if (body.username) {\n const existingUser = await userRepository\n .findOne({ where: { username: { eq: body.username } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Username already taken\", { username: body.username });\n throw new ConflictError(\"User with this username already exists\");\n }\n }\n\n if (body.email) {\n const existingUser = await userRepository\n .findOne({ where: { email: { eq: body.email } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Email already taken\", { email: body.email });\n throw new ConflictError(\"User with this email already exists\");\n }\n }\n\n if (body.phoneNumber) {\n const existingUser = await userRepository\n .findOne({ where: { phoneNumber: { eq: body.phoneNumber } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Phone number already taken\", {\n phoneNumber: body.phoneNumber,\n });\n throw new ConflictError(\"User with this phone number already exists\");\n }\n }\n }\n\n /**\n * Send email verification code.\n */\n protected async sendEmailVerification(\n email: string,\n realmName?: string,\n ): Promise<void> {\n this.log.debug(\"Sending email verification code\", { email });\n\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: email },\n });\n\n await this.userNotifications(realmName)?.emailVerification.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n this.log.debug(\"Email verification code sent\", { email });\n }\n\n /**\n * Send phone verification code.\n */\n protected async sendPhoneVerification(\n phoneNumber: string,\n realmName?: string,\n ): Promise<void> {\n this.log.debug(\"Sending phone verification code\", { phoneNumber });\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: phoneNumber },\n });\n\n await this.userNotifications(realmName)?.phoneVerification.push({\n contact: phoneNumber,\n variables: {\n phoneNumber,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n this.log.debug(\"Phone verification code sent\", { phoneNumber });\n } catch (error) {\n // Silent fail - verification service may have rate limiting\n this.log.warn(\"Failed to send phone verification code\", {\n phoneNumber,\n error,\n });\n }\n }\n\n /**\n * Verify email code using verification service.\n */\n protected async verifyEmailCode(email: string, code: string): Promise<void> {\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token: code },\n })\n .catch(() => {\n this.log.warn(\"Invalid email verification code\", { email });\n throw new BadRequestError(\"Invalid or expired email verification code\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Email verification code already used\", { email });\n throw new BadRequestError(\n \"Email verification code has already been used\",\n );\n }\n }\n\n /**\n * Verify phone code using verification service.\n */\n protected async verifyPhoneCode(\n phoneNumber: string,\n code: string,\n ): Promise<void> {\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: phoneNumber, token: code },\n })\n .catch(() => {\n this.log.warn(\"Invalid phone verification code\", { phoneNumber });\n throw new BadRequestError(\"Invalid or expired phone verification code\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Phone verification code already used\", { phoneNumber });\n throw new BadRequestError(\n \"Phone verification code has already been used\",\n );\n }\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { completePasswordResetRequestSchema } from \"../schemas/completePasswordResetRequestSchema.ts\";\nimport { completeRegistrationRequestSchema } from \"../schemas/completeRegistrationRequestSchema.ts\";\nimport { passwordResetIntentResponseSchema } from \"../schemas/passwordResetIntentResponseSchema.ts\";\nimport { registerQuerySchema } from \"../schemas/registerQuerySchema.ts\";\nimport { registerRequestSchema } from \"../schemas/registerRequestSchema.ts\";\nimport { registrationIntentResponseSchema } from \"../schemas/registrationIntentResponseSchema.ts\";\nimport { userResourceSchema } from \"../schemas/userResourceSchema.ts\";\nimport { CredentialService } from \"../services/CredentialService.ts\";\nimport { RegistrationService } from \"../services/RegistrationService.ts\";\nimport { UserService } from \"../services/UserService.ts\";\n\nexport class UserController {\n protected readonly url = \"/users\";\n protected readonly group = \"users\";\n protected readonly credentialService = $inject(CredentialService);\n protected readonly userService = $inject(UserService);\n protected readonly registrationService = $inject(RegistrationService);\n\n /**\n * Phase 1: Create a registration intent.\n * Validates data, creates verification sessions, and stores intent in cache.\n */\n public readonly createRegistrationIntent = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/register`,\n secure: false,\n schema: {\n body: registerRequestSchema,\n query: registerQuerySchema,\n response: registrationIntentResponseSchema,\n },\n handler: ({ body, query }) =>\n this.registrationService.createRegistrationIntent(\n body,\n query.userRealmName,\n ),\n });\n\n /**\n * Phase 2: Complete registration using an intent.\n * Validates verification codes and creates the user.\n */\n public readonly createUserFromIntent = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/register/complete`,\n secure: false,\n schema: {\n body: completeRegistrationRequestSchema,\n response: userResourceSchema,\n },\n handler: ({ body }) => this.registrationService.completeRegistration(body),\n });\n\n /**\n * Phase 1: Create a password reset intent.\n * Validates email, sends verification code, and stores intent in cache.\n */\n public readonly createPasswordResetIntent = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/password-reset`,\n secure: false,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: passwordResetIntentResponseSchema,\n },\n handler: ({ body, query }) =>\n this.credentialService.createPasswordResetIntent(\n body.email,\n query.userRealmName,\n ),\n });\n\n /**\n * Phase 2: Complete password reset using an intent.\n * Validates verification code, updates password, and invalidates sessions.\n */\n public readonly completePasswordReset = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/password-reset/complete`,\n secure: false,\n schema: {\n body: completePasswordResetRequestSchema,\n response: okSchema,\n },\n handler: async ({ body }) => {\n await this.credentialService.completePasswordReset(body);\n return { ok: true };\n },\n });\n\n // Legacy endpoints for backward compatibility\n\n /**\n * @deprecated Use createPasswordResetIntent instead\n */\n public requestPasswordReset = $action({\n path: \"/users/password-reset/request\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.credentialService.requestPasswordReset(\n body.email,\n query.userRealmName,\n );\n\n return {\n success: true,\n message:\n \"If an account exists with this email, a password reset code has been sent.\",\n };\n },\n });\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public validateResetToken = $action({\n path: \"/users/password-reset/validate\",\n group: this.group,\n schema: {\n query: t.object({\n email: t.email(),\n token: t.string(),\n userRealmName: t.optional(t.string()),\n }),\n response: t.object({\n valid: t.boolean(),\n email: t.optional(t.email()),\n }),\n },\n handler: async ({ query }) => {\n try {\n const email = await this.credentialService.validateResetToken(\n query.email,\n query.token,\n query.userRealmName,\n );\n return {\n valid: true,\n email,\n };\n } catch {\n return {\n valid: false,\n };\n }\n },\n });\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public resetPassword = $action({\n path: \"/users/password-reset/reset\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n token: t.string(),\n newPassword: t.string({ minLength: 8 }),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.credentialService.resetPassword(\n body.email,\n body.token,\n body.newPassword,\n query.userRealmName,\n );\n\n return {\n success: true,\n message: \"Password has been reset successfully. Please log in.\",\n };\n },\n });\n\n /**\n * Request email verification.\n * Generates a verification token using verification service and sends an email to the user.\n * @param method - The verification method: \"code\" (default) sends a 6-digit code, \"link\" sends a clickable verification link.\n * @param verifyUrl - Required when method is \"link\". The base URL for the verification link. Token and email will be appended as query params.\n */\n public requestEmailVerification = $action({\n path: \"/users/email-verification/request\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n method: t.optional(\n t.enum([\"code\", \"link\"], {\n default: \"code\",\n description:\n 'Verification method: \"code\" sends a 6-digit code, \"link\" sends a clickable verification link.',\n }),\n ),\n verifyUrl: t.optional(\n t.string({\n description:\n 'Base URL for verification link. Required when method is \"link\". Token and email will be appended as query params.',\n }),\n ),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n const method = query.method ?? \"code\";\n await this.userService.requestEmailVerification(\n body.email,\n query.userRealmName,\n method,\n query.verifyUrl,\n );\n\n return {\n success: true,\n message:\n method === \"link\"\n ? \"If an account exists with this email, a verification link has been sent.\"\n : \"If an account exists with this email, a verification code has been sent.\",\n };\n },\n });\n\n /**\n * Verify email with a valid token.\n * Updates the user's emailVerified status.\n */\n public verifyEmail = $action({\n path: \"/users/email-verification/verify\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n token: t.string(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.userService.verifyEmail(\n body.email,\n body.token,\n query.userRealmName,\n );\n\n return {\n success: true,\n message: \"Email has been verified successfully.\",\n };\n },\n });\n\n /**\n * Check if an email is verified.\n */\n public checkEmailVerification = $action({\n path: \"/users/email-verification/check\",\n group: this.group,\n schema: {\n query: t.object({\n email: t.email(),\n userRealmName: t.optional(t.string()),\n }),\n response: t.object({\n verified: t.boolean(),\n }),\n },\n handler: async ({ query }) => {\n const verified = await this.userService.isEmailVerified(\n query.email,\n query.userRealmName,\n );\n\n return {\n verified,\n };\n },\n });\n}\n","import type { FileLike, StreamLike } from \"alepha\";\n\n/**\n * Options for creating a file from a URL\n */\nexport interface CreateFileFromUrlOptions {\n /**\n * The URL to load the file from (file://, http://, or https://)\n */\n url: string;\n /**\n * The MIME type of the file (optional, will be detected from filename if not provided)\n */\n type?: string;\n /**\n * The name of the file (optional, will be extracted from URL if not provided)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a path (URL with file:// scheme)\n */\nexport interface CreateFileFromPathOptions {\n /**\n * The path to the file on the local filesystem\n */\n path: string;\n /**\n * The MIME type of the file (optional, will be detected from filename if not provided)\n */\n type?: string;\n /**\n * The name of the file (optional, will be extracted from URL if not provided)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a Buffer\n */\nexport interface CreateFileFromBufferOptions {\n /**\n * The Buffer containing the file data\n */\n buffer: Buffer;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a stream\n */\nexport interface CreateFileFromStreamOptions {\n /**\n * The readable stream containing the file data\n */\n stream: StreamLike;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n /**\n * The size of the file in bytes (optional)\n */\n size?: number;\n}\n\n/**\n * Options for creating a file from text content\n */\nexport interface CreateFileFromTextOptions {\n /**\n * The text content to create the file from\n */\n text: string;\n /**\n * The MIME type of the file (default: text/plain)\n */\n type?: string;\n /**\n * The name of the file (default: \"file.txt\")\n */\n name?: string;\n}\n\nexport interface CreateFileFromResponseOptions {\n /**\n * The Response object containing the file data\n */\n response: Response;\n /**\n * Override the name (optional, uses filename from Content-Disposition header if not provided)\n */\n name?: string;\n /**\n * Override the MIME type (optional, uses file.type if not provided)\n */\n type?: string;\n}\n\n/**\n * Options for creating a file from a Web File object\n */\nexport interface CreateFileFromWebFileOptions {\n /**\n * The Web File object\n */\n file: File;\n /**\n * Override the MIME type (optional, uses file.type if not provided)\n */\n type?: string;\n /**\n * Override the name (optional, uses file.name if not provided)\n */\n name?: string;\n /**\n * Override the size (optional, uses file.size if not provided)\n */\n size?: number;\n}\n\n/**\n * Options for creating a file from an ArrayBuffer\n */\nexport interface CreateFileFromArrayBufferOptions {\n /**\n * The ArrayBuffer containing the file data\n */\n arrayBuffer: ArrayBuffer;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n}\n\n/**\n * Union type for all createFile options\n */\nexport type CreateFileOptions =\n | CreateFileFromUrlOptions\n | CreateFileFromPathOptions\n | CreateFileFromBufferOptions\n | CreateFileFromStreamOptions\n | CreateFileFromTextOptions\n | CreateFileFromWebFileOptions\n | CreateFileFromResponseOptions\n | CreateFileFromArrayBufferOptions;\n\n/**\n * Options for rm (remove) operation\n */\nexport interface RmOptions {\n /**\n * If true, removes directories and their contents recursively\n */\n recursive?: boolean;\n /**\n * If true, no error will be thrown if the path does not exist\n */\n force?: boolean;\n}\n\n/**\n * Options for cp (copy) operation\n */\nexport interface CpOptions {\n /**\n * If true, copy directories recursively\n */\n recursive?: boolean;\n /**\n * If true, overwrite existing destination\n */\n force?: boolean;\n}\n\n/**\n * Options for mkdir operation\n */\nexport interface MkdirOptions {\n /**\n * If true, creates parent directories as needed\n *\n * @default true\n */\n recursive?: boolean;\n /**\n * If true, does not throw an error if the directory already exists\n *\n * @default true\n */\n force?: boolean;\n /**\n * File mode (permission and sticky bits)\n */\n mode?: number;\n}\n\n/**\n * Options for ls (list) operation\n */\nexport interface LsOptions {\n /**\n * If true, list contents of directories recursively\n */\n recursive?: boolean;\n /**\n * If true, include hidden files (starting with .)\n */\n hidden?: boolean;\n}\n\n/**\n * FileSystem interface providing utilities for working with files.\n */\nexport abstract class FileSystemProvider {\n /**\n * Joins multiple path segments into a single path.\n *\n * @param paths - The path segments to join\n * @returns The joined path\n */\n abstract join(...paths: string[]): string;\n\n /**\n * Creates a FileLike object from various sources.\n *\n * @param options - Options for creating the file\n * @returns A FileLike object\n */\n abstract createFile(options: CreateFileOptions): FileLike;\n\n /**\n * Removes a file or directory.\n *\n * @param path - The path to remove\n * @param options - Remove options\n */\n abstract rm(path: string, options?: RmOptions): Promise<void>;\n\n /**\n * Copies a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n * @param options - Copy options\n */\n abstract cp(src: string, dest: string, options?: CpOptions): Promise<void>;\n\n /**\n * Moves/renames a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n */\n abstract mv(src: string, dest: string): Promise<void>;\n\n /**\n * Creates a directory.\n *\n * @param path - The directory path to create\n * @param options - Mkdir options\n */\n abstract mkdir(path: string, options?: MkdirOptions): Promise<void>;\n\n /**\n * Lists files in a directory.\n *\n * @param path - The directory path to list\n * @param options - List options\n * @returns Array of filenames\n */\n abstract ls(path: string, options?: LsOptions): Promise<string[]>;\n\n /**\n * Checks if a file or directory exists.\n *\n * @param path - The path to check\n * @returns True if the path exists, false otherwise\n */\n abstract exists(path: string): Promise<boolean>;\n\n /**\n * Reads the content of a file.\n *\n * @param path - The file path to read\n * @returns The file content as a Buffer\n */\n abstract readFile(path: string): Promise<Buffer>;\n\n /**\n * Writes data to a file.\n *\n * @param path - The file path to write to\n * @param data - The data to write (Buffer or string)\n */\n abstract writeFile(\n path: string,\n data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void>;\n\n /**\n * Reads the content of a file as a string.\n *\n * @param path - The file path to read\n * @returns The file content as a string\n */\n abstract readTextFile(path: string): Promise<string>;\n\n /**\n * Reads the content of a file as JSON.\n *\n * @param path - The file path to read\n * @returns The parsed JSON content\n */\n abstract readJsonFile<T = unknown>(path: string): Promise<T>;\n}\n","import { join as nodeJoin } from \"node:path\";\nimport { $inject, type FileLike, Json } from \"alepha\";\nimport type {\n CpOptions,\n CreateFileOptions,\n FileSystemProvider,\n LsOptions,\n MkdirOptions,\n RmOptions,\n} from \"./FileSystemProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface MemoryFileSystemProviderOptions {\n /**\n * Error to throw on mkdir operations (for testing error handling)\n */\n mkdirError?: Error | null;\n /**\n * Error to throw on writeFile operations (for testing error handling)\n */\n writeFileError?: Error | null;\n /**\n * Error to throw on readFile operations (for testing error handling)\n */\n readFileError?: Error | null;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * In-memory implementation of FileSystemProvider for testing.\n *\n * This provider stores all files and directories in memory, making it ideal for\n * unit tests that need to verify file operations without touching the real file system.\n *\n * @example\n * ```typescript\n * // In tests, substitute the real FileSystemProvider with MemoryFileSystemProvider\n * const alepha = Alepha.create().with({\n * provide: FileSystemProvider,\n * use: MemoryFileSystemProvider,\n * });\n *\n * // Run code that uses FileSystemProvider\n * const service = alepha.inject(MyService);\n * await service.saveFile(\"test.txt\", \"Hello World\");\n *\n * // Verify the file was written\n * const memoryFs = alepha.inject(MemoryFileSystemProvider);\n * expect(memoryFs.files.get(\"test.txt\")?.toString()).toBe(\"Hello World\");\n * ```\n */\nexport class MemoryFileSystemProvider implements FileSystemProvider {\n protected json = $inject(Json);\n\n /**\n * In-memory storage for files (path -> content)\n */\n public files = new Map<string, Buffer>();\n\n /**\n * In-memory storage for directories\n */\n public directories = new Set<string>();\n\n /**\n * Track mkdir calls for test assertions\n */\n public mkdirCalls: Array<{ path: string; options?: MkdirOptions }> = [];\n\n /**\n * Track writeFile calls for test assertions\n */\n public writeFileCalls: Array<{ path: string; data: string }> = [];\n\n /**\n * Track readFile calls for test assertions\n */\n public readFileCalls: Array<string> = [];\n\n /**\n * Track rm calls for test assertions\n */\n public rmCalls: Array<{ path: string; options?: RmOptions }> = [];\n\n /**\n * Track join calls for test assertions\n */\n public joinCalls: Array<string[]> = [];\n\n /**\n * Error to throw on mkdir (for testing error handling)\n */\n public mkdirError: Error | null = null;\n\n /**\n * Error to throw on writeFile (for testing error handling)\n */\n public writeFileError: Error | null = null;\n\n /**\n * Error to throw on readFile (for testing error handling)\n */\n public readFileError: Error | null = null;\n\n constructor(options: MemoryFileSystemProviderOptions = {}) {\n this.mkdirError = options.mkdirError ?? null;\n this.writeFileError = options.writeFileError ?? null;\n this.readFileError = options.readFileError ?? null;\n }\n\n /**\n * Join path segments using forward slashes.\n * Uses Node's path.join for proper normalization (handles .. and .)\n */\n public join(...paths: string[]): string {\n this.joinCalls.push(paths);\n return nodeJoin(...paths);\n }\n\n /**\n * Create a FileLike object from various sources.\n */\n public createFile(options: CreateFileOptions): FileLike {\n if (\"path\" in options) {\n const filePath = options.path;\n const buffer = this.files.get(filePath);\n if (buffer === undefined) {\n throw new Error(\n `ENOENT: no such file or directory, open '${filePath}'`,\n );\n }\n return {\n name: options.name ?? filePath.split(\"/\").pop() ?? \"file\",\n type: options.type ?? \"application/octet-stream\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Stream not implemented in MemoryFileSystemProvider\");\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => buffer.toString(\"utf-8\"),\n };\n }\n\n if (\"buffer\" in options) {\n const buffer = options.buffer;\n return {\n name: options.name ?? \"file\",\n type: options.type ?? \"application/octet-stream\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Stream not implemented in MemoryFileSystemProvider\");\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => buffer.toString(\"utf-8\"),\n };\n }\n\n if (\"text\" in options) {\n const buffer = Buffer.from(options.text, \"utf-8\");\n return {\n name: options.name ?? \"file.txt\",\n type: options.type ?? \"text/plain\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Stream not implemented in MemoryFileSystemProvider\");\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => options.text,\n };\n }\n\n throw new Error(\n \"MemoryFileSystemProvider.createFile: unsupported options. Only buffer and text are supported.\",\n );\n }\n\n /**\n * Remove a file or directory from memory.\n */\n public async rm(path: string, options?: RmOptions): Promise<void> {\n this.rmCalls.push({ path, options });\n\n const exists = this.files.has(path) || this.directories.has(path);\n\n if (!exists && !options?.force) {\n throw new Error(`ENOENT: no such file or directory, rm '${path}'`);\n }\n\n if (this.directories.has(path)) {\n if (options?.recursive) {\n // Remove directory and all contents\n this.directories.delete(path);\n for (const filePath of this.files.keys()) {\n if (filePath.startsWith(`${path}/`)) {\n this.files.delete(filePath);\n }\n }\n for (const dirPath of this.directories) {\n if (dirPath.startsWith(`${path}/`)) {\n this.directories.delete(dirPath);\n }\n }\n } else {\n throw new Error(\n `EISDIR: illegal operation on a directory, rm '${path}'`,\n );\n }\n } else {\n this.files.delete(path);\n }\n }\n\n /**\n * Copy a file or directory in memory.\n */\n public async cp(\n src: string,\n dest: string,\n options?: CpOptions,\n ): Promise<void> {\n if (this.directories.has(src)) {\n if (!options?.recursive) {\n throw new Error(\n `Cannot copy directory without recursive option: ${src}`,\n );\n }\n // Copy directory and contents\n this.directories.add(dest);\n for (const [filePath, content] of this.files) {\n if (filePath.startsWith(`${src}/`)) {\n const newPath = filePath.replace(src, dest);\n this.files.set(newPath, Buffer.from(content));\n }\n }\n } else if (this.files.has(src)) {\n const content = this.files.get(src)!;\n this.files.set(dest, Buffer.from(content));\n } else {\n throw new Error(`ENOENT: no such file or directory, cp '${src}'`);\n }\n }\n\n /**\n * Move/rename a file or directory in memory.\n */\n public async mv(src: string, dest: string): Promise<void> {\n if (this.directories.has(src)) {\n // Move directory and contents\n this.directories.delete(src);\n this.directories.add(dest);\n for (const [filePath, content] of this.files) {\n if (filePath.startsWith(`${src}/`)) {\n const newPath = filePath.replace(src, dest);\n this.files.delete(filePath);\n this.files.set(newPath, content);\n }\n }\n } else if (this.files.has(src)) {\n const content = this.files.get(src)!;\n this.files.delete(src);\n this.files.set(dest, content);\n } else {\n throw new Error(`ENOENT: no such file or directory, mv '${src}'`);\n }\n }\n\n /**\n * Create a directory in memory.\n */\n public async mkdir(path: string, options?: MkdirOptions): Promise<void> {\n this.mkdirCalls.push({ path, options });\n\n if (this.mkdirError) {\n throw this.mkdirError;\n }\n\n if (this.directories.has(path) && !options?.recursive) {\n throw new Error(`EEXIST: file already exists, mkdir '${path}'`);\n }\n\n this.directories.add(path);\n\n // If recursive, create parent directories\n if (options?.recursive) {\n const parts = path.split(\"/\").filter(Boolean);\n let current = \"\";\n for (const part of parts) {\n current = current ? `${current}/${part}` : part;\n this.directories.add(current);\n }\n }\n }\n\n /**\n * List files in a directory.\n */\n public async ls(path: string, options?: LsOptions): Promise<string[]> {\n const normalizedPath = path.replace(/\\/$/, \"\");\n const entries = new Set<string>();\n\n // Find files in the directory\n for (const filePath of this.files.keys()) {\n if (filePath.startsWith(`${normalizedPath}/`)) {\n const relativePath = filePath.slice(normalizedPath.length + 1);\n const parts = relativePath.split(\"/\");\n\n if (options?.recursive) {\n entries.add(relativePath);\n } else {\n entries.add(parts[0]);\n }\n }\n }\n\n // Find subdirectories\n for (const dirPath of this.directories) {\n if (\n dirPath.startsWith(`${normalizedPath}/`) &&\n dirPath !== normalizedPath\n ) {\n const relativePath = dirPath.slice(normalizedPath.length + 1);\n const parts = relativePath.split(\"/\");\n\n if (options?.recursive) {\n entries.add(relativePath);\n } else if (parts.length === 1) {\n entries.add(parts[0]);\n }\n }\n }\n\n let result = Array.from(entries);\n\n // Filter hidden files unless requested\n if (!options?.hidden) {\n result = result.filter((entry) => !entry.startsWith(\".\"));\n }\n\n return result.sort();\n }\n\n /**\n * Check if a file or directory exists in memory.\n */\n public async exists(path: string): Promise<boolean> {\n return this.files.has(path) || this.directories.has(path);\n }\n\n /**\n * Read a file from memory.\n */\n public async readFile(path: string): Promise<Buffer> {\n this.readFileCalls.push(path);\n\n if (this.readFileError) {\n throw this.readFileError;\n }\n\n const content = this.files.get(path);\n if (!content) {\n throw new Error(`ENOENT: no such file or directory, open '${path}'`);\n }\n return content;\n }\n\n /**\n * Read a file from memory as text.\n */\n public async readTextFile(path: string): Promise<string> {\n const buffer = await this.readFile(path);\n return buffer.toString(\"utf-8\");\n }\n\n /**\n * Read a file from memory as JSON.\n */\n public async readJsonFile<T = unknown>(path: string): Promise<T> {\n const text = await this.readTextFile(path);\n return this.json.parse(text) as T;\n }\n\n /**\n * Write a file to memory.\n */\n public async writeFile(\n path: string,\n data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void> {\n const dataStr =\n typeof data === \"string\"\n ? data\n : data instanceof Buffer || data instanceof Uint8Array\n ? data.toString(\"utf-8\")\n : await data.text();\n\n this.writeFileCalls.push({ path, data: dataStr });\n\n if (this.writeFileError) {\n throw this.writeFileError;\n }\n\n const buffer =\n typeof data === \"string\"\n ? Buffer.from(data, \"utf-8\")\n : data instanceof Buffer\n ? data\n : data instanceof Uint8Array\n ? Buffer.from(data)\n : Buffer.from(await data.text(), \"utf-8\");\n\n this.files.set(path, buffer);\n }\n\n /**\n * Reset all in-memory state (useful between tests).\n */\n public reset(): void {\n this.files.clear();\n this.directories.clear();\n this.mkdirCalls = [];\n this.writeFileCalls = [];\n this.readFileCalls = [];\n this.rmCalls = [];\n this.joinCalls = [];\n this.mkdirError = null;\n this.writeFileError = null;\n this.readFileError = null;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Test assertion helpers\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Check if a file was written during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasWritten(\"/project/tsconfig.json\")).toBe(true);\n * ```\n */\n public wasWritten(path: string): boolean {\n return this.writeFileCalls.some((call) => call.path === path);\n }\n\n /**\n * Check if a file was written with content matching a pattern.\n *\n * @example\n * ```typescript\n * expect(fs.wasWrittenMatching(\"/project/tsconfig.json\", /extends/)).toBe(true);\n * ```\n */\n public wasWrittenMatching(path: string, pattern: RegExp): boolean {\n const call = this.writeFileCalls.find((c) => c.path === path);\n return call ? pattern.test(call.data) : false;\n }\n\n /**\n * Check if a file was read during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasRead(\"/project/package.json\")).toBe(true);\n * ```\n */\n public wasRead(path: string): boolean {\n return this.readFileCalls.includes(path);\n }\n\n /**\n * Check if a file was deleted during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasDeleted(\"/project/old-file.txt\")).toBe(true);\n * ```\n */\n public wasDeleted(path: string): boolean {\n return this.rmCalls.some((call) => call.path === path);\n }\n\n /**\n * Get the content of a file as a string (convenience method for testing).\n */\n public getFileContent(path: string): string | undefined {\n return this.files.get(path)?.toString(\"utf-8\");\n }\n}\n","import type { ShellProvider, ShellRunOptions } from \"./ShellProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface MemoryShellCall {\n command: string;\n options: ShellRunOptions;\n}\n\nexport interface MemoryShellProviderOptions {\n /**\n * Simulated outputs for specific commands.\n * Key is the command string, value is the stdout to return.\n */\n outputs?: Record<string, string>;\n\n /**\n * Commands that should throw an error.\n * Key is the command string, value is the error message.\n */\n errors?: Record<string, string>;\n\n /**\n * Commands that are considered \"installed\" in the system PATH.\n */\n installedCommands?: string[];\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * In-memory implementation of ShellProvider for testing.\n *\n * Records all commands that would be executed without actually running them.\n * Can be configured to return specific outputs or throw errors for testing.\n *\n * @example\n * ```typescript\n * // In tests, substitute the real ShellProvider with MemoryShellProvider\n * const alepha = Alepha.create().with({\n * provide: ShellProvider,\n * use: MemoryShellProvider,\n * });\n *\n * // Configure mock behavior\n * const shell = alepha.inject(MemoryShellProvider);\n * shell.configure({\n * outputs: { \"echo hello\": \"hello\\n\" },\n * errors: { \"failing-cmd\": \"Command failed\" },\n * });\n *\n * // Or use the fluent API\n * shell.outputs.set(\"another-cmd\", \"output\");\n * shell.errors.set(\"another-error\", \"Error message\");\n *\n * // Run code that uses ShellProvider\n * const service = alepha.inject(MyService);\n * await service.doSomething();\n *\n * // Verify commands were called\n * expect(shell.calls).toHaveLength(2);\n * expect(shell.calls[0].command).toBe(\"yarn install\");\n * ```\n */\nexport class MemoryShellProvider implements ShellProvider {\n /**\n * All recorded shell calls.\n */\n public calls: MemoryShellCall[] = [];\n\n /**\n * Simulated outputs for specific commands.\n */\n public outputs = new Map<string, string>();\n\n /**\n * Commands that should throw an error.\n */\n public errors = new Map<string, string>();\n\n /**\n * Commands considered installed in the system PATH.\n */\n public installedCommands = new Set<string>();\n\n /**\n * Configure the mock with predefined outputs, errors, and installed commands.\n */\n public configure(options: MemoryShellProviderOptions): this {\n if (options.outputs) {\n for (const [cmd, output] of Object.entries(options.outputs)) {\n this.outputs.set(cmd, output);\n }\n }\n if (options.errors) {\n for (const [cmd, error] of Object.entries(options.errors)) {\n this.errors.set(cmd, error);\n }\n }\n if (options.installedCommands) {\n for (const cmd of options.installedCommands) {\n this.installedCommands.add(cmd);\n }\n }\n return this;\n }\n\n /**\n * Record command and return simulated output.\n */\n public async run(\n command: string,\n options: ShellRunOptions = {},\n ): Promise<string> {\n this.calls.push({ command, options });\n\n // Check for configured error\n const errorMsg = this.errors.get(command);\n if (errorMsg) {\n throw new Error(errorMsg);\n }\n\n // Return configured output or empty string\n return this.outputs.get(command) ?? \"\";\n }\n\n /**\n * Check if a specific command was called.\n */\n public wasCalled(command: string): boolean {\n return this.calls.some((call) => call.command === command);\n }\n\n /**\n * Check if a command matching a pattern was called.\n */\n public wasCalledMatching(pattern: RegExp): boolean {\n return this.calls.some((call) => pattern.test(call.command));\n }\n\n /**\n * Get all calls matching a pattern.\n */\n public getCallsMatching(pattern: RegExp): MemoryShellCall[] {\n return this.calls.filter((call) => pattern.test(call.command));\n }\n\n /**\n * Check if a command is installed.\n */\n public async isInstalled(command: string): Promise<boolean> {\n return this.installedCommands.has(command);\n }\n\n /**\n * Reset all recorded state.\n */\n public reset(): void {\n this.calls = [];\n this.outputs.clear();\n this.errors.clear();\n this.installedCommands.clear();\n }\n}\n","import { Readable } from \"node:stream\";\n\nexport interface FileTypeResult {\n /**\n * The detected MIME type\n */\n mimeType: string;\n /**\n * The detected file extension\n */\n extension: string;\n /**\n * Whether the file type was verified by magic bytes\n */\n verified: boolean;\n /**\n * The stream (potentially wrapped to allow re-reading)\n */\n stream: Readable;\n}\n\n/**\n * Service for detecting file types and getting content types.\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n *\n * // Get content type from filename\n * const mimeType = detector.getContentType(\"image.png\"); // \"image/png\"\n *\n * // Detect file type by magic bytes\n * const stream = createReadStream('image.png');\n * const result = await detector.detectFileType(stream, 'image.png');\n * console.log(result.mimeType); // 'image/png'\n * console.log(result.verified); // true if magic bytes match\n * ```\n */\nexport class FileDetector {\n /**\n * Magic byte signatures for common file formats.\n * Each signature is represented as an array of bytes or null (wildcard).\n */\n protected static readonly MAGIC_BYTES: Record<\n string,\n { signature: (number | null)[]; mimeType: string }[]\n > = {\n // Images\n png: [\n {\n signature: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],\n mimeType: \"image/png\",\n },\n ],\n jpg: [\n { signature: [0xff, 0xd8, 0xff, 0xe0], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe1], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe2], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe3], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe8], mimeType: \"image/jpeg\" },\n ],\n jpeg: [\n { signature: [0xff, 0xd8, 0xff, 0xe0], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe1], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe2], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe3], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe8], mimeType: \"image/jpeg\" },\n ],\n gif: [\n {\n signature: [0x47, 0x49, 0x46, 0x38, 0x37, 0x61],\n mimeType: \"image/gif\",\n }, // GIF87a\n {\n signature: [0x47, 0x49, 0x46, 0x38, 0x39, 0x61],\n mimeType: \"image/gif\",\n }, // GIF89a\n ],\n webp: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x57,\n 0x45,\n 0x42,\n 0x50,\n ],\n mimeType: \"image/webp\",\n },\n ],\n bmp: [{ signature: [0x42, 0x4d], mimeType: \"image/bmp\" }],\n ico: [{ signature: [0x00, 0x00, 0x01, 0x00], mimeType: \"image/x-icon\" }],\n tiff: [\n { signature: [0x49, 0x49, 0x2a, 0x00], mimeType: \"image/tiff\" }, // Little-endian\n { signature: [0x4d, 0x4d, 0x00, 0x2a], mimeType: \"image/tiff\" }, // Big-endian\n ],\n tif: [\n { signature: [0x49, 0x49, 0x2a, 0x00], mimeType: \"image/tiff\" },\n { signature: [0x4d, 0x4d, 0x00, 0x2a], mimeType: \"image/tiff\" },\n ],\n\n // Documents\n pdf: [\n {\n signature: [0x25, 0x50, 0x44, 0x46, 0x2d],\n mimeType: \"application/pdf\",\n },\n ], // %PDF-\n zip: [\n { signature: [0x50, 0x4b, 0x03, 0x04], mimeType: \"application/zip\" },\n { signature: [0x50, 0x4b, 0x05, 0x06], mimeType: \"application/zip\" },\n { signature: [0x50, 0x4b, 0x07, 0x08], mimeType: \"application/zip\" },\n ],\n\n // Archives\n rar: [\n {\n signature: [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07],\n mimeType: \"application/vnd.rar\",\n },\n ],\n \"7z\": [\n {\n signature: [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c],\n mimeType: \"application/x-7z-compressed\",\n },\n ],\n tar: [\n {\n signature: [0x75, 0x73, 0x74, 0x61, 0x72],\n mimeType: \"application/x-tar\",\n },\n ],\n gz: [{ signature: [0x1f, 0x8b], mimeType: \"application/gzip\" }],\n tgz: [{ signature: [0x1f, 0x8b], mimeType: \"application/gzip\" }],\n\n // Audio\n mp3: [\n { signature: [0xff, 0xfb], mimeType: \"audio/mpeg\" },\n { signature: [0xff, 0xf3], mimeType: \"audio/mpeg\" },\n { signature: [0xff, 0xf2], mimeType: \"audio/mpeg\" },\n { signature: [0x49, 0x44, 0x33], mimeType: \"audio/mpeg\" }, // ID3\n ],\n wav: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x57,\n 0x41,\n 0x56,\n 0x45,\n ],\n mimeType: \"audio/wav\",\n },\n ],\n ogg: [{ signature: [0x4f, 0x67, 0x67, 0x53], mimeType: \"audio/ogg\" }],\n flac: [{ signature: [0x66, 0x4c, 0x61, 0x43], mimeType: \"audio/flac\" }], // fLaC\n\n // Video\n mp4: [\n {\n signature: [null, null, null, null, 0x66, 0x74, 0x79, 0x70],\n mimeType: \"video/mp4\",\n }, // ftyp\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x69,\n 0x73,\n 0x6f,\n 0x6d,\n ],\n mimeType: \"video/mp4\",\n }, // ftypisom\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x6d,\n 0x70,\n 0x34,\n 0x32,\n ],\n mimeType: \"video/mp4\",\n }, // ftypmp42\n ],\n webm: [{ signature: [0x1a, 0x45, 0xdf, 0xa3], mimeType: \"video/webm\" }],\n avi: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x41,\n 0x56,\n 0x49,\n 0x20,\n ],\n mimeType: \"video/x-msvideo\",\n },\n ],\n mov: [\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x71,\n 0x74,\n 0x20,\n 0x20,\n ],\n mimeType: \"video/quicktime\",\n },\n ],\n mkv: [\n { signature: [0x1a, 0x45, 0xdf, 0xa3], mimeType: \"video/x-matroska\" },\n ],\n\n // Office (DOCX, XLSX, PPTX are all ZIP-based)\n docx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n },\n ],\n xlsx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n },\n ],\n pptx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n },\n ],\n doc: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/msword\",\n },\n ],\n xls: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/vnd.ms-excel\",\n },\n ],\n ppt: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/vnd.ms-powerpoint\",\n },\n ],\n };\n\n /**\n * All possible format signatures for checking against actual file content\n */\n protected static readonly ALL_SIGNATURES = Object.entries(\n FileDetector.MAGIC_BYTES,\n ).flatMap(([ext, signatures]) => signatures.map((sig) => ({ ext, ...sig })));\n\n /**\n * MIME type map for file extensions.\n *\n * Can be used to get the content type of file based on its extension.\n * Feel free to add more mime types in your project!\n */\n public static readonly mimeMap: Record<string, string> = {\n // Documents\n json: \"application/json\",\n txt: \"text/plain\",\n html: \"text/html\",\n htm: \"text/html\",\n xml: \"application/xml\",\n csv: \"text/csv\",\n pdf: \"application/pdf\",\n md: \"text/markdown\",\n markdown: \"text/markdown\",\n rtf: \"application/rtf\",\n\n // Styles and scripts\n css: \"text/css\",\n js: \"application/javascript\",\n mjs: \"application/javascript\",\n ts: \"application/typescript\",\n jsx: \"text/jsx\",\n tsx: \"text/tsx\",\n\n // Archives\n zip: \"application/zip\",\n rar: \"application/vnd.rar\",\n \"7z\": \"application/x-7z-compressed\",\n tar: \"application/x-tar\",\n gz: \"application/gzip\",\n tgz: \"application/gzip\",\n\n // Images\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n bmp: \"image/bmp\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n\n // Audio\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n ogg: \"audio/ogg\",\n m4a: \"audio/mp4\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n\n // Video\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n avi: \"video/x-msvideo\",\n mov: \"video/quicktime\",\n wmv: \"video/x-ms-wmv\",\n flv: \"video/x-flv\",\n mkv: \"video/x-matroska\",\n\n // Office\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n\n // Fonts\n woff: \"font/woff\",\n woff2: \"font/woff2\",\n ttf: \"font/ttf\",\n otf: \"font/otf\",\n eot: \"application/vnd.ms-fontobject\",\n };\n\n /**\n * Reverse MIME type map for looking up extensions from MIME types.\n * Prefers shorter, more common extensions when multiple exist.\n */\n protected static readonly reverseMimeMap: Record<string, string> = (() => {\n const reverse: Record<string, string> = {};\n // Process in order so common extensions come first\n for (const [ext, mimeType] of Object.entries(FileDetector.mimeMap)) {\n // Only set if not already set (prefer first/shorter extension)\n if (!reverse[mimeType]) {\n reverse[mimeType] = ext;\n }\n }\n return reverse;\n })();\n\n /**\n * Returns the file extension for a given MIME type.\n *\n * @param mimeType - The MIME type to look up\n * @returns The file extension (without dot), or \"bin\" if not found\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const ext = detector.getExtensionFromMimeType(\"image/png\"); // \"png\"\n * const ext2 = detector.getExtensionFromMimeType(\"application/octet-stream\"); // \"bin\"\n * ```\n */\n getExtensionFromMimeType(mimeType: string): string {\n return FileDetector.reverseMimeMap[mimeType] || \"bin\";\n }\n /**\n * Returns the content type of file based on its filename.\n *\n * @param filename - The filename to check\n * @returns The MIME type\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const mimeType = detector.getContentType(\"image.png\"); // \"image/png\"\n * ```\n */\n getContentType(filename: string): string {\n const ext = filename.toLowerCase().split(\".\").pop() || \"\";\n return FileDetector.mimeMap[ext] || \"application/octet-stream\";\n }\n\n /**\n * Detects the file type by checking magic bytes against the stream content.\n *\n * @param stream - The readable stream to check\n * @param filename - The filename (used to get the extension)\n * @returns File type information including MIME type, extension, and verification status\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const stream = createReadStream('image.png');\n * const result = await detector.detectFileType(stream, 'image.png');\n * console.log(result.mimeType); // 'image/png'\n * console.log(result.verified); // true if magic bytes match\n * ```\n */\n async detectFileType(\n stream: Readable,\n filename: string,\n ): Promise<FileTypeResult> {\n // Get the expected MIME type from the filename extension\n const expectedMimeType = this.getContentType(filename);\n\n // Extract extension - only if filename contains a dot\n const lastDotIndex = filename.lastIndexOf(\".\");\n const ext =\n lastDotIndex > 0\n ? filename.substring(lastDotIndex + 1).toLowerCase()\n : \"\";\n\n // Read the first 16 bytes (enough for most magic byte checks)\n const { buffer, stream: newStream } = await this.peekBytes(stream, 16);\n\n // First, check if the extension's expected signature matches\n const expectedSignatures = FileDetector.MAGIC_BYTES[ext];\n if (expectedSignatures) {\n for (const { signature, mimeType } of expectedSignatures) {\n if (this.matchesSignature(buffer, signature)) {\n return {\n mimeType,\n extension: ext,\n verified: true,\n stream: newStream,\n };\n }\n }\n }\n\n // If the expected signature didn't match, try all other signatures\n for (const {\n ext: detectedExt,\n signature,\n mimeType,\n } of FileDetector.ALL_SIGNATURES) {\n if (detectedExt !== ext && this.matchesSignature(buffer, signature)) {\n return {\n mimeType,\n extension: detectedExt,\n verified: true,\n stream: newStream,\n };\n }\n }\n\n // If no magic bytes matched, fall back to extension-based detection\n // or return binary if extension is not recognized\n return {\n mimeType: expectedMimeType,\n extension: ext,\n verified: false,\n stream: newStream,\n };\n }\n\n /**\n * Reads all bytes from a stream and returns the first N bytes along with a new stream containing all data.\n * This approach reads the entire stream upfront to avoid complex async handling issues.\n *\n * @protected\n */\n protected async peekBytes(\n stream: Readable,\n numBytes: number,\n ): Promise<{ buffer: Buffer; stream: Readable }> {\n const chunks: Buffer[] = [];\n\n // Read the entire stream\n for await (const chunk of stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n\n const allData = Buffer.concat(chunks);\n const buffer = allData.subarray(0, numBytes);\n\n // Create a new stream with all the data\n const newStream = Readable.from(allData);\n\n return { buffer, stream: newStream };\n }\n\n /**\n * Checks if a buffer matches a magic byte signature.\n *\n * @protected\n */\n protected matchesSignature(\n buffer: Buffer,\n signature: (number | null)[],\n ): boolean {\n if (buffer.length < signature.length) {\n return false;\n }\n\n for (let i = 0; i < signature.length; i++) {\n if (signature[i] !== null && buffer[i] !== signature[i]) {\n return false;\n }\n }\n\n return true;\n }\n}\n","import { createReadStream } from \"node:fs\";\nimport {\n access,\n copyFile,\n cp as fsCp,\n mkdir as fsMkdir,\n readFile as fsReadFile,\n rm as fsRm,\n writeFile as fsWriteFile,\n readdir,\n rename,\n stat,\n} from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { PassThrough, Readable } from \"node:stream\";\nimport type { ReadableStream as NodeWebStream } from \"node:stream/web\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n $inject,\n AlephaError,\n type FileLike,\n isFileLike,\n Json,\n type StreamLike,\n} from \"alepha\";\nimport { FileDetector } from \"../services/FileDetector.ts\";\nimport type {\n CpOptions,\n CreateFileOptions,\n FileSystemProvider,\n LsOptions,\n MkdirOptions,\n RmOptions,\n} from \"./FileSystemProvider.ts\";\n\n/**\n * Node.js implementation of FileSystem interface.\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Create from URL\n * const file1 = fs.createFile({ url: \"file:///path/to/file.png\" });\n *\n * // Create from Buffer\n * const file2 = fs.createFile({ buffer: Buffer.from(\"hello\"), name: \"hello.txt\" });\n *\n * // Create from text\n * const file3 = fs.createFile({ text: \"Hello, world!\", name: \"greeting.txt\" });\n *\n * // File operations\n * await fs.mkdir(\"/tmp/mydir\", { recursive: true });\n * await fs.cp(\"/src/file.txt\", \"/dest/file.txt\");\n * await fs.mv(\"/old/path.txt\", \"/new/path.txt\");\n * const files = await fs.ls(\"/tmp\");\n * await fs.rm(\"/tmp/file.txt\");\n * ```\n */\nexport class NodeFileSystemProvider implements FileSystemProvider {\n protected detector = $inject(FileDetector);\n protected json = $inject(Json);\n\n public join(...paths: string[]): string {\n return join(...paths);\n }\n\n /**\n * Creates a FileLike object from various sources.\n *\n * @param options - Options for creating the file\n * @returns A FileLike object\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // From URL\n * const file1 = fs.createFile({ url: \"https://example.com/image.png\" });\n *\n * // From Buffer\n * const file2 = fs.createFile({\n * buffer: Buffer.from(\"hello\"),\n * name: \"hello.txt\",\n * type: \"text/plain\"\n * });\n *\n * // From text\n * const file3 = fs.createFile({ text: \"Hello!\", name: \"greeting.txt\" });\n *\n * // From stream with detection\n * const stream = createReadStream(\"/path/to/file.png\");\n * const file4 = fs.createFile({ stream, name: \"image.png\" });\n * ```\n */\n createFile(options: CreateFileOptions): FileLike {\n if (\"path\" in options) {\n const path = options.path;\n const filename = path.split(\"/\").pop() || \"file\";\n return this.createFileFromUrl(`file://${path}`, {\n type: options.type,\n name: options.name || filename,\n });\n }\n\n // Handle URL\n if (\"url\" in options) {\n return this.createFileFromUrl(options.url, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"response\" in options) {\n if (!options.response.body) {\n throw new AlephaError(\"Response has no body stream\");\n }\n const res = options.response;\n // guess size from content-length header if available\n const sizeHeader = res.headers.get(\"content-length\");\n const size = sizeHeader ? parseInt(sizeHeader, 10) : undefined;\n // guess name from content-disposition header if available\n let name = options.name;\n const contentDisposition = res.headers.get(\"content-disposition\");\n if (contentDisposition && !name) {\n const match = contentDisposition.match(/filename=\"?([^\"]+)\"?/);\n if (match) {\n name = match[1];\n }\n }\n // guess type from content-type header if available\n const type = options.type || res.headers.get(\"content-type\") || undefined;\n return this.createFileFromStream(options.response.body, {\n type,\n name,\n size,\n });\n }\n\n // Handle Web File\n if (\"file\" in options) {\n return this.createFileFromWebFile(options.file, {\n type: options.type,\n name: options.name,\n size: options.size,\n });\n }\n\n // Handle Buffer\n if (\"buffer\" in options) {\n return this.createFileFromBuffer(options.buffer, {\n type: options.type,\n name: options.name,\n });\n }\n\n // Handle ArrayBuffer\n if (\"arrayBuffer\" in options) {\n return this.createFileFromBuffer(Buffer.from(options.arrayBuffer), {\n type: options.type,\n name: options.name,\n });\n }\n\n // Handle text\n if (\"text\" in options) {\n return this.createFileFromBuffer(Buffer.from(options.text, \"utf-8\"), {\n type: options.type || \"text/plain\",\n name: options.name || \"file.txt\",\n });\n }\n\n // Handle stream\n if (\"stream\" in options) {\n return this.createFileFromStream(options.stream, {\n type: options.type,\n name: options.name,\n size: options.size,\n });\n }\n\n throw new AlephaError(\n \"Invalid createFile options: no valid source provided\",\n );\n }\n\n /**\n * Removes a file or directory.\n *\n * @param path - The path to remove\n * @param options - Remove options\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Remove a file\n * await fs.rm(\"/tmp/file.txt\");\n *\n * // Remove a directory recursively\n * await fs.rm(\"/tmp/mydir\", { recursive: true });\n *\n * // Remove with force (no error if doesn't exist)\n * await fs.rm(\"/tmp/maybe-exists.txt\", { force: true });\n * ```\n */\n async rm(path: string, options?: RmOptions): Promise<void> {\n await fsRm(path, options);\n }\n\n /**\n * Copies a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n * @param options - Copy options\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Copy a file\n * await fs.cp(\"/src/file.txt\", \"/dest/file.txt\");\n *\n * // Copy a directory recursively\n * await fs.cp(\"/src/dir\", \"/dest/dir\", { recursive: true });\n *\n * // Copy with force (overwrite existing)\n * await fs.cp(\"/src/file.txt\", \"/dest/file.txt\", { force: true });\n * ```\n */\n async cp(src: string, dest: string, options?: CpOptions): Promise<void> {\n // Check if source is a directory\n const srcStat = await stat(src);\n\n if (srcStat.isDirectory()) {\n if (!options?.recursive) {\n throw new Error(\n `Cannot copy directory without recursive option: ${src}`,\n );\n }\n // Use Node.js cp function for recursive directory copy\n await fsCp(src, dest, {\n recursive: true,\n force: options?.force ?? false,\n });\n } else {\n // For files, use copyFile\n await copyFile(src, dest);\n }\n }\n\n /**\n * Moves/renames a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Move/rename a file\n * await fs.mv(\"/old/path.txt\", \"/new/path.txt\");\n *\n * // Move a directory\n * await fs.mv(\"/old/dir\", \"/new/dir\");\n * ```\n */\n async mv(src: string, dest: string): Promise<void> {\n await rename(src, dest);\n }\n\n /**\n * Creates a directory.\n *\n * @param path - The directory path to create\n * @param options - Mkdir options\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Create a directory\n * await fs.mkdir(\"/tmp/mydir\");\n *\n * // Create nested directories\n * await fs.mkdir(\"/tmp/path/to/dir\", { recursive: true });\n *\n * // Create with specific permissions\n * await fs.mkdir(\"/tmp/mydir\", { mode: 0o755 });\n * ```\n */\n async mkdir(path: string, options: MkdirOptions = {}): Promise<void> {\n const p = fsMkdir(path, {\n recursive: options.recursive ?? true,\n mode: options.mode,\n });\n\n if (options.force === false) {\n await p;\n } else {\n await p.catch(() => {});\n }\n }\n\n /**\n * Lists files in a directory.\n *\n * @param path - The directory path to list\n * @param options - List options\n * @returns Array of filenames\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // List files in a directory\n * const files = await fs.ls(\"/tmp\");\n * console.log(files); // [\"file1.txt\", \"file2.txt\", \"subdir\"]\n *\n * // List with hidden files\n * const allFiles = await fs.ls(\"/tmp\", { hidden: true });\n *\n * // List recursively\n * const allFilesRecursive = await fs.ls(\"/tmp\", { recursive: true });\n * ```\n */\n async ls(path: string, options?: LsOptions): Promise<string[]> {\n const entries = await readdir(path);\n\n // Filter out hidden files if not requested\n const filteredEntries = options?.hidden\n ? entries\n : entries.filter((e) => !e.startsWith(\".\"));\n\n // If recursive, get all nested files\n if (options?.recursive) {\n const allFiles: string[] = [];\n\n for (const entry of filteredEntries) {\n const fullPath = join(path, entry);\n const entryStat = await stat(fullPath);\n\n if (entryStat.isDirectory()) {\n // Add directory entry\n allFiles.push(entry);\n // Recursively get files from subdirectory\n const subFiles = await this.ls(fullPath, options);\n allFiles.push(...subFiles.map((f) => join(entry, f)));\n } else {\n allFiles.push(entry);\n }\n }\n\n return allFiles;\n }\n\n return filteredEntries;\n }\n\n /**\n * Checks if a file or directory exists.\n *\n * @param path - The path to check\n * @returns True if the path exists, false otherwise\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * if (await fs.exists(\"/tmp/file.txt\")) {\n * console.log(\"File exists\");\n * }\n * ```\n */\n async exists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Reads the content of a file.\n *\n * @param path - The file path to read\n * @returns The file content as a Buffer\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * const buffer = await fs.readFile(\"/tmp/file.txt\");\n * console.log(buffer.toString(\"utf-8\"));\n * ```\n */\n async readFile(path: string): Promise<Buffer> {\n return await fsReadFile(path);\n }\n\n /**\n * Writes data to a file.\n *\n * @param path - The file path to write to\n * @param data - The data to write (Buffer or string)\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n *\n * // Write string\n * await fs.writeFile(\"/tmp/file.txt\", \"Hello, world!\");\n *\n * // Write Buffer\n * await fs.writeFile(\"/tmp/file.bin\", Buffer.from([0x01, 0x02, 0x03]));\n * ```\n */\n async writeFile(\n path: string,\n data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void> {\n if (isFileLike(data)) {\n await fsWriteFile(path, Readable.from(data.stream()));\n return;\n }\n await fsWriteFile(path, data);\n }\n\n /**\n * Reads the content of a file as a string.\n *\n * @param path - The file path to read\n * @returns The file content as a string\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n * const content = await fs.readTextFile(\"/tmp/file.txt\");\n * ```\n */\n async readTextFile(path: string): Promise<string> {\n const buffer = await this.readFile(path);\n return buffer.toString(\"utf-8\");\n }\n\n /**\n * Reads the content of a file as JSON.\n *\n * @param path - The file path to read\n * @returns The parsed JSON content\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(NodeFileSystemProvider);\n * const config = await fs.readJsonFile<{ name: string }>(\"/tmp/config.json\");\n * ```\n */\n async readJsonFile<T = unknown>(path: string): Promise<T> {\n const text = await this.readTextFile(path);\n return this.json.parse(text) as T;\n }\n\n /**\n * Creates a FileLike object from a Web File.\n *\n * @protected\n */\n protected createFileFromWebFile(\n source: File,\n options: {\n type?: string;\n name?: string;\n size?: number;\n } = {},\n ): FileLike {\n const name = options.name ?? source.name;\n return {\n name,\n type: options.type ?? (source.type || this.detector.getContentType(name)),\n size: options.size ?? source.size ?? 0,\n lastModified: source.lastModified || Date.now(),\n stream: () => source.stream(),\n arrayBuffer: async (): Promise<ArrayBuffer> => {\n return await source.arrayBuffer();\n },\n text: async (): Promise<string> => {\n return await source.text();\n },\n };\n }\n\n /**\n * Creates a FileLike object from a Buffer.\n *\n * @protected\n */\n protected createFileFromBuffer(\n source: Buffer,\n options: {\n type?: string;\n name?: string;\n } = {},\n ): FileLike {\n const name: string = options.name ?? \"file\";\n return {\n name,\n type: options.type ?? this.detector.getContentType(options.name ?? name),\n size: source.byteLength,\n lastModified: Date.now(),\n stream: (): Readable => Readable.from(source),\n arrayBuffer: async (): Promise<ArrayBuffer> => {\n return this.bufferToArrayBuffer(source);\n },\n text: async (): Promise<string> => {\n return source.toString(\"utf-8\");\n },\n };\n }\n\n /**\n * Creates a FileLike object from a stream.\n *\n * @protected\n */\n protected createFileFromStream(\n source: StreamLike,\n options: {\n type?: string;\n name?: string;\n size?: number;\n } = {},\n ): FileLike & { _buffer: null | Buffer } {\n let buffer: Buffer | null = null;\n\n return {\n name: options.name ?? \"file\",\n type:\n options.type ?? this.detector.getContentType(options.name ?? \"file\"),\n size: options.size ?? 0,\n lastModified: Date.now(),\n stream: () => source,\n _buffer: null as Buffer | null,\n arrayBuffer: async () => {\n buffer ??= await this.streamToBuffer(source);\n return this.bufferToArrayBuffer(buffer);\n },\n text: async () => {\n buffer ??= await this.streamToBuffer(source);\n return buffer.toString(\"utf-8\");\n },\n };\n }\n\n /**\n * Creates a FileLike object from a URL.\n *\n * @protected\n */\n protected createFileFromUrl(\n url: string,\n options: {\n type?: string;\n name?: string;\n } = {},\n ): FileLike {\n const parsedUrl = new URL(url);\n const filename =\n options.name || parsedUrl.pathname.split(\"/\").pop() || \"file\";\n let buffer: Buffer | null = null;\n\n return {\n name: filename,\n type: options.type ?? this.detector.getContentType(filename),\n size: 0, // Unknown size until loaded\n lastModified: Date.now(),\n stream: () => this.createStreamFromUrl(url),\n arrayBuffer: async () => {\n buffer ??= await this.loadFromUrl(url);\n return this.bufferToArrayBuffer(buffer);\n },\n text: async () => {\n buffer ??= await this.loadFromUrl(url);\n return buffer.toString(\"utf-8\");\n },\n filepath: url,\n };\n }\n\n /**\n * Gets a streaming response from a URL.\n *\n * @protected\n */\n protected getStreamingResponse(url: string): Readable {\n const stream = new PassThrough();\n\n fetch(url)\n .then((res) =>\n Readable.fromWeb(res.body as unknown as NodeWebStream).pipe(stream),\n )\n .catch((err) => stream.destroy(err));\n\n return stream;\n }\n\n /**\n * Loads data from a URL.\n *\n * @protected\n */\n protected async loadFromUrl(url: string): Promise<Buffer> {\n const parsedUrl = new URL(url);\n\n if (parsedUrl.protocol === \"file:\") {\n // Handle file:// URLs\n const filePath = fileURLToPath(url);\n return await fsReadFile(filePath);\n } else if (\n parsedUrl.protocol === \"http:\" ||\n parsedUrl.protocol === \"https:\"\n ) {\n // Handle HTTP/HTTPS URLs\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch ${url}: ${response.status} ${response.statusText}`,\n );\n }\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n } else {\n throw new Error(`Unsupported protocol: ${parsedUrl.protocol}`);\n }\n }\n\n /**\n * Creates a stream from a URL.\n *\n * @protected\n */\n protected createStreamFromUrl(url: string): Readable {\n const parsedUrl = new URL(url);\n\n if (parsedUrl.protocol === \"file:\") {\n // For file:// URLs, create a stream that reads the file\n return createReadStream(fileURLToPath(url));\n } else if (\n parsedUrl.protocol === \"http:\" ||\n parsedUrl.protocol === \"https:\"\n ) {\n // For HTTP/HTTPS URLs, create a stream that fetches the content\n return this.getStreamingResponse(url);\n } else {\n throw new AlephaError(`Unsupported protocol: ${parsedUrl.protocol}`);\n }\n }\n\n /**\n * Converts a stream-like object to a Buffer.\n *\n * @protected\n */\n protected async streamToBuffer(streamLike: StreamLike): Promise<Buffer> {\n const stream =\n streamLike instanceof Readable\n ? streamLike\n : Readable.fromWeb(streamLike as NodeWebStream);\n\n return new Promise<Buffer>((resolve, reject) => {\n const buffer: any[] = [];\n stream.on(\"data\", (chunk) => buffer.push(Buffer.from(chunk)));\n stream.on(\"end\", () => resolve(Buffer.concat(buffer)));\n stream.on(\"error\", (err) =>\n reject(new AlephaError(\"Error converting stream\", { cause: err })),\n );\n });\n }\n\n /**\n * Converts a Node.js Buffer to an ArrayBuffer.\n *\n * @protected\n */\n protected bufferToArrayBuffer(buffer: Buffer): ArrayBuffer {\n return buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer;\n }\n}\n","import { exec, spawn } from \"node:child_process\";\nimport { $inject, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { FileSystemProvider } from \"./FileSystemProvider.ts\";\nimport type { ShellProvider, ShellRunOptions } from \"./ShellProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Node.js implementation of ShellProvider.\n *\n * Executes shell commands using Node.js child_process module.\n * Supports binary resolution from node_modules/.bin for local packages.\n */\nexport class NodeShellProvider implements ShellProvider {\n protected readonly log = $logger();\n protected readonly fs = $inject(FileSystemProvider);\n\n /**\n * Run a shell command or binary.\n */\n public async run(\n command: string,\n options: ShellRunOptions = {},\n ): Promise<string> {\n const { resolve = false, capture = false, root, env } = options;\n const cwd = root ?? process.cwd();\n\n this.log.debug(`Shell: ${command}`, { cwd, resolve, capture });\n\n let executable: string;\n let args: string[];\n\n if (resolve) {\n const [bin, ...rest] = command.split(\" \");\n executable = await this.resolveExecutable(bin, cwd);\n args = rest;\n } else {\n [executable, ...args] = command.split(\" \");\n }\n\n if (capture) {\n return this.execCapture(command, { cwd, env });\n }\n\n return this.execInherit(executable, args, { cwd, env });\n }\n\n /**\n * Execute command with inherited stdio (streams to terminal).\n */\n protected async execInherit(\n executable: string,\n args: string[],\n options: { cwd: string; env?: Record<string, string> },\n ): Promise<string> {\n const proc = spawn(executable, args, {\n stdio: \"inherit\",\n cwd: options.cwd,\n env: {\n ...process.env,\n ...options.env,\n },\n });\n\n return new Promise<string>((resolve, reject) => {\n proc.on(\"exit\", (code) => {\n if (code === 0 || code === null) {\n resolve(\"\");\n } else {\n reject(new AlephaError(`Command exited with code ${code}`));\n }\n });\n proc.on(\"error\", reject);\n });\n }\n\n /**\n * Execute command and capture stdout.\n */\n protected execCapture(\n command: string,\n options: { cwd: string; env?: Record<string, string> },\n ): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n exec(\n command,\n {\n cwd: options.cwd,\n env: {\n ...process.env,\n LOG_FORMAT: \"pretty\",\n ...options.env,\n },\n },\n (err, stdout) => {\n if (err) {\n (err as any).stdout = stdout;\n reject(err);\n } else {\n resolve(stdout);\n }\n },\n );\n });\n }\n\n /**\n * Resolve executable path from node_modules/.bin.\n *\n * Search order:\n * 1. Local: node_modules/.bin/\n * 2. Pnpm nested: node_modules/alepha/node_modules/.bin/\n * 3. Monorepo: Walk up to 3 parent directories\n */\n protected async resolveExecutable(\n name: string,\n root: string,\n ): Promise<string> {\n const suffix = process.platform === \"win32\" ? \".cmd\" : \"\";\n\n // 1. Local node_modules\n let execPath = await this.findExecutable(\n root,\n `node_modules/.bin/${name}${suffix}`,\n );\n\n // 2. Pnpm nested (alepha's own node_modules)\n if (!execPath) {\n execPath = await this.findExecutable(\n root,\n `node_modules/alepha/node_modules/.bin/${name}${suffix}`,\n );\n }\n\n // 3. Monorepo: check parent directories (up to 3 levels)\n if (!execPath) {\n let parentDir = this.fs.join(root, \"..\");\n for (let i = 0; i < 3; i++) {\n execPath = await this.findExecutable(\n parentDir,\n `node_modules/.bin/${name}${suffix}`,\n );\n if (execPath) break;\n parentDir = this.fs.join(parentDir, \"..\");\n }\n }\n\n if (!execPath) {\n throw new AlephaError(\n `Could not find executable for '${name}'. Make sure the package is installed.`,\n );\n }\n\n return execPath;\n }\n\n /**\n * Check if executable exists at path.\n */\n protected async findExecutable(\n root: string,\n relativePath: string,\n ): Promise<string | undefined> {\n const fullPath = this.fs.join(root, relativePath);\n if (await this.fs.exists(fullPath)) {\n return fullPath;\n }\n return undefined;\n }\n\n /**\n * Check if a command is installed and available in the system PATH.\n */\n public isInstalled(command: string): Promise<boolean> {\n return new Promise((resolve) => {\n const check =\n process.platform === \"win32\"\n ? `where ${command}`\n : `command -v ${command}`;\n exec(check, (error) => resolve(!error));\n });\n }\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ShellRunOptions {\n /**\n * Working directory for the command.\n */\n root?: string;\n\n /**\n * Additional environment variables.\n */\n env?: Record<string, string>;\n\n /**\n * Resolve the executable from node_modules/.bin.\n * Supports local project, pnpm nested, and monorepo structures.\n * @default false\n */\n resolve?: boolean;\n\n /**\n * Capture stdout instead of inheriting stdio.\n * When true, returns stdout as string.\n * When false, streams output to terminal.\n * @default false\n */\n capture?: boolean;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Abstract provider for executing shell commands and binaries.\n *\n * Implementations:\n * - `NodeShellProvider` - Real shell execution using Node.js child_process\n * - `MemoryShellProvider` - In-memory mock for testing\n *\n * @example\n * ```typescript\n * class MyService {\n * protected readonly shell = $inject(ShellProvider);\n *\n * async build() {\n * // Run shell command directly\n * await this.shell.run(\"yarn install\");\n *\n * // Run local binary with resolution\n * await this.shell.run(\"vite build\", { resolve: true });\n *\n * // Capture output\n * const output = await this.shell.run(\"echo hello\", { capture: true });\n * }\n * }\n * ```\n */\nexport abstract class ShellProvider {\n /**\n * Run a shell command or binary.\n *\n * @param command - The command to run\n * @param options - Execution options\n * @returns stdout if capture is true, empty string otherwise\n */\n abstract run(command: string, options?: ShellRunOptions): Promise<string>;\n\n /**\n * Check if a command is installed and available in the system PATH.\n *\n * @param command - The command name to check\n * @returns true if the command is available\n */\n abstract isInstalled(command: string): Promise<boolean>;\n}\n","import { $module } from \"alepha\";\nimport { FileSystemProvider } from \"./providers/FileSystemProvider.ts\";\nimport { MemoryFileSystemProvider } from \"./providers/MemoryFileSystemProvider.ts\";\nimport { MemoryShellProvider } from \"./providers/MemoryShellProvider.ts\";\nimport { NodeFileSystemProvider } from \"./providers/NodeFileSystemProvider.ts\";\nimport { NodeShellProvider } from \"./providers/NodeShellProvider.ts\";\nimport { ShellProvider } from \"./providers/ShellProvider.ts\";\nimport { FileDetector } from \"./services/FileDetector.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/FileError.ts\";\nexport * from \"./providers/FileSystemProvider.ts\";\nexport * from \"./providers/MemoryFileSystemProvider.ts\";\nexport * from \"./providers/MemoryShellProvider.ts\";\nexport * from \"./providers/NodeFileSystemProvider.ts\";\nexport * from \"./providers/NodeShellProvider.ts\";\nexport * from \"./providers/ShellProvider.ts\";\nexport * from \"./services/FileDetector.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | Stability | Since | Runtime |\n * |-----------|-------|---------|\n * | 3 - stable | 0.14.0 | node, bun, browser|\n *\n * System-level abstractions for portable code across runtimes.\n *\n * **Features:**\n * - File system operations (read, write, exists, etc.)\n * - Shell command execution\n * - File type detection and MIME utilities\n * - Memory implementations for testing\n *\n * @module alepha.system\n */\nexport const AlephaSystem = $module({\n name: \"alepha.system\",\n primitives: [],\n services: [\n FileDetector,\n FileSystemProvider,\n MemoryFileSystemProvider,\n NodeFileSystemProvider,\n ShellProvider,\n MemoryShellProvider,\n NodeShellProvider,\n ],\n register: (alepha) =>\n alepha\n .with({\n optional: true,\n provide: FileSystemProvider,\n use: NodeFileSystemProvider,\n })\n .with({\n optional: true,\n provide: ShellProvider,\n use: alepha.isTest() ? MemoryShellProvider : NodeShellProvider,\n }),\n});\n","import { randomInt } from \"node:crypto\";\nimport { $inject, Alepha } from \"alepha\";\nimport type { FileController } from \"alepha/api/files\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport {\n CryptoProvider,\n InvalidCredentialsError,\n type UserAccount,\n} from \"alepha/security\";\nimport { type ServerRequest, UnauthorizedError } from \"alepha/server\";\nimport type { OAuth2Profile } from \"alepha/server/auth\";\nimport { $client } from \"alepha/server/links\";\nimport { FileSystemProvider } from \"alepha/system\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport { UserAudits } from \"./UserAudits.ts\";\n\nexport class SessionService {\n protected readonly alepha = $inject(Alepha);\n protected readonly fsp = $inject(FileSystemProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly log = $logger();\n protected readonly realmProvider = $inject(RealmProvider);\n protected readonly fileController = $client<FileController>();\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n public users(userRealmName?: string) {\n return this.realmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.realmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Check if user should be auto-promoted to admin based on adminEmails/adminUsernames settings.\n * If user matches and doesn't have admin role, promote them.\n */\n protected async ensureAdminRole(\n user: {\n id: string;\n email?: string | null;\n username?: string | null;\n roles: string[];\n },\n userRealmName?: string,\n ): Promise<boolean> {\n if (user.roles.includes(\"admin\")) return false;\n\n const { settings, name } = this.realmProvider.getRealm(userRealmName);\n const adminEmails = settings.adminEmails ?? [];\n const adminUsernames = settings.adminUsernames ?? [];\n\n const isAdminByEmail = user.email && adminEmails.includes(user.email);\n const isAdminByUsername =\n user.username && adminUsernames.includes(user.username);\n\n if (!isAdminByEmail && !isAdminByUsername) return false;\n\n // Promote to admin\n user.roles = [...user.roles.filter((r) => r !== \"admin\"), \"admin\"];\n await this.users(userRealmName).updateById(user.id, { roles: user.roles });\n\n const reason = isAdminByEmail ? \"adminEmails\" : \"adminUsernames\";\n this.log.info(`User auto-promoted to admin via ${reason} setting`, {\n userId: user.id,\n email: user.email,\n username: user.username,\n realm: name,\n });\n\n await this.userAudits(userRealmName)?.recordUser(\"role_change\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: name,\n resourceId: user.id,\n description: `User auto-promoted to admin via ${reason} setting`,\n metadata: { addedRole: \"admin\", reason },\n });\n\n return true;\n }\n\n /**\n * Random delay to prevent timing attacks (50-200ms)\n * Uses cryptographically secure random number generation\n */\n protected randomDelay(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, randomInt(50, 201)));\n }\n\n /**\n * Validate user credentials and return the user if valid.\n */\n public async login(\n provider: string,\n username: string,\n password: string,\n userRealmName?: string,\n ): Promise<UserEntity> {\n const { settings, name } = this.realmProvider.getRealm(userRealmName);\n const isEmail = username.includes(\"@\");\n const isPhone = /^[+\\d][\\d\\s()-]+$/.test(username);\n const isUsername = !isEmail && !isPhone;\n const identities = this.identities(userRealmName);\n const users = this.users(userRealmName);\n\n await this.randomDelay();\n\n try {\n const where = users.createQueryWhere();\n\n where.realm = name;\n\n if (settings.usernameEnabled !== false && isUsername) {\n // validate username format if regex is provided\n if (settings.usernameRegExp) {\n const regex = new RegExp(settings.usernameRegExp);\n if (!regex.test(username)) {\n this.log.warn(\"Username does not match required format\", {\n provider,\n username,\n realm: name,\n });\n\n await this.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"Username does not match required format\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n }\n where.username = username;\n } else if (settings.emailEnabled !== false && isEmail) {\n where.email = username;\n } else if (settings.phoneEnabled === true && isPhone) {\n where.phoneNumber = username;\n } else {\n this.log.warn(\"Invalid login identifier format\", {\n provider,\n username,\n realm: name,\n });\n\n await this.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"Invalid login identifier format\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n\n const user = await users.findOne({ where }).catch(() => undefined);\n if (!user) {\n this.log.warn(\"User not found during login attempt\", {\n provider,\n username,\n realm: name,\n });\n\n await this.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"User not found\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n\n const identity = await identities.findOne({\n where: {\n provider: { eq: provider },\n userId: { eq: user.id },\n },\n });\n\n const storedPassword = identity.password;\n if (!storedPassword) {\n this.log.error(\"Identity has no password configured\", {\n provider,\n username,\n identityId: identity.id,\n realm: name,\n });\n throw new InvalidCredentialsError();\n }\n\n const valid = await this.cryptoProvider.verifyPassword(\n password,\n storedPassword,\n );\n\n if (!valid) {\n this.log.warn(\"Invalid password during login attempt\", {\n provider,\n username,\n realm: name,\n });\n\n await this.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n resourceId: user.id,\n description: \"Invalid password\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n\n await this.userAudits(userRealmName)?.recordAuth(\"login\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: name,\n resourceId: user.id,\n description: `User logged in via ${provider}`,\n metadata: { provider, username },\n });\n\n // Auto-promote to admin if configured\n await this.ensureAdminRole(user, userRealmName);\n\n return user;\n } catch (error) {\n if (error instanceof InvalidCredentialsError) {\n // TODO: store failed login attempts (with request data) and lock account after threshold\n throw error;\n }\n\n this.log.warn(\"Error during login attempt\", error);\n\n throw new InvalidCredentialsError();\n }\n }\n\n public async createSession(\n user: UserAccount,\n expiresIn: number,\n userRealmName?: string,\n ) {\n this.log.trace(\"Creating session\", { userId: user.id, expiresIn });\n\n const request = this.alepha.context.get<ServerRequest>(\"request\");\n const refreshToken = this.cryptoProvider.randomUUID();\n\n const expiresAt = this.dateTimeProvider\n .now()\n .add(expiresIn, \"seconds\")\n .toISOString();\n\n const session = await this.sessions(userRealmName).create({\n userId: user.id,\n expiresAt,\n ip: request?.ip,\n userAgent: request?.userAgent,\n refreshToken,\n });\n\n this.log.info(\"Session created\", {\n sessionId: session.id,\n userId: user.id,\n ip: request?.ip,\n });\n\n return {\n refreshToken,\n sessionId: session.id,\n };\n }\n\n public async refreshSession(refreshToken: string, userRealmName?: string) {\n this.log.trace(\"Refreshing session\");\n\n const session = await this.sessions(userRealmName).findOne({\n where: {\n refreshToken: { eq: refreshToken },\n },\n });\n\n const now = this.dateTimeProvider.now();\n const expiresAt = this.dateTimeProvider.of(session.expiresAt);\n\n if (this.dateTimeProvider.of(session.expiresAt) < now) {\n this.log.debug(\"Session expired during refresh\", {\n sessionId: session.id,\n userId: session.userId,\n });\n await this.sessions(userRealmName).deleteById(refreshToken);\n throw new UnauthorizedError(\"Session expired\");\n }\n\n const user = await this.users(userRealmName).findOne({\n where: {\n id: { eq: session.userId },\n },\n });\n\n // Auto-promote to admin if configured (handles \"I promote you admin\" case)\n await this.ensureAdminRole(user, userRealmName);\n\n this.log.debug(\"Session refreshed\", {\n sessionId: session.id,\n userId: session.userId,\n });\n\n return {\n user,\n expiresIn: expiresAt.unix() - now.unix(),\n sessionId: session.id,\n };\n }\n\n public async deleteSession(refreshToken: string, userRealmName?: string) {\n this.log.trace(\"Deleting session\");\n\n // Get session info before deletion for audit\n const session = await this.sessions(userRealmName)\n .findOne({\n where: { refreshToken: { eq: refreshToken } },\n })\n .catch(() => undefined);\n\n await this.sessions(userRealmName).deleteOne({\n refreshToken,\n });\n this.log.debug(\"Session deleted\");\n\n if (session) {\n const { name } = this.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.recordAuth(\"logout\", {\n userId: session.userId,\n userRealm: name,\n sessionId: session.id,\n description: \"User logged out\",\n });\n }\n }\n\n public async link(\n provider: string,\n profile: OAuth2Profile,\n userRealmName?: string,\n ) {\n this.log.trace(\"Linking OAuth2 profile\", {\n provider,\n profileSub: profile.sub,\n email: profile.email,\n });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n const identities = this.identities(userRealmName);\n const users = this.users(userRealmName);\n\n const identity = await identities\n .findOne({\n where: {\n provider,\n providerUserId: profile.sub,\n },\n })\n .catch(() => undefined);\n\n // existing identity found, return associated user\n if (identity) {\n this.log.debug(\"Existing identity found\", {\n provider,\n identityId: identity.id,\n userId: identity.userId,\n });\n\n const user = await users.findById(identity.userId);\n\n await this.userAudits(userRealmName)?.recordAuth(\"login\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: `User logged in via OAuth2 (${provider})`,\n metadata: { provider, providerUserId: profile.sub },\n });\n\n // Auto-promote to admin if configured\n await this.ensureAdminRole(user, userRealmName);\n\n return user;\n }\n\n if (!profile.email) {\n this.log.debug(\"OAuth2 profile has no email, returning profile as-is\", {\n provider,\n profileSub: profile.sub,\n });\n return {\n id: profile.sub,\n ...profile,\n };\n }\n\n const existing = await users\n .findOne({\n where: {\n email: profile.email,\n },\n })\n .catch(() => undefined);\n\n if (existing) {\n this.log.debug(\"Linking OAuth2 profile to existing user by email\", {\n provider,\n profileSub: profile.sub,\n userId: existing.id,\n email: profile.email,\n });\n await identities.create({\n provider,\n providerUserId: profile.sub,\n userId: existing.id,\n });\n\n await this.userAudits(userRealmName)?.recordAuth(\"login\", {\n userId: existing.id,\n userEmail: existing.email ?? undefined,\n userRealm: realm.name,\n resourceId: existing.id,\n description: `OAuth2 identity linked to existing user (${provider})`,\n metadata: { provider, providerUserId: profile.sub, linked: true },\n });\n\n // Auto-promote to admin if configured\n await this.ensureAdminRole(existing, userRealmName);\n\n return existing;\n }\n\n // TODO: check usernames for uniqueness, add suffix if needed (e.g. john.doe1)\n // TODO: username must match a-zA-Z0-9._-\n\n const user = await users.create({\n realm: realm.name,\n username: profile.email.split(\"@\")[0],\n email: profile.email,\n // we trust the OAuth2 provider\n emailVerified: true,\n roles: [\"user\"], // TODO: make default roles configurable via realm settings\n });\n\n if (profile.picture) {\n this.log.debug(\"Fetching user profile picture from OAuth2 provider\", {\n provider,\n url: profile.picture,\n });\n try {\n const response = await fetch(profile.picture);\n const file = this.fsp.createFile({\n response,\n });\n if (response.ok && response.body) {\n const fileEntity = await this.fileController.uploadFile(\n {\n body: { file },\n },\n {\n user,\n },\n );\n await users.updateById(user.id, { picture: fileEntity.id });\n }\n } catch (error) {\n this.log.warn(\"Failed to fetch user profile picture\", error);\n }\n }\n\n await this.identities(userRealmName).create({\n provider,\n providerUserId: profile.sub,\n userId: user.id,\n });\n\n this.log.info(\"New user created via OAuth2 link\", {\n provider,\n userId: user.id,\n email: user.email,\n username: user.username,\n });\n\n // Audit: user created via OAuth\n await this.userAudits(userRealmName)?.recordUser(\"create\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: `User created via OAuth2 (${provider})`,\n metadata: {\n provider,\n providerUserId: profile.sub,\n username: user.username,\n email: user.email,\n },\n });\n\n // Audit: login event\n await this.userAudits(userRealmName)?.recordAuth(\"login\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: `First login via OAuth2 (${provider})`,\n metadata: { provider, providerUserId: profile.sub, firstLogin: true },\n });\n\n // Auto-promote to admin if configured\n await this.ensureAdminRole(user, userRealmName);\n\n return user;\n }\n}\n","import { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const adminApiKeyQuerySchema = t.extend(pageQuerySchema, {\n userId: t.optional(t.uuid()),\n includeRevoked: t.optional(t.boolean()),\n});\n","import { t } from \"alepha\";\n\nexport const adminApiKeyResourceSchema = t.object({\n id: t.uuid(),\n userId: t.uuid(),\n name: t.string(),\n description: t.optional(t.string()),\n tokenPrefix: t.string(),\n tokenSuffix: t.string(),\n roles: t.array(t.string()),\n createdAt: t.datetime(),\n lastUsedAt: t.optional(t.datetime()),\n lastUsedIp: t.optional(t.string()),\n expiresAt: t.optional(t.datetime()),\n revokedAt: t.optional(t.datetime()),\n usageCount: t.integer(),\n});\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const apiKeyEntity = $entity({\n name: \"api_keys\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n\n // Owner\n userId: t.uuid(),\n\n // Key metadata\n name: t.text({ maxLength: 100 }),\n description: t.optional(t.text({ maxLength: 500 })),\n\n // Token (hashed) - internal, not user input\n tokenHash: t.string({ maxLength: 256 }),\n tokenPrefix: t.string({ maxLength: 10 }),\n tokenSuffix: t.string({ maxLength: 8 }),\n\n // Roles (snapshot from user at creation)\n roles: db.default(t.array(t.string()), []),\n\n // Tracking\n lastUsedAt: t.optional(t.datetime()),\n lastUsedIp: t.optional(t.string({ maxLength: 45 })),\n usageCount: db.default(t.integer(), 0),\n\n // Lifecycle\n expiresAt: t.optional(t.datetime()),\n revokedAt: t.optional(t.datetime()),\n }),\n indexes: [\n { columns: [\"userId\", \"name\"], unique: true },\n { columns: [\"tokenHash\"], unique: true },\n ],\n});\n\nexport type ApiKeyEntity = Static<typeof apiKeyEntity.schema>;\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { $inject, Alepha } from \"alepha\";\nimport { $cache } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository, sql } from \"alepha/orm\";\nimport type { IssuerResolver, UserInfo } from \"alepha/security\";\nimport {\n ForbiddenError,\n NotFoundError,\n type ServerRequest,\n} from \"alepha/server\";\nimport { type ApiKeyEntity, apiKeyEntity } from \"../entities/apiKeyEntity.ts\";\n\nexport class ApiKeyService {\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n protected readonly repo = $repository(apiKeyEntity);\n\n /**\n * Cache validated API keys for 15 minutes.\n */\n protected readonly validationCache = $cache<ApiKeyEntity | null, [string]>({\n name: \"api-key-validation\",\n ttl: [15, \"minutes\"],\n });\n\n // -------------------------------------------------------------------------\n // Resolver\n // -------------------------------------------------------------------------\n\n /**\n * Create an issuer resolver for API key authentication.\n * Lower priority means it runs before JWT resolver.\n *\n * @param options.priority - Priority of this resolver (default: 50, JWT is 100)\n * @param options.prefix - API key prefix to match in Bearer header (default: \"ak\")\n */\n public createResolver(\n options: { priority?: number; prefix?: string } = {},\n ): IssuerResolver {\n const { priority = 50, prefix = \"ak\" } = options;\n const prefixPattern = `${prefix}_`;\n\n return {\n priority,\n onRequest: async (req: ServerRequest) => {\n // Try query param first\n const url = typeof req.url === \"string\" ? new URL(req.url) : req.url;\n let token = url.searchParams.get(\"api_key\");\n\n // Try Bearer header - only if token starts with expected prefix\n if (!token) {\n const auth = req.headers.authorization;\n if (auth?.startsWith(\"Bearer \")) {\n const bearerToken = auth.slice(7);\n if (bearerToken.startsWith(prefixPattern)) {\n token = bearerToken;\n }\n }\n }\n\n if (!token) {\n return null;\n }\n\n return this.validate(token);\n },\n };\n }\n\n // -------------------------------------------------------------------------\n // CRUD\n // -------------------------------------------------------------------------\n\n /**\n * Create a new API key for a user.\n * Returns both the API key entity and the plain token (which is only available once).\n */\n public async create(options: {\n userId: string;\n name: string;\n roles: string[];\n description?: string;\n expiresAt?: Date;\n prefix?: string;\n }): Promise<{ apiKey: ApiKeyEntity; token: string }> {\n const prefix = options.prefix ?? \"ak\";\n const random = randomBytes(24).toString(\"base64url\");\n const token = `${prefix}_${random}`;\n const hash = this.hashToken(token);\n const suffix = token.slice(-8);\n\n const apiKey = await this.repo.create({\n userId: options.userId,\n name: options.name,\n description: options.description,\n tokenHash: hash,\n tokenPrefix: prefix,\n tokenSuffix: suffix,\n roles: options.roles,\n expiresAt: options.expiresAt?.toISOString(),\n });\n\n this.log.info(\"API key created\", {\n apiKeyId: apiKey.id,\n userId: options.userId,\n name: options.name,\n });\n\n return { apiKey, token };\n }\n\n /**\n * List all non-revoked API keys for a user.\n */\n public async list(userId: string): Promise<ApiKeyEntity[]> {\n return this.repo.findMany({\n where: {\n userId: { eq: userId },\n revokedAt: { isNull: true },\n },\n orderBy: { column: \"createdAt\", direction: \"desc\" },\n });\n }\n\n // -------------------------------------------------------------------------\n // Admin Operations\n // -------------------------------------------------------------------------\n\n /**\n * Find all API keys with optional filtering (admin only).\n */\n public async findAll(query: {\n userId?: string;\n includeRevoked?: boolean;\n page?: number;\n size?: number;\n sort?: string;\n }) {\n query.sort ??= \"-createdAt\";\n\n const where = this.repo.createQueryWhere();\n\n if (query.userId) {\n where.userId = { eq: query.userId };\n }\n\n if (!query.includeRevoked) {\n where.revokedAt = { isNull: true };\n }\n\n return this.repo.paginate(query, { where }, { count: true });\n }\n\n /**\n * Get an API key by ID (admin only).\n */\n public async getById(id: string): Promise<ApiKeyEntity> {\n const apiKey = await this.repo.findById(id).catch(() => null);\n\n if (!apiKey) {\n throw new NotFoundError(\"API key not found\");\n }\n\n return apiKey;\n }\n\n /**\n * Revoke any API key (admin only).\n */\n public async revokeByAdmin(id: string): Promise<void> {\n const apiKey = await this.repo.findById(id).catch(() => null);\n\n if (!apiKey) {\n throw new NotFoundError(\"API key not found\");\n }\n\n if (apiKey.revokedAt) {\n return; // Already revoked\n }\n\n // Invalidate cache\n await this.validationCache.invalidate(apiKey.tokenHash);\n\n await this.repo.updateById(id, {\n revokedAt: this.dateTimeProvider.now().toISOString(),\n });\n\n this.log.info(\"API key revoked by admin\", {\n apiKeyId: id,\n userId: apiKey.userId,\n });\n }\n\n // -------------------------------------------------------------------------\n // User Operations\n // -------------------------------------------------------------------------\n\n /**\n * Revoke an API key. Only the owner can revoke their own keys.\n */\n public async revoke(id: string, userId: string): Promise<void> {\n const apiKey = await this.repo.findById(id).catch(() => null);\n\n if (!apiKey) {\n throw new NotFoundError(\"API key not found\");\n }\n\n if (apiKey.userId !== userId) {\n throw new ForbiddenError(\"Not your API key\");\n }\n\n await this.validationCache.invalidate(apiKey.tokenHash);\n\n await this.repo.updateById(id, {\n revokedAt: this.dateTimeProvider.now().toISOString(),\n });\n\n this.log.info(\"API key revoked\", {\n apiKeyId: id,\n userId,\n });\n }\n\n // -------------------------------------------------------------------------\n // Validation\n // -------------------------------------------------------------------------\n\n /**\n * Validate an API key token and return user info if valid.\n */\n public async validate(token: string): Promise<UserInfo | null> {\n // Quick check for API key format\n if (!token.includes(\"_\")) {\n return null;\n }\n\n const hash = this.hashToken(token);\n\n // Try cache first\n let apiKey = await this.validationCache.get(hash);\n\n // If not in cache, look up in database\n if (apiKey === undefined) {\n apiKey = await this.repo\n .findOne({\n where: { tokenHash: { eq: hash } },\n })\n .catch(() => null);\n\n // Store in cache (even if null, to prevent repeated lookups)\n if (apiKey) {\n await this.validationCache.set(hash, apiKey);\n }\n }\n\n if (!apiKey) {\n return null;\n }\n\n // Check revocation\n if (apiKey.revokedAt) {\n return null;\n }\n\n // Check expiration\n if (\n apiKey.expiresAt &&\n this.dateTimeProvider.now().isAfter(apiKey.expiresAt)\n ) {\n return null;\n }\n\n // Update usage stats (fire and forget)\n this.updateUsage(apiKey.id).catch((error) => {\n this.log.warn(\"Failed to update API key usage\", { error });\n });\n\n return {\n id: apiKey.userId,\n roles: apiKey.roles,\n };\n }\n\n /**\n * Update usage statistics for an API key.\n */\n protected async updateUsage(id: string): Promise<void> {\n const request = this.alepha.context.get<ServerRequest>(\"request\");\n\n await this.repo.updateById(id, {\n lastUsedAt: this.dateTimeProvider.now().toISOString(),\n lastUsedIp: request?.ip,\n usageCount: sql`${this.repo.table.usageCount} + 1`,\n });\n }\n\n /**\n * Hash a token using SHA-256.\n */\n protected hashToken(token: string): string {\n return createHash(\"sha256\").update(token).digest(\"hex\");\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { adminApiKeyQuerySchema } from \"../schemas/adminApiKeyQuerySchema.ts\";\nimport { adminApiKeyResourceSchema } from \"../schemas/adminApiKeyResourceSchema.ts\";\nimport { ApiKeyService } from \"../services/ApiKeyService.ts\";\n\n/**\n * REST API controller for admin API key management.\n * Admins can list, view, and revoke any API key.\n */\nexport class AdminApiKeyController {\n protected readonly url = \"/admin/api-keys\";\n protected readonly group = \"admin:api-keys\";\n protected readonly apiKeyService = $inject(ApiKeyService);\n\n /**\n * Find all API keys with optional filtering.\n */\n public readonly findApiKeys = $action({\n path: this.url,\n group: this.group,\n secure: true,\n description: \"Find API keys with pagination and filtering\",\n schema: {\n query: adminApiKeyQuerySchema,\n response: t.page(adminApiKeyResourceSchema),\n },\n handler: ({ query }) => {\n const { userId, includeRevoked, ...pagination } = query;\n return this.apiKeyService.findAll({\n userId,\n includeRevoked,\n ...pagination,\n });\n },\n });\n\n /**\n * Get an API key by ID.\n */\n public readonly getApiKey = $action({\n path: `${this.url}/:id`,\n group: this.group,\n secure: true,\n description: \"Get an API key by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: adminApiKeyResourceSchema,\n },\n handler: ({ params }) => this.apiKeyService.getById(params.id),\n });\n\n /**\n * Revoke any API key.\n */\n public readonly revokeApiKey = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n secure: true,\n description: \"Revoke an API key\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: okSchema,\n },\n handler: async ({ params }) => {\n await this.apiKeyService.revokeByAdmin(params.id);\n return { ok: true, id: params.id };\n },\n });\n}\n","import { t } from \"alepha\";\n\nexport const createApiKeyBodySchema = t.object({\n name: t.text({ minLength: 1, maxLength: 100 }),\n description: t.optional(t.text({ maxLength: 500 })),\n expiresAt: t.optional(t.datetime()),\n});\n","import { t } from \"alepha\";\n\nexport const createApiKeyResponseSchema = t.object({\n id: t.uuid(),\n name: t.string(),\n token: t.string(),\n tokenSuffix: t.string(),\n roles: t.array(t.string()),\n createdAt: t.datetime(),\n expiresAt: t.optional(t.datetime()),\n});\n","import { t } from \"alepha\";\n\nexport const listApiKeyItemSchema = t.object({\n id: t.uuid(),\n name: t.string(),\n tokenPrefix: t.string(),\n tokenSuffix: t.string(),\n roles: t.array(t.string()),\n createdAt: t.datetime(),\n lastUsedAt: t.optional(t.datetime()),\n expiresAt: t.optional(t.datetime()),\n usageCount: t.integer(),\n});\n\nexport const listApiKeyResponseSchema = t.array(listApiKeyItemSchema);\n","import { t } from \"alepha\";\n\nexport const revokeApiKeyParamsSchema = t.object({\n id: t.uuid(),\n});\n","import { t } from \"alepha\";\n\nexport const revokeApiKeyResponseSchema = t.object({\n ok: t.boolean(),\n});\n","import { $inject } from \"alepha\";\nimport { $action } from \"alepha/server\";\nimport { createApiKeyBodySchema } from \"../schemas/createApiKeyBodySchema.ts\";\nimport { createApiKeyResponseSchema } from \"../schemas/createApiKeyResponseSchema.ts\";\nimport { listApiKeyResponseSchema } from \"../schemas/listApiKeyResponseSchema.ts\";\nimport { revokeApiKeyParamsSchema } from \"../schemas/revokeApiKeyParamsSchema.ts\";\nimport { revokeApiKeyResponseSchema } from \"../schemas/revokeApiKeyResponseSchema.ts\";\nimport { ApiKeyService } from \"../services/ApiKeyService.ts\";\n\n/**\n * REST API controller for user's own API key management.\n * Users can create, list, and revoke their own API keys.\n */\nexport class ApiKeyController {\n protected readonly url = \"/api-keys\";\n protected readonly group = \"api-keys\";\n protected readonly apiKeyService = $inject(ApiKeyService);\n\n /**\n * Create a new API key for the authenticated user.\n * The token is only returned once upon creation.\n */\n public readonly createApiKey = $action({\n method: \"POST\",\n path: this.url,\n group: this.group,\n description: \"Create a new API key\",\n secure: true,\n schema: {\n body: createApiKeyBodySchema,\n response: createApiKeyResponseSchema,\n },\n handler: async (request) => {\n const { apiKey, token } = await this.apiKeyService.create({\n userId: request.user.id,\n name: request.body.name,\n description: request.body.description,\n roles: request.user.roles ?? [],\n expiresAt: request.body.expiresAt\n ? new Date(request.body.expiresAt)\n : undefined,\n });\n\n return {\n id: apiKey.id,\n name: apiKey.name,\n token,\n tokenSuffix: apiKey.tokenSuffix,\n roles: apiKey.roles,\n createdAt: apiKey.createdAt,\n expiresAt: apiKey.expiresAt,\n };\n },\n });\n\n /**\n * List all active API keys for the authenticated user.\n * Does not return the actual tokens.\n */\n public readonly listApiKeys = $action({\n path: this.url,\n group: this.group,\n description: \"List your API keys\",\n secure: true,\n schema: {\n response: listApiKeyResponseSchema,\n },\n handler: async (request) => {\n const apiKeys = await this.apiKeyService.list(request.user.id);\n\n return apiKeys.map((apiKey) => ({\n id: apiKey.id,\n name: apiKey.name,\n tokenPrefix: apiKey.tokenPrefix,\n tokenSuffix: apiKey.tokenSuffix,\n roles: apiKey.roles,\n createdAt: apiKey.createdAt,\n lastUsedAt: apiKey.lastUsedAt,\n expiresAt: apiKey.expiresAt,\n usageCount: apiKey.usageCount,\n }));\n },\n });\n\n /**\n * Revoke an API key. Only the owner can revoke their own keys.\n */\n public readonly revokeApiKey = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Revoke an API key\",\n secure: true,\n schema: {\n params: revokeApiKeyParamsSchema,\n response: revokeApiKeyResponseSchema,\n },\n handler: async (request) => {\n await this.apiKeyService.revoke(request.params.id, request.user.id);\n return { ok: true };\n },\n });\n}\n","import { $module } from \"alepha\";\nimport { AdminApiKeyController } from \"./controllers/AdminApiKeyController.ts\";\nimport { ApiKeyController } from \"./controllers/ApiKeyController.ts\";\nimport { ApiKeyService } from \"./services/ApiKeyService.ts\";\n\nexport * from \"./controllers/AdminApiKeyController.ts\";\nexport * from \"./controllers/ApiKeyController.ts\";\nexport * from \"./entities/apiKeyEntity.ts\";\nexport * from \"./schemas/adminApiKeyQuerySchema.ts\";\nexport * from \"./schemas/adminApiKeyResourceSchema.ts\";\nexport * from \"./schemas/createApiKeyBodySchema.ts\";\nexport * from \"./schemas/createApiKeyResponseSchema.ts\";\nexport * from \"./schemas/listApiKeyResponseSchema.ts\";\nexport * from \"./schemas/revokeApiKeyParamsSchema.ts\";\nexport * from \"./schemas/revokeApiKeyResponseSchema.ts\";\nexport * from \"./services/ApiKeyService.ts\";\n\n/**\n * | Stability | Since | Runtime |\n * |-----------|-------|---------|\n * | 3 - stable | 0.11.0 | node, bun, workerd|\n *\n * API key management module for programmatic access.\n *\n * **Features:**\n * - Create API keys with role snapshots\n * - List and revoke API keys\n * - 15-minute validation caching\n * - Query param (?api_key=) and Bearer header support\n *\n * **Integration:**\n * To enable API key authentication for an issuer, register the resolver:\n *\n * ```ts\n * class MyApp {\n * apiKeyService = $inject(ApiKeyService);\n * issuer = $issuer({\n * secret: env.APP_SECRET,\n * resolvers: [this.apiKeyService.createResolver()],\n * });\n * }\n * ```\n *\n * @module alepha.api.keys\n */\nexport const AlephaApiKeys = $module({\n name: \"alepha.api.keys\",\n services: [ApiKeyService, ApiKeyController, AdminApiKeyController],\n});\n","import { $bucket } from \"alepha/bucket\";\n\n/**\n * User-specific file storage wrapper service.\n *\n * This service provides file storage for user-related files such as:\n * - User avatars/profile pictures\n *\n * It is lazy-loaded when the `files` feature is enabled in the realm.\n */\nexport class UserFiles {\n /**\n * Bucket for user avatar storage.\n */\n public readonly avatars = $bucket({\n maxSize: 5 * 1024 * 1024, // 5 MB\n mimeTypes: [\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"],\n });\n}\n","import { $inject } from \"alepha\";\nimport { $job } from \"alepha/api/jobs\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { sessions } from \"../entities/sessions.ts\";\n\n/**\n * User-specific jobs wrapper service.\n *\n * This service handles user-related scheduled jobs such as:\n * - Session purge (cleaning up expired sessions)\n * - Verification code cleanup\n * - Inactive user notifications\n *\n * It is lazy-loaded when the `jobs` feature is enabled in the realm.\n */\nexport class UserJobs {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly sessionRepository = $repository(sessions);\n\n /**\n * Purge expired sessions from the database.\n *\n * This job runs daily at 3:00 AM and removes all sessions\n * where the `expiresAt` timestamp has passed.\n */\n public readonly purgeExpiredSessions = $job({\n name: \"users.purgeExpiredSessions\",\n description: \"Remove expired user sessions from the database\",\n cron: \"0 3 * * *\", // Daily at 3:00 AM\n handler: async () => {\n const now = this.dateTimeProvider.nowISOString();\n\n this.log.info(\"Starting expired sessions purge\", { cutoffTime: now });\n\n const expiredSessions = await this.sessionRepository.findMany({\n where: {\n expiresAt: { lt: now },\n },\n });\n\n if (expiredSessions.length === 0) {\n this.log.info(\"No expired sessions found\");\n return;\n }\n\n this.log.info(\"Found expired sessions\", {\n count: expiredSessions.length,\n });\n\n const deletedIds = await this.sessionRepository.deleteMany({\n expiresAt: { lt: now },\n });\n\n this.log.info(\"Expired sessions purged successfully\", {\n deletedCount: deletedIds.length,\n });\n },\n });\n}\n","import { $config } from \"alepha/api/parameters\";\nimport { realmAuthSettingsAtom } from \"../atoms/realmAuthSettingsAtom.ts\";\n\n/**\n * User-specific configuration service.\n *\n * This service wraps the core ConfigStore to provide realm settings management.\n * It is lazy-loaded when the `parameters` feature is enabled in the realm.\n */\nexport class UserParameters {\n /**\n * Realm authentication settings configuration.\n *\n * Controls user registration, login methods, verification requirements,\n * and password policies for the realm.\n */\n public readonly realmSettings = $config({\n name: \"alepha.api.users.realmSettings\",\n description: \"Realm authentication and registration settings\",\n schema: realmAuthSettingsAtom.schema,\n default: realmAuthSettingsAtom.options.default,\n });\n}\n","import { $context } from \"alepha\";\nimport { AlephaApiKeys, ApiKeyService } from \"alepha/api/keys\";\nimport { AlephaApiVerification } from \"alepha/api/verifications\";\nimport type { Repository } from \"alepha/orm\";\nimport {\n $issuer,\n type IssuerPrimitive,\n type IssuerPrimitiveOptions,\n type IssuerResolver,\n SecurityProvider,\n} from \"alepha/security\";\nimport {\n $authCredentials,\n $authGithub,\n $authGoogle,\n type AuthPrimitive,\n type Credentials,\n type LinkAccountOptions,\n type WithLinkFn,\n type WithLoginFn,\n} from \"alepha/server/auth\";\nimport type { RealmAuthSettings } from \"../atoms/realmAuthSettingsAtom.ts\";\nimport type { identities } from \"../entities/identities.ts\";\nimport type { sessions } from \"../entities/sessions.ts\";\nimport { DEFAULT_USER_REALM_NAME, type users } from \"../entities/users.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport { SessionService } from \"../services/SessionService.ts\";\nimport { UserAudits } from \"../services/UserAudits.ts\";\nimport { UserFiles } from \"../services/UserFiles.ts\";\nimport { UserJobs } from \"../services/UserJobs.ts\";\nimport { UserNotifications } from \"../services/UserNotifications.ts\";\nimport { UserParameters } from \"../services/UserParameters.ts\";\n\nexport type RealmPrimitive = IssuerPrimitive & WithLinkFn & WithLoginFn;\n\n/**\n * Already configured realm for user management.\n *\n * Realm contains two roles: `admin` and `user`.\n *\n * - `admin`: Has full access to all resources and permissions.\n * - `user`: Has access to their own resources and permissions, but cannot access admin-level resources.\n *\n * Realm uses session management for handling user sessions.\n *\n * Environment Variables:\n * - `APP_SECRET`: Secret key for signing tokens (if not provided in options).\n */\n\nexport const $realm = (options: RealmOptions = {}): RealmPrimitive => {\n const { alepha } = $context();\n const sessionService = alepha.inject(SessionService);\n const securityProvider = alepha.inject(SecurityProvider);\n const realmProvider = alepha.inject(RealmProvider);\n\n const name = options.issuer?.name ?? DEFAULT_USER_REALM_NAME;\n\n options.settings ??= {};\n\n if (options.settings.emailRequired) {\n options.settings.emailEnabled = true;\n }\n\n if (options.settings.usernameRequired) {\n options.settings.usernameEnabled = true;\n }\n\n if (options.settings.phoneRequired) {\n options.settings.phoneEnabled = true;\n }\n\n // Merge features with defaults\n const features: RealmFeatures = {\n jobs: false,\n notifications: false,\n apiKeys: false,\n parameters: false,\n files: false,\n audits: false,\n organizations: false,\n ...options.features,\n };\n\n // When notifications are disabled, force verification-dependent settings to false\n // These features require sending codes via email/SMS which won't work without notifications\n if (!features.notifications) {\n options.settings.verifyEmailRequired = false;\n options.settings.verifyPhoneRequired = false;\n options.settings.resetPasswordAllowed = false;\n }\n\n const realmRegistration = realmProvider.register(name, options);\n\n // Enable features based on configuration\n // Each feature registers its wrapper service which internally uses the module primitives\n if (features.files) {\n alepha.with(UserFiles);\n }\n\n if (features.audits) {\n alepha.with(UserAudits);\n }\n\n if (features.jobs) {\n alepha.with(UserJobs);\n }\n\n if (features.notifications) {\n alepha.with(AlephaApiVerification);\n alepha.with(UserNotifications);\n }\n\n if (features.parameters) {\n alepha.with(UserParameters);\n }\n\n // Collect custom resolvers that will be registered during $issuer.onInit()\n // This ensures they are registered AFTER the realm is created (not on the default test realm)\n const customResolvers: IssuerResolver[] = [\n ...(options.issuer?.resolvers ?? []),\n ];\n\n // Enable API key authentication - must be added to customResolvers before $issuer() call\n if (features.apiKeys) {\n alepha.with(AlephaApiKeys);\n const apiKeyService = alepha.inject(ApiKeyService);\n customResolvers.push(apiKeyService.createResolver());\n }\n\n const realm: RealmPrimitive = $issuer({\n ...options.issuer,\n name,\n secret: options.secret ?? securityProvider.secretKey,\n resolvers: customResolvers,\n roles: options.issuer?.roles ?? [\n {\n name: \"admin\",\n permissions: [\n {\n name: \"*\",\n },\n ],\n },\n {\n name: \"user\",\n permissions: [\n {\n name: \"*\",\n ownership: true,\n exclude: [\"admin:*\"],\n },\n ],\n },\n ],\n settings: {\n accessToken: {\n expiration: [15, \"minutes\"],\n },\n refreshToken: {\n expiration: [30, \"days\"],\n },\n onCreateSession: async (user, config) => {\n return sessionService.createSession(user, config.expiresIn);\n },\n onRefreshSession: async (refreshToken) => {\n return sessionService.refreshSession(refreshToken);\n },\n onDeleteSession: async (refreshToken) => {\n await sessionService.deleteSession(refreshToken);\n },\n ...options.issuer?.settings,\n },\n });\n\n realm.link = (name: string) => {\n return (ctx: LinkAccountOptions) =>\n sessionService.link(name, ctx.user, realm.name);\n };\n\n realm.login = (name: string) => {\n return (credentials: Credentials) => {\n return sessionService.login(\n name,\n credentials.username,\n credentials.password,\n realm.name,\n );\n };\n };\n\n const identities = options.identities ?? {\n credentials: true,\n };\n\n if (identities) {\n const auth: Record<string, AuthPrimitive> = {};\n if (identities.credentials) {\n auth.credentials = $authCredentials(realm);\n } else {\n // if credentials auth is disabled, disable registration as well\n realmRegistration.settings.registrationAllowed = false;\n }\n\n if (identities.google) {\n auth.google = $authGoogle(realm);\n }\n\n if (identities.github) {\n auth.github = $authGithub(realm);\n }\n\n alepha.with(() => auth);\n }\n\n return realm;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RealmFeatures {\n /**\n * Enable job execution tracking and purge functionality.\n *\n * @default false\n */\n jobs?: boolean;\n\n /**\n * Enable notification system for password reset, verification emails, etc.\n *\n * @default false\n */\n notifications?: boolean;\n\n /**\n * Enable API key authentication for programmatic access.\n *\n * When enabled, users can create API keys to access protected endpoints\n * without using JWT tokens. API keys are useful for:\n * - Programmatic access (CLI tools, scripts)\n * - Long-lived authentication tokens\n * - Third-party integrations (MCP servers)\n *\n * API keys can be passed via:\n * - Query parameter: `?api_key=ak_xxx`\n * - Bearer header: `Authorization: Bearer ak_xxx`\n *\n * @default false\n */\n apiKeys?: boolean;\n\n /**\n * Enable runtime configuration management.\n *\n * Allows configuring realm settings at runtime with versioning and scheduled activation.\n *\n * @default false\n */\n parameters?: boolean;\n\n /**\n * Enable file management for avatar uploads and attachments.\n *\n * @default false\n */\n files?: boolean;\n\n /**\n * Enable audit trail for compliance and event logging.\n *\n * @default false\n */\n audits?: boolean;\n\n /**\n * Enable organization management to group users.\n *\n * @default false\n */\n organizations?: boolean;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RealmOptions {\n /**\n * Secret key for signing tokens.\n *\n * If not provided, the secret from the SecurityProvider will be used (usually from the APP_SECRET environment variable).\n */\n secret?: string;\n\n /**\n * Issuer configuration options.\n *\n * It's already pre-configured for user management with admin and user roles.\n */\n issuer?: Partial<IssuerPrimitiveOptions>;\n\n /**\n * Override entities.\n */\n entities?: {\n users?: Repository<typeof users.schema>;\n identities?: Repository<typeof identities.schema>;\n sessions?: Repository<typeof sessions.schema>;\n };\n\n settings?: Partial<RealmAuthSettings>;\n\n identities?: {\n credentials?: true;\n google?: true;\n github?: true;\n };\n\n /**\n * Enable or disable realm features.\n *\n * Features control which modules are loaded with the realm.\n */\n features?: Partial<RealmFeatures>;\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const loginSchema = t.object({\n username: t.text({\n minLength: 3,\n maxLength: 100,\n description: \"Username or email address for login\",\n }),\n password: t.text({\n minLength: 6,\n description: \"User password\",\n }),\n});\n\nexport type LoginInput = Static<typeof loginSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const registerSchema = t.object({\n username: t.string({\n minLength: 3,\n maxLength: 20,\n pattern: /^[a-zA-Z0-9_]+$/,\n description: \"Username for the new account\",\n }),\n email: t.email({\n description: \"Email address for the new account\",\n }),\n password: t.string({\n minLength: 8,\n description: \"Password for the new account\",\n }),\n confirmPassword: t.string({\n minLength: 8,\n description: \"Confirmation of the password\",\n }),\n firstName: t.optional(\n t.string({\n maxLength: 100,\n description: \"User's first name\",\n }),\n ),\n lastName: t.optional(\n t.string({\n maxLength: 100,\n description: \"User's last name\",\n }),\n ),\n});\n\nexport type RegisterInput = Static<typeof registerSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const resetPasswordRequestSchema = t.object({\n email: t.email({\n description: \"Email address to send password reset link\",\n }),\n});\n\nexport const resetPasswordSchema = t.object({\n token: t.string({\n description: \"Password reset token from email\",\n }),\n password: t.string({\n minLength: 8,\n description: \"New password\",\n }),\n confirmPassword: t.string({\n minLength: 8,\n description: \"Confirmation of the new password\",\n }),\n});\n\nexport type ResetPasswordRequest = Static<typeof resetPasswordRequestSchema>;\nexport type ResetPasswordInput = Static<typeof resetPasswordSchema>;\n","import { $module } from \"alepha\";\nimport { AlephaEmail } from \"alepha/email\";\nimport { AlephaServerCompress } from \"alepha/server/compress\";\nimport { AlephaServerHelmet } from \"alepha/server/helmet\";\nimport { AdminIdentityController } from \"./controllers/AdminIdentityController.ts\";\nimport { AdminSessionController } from \"./controllers/AdminSessionController.ts\";\nimport { AdminUserController } from \"./controllers/AdminUserController.ts\";\nimport { RealmController } from \"./controllers/RealmController.ts\";\nimport { UserController } from \"./controllers/UserController.ts\";\nimport { RealmProvider } from \"./providers/RealmProvider.ts\";\nimport { CredentialService } from \"./services/CredentialService.ts\";\nimport { IdentityService } from \"./services/IdentityService.ts\";\nimport { RegistrationService } from \"./services/RegistrationService.ts\";\nimport { SessionCrudService } from \"./services/SessionCrudService.ts\";\nimport { SessionService } from \"./services/SessionService.ts\";\nimport { UserService } from \"./services/UserService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./atoms/realmAuthSettingsAtom.ts\";\nexport * from \"./controllers/AdminIdentityController.ts\";\nexport * from \"./controllers/AdminSessionController.ts\";\nexport * from \"./controllers/AdminUserController.ts\";\nexport * from \"./controllers/RealmController.ts\";\nexport * from \"./controllers/UserController.ts\";\nexport * from \"./entities/identities.ts\";\nexport * from \"./entities/sessions.ts\";\nexport * from \"./entities/users.ts\";\nexport * from \"./primitives/$realm.ts\";\nexport * from \"./providers/RealmProvider.ts\";\nexport * from \"./schemas/completePasswordResetRequestSchema.ts\";\nexport * from \"./schemas/completeRegistrationRequestSchema.ts\";\nexport * from \"./schemas/createUserSchema.ts\";\nexport * from \"./schemas/identityQuerySchema.ts\";\nexport * from \"./schemas/identityResourceSchema.ts\";\nexport * from \"./schemas/loginSchema.ts\";\nexport * from \"./schemas/passwordResetIntentResponseSchema.ts\";\nexport * from \"./schemas/realmConfigSchema.ts\";\nexport * from \"./schemas/registerSchema.ts\";\nexport * from \"./schemas/registrationIntentResponseSchema.ts\";\nexport * from \"./schemas/resetPasswordSchema.ts\";\nexport * from \"./schemas/sessionQuerySchema.ts\";\nexport * from \"./schemas/sessionResourceSchema.ts\";\nexport * from \"./schemas/updateUserSchema.ts\";\nexport * from \"./schemas/userQuerySchema.ts\";\nexport * from \"./schemas/userResourceSchema.ts\";\nexport * from \"./services/CredentialService.ts\";\nexport * from \"./services/IdentityService.ts\";\nexport * from \"./services/RegistrationService.ts\";\nexport * from \"./services/SessionCrudService.ts\";\nexport * from \"./services/SessionService.ts\";\nexport * from \"./services/UserAudits.ts\";\nexport * from \"./services/UserFiles.ts\";\nexport * from \"./services/UserJobs.ts\";\nexport * from \"./services/UserNotifications.ts\";\nexport * from \"./services/UserParameters.ts\";\nexport * from \"./services/UserService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | Stability | Since | Runtime |\n * |-----------|-------|---------|\n * | 3 - stable | 0.5.0 | node, bun, workerd|\n *\n * Complete user management with multi-realm support for multi-tenant applications.\n *\n * **Features:**\n * - User registration, login, and profile management\n * - Password reset workflows\n * - Email verification\n * - Session management with multiple devices\n * - Identity management (social logins, SSO)\n * - Multi-realm support for tenant isolation\n * - Credential management\n * - Entities: `users`, `identities`, `sessions`\n *\n * @module alepha.api.users\n */\nexport const AlephaApiUsers = $module({\n name: \"alepha.api.users\",\n services: [\n AlephaServerHelmet,\n AlephaServerCompress,\n AlephaEmail,\n RealmProvider,\n SessionService,\n SessionCrudService,\n CredentialService,\n RegistrationService,\n UserService,\n IdentityService,\n UserController,\n AdminUserController,\n AdminSessionController,\n AdminIdentityController,\n RealmController,\n ],\n});\n"],"mappings":"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AAW,CAAC,KAAM,IAAK;IAAE,UAAU;IAAc;GACnD;IAAE,WAAW,CAAC,KAAM,IAAK;IAAE,UAAU;IAAc;GACnD;IAAE,WAAW;KAAC;KAAM;KAAM;KAAK;IAAE,UAAU;IAAc;GAC1D;EACD,KAAK,CACH;GACE,WAAW;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACD,UAAU;GACX,CACF;EACD,KAAK,CAAC;GAAE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GAAE,UAAU;GAAa,CAAC;EACrE,MAAM,CAAC;GAAE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GAAE,UAAU;GAAc,CAAC;EAGvE,KAAK;GACH;IACE,WAAW;KAAC;KAAM;KAAM;KAAM;KAAM;KAAM;KAAM;KAAM;KAAK;IAC3D,UAAU;IACX;GACD;IACE,WAAW;KACT;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACD;IACD,UAAU;IACX;GACD;IACE,WAAW;KACT;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACD;IACD,UAAU;IACX;GACF;EACD,MAAM,CAAC;GAAE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GAAE,UAAU;GAAc,CAAC;EACvE,KAAK,CACH;GACE,WAAW;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACD,UAAU;GACX,CACF;EACD,KAAK,CACH;GACE,WAAW;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACD,UAAU;GACX,CACF;EACD,KAAK,CACH;GAAE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GAAE,UAAU;GAAoB,CACtE;EAGD,MAAM,CACJ;GACE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GACnC,UACE;GACH,CACF;EACD,MAAM,CACJ;GACE,WAAW;IAAC;IAAM;IAAM;IAAM;IAAK;GACnC,UACE;GACH,CACF;EACD,MAAM,CACJ;GACE,WAAW;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"}
|