alepha 0.14.3 → 0.14.4

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.
Files changed (114) hide show
  1. package/README.md +1 -1
  2. package/dist/api/audits/index.d.ts +338 -417
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/files/index.d.ts +1 -80
  5. package/dist/api/files/index.d.ts.map +1 -1
  6. package/dist/api/jobs/index.d.ts +156 -235
  7. package/dist/api/jobs/index.d.ts.map +1 -1
  8. package/dist/api/notifications/index.d.ts +170 -249
  9. package/dist/api/notifications/index.d.ts.map +1 -1
  10. package/dist/api/parameters/index.d.ts +266 -345
  11. package/dist/api/parameters/index.d.ts.map +1 -1
  12. package/dist/api/users/index.d.ts +755 -834
  13. package/dist/api/users/index.d.ts.map +1 -1
  14. package/dist/api/verifications/index.d.ts +125 -125
  15. package/dist/api/verifications/index.d.ts.map +1 -1
  16. package/dist/cli/index.d.ts +116 -20
  17. package/dist/cli/index.d.ts.map +1 -1
  18. package/dist/cli/index.js +212 -124
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/command/index.d.ts +6 -11
  21. package/dist/command/index.d.ts.map +1 -1
  22. package/dist/command/index.js +2 -2
  23. package/dist/command/index.js.map +1 -1
  24. package/dist/core/index.browser.js +26 -4
  25. package/dist/core/index.browser.js.map +1 -1
  26. package/dist/core/index.d.ts +16 -1
  27. package/dist/core/index.d.ts.map +1 -1
  28. package/dist/core/index.js +26 -4
  29. package/dist/core/index.js.map +1 -1
  30. package/dist/core/index.native.js +26 -4
  31. package/dist/core/index.native.js.map +1 -1
  32. package/dist/logger/index.d.ts +1 -1
  33. package/dist/logger/index.d.ts.map +1 -1
  34. package/dist/logger/index.js +12 -2
  35. package/dist/logger/index.js.map +1 -1
  36. package/dist/mcp/index.d.ts.map +1 -1
  37. package/dist/mcp/index.js +1 -1
  38. package/dist/mcp/index.js.map +1 -1
  39. package/dist/orm/index.d.ts +37 -173
  40. package/dist/orm/index.d.ts.map +1 -1
  41. package/dist/orm/index.js +193 -422
  42. package/dist/orm/index.js.map +1 -1
  43. package/dist/server/auth/index.d.ts +167 -167
  44. package/dist/server/cache/index.d.ts +12 -0
  45. package/dist/server/cache/index.d.ts.map +1 -1
  46. package/dist/server/cache/index.js +55 -2
  47. package/dist/server/cache/index.js.map +1 -1
  48. package/dist/server/compress/index.d.ts +6 -0
  49. package/dist/server/compress/index.d.ts.map +1 -1
  50. package/dist/server/compress/index.js +36 -1
  51. package/dist/server/compress/index.js.map +1 -1
  52. package/dist/server/core/index.browser.js +2 -2
  53. package/dist/server/core/index.browser.js.map +1 -1
  54. package/dist/server/core/index.d.ts +10 -10
  55. package/dist/server/core/index.d.ts.map +1 -1
  56. package/dist/server/core/index.js +6 -3
  57. package/dist/server/core/index.js.map +1 -1
  58. package/dist/server/links/index.d.ts +39 -39
  59. package/dist/server/links/index.d.ts.map +1 -1
  60. package/dist/server/security/index.d.ts +9 -9
  61. package/dist/server/static/index.d.ts.map +1 -1
  62. package/dist/server/static/index.js +4 -0
  63. package/dist/server/static/index.js.map +1 -1
  64. package/dist/server/swagger/index.d.ts.map +1 -1
  65. package/dist/server/swagger/index.js +2 -3
  66. package/dist/server/swagger/index.js.map +1 -1
  67. package/dist/vite/index.d.ts +101 -106
  68. package/dist/vite/index.d.ts.map +1 -1
  69. package/dist/vite/index.js +571 -508
  70. package/dist/vite/index.js.map +1 -1
  71. package/package.json +1 -1
  72. package/src/cli/apps/AlephaCli.ts +0 -2
  73. package/src/cli/atoms/buildOptions.ts +88 -0
  74. package/src/cli/commands/build.ts +32 -69
  75. package/src/cli/commands/db.ts +0 -4
  76. package/src/cli/commands/dev.ts +16 -4
  77. package/src/cli/commands/gen/env.ts +53 -0
  78. package/src/cli/commands/gen/openapi.ts +1 -1
  79. package/src/cli/commands/gen/resource.ts +15 -0
  80. package/src/cli/commands/gen.ts +7 -1
  81. package/src/cli/commands/init.ts +0 -1
  82. package/src/cli/commands/test.ts +0 -1
  83. package/src/cli/commands/verify.ts +1 -1
  84. package/src/cli/defineConfig.ts +49 -7
  85. package/src/cli/index.ts +0 -1
  86. package/src/cli/services/AlephaCliUtils.ts +36 -25
  87. package/src/command/helpers/Runner.spec.ts +2 -2
  88. package/src/command/helpers/Runner.ts +1 -1
  89. package/src/command/primitives/$command.ts +0 -6
  90. package/src/command/providers/CliProvider.ts +1 -3
  91. package/src/core/Alepha.ts +42 -0
  92. package/src/logger/index.ts +15 -3
  93. package/src/mcp/transports/StdioMcpTransport.ts +1 -1
  94. package/src/orm/index.ts +2 -8
  95. package/src/queue/core/providers/WorkerProvider.spec.ts +48 -32
  96. package/src/server/cache/providers/ServerCacheProvider.spec.ts +183 -0
  97. package/src/server/cache/providers/ServerCacheProvider.ts +94 -9
  98. package/src/server/compress/providers/ServerCompressProvider.ts +61 -2
  99. package/src/server/core/helpers/ServerReply.ts +2 -2
  100. package/src/server/core/providers/ServerProvider.ts +11 -1
  101. package/src/server/static/providers/ServerStaticProvider.ts +10 -0
  102. package/src/server/swagger/providers/ServerSwaggerProvider.ts +5 -8
  103. package/src/vite/helpers/importViteReact.ts +13 -0
  104. package/src/vite/index.ts +1 -21
  105. package/src/vite/plugins/viteAlephaDev.ts +16 -1
  106. package/src/vite/plugins/viteAlephaSsrPreload.ts +222 -0
  107. package/src/vite/tasks/buildClient.ts +11 -0
  108. package/src/vite/tasks/buildServer.ts +47 -3
  109. package/src/vite/tasks/devServer.ts +69 -0
  110. package/src/vite/tasks/index.ts +2 -1
  111. package/src/cli/assets/viteConfigTs.ts +0 -14
  112. package/src/cli/commands/run.ts +0 -24
  113. package/src/vite/plugins/viteAlepha.ts +0 -37
  114. package/src/vite/plugins/viteAlephaBuild.ts +0 -281
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/server/cache/providers/ServerCacheProvider.ts","../../../src/server/cache/index.ts"],"sourcesContent":["import type { BinaryLike } from \"node:crypto\";\nimport { createHash } from \"node:crypto\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport { $cache, type CachePrimitiveOptions } from \"alepha/cache\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport {\n ActionPrimitive,\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 ActionPrimitive<TConfig extends RequestConfigSchema> {\n invalidate: () => Promise<void>;\n }\n}\n\nActionPrimitive.prototype.invalidate = async function (\n this: ActionPrimitive<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 CachePrimitiveOptions is provided, it will be used to configure the cache storage.\n *\n * @default false\n */\n store?: true | DurationLike | CachePrimitiveOptions;\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"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/server/cache/providers/ServerCacheProvider.ts","../../../src/server/cache/index.ts"],"sourcesContent":["import type { BinaryLike } from \"node:crypto\";\nimport { createHash } from \"node:crypto\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport { $cache, type CachePrimitiveOptions } from \"alepha/cache\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport {\n ActionPrimitive,\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 ActionPrimitive<TConfig extends RequestConfigSchema> {\n invalidate: () => Promise<void>;\n }\n}\n\nActionPrimitive.prototype.invalidate = async function (\n this: ActionPrimitive<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 // Don't cache error responses (status >= 400)\n if (response.status && response.status >= 400) {\n return;\n }\n\n // Initialize headers if not present\n response.headers ??= {};\n\n const key = this.createCacheKey(route, request);\n\n // Handle ReadableStream responses (e.g., SSR streaming)\n if (response.body instanceof ReadableStream && shouldStore) {\n // Tee the stream: one for client, one for cache collection\n const [clientStream, cacheStream] = (\n response.body as ReadableStream<Uint8Array>\n ).tee();\n\n // Replace response body with client stream (continues streaming to client)\n response.body = clientStream as typeof response.body;\n\n // Collect cache stream in background (non-blocking)\n this.collectStreamForCache(\n cacheStream,\n key,\n response.status,\n response.headers?.[\"content-type\"],\n shouldUseEtag,\n )\n .then((hash) => {\n if (shouldUseEtag && hash) {\n // Note: headers already sent for streaming, etag only useful for future requests\n this.log.trace(\"Stream cached with hash\", { key, hash });\n }\n })\n .catch((err) => {\n this.log.warn(\"Failed to cache stream\", { key, error: err });\n });\n\n return;\n }\n\n // Only process string responses (text, html, json, etc.)\n if (typeof response.body !== \"string\") {\n return;\n }\n\n const generatedEtag = this.generateETag(response.body);\n const lastModified = this.time.toISOString();\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 /**\n * Collect a ReadableStream into a string and store it in the cache.\n * This runs in the background while the original stream is sent to the client.\n *\n * @param stream - The stream to collect\n * @param key - Cache key\n * @param status - HTTP status code\n * @param contentType - Content-Type header\n * @param generateEtag - Whether to generate and return an ETag\n * @returns The generated ETag hash, or undefined\n */\n protected async collectStreamForCache(\n stream: ReadableStream<Uint8Array>,\n key: string,\n status: number | undefined,\n contentType: string | undefined,\n generateEtag: boolean,\n ): Promise<string | undefined> {\n const chunks: Uint8Array[] = [];\n const reader = stream.getReader();\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n\n // Combine chunks into a single string\n const decoder = new TextDecoder();\n const body =\n chunks\n .map((chunk) => decoder.decode(chunk, { stream: true }))\n .join(\"\") + decoder.decode(); // Flush remaining\n\n const hash = this.generateETag(body);\n const lastModified = this.time.toISOString();\n\n this.log.trace(\"Storing streamed response\", { key });\n\n await this.cache.set(key, {\n body,\n status,\n contentType,\n lastModified,\n hash,\n });\n\n return generateEtag ? hash : undefined;\n } finally {\n reader.releaseLock();\n }\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 CachePrimitiveOptions is provided, it will be used to configure the cache storage.\n *\n * @default false\n */\n store?: true | DurationLike | CachePrimitiveOptions;\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"],"mappings":"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"}
@@ -2,6 +2,7 @@ import * as alepha1 from "alepha";
2
2
  import { Alepha } from "alepha";
3
3
  import { ServerResponse } from "alepha/server";
4
4
  import { Transform } from "node:stream";
5
+ import { ReadableStream } from "node:stream/web";
5
6
 
6
7
  //#region ../../src/server/compress/providers/ServerCompressProvider.d.ts
