alepha 0.13.0 → 0.13.1
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/dist/api-jobs/index.d.ts +26 -26
- package/dist/api-users/index.d.ts +1 -1
- package/dist/cli/{dist-Sz2EXvQX.cjs → dist-Dl9Vl7Ur.js} +17 -13
- package/dist/cli/{dist-BBPjuQ56.js.map → dist-Dl9Vl7Ur.js.map} +1 -1
- package/dist/cli/index.d.ts +3 -11
- package/dist/cli/index.js +106 -74
- package/dist/cli/index.js.map +1 -1
- package/dist/email/index.js +71 -73
- package/dist/email/index.js.map +1 -1
- package/dist/orm/index.d.ts +1 -1
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/index.d.ts +4 -4
- package/dist/retry/index.d.ts +1 -1
- package/dist/retry/index.js +2 -2
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +6 -6
- package/dist/security/index.d.ts +28 -28
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server-health/index.d.ts +17 -17
- package/dist/server-metrics/index.js +170 -174
- package/dist/server-metrics/index.js.map +1 -1
- package/dist/server-security/index.d.ts +9 -9
- package/dist/vite/index.js +4 -5
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts +7 -7
- package/package.json +52 -103
- package/src/cli/apps/AlephaPackageBuilderCli.ts +7 -2
- package/src/cli/assets/appRouterTs.ts +9 -0
- package/src/cli/assets/indexHtml.ts +2 -1
- package/src/cli/assets/mainBrowserTs.ts +10 -0
- package/src/cli/commands/CoreCommands.ts +6 -5
- package/src/cli/commands/DrizzleCommands.ts +65 -57
- package/src/cli/commands/VerifyCommands.ts +1 -1
- package/src/cli/services/ProjectUtils.ts +44 -38
- package/src/orm/providers/DrizzleKitProvider.ts +1 -1
- package/src/retry/descriptors/$retry.ts +5 -3
- package/src/server/providers/NodeHttpServerProvider.ts +1 -1
- package/src/vite/helpers/boot.ts +3 -3
- package/dist/api-files/index.cjs +0 -1293
- package/dist/api-files/index.cjs.map +0 -1
- package/dist/api-files/index.d.cts +0 -829
- package/dist/api-jobs/index.cjs +0 -274
- package/dist/api-jobs/index.cjs.map +0 -1
- package/dist/api-jobs/index.d.cts +0 -654
- package/dist/api-notifications/index.cjs +0 -380
- package/dist/api-notifications/index.cjs.map +0 -1
- package/dist/api-notifications/index.d.cts +0 -289
- package/dist/api-parameters/index.cjs +0 -66
- package/dist/api-parameters/index.cjs.map +0 -1
- package/dist/api-parameters/index.d.cts +0 -84
- package/dist/api-users/index.cjs +0 -6009
- package/dist/api-users/index.cjs.map +0 -1
- package/dist/api-users/index.d.cts +0 -4740
- package/dist/api-verifications/index.cjs +0 -407
- package/dist/api-verifications/index.cjs.map +0 -1
- package/dist/api-verifications/index.d.cts +0 -207
- package/dist/batch/index.cjs +0 -408
- package/dist/batch/index.cjs.map +0 -1
- package/dist/batch/index.d.cts +0 -330
- package/dist/bin/index.cjs +0 -17
- package/dist/bin/index.cjs.map +0 -1
- package/dist/bin/index.d.cts +0 -1
- package/dist/bucket/index.cjs +0 -303
- package/dist/bucket/index.cjs.map +0 -1
- package/dist/bucket/index.d.cts +0 -355
- package/dist/cache/index.cjs +0 -241
- package/dist/cache/index.cjs.map +0 -1
- package/dist/cache/index.d.cts +0 -202
- package/dist/cache-redis/index.cjs +0 -84
- package/dist/cache-redis/index.cjs.map +0 -1
- package/dist/cache-redis/index.d.cts +0 -40
- package/dist/cli/chunk-DSlc6foC.cjs +0 -43
- package/dist/cli/dist-BBPjuQ56.js +0 -2778
- package/dist/cli/dist-Sz2EXvQX.cjs.map +0 -1
- package/dist/cli/index.cjs +0 -1241
- package/dist/cli/index.cjs.map +0 -1
- package/dist/cli/index.d.cts +0 -422
- package/dist/command/index.cjs +0 -693
- package/dist/command/index.cjs.map +0 -1
- package/dist/command/index.d.cts +0 -340
- package/dist/core/index.cjs +0 -2264
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.d.cts +0 -1927
- package/dist/datetime/index.cjs +0 -318
- package/dist/datetime/index.cjs.map +0 -1
- package/dist/datetime/index.d.cts +0 -145
- package/dist/email/index.cjs +0 -10874
- package/dist/email/index.cjs.map +0 -1
- package/dist/email/index.d.cts +0 -186
- package/dist/fake/index.cjs +0 -34641
- package/dist/fake/index.cjs.map +0 -1
- package/dist/fake/index.d.cts +0 -74
- package/dist/file/index.cjs +0 -1212
- package/dist/file/index.cjs.map +0 -1
- package/dist/file/index.d.cts +0 -698
- package/dist/lock/index.cjs +0 -226
- package/dist/lock/index.cjs.map +0 -1
- package/dist/lock/index.d.cts +0 -361
- package/dist/lock-redis/index.cjs +0 -113
- package/dist/lock-redis/index.cjs.map +0 -1
- package/dist/lock-redis/index.d.cts +0 -24
- package/dist/logger/index.cjs +0 -521
- package/dist/logger/index.cjs.map +0 -1
- package/dist/logger/index.d.cts +0 -281
- package/dist/orm/index.cjs +0 -2986
- package/dist/orm/index.cjs.map +0 -1
- package/dist/orm/index.d.cts +0 -2213
- package/dist/queue/index.cjs +0 -1044
- package/dist/queue/index.cjs.map +0 -1
- package/dist/queue/index.d.cts +0 -1265
- package/dist/queue-redis/index.cjs +0 -873
- package/dist/queue-redis/index.cjs.map +0 -1
- package/dist/queue-redis/index.d.cts +0 -82
- package/dist/redis/index.cjs +0 -153
- package/dist/redis/index.cjs.map +0 -1
- package/dist/redis/index.d.cts +0 -82
- package/dist/retry/index.cjs +0 -146
- package/dist/retry/index.cjs.map +0 -1
- package/dist/retry/index.d.cts +0 -172
- package/dist/router/index.cjs +0 -111
- package/dist/router/index.cjs.map +0 -1
- package/dist/router/index.d.cts +0 -46
- package/dist/scheduler/index.cjs +0 -576
- package/dist/scheduler/index.cjs.map +0 -1
- package/dist/scheduler/index.d.cts +0 -145
- package/dist/security/index.cjs +0 -2402
- package/dist/security/index.cjs.map +0 -1
- package/dist/security/index.d.cts +0 -598
- package/dist/server/index.cjs +0 -1680
- package/dist/server/index.cjs.map +0 -1
- package/dist/server/index.d.cts +0 -810
- package/dist/server-auth/index.cjs +0 -3146
- package/dist/server-auth/index.cjs.map +0 -1
- package/dist/server-auth/index.d.cts +0 -1164
- package/dist/server-cache/index.cjs +0 -252
- package/dist/server-cache/index.cjs.map +0 -1
- package/dist/server-cache/index.d.cts +0 -164
- package/dist/server-compress/index.cjs +0 -141
- package/dist/server-compress/index.cjs.map +0 -1
- package/dist/server-compress/index.d.cts +0 -38
- package/dist/server-cookies/index.cjs +0 -234
- package/dist/server-cookies/index.cjs.map +0 -1
- package/dist/server-cookies/index.d.cts +0 -144
- package/dist/server-cors/index.cjs +0 -201
- package/dist/server-cors/index.cjs.map +0 -1
- package/dist/server-cors/index.d.cts +0 -140
- package/dist/server-health/index.cjs +0 -62
- package/dist/server-health/index.cjs.map +0 -1
- package/dist/server-health/index.d.cts +0 -58
- package/dist/server-helmet/index.cjs +0 -131
- package/dist/server-helmet/index.cjs.map +0 -1
- package/dist/server-helmet/index.d.cts +0 -97
- package/dist/server-links/index.cjs +0 -992
- package/dist/server-links/index.cjs.map +0 -1
- package/dist/server-links/index.d.cts +0 -513
- package/dist/server-metrics/index.cjs +0 -4535
- package/dist/server-metrics/index.cjs.map +0 -1
- package/dist/server-metrics/index.d.cts +0 -35
- package/dist/server-multipart/index.cjs +0 -237
- package/dist/server-multipart/index.cjs.map +0 -1
- package/dist/server-multipart/index.d.cts +0 -50
- package/dist/server-proxy/index.cjs +0 -186
- package/dist/server-proxy/index.cjs.map +0 -1
- package/dist/server-proxy/index.d.cts +0 -234
- package/dist/server-rate-limit/index.cjs +0 -241
- package/dist/server-rate-limit/index.cjs.map +0 -1
- package/dist/server-rate-limit/index.d.cts +0 -183
- package/dist/server-security/index.cjs +0 -316
- package/dist/server-security/index.cjs.map +0 -1
- package/dist/server-security/index.d.cts +0 -173
- package/dist/server-static/index.cjs +0 -170
- package/dist/server-static/index.cjs.map +0 -1
- package/dist/server-static/index.d.cts +0 -121
- package/dist/server-swagger/index.cjs +0 -1021
- package/dist/server-swagger/index.cjs.map +0 -1
- package/dist/server-swagger/index.d.cts +0 -382
- package/dist/sms/index.cjs +0 -221
- package/dist/sms/index.cjs.map +0 -1
- package/dist/sms/index.d.cts +0 -130
- package/dist/thread/index.cjs +0 -350
- package/dist/thread/index.cjs.map +0 -1
- package/dist/thread/index.d.cts +0 -260
- package/dist/topic/index.cjs +0 -282
- package/dist/topic/index.cjs.map +0 -1
- package/dist/topic/index.d.cts +0 -523
- package/dist/topic-redis/index.cjs +0 -71
- package/dist/topic-redis/index.cjs.map +0 -1
- package/dist/topic-redis/index.d.cts +0 -42
- package/dist/vite/index.cjs +0 -1077
- package/dist/vite/index.cjs.map +0 -1
- package/dist/vite/index.d.cts +0 -542
- package/dist/websocket/index.cjs +0 -1117
- package/dist/websocket/index.cjs.map +0 -1
- package/dist/websocket/index.d.cts +0 -861
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["Alepha","ServerRouterProvider","HttpError","Descriptor","KIND","SecurityProvider","JwtProvider","Alepha","$action","userAccountInfoSchema","ForbiddenError","user: UserAccountToken | undefined","UnauthorizedError","ownership: boolean | string | undefined","$realm","$role","$permission","AlephaServer","AlephaSecurity","Alepha","DateTimeProvider","directives: string[]","params: string[]","AlephaCache","Descriptor","KIND","Alepha","ServerRouterProvider","DateTimeProvider","FileDetector","AlephaServer","Descriptor","KIND","t","ServerRouterProvider","ServerProvider","Alepha","FileSystemProvider","alepha","$action","openApi: OpenApiDocument","AlephaSecurity","schemas: Record<string, any>","operation: OpenApiOperation","schema: any","AlephaServer"],"sources":["../../src/server-security/providers/ServerBasicAuthProvider.ts","../../src/server-security/descriptors/$basicAuth.ts","../../src/server-security/providers/ServerSecurityProvider.ts","../../src/server-security/index.ts","../../src/server-cache/providers/ServerCacheProvider.ts","../../src/server-cache/index.ts","../../src/server-static/descriptors/$serve.ts","../../src/server-static/providers/ServerStaticProvider.ts","../../src/server-static/index.ts","../../src/server-swagger/descriptors/$swagger.ts","../../src/server-swagger/providers/ServerSwaggerProvider.ts","../../src/server-swagger/index.ts"],"sourcesContent":["import { timingSafeEqual } from \"node:crypto\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n HttpError,\n type ServerRequest,\n ServerRouterProvider,\n} from \"alepha/server\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface BasicAuthOptions {\n username: string;\n password: string;\n}\n\nexport interface BasicAuthDescriptorConfig extends BasicAuthOptions {\n /** Name identifier for this basic auth (default: property key) */\n name?: string;\n /** Path patterns to match (supports wildcards like /devtools/*) */\n paths?: string[];\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerBasicAuthProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly routerProvider = $inject(ServerRouterProvider);\n protected readonly realm = \"Secure Area\";\n\n /**\n * Registered basic auth descriptors with their configurations\n */\n public readonly registeredAuths: BasicAuthDescriptorConfig[] = [];\n\n /**\n * Register a basic auth configuration (called by descriptors)\n */\n public registerAuth(config: BasicAuthDescriptorConfig): void {\n this.registeredAuths.push(config);\n }\n\n public readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n for (const auth of this.registeredAuths) {\n if (auth.paths) {\n for (const pattern of auth.paths) {\n const matchedRoutes = this.routerProvider.getRoutes(pattern);\n for (const route of matchedRoutes) {\n route.secure = {\n basic: {\n username: auth.username,\n password: auth.password,\n },\n };\n }\n }\n }\n }\n\n if (this.registeredAuths.length > 0) {\n this.log.info(\n `Initialized with ${this.registeredAuths.length} registered basic-auth configurations.`,\n );\n }\n },\n });\n\n /**\n * Hook into server:onRequest to check basic auth\n */\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: async ({ route, request }) => {\n const routeAuth = route.secure;\n if (\n typeof routeAuth === \"object\" &&\n \"basic\" in routeAuth &&\n routeAuth.basic\n ) {\n this.checkAuth(request, routeAuth.basic);\n }\n },\n });\n\n /**\n * Hook into action:onRequest to check basic auth for actions\n */\n public readonly onActionRequest = $hook({\n on: \"action:onRequest\",\n handler: async ({ action, request }) => {\n const routeAuth = action.route.secure;\n if (isBasicAuth(routeAuth)) {\n this.checkAuth(request, routeAuth.basic);\n }\n },\n });\n\n /**\n * Check basic authentication\n */\n public checkAuth(request: ServerRequest, options: BasicAuthOptions): void {\n const authHeader = request.headers?.authorization;\n\n if (!authHeader || !authHeader.startsWith(\"Basic \")) {\n this.sendAuthRequired(request);\n throw new HttpError({\n status: 401,\n message: \"Authentication required\",\n });\n }\n\n // decode base64 credentials\n const base64Credentials = authHeader.slice(6); // Remove \"Basic \"\n const credentials = Buffer.from(base64Credentials, \"base64\").toString(\n \"utf-8\",\n );\n\n // split only on the first colon to handle passwords with colons\n const colonIndex = credentials.indexOf(\":\");\n const username =\n colonIndex !== -1 ? credentials.slice(0, colonIndex) : credentials;\n const password = colonIndex !== -1 ? credentials.slice(colonIndex + 1) : \"\";\n\n // verify credentials using timing-safe comparison to prevent timing attacks\n const isValid = this.timingSafeCredentialCheck(\n username,\n password,\n options.username,\n options.password,\n );\n\n if (!isValid) {\n this.sendAuthRequired(request);\n this.log.warn(`Failed basic auth attempt for user`, {\n username,\n });\n throw new HttpError({\n status: 401,\n message: \"Invalid credentials\",\n });\n }\n }\n\n /**\n * Performs a timing-safe comparison of credentials to prevent timing attacks.\n * Always compares both username and password to avoid leaking which one is wrong.\n */\n protected timingSafeCredentialCheck(\n inputUsername: string,\n inputPassword: string,\n expectedUsername: string,\n expectedPassword: string,\n ): boolean {\n // Convert to buffers for timing-safe comparison\n const inputUserBuf = Buffer.from(inputUsername, \"utf-8\");\n const expectedUserBuf = Buffer.from(expectedUsername, \"utf-8\");\n const inputPassBuf = Buffer.from(inputPassword, \"utf-8\");\n const expectedPassBuf = Buffer.from(expectedPassword, \"utf-8\");\n\n // timingSafeEqual requires same-length buffers\n // When lengths differ, we compare against a dummy buffer to maintain constant time\n const userMatch = this.safeCompare(inputUserBuf, expectedUserBuf);\n const passMatch = this.safeCompare(inputPassBuf, expectedPassBuf);\n\n // Both must match - bitwise AND avoids short-circuit evaluation\n // eslint-disable-next-line no-bitwise\n return (userMatch & passMatch) === 1;\n }\n\n /**\n * Compares two buffers in constant time, handling different lengths safely.\n * Returns 1 if equal, 0 if not equal.\n */\n protected safeCompare(input: Buffer, expected: Buffer): number {\n // If lengths differ, compare input against itself to maintain timing\n // but return 0 (not equal)\n if (input.length !== expected.length) {\n // Still perform a comparison to keep timing consistent\n timingSafeEqual(input, input);\n return 0;\n }\n\n return timingSafeEqual(input, expected) ? 1 : 0;\n }\n\n /**\n * Send WWW-Authenticate header\n */\n protected sendAuthRequired(request: ServerRequest): void {\n request.reply.setHeader(\"WWW-Authenticate\", `Basic realm=\"${this.realm}\"`);\n }\n}\n\nexport const isBasicAuth = (\n value: unknown,\n): value is { basic: BasicAuthOptions } => {\n return (\n typeof value === \"object\" && !!value && \"basic\" in value && !!value.basic\n );\n};\n","import { $inject, createDescriptor, Descriptor, KIND } from \"alepha\";\nimport type { ServerRequest } from \"alepha/server\";\nimport type {\n BasicAuthDescriptorConfig,\n BasicAuthOptions,\n} from \"../providers/ServerBasicAuthProvider.ts\";\nimport { ServerBasicAuthProvider } from \"../providers/ServerBasicAuthProvider.ts\";\n\n/**\n * Declares HTTP Basic Authentication for server routes.\n * This descriptor provides methods to protect routes with username/password authentication.\n */\nexport const $basicAuth = (\n options: BasicAuthDescriptorConfig,\n): AbstractBasicAuthDescriptor => {\n return createDescriptor(BasicAuthDescriptor, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface AbstractBasicAuthDescriptor {\n readonly name: string;\n readonly options: BasicAuthDescriptorConfig;\n check(request: ServerRequest, options?: BasicAuthOptions): void;\n}\n\nexport class BasicAuthDescriptor\n extends Descriptor<BasicAuthDescriptorConfig>\n implements AbstractBasicAuthDescriptor\n{\n protected readonly serverBasicAuthProvider = $inject(ServerBasicAuthProvider);\n\n public get name(): string {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n protected onInit() {\n // Register this auth configuration with the provider\n this.serverBasicAuthProvider.registerAuth(this.options);\n }\n\n /**\n * Checks basic auth for the given request using this descriptor's configuration.\n */\n public check(request: ServerRequest, options?: BasicAuthOptions): void {\n const mergedOptions = { ...this.options, ...options };\n this.serverBasicAuthProvider.checkAuth(request, mergedOptions);\n }\n}\n\n$basicAuth[KIND] = BasicAuthDescriptor;\n","import { randomUUID } from \"node:crypto\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n JwtProvider,\n type Permission,\n SecurityProvider,\n type UserAccountToken,\n userAccountInfoSchema,\n} from \"alepha/security\";\nimport {\n $action,\n ForbiddenError,\n type ServerRequest,\n UnauthorizedError,\n} from \"alepha/server\";\nimport {\n type BasicAuthOptions,\n isBasicAuth,\n} from \"./ServerBasicAuthProvider.ts\";\n\nexport class ServerSecurityProvider {\n protected readonly log = $logger();\n protected readonly securityProvider = $inject(SecurityProvider);\n protected readonly jwtProvider = $inject(JwtProvider);\n protected readonly alepha = $inject(Alepha);\n\n protected readonly onConfigure = $hook({\n on: \"configure\",\n handler: async () => {\n for (const action of this.alepha.descriptors($action)) {\n // -------------------------------------------------------------------------------------------------------------\n // if the action is disabled or not secure, we do NOT create a permission for it\n // -------------------------------------------------------------------------------------------------------------\n if (\n action.options.disabled ||\n action.options.secure === false ||\n this.securityProvider.getRealms().length === 0\n ) {\n continue;\n }\n\n const secure = action.options.secure;\n if (typeof secure !== \"object\") {\n this.securityProvider.createPermission({\n name: action.name,\n group: action.group,\n method: action.route.method,\n path: action.route.path,\n });\n }\n }\n },\n });\n\n // -------------------------------------------------------------------------------------------------------------------\n\n protected readonly onActionRequest = $hook({\n on: \"action:onRequest\",\n handler: async ({ action, request, options }) => {\n // if you set explicitly secure: false, we assume you don't want any security check\n // but only if no user is provided in options\n if (action.options.secure === false && !options.user) {\n this.log.trace(\"Skipping security check for route\");\n return;\n }\n\n if (isBasicAuth(action.route.secure)) {\n return;\n }\n\n const permission = this.securityProvider\n .getPermissions()\n .find(\n (it) =>\n it.path === action.route.path && it.method === action.route.method,\n );\n\n try {\n request.user = this.createUserFromLocalFunctionContext(\n options,\n permission,\n );\n\n const route = action.route;\n if (typeof route.secure === \"object\") {\n this.check(request.user, route.secure);\n }\n\n this.alepha.state.set(\n \"alepha.server.request.user\",\n this.alepha.codec.decode(userAccountInfoSchema, request.user),\n );\n } catch (error) {\n if (action.options.secure || permission) {\n throw error;\n }\n // else, we skip the security check\n this.log.trace(\"Skipping security check for action\");\n }\n },\n });\n\n protected readonly onRequest = $hook({\n on: \"server:onRequest\",\n priority: \"last\",\n handler: async ({ request, route }) => {\n // if you set explicitly secure: false, we assume you don't want any security check\n if (route.secure === false) {\n this.log.trace(\n \"Skipping security check for route - explicitly disabled\",\n );\n return;\n }\n\n if (isBasicAuth(route.secure)) {\n return;\n }\n\n const permission = this.securityProvider\n .getPermissions()\n .find((it) => it.path === route.path && it.method === route.method);\n\n if (!request.headers.authorization && !route.secure && !permission) {\n this.log.trace(\n \"Skipping security check for route - no authorization header and not secure\",\n );\n return;\n }\n\n try {\n // set user to request\n request.user = await this.securityProvider.createUserFromToken(\n request.headers.authorization,\n { permission },\n );\n\n if (typeof route.secure === \"object\") {\n this.check(request.user, route.secure);\n }\n\n this.alepha.state.set(\n \"alepha.server.request.user\",\n // remove sensitive info\n this.alepha.codec.decode(userAccountInfoSchema, request.user),\n );\n\n this.log.trace(\"User set from request token\", {\n user: request.user,\n permission,\n });\n } catch (error) {\n if (route.secure || permission) {\n throw error;\n }\n\n // else, we skip the security check\n this.log.trace(\n \"Skipping security check for route - error occurred\",\n error,\n );\n }\n },\n });\n\n // -------------------------------------------------------------------------------------------------------------------\n\n protected check(user: UserAccountToken, secure: ServerRouteSecure) {\n if (secure.realm) {\n if (user.realm !== secure.realm) {\n throw new ForbiddenError(\n `User must belong to realm '${secure.realm}' to access this route`,\n );\n }\n }\n }\n\n /**\n * Get the user account token for a local action call.\n * There are three possible sources for the user:\n * - `options.user`: the user passed in the options\n * - `\"system\"`: the system user from the state (you MUST set state `server.security.system.user`)\n * - `\"context\"`: the user from the request context (you MUST be in an HTTP request context)\n *\n * Priority order: `options.user` > `\"system\"` > `\"context\"`.\n *\n * In testing environment, if no user is provided, a test user is created based on the SecurityProvider's roles.\n */\n protected createUserFromLocalFunctionContext(\n options: { user?: UserAccountToken | \"system\" | \"context\" },\n permission?: Permission,\n ): UserAccountToken {\n const fromOptions =\n typeof options.user === \"object\" ? options.user : undefined;\n\n const type = typeof options.user === \"string\" ? options.user : undefined;\n\n let user: UserAccountToken | undefined;\n\n const fromContext = this.alepha.context.get<ServerRequest>(\"request\")?.user;\n const fromSystem = this.alepha.state.get(\n \"alepha.server.security.system.user\",\n );\n\n if (type === \"system\") {\n user = fromSystem;\n } else if (type === \"context\") {\n user = fromContext;\n } else {\n user = fromOptions ?? fromContext ?? fromSystem;\n }\n\n if (!user) {\n // in testing mode, we create a test user\n if (this.alepha.isTest() && !(\"user\" in options)) {\n return this.createTestUser();\n }\n\n throw new UnauthorizedError(\"User is required for calling this action\");\n }\n\n const roles =\n user.roles ??\n (this.alepha.isTest()\n ? this.securityProvider.getRoles().map((role) => role.name)\n : []);\n let ownership: boolean | string | undefined;\n\n if (permission) {\n const result = this.securityProvider.checkPermission(\n permission,\n ...roles,\n );\n if (!result.isAuthorized) {\n throw new ForbiddenError(\n `Permission '${this.securityProvider.permissionToString(permission)}' is required for this route`,\n );\n }\n ownership = result.ownership;\n }\n\n // create a new user object with ownership if needed\n return {\n ...user,\n ownership,\n };\n }\n\n // ---------------------------------------------------------------------------------------------------------------\n // TESTING ONLY\n // ---------------------------------------------------------------------------------------------------------------\n\n protected createTestUser(): UserAccountToken {\n return {\n id: randomUUID(),\n name: \"Test\",\n roles: this.securityProvider.getRoles().map((role) => role.name),\n };\n }\n\n protected readonly onClientRequest = $hook({\n on: \"client:onRequest\",\n handler: async ({ request, options }) => {\n if (!this.alepha.isTest()) {\n return;\n }\n\n // skip helper if user is explicitly set to undefined\n if (\"user\" in options && options.user === undefined) {\n return;\n }\n\n request.headers = new Headers(request.headers);\n\n if (!request.headers.has(\"authorization\")) {\n const test = this.createTestUser();\n const user =\n typeof options?.user === \"object\" ? options.user : undefined;\n const sub = user?.id ?? test.id;\n const roles = user?.roles ?? test.roles;\n\n const token = await this.jwtProvider.create(\n {\n sub,\n roles,\n },\n user?.realm ?? this.securityProvider.getRealms()[0]?.name,\n );\n\n request.headers.set(\"authorization\", `Bearer ${token}`);\n }\n },\n });\n}\n\nexport type ServerRouteSecure = {\n realm?: string;\n basic?: BasicAuthOptions;\n};\n","import { $module } from \"alepha\";\nimport {\n $permission,\n $realm,\n $role,\n AlephaSecurity,\n type UserAccount,\n type UserAccountToken,\n} from \"alepha/security\";\nimport { AlephaServer, type FetchOptions } from \"alepha/server\";\nimport { $basicAuth } from \"./descriptors/$basicAuth.ts\";\nimport { ServerBasicAuthProvider } from \"./providers/ServerBasicAuthProvider.ts\";\nimport {\n type ServerRouteSecure,\n ServerSecurityProvider,\n} from \"./providers/ServerSecurityProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./descriptors/$basicAuth.ts\";\nexport * from \"./providers/ServerBasicAuthProvider.ts\";\nexport * from \"./providers/ServerSecurityProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface State {\n /**\n * Real (or fake) user account, used for internal actions.\n *\n * If you define this, you assume that all actions are executed by this user by default.\n * > To force a different user, you need to pass it explicitly in the options.\n */\n\n \"alepha.server.security.system.user\"?: UserAccountToken;\n\n /**\n * The authenticated user account attached to the server request state.\n *\n * @internal\n */\n \"alepha.server.request.user\"?: UserAccount;\n }\n}\n\ndeclare module \"alepha/server\" {\n interface ServerRequest<TConfig> {\n user?: UserAccountToken; // for all routes, user is maybe present\n }\n\n interface ServerActionRequest<TConfig> {\n user: UserAccountToken; // for actions, user is always present\n }\n\n interface ServerRoute {\n /**\n * If true, the route will be protected by the security provider.\n * All actions are secure by default, but you can disable it for specific actions.\n */\n secure?: boolean | ServerRouteSecure;\n }\n\n interface ClientRequestOptions extends FetchOptions {\n /**\n * Forward user from the previous request.\n * If \"system\", use system user. @see {ServerSecurityProvider.localSystemUser}\n * If \"context\", use the user from the current context (e.g. request).\n *\n * @default \"system\" if provided, else \"context\" if available.\n */\n user?: UserAccountToken | \"system\" | \"context\";\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Server that provides security features. Based on the Alepha Security module.\n *\n * By default, all $action will be guarded by a permission check.\n *\n * @see {@link ServerSecurityProvider}\n * @module alepha.server.security\n */\nexport const AlephaServerSecurity = $module({\n name: \"alepha.server.security\",\n descriptors: [$realm, $role, $permission, $basicAuth],\n services: [\n AlephaServer,\n AlephaSecurity,\n ServerSecurityProvider,\n ServerBasicAuthProvider,\n ],\n});\n","import type { BinaryLike } from \"node:crypto\";\nimport { createHash } from \"node:crypto\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport { $cache, type CacheDescriptorOptions } from \"alepha/cache\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport {\n ActionDescriptor,\n type RequestConfigSchema,\n type ServerRequest,\n type ServerRoute,\n} from \"alepha/server\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/server\" {\n interface ServerRoute {\n /**\n * Enable caching for this route.\n * - If true: enables both store and etag\n * - If object: fine-grained control over store, etag, and cache-control headers\n *\n * @default false\n */\n cache?: ServerRouteCache;\n }\n\n interface ActionDescriptor<TConfig extends RequestConfigSchema> {\n invalidate: () => Promise<void>;\n }\n}\n\nActionDescriptor.prototype.invalidate = async function (\n this: ActionDescriptor<RequestConfigSchema>,\n) {\n await this.alepha.inject(ServerCacheProvider).invalidate(this.route);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerCacheProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly time = $inject(DateTimeProvider);\n protected readonly cache = $cache<RouteCacheEntry>({\n provider: \"memory\",\n });\n\n public generateETag(content: BinaryLike): string {\n return `\"${createHash(\"md5\").update(content).digest(\"hex\")}\"`;\n }\n\n public async invalidate(route: ServerRoute) {\n const cache = route.cache;\n if (!cache) {\n return;\n }\n\n await this.cache.invalidate(this.createCacheKey(route));\n }\n\n protected readonly onActionRequest = $hook({\n on: \"action:onRequest\",\n handler: async ({ action, request }) => {\n const cache = action.route.cache;\n\n const shouldStore = this.shouldStore(cache);\n\n // Only check cache if storing is enabled\n if (shouldStore) {\n const key = this.createCacheKey(action.route, request);\n const cached = await this.cache.get(key);\n\n if (cached) {\n const body =\n cached.contentType === \"application/json\"\n ? JSON.parse(cached.body)\n : cached.body;\n\n this.log.trace(\"Cache hit for action\", {\n key,\n action: action.name,\n });\n\n request.reply.body = body; // just re-use, full trust\n } else {\n this.log.trace(\"Cache miss for action\", {\n key,\n action: action.name,\n });\n }\n }\n },\n });\n\n protected readonly onActionResponse = $hook({\n on: \"action:onResponse\",\n handler: async ({ action, request, response }) => {\n const cache = action.route.cache;\n\n const shouldStore = this.shouldStore(cache);\n\n if (!shouldStore || !response) {\n return;\n }\n\n // Don't cache error responses (status >= 400)\n if (request.reply.status && request.reply.status >= 400) {\n return;\n }\n\n // TODO: serialize the response body, exactly like in the server response hook\n // this is bad\n const contentType =\n typeof response === \"string\" ? \"text/plain\" : \"application/json\";\n const body =\n contentType === \"text/plain\" ? response : JSON.stringify(response);\n\n const generatedEtag = this.generateETag(body);\n const lastModified = this.time.toISOString();\n\n // Store response for cached actions\n const key = this.createCacheKey(action.route, request);\n\n this.log.trace(\"Storing response\", {\n key,\n action: action.name,\n });\n\n await this.cache.set(key, {\n body: body,\n lastModified,\n contentType: contentType,\n hash: generatedEtag,\n });\n\n // Set Cache-Control header if configured (for HTTP responses)\n const cacheControl = this.buildCacheControlHeader(cache);\n if (cacheControl) {\n request.reply.setHeader(\"cache-control\", cacheControl);\n }\n },\n });\n\n protected readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: async ({ route, request }) => {\n const cache = route.cache;\n\n const shouldStore = this.shouldStore(cache);\n const shouldUseEtag = this.shouldUseEtag(cache);\n\n // Check for cached response or ETag\n if (!shouldStore && !shouldUseEtag) {\n return;\n }\n\n const key = this.createCacheKey(route, request);\n const cached = await this.cache.get(key);\n\n if (cached) {\n // Check if client has matching ETag - return 304 for both cached and etag-only routes\n if (\n request.headers[\"if-none-match\"] === cached.hash ||\n request.headers[\"if-modified-since\"] === cached.lastModified\n ) {\n request.reply.status = 304;\n request.reply.setHeader(\"etag\", cached.hash);\n request.reply.setHeader(\"last-modified\", cached.lastModified);\n this.log.trace(\"ETag match, returning 304\", {\n route: route.path,\n etag: cached.hash,\n });\n return;\n }\n\n // Only serve cached content if storing is enabled (not for etag-only routes)\n if (shouldStore) {\n this.log.trace(\"Cache hit for route\", {\n key,\n route: route.path,\n });\n\n // if the cache is found, we can skip the request processing\n // and return the cached response\n request.reply.body = cached.body;\n request.reply.status = cached.status ?? 200;\n\n if (cached.contentType) {\n request.reply.setHeader(\"Content-Type\", cached.contentType);\n }\n\n request.reply.setHeader(\"etag\", cached.hash);\n request.reply.setHeader(\"last-modified\", cached.lastModified);\n }\n } else if (shouldStore) {\n this.log.trace(\"Cache miss for route\", {\n key,\n route: route.path,\n });\n }\n },\n });\n\n protected readonly onSend = $hook({\n on: \"server:onSend\",\n handler: async ({ route, request }) => {\n // before sending the response, check if the ETag matches\n // and if so, return a 304 Not Modified response\n // -> this is only relevant for etag-only routes, not cached routes <-\n const cache = route.cache;\n\n const shouldStore = this.shouldStore(cache);\n const shouldUseEtag = this.shouldUseEtag(cache);\n\n if (request.reply.headers.etag) {\n // ETag already set, skip\n return;\n }\n\n if (\n !shouldStore &&\n shouldUseEtag &&\n request.reply.body != null &&\n (typeof request.reply.body === \"string\" ||\n Buffer.isBuffer(request.reply.body))\n ) {\n const generatedEtag = this.generateETag(request.reply.body);\n\n if (request.headers[\"if-none-match\"] === generatedEtag) {\n request.reply.status = 304;\n request.reply.body = undefined;\n request.reply.setHeader(\"etag\", generatedEtag);\n this.log.trace(\"ETag match on send, returning 304\", {\n route: route.path,\n etag: generatedEtag,\n });\n return;\n }\n }\n },\n });\n\n protected readonly onResponse = $hook({\n on: \"server:onResponse\",\n priority: \"first\",\n handler: async ({ route, request, response }) => {\n const cache = route.cache;\n\n // Set Cache-Control header if configured\n const cacheControl = this.buildCacheControlHeader(cache);\n if (cacheControl) {\n response.headers[\"cache-control\"] = cacheControl;\n }\n\n const shouldStore = this.shouldStore(cache);\n const shouldUseEtag = this.shouldUseEtag(cache);\n\n // Skip if neither cache nor etag is enabled\n if (!shouldStore && !shouldUseEtag) {\n return;\n }\n\n // Only process string responses (text, html, json, etc.)\n // Buffer is not supported by alepha/cache for now\n if (typeof response.body !== \"string\") {\n return;\n }\n\n // Don't cache error responses (status >= 400)\n if (response.status && response.status >= 400) {\n return;\n }\n\n const key = this.createCacheKey(route, request);\n const generatedEtag = this.generateETag(response.body);\n const lastModified = this.time.toISOString();\n\n // Initialize headers if not present\n response.headers ??= {};\n\n // Store response if storing is enabled\n if (shouldStore) {\n this.log.trace(\"Storing response\", {\n key,\n route: route.path,\n cache: !!cache,\n etag: shouldUseEtag,\n });\n\n await this.cache.set(key, {\n body: response.body,\n status: response.status,\n contentType: response.headers?.[\"content-type\"],\n lastModified,\n hash: generatedEtag,\n });\n }\n\n // Set ETag headers if etag is enabled\n if (shouldUseEtag) {\n response.headers.etag = generatedEtag;\n response.headers[\"last-modified\"] = lastModified;\n }\n },\n });\n\n public buildCacheControlHeader(cache?: ServerRouteCache): string | undefined {\n if (!cache) {\n return undefined;\n }\n\n // If cache is true or a DurationLike, no Cache-Control header is set\n if (\n cache === true ||\n typeof cache === \"string\" ||\n typeof cache === \"number\"\n ) {\n return undefined;\n }\n\n const control = cache.control;\n if (!control) {\n return undefined;\n }\n\n // If control is a string, return it directly\n if (typeof control === \"string\") {\n return control;\n }\n\n // If control is true, return default Cache-Control\n if (control === true) {\n return \"public, max-age=300\";\n }\n\n // Build Cache-Control from object directives\n const directives: string[] = [];\n\n if (control.public) {\n directives.push(\"public\");\n }\n if (control.private) {\n directives.push(\"private\");\n }\n if (control.noCache) {\n directives.push(\"no-cache\");\n }\n if (control.noStore) {\n directives.push(\"no-store\");\n }\n if (control.maxAge !== undefined) {\n const maxAgeSeconds = this.durationToSeconds(control.maxAge);\n directives.push(`max-age=${maxAgeSeconds}`);\n }\n if (control.sMaxAge !== undefined) {\n const sMaxAgeSeconds = this.durationToSeconds(control.sMaxAge);\n directives.push(`s-maxage=${sMaxAgeSeconds}`);\n }\n if (control.mustRevalidate) {\n directives.push(\"must-revalidate\");\n }\n if (control.proxyRevalidate) {\n directives.push(\"proxy-revalidate\");\n }\n if (control.immutable) {\n directives.push(\"immutable\");\n }\n\n return directives.length > 0 ? directives.join(\", \") : undefined;\n }\n\n protected durationToSeconds(duration: number | DurationLike): number {\n if (typeof duration === \"number\") {\n return duration;\n }\n\n return this.time.duration(duration).asSeconds();\n }\n\n protected shouldStore(cache?: ServerRouteCache): boolean {\n if (!cache) return false;\n if (cache === true) return true;\n if (typeof cache === \"object\" && cache.store) return true;\n return false;\n }\n\n protected shouldUseEtag(cache?: ServerRouteCache): boolean {\n // cache: true enables etag\n if (cache === true) return true;\n // Check object form\n if (typeof cache === \"object\" && cache.etag) return true;\n return false;\n }\n\n protected createCacheKey(route: ServerRoute, config?: ServerRequest): string {\n const params: string[] = [];\n for (const [key, value] of Object.entries(config?.params ?? {})) {\n params.push(`${key}=${value}`);\n }\n for (const [key, value] of Object.entries(config?.query ?? {})) {\n params.push(`${key}=${value}`);\n }\n\n return `${route.method}:${route.path.replaceAll(\":\", \"\")}:${params.join(\",\").replaceAll(\":\", \"\")}`;\n }\n}\n\nexport type ServerRouteCache =\n /**\n * If true, enables caching with:\n * - store: true\n * - etag: true\n */\n | boolean\n /**\n * Object configuration for fine-grained cache control.\n *\n * If empty, no caching will be applied.\n */\n | {\n /**\n * If true, enables storing cached responses. (in-memory, Redis, @see alepha/cache for other providers)\n * If a DurationLike is provided, it will be used as the TTL for the cache.\n * If CacheDescriptorOptions is provided, it will be used to configure the cache storage.\n *\n * @default false\n */\n store?: true | DurationLike | CacheDescriptorOptions;\n /**\n * If true, enables ETag support for the cached responses.\n */\n etag?: true;\n /**\n * - If true, sets Cache-Control to \"public, max-age=300\" (5 minutes).\n * - If string, sets Cache-Control to the provided value directly.\n * - If object, configures Cache-Control directives.\n */\n control?: /**\n * If true, sets Cache-Control to \"public, max-age=300\" (5 minutes).\n */\n | true\n /**\n * If string, sets Cache-Control to the provided value directly.\n */\n | string\n /**\n * If object, configures Cache-Control directives.\n */\n | {\n /**\n * Indicates that the response may be cached by any cache.\n */\n public?: boolean;\n /**\n * Indicates that the response is intended for a single user and must not be stored by a shared cache.\n */\n private?: boolean;\n /**\n * Forces caches to submit the request to the origin server for validation before releasing a cached copy.\n */\n noCache?: boolean;\n /**\n * Instructs caches not to store the response.\n */\n noStore?: boolean;\n /**\n * Maximum amount of time a resource is considered fresh.\n * Can be specified as a number (seconds) or as a DurationLike object.\n *\n * @example 300 // 5 minutes in seconds\n * @example { minutes: 5 } // 5 minutes\n * @example { hours: 1 } // 1 hour\n */\n maxAge?: number | DurationLike;\n /**\n * Overrides max-age for shared caches (e.g., CDNs).\n * Can be specified as a number (seconds) or as a DurationLike object.\n */\n sMaxAge?: number | DurationLike;\n /**\n * Indicates that once a resource becomes stale, caches must not use it without successful validation.\n */\n mustRevalidate?: boolean;\n /**\n * Similar to must-revalidate, but only for shared caches.\n */\n proxyRevalidate?: boolean;\n /**\n * Indicates that the response can be stored but must be revalidated before each use.\n */\n immutable?: boolean;\n };\n };\n\ninterface RouteCacheEntry {\n contentType?: string;\n body: any;\n status?: number;\n lastModified: string;\n hash: string;\n}\n","import { $module } from \"alepha\";\nimport { AlephaCache } from \"alepha/cache\";\nimport { ServerCacheProvider } from \"./providers/ServerCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/ServerCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Server that provides server-side caching capabilities.\n * It uses the Alepha Cache module to cache responses from server actions ($action).\n * It also provides a ETag-based cache invalidation mechanism.\n *\n * @example\n * ```ts\n * import { Alepha } from \"alepha\";\n * import { $action } from \"alepha/server\";\n * import { AlephaServerCache } from \"alepha/server/cache\";\n *\n * class ApiServer {\n * hello = $action({\n * cache: true,\n * handler: () => \"Hello, World!\",\n * });\n * }\n *\n * const alepha = Alepha.create()\n * .with(AlephaServerCache)\n * .with(ApiServer);\n *\n * run(alepha);\n * ```\n *\n * @see {@link ServerCacheProvider}\n * @module alepha.server.cache\n */\nexport const AlephaServerCache = $module({\n name: \"alepha.server.cache\",\n services: [AlephaCache, ServerCacheProvider],\n});\n","import { createDescriptor, Descriptor, KIND } from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\n\n/**\n * Create a new static file handler.\n */\nexport const $serve = (\n options: ServeDescriptorOptions = {},\n): ServeDescriptor => {\n return createDescriptor(ServeDescriptor, options);\n};\n\nexport interface ServeDescriptorOptions {\n /**\n * Prefix for the served path.\n *\n * @default \"/\"\n */\n path?: string;\n\n /**\n * Path to the directory to serve.\n *\n * @default process.cwd()\n */\n root?: string;\n\n /**\n * If true, descriptor will be ignored.\n *\n * @default false\n */\n disabled?: boolean;\n\n /**\n * Whether to keep dot files (e.g. `.gitignore`, `.env`) in the served directory.\n *\n * @default true\n */\n ignoreDotEnvFiles?: boolean;\n\n /**\n * Whether to use the index.html file when the path is a directory.\n *\n * @default true\n */\n indexFallback?: boolean;\n\n /**\n * Force all requests \"not found\" to be served with the index.html file.\n * This is useful for single-page applications (SPAs) that use client-side only routing.\n */\n historyApiFallback?: boolean;\n\n /**\n * Optional name of the descriptor.\n * This is used for logging and debugging purposes.\n *\n * @default Key name.\n */\n name?: string;\n\n /**\n * Whether to use cache control headers.\n *\n * @default {}\n */\n cacheControl?: Partial<CacheControlOptions> | false;\n}\n\nexport interface CacheControlOptions {\n /**\n * Whether to use cache control headers.\n *\n * @default [.js, .css]\n */\n fileTypes: string[];\n\n /**\n * The maximum age of the cache in seconds.\n *\n * @default 60 * 60 * 24 * 2 // 2 days\n */\n maxAge: DurationLike;\n\n /**\n * Whether to use immutable cache control headers.\n *\n * @default true\n */\n immutable: boolean;\n}\n\nexport class ServeDescriptor extends Descriptor<ServeDescriptorOptions> {}\n\n$serve[KIND] = ServeDescriptor;\n","import { createReadStream } from \"node:fs\";\nimport { access, readdir, stat } from \"node:fs/promises\";\nimport { basename, isAbsolute, join } from \"node:path\";\nimport type { Readable as NodeStream } from \"node:stream\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { FileDetector } from \"alepha/file\";\nimport { $logger } from \"alepha/logger\";\nimport { type ServerHandler, ServerRouterProvider } from \"alepha/server\";\nimport { $serve, type ServeDescriptorOptions } from \"../descriptors/$serve.ts\";\n\nexport class ServerStaticProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly routerProvider = $inject(ServerRouterProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly fileDetector = $inject(FileDetector);\n protected readonly log = $logger();\n protected readonly directories: ServeDirectory[] = [];\n\n protected readonly configure = $hook({\n on: \"configure\",\n handler: async () => {\n await Promise.all(\n this.alepha\n .descriptors($serve)\n .map((it) => this.createStaticServer(it.options)),\n );\n },\n });\n\n public async createStaticServer(\n options: ServeDescriptorOptions,\n ): Promise<void> {\n const prefix = options.path ?? \"/\";\n\n let root = options.root ?? process.cwd();\n if (!isAbsolute(root)) {\n root = join(process.cwd(), root);\n }\n\n this.log.debug(\"Serve static files\", { prefix, root });\n\n await stat(root);\n\n // 1. get all files in the root directory (recursively)\n const files = await this.getAllFiles(root, options.ignoreDotEnvFiles);\n\n // 2. create a $route for each file (yes, this could be a lot of routes)\n const routes = await Promise.all(\n files.map(async (file) => {\n const path = file.replace(root, \"\").replace(/\\\\/g, \"/\");\n this.log.trace(`Mount ${join(prefix, path)} -> ${join(root, path)}`);\n return {\n path: join(prefix, encodeURI(path)),\n handler: await this.createFileHandler(join(root, path), options),\n };\n }),\n );\n\n for (const route of routes) {\n this.routerProvider.createRoute(route);\n\n // if route is for index.html, also create a route without it\n // e.g. /my/path/index.html -> /my/path/\n if (\n options.indexFallback !== false &&\n route.path.endsWith(\"index.html\")\n ) {\n this.routerProvider.createRoute({\n path: route.path.replace(/index\\.html$/, \"\"),\n handler: route.handler,\n });\n }\n }\n\n // 3. store the directory info for reference\n this.directories.push({\n options,\n files: files.map((file) => file.replace(root, \"\").replace(/\\\\/g, \"/\")),\n });\n\n // bonus! for SPAs, handle history API fallback\n if (options.historyApiFallback) {\n // meaning all unmatched routes should serve index.html\n this.routerProvider.createRoute({\n path: join(prefix, \"*\").replace(/\\\\/g, \"/\"),\n handler: async (request) => {\n const { reply } = request;\n\n if (request.url.pathname.includes(\".\")) {\n // If the request is for a file (e.g., /style.css), do not fall back\n reply.headers[\"content-type\"] = \"text/plain\";\n reply.body = \"Not Found\";\n reply.status = 404;\n return;\n }\n\n reply.headers[\"content-type\"] = \"text/html\";\n reply.status = 200;\n\n // Serve index.html for all unmatched routes\n return createReadStream(join(root, \"index.html\"));\n },\n });\n }\n }\n\n public async createFileHandler(\n filepath: string,\n options: ServeDescriptorOptions,\n ): Promise<ServerHandler> {\n const filename = basename(filepath);\n\n const hasGzip = await access(`${filepath}.gz`)\n .then(() => true)\n .catch(() => false);\n\n const hasBr = await access(`${filepath}.br`)\n .then(() => true)\n .catch(() => false);\n\n const fileStat = await stat(filepath);\n const lastModified = fileStat.mtime.toUTCString();\n const etag = `\"${fileStat.size}-${fileStat.mtime.getTime()}\"`;\n const contentType = this.fileDetector.getContentType(filename);\n const cacheControl = this.getCacheControl(filename, options);\n\n return async (request): Promise<NodeStream | undefined> => {\n const { headers, reply } = request;\n let path = filepath;\n\n const encoding = headers[\"accept-encoding\"];\n if (encoding) {\n if (hasBr && encoding.includes(\"br\")) {\n reply.headers[\"content-encoding\"] = \"br\";\n path += \".br\";\n } else if (hasGzip && encoding.includes(\"gzip\")) {\n reply.headers[\"content-encoding\"] = \"gzip\";\n path += \".gz\";\n }\n }\n\n reply.headers[\"content-type\"] = contentType;\n reply.headers[\"accept-ranges\"] = \"bytes\";\n reply.headers[\"last-modified\"] = lastModified;\n\n if (cacheControl) {\n reply.headers[\"cache-control\"] =\n `public, max-age=${cacheControl.maxAge}`;\n if (cacheControl.immutable) {\n reply.headers[\"cache-control\"] += \", immutable\";\n }\n }\n\n reply.headers.etag = etag;\n if (\n headers[\"if-none-match\"] === etag ||\n headers[\"if-modified-since\"] === lastModified\n ) {\n reply.status = 304;\n return;\n }\n\n return createReadStream(path);\n };\n }\n\n protected getCacheFileTypes(): string[] {\n return [\n \".js\",\n \".css\",\n \".woff\",\n \".woff2\",\n \".ttf\",\n \".eot\",\n \".otf\",\n \".jpg\",\n \".jpeg\",\n \".png\",\n \".svg\",\n \".gif\",\n ];\n }\n\n protected getCacheControl(\n filename: string,\n options: ServeDescriptorOptions,\n ): { maxAge: number; immutable: boolean } | undefined {\n if (!options.cacheControl) {\n return;\n }\n\n const fileTypes =\n options.cacheControl.fileTypes ?? this.getCacheFileTypes();\n\n for (const type of fileTypes) {\n if (filename.endsWith(type)) {\n return {\n immutable: options.cacheControl.immutable ?? true,\n maxAge: this.dateTimeProvider\n .duration(options.cacheControl.maxAge ?? [30, \"days\"])\n .as(\"seconds\"),\n };\n }\n }\n }\n\n public async getAllFiles(\n dir: string,\n ignoreDotEnvFiles = true,\n ): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n\n const files = await Promise.all(\n entries.map((dirent) => {\n // skip .env & other dot files\n if (ignoreDotEnvFiles && dirent.name.startsWith(\".\")) {\n return [];\n }\n\n const fullPath = join(dir, dirent.name);\n return dirent.isDirectory() ? this.getAllFiles(fullPath) : fullPath;\n }),\n );\n\n return files.flat();\n }\n}\n\nexport interface ServeDirectory {\n options: ServeDescriptorOptions;\n files: string[];\n}\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { $serve } from \"./descriptors/$serve.ts\";\nimport { ServerStaticProvider } from \"./providers/ServerStaticProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./descriptors/$serve.ts\";\nexport * from \"./providers/ServerStaticProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Create static file server with `$static()`.\n *\n * @see {@link ServerStaticProvider}\n * @module alepha.server.static\n */\nexport const AlephaServerStatic = $module({\n name: \"alepha.server.static\",\n descriptors: [$serve],\n services: [AlephaServer, ServerStaticProvider],\n});\n","import { createDescriptor, Descriptor, KIND } from \"alepha\";\n\n/**\n * Creates an OpenAPI/Swagger documentation descriptor with interactive UI.\n *\n * Automatically generates API documentation from your $action descriptors and serves\n * an interactive Swagger UI for testing endpoints. Supports customization, tag filtering,\n * and OAuth configuration.\n *\n * @example\n * ```ts\n * class App {\n * docs = $swagger({\n * prefix: \"/api-docs\",\n * info: {\n * title: \"My API\",\n * version: \"1.0.0\",\n * description: \"REST API documentation\"\n * },\n * excludeTags: [\"internal\"],\n * ui: { root: \"/swagger\" }\n * });\n * }\n * ```\n */\nexport const $swagger = (\n options: SwaggerDescriptorOptions = {},\n): SwaggerDescriptor => {\n return createDescriptor(SwaggerDescriptor, options);\n};\n\nexport interface SwaggerDescriptorOptions {\n info?: OpenApiDocument[\"info\"];\n\n /**\n * @default: \"/docs\"\n */\n prefix?: string;\n\n /**\n * If true, docs will be disabled.\n */\n disabled?: boolean;\n\n /**\n * Tags to exclude from the documentation.\n */\n excludeTags?: string[];\n\n /**\n * Enable Swagger UI.\n *\n * @default true\n */\n ui?: boolean | SwaggerUiOptions;\n\n /**\n * Function to rewrite the OpenAPI document before serving it.\n */\n rewrite?: (doc: OpenApiDocument) => void;\n}\n\nexport interface SwaggerUiOptions {\n root?: string;\n\n initOAuth?: {\n /**\n * Default clientId.\n */\n clientId?: string;\n\n /**\n * realm query parameter (for oauth1) added to authorizationUrl and tokenUrl.\n */\n realm?: string;\n\n /**\n * application name, displayed in authorization popup.\n */\n appName?: string;\n\n /**\n * scope separator for passing scopes, encoded before calling, default\n * value is a space (encoded value %20).\n *\n * @default ' '\n */\n scopeSeparator?: string;\n\n /**\n * string array or scope separator (i.e. space) separated string of\n * initially selected oauth scopes\n *\n * @default []\n */\n scopes?: string | string[];\n\n /**\n * Additional query parameters added to authorizationUrl and tokenUrl.\n * MUST be an object\n */\n additionalQueryStringParams?: { [key: string]: any };\n\n /**\n * Only activated for the accessCode flow. During the authorization_code\n * request to the tokenUrl, pass the Client Password using the HTTP Basic\n * Authentication scheme (Authorization header with Basic\n * base64encode(client_id + client_secret)).\n *\n * @default false\n */\n useBasicAuthenticationWithAccessCodeGrant?: boolean;\n\n /**\n * Only applies to Authorization Code flows. Proof Key for Code Exchange\n * brings enhanced security for OAuth public clients.\n *\n * @default false\n */\n usePkceWithAuthorizationCodeGrant?: boolean;\n };\n}\n\nexport class SwaggerDescriptor extends Descriptor<SwaggerDescriptorOptions> {}\n\n$swagger[KIND] = SwaggerDescriptor;\n\nexport interface OpenApiDocument {\n openapi: string;\n info: {\n title: string;\n version: string;\n description?: string;\n };\n paths: Record<string, any>;\n components?: {\n schemas?: Record<string, any>;\n securitySchemes?: Record<string, any>;\n };\n}\n\nexport interface OpenApiOperation {\n tags?: string[];\n summary?: string;\n description?: string;\n operationId?: string;\n parameters?: Array<{\n name: string;\n in: \"query\" | \"header\" | \"path\" | \"cookie\";\n description?: string;\n required?: boolean;\n schema: any;\n }>;\n requestBody?: {\n description?: string;\n content: Record<\n string,\n {\n schema: any;\n }\n >;\n required?: boolean;\n };\n responses: Record<\n string,\n {\n description: string;\n content?: Record<\n string,\n {\n schema: any;\n }\n >;\n }\n >;\n security?: Array<Record<string, any[]>>;\n}\n","import { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n $atom,\n $hook,\n $inject,\n $use,\n Alepha,\n isTypeFile,\n type Static,\n type TObject,\n type TSchema,\n t,\n} from \"alepha\";\nimport { FileSystemProvider } from \"alepha/file\";\nimport { $logger } from \"alepha/logger\";\nimport { AlephaSecurity } from \"alepha/security\";\nimport {\n $action,\n type ActionDescriptor,\n type RequestConfigSchema,\n ServerProvider,\n ServerRouterProvider,\n} from \"alepha/server\";\nimport { ServerStaticProvider } from \"alepha/server/static\";\nimport {\n $swagger,\n type OpenApiDocument,\n type OpenApiOperation,\n type SwaggerDescriptorOptions,\n} from \"../descriptors/$swagger.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Swagger provider configuration atom\n */\nexport const swaggerOptions = $atom({\n name: \"alepha.server.swagger.options\",\n schema: t.object({\n excludeKeys: t.optional(\n t.array(t.string(), {\n description: \"Keys to exclude from swagger schema\",\n }),\n ),\n }),\n default: {\n excludeKeys: [],\n },\n});\n\nexport type ServerSwaggerProviderOptions = Static<typeof swaggerOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [swaggerOptions.key]: ServerSwaggerProviderOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerSwaggerProvider {\n protected readonly serverStaticProvider = $inject(ServerStaticProvider);\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n protected readonly serverProvider = $inject(ServerProvider);\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly options = $use(swaggerOptions);\n protected readonly fs = $inject(FileSystemProvider);\n\n public json?: OpenApiDocument;\n\n protected readonly configure = $hook({\n on: \"configure\",\n priority: \"last\", // wait for all configurations, sometimes some actions are registered late!\n handler: async (alepha) => {\n const options = alepha.descriptors($swagger)?.[0]?.options;\n if (!options) {\n return;\n }\n\n this.json = await this.createSwagger(options);\n },\n });\n\n public async createSwagger(\n options: SwaggerDescriptorOptions,\n ): Promise<OpenApiDocument | undefined> {\n if (options.disabled) {\n return;\n }\n\n const json = this.configureOpenApi(\n this.alepha.descriptors($action),\n options,\n );\n\n if (options.rewrite) {\n options.rewrite(json);\n }\n\n const prefix = options.prefix ?? \"/docs\";\n\n this.configureSwaggerApi(prefix, json);\n\n if (options.ui !== false) {\n await this.configureSwaggerUi(prefix, options);\n }\n\n return json;\n }\n\n protected configureOpenApi(\n actions: ActionDescriptor<RequestConfigSchema>[],\n doc: SwaggerDescriptorOptions,\n ): OpenApiDocument {\n const openApi: OpenApiDocument = {\n openapi: \"3.0.0\",\n info: doc.info ?? {\n title: \"API Documentation\",\n version: \"1.0.0\",\n },\n paths: {},\n components: {},\n };\n\n const hasSecurity = this.alepha.has(AlephaSecurity);\n if (hasSecurity && openApi.components) {\n openApi.components.securitySchemes = {\n bearerAuth: {\n type: \"http\",\n scheme: \"bearer\",\n bearerFormat: \"JWT\",\n },\n };\n }\n\n const excludeTags = doc.excludeTags ?? [];\n const schemas: Record<string, any> = {};\n\n const schema = (source: TSchema) => {\n if (\"title\" in source && typeof source.title === \"string\") {\n schemas[source.title] = copy(source);\n return { $ref: `#/components/schemas/${source.title}` };\n }\n return copy(source);\n };\n\n const copy = (obj: any) => {\n const newValue = JSON.parse(JSON.stringify(obj));\n this.removePrivateFields(newValue, [\n ...(this.options.excludeKeys || []),\n \"~options\",\n ]);\n return newValue;\n };\n\n for (const route of actions) {\n if (!route.options.schema) {\n continue;\n }\n\n const response = this.getResponseSchema(route);\n if (!response) {\n continue;\n }\n\n if (excludeTags.includes(route.group)) {\n continue;\n }\n\n if (route.options.hide) {\n continue;\n }\n\n const operation: OpenApiOperation = {\n operationId: route.name,\n summary: route.options.summary,\n description: route.options.description,\n tags: [route.group.replaceAll(\":\", \" / \")],\n responses: {\n [response.status]: {\n description: \"\",\n content: response.type\n ? {\n [response.type]: {\n schema: schema(response.schema),\n },\n }\n : undefined,\n },\n },\n };\n\n if (route.options.secure !== false && hasSecurity) {\n operation.security = [{ bearerAuth: [] }];\n }\n\n const g = t.raw;\n\n if (\n g.IsObject(route.options.schema.body) ||\n g.IsArray(route.options.schema.body)\n ) {\n if (\n g.IsObject(route.options.schema.body) &&\n this.isBodyMultipart(route.options.schema.body)\n ) {\n operation.requestBody = {\n required: true,\n content: {\n \"multipart/form-data\": {\n schema: schema(route.options.schema.body),\n },\n },\n };\n } else {\n operation.requestBody = {\n required: true,\n content: {\n \"application/json\": {\n schema: schema(route.options.schema.body),\n },\n },\n };\n }\n }\n\n if (g.IsObject(route.options.schema.query)) {\n operation.parameters ??= [];\n for (const [key, value] of Object.entries(\n route.options.schema.query.properties,\n )) {\n operation.parameters.push({\n name: key,\n in: \"query\",\n required: false,\n schema: schema(value),\n });\n }\n }\n\n if (g.IsObject(route.options.schema.params)) {\n operation.parameters ??= [];\n for (const [key, value] of Object.entries(\n route.options.schema.params.properties,\n )) {\n const description =\n \"description\" in value && typeof value.description === \"string\"\n ? value.description\n : undefined;\n const ref = schema(value);\n delete ref.description;\n operation.parameters.push({\n name: key,\n in: \"path\",\n required: true,\n description,\n schema: ref,\n });\n }\n }\n\n const url = route.prefix + this.replacePathParams(route.path);\n\n openApi.paths[url] = {\n ...openApi.paths[url],\n [route.method.toLowerCase()]: operation,\n };\n }\n\n if (openApi.components) openApi.components.schemas = schemas;\n\n return JSON.parse(JSON.stringify(openApi));\n }\n\n public isBodyMultipart(schema: TObject): boolean {\n for (const key in schema.properties) {\n if (isTypeFile(schema.properties[key])) {\n return true;\n }\n }\n return false;\n }\n\n public replacePathParams(url: string): string {\n return url.replace(/:\\w+/g, (match) => {\n const paramName = match.slice(1);\n return `{${paramName}}`;\n });\n }\n\n public getResponseSchema(route: ActionDescriptor<RequestConfigSchema>):\n | {\n type?: string;\n schema?: any;\n status: number;\n }\n | undefined {\n const schema: any = route.options.schema?.response;\n if (!schema) {\n return {\n status: 204,\n };\n }\n\n if (t.schema.isObject(schema) || t.schema.isArray(schema)) {\n return {\n schema,\n status: 200,\n type: \"application/json\",\n };\n }\n\n if (t.schema.isString(schema)) {\n return {\n schema,\n status: 200,\n type: \"text/plain\",\n };\n }\n\n if (isTypeFile(schema)) {\n return {\n schema,\n status: 200,\n type: \"application/octet-stream\",\n };\n }\n\n const status = Object.keys(schema)[0];\n if (t.schema.isObject(schema[status]) || isTypeFile(schema[status])) {\n return {\n schema,\n type: t.schema.isObject(schema[status])\n ? \"application/json\"\n : \"application/octet-stream\",\n status: Number(status),\n };\n }\n }\n\n protected configureSwaggerApi(prefix: string, json: OpenApiDocument): void {\n this.serverRouterProvider.createRoute({\n method: \"GET\",\n path: `${prefix}/json`,\n cache: {\n etag: true,\n },\n schema: {\n response: t.json(),\n },\n handler: () => json,\n });\n this.log.info(`Swagger API available at ${prefix}/json`);\n }\n\n protected async configureSwaggerUi(\n prefix: string,\n options: SwaggerDescriptorOptions,\n ): Promise<void> {\n const ui = typeof options.ui === \"object\" ? options.ui : {};\n const initializer = `\nwindow.onload = function() {\n\twindow.ui = SwaggerUIBundle({\n\t\turl: \"/docs/json\",\n\t\tdom_id: '#swagger-ui',\n\t\tdeepLinking: true,\n\t\tpresets: [\n\t\t\tSwaggerUIBundle.presets.apis,\n\t\t\tSwaggerUIStandalonePreset\n\t\t],\n\t\tplugins: [\n\t\t\tSwaggerUIBundle.plugins.DownloadUrl\n\t\t],\n\t\tlayout: \"BaseLayout\"\n\t});\n\n document.body.style.backgroundColor = \"#f2f2f2\";\n\n\tconst options = ${JSON.stringify(ui)};\n\n\tif (options.initOAuth) {\n\t\tui.initOAuth(options.initOAuth);\n\t}\n};\n\t\t`.trim();\n\n const dirname = fileURLToPath(import.meta.url);\n\n const root = await this.getAssetPath(\n ui.root,\n join(dirname, \"../../assets/swagger-ui\"),\n join(dirname, \"../../../../assets/swagger-ui\"),\n );\n\n if (!root) {\n this.log.warn(`Failed to locate Swagger UI assets for path ${prefix}`);\n return;\n }\n\n await this.serverStaticProvider.createStaticServer({\n path: prefix,\n root,\n });\n\n this.serverRouterProvider.createRoute({\n method: \"GET\",\n path: `${prefix}/swagger-initializer.js`,\n cache: {\n etag: true,\n },\n handler: ({ reply }) => {\n reply.headers[\"content-type\"] = \"application/javascript; charset=utf-8\";\n return initializer;\n },\n });\n\n this.log.info(\n `Swagger UI available at ${this.serverProvider.hostname}${prefix}/`,\n );\n }\n\n protected async getAssetPath(\n ...paths: (string | undefined)[]\n ): Promise<string | undefined> {\n for (const path of paths) {\n if (!path) continue;\n const exists = await this.fs.exists(path);\n if (exists) {\n return path;\n }\n }\n }\n\n public removePrivateFields<T extends Record<string, any>>(\n obj: T,\n excludeList: string[],\n ): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n\n const visited = new WeakSet();\n\n const traverse = (o: any): void => {\n if (visited.has(o)) return;\n visited.add(o);\n\n if (Array.isArray(o)) {\n for (let i = 0; i < o.length; i++) {\n const item = o[i];\n if (item !== null && typeof item === \"object\") {\n traverse(item);\n }\n }\n } else {\n for (const excludeKey of excludeList) {\n if (excludeKey in o) {\n delete o[excludeKey];\n }\n }\n for (const key in o) {\n const item = o[key];\n if (item !== null && typeof item === \"object\") {\n traverse(item);\n }\n }\n }\n };\n\n traverse(obj);\n return obj;\n }\n}\n","import \"alepha/server/security\";\nimport { $module } from \"alepha\";\nimport { AlephaServer, type RequestConfigSchema } from \"alepha/server\";\nimport { AlephaServerCache } from \"alepha/server/cache\";\nimport { AlephaServerStatic } from \"alepha/server/static\";\nimport { $swagger } from \"./descriptors/$swagger.ts\";\nimport { ServerSwaggerProvider } from \"./providers/ServerSwaggerProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./descriptors/$swagger.ts\";\nexport * from \"./providers/ServerSwaggerProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/server\" {\n interface ActionDescriptorOptions<TConfig extends RequestConfigSchema> {\n /**\n * Short description of the route.\n */\n summary?: string;\n\n /**\n * Don't include this action in the Swagger documentation.\n */\n hide?: boolean;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Server that provides Swagger documentation capabilities.\n * It generates OpenAPI v3 documentation for the server's endpoints ($action).\n * It also provides a Swagger UI for interactive API documentation.\n *\n * @see {@link ServerSwaggerProvider}\n * @module alepha.server.swagger\n */\nexport const AlephaServerSwagger = $module({\n name: \"alepha.server.swagger\",\n descriptors: [$swagger],\n services: [ServerSwaggerProvider],\n register: (alepha) => {\n alepha.with(AlephaServer);\n alepha.with(AlephaServerCache);\n alepha.with(AlephaServerStatic);\n alepha.with(ServerSwaggerProvider);\n alepha.state.push(\"alepha.build.assets\", \"alepha\");\n },\n});\n"],"mappings":"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"}
|
|
@@ -1,382 +0,0 @@
|
|
|
1
|
-
import * as alepha17 from "alepha";
|
|
2
|
-
import { Alepha, Descriptor, KIND, Static, TObject } from "alepha";
|
|
3
|
-
import { UserAccount, UserAccountToken } from "alepha/security";
|
|
4
|
-
import { ActionDescriptor, FetchOptions, RequestConfigSchema, ServerHandler, ServerProvider, ServerRouterProvider } from "alepha/server";
|
|
5
|
-
import * as alepha_logger2 from "alepha/logger";
|
|
6
|
-
import { FileDetector, FileSystemProvider } from "alepha/file";
|
|
7
|
-
import { DateTimeProvider, DurationLike } from "alepha/datetime";
|
|
8
|
-
|
|
9
|
-
//#region src/server-security/providers/ServerBasicAuthProvider.d.ts
|
|
10
|
-
interface BasicAuthOptions {
|
|
11
|
-
username: string;
|
|
12
|
-
password: string;
|
|
13
|
-
}
|
|
14
|
-
//#endregion
|
|
15
|
-
//#region src/server-security/providers/ServerSecurityProvider.d.ts
|
|
16
|
-
|
|
17
|
-
type ServerRouteSecure = {
|
|
18
|
-
realm?: string;
|
|
19
|
-
basic?: BasicAuthOptions;
|
|
20
|
-
};
|
|
21
|
-
//#endregion
|
|
22
|
-
//#region src/server-security/index.d.ts
|
|
23
|
-
declare module "alepha" {
|
|
24
|
-
interface State {
|
|
25
|
-
/**
|
|
26
|
-
* Real (or fake) user account, used for internal actions.
|
|
27
|
-
*
|
|
28
|
-
* If you define this, you assume that all actions are executed by this user by default.
|
|
29
|
-
* > To force a different user, you need to pass it explicitly in the options.
|
|
30
|
-
*/
|
|
31
|
-
"alepha.server.security.system.user"?: UserAccountToken;
|
|
32
|
-
/**
|
|
33
|
-
* The authenticated user account attached to the server request state.
|
|
34
|
-
*
|
|
35
|
-
* @internal
|
|
36
|
-
*/
|
|
37
|
-
"alepha.server.request.user"?: UserAccount;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
declare module "alepha/server" {
|
|
41
|
-
interface ServerRequest<TConfig> {
|
|
42
|
-
user?: UserAccountToken;
|
|
43
|
-
}
|
|
44
|
-
interface ServerActionRequest<TConfig> {
|
|
45
|
-
user: UserAccountToken;
|
|
46
|
-
}
|
|
47
|
-
interface ServerRoute {
|
|
48
|
-
/**
|
|
49
|
-
* If true, the route will be protected by the security provider.
|
|
50
|
-
* All actions are secure by default, but you can disable it for specific actions.
|
|
51
|
-
*/
|
|
52
|
-
secure?: boolean | ServerRouteSecure;
|
|
53
|
-
}
|
|
54
|
-
interface ClientRequestOptions extends FetchOptions {
|
|
55
|
-
/**
|
|
56
|
-
* Forward user from the previous request.
|
|
57
|
-
* If "system", use system user. @see {ServerSecurityProvider.localSystemUser}
|
|
58
|
-
* If "context", use the user from the current context (e.g. request).
|
|
59
|
-
*
|
|
60
|
-
* @default "system" if provided, else "context" if available.
|
|
61
|
-
*/
|
|
62
|
-
user?: UserAccountToken | "system" | "context";
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Plugin for Alepha Server that provides security features. Based on the Alepha Security module.
|
|
67
|
-
*
|
|
68
|
-
* By default, all $action will be guarded by a permission check.
|
|
69
|
-
*
|
|
70
|
-
* @see {@link ServerSecurityProvider}
|
|
71
|
-
* @module alepha.server.security
|
|
72
|
-
*/
|
|
73
|
-
//#endregion
|
|
74
|
-
//#region src/server-swagger/descriptors/$swagger.d.ts
|
|
75
|
-
/**
|
|
76
|
-
* Creates an OpenAPI/Swagger documentation descriptor with interactive UI.
|
|
77
|
-
*
|
|
78
|
-
* Automatically generates API documentation from your $action descriptors and serves
|
|
79
|
-
* an interactive Swagger UI for testing endpoints. Supports customization, tag filtering,
|
|
80
|
-
* and OAuth configuration.
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* ```ts
|
|
84
|
-
* class App {
|
|
85
|
-
* docs = $swagger({
|
|
86
|
-
* prefix: "/api-docs",
|
|
87
|
-
* info: {
|
|
88
|
-
* title: "My API",
|
|
89
|
-
* version: "1.0.0",
|
|
90
|
-
* description: "REST API documentation"
|
|
91
|
-
* },
|
|
92
|
-
* excludeTags: ["internal"],
|
|
93
|
-
* ui: { root: "/swagger" }
|
|
94
|
-
* });
|
|
95
|
-
* }
|
|
96
|
-
* ```
|
|
97
|
-
*/
|
|
98
|
-
declare const $swagger: {
|
|
99
|
-
(options?: SwaggerDescriptorOptions): SwaggerDescriptor;
|
|
100
|
-
[KIND]: typeof SwaggerDescriptor;
|
|
101
|
-
};
|
|
102
|
-
interface SwaggerDescriptorOptions {
|
|
103
|
-
info?: OpenApiDocument["info"];
|
|
104
|
-
/**
|
|
105
|
-
* @default: "/docs"
|
|
106
|
-
*/
|
|
107
|
-
prefix?: string;
|
|
108
|
-
/**
|
|
109
|
-
* If true, docs will be disabled.
|
|
110
|
-
*/
|
|
111
|
-
disabled?: boolean;
|
|
112
|
-
/**
|
|
113
|
-
* Tags to exclude from the documentation.
|
|
114
|
-
*/
|
|
115
|
-
excludeTags?: string[];
|
|
116
|
-
/**
|
|
117
|
-
* Enable Swagger UI.
|
|
118
|
-
*
|
|
119
|
-
* @default true
|
|
120
|
-
*/
|
|
121
|
-
ui?: boolean | SwaggerUiOptions;
|
|
122
|
-
/**
|
|
123
|
-
* Function to rewrite the OpenAPI document before serving it.
|
|
124
|
-
*/
|
|
125
|
-
rewrite?: (doc: OpenApiDocument) => void;
|
|
126
|
-
}
|
|
127
|
-
interface SwaggerUiOptions {
|
|
128
|
-
root?: string;
|
|
129
|
-
initOAuth?: {
|
|
130
|
-
/**
|
|
131
|
-
* Default clientId.
|
|
132
|
-
*/
|
|
133
|
-
clientId?: string;
|
|
134
|
-
/**
|
|
135
|
-
* realm query parameter (for oauth1) added to authorizationUrl and tokenUrl.
|
|
136
|
-
*/
|
|
137
|
-
realm?: string;
|
|
138
|
-
/**
|
|
139
|
-
* application name, displayed in authorization popup.
|
|
140
|
-
*/
|
|
141
|
-
appName?: string;
|
|
142
|
-
/**
|
|
143
|
-
* scope separator for passing scopes, encoded before calling, default
|
|
144
|
-
* value is a space (encoded value %20).
|
|
145
|
-
*
|
|
146
|
-
* @default ' '
|
|
147
|
-
*/
|
|
148
|
-
scopeSeparator?: string;
|
|
149
|
-
/**
|
|
150
|
-
* string array or scope separator (i.e. space) separated string of
|
|
151
|
-
* initially selected oauth scopes
|
|
152
|
-
*
|
|
153
|
-
* @default []
|
|
154
|
-
*/
|
|
155
|
-
scopes?: string | string[];
|
|
156
|
-
/**
|
|
157
|
-
* Additional query parameters added to authorizationUrl and tokenUrl.
|
|
158
|
-
* MUST be an object
|
|
159
|
-
*/
|
|
160
|
-
additionalQueryStringParams?: {
|
|
161
|
-
[key: string]: any;
|
|
162
|
-
};
|
|
163
|
-
/**
|
|
164
|
-
* Only activated for the accessCode flow. During the authorization_code
|
|
165
|
-
* request to the tokenUrl, pass the Client Password using the HTTP Basic
|
|
166
|
-
* Authentication scheme (Authorization header with Basic
|
|
167
|
-
* base64encode(client_id + client_secret)).
|
|
168
|
-
*
|
|
169
|
-
* @default false
|
|
170
|
-
*/
|
|
171
|
-
useBasicAuthenticationWithAccessCodeGrant?: boolean;
|
|
172
|
-
/**
|
|
173
|
-
* Only applies to Authorization Code flows. Proof Key for Code Exchange
|
|
174
|
-
* brings enhanced security for OAuth public clients.
|
|
175
|
-
*
|
|
176
|
-
* @default false
|
|
177
|
-
*/
|
|
178
|
-
usePkceWithAuthorizationCodeGrant?: boolean;
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
declare class SwaggerDescriptor extends Descriptor<SwaggerDescriptorOptions> {}
|
|
182
|
-
interface OpenApiDocument {
|
|
183
|
-
openapi: string;
|
|
184
|
-
info: {
|
|
185
|
-
title: string;
|
|
186
|
-
version: string;
|
|
187
|
-
description?: string;
|
|
188
|
-
};
|
|
189
|
-
paths: Record<string, any>;
|
|
190
|
-
components?: {
|
|
191
|
-
schemas?: Record<string, any>;
|
|
192
|
-
securitySchemes?: Record<string, any>;
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
interface OpenApiOperation {
|
|
196
|
-
tags?: string[];
|
|
197
|
-
summary?: string;
|
|
198
|
-
description?: string;
|
|
199
|
-
operationId?: string;
|
|
200
|
-
parameters?: Array<{
|
|
201
|
-
name: string;
|
|
202
|
-
in: "query" | "header" | "path" | "cookie";
|
|
203
|
-
description?: string;
|
|
204
|
-
required?: boolean;
|
|
205
|
-
schema: any;
|
|
206
|
-
}>;
|
|
207
|
-
requestBody?: {
|
|
208
|
-
description?: string;
|
|
209
|
-
content: Record<string, {
|
|
210
|
-
schema: any;
|
|
211
|
-
}>;
|
|
212
|
-
required?: boolean;
|
|
213
|
-
};
|
|
214
|
-
responses: Record<string, {
|
|
215
|
-
description: string;
|
|
216
|
-
content?: Record<string, {
|
|
217
|
-
schema: any;
|
|
218
|
-
}>;
|
|
219
|
-
}>;
|
|
220
|
-
security?: Array<Record<string, any[]>>;
|
|
221
|
-
}
|
|
222
|
-
//#endregion
|
|
223
|
-
//#region src/server-static/descriptors/$serve.d.ts
|
|
224
|
-
interface ServeDescriptorOptions {
|
|
225
|
-
/**
|
|
226
|
-
* Prefix for the served path.
|
|
227
|
-
*
|
|
228
|
-
* @default "/"
|
|
229
|
-
*/
|
|
230
|
-
path?: string;
|
|
231
|
-
/**
|
|
232
|
-
* Path to the directory to serve.
|
|
233
|
-
*
|
|
234
|
-
* @default process.cwd()
|
|
235
|
-
*/
|
|
236
|
-
root?: string;
|
|
237
|
-
/**
|
|
238
|
-
* If true, descriptor will be ignored.
|
|
239
|
-
*
|
|
240
|
-
* @default false
|
|
241
|
-
*/
|
|
242
|
-
disabled?: boolean;
|
|
243
|
-
/**
|
|
244
|
-
* Whether to keep dot files (e.g. `.gitignore`, `.env`) in the served directory.
|
|
245
|
-
*
|
|
246
|
-
* @default true
|
|
247
|
-
*/
|
|
248
|
-
ignoreDotEnvFiles?: boolean;
|
|
249
|
-
/**
|
|
250
|
-
* Whether to use the index.html file when the path is a directory.
|
|
251
|
-
*
|
|
252
|
-
* @default true
|
|
253
|
-
*/
|
|
254
|
-
indexFallback?: boolean;
|
|
255
|
-
/**
|
|
256
|
-
* Force all requests "not found" to be served with the index.html file.
|
|
257
|
-
* This is useful for single-page applications (SPAs) that use client-side only routing.
|
|
258
|
-
*/
|
|
259
|
-
historyApiFallback?: boolean;
|
|
260
|
-
/**
|
|
261
|
-
* Optional name of the descriptor.
|
|
262
|
-
* This is used for logging and debugging purposes.
|
|
263
|
-
*
|
|
264
|
-
* @default Key name.
|
|
265
|
-
*/
|
|
266
|
-
name?: string;
|
|
267
|
-
/**
|
|
268
|
-
* Whether to use cache control headers.
|
|
269
|
-
*
|
|
270
|
-
* @default {}
|
|
271
|
-
*/
|
|
272
|
-
cacheControl?: Partial<CacheControlOptions> | false;
|
|
273
|
-
}
|
|
274
|
-
interface CacheControlOptions {
|
|
275
|
-
/**
|
|
276
|
-
* Whether to use cache control headers.
|
|
277
|
-
*
|
|
278
|
-
* @default [.js, .css]
|
|
279
|
-
*/
|
|
280
|
-
fileTypes: string[];
|
|
281
|
-
/**
|
|
282
|
-
* The maximum age of the cache in seconds.
|
|
283
|
-
*
|
|
284
|
-
* @default 60 * 60 * 24 * 2 // 2 days
|
|
285
|
-
*/
|
|
286
|
-
maxAge: DurationLike;
|
|
287
|
-
/**
|
|
288
|
-
* Whether to use immutable cache control headers.
|
|
289
|
-
*
|
|
290
|
-
* @default true
|
|
291
|
-
*/
|
|
292
|
-
immutable: boolean;
|
|
293
|
-
}
|
|
294
|
-
//#endregion
|
|
295
|
-
//#region src/server-static/providers/ServerStaticProvider.d.ts
|
|
296
|
-
declare class ServerStaticProvider {
|
|
297
|
-
protected readonly alepha: Alepha;
|
|
298
|
-
protected readonly routerProvider: ServerRouterProvider;
|
|
299
|
-
protected readonly dateTimeProvider: DateTimeProvider;
|
|
300
|
-
protected readonly fileDetector: FileDetector;
|
|
301
|
-
protected readonly log: alepha_logger2.Logger;
|
|
302
|
-
protected readonly directories: ServeDirectory[];
|
|
303
|
-
protected readonly configure: alepha17.HookDescriptor<"configure">;
|
|
304
|
-
createStaticServer(options: ServeDescriptorOptions): Promise<void>;
|
|
305
|
-
createFileHandler(filepath: string, options: ServeDescriptorOptions): Promise<ServerHandler>;
|
|
306
|
-
protected getCacheFileTypes(): string[];
|
|
307
|
-
protected getCacheControl(filename: string, options: ServeDescriptorOptions): {
|
|
308
|
-
maxAge: number;
|
|
309
|
-
immutable: boolean;
|
|
310
|
-
} | undefined;
|
|
311
|
-
getAllFiles(dir: string, ignoreDotEnvFiles?: boolean): Promise<string[]>;
|
|
312
|
-
}
|
|
313
|
-
interface ServeDirectory {
|
|
314
|
-
options: ServeDescriptorOptions;
|
|
315
|
-
files: string[];
|
|
316
|
-
}
|
|
317
|
-
//#endregion
|
|
318
|
-
//#region src/server-swagger/providers/ServerSwaggerProvider.d.ts
|
|
319
|
-
/**
|
|
320
|
-
* Swagger provider configuration atom
|
|
321
|
-
*/
|
|
322
|
-
declare const swaggerOptions: alepha17.Atom<TObject<{
|
|
323
|
-
excludeKeys: alepha17.TOptional<alepha17.TArray<alepha17.TString>>;
|
|
324
|
-
}>, "alepha.server.swagger.options">;
|
|
325
|
-
type ServerSwaggerProviderOptions = Static<typeof swaggerOptions.schema>;
|
|
326
|
-
declare module "alepha" {
|
|
327
|
-
interface State {
|
|
328
|
-
[swaggerOptions.key]: ServerSwaggerProviderOptions;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
declare class ServerSwaggerProvider {
|
|
332
|
-
protected readonly serverStaticProvider: ServerStaticProvider;
|
|
333
|
-
protected readonly serverRouterProvider: ServerRouterProvider;
|
|
334
|
-
protected readonly serverProvider: ServerProvider;
|
|
335
|
-
protected readonly alepha: Alepha;
|
|
336
|
-
protected readonly log: alepha_logger2.Logger;
|
|
337
|
-
protected readonly options: Readonly<{
|
|
338
|
-
excludeKeys?: string[] | undefined;
|
|
339
|
-
}>;
|
|
340
|
-
protected readonly fs: FileSystemProvider;
|
|
341
|
-
json?: OpenApiDocument;
|
|
342
|
-
protected readonly configure: alepha17.HookDescriptor<"configure">;
|
|
343
|
-
createSwagger(options: SwaggerDescriptorOptions): Promise<OpenApiDocument | undefined>;
|
|
344
|
-
protected configureOpenApi(actions: ActionDescriptor<RequestConfigSchema>[], doc: SwaggerDescriptorOptions): OpenApiDocument;
|
|
345
|
-
isBodyMultipart(schema: TObject): boolean;
|
|
346
|
-
replacePathParams(url: string): string;
|
|
347
|
-
getResponseSchema(route: ActionDescriptor<RequestConfigSchema>): {
|
|
348
|
-
type?: string;
|
|
349
|
-
schema?: any;
|
|
350
|
-
status: number;
|
|
351
|
-
} | undefined;
|
|
352
|
-
protected configureSwaggerApi(prefix: string, json: OpenApiDocument): void;
|
|
353
|
-
protected configureSwaggerUi(prefix: string, options: SwaggerDescriptorOptions): Promise<void>;
|
|
354
|
-
protected getAssetPath(...paths: (string | undefined)[]): Promise<string | undefined>;
|
|
355
|
-
removePrivateFields<T extends Record<string, any>>(obj: T, excludeList: string[]): T;
|
|
356
|
-
}
|
|
357
|
-
//#endregion
|
|
358
|
-
//#region src/server-swagger/index.d.ts
|
|
359
|
-
declare module "alepha/server" {
|
|
360
|
-
interface ActionDescriptorOptions<TConfig extends RequestConfigSchema> {
|
|
361
|
-
/**
|
|
362
|
-
* Short description of the route.
|
|
363
|
-
*/
|
|
364
|
-
summary?: string;
|
|
365
|
-
/**
|
|
366
|
-
* Don't include this action in the Swagger documentation.
|
|
367
|
-
*/
|
|
368
|
-
hide?: boolean;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Plugin for Alepha Server that provides Swagger documentation capabilities.
|
|
373
|
-
* It generates OpenAPI v3 documentation for the server's endpoints ($action).
|
|
374
|
-
* It also provides a Swagger UI for interactive API documentation.
|
|
375
|
-
*
|
|
376
|
-
* @see {@link ServerSwaggerProvider}
|
|
377
|
-
* @module alepha.server.swagger
|
|
378
|
-
*/
|
|
379
|
-
declare const AlephaServerSwagger: alepha17.Service<alepha17.Module>;
|
|
380
|
-
//#endregion
|
|
381
|
-
export { $swagger, AlephaServerSwagger, OpenApiDocument, OpenApiOperation, ServerSwaggerProvider, ServerSwaggerProviderOptions, SwaggerDescriptor, SwaggerDescriptorOptions, SwaggerUiOptions, swaggerOptions };
|
|
382
|
-
//# sourceMappingURL=index.d.cts.map
|