7
8
  declare module "alepha" {
@@ -19,6 +20,11 @@ declare class ServerCompressProvider {
19
20
  readonly onResponse: alepha1.HookPrimitive<"server:onResponse">;
20
21
  protected isAllowedContentType(contentType: string | undefined): boolean;
21
22
  protected compress(encoding: keyof typeof ServerCompressProvider.compressors, response: ServerResponse): Promise<void>;
23
+ /**
24
+ * Create a compressed stream that flushes after each chunk.
25
+ * This is essential for streaming SSR - ensures each chunk is sent immediately.
26
+ */
27
+ protected createFlushingCompressStream(input: ReadableStream, createCompressor: (options?: any) => Transform, encoding: string, params: Record<number, any>): ReadableStream<Uint8Array>;
22
28
  protected getParams(encoding: keyof typeof ServerCompressProvider.compressors): Record<number, any>;
23
29
  protected setHeaders(response: ServerResponse, encoding: keyof typeof ServerCompressProvider.compressors): void;
24
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/compress/providers/ServerCompressProvider.ts","../../../src/server/compress/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;uCAgBuC;EAXa;;AAAA,cAevC,sBAAA,CAJuD;EAAA,OAAA,WAAA,EAK9C,MAL8C,CAAA,MAAA,EAAA;IAAA,QAAA,EAAA,CAAA,GAAA,IAAA,EAAA,GAAA,EAAA,EAAA,GAQ9B,OAR8B,CAQtB,MARsB,CAAA;IAIvD,MAAA,EAAA,CAAA,OAAsB,CAAtB,EAAA,GAAsB,EAAA,GAKA,SALA;EAIW,CAAA,GAAA,SAAA,CAAA;EAAR,mBAAA,MAAA,EAsBX,MAtBW;EACH,cAAA,OAAA,CAAA,CAAA,EAuBR,6BAvBQ;EAJb,SAAA,UAAA,EA2BkC,OAAA,CAa5B,aAxCN,CAAA,mBAAA,CAAA;EAyBK,UAAA,oBAAA,CAAA,WAAA,EAAA,MAAA,GAAA,SAAA,CAAA,EAAA,OAAA;EAEA,UAAA,QAAA,CAAA,QAAA,EAAA,MAAA,OAwDA,sBAAA,CAAuB,WAxDvB,EAAA,QAAA,EAyDb,cAzDa,CAAA,EA0DtB,OA1DsB,CAAA,IAAA,CAAA;EAA6B,UAa5B,SAAA,CAAA,QAAA,EAAA,MAAA,OAoFD,sBAAA,CAAuB,WApFtB,CAAA,EAqFvB,MArFuB,CAAA,MAAA,EAAA,GAAA,CAAA;EA2CD,UAAA,UAAuB,CAAA,QAAA,EA0DpC,cA1DoC,EAAA,QAAA,EAAA,MAAA,OA2DvB,sBAAA,CAAuB,WA3DA,CAAA,EAAA,IAAA;;AAE7C,UAiEY,6BAAA,CAjEZ;EAuCsB,mBAAA,EAAuB,MAAA,EAAA;;;;;;;;;AA5IE,cCUvC,oBDVuC,ECUnB,OAAA,CAAA,ODVmB,CCalD,OAAA,CAH+B,MAAA,CDVmB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/compress/providers/ServerCompressProvider.ts","../../../src/server/compress/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;uCAgBuC;EAXa;;AAAA,cAevC,sBAAA,CAJuD;EAAA,OAAA,WAAA,EAK9C,MAL8C,CAAA,MAAA,EAAA;IAAA,QAAA,EAAA,CAAA,GAAA,IAAA,EAAA,GAAA,EAAA,EAAA,GAQ9B,OAR8B,CAQtB,MARsB,CAAA;IAIvD,MAAA,EAAA,CAAA,OAAsB,CAAtB,EAAA,GAAsB,EAAA,GAKA,SALA;EAIW,CAAA,GAAA,SAAA,CAAA;EAAR,mBAAA,MAAA,EAsBX,MAtBW;EACH,cAAA,OAAA,CAAA,CAAA,EAuBR,6BAvBQ;EAJb,SAAA,UAAA,EA2BkC,OAAA,CAa5B,aAxCN,CAAA,mBAAA,CAAA;EAyBK,UAAA,oBAAA,CAAA,WAAA,EAAA,MAAA,GAAA,SAAA,CAAA,EAAA,OAAA;EAEA,UAAA,QAAA,CAAA,QAAA,EAAA,MAAA,OAwDA,sBAAA,CAAuB,WAxDvB,EAAA,QAAA,EAyDb,cAzDa,CAAA,EA0DtB,OA1DsB,CAAA,IAAA,CAAA;EAA6B;;;;EAyG7C,UAAA,4BAAA,CAAA,KAAA,EAAA,cAAA,EAAA,gBAAA,EAAA,CAAA,OAAA,CAAA,EAAA,GAAA,EAAA,GAC8B,SAD9B,EAAA,QAAA,EAAA,MAAA,EAAA,MAAA,EAGC,MAHD,CAAA,MAAA,EAAA,GAAA,CAAA,CAAA,EAIN,cAJM,CAIS,UAJT,CAAA;EAC8B,UAAA,SAAA,CAAA,QAAA,EAAA,MAAA,OAkDd,sBAAA,CAAuB,WAlDT,CAAA,EAmDpC,MAnDoC,CAAA,MAAA,EAAA,GAAA,CAAA;EAE7B,UAAA,UAAA,CAAA,QAAA,EAiEE,cAjEF,EAAA,QAAA,EAAA,MAAA,OAkEe,sBAAA,CAAuB,WAlEtC,CAAA,EAAA,IAAA;;AACP,UAyEY,6BAAA,CAzEZ;EA+CsB,mBAAA,EAAuB,MAAA,EAAA;;;;;;;;;cC7LrC,sBAAoB,OAAA,CAAA,QAG/B,OAAA,CAH+B,MAAA"}
@@ -81,9 +81,44 @@ var ServerCompressProvider = class ServerCompressProvider {
81
81
  }
82
82
  if (typeof body === "object" && body instanceof ReadableStream) {
83
83
  this.setHeaders(response, encoding);
84
- response.body = Readable.fromWeb(body).pipe(compressor.stream({ params }));
84
+ response.body = this.createFlushingCompressStream(body, compressor.stream, encoding, params);
85
85
  }
86
86
  }
87
+ /**
88
+ * Create a compressed stream that flushes after each chunk.
89
+ * This is essential for streaming SSR - ensures each chunk is sent immediately.
90
+ */
91
+ createFlushingCompressStream(input, createCompressor, encoding, params) {
92
+ const compressor = createCompressor({
93
+ params,
94
+ flush: zlib.constants.Z_SYNC_FLUSH
95
+ });
96
+ const reader = Readable.fromWeb(input);
97
+ return new ReadableStream({ start(controller) {
98
+ compressor.on("data", (chunk) => {
99
+ controller.enqueue(new Uint8Array(chunk));
100
+ });
101
+ compressor.on("end", () => {
102
+ controller.close();
103
+ });
104
+ compressor.on("error", (err) => {
105
+ controller.error(err);
106
+ });
107
+ reader.on("data", (chunk) => {
108
+ compressor.write(chunk);
109
+ const zlibStream = compressor;
110
+ if (encoding === "gzip") zlibStream.flush(zlib.constants.Z_SYNC_FLUSH);
111
+ else if (encoding === "br") zlibStream.flush(zlib.constants.BROTLI_OPERATION_FLUSH);
112
+ else if (encoding === "zstd") zlibStream.flush();
113
+ });
114
+ reader.on("end", () => {
115
+ compressor.end();
116
+ });
117
+ reader.on("error", (err) => {
118
+ controller.error(err);
119
+ });
120
+ } });
121
+ }
87
122
  getParams(encoding) {
88
123
  if (encoding === "zstd") return { [zlib.constants.ZSTD_c_compressionLevel]: 3 };
89
124
  if (encoding === "br") return {};
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/server/compress/providers/ServerCompressProvider.ts","../../../src/server/compress/index.ts"],"sourcesContent":["import { Readable, type Transform } from \"node:stream\";\nimport { ReadableStream } from \"node:stream/web\";\nimport { promisify } from \"node:util\";\nimport * as zlib from \"node:zlib\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport type { ServerResponse } from \"alepha/server\";\n\nconst gzip = promisify(zlib.gzip);\nconst createGzip = zlib.createGzip;\nconst brotli = promisify(zlib.brotliCompress);\nconst createBrotliCompress = zlib.createBrotliCompress;\nconst zstd = zlib.zstdCompress ? promisify(zlib.zstdCompress) : undefined;\nconst createZstdCompress = zstd ? zlib.createZstdCompress : undefined;\n\ndeclare module \"alepha\" {\n interface State {\n \"alepha.server.compress.options\"?: ServerCompressProviderOptions;\n }\n}\n\nexport class ServerCompressProvider {\n static compressors: Record<\n string,\n | {\n compress: (...args: any[]) => Promise<Buffer>;\n stream: (options?: any) => Transform;\n }\n | undefined\n > = {\n gzip: {\n compress: gzip,\n stream: createGzip,\n },\n br: {\n compress: brotli,\n stream: createBrotliCompress,\n },\n zstd:\n zstd && createZstdCompress\n ? {\n compress: zstd,\n stream: createZstdCompress,\n }\n : undefined,\n };\n\n protected readonly alepha = $inject(Alepha);\n\n protected get options(): ServerCompressProviderOptions {\n return {\n allowedContentTypes: [\n \"application/json\",\n \"text/html\",\n \"application/javascript\",\n \"text/plain\",\n \"text/css\",\n ],\n ...this.alepha.store.get(\"alepha.server.compress.options\"),\n };\n }\n\n public readonly onResponse = $hook({\n on: \"server:onResponse\",\n handler: async ({ request, response }) => {\n // skip if already compressed\n if (response.headers[\"content-encoding\"]) {\n return;\n }\n\n const acceptEncoding = request.headers[\"accept-encoding\"]; // skip if no accept-encoding header\n if (!acceptEncoding) {\n return;\n }\n\n // skip if not json or html (for now)\n if (!this.isAllowedContentType(response.headers[\"content-type\"])) {\n return;\n }\n\n for (const encoding of [\"zstd\", \"br\", \"gzip\"] as const) {\n if (\n acceptEncoding.includes(encoding) &&\n ServerCompressProvider.compressors[encoding]\n ) {\n await this.compress(encoding, response);\n return;\n }\n }\n },\n });\n\n protected isAllowedContentType(contentType: string | undefined): boolean {\n if (!contentType) {\n return false;\n }\n\n const lowerContentType = contentType.toLowerCase();\n\n return !!this.options.allowedContentTypes.find((it) =>\n lowerContentType.includes(it),\n );\n }\n\n protected async compress(\n encoding: keyof typeof ServerCompressProvider.compressors,\n response: ServerResponse,\n ): Promise<void> {\n const body = response.body; // can be string or Buffer or ArrayBuffer or Readable\n\n const compressor = ServerCompressProvider.compressors[encoding];\n if (!compressor) {\n return;\n }\n\n const params = this.getParams(encoding);\n\n if (\n typeof body === \"string\" ||\n Buffer.isBuffer(body) ||\n body instanceof ArrayBuffer\n ) {\n const compressed = await compressor.compress(body, {\n params,\n });\n this.setHeaders(response, encoding);\n response.headers[\"content-length\"] = compressed.length.toString();\n response.body = compressed;\n return;\n }\n\n if (typeof body === \"object\" && body instanceof Readable) {\n this.setHeaders(response, encoding);\n response.body = body.pipe(compressor.stream({ params }));\n return;\n }\n\n if (typeof body === \"object\" && body instanceof ReadableStream) {\n this.setHeaders(response, encoding);\n response.body = Readable.fromWeb(body).pipe(\n compressor.stream({ params }),\n );\n }\n }\n\n protected getParams(\n encoding: keyof typeof ServerCompressProvider.compressors,\n ): Record<number, any> {\n if (encoding === \"zstd\") {\n return {\n [zlib.constants.ZSTD_c_compressionLevel]: 3, // default compression level for zstd\n };\n }\n if (encoding === \"br\") {\n return {};\n }\n if (encoding === \"gzip\") {\n return {};\n }\n return {};\n }\n\n protected setHeaders(\n response: ServerResponse,\n encoding: keyof typeof ServerCompressProvider.compressors,\n ): void {\n response.headers.vary = \"content-encoding\";\n response.headers[\"content-encoding\"] = encoding;\n response.headers[\"cache-control\"] = \"no-cache\";\n }\n}\n\nexport interface ServerCompressProviderOptions {\n allowedContentTypes: string[];\n}\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { ServerCompressProvider } from \"./providers/ServerCompressProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/ServerCompressProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Server that provides server-side compression capabilities.\n *\n * Compresses responses using gzip, brotli, or zstd based on the `Accept-Encoding` header.\n */\nexport const AlephaServerCompress = $module({\n name: \"alepha.server.compress\",\n services: [AlephaServer, ServerCompressProvider],\n});\n"],"mappings":";;;;;;;;AAOA,MAAM,OAAO,UAAU,KAAK,KAAK;AACjC,MAAM,aAAa,KAAK;AACxB,MAAM,SAAS,UAAU,KAAK,eAAe;AAC7C,MAAM,uBAAuB,KAAK;AAClC,MAAM,OAAO,KAAK,eAAe,UAAU,KAAK,aAAa,GAAG;AAChE,MAAM,qBAAqB,OAAO,KAAK,qBAAqB;AAQ5D,IAAa,yBAAb,MAAa,uBAAuB;CAClC,OAAO,cAOH;EACF,MAAM;GACJ,UAAU;GACV,QAAQ;GACT;EACD,IAAI;GACF,UAAU;GACV,QAAQ;GACT;EACD,MACE,QAAQ,qBACJ;GACE,UAAU;GACV,QAAQ;GACT,GACD;EACP;CAED,AAAmB,SAAS,QAAQ,OAAO;CAE3C,IAAc,UAAyC;AACrD,SAAO;GACL,qBAAqB;IACnB;IACA;IACA;IACA;IACA;IACD;GACD,GAAG,KAAK,OAAO,MAAM,IAAI,iCAAiC;GAC3D;;CAGH,AAAgB,aAAa,MAAM;EACjC,IAAI;EACJ,SAAS,OAAO,EAAE,SAAS,eAAe;AAExC,OAAI,SAAS,QAAQ,oBACnB;GAGF,MAAM,iBAAiB,QAAQ,QAAQ;AACvC,OAAI,CAAC,eACH;AAIF,OAAI,CAAC,KAAK,qBAAqB,SAAS,QAAQ,gBAAgB,CAC9D;AAGF,QAAK,MAAM,YAAY;IAAC;IAAQ;IAAM;IAAO,CAC3C,KACE,eAAe,SAAS,SAAS,IACjC,uBAAuB,YAAY,WACnC;AACA,UAAM,KAAK,SAAS,UAAU,SAAS;AACvC;;;EAIP,CAAC;CAEF,AAAU,qBAAqB,aAA0C;AACvE,MAAI,CAAC,YACH,QAAO;EAGT,MAAM,mBAAmB,YAAY,aAAa;AAElD,SAAO,CAAC,CAAC,KAAK,QAAQ,oBAAoB,MAAM,OAC9C,iBAAiB,SAAS,GAAG,CAC9B;;CAGH,MAAgB,SACd,UACA,UACe;EACf,MAAM,OAAO,SAAS;EAEtB,MAAM,aAAa,uBAAuB,YAAY;AACtD,MAAI,CAAC,WACH;EAGF,MAAM,SAAS,KAAK,UAAU,SAAS;AAEvC,MACE,OAAO,SAAS,YAChB,OAAO,SAAS,KAAK,IACrB,gBAAgB,aAChB;GACA,MAAM,aAAa,MAAM,WAAW,SAAS,MAAM,EACjD,QACD,CAAC;AACF,QAAK,WAAW,UAAU,SAAS;AACnC,YAAS,QAAQ,oBAAoB,WAAW,OAAO,UAAU;AACjE,YAAS,OAAO;AAChB;;AAGF,MAAI,OAAO,SAAS,YAAY,gBAAgB,UAAU;AACxD,QAAK,WAAW,UAAU,SAAS;AACnC,YAAS,OAAO,KAAK,KAAK,WAAW,OAAO,EAAE,QAAQ,CAAC,CAAC;AACxD;;AAGF,MAAI,OAAO,SAAS,YAAY,gBAAgB,gBAAgB;AAC9D,QAAK,WAAW,UAAU,SAAS;AACnC,YAAS,OAAO,SAAS,QAAQ,KAAK,CAAC,KACrC,WAAW,OAAO,EAAE,QAAQ,CAAC,CAC9B;;;CAIL,AAAU,UACR,UACqB;AACrB,MAAI,aAAa,OACf,QAAO,GACJ,KAAK,UAAU,0BAA0B,GAC3C;AAEH,MAAI,aAAa,KACf,QAAO,EAAE;AAEX,MAAI,aAAa,OACf,QAAO,EAAE;AAEX,SAAO,EAAE;;CAGX,AAAU,WACR,UACA,UACM;AACN,WAAS,QAAQ,OAAO;AACxB,WAAS,QAAQ,sBAAsB;AACvC,WAAS,QAAQ,mBAAmB;;;;;;;;;;;ACxJxC,MAAa,uBAAuB,QAAQ;CAC1C,MAAM;CACN,UAAU,CAAC,cAAc,uBAAuB;CACjD,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/server/compress/providers/ServerCompressProvider.ts","../../../src/server/compress/index.ts"],"sourcesContent":["import { Readable, type Transform } from \"node:stream\";\nimport { ReadableStream } from \"node:stream/web\";\nimport { promisify } from \"node:util\";\nimport * as zlib from \"node:zlib\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport type { ServerResponse } from \"alepha/server\";\n\nconst gzip = promisify(zlib.gzip);\nconst createGzip = zlib.createGzip;\nconst brotli = promisify(zlib.brotliCompress);\nconst createBrotliCompress = zlib.createBrotliCompress;\nconst zstd = zlib.zstdCompress ? promisify(zlib.zstdCompress) : undefined;\nconst createZstdCompress = zstd ? zlib.createZstdCompress : undefined;\n\ndeclare module \"alepha\" {\n interface State {\n \"alepha.server.compress.options\"?: ServerCompressProviderOptions;\n }\n}\n\nexport class ServerCompressProvider {\n static compressors: Record<\n string,\n | {\n compress: (...args: any[]) => Promise<Buffer>;\n stream: (options?: any) => Transform;\n }\n | undefined\n > = {\n gzip: {\n compress: gzip,\n stream: createGzip,\n },\n br: {\n compress: brotli,\n stream: createBrotliCompress,\n },\n zstd:\n zstd && createZstdCompress\n ? {\n compress: zstd,\n stream: createZstdCompress,\n }\n : undefined,\n };\n\n protected readonly alepha = $inject(Alepha);\n\n protected get options(): ServerCompressProviderOptions {\n return {\n allowedContentTypes: [\n \"application/json\",\n \"text/html\",\n \"application/javascript\",\n \"text/plain\",\n \"text/css\",\n ],\n ...this.alepha.store.get(\"alepha.server.compress.options\"),\n };\n }\n\n public readonly onResponse = $hook({\n on: \"server:onResponse\",\n handler: async ({ request, response }) => {\n // skip if already compressed\n if (response.headers[\"content-encoding\"]) {\n return;\n }\n\n const acceptEncoding = request.headers[\"accept-encoding\"]; // skip if no accept-encoding header\n if (!acceptEncoding) {\n return;\n }\n\n // skip if not json or html (for now)\n if (!this.isAllowedContentType(response.headers[\"content-type\"])) {\n return;\n }\n\n for (const encoding of [\"zstd\", \"br\", \"gzip\"] as const) {\n if (\n acceptEncoding.includes(encoding) &&\n ServerCompressProvider.compressors[encoding]\n ) {\n await this.compress(encoding, response);\n return;\n }\n }\n },\n });\n\n protected isAllowedContentType(contentType: string | undefined): boolean {\n if (!contentType) {\n return false;\n }\n\n const lowerContentType = contentType.toLowerCase();\n\n return !!this.options.allowedContentTypes.find((it) =>\n lowerContentType.includes(it),\n );\n }\n\n protected async compress(\n encoding: keyof typeof ServerCompressProvider.compressors,\n response: ServerResponse,\n ): Promise<void> {\n const body = response.body; // can be string or Buffer or ArrayBuffer or Readable\n\n const compressor = ServerCompressProvider.compressors[encoding];\n if (!compressor) {\n return;\n }\n\n const params = this.getParams(encoding);\n\n if (\n typeof body === \"string\" ||\n Buffer.isBuffer(body) ||\n body instanceof ArrayBuffer\n ) {\n const compressed = await compressor.compress(body, {\n params,\n });\n this.setHeaders(response, encoding);\n response.headers[\"content-length\"] = compressed.length.toString();\n response.body = compressed;\n return;\n }\n\n if (typeof body === \"object\" && body instanceof Readable) {\n this.setHeaders(response, encoding);\n response.body = body.pipe(compressor.stream({ params }));\n return;\n }\n\n if (typeof body === \"object\" && body instanceof ReadableStream) {\n this.setHeaders(response, encoding);\n // For streaming responses, use flush mode to avoid buffering\n response.body = this.createFlushingCompressStream(\n body,\n compressor.stream,\n encoding,\n params,\n );\n }\n }\n\n /**\n * Create a compressed stream that flushes after each chunk.\n * This is essential for streaming SSR - ensures each chunk is sent immediately.\n */\n protected createFlushingCompressStream(\n input: ReadableStream,\n createCompressor: (options?: any) => Transform,\n encoding: string,\n params: Record<number, any>,\n ): ReadableStream<Uint8Array> {\n const compressor = createCompressor({\n params,\n flush: zlib.constants.Z_SYNC_FLUSH,\n });\n const reader = Readable.fromWeb(input);\n\n return new ReadableStream<Uint8Array>({\n start(controller) {\n compressor.on(\"data\", (chunk: Buffer) => {\n controller.enqueue(new Uint8Array(chunk));\n });\n\n compressor.on(\"end\", () => {\n controller.close();\n });\n\n compressor.on(\"error\", (err) => {\n controller.error(err);\n });\n\n reader.on(\"data\", (chunk: Buffer) => {\n compressor.write(chunk);\n // Force flush after each chunk for streaming\n // Cast to any because flush() exists on zlib streams but not in Transform type\n const zlibStream = compressor as any;\n if (encoding === \"gzip\") {\n zlibStream.flush(zlib.constants.Z_SYNC_FLUSH);\n } else if (encoding === \"br\") {\n zlibStream.flush(zlib.constants.BROTLI_OPERATION_FLUSH);\n } else if (encoding === \"zstd\") {\n zlibStream.flush();\n }\n });\n\n reader.on(\"end\", () => {\n compressor.end();\n });\n\n reader.on(\"error\", (err) => {\n controller.error(err);\n });\n },\n });\n }\n\n protected getParams(\n encoding: keyof typeof ServerCompressProvider.compressors,\n ): Record<number, any> {\n if (encoding === \"zstd\") {\n return {\n [zlib.constants.ZSTD_c_compressionLevel]: 3, // default compression level for zstd\n };\n }\n if (encoding === \"br\") {\n return {};\n }\n if (encoding === \"gzip\") {\n return {};\n }\n return {};\n }\n\n protected setHeaders(\n response: ServerResponse,\n encoding: keyof typeof ServerCompressProvider.compressors,\n ): void {\n response.headers.vary = \"content-encoding\";\n response.headers[\"content-encoding\"] = encoding;\n response.headers[\"cache-control\"] = \"no-cache\";\n }\n}\n\nexport interface ServerCompressProviderOptions {\n allowedContentTypes: string[];\n}\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { ServerCompressProvider } from \"./providers/ServerCompressProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/ServerCompressProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Server that provides server-side compression capabilities.\n *\n * Compresses responses using gzip, brotli, or zstd based on the `Accept-Encoding` header.\n */\nexport const AlephaServerCompress = $module({\n name: \"alepha.server.compress\",\n services: [AlephaServer, ServerCompressProvider],\n});\n"],"mappings":"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"}
@@ -405,9 +405,9 @@ var ServerReply = class {
405
405
  status;
406
406
  body;
407
407
  /**
408
- * Redirect to a given URL with optional status code (default 302).
408
+ * Redirect to a given URL with optional status code (default 301).
409
409
  */
410
- redirect(url, status = 302) {
410
+ redirect(url, status = 301) {
411
411
  this.status = status;
412
412
  this.headers.location = url;
413
413
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.browser.js","names":[],"sources":["../../../src/server/core/errors/HttpError.ts","../../../src/server/core/helpers/isMultipart.ts","../../../src/server/core/schemas/errorSchema.ts","../../../src/server/core/services/HttpClient.ts","../../../src/server/core/constants/routeMethods.ts","../../../src/server/core/errors/BadRequestError.ts","../../../src/server/core/errors/ConflictError.ts","../../../src/server/core/errors/ForbiddenError.ts","../../../src/server/core/errors/NotFoundError.ts","../../../src/server/core/errors/UnauthorizedError.ts","../../../src/server/core/errors/ValidationError.ts","../../../src/server/core/helpers/ServerReply.ts","../../../src/server/core/schemas/okSchema.ts","../../../src/server/core/index.browser.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\nimport type { ErrorSchema } from \"../schemas/errorSchema.ts\";\n\nexport const isHttpError = (\n error: unknown,\n status?: number,\n): error is HttpErrorLike => {\n const isError =\n !!error &&\n typeof error === \"object\" &&\n \"message\" in error &&\n typeof error.message === \"string\" &&\n \"status\" in error &&\n typeof error.status === \"number\";\n\n if (!isError) {\n return false;\n }\n\n if (status) {\n return (error as HttpErrorLike).status === status;\n }\n\n return true;\n};\n\nexport class HttpError extends AlephaError {\n public name = \"HttpError\";\n\n static is = isHttpError;\n\n static toJSON(error: HttpError): ErrorSchema {\n const json: Record<string, unknown> = {\n error: error.error,\n status: error.status,\n message: error.message,\n };\n\n if (error.details) json.details = error.details;\n if (error.requestId) json.requestId = error.requestId;\n if (error.reason) json.cause = error.reason;\n\n return json as ErrorSchema;\n }\n\n public readonly error: string;\n public readonly status: number;\n\n public readonly requestId?: string;\n public readonly details?: string;\n public readonly reason?: {\n name: string;\n message: string;\n };\n\n constructor(options: Partial<ErrorSchema>, cause?: unknown) {\n super(options.message, {\n cause,\n });\n\n this.status = options.status ?? 500;\n this.details = options.details;\n this.requestId = options.requestId;\n\n if (typeof options.cause === \"object\") {\n this.reason = {\n name: (options.cause as { name: string }).name,\n message: (options.cause as { message: string }).message,\n };\n } else if (cause instanceof Error) {\n this.reason = {\n name: cause.name,\n message: cause.message,\n };\n }\n\n if (this.constructor.name === \"HttpError\") {\n this.error =\n options.error ?? errorNameByStatus[this.status] ?? \"HttpError\";\n } else {\n this.error = this.constructor.name;\n }\n }\n}\n\nexport const errorNameByStatus: Record<number, string> = {\n 400: \"BadRequestError\",\n 401: \"UnauthorizedError\",\n 403: \"ForbiddenError\",\n 404: \"NotFoundError\",\n 405: \"MethodNotAllowedError\",\n 409: \"ConflictError\",\n 410: \"GoneError\",\n 413: \"PayloadTooLargeError\",\n 415: \"UnsupportedMediaTypeError\",\n 429: \"TooManyRequestsError\",\n 500: \"InternalServerError\",\n 501: \"NotImplementedError\",\n 502: \"BadGatewayError\",\n 503: \"ServiceUnavailableError\",\n 504: \"GatewayTimeoutError\",\n};\n\nexport interface HttpErrorLike extends Error {\n status: number;\n}\n","import type { RequestConfigSchema } from \"../interfaces/ServerRequest.ts\";\n\n/**\n * Checks if the route has multipart/form-data request body.\n */\nexport const isMultipart = (options: {\n schema?: RequestConfigSchema;\n requestBodyType?: string;\n}): boolean => {\n if (options.requestBodyType === \"multipart/form-data\") {\n return true;\n }\n\n if (options.schema?.body && \"properties\" in options.schema.body) {\n const properties: Record<string, any> = options.schema.body.properties;\n for (const key in properties) {\n if (properties[key].format === \"binary\") {\n return true;\n }\n }\n }\n\n return false;\n};\n","import { type Static, t } from \"alepha\";\n\nexport const errorSchema = t.object(\n {\n error: t.text({ description: \"HTTP error name\" }),\n status: t.integer({\n description: \"HTTP status code\",\n }),\n message: t.text({\n description: \"Short text which describe the error\",\n size: \"rich\",\n }),\n details: t.optional(\n t.text({\n description: \"Detailed description of the error\",\n size: \"rich\",\n }),\n ),\n requestId: t.optional(t.text()),\n cause: t.optional(\n t.object({\n name: t.text(),\n message: t.text({\n description: \"Cause Error message\",\n size: \"rich\",\n }),\n }),\n ),\n },\n {\n title: \"HttpError\",\n description: \"Generic response after a failed operation\",\n },\n);\n\nexport type ErrorSchema = Static<typeof errorSchema>;\n","import {\n $inject,\n Alepha,\n type FileLike,\n isFileLike,\n type Static,\n type TObject,\n type TSchema,\n} from \"alepha\";\nimport { $cache } from \"alepha/cache\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError } from \"../errors/HttpError.ts\";\nimport { isMultipart } from \"../helpers/isMultipart.ts\";\nimport type {\n ServerRequestConfigEntry,\n TRequestBody,\n TResponseBody,\n} from \"../interfaces/ServerRequest.ts\";\nimport type { ClientRequestOptions } from \"../primitives/$action.ts\";\nimport { errorSchema } from \"../schemas/errorSchema.ts\";\n\nexport class HttpClient {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public readonly cache = $cache<HttpClientCache>();\n\n protected readonly pendingRequests: HttpClientPendingRequests = {};\n\n public async fetchAction(args: FetchActionArgs): Promise<FetchResponse> {\n const route = args.action; // our link to fetch\n const options = args.options ?? {}; // fetch standard options, cache, etc.\n const config = args.config ?? {}; // params, query, body, etc.\n const host = args.host ?? \"\"; // remote host, e.g. \"https://api.example.com\" or empty (for browser)\n\n const request: RequestInit = {\n ...options.request,\n };\n\n const method = route.method;\n const headers: Record<string, string> = {};\n const url = this.url(host, route, config);\n\n await this.alepha.events.emit(\"client:onRequest\", {\n route,\n config,\n options,\n headers,\n request,\n });\n\n request.method ??= method;\n\n await this.body(request, headers, route, config);\n\n request.headers = {\n ...config.headers,\n ...Object.fromEntries(new Headers(request.headers).entries()),\n ...headers,\n };\n\n return await this.fetch(url, {\n ...request,\n schema: route.schema,\n ...options,\n });\n }\n\n public async fetch<T extends TSchema>(\n url: string,\n request: RequestInitWithOptions<T> = {}, // standard options\n ): Promise<FetchResponse<Static<T>>> {\n const options = {\n cache: request.localCache,\n schema: request.schema?.response,\n key: request.key,\n };\n\n request.method ??= \"GET\";\n\n this.log.trace(\"Request\", {\n url,\n method: request.method,\n body: request.body,\n headers: request.headers,\n options,\n });\n\n // Only add automatic ETag if user didn't explicitly provide headers\n const cached = await this.cache.get(url);\n if (cached && request.method === \"GET\") {\n if (cached.etag) {\n request.headers = new Headers(request.headers);\n if (!request.headers.has(\"if-none-match\")) {\n request.headers.set(\"if-none-match\", cached.etag);\n }\n } else {\n return {\n data: cached.data as Static<T>,\n status: 200,\n statusText: \"OK\",\n headers: new Headers(),\n };\n }\n }\n\n await this.alepha.events.emit(\"client:beforeFetch\", {\n url,\n options,\n request,\n });\n\n // make a key for the request\n // this will be used to check if the request is already pending\n const key =\n options.key ??\n JSON.stringify({\n url,\n method: request.method,\n body: request.body,\n });\n\n const existing = this.pendingRequests[key];\n if (existing) {\n this.log.info(\"Request already pending\", key);\n return existing;\n }\n\n this.pendingRequests[key] = fetch(url, request)\n .then(async (response) => {\n this.log.debug(\"Response\", {\n url,\n status: response.status,\n });\n\n const fetchResponse: FetchResponse = {\n data: await this.responseData(response, options),\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n raw: response,\n };\n\n if (request.method === \"GET\") {\n if (options.cache) {\n await this.cache.set(\n url,\n { data: fetchResponse.data },\n typeof options.cache === \"boolean\" ? undefined : options.cache,\n );\n } else if (!this.alepha.isBrowser()) {\n // only cache etag on server, browser can handle etag itself\n const etag = response.headers.get(\"etag\") ?? undefined;\n if (etag) {\n await this.cache.set(url, { data: fetchResponse.data, etag });\n }\n }\n }\n\n return fetchResponse;\n })\n .finally(() => {\n delete this.pendingRequests[key];\n });\n\n return this.pendingRequests[key];\n }\n\n protected url(\n host: string,\n action: HttpAction,\n args: ServerRequestConfigEntry,\n ) {\n let url = host;\n\n if (action.prefix) {\n url += action.prefix;\n }\n\n url += action.path;\n url = this.pathVariables(url, action, args);\n url = this.queryParams(url, action, args);\n\n return url;\n }\n\n protected async body(\n init: RequestInit,\n headers: Record<string, string>,\n action: HttpAction,\n args: ServerRequestConfigEntry = {},\n ) {\n const hasHeader =\n typeof init.headers === \"object\" &&\n \"content-type\" in init.headers &&\n init.headers[\"content-type\"] === \"multipart/form-data\";\n\n if (hasHeader || isMultipart(action)) {\n if (typeof init.headers === \"object\" && \"content-type\" in init.headers) {\n delete init.headers[\"content-type\"]; // fetch() will fill this for us\n }\n\n const formData = new FormData();\n\n for (const [key, value] of Object.entries(args.body ?? {})) {\n if (typeof value === \"string\") {\n formData.append(key, value);\n continue;\n }\n if (value instanceof Blob) {\n formData.append(key, value);\n continue;\n }\n if (isFileLike(value)) {\n // FileLike must be transformed to WebFile\n formData.append(\n key,\n new File([await value.arrayBuffer()], value.name, {\n type: value.type,\n }),\n );\n }\n }\n\n init.body = formData;\n\n return;\n }\n\n if (!init.body && action.schema?.body) {\n headers[\"content-type\"] = \"application/json\";\n init.body = this.alepha.codec.encode(action.schema?.body, args.body, {\n as: \"string\",\n });\n }\n }\n\n protected async responseData(\n response: Response,\n options: FetchOptions,\n ): Promise<any> {\n if (response.status === 304) {\n let cacheKey = response.url;\n if (typeof window !== \"undefined\") {\n cacheKey = cacheKey.replace(window.location.origin, \"\");\n }\n\n const cached = await this.cache.get(cacheKey);\n if (cached) {\n return cached.data;\n }\n\n // if no cached data (etag-only routes), return empty string\n return \"\";\n }\n\n if (response.status === 204) {\n return;\n }\n\n if (this.isMaybeFile(response)) {\n return this.createFileLike(response);\n }\n\n if (response.headers.get(\"Content-Type\")?.startsWith(\"text/\")) {\n return await response.text();\n }\n\n if (response.headers.get(\"Content-Type\") === \"application/json\") {\n const json = await response.json();\n\n if (response.status >= 400) {\n const jsonError = this.alepha.codec.decode(errorSchema, json);\n const error = new HttpError(jsonError);\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n if (options.schema) {\n return this.alepha.codec.decode(options.schema, json);\n }\n\n return json;\n }\n\n if (response.status >= 400) {\n const error = new HttpError({\n status: response.status,\n message: `An error occurred while fetching the resource. (${response.statusText})`,\n });\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n return response;\n }\n\n protected isMaybeFile(response: Response): boolean {\n const contentType = response.headers.get(\"Content-Type\");\n if (!contentType) {\n return false;\n }\n\n if (response.headers.get(\"Content-Disposition\")?.includes(\"attachment\")) {\n return true; // If Content-Disposition indicates an attachment, treat it as a file\n }\n\n return (\n contentType.startsWith(\"application/octet-stream\") ||\n contentType.startsWith(\"application/pdf\") ||\n contentType.startsWith(\"application/zip\") ||\n contentType.startsWith(\"image/\") ||\n contentType.startsWith(\"video/\") ||\n contentType.startsWith(\"audio/\")\n );\n }\n\n protected createFileLike(response: Response, defaultFileName = \"\"): FileLike {\n const match = (response.headers.get(\"Content-Disposition\") ?? \"\").match(\n /filename=\"(.+)\"/,\n );\n return {\n name: match?.[1] ? match[1] : defaultFileName,\n type: response.headers.get(\"Content-Type\") ?? \"application/octet-stream\",\n size: Number(response.headers.get(\"Content-Length\") ?? 0),\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Not implemented\");\n },\n arrayBuffer: async () => {\n return await response.arrayBuffer();\n },\n text: async () => {\n return await response.text();\n },\n };\n }\n\n public pathVariables(\n url: string,\n action: { schema?: { params?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.params === \"object\") {\n const params = action.schema?.params\n ? (this.alepha.codec.decode(\n action.schema.params,\n args.params,\n ) as Record<string, any>)\n : args.params;\n\n for (const key of Object.keys(params)) {\n url = url.replace(`:${key}`, params[key]);\n url = url.replace(`{${key}}`, params[key]);\n }\n }\n\n return url;\n }\n\n public queryParams(\n url: string,\n action: { schema?: { query?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.query === \"object\") {\n const query = action.schema?.query\n ? this.alepha.codec.decode(action.schema.query, args.query ?? {})\n : args.query;\n\n for (const key of Object.keys(query)) {\n if (query[key] === undefined) {\n delete query[key];\n }\n if (typeof query[key] === \"object\") {\n query[key] = JSON.stringify(query[key]);\n }\n }\n\n return `${url}?${new URLSearchParams(\n query as Record<string, string>,\n ).toString()}`;\n }\n return url;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface FetchOptions<T extends TSchema = TSchema> {\n /**\n * Key to identify the request in the pending requests.\n */\n key?: string;\n\n /**\n * The schema to validate the response against.\n */\n schema?: {\n response?: T;\n };\n\n /**\n * Built-in cache options.\n */\n localCache?: boolean | number | DurationLike;\n}\n\nexport type RequestInitWithOptions<T extends TSchema = TSchema> = RequestInit &\n FetchOptions<T>;\n\nexport interface FetchResponse<T = any> {\n data: T;\n status: number;\n statusText: string;\n headers: Headers;\n raw?: Response;\n}\n\nexport type HttpClientPendingRequests = Record<\n string,\n Promise<any> | undefined\n>;\n\ninterface HttpClientCache {\n data: any;\n etag?: string;\n}\n\nexport interface FetchActionArgs {\n action: HttpAction;\n host?: string;\n config?: ServerRequestConfigEntry;\n options?: ClientRequestOptions;\n}\n\nexport interface HttpAction {\n method?: string;\n prefix?: string;\n path: string;\n requestBodyType?: string;\n schema?: {\n params?: TObject;\n query?: TObject;\n body?: TRequestBody;\n response?: TResponseBody;\n };\n}\n","export const routeMethods = [\n // list of supported http methods\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"HEAD\",\n \"OPTIONS\",\n \"CONNECT\",\n \"TRACE\",\n] as const;\n\nexport type RouteMethod = (typeof routeMethods)[number];\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class BadRequestError extends HttpError {\n constructor(message = \"Invalid request body\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ConflictError extends HttpError {\n constructor(message = \"Entity already exists\", cause?: unknown) {\n super(\n {\n message,\n status: 409,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ForbiddenError extends HttpError {\n constructor(\n message = \"No permission to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 403,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class NotFoundError extends HttpError {\n constructor(message = \"Resource not found\", cause?: unknown) {\n super(\n {\n message,\n status: 404,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class UnauthorizedError extends HttpError {\n readonly name = \"UnauthorizedError\";\n\n constructor(\n message = \"Not allowed to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 401,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ValidationError extends HttpError {\n constructor(message = \"Validation has failed\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","/**\n * Helper for building server replies.\n */\nexport class ServerReply {\n // TODO: make it private\n public headers: Record<string, string> & {\n \"set-cookie\"?: string[];\n } = {};\n\n public status?: number; // default 200, or 204 (no content)\n\n public body?: any;\n\n /**\n * Redirect to a given URL with optional status code (default 302).\n */\n public redirect(url: string, status: number = 302): void {\n this.status = status;\n this.headers.location = url;\n }\n\n // TODO: check if status / header is already set and throw an error if so (for allow to override with force flag)\n\n /**\n * Set the response status code.\n */\n public setStatus(status: number): this {\n this.status = status;\n return this;\n }\n\n /**\n * Set a response header.\n */\n public setHeader(name: string, value: string): this {\n this.headers[name.toLowerCase()] = value;\n return this;\n }\n\n /**\n * Set the response body.\n */\n public setBody(body: any): this {\n this.body = body;\n return this;\n }\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const okSchema = t.object(\n {\n ok: t.boolean({ description: \"True when operation succeed\" }),\n id: t.optional(t.union([t.text(), t.integer()])),\n count: t.optional(\n t.number({ description: \"Number of resources affected\" }),\n ),\n },\n {\n title: \"Ok\",\n description: \"Generic response after a successful operation on a resource\",\n },\n);\n\nexport type Ok = Static<typeof okSchema>;\n","import { $module } from \"alepha\";\nimport { HttpClient } from \"./services/HttpClient.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./index.shared.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaServer = $module({\n name: \"alepha.server\",\n primitives: [],\n services: [HttpClient],\n});\n"],"mappings":";;;;;AAGA,MAAa,eACX,OACA,WAC2B;AAS3B,KAAI,EAPF,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,aAAa,SACb,OAAO,MAAM,YAAY,YACzB,YAAY,SACZ,OAAO,MAAM,WAAW,UAGxB,QAAO;AAGT,KAAI,OACF,QAAQ,MAAwB,WAAW;AAG7C,QAAO;;AAGT,IAAa,YAAb,cAA+B,YAAY;CACzC,AAAO,OAAO;CAEd,OAAO,KAAK;CAEZ,OAAO,OAAO,OAA+B;EAC3C,MAAM,OAAgC;GACpC,OAAO,MAAM;GACb,QAAQ,MAAM;GACd,SAAS,MAAM;GAChB;AAED,MAAI,MAAM,QAAS,MAAK,UAAU,MAAM;AACxC,MAAI,MAAM,UAAW,MAAK,YAAY,MAAM;AAC5C,MAAI,MAAM,OAAQ,MAAK,QAAQ,MAAM;AAErC,SAAO;;CAGT,AAAgB;CAChB,AAAgB;CAEhB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAKhB,YAAY,SAA+B,OAAiB;AAC1D,QAAM,QAAQ,SAAS,EACrB,OACD,CAAC;AAEF,OAAK,SAAS,QAAQ,UAAU;AAChC,OAAK,UAAU,QAAQ;AACvB,OAAK,YAAY,QAAQ;AAEzB,MAAI,OAAO,QAAQ,UAAU,SAC3B,MAAK,SAAS;GACZ,MAAO,QAAQ,MAA2B;GAC1C,SAAU,QAAQ,MAA8B;GACjD;WACQ,iBAAiB,MAC1B,MAAK,SAAS;GACZ,MAAM,MAAM;GACZ,SAAS,MAAM;GAChB;AAGH,MAAI,KAAK,YAAY,SAAS,YAC5B,MAAK,QACH,QAAQ,SAAS,kBAAkB,KAAK,WAAW;MAErD,MAAK,QAAQ,KAAK,YAAY;;;AAKpC,MAAa,oBAA4C;CACvD,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;;;;;;;AChGD,MAAa,eAAe,YAGb;AACb,KAAI,QAAQ,oBAAoB,sBAC9B,QAAO;AAGT,KAAI,QAAQ,QAAQ,QAAQ,gBAAgB,QAAQ,OAAO,MAAM;EAC/D,MAAM,aAAkC,QAAQ,OAAO,KAAK;AAC5D,OAAK,MAAM,OAAO,WAChB,KAAI,WAAW,KAAK,WAAW,SAC7B,QAAO;;AAKb,QAAO;;;;;ACpBT,MAAa,cAAc,EAAE,OAC3B;CACE,OAAO,EAAE,KAAK,EAAE,aAAa,mBAAmB,CAAC;CACjD,QAAQ,EAAE,QAAQ,EAChB,aAAa,oBACd,CAAC;CACF,SAAS,EAAE,KAAK;EACd,aAAa;EACb,MAAM;EACP,CAAC;CACF,SAAS,EAAE,SACT,EAAE,KAAK;EACL,aAAa;EACb,MAAM;EACP,CAAC,CACH;CACD,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC;CAC/B,OAAO,EAAE,SACP,EAAE,OAAO;EACP,MAAM,EAAE,MAAM;EACd,SAAS,EAAE,KAAK;GACd,aAAa;GACb,MAAM;GACP,CAAC;EACH,CAAC,CACH;CACF,EACD;CACE,OAAO;CACP,aAAa;CACd,CACF;;;;ACXD,IAAa,aAAb,MAAwB;CACtB,AAAmB,MAAM,SAAS;CAClC,AAAmB,SAAS,QAAQ,OAAO;CAE3C,AAAgB,QAAQ,QAAyB;CAEjD,AAAmB,kBAA6C,EAAE;CAElE,MAAa,YAAY,MAA+C;EACtE,MAAM,QAAQ,KAAK;EACnB,MAAM,UAAU,KAAK,WAAW,EAAE;EAClC,MAAM,SAAS,KAAK,UAAU,EAAE;EAChC,MAAM,OAAO,KAAK,QAAQ;EAE1B,MAAM,UAAuB,EAC3B,GAAG,QAAQ,SACZ;EAED,MAAM,SAAS,MAAM;EACrB,MAAM,UAAkC,EAAE;EAC1C,MAAM,MAAM,KAAK,IAAI,MAAM,OAAO,OAAO;AAEzC,QAAM,KAAK,OAAO,OAAO,KAAK,oBAAoB;GAChD;GACA;GACA;GACA;GACA;GACD,CAAC;AAEF,UAAQ,WAAW;AAEnB,QAAM,KAAK,KAAK,SAAS,SAAS,OAAO,OAAO;AAEhD,UAAQ,UAAU;GAChB,GAAG,OAAO;GACV,GAAG,OAAO,YAAY,IAAI,QAAQ,QAAQ,QAAQ,CAAC,SAAS,CAAC;GAC7D,GAAG;GACJ;AAED,SAAO,MAAM,KAAK,MAAM,KAAK;GAC3B,GAAG;GACH,QAAQ,MAAM;GACd,GAAG;GACJ,CAAC;;CAGJ,MAAa,MACX,KACA,UAAqC,EAAE,EACJ;EACnC,MAAM,UAAU;GACd,OAAO,QAAQ;GACf,QAAQ,QAAQ,QAAQ;GACxB,KAAK,QAAQ;GACd;AAED,UAAQ,WAAW;AAEnB,OAAK,IAAI,MAAM,WAAW;GACxB;GACA,QAAQ,QAAQ;GAChB,MAAM,QAAQ;GACd,SAAS,QAAQ;GACjB;GACD,CAAC;EAGF,MAAM,SAAS,MAAM,KAAK,MAAM,IAAI,IAAI;AACxC,MAAI,UAAU,QAAQ,WAAW,MAC/B,KAAI,OAAO,MAAM;AACf,WAAQ,UAAU,IAAI,QAAQ,QAAQ,QAAQ;AAC9C,OAAI,CAAC,QAAQ,QAAQ,IAAI,gBAAgB,CACvC,SAAQ,QAAQ,IAAI,iBAAiB,OAAO,KAAK;QAGnD,QAAO;GACL,MAAM,OAAO;GACb,QAAQ;GACR,YAAY;GACZ,SAAS,IAAI,SAAS;GACvB;AAIL,QAAM,KAAK,OAAO,OAAO,KAAK,sBAAsB;GAClD;GACA;GACA;GACD,CAAC;EAIF,MAAM,MACJ,QAAQ,OACR,KAAK,UAAU;GACb;GACA,QAAQ,QAAQ;GAChB,MAAM,QAAQ;GACf,CAAC;EAEJ,MAAM,WAAW,KAAK,gBAAgB;AACtC,MAAI,UAAU;AACZ,QAAK,IAAI,KAAK,2BAA2B,IAAI;AAC7C,UAAO;;AAGT,OAAK,gBAAgB,OAAO,MAAM,KAAK,QAAQ,CAC5C,KAAK,OAAO,aAAa;AACxB,QAAK,IAAI,MAAM,YAAY;IACzB;IACA,QAAQ,SAAS;IAClB,CAAC;GAEF,MAAM,gBAA+B;IACnC,MAAM,MAAM,KAAK,aAAa,UAAU,QAAQ;IAChD,QAAQ,SAAS;IACjB,YAAY,SAAS;IACrB,SAAS,SAAS;IAClB,KAAK;IACN;AAED,OAAI,QAAQ,WAAW,OACrB;QAAI,QAAQ,MACV,OAAM,KAAK,MAAM,IACf,KACA,EAAE,MAAM,cAAc,MAAM,EAC5B,OAAO,QAAQ,UAAU,YAAY,SAAY,QAAQ,MAC1D;aACQ,CAAC,KAAK,OAAO,WAAW,EAAE;KAEnC,MAAM,OAAO,SAAS,QAAQ,IAAI,OAAO,IAAI;AAC7C,SAAI,KACF,OAAM,KAAK,MAAM,IAAI,KAAK;MAAE,MAAM,cAAc;MAAM;MAAM,CAAC;;;AAKnE,UAAO;IACP,CACD,cAAc;AACb,UAAO,KAAK,gBAAgB;IAC5B;AAEJ,SAAO,KAAK,gBAAgB;;CAG9B,AAAU,IACR,MACA,QACA,MACA;EACA,IAAI,MAAM;AAEV,MAAI,OAAO,OACT,QAAO,OAAO;AAGhB,SAAO,OAAO;AACd,QAAM,KAAK,cAAc,KAAK,QAAQ,KAAK;AAC3C,QAAM,KAAK,YAAY,KAAK,QAAQ,KAAK;AAEzC,SAAO;;CAGT,MAAgB,KACd,MACA,SACA,QACA,OAAiC,EAAE,EACnC;AAMA,MAJE,OAAO,KAAK,YAAY,YACxB,kBAAkB,KAAK,WACvB,KAAK,QAAQ,oBAAoB,yBAElB,YAAY,OAAO,EAAE;AACpC,OAAI,OAAO,KAAK,YAAY,YAAY,kBAAkB,KAAK,QAC7D,QAAO,KAAK,QAAQ;GAGtB,MAAM,WAAW,IAAI,UAAU;AAE/B,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC,EAAE;AAC1D,QAAI,OAAO,UAAU,UAAU;AAC7B,cAAS,OAAO,KAAK,MAAM;AAC3B;;AAEF,QAAI,iBAAiB,MAAM;AACzB,cAAS,OAAO,KAAK,MAAM;AAC3B;;AAEF,QAAI,WAAW,MAAM,CAEnB,UAAS,OACP,KACA,IAAI,KAAK,CAAC,MAAM,MAAM,aAAa,CAAC,EAAE,MAAM,MAAM,EAChD,MAAM,MAAM,MACb,CAAC,CACH;;AAIL,QAAK,OAAO;AAEZ;;AAGF,MAAI,CAAC,KAAK,QAAQ,OAAO,QAAQ,MAAM;AACrC,WAAQ,kBAAkB;AAC1B,QAAK,OAAO,KAAK,OAAO,MAAM,OAAO,OAAO,QAAQ,MAAM,KAAK,MAAM,EACnE,IAAI,UACL,CAAC;;;CAIN,MAAgB,aACd,UACA,SACc;AACd,MAAI,SAAS,WAAW,KAAK;GAC3B,IAAI,WAAW,SAAS;AACxB,OAAI,OAAO,WAAW,YACpB,YAAW,SAAS,QAAQ,OAAO,SAAS,QAAQ,GAAG;GAGzD,MAAM,SAAS,MAAM,KAAK,MAAM,IAAI,SAAS;AAC7C,OAAI,OACF,QAAO,OAAO;AAIhB,UAAO;;AAGT,MAAI,SAAS,WAAW,IACtB;AAGF,MAAI,KAAK,YAAY,SAAS,CAC5B,QAAO,KAAK,eAAe,SAAS;AAGtC,MAAI,SAAS,QAAQ,IAAI,eAAe,EAAE,WAAW,QAAQ,CAC3D,QAAO,MAAM,SAAS,MAAM;AAG9B,MAAI,SAAS,QAAQ,IAAI,eAAe,KAAK,oBAAoB;GAC/D,MAAM,OAAO,MAAM,SAAS,MAAM;AAElC,OAAI,SAAS,UAAU,KAAK;IAE1B,MAAM,QAAQ,IAAI,UADA,KAAK,OAAO,MAAM,OAAO,aAAa,KAAK,CACvB;AAEtC,UAAM,KAAK,OAAO,OAAO,KAAK,kBAAkB,EAC9C,OACD,CAAC;AAEF,UAAM;;AAGR,OAAI,QAAQ,OACV,QAAO,KAAK,OAAO,MAAM,OAAO,QAAQ,QAAQ,KAAK;AAGvD,UAAO;;AAGT,MAAI,SAAS,UAAU,KAAK;GAC1B,MAAM,QAAQ,IAAI,UAAU;IAC1B,QAAQ,SAAS;IACjB,SAAS,mDAAmD,SAAS,WAAW;IACjF,CAAC;AAEF,SAAM,KAAK,OAAO,OAAO,KAAK,kBAAkB,EAC9C,OACD,CAAC;AAEF,SAAM;;AAGR,SAAO;;CAGT,AAAU,YAAY,UAA6B;EACjD,MAAM,cAAc,SAAS,QAAQ,IAAI,eAAe;AACxD,MAAI,CAAC,YACH,QAAO;AAGT,MAAI,SAAS,QAAQ,IAAI,sBAAsB,EAAE,SAAS,aAAa,CACrE,QAAO;AAGT,SACE,YAAY,WAAW,2BAA2B,IAClD,YAAY,WAAW,kBAAkB,IACzC,YAAY,WAAW,kBAAkB,IACzC,YAAY,WAAW,SAAS,IAChC,YAAY,WAAW,SAAS,IAChC,YAAY,WAAW,SAAS;;CAIpC,AAAU,eAAe,UAAoB,kBAAkB,IAAc;EAC3E,MAAM,SAAS,SAAS,QAAQ,IAAI,sBAAsB,IAAI,IAAI,MAChE,kBACD;AACD,SAAO;GACL,MAAM,QAAQ,KAAK,MAAM,KAAK;GAC9B,MAAM,SAAS,QAAQ,IAAI,eAAe,IAAI;GAC9C,MAAM,OAAO,SAAS,QAAQ,IAAI,iBAAiB,IAAI,EAAE;GACzD,cAAc,KAAK,KAAK;GACxB,cAAc;AACZ,UAAM,IAAI,MAAM,kBAAkB;;GAEpC,aAAa,YAAY;AACvB,WAAO,MAAM,SAAS,aAAa;;GAErC,MAAM,YAAY;AAChB,WAAO,MAAM,SAAS,MAAM;;GAE/B;;CAGH,AAAO,cACL,KACA,QACA,OAAiC,EAAE,EAC3B;AACR,MAAI,OAAO,KAAK,WAAW,UAAU;GACnC,MAAM,SAAS,OAAO,QAAQ,SACzB,KAAK,OAAO,MAAM,OACjB,OAAO,OAAO,QACd,KAAK,OACN,GACD,KAAK;AAET,QAAK,MAAM,OAAO,OAAO,KAAK,OAAO,EAAE;AACrC,UAAM,IAAI,QAAQ,IAAI,OAAO,OAAO,KAAK;AACzC,UAAM,IAAI,QAAQ,IAAI,IAAI,IAAI,OAAO,KAAK;;;AAI9C,SAAO;;CAGT,AAAO,YACL,KACA,QACA,OAAiC,EAAE,EAC3B;AACR,MAAI,OAAO,KAAK,UAAU,UAAU;GAClC,MAAM,QAAQ,OAAO,QAAQ,QACzB,KAAK,OAAO,MAAM,OAAO,OAAO,OAAO,OAAO,KAAK,SAAS,EAAE,CAAC,GAC/D,KAAK;AAET,QAAK,MAAM,OAAO,OAAO,KAAK,MAAM,EAAE;AACpC,QAAI,MAAM,SAAS,OACjB,QAAO,MAAM;AAEf,QAAI,OAAO,MAAM,SAAS,SACxB,OAAM,OAAO,KAAK,UAAU,MAAM,KAAK;;AAI3C,UAAO,GAAG,IAAI,GAAG,IAAI,gBACnB,MACD,CAAC,UAAU;;AAEd,SAAO;;;;;;ACxYX,MAAa,eAAe;CAE1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;ACTD,IAAa,kBAAb,cAAqC,UAAU;CAC7C,YAAY,UAAU,wBAAwB,OAAiB;AAC7D,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;ACRL,IAAa,gBAAb,cAAmC,UAAU;CAC3C,YAAY,UAAU,yBAAyB,OAAiB;AAC9D,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;ACRL,IAAa,iBAAb,cAAoC,UAAU;CAC5C,YACE,UAAU,yCACV,OACA;AACA,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;ACXL,IAAa,gBAAb,cAAmC,UAAU;CAC3C,YAAY,UAAU,sBAAsB,OAAiB;AAC3D,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;ACRL,IAAa,oBAAb,cAAuC,UAAU;CAC/C,AAAS,OAAO;CAEhB,YACE,UAAU,uCACV,OACA;AACA,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;ACbL,IAAa,kBAAb,cAAqC,UAAU;CAC7C,YAAY,UAAU,yBAAyB,OAAiB;AAC9D,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;;;;ACPL,IAAa,cAAb,MAAyB;CAEvB,AAAO,UAEH,EAAE;CAEN,AAAO;CAEP,AAAO;;;;CAKP,AAAO,SAAS,KAAa,SAAiB,KAAW;AACvD,OAAK,SAAS;AACd,OAAK,QAAQ,WAAW;;;;;CAQ1B,AAAO,UAAU,QAAsB;AACrC,OAAK,SAAS;AACd,SAAO;;;;;CAMT,AAAO,UAAU,MAAc,OAAqB;AAClD,OAAK,QAAQ,KAAK,aAAa,IAAI;AACnC,SAAO;;;;;CAMT,AAAO,QAAQ,MAAiB;AAC9B,OAAK,OAAO;AACZ,SAAO;;;;;;ACzCX,MAAa,WAAW,EAAE,OACxB;CACE,IAAI,EAAE,QAAQ,EAAE,aAAa,+BAA+B,CAAC;CAC7D,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;CAChD,OAAO,EAAE,SACP,EAAE,OAAO,EAAE,aAAa,gCAAgC,CAAC,CAC1D;CACF,EACD;CACE,OAAO;CACP,aAAa;CACd,CACF;;;;ACND,MAAa,eAAe,QAAQ;CAClC,MAAM;CACN,YAAY,EAAE;CACd,UAAU,CAAC,WAAW;CACvB,CAAC"}
1
+ {"version":3,"file":"index.browser.js","names":[],"sources":["../../../src/server/core/errors/HttpError.ts","../../../src/server/core/helpers/isMultipart.ts","../../../src/server/core/schemas/errorSchema.ts","../../../src/server/core/services/HttpClient.ts","../../../src/server/core/constants/routeMethods.ts","../../../src/server/core/errors/BadRequestError.ts","../../../src/server/core/errors/ConflictError.ts","../../../src/server/core/errors/ForbiddenError.ts","../../../src/server/core/errors/NotFoundError.ts","../../../src/server/core/errors/UnauthorizedError.ts","../../../src/server/core/errors/ValidationError.ts","../../../src/server/core/helpers/ServerReply.ts","../../../src/server/core/schemas/okSchema.ts","../../../src/server/core/index.browser.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\nimport type { ErrorSchema } from \"../schemas/errorSchema.ts\";\n\nexport const isHttpError = (\n error: unknown,\n status?: number,\n): error is HttpErrorLike => {\n const isError =\n !!error &&\n typeof error === \"object\" &&\n \"message\" in error &&\n typeof error.message === \"string\" &&\n \"status\" in error &&\n typeof error.status === \"number\";\n\n if (!isError) {\n return false;\n }\n\n if (status) {\n return (error as HttpErrorLike).status === status;\n }\n\n return true;\n};\n\nexport class HttpError extends AlephaError {\n public name = \"HttpError\";\n\n static is = isHttpError;\n\n static toJSON(error: HttpError): ErrorSchema {\n const json: Record<string, unknown> = {\n error: error.error,\n status: error.status,\n message: error.message,\n };\n\n if (error.details) json.details = error.details;\n if (error.requestId) json.requestId = error.requestId;\n if (error.reason) json.cause = error.reason;\n\n return json as ErrorSchema;\n }\n\n public readonly error: string;\n public readonly status: number;\n\n public readonly requestId?: string;\n public readonly details?: string;\n public readonly reason?: {\n name: string;\n message: string;\n };\n\n constructor(options: Partial<ErrorSchema>, cause?: unknown) {\n super(options.message, {\n cause,\n });\n\n this.status = options.status ?? 500;\n this.details = options.details;\n this.requestId = options.requestId;\n\n if (typeof options.cause === \"object\") {\n this.reason = {\n name: (options.cause as { name: string }).name,\n message: (options.cause as { message: string }).message,\n };\n } else if (cause instanceof Error) {\n this.reason = {\n name: cause.name,\n message: cause.message,\n };\n }\n\n if (this.constructor.name === \"HttpError\") {\n this.error =\n options.error ?? errorNameByStatus[this.status] ?? \"HttpError\";\n } else {\n this.error = this.constructor.name;\n }\n }\n}\n\nexport const errorNameByStatus: Record<number, string> = {\n 400: \"BadRequestError\",\n 401: \"UnauthorizedError\",\n 403: \"ForbiddenError\",\n 404: \"NotFoundError\",\n 405: \"MethodNotAllowedError\",\n 409: \"ConflictError\",\n 410: \"GoneError\",\n 413: \"PayloadTooLargeError\",\n 415: \"UnsupportedMediaTypeError\",\n 429: \"TooManyRequestsError\",\n 500: \"InternalServerError\",\n 501: \"NotImplementedError\",\n 502: \"BadGatewayError\",\n 503: \"ServiceUnavailableError\",\n 504: \"GatewayTimeoutError\",\n};\n\nexport interface HttpErrorLike extends Error {\n status: number;\n}\n","import type { RequestConfigSchema } from \"../interfaces/ServerRequest.ts\";\n\n/**\n * Checks if the route has multipart/form-data request body.\n */\nexport const isMultipart = (options: {\n schema?: RequestConfigSchema;\n requestBodyType?: string;\n}): boolean => {\n if (options.requestBodyType === \"multipart/form-data\") {\n return true;\n }\n\n if (options.schema?.body && \"properties\" in options.schema.body) {\n const properties: Record<string, any> = options.schema.body.properties;\n for (const key in properties) {\n if (properties[key].format === \"binary\") {\n return true;\n }\n }\n }\n\n return false;\n};\n","import { type Static, t } from \"alepha\";\n\nexport const errorSchema = t.object(\n {\n error: t.text({ description: \"HTTP error name\" }),\n status: t.integer({\n description: \"HTTP status code\",\n }),\n message: t.text({\n description: \"Short text which describe the error\",\n size: \"rich\",\n }),\n details: t.optional(\n t.text({\n description: \"Detailed description of the error\",\n size: \"rich\",\n }),\n ),\n requestId: t.optional(t.text()),\n cause: t.optional(\n t.object({\n name: t.text(),\n message: t.text({\n description: \"Cause Error message\",\n size: \"rich\",\n }),\n }),\n ),\n },\n {\n title: \"HttpError\",\n description: \"Generic response after a failed operation\",\n },\n);\n\nexport type ErrorSchema = Static<typeof errorSchema>;\n","import {\n $inject,\n Alepha,\n type FileLike,\n isFileLike,\n type Static,\n type TObject,\n type TSchema,\n} from \"alepha\";\nimport { $cache } from \"alepha/cache\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError } from \"../errors/HttpError.ts\";\nimport { isMultipart } from \"../helpers/isMultipart.ts\";\nimport type {\n ServerRequestConfigEntry,\n TRequestBody,\n TResponseBody,\n} from \"../interfaces/ServerRequest.ts\";\nimport type { ClientRequestOptions } from \"../primitives/$action.ts\";\nimport { errorSchema } from \"../schemas/errorSchema.ts\";\n\nexport class HttpClient {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public readonly cache = $cache<HttpClientCache>();\n\n protected readonly pendingRequests: HttpClientPendingRequests = {};\n\n public async fetchAction(args: FetchActionArgs): Promise<FetchResponse> {\n const route = args.action; // our link to fetch\n const options = args.options ?? {}; // fetch standard options, cache, etc.\n const config = args.config ?? {}; // params, query, body, etc.\n const host = args.host ?? \"\"; // remote host, e.g. \"https://api.example.com\" or empty (for browser)\n\n const request: RequestInit = {\n ...options.request,\n };\n\n const method = route.method;\n const headers: Record<string, string> = {};\n const url = this.url(host, route, config);\n\n await this.alepha.events.emit(\"client:onRequest\", {\n route,\n config,\n options,\n headers,\n request,\n });\n\n request.method ??= method;\n\n await this.body(request, headers, route, config);\n\n request.headers = {\n ...config.headers,\n ...Object.fromEntries(new Headers(request.headers).entries()),\n ...headers,\n };\n\n return await this.fetch(url, {\n ...request,\n schema: route.schema,\n ...options,\n });\n }\n\n public async fetch<T extends TSchema>(\n url: string,\n request: RequestInitWithOptions<T> = {}, // standard options\n ): Promise<FetchResponse<Static<T>>> {\n const options = {\n cache: request.localCache,\n schema: request.schema?.response,\n key: request.key,\n };\n\n request.method ??= \"GET\";\n\n this.log.trace(\"Request\", {\n url,\n method: request.method,\n body: request.body,\n headers: request.headers,\n options,\n });\n\n // Only add automatic ETag if user didn't explicitly provide headers\n const cached = await this.cache.get(url);\n if (cached && request.method === \"GET\") {\n if (cached.etag) {\n request.headers = new Headers(request.headers);\n if (!request.headers.has(\"if-none-match\")) {\n request.headers.set(\"if-none-match\", cached.etag);\n }\n } else {\n return {\n data: cached.data as Static<T>,\n status: 200,\n statusText: \"OK\",\n headers: new Headers(),\n };\n }\n }\n\n await this.alepha.events.emit(\"client:beforeFetch\", {\n url,\n options,\n request,\n });\n\n // make a key for the request\n // this will be used to check if the request is already pending\n const key =\n options.key ??\n JSON.stringify({\n url,\n method: request.method,\n body: request.body,\n });\n\n const existing = this.pendingRequests[key];\n if (existing) {\n this.log.info(\"Request already pending\", key);\n return existing;\n }\n\n this.pendingRequests[key] = fetch(url, request)\n .then(async (response) => {\n this.log.debug(\"Response\", {\n url,\n status: response.status,\n });\n\n const fetchResponse: FetchResponse = {\n data: await this.responseData(response, options),\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n raw: response,\n };\n\n if (request.method === \"GET\") {\n if (options.cache) {\n await this.cache.set(\n url,\n { data: fetchResponse.data },\n typeof options.cache === \"boolean\" ? undefined : options.cache,\n );\n } else if (!this.alepha.isBrowser()) {\n // only cache etag on server, browser can handle etag itself\n const etag = response.headers.get(\"etag\") ?? undefined;\n if (etag) {\n await this.cache.set(url, { data: fetchResponse.data, etag });\n }\n }\n }\n\n return fetchResponse;\n })\n .finally(() => {\n delete this.pendingRequests[key];\n });\n\n return this.pendingRequests[key];\n }\n\n protected url(\n host: string,\n action: HttpAction,\n args: ServerRequestConfigEntry,\n ) {\n let url = host;\n\n if (action.prefix) {\n url += action.prefix;\n }\n\n url += action.path;\n url = this.pathVariables(url, action, args);\n url = this.queryParams(url, action, args);\n\n return url;\n }\n\n protected async body(\n init: RequestInit,\n headers: Record<string, string>,\n action: HttpAction,\n args: ServerRequestConfigEntry = {},\n ) {\n const hasHeader =\n typeof init.headers === \"object\" &&\n \"content-type\" in init.headers &&\n init.headers[\"content-type\"] === \"multipart/form-data\";\n\n if (hasHeader || isMultipart(action)) {\n if (typeof init.headers === \"object\" && \"content-type\" in init.headers) {\n delete init.headers[\"content-type\"]; // fetch() will fill this for us\n }\n\n const formData = new FormData();\n\n for (const [key, value] of Object.entries(args.body ?? {})) {\n if (typeof value === \"string\") {\n formData.append(key, value);\n continue;\n }\n if (value instanceof Blob) {\n formData.append(key, value);\n continue;\n }\n if (isFileLike(value)) {\n // FileLike must be transformed to WebFile\n formData.append(\n key,\n new File([await value.arrayBuffer()], value.name, {\n type: value.type,\n }),\n );\n }\n }\n\n init.body = formData;\n\n return;\n }\n\n if (!init.body && action.schema?.body) {\n headers[\"content-type\"] = \"application/json\";\n init.body = this.alepha.codec.encode(action.schema?.body, args.body, {\n as: \"string\",\n });\n }\n }\n\n protected async responseData(\n response: Response,\n options: FetchOptions,\n ): Promise<any> {\n if (response.status === 304) {\n let cacheKey = response.url;\n if (typeof window !== \"undefined\") {\n cacheKey = cacheKey.replace(window.location.origin, \"\");\n }\n\n const cached = await this.cache.get(cacheKey);\n if (cached) {\n return cached.data;\n }\n\n // if no cached data (etag-only routes), return empty string\n return \"\";\n }\n\n if (response.status === 204) {\n return;\n }\n\n if (this.isMaybeFile(response)) {\n return this.createFileLike(response);\n }\n\n if (response.headers.get(\"Content-Type\")?.startsWith(\"text/\")) {\n return await response.text();\n }\n\n if (response.headers.get(\"Content-Type\") === \"application/json\") {\n const json = await response.json();\n\n if (response.status >= 400) {\n const jsonError = this.alepha.codec.decode(errorSchema, json);\n const error = new HttpError(jsonError);\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n if (options.schema) {\n return this.alepha.codec.decode(options.schema, json);\n }\n\n return json;\n }\n\n if (response.status >= 400) {\n const error = new HttpError({\n status: response.status,\n message: `An error occurred while fetching the resource. (${response.statusText})`,\n });\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n return response;\n }\n\n protected isMaybeFile(response: Response): boolean {\n const contentType = response.headers.get(\"Content-Type\");\n if (!contentType) {\n return false;\n }\n\n if (response.headers.get(\"Content-Disposition\")?.includes(\"attachment\")) {\n return true; // If Content-Disposition indicates an attachment, treat it as a file\n }\n\n return (\n contentType.startsWith(\"application/octet-stream\") ||\n contentType.startsWith(\"application/pdf\") ||\n contentType.startsWith(\"application/zip\") ||\n contentType.startsWith(\"image/\") ||\n contentType.startsWith(\"video/\") ||\n contentType.startsWith(\"audio/\")\n );\n }\n\n protected createFileLike(response: Response, defaultFileName = \"\"): FileLike {\n const match = (response.headers.get(\"Content-Disposition\") ?? \"\").match(\n /filename=\"(.+)\"/,\n );\n return {\n name: match?.[1] ? match[1] : defaultFileName,\n type: response.headers.get(\"Content-Type\") ?? \"application/octet-stream\",\n size: Number(response.headers.get(\"Content-Length\") ?? 0),\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Not implemented\");\n },\n arrayBuffer: async () => {\n return await response.arrayBuffer();\n },\n text: async () => {\n return await response.text();\n },\n };\n }\n\n public pathVariables(\n url: string,\n action: { schema?: { params?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.params === \"object\") {\n const params = action.schema?.params\n ? (this.alepha.codec.decode(\n action.schema.params,\n args.params,\n ) as Record<string, any>)\n : args.params;\n\n for (const key of Object.keys(params)) {\n url = url.replace(`:${key}`, params[key]);\n url = url.replace(`{${key}}`, params[key]);\n }\n }\n\n return url;\n }\n\n public queryParams(\n url: string,\n action: { schema?: { query?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.query === \"object\") {\n const query = action.schema?.query\n ? this.alepha.codec.decode(action.schema.query, args.query ?? {})\n : args.query;\n\n for (const key of Object.keys(query)) {\n if (query[key] === undefined) {\n delete query[key];\n }\n if (typeof query[key] === \"object\") {\n query[key] = JSON.stringify(query[key]);\n }\n }\n\n return `${url}?${new URLSearchParams(\n query as Record<string, string>,\n ).toString()}`;\n }\n return url;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface FetchOptions<T extends TSchema = TSchema> {\n /**\n * Key to identify the request in the pending requests.\n */\n key?: string;\n\n /**\n * The schema to validate the response against.\n */\n schema?: {\n response?: T;\n };\n\n /**\n * Built-in cache options.\n */\n localCache?: boolean | number | DurationLike;\n}\n\nexport type RequestInitWithOptions<T extends TSchema = TSchema> = RequestInit &\n FetchOptions<T>;\n\nexport interface FetchResponse<T = any> {\n data: T;\n status: number;\n statusText: string;\n headers: Headers;\n raw?: Response;\n}\n\nexport type HttpClientPendingRequests = Record<\n string,\n Promise<any> | undefined\n>;\n\ninterface HttpClientCache {\n data: any;\n etag?: string;\n}\n\nexport interface FetchActionArgs {\n action: HttpAction;\n host?: string;\n config?: ServerRequestConfigEntry;\n options?: ClientRequestOptions;\n}\n\nexport interface HttpAction {\n method?: string;\n prefix?: string;\n path: string;\n requestBodyType?: string;\n schema?: {\n params?: TObject;\n query?: TObject;\n body?: TRequestBody;\n response?: TResponseBody;\n };\n}\n","export const routeMethods = [\n // list of supported http methods\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"HEAD\",\n \"OPTIONS\",\n \"CONNECT\",\n \"TRACE\",\n] as const;\n\nexport type RouteMethod = (typeof routeMethods)[number];\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class BadRequestError extends HttpError {\n constructor(message = \"Invalid request body\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ConflictError extends HttpError {\n constructor(message = \"Entity already exists\", cause?: unknown) {\n super(\n {\n message,\n status: 409,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ForbiddenError extends HttpError {\n constructor(\n message = \"No permission to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 403,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class NotFoundError extends HttpError {\n constructor(message = \"Resource not found\", cause?: unknown) {\n super(\n {\n message,\n status: 404,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class UnauthorizedError extends HttpError {\n readonly name = \"UnauthorizedError\";\n\n constructor(\n message = \"Not allowed to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 401,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ValidationError extends HttpError {\n constructor(message = \"Validation has failed\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","/**\n * Helper for building server replies.\n */\nexport class ServerReply {\n // TODO: make it private\n public headers: Record<string, string> & {\n \"set-cookie\"?: string[];\n } = {};\n\n public status?: number; // default 200, or 204 (no content)\n\n public body?: any;\n\n /**\n * Redirect to a given URL with optional status code (default 301).\n */\n public redirect(url: string, status: number = 301): void {\n this.status = status;\n this.headers.location = url;\n }\n\n // TODO: check if status / header is already set and throw an error if so (for allow to override with force flag)\n\n /**\n * Set the response status code.\n */\n public setStatus(status: number): this {\n this.status = status;\n return this;\n }\n\n /**\n * Set a response header.\n */\n public setHeader(name: string, value: string): this {\n this.headers[name.toLowerCase()] = value;\n return this;\n }\n\n /**\n * Set the response body.\n */\n public setBody(body: any): this {\n this.body = body;\n return this;\n }\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const okSchema = t.object(\n {\n ok: t.boolean({ description: \"True when operation succeed\" }),\n id: t.optional(t.union([t.text(), t.integer()])),\n count: t.optional(\n t.number({ description: \"Number of resources affected\" }),\n ),\n },\n {\n title: \"Ok\",\n description: \"Generic response after a successful operation on a resource\",\n },\n);\n\nexport type Ok = Static<typeof okSchema>;\n","import { $module } from \"alepha\";\nimport { HttpClient } from \"./services/HttpClient.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./index.shared.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaServer = $module({\n name: \"alepha.server\",\n primitives: [],\n services: [HttpClient],\n});\n"],"mappings":";;;;;AAGA,MAAa,eACX,OACA,WAC2B;AAS3B,KAAI,EAPF,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,aAAa,SACb,OAAO,MAAM,YAAY,YACzB,YAAY,SACZ,OAAO,MAAM,WAAW,UAGxB,QAAO;AAGT,KAAI,OACF,QAAQ,MAAwB,WAAW;AAG7C,QAAO;;AAGT,IAAa,YAAb,cAA+B,YAAY;CACzC,AAAO,OAAO;CAEd,OAAO,KAAK;CAEZ,OAAO,OAAO,OAA+B;EAC3C,MAAM,OAAgC;GACpC,OAAO,MAAM;GACb,QAAQ,MAAM;GACd,SAAS,MAAM;GAChB;AAED,MAAI,MAAM,QAAS,MAAK,UAAU,MAAM;AACxC,MAAI,MAAM,UAAW,MAAK,YAAY,MAAM;AAC5C,MAAI,MAAM,OAAQ,MAAK,QAAQ,MAAM;AAErC,SAAO;;CAGT,AAAgB;CAChB,AAAgB;CAEhB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAKhB,YAAY,SAA+B,OAAiB;AAC1D,QAAM,QAAQ,SAAS,EACrB,OACD,CAAC;AAEF,OAAK,SAAS,QAAQ,UAAU;AAChC,OAAK,UAAU,QAAQ;AACvB,OAAK,YAAY,QAAQ;AAEzB,MAAI,OAAO,QAAQ,UAAU,SAC3B,MAAK,SAAS;GACZ,MAAO,QAAQ,MAA2B;GAC1C,SAAU,QAAQ,MAA8B;GACjD;WACQ,iBAAiB,MAC1B,MAAK,SAAS;GACZ,MAAM,MAAM;GACZ,SAAS,MAAM;GAChB;AAGH,MAAI,KAAK,YAAY,SAAS,YAC5B,MAAK,QACH,QAAQ,SAAS,kBAAkB,KAAK,WAAW;MAErD,MAAK,QAAQ,KAAK,YAAY;;;AAKpC,MAAa,oBAA4C;CACvD,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;;;;;;;AChGD,MAAa,eAAe,YAGb;AACb,KAAI,QAAQ,oBAAoB,sBAC9B,QAAO;AAGT,KAAI,QAAQ,QAAQ,QAAQ,gBAAgB,QAAQ,OAAO,MAAM;EAC/D,MAAM,aAAkC,QAAQ,OAAO,KAAK;AAC5D,OAAK,MAAM,OAAO,WAChB,KAAI,WAAW,KAAK,WAAW,SAC7B,QAAO;;AAKb,QAAO;;;;;ACpBT,MAAa,cAAc,EAAE,OAC3B;CACE,OAAO,EAAE,KAAK,EAAE,aAAa,mBAAmB,CAAC;CACjD,QAAQ,EAAE,QAAQ,EAChB,aAAa,oBACd,CAAC;CACF,SAAS,EAAE,KAAK;EACd,aAAa;EACb,MAAM;EACP,CAAC;CACF,SAAS,EAAE,SACT,EAAE,KAAK;EACL,aAAa;EACb,MAAM;EACP,CAAC,CACH;CACD,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC;CAC/B,OAAO,EAAE,SACP,EAAE,OAAO;EACP,MAAM,EAAE,MAAM;EACd,SAAS,EAAE,KAAK;GACd,aAAa;GACb,MAAM;GACP,CAAC;EACH,CAAC,CACH;CACF,EACD;CACE,OAAO;CACP,aAAa;CACd,CACF;;;;ACXD,IAAa,aAAb,MAAwB;CACtB,AAAmB,MAAM,SAAS;CAClC,AAAmB,SAAS,QAAQ,OAAO;CAE3C,AAAgB,QAAQ,QAAyB;CAEjD,AAAmB,kBAA6C,EAAE;CAElE,MAAa,YAAY,MAA+C;EACtE,MAAM,QAAQ,KAAK;EACnB,MAAM,UAAU,KAAK,WAAW,EAAE;EAClC,MAAM,SAAS,KAAK,UAAU,EAAE;EAChC,MAAM,OAAO,KAAK,QAAQ;EAE1B,MAAM,UAAuB,EAC3B,GAAG,QAAQ,SACZ;EAED,MAAM,SAAS,MAAM;EACrB,MAAM,UAAkC,EAAE;EAC1C,MAAM,MAAM,KAAK,IAAI,MAAM,OAAO,OAAO;AAEzC,QAAM,KAAK,OAAO,OAAO,KAAK,oBAAoB;GAChD;GACA;GACA;GACA;GACA;GACD,CAAC;AAEF,UAAQ,WAAW;AAEnB,QAAM,KAAK,KAAK,SAAS,SAAS,OAAO,OAAO;AAEhD,UAAQ,UAAU;GAChB,GAAG,OAAO;GACV,GAAG,OAAO,YAAY,IAAI,QAAQ,QAAQ,QAAQ,CAAC,SAAS,CAAC;GAC7D,GAAG;GACJ;AAED,SAAO,MAAM,KAAK,MAAM,KAAK;GAC3B,GAAG;GACH,QAAQ,MAAM;GACd,GAAG;GACJ,CAAC;;CAGJ,MAAa,MACX,KACA,UAAqC,EAAE,EACJ;EACnC,MAAM,UAAU;GACd,OAAO,QAAQ;GACf,QAAQ,QAAQ,QAAQ;GACxB,KAAK,QAAQ;GACd;AAED,UAAQ,WAAW;AAEnB,OAAK,IAAI,MAAM,WAAW;GACxB;GACA,QAAQ,QAAQ;GAChB,MAAM,QAAQ;GACd,SAAS,QAAQ;GACjB;GACD,CAAC;EAGF,MAAM,SAAS,MAAM,KAAK,MAAM,IAAI,IAAI;AACxC,MAAI,UAAU,QAAQ,WAAW,MAC/B,KAAI,OAAO,MAAM;AACf,WAAQ,UAAU,IAAI,QAAQ,QAAQ,QAAQ;AAC9C,OAAI,CAAC,QAAQ,QAAQ,IAAI,gBAAgB,CACvC,SAAQ,QAAQ,IAAI,iBAAiB,OAAO,KAAK;QAGnD,QAAO;GACL,MAAM,OAAO;GACb,QAAQ;GACR,YAAY;GACZ,SAAS,IAAI,SAAS;GACvB;AAIL,QAAM,KAAK,OAAO,OAAO,KAAK,sBAAsB;GAClD;GACA;GACA;GACD,CAAC;EAIF,MAAM,MACJ,QAAQ,OACR,KAAK,UAAU;GACb;GACA,QAAQ,QAAQ;GAChB,MAAM,QAAQ;GACf,CAAC;EAEJ,MAAM,WAAW,KAAK,gBAAgB;AACtC,MAAI,UAAU;AACZ,QAAK,IAAI,KAAK,2BAA2B,IAAI;AAC7C,UAAO;;AAGT,OAAK,gBAAgB,OAAO,MAAM,KAAK,QAAQ,CAC5C,KAAK,OAAO,aAAa;AACxB,QAAK,IAAI,MAAM,YAAY;IACzB;IACA,QAAQ,SAAS;IAClB,CAAC;GAEF,MAAM,gBAA+B;IACnC,MAAM,MAAM,KAAK,aAAa,UAAU,QAAQ;IAChD,QAAQ,SAAS;IACjB,YAAY,SAAS;IACrB,SAAS,SAAS;IAClB,KAAK;IACN;AAED,OAAI,QAAQ,WAAW,OACrB;QAAI,QAAQ,MACV,OAAM,KAAK,MAAM,IACf,KACA,EAAE,MAAM,cAAc,MAAM,EAC5B,OAAO,QAAQ,UAAU,YAAY,SAAY,QAAQ,MAC1D;aACQ,CAAC,KAAK,OAAO,WAAW,EAAE;KAEnC,MAAM,OAAO,SAAS,QAAQ,IAAI,OAAO,IAAI;AAC7C,SAAI,KACF,OAAM,KAAK,MAAM,IAAI,KAAK;MAAE,MAAM,cAAc;MAAM;MAAM,CAAC;;;AAKnE,UAAO;IACP,CACD,cAAc;AACb,UAAO,KAAK,gBAAgB;IAC5B;AAEJ,SAAO,KAAK,gBAAgB;;CAG9B,AAAU,IACR,MACA,QACA,MACA;EACA,IAAI,MAAM;AAEV,MAAI,OAAO,OACT,QAAO,OAAO;AAGhB,SAAO,OAAO;AACd,QAAM,KAAK,cAAc,KAAK,QAAQ,KAAK;AAC3C,QAAM,KAAK,YAAY,KAAK,QAAQ,KAAK;AAEzC,SAAO;;CAGT,MAAgB,KACd,MACA,SACA,QACA,OAAiC,EAAE,EACnC;AAMA,MAJE,OAAO,KAAK,YAAY,YACxB,kBAAkB,KAAK,WACvB,KAAK,QAAQ,oBAAoB,yBAElB,YAAY,OAAO,EAAE;AACpC,OAAI,OAAO,KAAK,YAAY,YAAY,kBAAkB,KAAK,QAC7D,QAAO,KAAK,QAAQ;GAGtB,MAAM,WAAW,IAAI,UAAU;AAE/B,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC,EAAE;AAC1D,QAAI,OAAO,UAAU,UAAU;AAC7B,cAAS,OAAO,KAAK,MAAM;AAC3B;;AAEF,QAAI,iBAAiB,MAAM;AACzB,cAAS,OAAO,KAAK,MAAM;AAC3B;;AAEF,QAAI,WAAW,MAAM,CAEnB,UAAS,OACP,KACA,IAAI,KAAK,CAAC,MAAM,MAAM,aAAa,CAAC,EAAE,MAAM,MAAM,EAChD,MAAM,MAAM,MACb,CAAC,CACH;;AAIL,QAAK,OAAO;AAEZ;;AAGF,MAAI,CAAC,KAAK,QAAQ,OAAO,QAAQ,MAAM;AACrC,WAAQ,kBAAkB;AAC1B,QAAK,OAAO,KAAK,OAAO,MAAM,OAAO,OAAO,QAAQ,MAAM,KAAK,MAAM,EACnE,IAAI,UACL,CAAC;;;CAIN,MAAgB,aACd,UACA,SACc;AACd,MAAI,SAAS,WAAW,KAAK;GAC3B,IAAI,WAAW,SAAS;AACxB,OAAI,OAAO,WAAW,YACpB,YAAW,SAAS,QAAQ,OAAO,SAAS,QAAQ,GAAG;GAGzD,MAAM,SAAS,MAAM,KAAK,MAAM,IAAI,SAAS;AAC7C,OAAI,OACF,QAAO,OAAO;AAIhB,UAAO;;AAGT,MAAI,SAAS,WAAW,IACtB;AAGF,MAAI,KAAK,YAAY,SAAS,CAC5B,QAAO,KAAK,eAAe,SAAS;AAGtC,MAAI,SAAS,QAAQ,IAAI,eAAe,EAAE,WAAW,QAAQ,CAC3D,QAAO,MAAM,SAAS,MAAM;AAG9B,MAAI,SAAS,QAAQ,IAAI,eAAe,KAAK,oBAAoB;GAC/D,MAAM,OAAO,MAAM,SAAS,MAAM;AAElC,OAAI,SAAS,UAAU,KAAK;IAE1B,MAAM,QAAQ,IAAI,UADA,KAAK,OAAO,MAAM,OAAO,aAAa,KAAK,CACvB;AAEtC,UAAM,KAAK,OAAO,OAAO,KAAK,kBAAkB,EAC9C,OACD,CAAC;AAEF,UAAM;;AAGR,OAAI,QAAQ,OACV,QAAO,KAAK,OAAO,MAAM,OAAO,QAAQ,QAAQ,KAAK;AAGvD,UAAO;;AAGT,MAAI,SAAS,UAAU,KAAK;GAC1B,MAAM,QAAQ,IAAI,UAAU;IAC1B,QAAQ,SAAS;IACjB,SAAS,mDAAmD,SAAS,WAAW;IACjF,CAAC;AAEF,SAAM,KAAK,OAAO,OAAO,KAAK,kBAAkB,EAC9C,OACD,CAAC;AAEF,SAAM;;AAGR,SAAO;;CAGT,AAAU,YAAY,UAA6B;EACjD,MAAM,cAAc,SAAS,QAAQ,IAAI,eAAe;AACxD,MAAI,CAAC,YACH,QAAO;AAGT,MAAI,SAAS,QAAQ,IAAI,sBAAsB,EAAE,SAAS,aAAa,CACrE,QAAO;AAGT,SACE,YAAY,WAAW,2BAA2B,IAClD,YAAY,WAAW,kBAAkB,IACzC,YAAY,WAAW,kBAAkB,IACzC,YAAY,WAAW,SAAS,IAChC,YAAY,WAAW,SAAS,IAChC,YAAY,WAAW,SAAS;;CAIpC,AAAU,eAAe,UAAoB,kBAAkB,IAAc;EAC3E,MAAM,SAAS,SAAS,QAAQ,IAAI,sBAAsB,IAAI,IAAI,MAChE,kBACD;AACD,SAAO;GACL,MAAM,QAAQ,KAAK,MAAM,KAAK;GAC9B,MAAM,SAAS,QAAQ,IAAI,eAAe,IAAI;GAC9C,MAAM,OAAO,SAAS,QAAQ,IAAI,iBAAiB,IAAI,EAAE;GACzD,cAAc,KAAK,KAAK;GACxB,cAAc;AACZ,UAAM,IAAI,MAAM,kBAAkB;;GAEpC,aAAa,YAAY;AACvB,WAAO,MAAM,SAAS,aAAa;;GAErC,MAAM,YAAY;AAChB,WAAO,MAAM,SAAS,MAAM;;GAE/B;;CAGH,AAAO,cACL,KACA,QACA,OAAiC,EAAE,EAC3B;AACR,MAAI,OAAO,KAAK,WAAW,UAAU;GACnC,MAAM,SAAS,OAAO,QAAQ,SACzB,KAAK,OAAO,MAAM,OACjB,OAAO,OAAO,QACd,KAAK,OACN,GACD,KAAK;AAET,QAAK,MAAM,OAAO,OAAO,KAAK,OAAO,EAAE;AACrC,UAAM,IAAI,QAAQ,IAAI,OAAO,OAAO,KAAK;AACzC,UAAM,IAAI,QAAQ,IAAI,IAAI,IAAI,OAAO,KAAK;;;AAI9C,SAAO;;CAGT,AAAO,YACL,KACA,QACA,OAAiC,EAAE,EAC3B;AACR,MAAI,OAAO,KAAK,UAAU,UAAU;GAClC,MAAM,QAAQ,OAAO,QAAQ,QACzB,KAAK,OAAO,MAAM,OAAO,OAAO,OAAO,OAAO,KAAK,SAAS,EAAE,CAAC,GAC/D,KAAK;AAET,QAAK,MAAM,OAAO,OAAO,KAAK,MAAM,EAAE;AACpC,QAAI,MAAM,SAAS,OACjB,QAAO,MAAM;AAEf,QAAI,OAAO,MAAM,SAAS,SACxB,OAAM,OAAO,KAAK,UAAU,MAAM,KAAK;;AAI3C,UAAO,GAAG,IAAI,GAAG,IAAI,gBACnB,MACD,CAAC,UAAU;;AAEd,SAAO;;;;;;ACxYX,MAAa,eAAe;CAE1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;ACTD,IAAa,kBAAb,cAAqC,UAAU;CAC7C,YAAY,UAAU,wBAAwB,OAAiB;AAC7D,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;ACRL,IAAa,gBAAb,cAAmC,UAAU;CAC3C,YAAY,UAAU,yBAAyB,OAAiB;AAC9D,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;ACRL,IAAa,iBAAb,cAAoC,UAAU;CAC5C,YACE,UAAU,yCACV,OACA;AACA,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;ACXL,IAAa,gBAAb,cAAmC,UAAU;CAC3C,YAAY,UAAU,sBAAsB,OAAiB;AAC3D,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;ACRL,IAAa,oBAAb,cAAuC,UAAU;CAC/C,AAAS,OAAO;CAEhB,YACE,UAAU,uCACV,OACA;AACA,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;ACbL,IAAa,kBAAb,cAAqC,UAAU;CAC7C,YAAY,UAAU,yBAAyB,OAAiB;AAC9D,QACE;GACE;GACA,QAAQ;GACT,EACD,MACD;;;;;;;;;ACPL,IAAa,cAAb,MAAyB;CAEvB,AAAO,UAEH,EAAE;CAEN,AAAO;CAEP,AAAO;;;;CAKP,AAAO,SAAS,KAAa,SAAiB,KAAW;AACvD,OAAK,SAAS;AACd,OAAK,QAAQ,WAAW;;;;;CAQ1B,AAAO,UAAU,QAAsB;AACrC,OAAK,SAAS;AACd,SAAO;;;;;CAMT,AAAO,UAAU,MAAc,OAAqB;AAClD,OAAK,QAAQ,KAAK,aAAa,IAAI;AACnC,SAAO;;;;;CAMT,AAAO,QAAQ,MAAiB;AAC9B,OAAK,OAAO;AACZ,SAAO;;;;;;ACzCX,MAAa,WAAW,EAAE,OACxB;CACE,IAAI,EAAE,QAAQ,EAAE,aAAa,+BAA+B,CAAC;CAC7D,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;CAChD,OAAO,EAAE,SACP,EAAE,OAAO,EAAE,aAAa,gCAAgC,CAAC,CAC1D;CACF,EACD;CACE,OAAO;CACP,aAAa;CACd,CACF;;;;ACND,MAAa,eAAe,QAAQ;CAClC,MAAM;CACN,YAAY,EAAE;CACd,UAAU,CAAC,WAAW;CACvB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import * as alepha19 from "alepha";
2
2
  import { Alepha, AlephaError, Async, FileLike, KIND, Primitive, Static, StreamLike, TArray, TFile, TObject, TRecord, TSchema, TStream, TString, TVoid } from "alepha";
3
- import * as alepha_logger6 from "alepha/logger";
3
+ import * as alepha_logger3 from "alepha/logger";
4
4
  import { Readable } from "node:stream";
5
5
  import { DateTimeProvider, DurationLike } from "alepha/datetime";
6
6
  import { ReadableStream } from "node:stream/web";
@@ -58,7 +58,7 @@ declare class ServerReply {
58
58
  status?: number;
59
59
  body?: any;
60
60
  /**
61
- * Redirect to a given URL with optional status code (default 302).
61
+ * Redirect to a given URL with optional status code (default 301).
62
62
  */
63
63
  redirect(url: string, status?: number): void;
64
64
  /**
@@ -226,7 +226,7 @@ declare class ServerRequestParser {
226
226
  //#region ../../src/server/core/providers/ServerTimingProvider.d.ts
227
227
  type TimingMap = Record<string, [number, number]>;
228
228
  declare class ServerTimingProvider {
229
- protected readonly log: alepha_logger6.Logger;
229
+ protected readonly log: alepha_logger3.Logger;
230
230
  protected readonly alepha: Alepha;
231
231
  options: {
232
232
  prefix: string;
@@ -249,7 +249,7 @@ declare class ServerTimingProvider {
249
249
  * - $page => React route (for SSR)
250
250
  */
251
251
  declare class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {
252
- protected readonly log: alepha_logger6.Logger;
252
+ protected readonly log: alepha_logger3.Logger;
253
253
  protected readonly alepha: Alepha;
254
254
  protected readonly routes: ServerRoute[];
255
255
  protected readonly serverTimingProvider: ServerTimingProvider;
@@ -288,7 +288,7 @@ declare class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {
288
288
  * ServerProvider supports both Node.js HTTP requests and Web (Fetch API) requests.
289
289
  */
290
290
  declare class ServerProvider {
291
- protected readonly log: alepha_logger6.Logger;
291
+ protected readonly log: alepha_logger3.Logger;
292
292
  protected readonly alepha: Alepha;
293
293
  protected readonly dateTimeProvider: DateTimeProvider;
294
294
  protected readonly router: ServerRouterProvider;
@@ -320,7 +320,7 @@ declare class ServerProvider {
320
320
  //#endregion
321
321
  //#region ../../src/server/core/services/HttpClient.d.ts
322
322
  declare class HttpClient {
323
- protected readonly log: alepha_logger6.Logger;
323
+ protected readonly log: alepha_logger3.Logger;
324
324
  protected readonly alepha: Alepha;
325
325
  readonly cache: alepha_cache0.CachePrimitiveFn<HttpClientCache, any[]>;
326
326
  protected readonly pendingRequests: HttpClientPendingRequests;
@@ -536,7 +536,7 @@ interface ActionPrimitiveOptions<TConfig extends RequestConfigSchema> extends Om
536
536
  handler: ServerActionHandler<TConfig>;
537
537
  }
538
538
  declare class ActionPrimitive<TConfig extends RequestConfigSchema> extends Primitive<ActionPrimitiveOptions<TConfig>> {
539
- protected readonly log: alepha_logger6.Logger;
539
+ protected readonly log: alepha_logger3.Logger;
540
540
  protected readonly env: {
541
541
  SERVER_API_PREFIX: string;
542
542
  };
@@ -684,7 +684,7 @@ declare module "alepha" {
684
684
  declare class BunHttpServerProvider extends ServerProvider {
685
685
  protected readonly alepha: Alepha;
686
686
  protected readonly dateTimeProvider: DateTimeProvider;
687
- protected readonly log: alepha_logger6.Logger;
687
+ protected readonly log: alepha_logger3.Logger;
688
688
  protected readonly env: {
689
689
  SERVER_PORT: number;
690
690
  SERVER_HOST: string;
@@ -709,7 +709,7 @@ declare module "alepha" {
709
709
  declare class NodeHttpServerProvider extends ServerProvider {
710
710
  protected readonly alepha: Alepha;
711
711
  protected readonly dateTimeProvider: DateTimeProvider;
712
- protected readonly log: alepha_logger6.Logger;
712
+ protected readonly log: alepha_logger3.Logger;
713
713
  protected readonly env: {
714
714
  SERVER_PORT: number;
715
715
  SERVER_HOST: string;
@@ -726,7 +726,7 @@ declare class NodeHttpServerProvider extends ServerProvider {
726
726
  //#endregion
727
727
  //#region ../../src/server/core/providers/ServerLoggerProvider.d.ts
728
728
  declare class ServerLoggerProvider {
729
- protected readonly log: alepha_logger6.Logger;
729
+ protected readonly log: alepha_logger3.Logger;
730
730
  protected readonly alepha: Alepha;
731
731
  readonly onRequest: alepha19.HookPrimitive<"server:onRequest">;
732
732
  readonly onError: alepha19.HookPrimitive<"server:onError">;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/core/schemas/errorSchema.ts","../../../src/server/core/errors/HttpError.ts","../../../src/server/core/constants/routeMethods.ts","../../../src/server/core/helpers/ServerReply.ts","../../../src/server/core/services/UserAgentParser.ts","../../../src/server/core/interfaces/ServerRequest.ts","../../../src/server/core/services/ServerRequestParser.ts","../../../src/server/core/providers/ServerTimingProvider.ts","../../../src/server/core/providers/ServerRouterProvider.ts","../../../src/server/core/providers/ServerProvider.ts","../../../src/server/core/services/HttpClient.ts","../../../src/server/core/primitives/$action.ts","../../../src/server/core/errors/BadRequestError.ts","../../../src/server/core/errors/ConflictError.ts","../../../src/server/core/errors/ForbiddenError.ts","../../../src/server/core/errors/NotFoundError.ts","../../../src/server/core/errors/UnauthorizedError.ts","../../../src/server/core/errors/ValidationError.ts","../../../src/server/core/helpers/isMultipart.ts","../../../src/server/core/schemas/okSchema.ts","../../../src/server/core/primitives/$route.ts","../../../src/server/core/providers/BunHttpServerProvider.ts","../../../src/server/core/providers/NodeHttpServerProvider.ts","../../../src/server/core/providers/ServerLoggerProvider.ts","../../../src/server/core/providers/ServerNotReadyProvider.ts","../../../src/server/core/index.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/core/schemas/errorSchema.ts","../../../src/server/core/errors/HttpError.ts","../../../src/server/core/constants/routeMethods.ts","../../../src/server/core/helpers/ServerReply.ts","../../../src/server/core/services/UserAgentParser.ts","../../../src/server/core/interfaces/ServerRequest.ts","../../../src/server/core/services/ServerRequestParser.ts","../../../src/server/core/providers/ServerTimingProvider.ts","../../../src/server/core/providers/ServerRouterProvider.ts","../../../src/server/core/providers/ServerProvider.ts","../../../src/server/core/services/HttpClient.ts","../../../src/server/core/primitives/$action.ts","../../../src/server/core/errors/BadRequestError.ts","../../../src/server/core/errors/ConflictError.ts","../../../src/server/core/errors/ForbiddenError.ts","../../../src/server/core/errors/NotFoundError.ts","../../../src/server/core/errors/UnauthorizedError.ts","../../../src/server/core/errors/ValidationError.ts","../../../src/server/core/helpers/isMultipart.ts","../../../src/server/core/schemas/okSchema.ts","../../../src/server/core/primitives/$route.ts","../../../src/server/core/providers/BunHttpServerProvider.ts","../../../src/server/core/providers/NodeHttpServerProvider.ts","../../../src/server/core/providers/ServerLoggerProvider.ts","../../../src/server/core/providers/ServerNotReadyProvider.ts","../../../src/server/core/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;cAEa,sBAAW;SA+BvB,QAAA,CAAA;;;;;;;;;AA/BD,CAAA,CAAA;AA+BC,KAEW,WAAA,GAAc,MAFzB,CAAA,OAEuC,WAFvC,CAAA;;;cC9BY,2DAGD;cAoBC,SAAA,SAAkB,WAAA;;2DApBnB;uBAyBW,YAAY;;;;;ED7BtB,SAAA,MA+BZ,CAAA,EAAA;IAAA,IAAA,EAAA,MAAA;;;uBCsBsB,QAAQ;;cA8BlB,mBAAmB;UAkBf,aAAA,SAAsB;;;;;cCvG1B;KAaD,WAAA,WAAsB;;;;;;cCVrB,WAAA;WAEK;;;;;;;AHHlB;EA+BC,QAAA,CAAA,GAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;;;;;;;;;;;EA/BuB,OAAA,CAAA,IAAA,EAAA,GAAA,CAAA,EAAA,IAAA;AAiCxB;;;UInCiB,aAAA;;;;;;;;;;;AJEJ,cIiCA,eAAA,CJFZ;EAAA,KAAA,CAAA,SAAA,CAAA,EAAA,MAAA,CAAA,EIGuC,aJHvC;;;;KKVW,YAAA,GAAe,UAAU,UAAU,SAAS,UAAU;KACtD,aAAA,GACR,UACA,UACA,UACA,QACA,SACA,UACA;UAEa,mBAAA;EL/BJ,IAAA,CAAA,EKgCJ,YLDR;EAAA,MAAA,CAAA,EKEU,OLFV;UKGS;YACE;aACC;;UAGI,oCACC,sBAAsB;QAEhC,wBAAwB,eAAe,OAAO;WAE3C,2BAA2B,UAChC,OAAO,sBACP;UAEI,0BAA0B,UAC9B,OAAO,qBACP;SAEG,yBAAyB,UAC5B,OAAO,oBACP;;KAGM,wBLzDY,CAAA,gBK0DN,mBL1DM,GK0DgB,mBL1DhB,CAAA,GK2DpB,OL3DoB,CK2DZ,mBL3DY,CK2DQ,OL3DR,CAAA,CAAA;AAAA,UK+DP,aL/DO,CAAA,gBKgEN,mBLhEM,GKgEgB,mBLhEhB,CAAA,SKiEd,mBLjEc,CKiEM,OLjEN,CAAA,CAAA;EAiCZ;;;UKoCF;EJpEG;AAuBb;;EAKuB,GAAA,EI6ChB,GJ7CgB;EAAY;;;EALJ,SAAA,EAAA,MAAA;EAAW;AA2D1C;AAkBA;;;;ACvGA;AAaA;;;;ACVA;;;;ECHiB,SAAA,ECoGJ,aDpGiB;EAmCjB;;;YCsED;EAlFA;;;EAAmC,KAAA,EAuFtC,WAvFsC;EAAS;;;EAC5C,GAAA,EA2FL,gBA3FkB;;AAErB,UA8Fa,WA9Fb,CAAA,gBA+Fc,mBA/Fd,GA+FoC,mBA/FpC,CAAA,SAgGM,KAhGN,CAAA;EACA;;;EAGA,OAAA,EAgGO,aAhGP,CAgGqB,OAhGrB,CAAA;EACA;;AAEJ;EACS,MAAA,CAAA,EAiGE,WAjGF;EACE;;;;;AAMX;;;;;;;;;EAMa,MAAA,CAAA,EAoGF,OApGE;EAAP;;;EAG8B,MAAA,CAAA,EAAA,OAAA;;AAC9B,KA0GM,kBA1GN,CAAA,gBA2GY,mBA3GZ,GA2GkC,mBA3GlC,CAAA,GA4GF,OA5GE,CAAA,UAAA,CAAA,SA4G0B,aA5G1B,GA6GF,MA7GE,CA6GK,OA7GL,CAAA,UAAA,CAAA,CAAA,GA8GF,gBA9GE;AACA,KA+GM,YAAA,GA/GN,MAAA,GAAA,MAAA,GAAA,MAAA,GAAA,MAAA,GAAA,KAAA;AAEG,KA+GG,gBAAA,GA/GH,MAAA,GAiHE,MAjHF,GAiHW,UAjHX,GAAA,SAAA,GAAA,IAAA,GAAA,IAAA;AAAyB,KAmHtB,aAnHsB,CAAA,gBAoHhB,mBApHgB,GAoHM,mBApHN,CAAA,GAAA,CAAA,OAAA,EAqHpB,aArHoB,CAqHN,OArHM,CAAA,EAAA,GAqHO,KArHP,CAqHa,kBArHb,CAqHgC,OArHhC,CAAA,CAAA;AACrB,UAsHI,cAAA,CAtHJ;EAAP,IAAA,EAAA,MAAA,GAuHW,MAvHX,GAuHoB,WAvHpB,GAuHkC,QAvHlC,GAuH+C,cAvH/C;EACA,OAAA,EAuHK,MAvHL,CAAA,MAAA,EAAA,MAAA,CAAA;EAAM,MAAA,EAAA,MAAA;AAGZ;AACkB,KAuHN,yBAAA,GAvHM,CAAA,OAAA,EAwHP,iBAxHO,EAAA,GAyHb,OAzHa,CAyHL,cAzHK,CAAA;AAAsB,UA2HvB,kBAAA,SAA2B,KA3HJ,CAAA;EACR,OAAA,EA2HrB,yBA3HqB;;AAA5B,UA8Ha,iBAAA,CA9Hb;EAAO,MAAA,EA+HD,WA/HC;EAIM,GAAA,EA4HV,GA5HU;EACC,OAAA,EA4HP,MA5HO,CAAA,MAAA,EAAA,MAAA,CAAA;EAAsB,KAAA,EA6H/B,MA7H+B,CAAA,MAAA,EAAA,MAAA,CAAA;EACV,MAAA,EA6HpB,MA7HoB,CAAA,MAAA,EAAA,MAAA,CAAA;EAIpB,GAAA,EA0HH,gBA1HG;;AA6BG,UAgGI,gBAAA,CAhGJ;EAKD,IAAA,CAAA,EA4FH,gBA5FG;EAKH,GAAA,CAAA,EAwFD,eAxFC;;AA3CC,UAsIO,gBAAA,CAtIP;EAAmB,GAAA,EAuItB,eAvIsB;EAqDZ,GAAA,EAmFV,gBAnFqB;;AACY,UAqFvB,eAAA,CArFuB;EAKf,GAAA,EAiFlB,OAjFkB;EAAd,GAAA,CAAA,EAkFH,QAlFG;;;;cCtHE,mBAAA;6BACc;sCACS;kCAEK,oBAAoB;wBAuB9B;+BAIO,oBAAiB;wBAIxB;;;;KCvC1B,SAAA,GAAY;cAEJ,oBAAA;0BAAoB,cAAA,CACT;6BACG;;;;;sBAAA,QAAA,CASA;uBAAA,QAAA,CAaC;EP5Bf,cA+BZ,WAAA,CAAA,CAAA,EAAA,MAAA;EAAA,WAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;8CO6E6C;;;;;;;;;AP5G9C;;cQyBa,oBAAA,SAA6B,eAAe;0BAAkB,cAAA,CACnD;6BACG;6BACE;2CACY;0CACD;;;;;;;ERG5B,SAAA,CAAA,OAA4B,CAAjB,EAAA,MAAiB,CAAA,EQKF,WRLZ,EAAA;8BQmBW,sBAAsB,4BAChD,YAAY;kCA4BW;oCAYrB,sBACF,2BACO,eAAY;IP9FjB,MAAA,EAAA,MAqBZ;IAEY,OAAA,QAAU,CAAA,MAAA,EAAA,MAAA,CAAA,GAAA;MApBX,YAAA,CAAA,EAAA,MAAA,EAAA;IAyBW,CAAA;IAAY,IAAA,EAAA,GAAA;EAwBJ,CAAA,CAAA;EAAR,UAAA,eAAA,CAAA,KAAA,EOkFZ,WPlFY,EAAA,OAAA,EOmFV,aPnFU,EAAA,YAAA,EOoFL,YPpFK,CAAA,EOoFO,OPpFP,CAAA,IAAA,CAAA;EA7BQ,iBAAA,CAAA,KAAA,EO4KpB,WP5KoB,EAAA,KAAA,EO6KpB,WP7KoB,EAAA,YAAA,EO8Kb,YP9Ka,CAAA,EAAA,IAAA;EAAW,UAAA,eAAA,CAAA,MAAA,CAAA,EOyOL,mBPzOK,CAAA,EOyOiB,YPzOjB;EA2D7B,UAAA,YAgBZ,CAAA,KAAA,EO8LU,WP9M2B,EAAA,OAAA,EO+MzB,aP/MyB,EAAA,KAAA,EOgN3B,KPhN2B,CAAA,EOgNtB,OPhNsB,CAAA,IAAA,CAAA;EAkBrB,eAAA,CAAc,KAAA,EAAA;aOsPT;cACT;;;;;;;;;;AR5Vb;AA+BC,cSbY,cAAA,CTaZ;0BSb0B,cAAA,CACH;6BACG;uCACU;6BACV;;;;;;oCAAA,QAAA,CAcO;;;ATHlC;mCSGkC,QAAA,CAQD;;;AR3CjC;AAuBA;;EAKuB,iBAAA,CAAA,gBAAA,EQ4BD,gBR5BC,CAAA,EQ6BlB,OR7BkB,CAAA,IAAA,CAAA;EAAY;;;EALJ,gBAAA,CAAA,EAAA,EQ4HK,eR5HL,CAAA,EQ4HuB,OR5HvB,CAAA,IAAA,CAAA;EAAW;AA2D1C;AAkBA;iDQ2JY,gBACC;;;;cC7OA,UAAA;0BAAU,cAAA,CACC;6BACG;kBAEJ,aAAA,CAAA,iBAAA;sCAEe;oBAEL,kBAAkB,QAAQ;kBAuC5B,gCAElB,uBAAuB,KAC/B,QAAQ,cAAc,OAAO;EVtErB,UAAA,GAAA,CA+BZ,IAAA,EAAA,MAAA,EAAA,MAAA,EU0IW,UV1IX,EAAA,IAAA,EU2IS,wBV3IT,CAAA,EAAA,MAAA;EAAA,UAAA,IAAA,CAAA,IAAA,EU2JS,WV3JT,EAAA,OAAA,EU4JY,MV5JZ,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA,MAAA,EU6JW,UV7JX,EAAA,IAAA,CAAA,EU8JS,wBV9JT,CAAA,EU8JsC,OV9JtC,CAAA,IAAA,CAAA;mCU8Ma,mBACD,eACR;kCAiE6B;qCAoBG,qCAAiC;;;eAuBpC;;YACxB;;;MV5Vc,KAAA,CAAA,EUiXS,OVjXT;IAAA,CAAA;EAiCZ,CAAA,EAAA,IAA4B,CAA5B,EUiVF,wBVjV8B,CAAA,EAAA,MAAd;;UU2WT,uBAAuB,UAAU;;AT3YlD;AAuBA;EApBY,GAAA,CAAA,EAAA,MAAA;EAyBW;;;EAwBA,MAAA,CAAA,EAAA;IA7BQ,QAAA,CAAA,ES8XhB,CT9XgB;EAAW,CAAA;EA2D7B;AAkBb;;kCSuTkC;;AR9ZrB,KQiaD,sBRtZF,CAAA,UQsZmC,ORtZnC,GQsZ6C,ORtZ7C,CAAA,GQsZwD,WRtZxD,GQuZR,YRvZQ,CQuZK,CRvZL,CAAA;AAEE,UQuZK,aRvZiB,CAAA,IAAA,GAAA,CAAA,CAAA;QQwZ1B;;;EPlaK,OAAA,EOqaF,OPraa;QOsahB;;KAGI,yBAAA,GAA4B,eAEtC;AN9aF,UMibU,eAAA,CNjboB;EAmCjB,IAAA,EAAA,GAAA;;;UMmZI,eAAA;EL/ZL,MAAA,EKgaF,ULhac;EAAG,IAAA,CAAA,EAAA,MAAA;EAAU,MAAA,CAAA,EKka1B,wBLla0B;EAAU,OAAA,CAAA,EKmanC,oBLnamC;;AAAmB,UKsajD,UAAA,CLtaiD;EAAO,MAAA,CAAA,EAAA,MAAA;EAC7D,MAAA,CAAA,EAAA,MAAA;EACR,IAAA,EAAA,MAAA;EACA,eAAA,CAAA,EAAA,MAAA;EACA,MAAA,CAAA,EAAA;IACA,MAAA,CAAA,EKuaS,OLvaT;IACA,KAAA,CAAA,EKuaQ,OLvaR;IACA,IAAA,CAAA,EKuaO,YLvaP;IACA,QAAA,CAAA,EKuaW,aLvaX;EAAK,CAAA;AAET;;;;;;;;AL/BA;;;;;;;;;;;;;;AAiCA;;;;AChCA;AAuBA;;;;;;;;AA2DA;AAkBA;;;;ACvGA;AAaA;;;;ACVA;;;;ACHA;AAmCA;;;;ACZA;;;;;;;AACA;;;;;;;;;AASA;;;;;;;AAQiB,cMgEJ,ONhEuB,EAAA;EAClB,CAAA,gBM+DsB,mBN/DtB,CAAA,CAAA,OAAA,EMgEP,sBNhEO,CMgEgB,ONhEhB,CAAA,CAAA,EMiEf,iBNjEe,CMiEG,ONjEH,CAAA;EAAsB,MAAA,EAAA,sBAAA;CAEhC;AAAwB,UMiFf,sBNjFe,CAAA,gBMiFwB,mBNjFxB,CAAA,SMkFtB,INlFsB,CMkFjB,WNlFiB,EAAA,SAAA,GAAA,MAAA,GAAA,QAAA,GAAA,WAAA,CAAA,CAAA;EAAsB;;;;;;EAIhD,IAAA,CAAA,EAAA,MAAA;EAEI;;;;;;;;;;;AASV;;;;;;;AAMA;;;;EAMU,KAAA,CAAA,EAAA,MAAA;EAKH;;;EAkCE,IAAA,CAAA,EAAA,MAAA;EAKF;;;AAKP;;;;;EAWW,MAAA,CAAA,EMyCA,WNzCA;EAgBA;;;AAUX;;;;EAEgC,MAAA,CAAA,EMsBrB,ONtBqB;EACrB;;;EACS,WAAA,CAAA,EAAA,MAAA;EAER;AAEZ;AAIA;;EACwC,QAAA,CAAA,EAAA,OAAA;EACZ;;;EAAmB,OAAA,EM0BpC,mBN1BoC,CM0BhB,ON1BgB,CAAA;;AAAD,cMsCjC,eNtCiC,CAAA,gBMuC5B,mBNvC4B,CAAA,SMwCpC,SNxCoC,CMwC1B,sBNxC0B,CMwCH,ONxCG,CAAA,CAAA,CAAA;EAE7B,mBAAc,GAAA,EMsCW,cAAA,CAClB,MNvCO;EACd,mBAAA,GAAA,EAAA;IAAS,iBAAA,EAAA,MAAA;EAAc,CAAA;EAAa,mBAAA,UAAA,EMwCtB,UNxCsB;EAC1C,mBAAA,cAAA,EMwCwB,cNxCxB;EAAM,mBAAA,oBAAA,EMyCwB,oBNzCxB;EAIL,UAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EACD,IAAA,MAAA,CAAA,CAAA,EAAA,MAAA;EACE,IAAA,KAAA,CAAA,CAAA,EMmDS,WNnDT;EAAR;;AAEL;EAIiB,IAAA,IAAA,CAAA,CAAA,EAAA,MAAA;EACP;;;EAGD,IAAA,KAAA,CAAA,CAAA,EAAA,MAAA;EACC;;;EAIO,IAAA,MAAA,CAAA,CAAA,EM6DM,WN7DU;EAKhB;AAKjB;;;;ECtMa,IAAA,IAAA,CAAA,CAAA,EAAA,MAAA;EACc,IAAA,MAAA,CAAA,CAAA,EKmRJ,OLnRI,GAAA,SAAA;EACS,kBAAA,CAAA,CAAA,EAAA,MAAA,GAAA,SAAA;EAEK;;;;EA2Bc,GAAA,CAAA,MAAA,CAAA,EKoR1C,kBLpR0C,CKoRvB,OLpRuB,CAAA,EAAA,OAAA,CAAA,EKqR1C,oBLrR0C,CAAA,EKsRlD,OLtRkD,CKsR1C,qBLtR0C,CKsRpB,OLtRoB,CAAA,CAAA;EAIxB;;;iBKoXlB,mBAAmB,oBAClB,uBACT,QAAQ,cAAc,sBAAsB;;AJ7Z5C,UIuaY,iBJvaA,CAAM,gBIua4B,mBJva5B,CAAA,SIwab,eJxaa,CIwaG,OJxaH,CAAA,CAAA;EAEV,CAAA,MAAoB,CAApB,EIwaA,kBJxaoB,CIwaD,OJxaC,CAAA,EAAA,OAAA,CAAA,EIyanB,oBJzamB,CAAA,EI0a5B,OJ1a4B,CI0apB,qBJ1aoB,CI0aE,OJ1aF,CAAA,CAAA;;AAEN,KI+af,kBJ/ae,CAAA,gBIgbT,mBJhbS,EAAA,IIibrB,2BJjbqB,CIibO,OJjbP,CAAA,CAAA,GAAA,QASA,MI0ab,CJ1aa,II0aR,CJ1aQ,CI0aN,CJ1aM,CAAA,SAAA,SAAA,GAAA,KAAA,GI0ayB,CJ1azB,GI0a6B,CJ1a7B,CI0a+B,CJ1a/B,CAAA,EAAA;AA6FmB,KIgVlC,2BJhVkC,CAAA,gBIgVU,mBJhVV,CAAA,GAAA;EAAS,IAAA,EIiV/C,OJjV+C,CAAA,MAAA,CAAA,SIiVvB,OJjVuB,GIiVb,MJjVa,CIiVN,OJjVM,CAAA,MAAA,CAAA,CAAA,GAAA,SAAA;UImV7C,0BAA0B,UAC9B,OAAO;YAGD,2BAA2B,UACjC,OAAO;UAGH,yBAAyB,UAC7B,QAAQ,OAAO;AH/arB,CAAA;AAAyD,UGmbxC,oBAAA,SAA6B,YHnbW,CAAA;EAAkB;;;EAIlC,OAAA,CAAA,EGmb7B,WHnb6B;;AASH,KG6a1B,qBH7a0B,CAAA,gBG6aY,mBH7aZ,CAAA,GG8apC,OH9aoC,CAAA,UAAA,CAAA,SG8aR,OH9aQ,GG8aE,MH9aF,CG8aS,OH9aT,CAAA,UAAA,CAAA,CAAA,GAAA,GAAA;;;;AAe3B,KGoaC,mBHpaD,CAAA,gBGqaO,mBHraP,GGqa6B,mBHra7B,CAAA,GAAA,CAAA,OAAA,EGuaA,mBHvaA,CGuaoB,OHvapB,CAAA,EAAA,GGwaN,KHxaM,CGwaA,kBHxaA,CGwamB,OHxanB,CAAA,CAAA;;;;;;;;AAmFE,UG8VI,mBH9VJ,CAAA,gBG8VwC,mBH9VxC,CAAA,SG+VH,aH/VG,CG+VW,OH/VX,CAAA,CAAA;;;cIxIA,eAAA,SAAwB,SAAA;;;;;cCAxB,aAAA,SAAsB,SAAA;;;;;cCAtB,cAAA,SAAuB,SAAA;;;;;cCAvB,aAAA,SAAsB,SAAA;;;;;cCAtB,iBAAA,SAA0B,SAAA;;;;;;cCA1B,eAAA,SAAwB,SAAA;;;;;;;;cCGxB;WACF;;;;;cCHE,mBAAQ;MAYpB,QAAA,CAAA;;;;KAEW,EAAA,GAAK,cAAc;;;;;;;;;;;AnBflB,coBaA,MpBkBZ,EAAA;EAAA,CAAA,gBoBlBsC,mBpBkBtC,CAAA,CAAA,OAAA,EoBjBU,qBpBiBV,CoBjBgC,OpBiBhC,CAAA,CAAA,EoBhBE,cpBgBF,CoBhBiB,OpBgBjB,CAAA;;;UoBVgB,sCACC,sBAAsB,6BAC9B,YAAY;cAIT,+BACK,6BACR,UAAU,sBAAsB;2CACD;;;;;cC1BnC,sBAAS;eAWb,QAAA,CAAA;;;;wBAGsB,QAAQ,cAAc;ArBlB9C;AA+BC,cqBVY,qBAAA,SAA8B,cAAA,CrBU1C;6BqBT0B;uCACU;0BAAA,cAAA,CACb;;;;;6BAEG;wBAEH,kBAAkB,GAAA,CAAI;;WrB5BtB,KAAA,EqB4BU,QAAA,CASX,arBrCC,CAAA,OAAA,CAAA;EAAA,mBAAA,IAAA,EqBqCD,QAAA,CAOE,arB5CD,CAAA,MAAA,CAAA;EAiCZ,UAAA,MAAW,CAAA,CAAA,EqBwBC,OrBxBgB,CAAA,IAAA,CAAA;qBqBiFjB;;;;cCxGjB,oBAAS;eAWb,QAAA,CAAA;;;;wBAGsB,QAAQ,cAAc,atBxBjC;;csB2BA,sBAAA,SAA+B,cAAA;6BACjB;uCACU;0BAAA,cAAA,CACb;;;;;6BAEG;;WtBhCH,MAAA,EsB4CA,MtB5CA,CAAA,OsB4CA,etB5CA,EAAA,OsB4CA,gBtB5CA,CAAA;EAAA,SAAA,KAAA,EsB4CA,QAAA,CASD,atBrDC,CAAA,OAAA,CAAA;EAiCZ,UAAA,gBAA4B,CAAA,IAAA,EAAA,CAAA,GAAA,EsB6BxB,etB7BgB,EAAA,GAAA,EsB6BM,gBtB7BN,EAAA,GAAA,IAAA,CAAA,EsB8B3B,MtB9B2B;2BsB8BrB,QAAA,CAUc;sBAaD;qBAoBD;ArBzGvB;;;csBAa,oBAAA;0BAAoB,cAAA,CACT;6BACG;sBAAA,QAAA,CAEA;oBAAA,QAAA,CAyBF;uBAAA,QAAA,CAQG;;;;;;;;;;;cC9Bf,sBAAA;6BACc;sBAAA,QAAA,CAEA;AxBX3B;;;;;2ByBiC2B;;;;MzBjCd,MA+BZ,EyBQa,ezBRb,CyBQ6B,mBzBR7B,CAAA;MAAA,OAAA,EyBSc,azBTd;eyBUc;;;cAGD,gBAAgB;eACf;eACA;;;;aAMF;MzBpDW,OAAA,EyBqDT,azBrDS;IAAA,CAAA;IAiCZ,gBAAW,EAAA;ayBuBV;eACE;aACF;IxBzDA,CAAA;IAuBA,eAAU,EAAA;MApBX,KAAA,EwB2DC,WxB3DD;MAyBW,OAAA,EwBmCR,axBnCQ;IAAY,CAAA;IAwBJ,mBAAA,EAAA;MAAR,KAAA,EwBeV,WxBfU;MA7BQ,OAAA,EwB6ChB,axB7CgB;MAAW,QAAA,EwB8C1B,cxB9C0B;IA2D7B,CAAA;IAkBI,kBAAc,EAAA;awB1BlB;cACC;eACC;MvB/EF,OAWH,EuBqEK,MvBrEL,CAAA,MAAA,EAAA,MAAA,CAAA;MAEE,OAAW,EuBoER,WvBpEmB;;;;MCVrB,OAAW,EsBkFT,YtBhFG;esBiFH;;;MrBtFE,KAAA,CAAA,EqByFH,UrBzFgB;MAmCjB,KAAA,EqBuDA,SrBvDe;;oBqB2DR;mBACD;EpBxEP;;;;;AAUZ;;;;;;;AAQA;AACkB,coBoFL,YpBpFK,EoBoFO,QAAA,CAAA,OpBpFP,CoB+HhB,QAAA,CA3CuB,MAAA,CpBpFP"}
@@ -31,9 +31,9 @@ var ServerReply = class {
31
31
  status;
32
32
  body;
33
33
  /**
34
- * Redirect to a given URL with optional status code (default 302).
34
+ * Redirect to a given URL with optional status code (default 301).
35
35
  */
36
- redirect(url, status = 302) {
36
+ redirect(url, status = 301) {
37
37
  this.status = status;
38
38
  this.headers.location = url;
39
39
  }
@@ -536,6 +536,7 @@ var ServerProvider = class {
536
536
  router = $inject(ServerRouterProvider);
537
537
  internalServerErrorMessage = "Internal Server Error";
538
538
  get hostname() {
539
+ if (this.alepha.isViteDev()) return `http://localhost:${this.alepha.env.SERVER_PORT}`;
539
540
  return "";
540
541
  }
541
542
  /**
@@ -602,8 +603,10 @@ var ServerProvider = class {
602
603
  }
603
604
  if (response.body instanceof ReadableStream) {
604
605
  res.writeHead(response.status, response.headers);
606
+ res.flushHeaders();
607
+ res.socket?.setNoDelay(true);
605
608
  try {
606
- for await (const chunk of response.body) res.write(chunk);
609
+ for await (const chunk of response.body) if (!res.write(chunk)) await new Promise((resolve) => res.once("drain", resolve));
607
610
  } catch (error) {
608
611
  this.log.error("Error piping proxy response stream", error);
609
612
  } finally {