@farm.js/core 0.1.0-beta.75 → 0.1.0-beta.76

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 (57) hide show
  1. package/dist/agent-runtime.cjs +7 -0
  2. package/dist/agent-runtime.cjs.map +1 -1
  3. package/dist/agent-runtime.mjs +7 -0
  4. package/dist/agent-runtime.mjs.map +1 -1
  5. package/dist/{app-BkCh7H09.d.mts → app-Bq-Tr90H.d.mts} +2 -0
  6. package/dist/{app-CZIEOZIa.d.ts → app-CQ6uhLEj.d.ts} +2 -0
  7. package/dist/{chunk-TWN6IBIL.mjs → chunk-ENS436YL.mjs} +2 -2
  8. package/dist/{chunk-IIKQBOGZ.mjs → chunk-MV5WATSI.mjs} +2 -2
  9. package/dist/{chunk-IIKQBOGZ.mjs.map → chunk-MV5WATSI.mjs.map} +1 -1
  10. package/dist/{chunk-S6FO7OXA.mjs → chunk-NN6KJF7I.mjs} +52 -7
  11. package/dist/chunk-NN6KJF7I.mjs.map +1 -0
  12. package/dist/{chunk-DKD7DGTE.mjs → chunk-OL7GH5ZC.mjs} +2 -2
  13. package/dist/{chunk-UGK7TW2V.mjs → chunk-P57PPOQL.mjs} +18 -6
  14. package/dist/chunk-P57PPOQL.mjs.map +1 -0
  15. package/dist/client/lifecycle.mjs +2 -2
  16. package/dist/client-plugins.cjs +1 -1
  17. package/dist/client-plugins.cjs.map +1 -1
  18. package/dist/client-plugins.mjs +2 -2
  19. package/dist/client.cjs +1 -1
  20. package/dist/client.cjs.map +1 -1
  21. package/dist/client.mjs +2 -2
  22. package/dist/index.cjs +75 -11
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.mts +1 -1
  25. package/dist/index.d.ts +1 -1
  26. package/dist/index.mjs +5 -5
  27. package/dist/internal/build-runtime.cjs +58 -6
  28. package/dist/internal/build-runtime.cjs.map +1 -1
  29. package/dist/internal/build-runtime.mjs +3 -3
  30. package/dist/internal/client-runtime.cjs +1 -1
  31. package/dist/internal/client-runtime.cjs.map +1 -1
  32. package/dist/internal/client-runtime.mjs +2 -2
  33. package/dist/internal/product-telemetry-runtime.cjs +1 -1
  34. package/dist/internal/product-telemetry-runtime.cjs.map +1 -1
  35. package/dist/internal/product-telemetry-runtime.mjs +1 -1
  36. package/dist/plugin.cjs +51 -6
  37. package/dist/plugin.cjs.map +1 -1
  38. package/dist/plugin.mjs +2 -2
  39. package/dist/server-plugins.cjs +17 -5
  40. package/dist/server-plugins.cjs.map +1 -1
  41. package/dist/server-plugins.mjs +1 -1
  42. package/dist/server.cjs +68 -11
  43. package/dist/server.cjs.map +1 -1
  44. package/dist/server.d.mts +1 -1
  45. package/dist/server.d.ts +1 -1
  46. package/dist/server.mjs +3 -3
  47. package/dist/version.cjs +1 -1
  48. package/dist/version.cjs.map +1 -1
  49. package/dist/version.mjs +1 -1
  50. package/dist/vite.cjs +51 -6
  51. package/dist/vite.cjs.map +1 -1
  52. package/dist/vite.mjs +2 -2
  53. package/package.json +1 -1
  54. package/dist/chunk-S6FO7OXA.mjs.map +0 -1
  55. package/dist/chunk-UGK7TW2V.mjs.map +0 -1
  56. /package/dist/{chunk-TWN6IBIL.mjs.map → chunk-ENS436YL.mjs.map} +0 -0
  57. /package/dist/{chunk-DKD7DGTE.mjs.map → chunk-OL7GH5ZC.mjs.map} +0 -0
package/dist/plugin.mjs CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  import "./chunk-RQ633A5I.mjs";
7
7
  import {
8
8
  farmPlugin
9
- } from "./chunk-S6FO7OXA.mjs";
9
+ } from "./chunk-NN6KJF7I.mjs";
10
10
  import "./chunk-4RDYOSFE.mjs";
11
11
  import "./chunk-C6JEXU6F.mjs";
12
12
  import "./chunk-AMZZCRQ7.mjs";
@@ -66,7 +66,7 @@ import "./chunk-RBRLLZL5.mjs";
66
66
  import "./chunk-WZ5QFROO.mjs";
67
67
  import "./chunk-ZRBCBOEX.mjs";
68
68
  import "./chunk-FXG2PS27.mjs";
69
- import "./chunk-IIKQBOGZ.mjs";
69
+ import "./chunk-MV5WATSI.mjs";
70
70
  import "./chunk-I3FMD5VJ.mjs";
71
71
  import "./chunk-OMQWEX4N.mjs";
72
72
  import "./chunk-44ZCFSKE.mjs";
@@ -404,17 +404,18 @@ function selectEncoding(header) {
404
404
  return brotli >= gzip ? "br" : "gzip";
405
405
  }
406
406
  __name(selectEncoding, "selectEncoding");
407
- function canCompress(request, response) {
408
- if (request.method === "HEAD" || !response.body || response.status === 204 || response.status === 205 || response.status === 304 || response.headers.has("content-encoding") || response.headers.has("content-range") || response.headers.get("cache-control")?.toLowerCase().includes("no-transform")) {
407
+ function isCompressionEligible(request, response) {
408
+ if (request.method !== "HEAD" && !response.body || response.status === 204 || response.status === 205 || response.status === 304 || response.headers.has("content-encoding") || response.headers.has("content-range") || response.headers.get("cache-control")?.toLowerCase().includes("no-transform")) {
409
409
  return false;
410
410
  }
411
411
  const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
412
412
  return contentType !== "text/event-stream";
413
413
  }
414
- __name(canCompress, "canCompress");
414
+ __name(isCompressionEligible, "isCompressionEligible");
415
415
  function appendVary(headers, value) {
416
416
  const current = headers.get("vary");
417
417
  const values = current ? current.split(",").map((item) => item.trim()).filter(Boolean) : [];
418
+ if (values.includes("*")) return;
418
419
  if (!values.some((item) => item.toLowerCase() === value.toLowerCase())) {
419
420
  values.push(value);
420
421
  }
@@ -440,6 +441,16 @@ function compressResponse(response, encoding) {
440
441
  });
441
442
  }
442
443
  __name(compressResponse, "compressResponse");
444
+ function varyIdentityResponse(response) {
445
+ const headers = new Headers(response.headers);
446
+ appendVary(headers, "Accept-Encoding");
447
+ return new Response(response.body, {
448
+ status: response.status,
449
+ statusText: response.statusText,
450
+ headers
451
+ });
452
+ }
453
+ __name(varyIdentityResponse, "varyIdentityResponse");
443
454
  function createCompressionPlugin({
444
455
  beforeRequest: overrideBeforeRequest,
445
456
  afterResponse: overrideAfterResponse
@@ -449,9 +460,10 @@ function createCompressionPlugin({
449
460
  enforce: "post",
450
461
  runtime: {
451
462
  after({ request, response, isProd }) {
452
- if (!isProd || !canCompress(request, response)) return;
463
+ if (!isProd || !isCompressionEligible(request, response)) return;
464
+ if (request.method === "HEAD") return varyIdentityResponse(response);
453
465
  const encoding = selectEncoding(request.headers.get("accept-encoding") ?? "");
454
- if (!encoding) return;
466
+ if (!encoding) return varyIdentityResponse(response);
455
467
  return compressResponse(response, encoding);
456
468
  }
457
469
  },
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server-plugins.ts","../src/navigation-errors.ts","../src/i18n/routing.ts","../src/plugins/route-pattern.ts","../src/plugins/redirects.ts","../src/plugins/headers.ts","../src/plugins/rewrites.ts","../src/plugins/env.ts","../src/plugins/compression.ts","../src/plugins/logger.ts"],"sourcesContent":["// Server-side plugins export\nexport {\n createRedirectsPlugin,\n createHeadersPlugin,\n createRewritesPlugin,\n createEnvPlugin,\n createCompressionPlugin,\n createLoggerPlugin,\n} from \"./plugins\";\n\nexport type {\n FarmPlugin,\n FarmPluginContext,\n FarmPluginLifecycle,\n FarmRequestPluginContext,\n FarmRequestStore,\n} from \"./plugin\";\nexport type { RedirectConfig, HeaderConfig, RewriteConfig } from \"./config\";\n","export type FarmRedirectStatus = 301 | 302 | 303 | 307 | 308;\n\nexport interface FarmRedirectSignal {\n url: string;\n status: FarmRedirectStatus;\n}\n\nconst REDIRECT_ERROR_CODE = \"FARM_REDIRECT\";\nconst NOT_FOUND_ERROR_CODE = \"FARM_NOT_FOUND\";\nconst REDIRECT_ERROR_SYMBOL = Symbol.for(\"farm.navigation.redirect\");\nconst NOT_FOUND_ERROR_SYMBOL = Symbol.for(\"farm.navigation.notFound\");\n\ntype FarmNavigationError = Error & {\n digest: string;\n [REDIRECT_ERROR_SYMBOL]?: FarmRedirectSignal;\n [NOT_FOUND_ERROR_SYMBOL]?: true;\n};\n\nexport function redirect(url: string, status: FarmRedirectStatus = 307): never {\n throw createRedirectError(url, status);\n}\n\nexport function permanentRedirect(url: string): never {\n redirect(url, 308);\n}\n\nexport function notFound(): never {\n const error = new Error(NOT_FOUND_ERROR_CODE) as FarmNavigationError;\n error.digest = NOT_FOUND_ERROR_CODE;\n error[NOT_FOUND_ERROR_SYMBOL] = true;\n throw error;\n}\n\nexport function isFarmRedirectError(error: unknown): boolean {\n return Boolean(getFarmRedirectError(error));\n}\n\nexport function getFarmRedirectError(error: unknown): FarmRedirectSignal | null {\n if (!error || typeof error !== \"object\") return null;\n const candidate = error as Partial<FarmNavigationError>;\n if (candidate[REDIRECT_ERROR_SYMBOL]) {\n return candidate[REDIRECT_ERROR_SYMBOL] as FarmRedirectSignal;\n }\n\n if (\n typeof candidate.digest === \"string\" &&\n candidate.digest.startsWith(`${REDIRECT_ERROR_CODE};`)\n ) {\n const [, status, ...urlParts] = candidate.digest.split(\";\");\n const parsedStatus = Number(status);\n if (isFarmRedirectStatus(parsedStatus)) {\n return {\n status: parsedStatus,\n url: urlParts.join(\";\"),\n };\n }\n }\n\n return null;\n}\n\nexport function isFarmNotFoundError(error: unknown): boolean {\n if (!error || typeof error !== \"object\") return false;\n const candidate = error as Partial<FarmNavigationError>;\n return Boolean(candidate[NOT_FOUND_ERROR_SYMBOL] || candidate.digest === NOT_FOUND_ERROR_CODE);\n}\n\nfunction createRedirectError(url: string, status: FarmRedirectStatus): FarmNavigationError {\n const error = new Error(`${REDIRECT_ERROR_CODE};${status};${url}`) as FarmNavigationError;\n error.digest = `${REDIRECT_ERROR_CODE};${status};${url}`;\n error[REDIRECT_ERROR_SYMBOL] = { url, status };\n return error;\n}\n\nexport function isFarmRedirectStatus(status: unknown): status is FarmRedirectStatus {\n return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;\n}\n","import type { FarmI18nDirection, FarmI18nRouting, ResolvedFarmI18nConfig } from \"./types\";\n\nexport interface FarmLocalePathConfig {\n locales: readonly string[];\n defaultLocale: string;\n routing: FarmI18nRouting;\n}\n\nexport interface FarmLocalePathMatch {\n locale?: string;\n pathname: string;\n explicit: boolean;\n}\n\nconst RTL_LANGUAGES = new Set([\n \"ar\",\n \"arc\",\n \"ckb\",\n \"dv\",\n \"fa\",\n \"he\",\n \"ku\",\n \"nqo\",\n \"ps\",\n \"sd\",\n \"syr\",\n \"ug\",\n \"ur\",\n \"yi\",\n]);\n\nexport function resolveFarmLocalePath(\n pathname: string,\n config: FarmLocalePathConfig,\n): FarmLocalePathMatch {\n const normalized = normalizePathname(pathname);\n if (config.routing === \"none\") {\n return { pathname: normalized, explicit: false };\n }\n\n const segments = normalized.split(\"/\").filter(Boolean);\n const firstSegment = segments[0];\n const locale = config.locales.find(\n (candidate) => candidate.toLowerCase() === firstSegment?.toLowerCase(),\n );\n if (!locale) {\n return { pathname: normalized, explicit: false };\n }\n\n const remaining = segments.slice(1);\n return {\n locale,\n pathname: remaining.length > 0 ? `/${remaining.join(\"/\")}` : \"/\",\n explicit: true,\n };\n}\n\nexport function stripFarmLocaleFromPathname(\n pathname: string,\n config: FarmLocalePathConfig,\n): string {\n return resolveFarmLocalePath(pathname, config).pathname;\n}\n\nexport function localizeFarmPathname(\n pathname: string,\n locale: string,\n config: FarmLocalePathConfig,\n): string {\n const internalPathname = resolveFarmLocalePath(pathname, config).pathname;\n if (config.routing === \"none\") return internalPathname;\n if (config.routing === \"prefix-except-default\" && locale === config.defaultLocale) {\n return internalPathname;\n }\n return internalPathname === \"/\" ? `/${locale}` : `/${locale}${internalPathname}`;\n}\n\nexport function localizeFarmHref(\n href: string,\n locale: string,\n config: FarmLocalePathConfig,\n): string {\n if (!href.startsWith(\"/\") || href.startsWith(\"//\")) return href;\n const url = new URL(href, \"http://farm.local\");\n url.pathname = localizeFarmPathname(url.pathname, locale, config);\n return `${url.pathname}${url.search}${url.hash}`;\n}\n\nexport function getFarmLocaleDirection(\n locale: string,\n direction: ResolvedFarmI18nConfig[\"direction\"] | undefined,\n): FarmI18nDirection {\n const configured = direction?.[locale];\n if (configured) return configured;\n const language = locale.split(\"-\")[0]?.toLowerCase() || locale.toLowerCase();\n return RTL_LANGUAGES.has(language) ? \"rtl\" : \"ltr\";\n}\n\nfunction normalizePathname(pathname: string): string {\n const withLeadingSlash = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n if (withLeadingSlash === \"/\") return \"/\";\n return withLeadingSlash.replace(/\\/{2,}/g, \"/\").replace(/\\/$/, \"\") || \"/\";\n}\n","import { localizeFarmHref, resolveFarmLocalePath } from \"../i18n/routing\";\nimport type { ResolvedFarmI18nConfig } from \"../i18n/types\";\n\ntype ConfigRoutePatternToken =\n | { kind: \"param\"; name: string; captureIndex: number }\n | { kind: \"wildcard\"; captureIndex: number };\n\nexport interface CompiledConfigRoutePattern {\n regex: RegExp;\n tokens: ConfigRoutePatternToken[];\n}\n\nexport function resolveConfigRoutePathname(\n pathname: string,\n i18n?: ResolvedFarmI18nConfig,\n): { pathname: string; locale?: string } {\n if (!i18n?.enabled) return { pathname };\n const match = resolveFarmLocalePath(pathname, i18n);\n return { pathname: match.pathname, locale: match.locale };\n}\n\nexport function localizeConfigRouteDestination(\n destination: string,\n locale: string | undefined,\n i18n?: ResolvedFarmI18nConfig,\n): string {\n return locale && i18n?.enabled ? localizeFarmHref(destination, locale, i18n) : destination;\n}\n\nfunction escapeRegexCharacter(character: string): string {\n return /[\\\\^$.*+?()[\\]{}|]/.test(character) ? `\\\\${character}` : character;\n}\n\nexport function compileConfigRoutePattern(source: string): CompiledConfigRoutePattern {\n const tokens: ConfigRoutePatternToken[] = [];\n let pattern = \"\";\n let captureIndex = 1;\n\n for (let index = 0; index < source.length; ) {\n const rest = source.slice(index);\n const parameter = rest.match(/^:([A-Za-z0-9_]+)(\\*)?/);\n if (parameter) {\n tokens.push({ kind: \"param\", name: parameter[1], captureIndex });\n pattern += parameter[2] ? \"(.*)\" : \"([^/]+)\";\n captureIndex += 1;\n index += parameter[0].length;\n continue;\n }\n\n if (source[index] === \"*\") {\n tokens.push({ kind: \"wildcard\", captureIndex });\n pattern += \"(.*)\";\n captureIndex += 1;\n index += 1;\n continue;\n }\n\n pattern += escapeRegexCharacter(source[index]);\n index += 1;\n }\n\n return { regex: new RegExp(`^${pattern}$`), tokens };\n}\n\nexport function interpolateConfigRouteDestination(\n destination: string,\n match: RegExpMatchArray,\n tokens: readonly ConfigRoutePatternToken[],\n): string {\n const namedCaptures = new Map<string, string>();\n const wildcardCaptures: string[] = [];\n\n for (const token of tokens) {\n const value = match[token.captureIndex] || \"\";\n if (token.kind === \"param\") {\n namedCaptures.set(token.name, value);\n } else {\n wildcardCaptures.push(value);\n }\n }\n\n let result = \"\";\n let wildcardIndex = 0;\n for (let index = 0; index < destination.length; ) {\n const rest = destination.slice(index);\n const parameter = rest.match(/^:([A-Za-z0-9_]+)(\\*)?/);\n if (parameter) {\n const value = namedCaptures.get(parameter[1]);\n result += value === undefined ? parameter[0] : value;\n index += parameter[0].length;\n continue;\n }\n\n const capture = rest.match(/^\\$(\\d+)/);\n if (capture) {\n result += match[Number(capture[1])] || \"\";\n index += capture[0].length;\n continue;\n }\n\n if (destination[index] === \"*\") {\n result += wildcardCaptures[wildcardIndex] || \"\";\n wildcardIndex += 1;\n index += 1;\n continue;\n }\n\n result += destination[index];\n index += 1;\n }\n\n return result;\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { RedirectConfig } from \"../config\";\nimport type { FarmRequest, FarmResponse } from \"../types\";\nimport type { ResolvedFarmI18nConfig } from \"../i18n/types\";\nimport { isFarmRedirectStatus } from \"../navigation-errors\";\nimport {\n compileConfigRoutePattern,\n interpolateConfigRouteDestination,\n localizeConfigRouteDestination,\n resolveConfigRoutePathname,\n} from \"./route-pattern\";\n\nexport function createRedirectsPlugin(\n redirects: RedirectConfig[],\n {\n beforeRequest: overrideBeforeRequest,\n afterResponse: overrideAfterResponse,\n i18n,\n }: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n i18n?: ResolvedFarmI18nConfig;\n } = {},\n): FarmPlugin {\n for (const redirect of redirects) {\n if (redirect.statusCode !== undefined && !isFarmRedirectStatus(redirect.statusCode)) {\n throw new RangeError(\n `Redirect \"${redirect.source}\" statusCode must be one of 301, 302, 303, 307, or 308.`,\n );\n }\n }\n const compiledRedirects = redirects.map((redirect) => ({\n redirect,\n pattern: compileConfigRoutePattern(redirect.source),\n }));\n\n return {\n name: \"farm:redirects\",\n enforce: \"pre\",\n async beforeRequest(req, res, context) {\n if (overrideBeforeRequest) {\n await overrideBeforeRequest(req, res, context);\n }\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n const routePath = resolveConfigRoutePathname(url.pathname, i18n);\n const pathname = routePath.pathname;\n\n for (const { redirect, pattern } of compiledRedirects) {\n const match = pathname.match(pattern.regex);\n if (match) {\n const destination = localizeConfigRouteDestination(\n interpolateConfigRouteDestination(redirect.destination, match, pattern.tokens),\n routePath.locale,\n i18n,\n );\n\n const statusCode = redirect.statusCode ?? (redirect.permanent ? 308 : 307);\n\n res.writeHead(statusCode, {\n Location: destination,\n });\n res.end();\n return;\n }\n }\n },\n\n async afterResponse(req, res, context) {\n if (overrideAfterResponse) {\n await overrideAfterResponse(req, res, context);\n }\n },\n };\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { HeaderConfig } from \"../config\";\nimport type { FarmRequest, FarmResponse } from \"../types\";\nimport type { ResolvedFarmI18nConfig } from \"../i18n/types\";\nimport { compileConfigRoutePattern, resolveConfigRoutePathname } from \"./route-pattern\";\n\nconst FARM_CONFIG_HEADERS_FINALIZER = Symbol.for(\"farm.configHeadersFinalizer\");\n\nfunction appendConfiguredLinkHeader(res: FarmResponse, value: string): void {\n const current = res.getHeader(\"Link\");\n if (current === undefined) {\n res.setHeader(\"Link\", value);\n return;\n }\n\n const currentValue = Array.isArray(current) ? current.join(\", \") : String(current);\n if (currentValue !== value && !currentValue.endsWith(`, ${value}`)) {\n res.setHeader(\"Link\", `${currentValue}, ${value}`);\n }\n}\n\nfunction applyResponseHeaders(\n res: FarmResponse,\n matchedHeaders: readonly HeaderConfig[\"headers\"][],\n): void {\n for (const headers of matchedHeaders) {\n for (const header of headers) {\n if (header.key.toLowerCase() === \"link\") {\n appendConfiguredLinkHeader(res, header.value);\n } else {\n res.setHeader(header.key, header.value);\n }\n }\n }\n}\n\nfunction applyWriteHeadHeaders(res: FarmResponse, headers: unknown): void {\n if (Array.isArray(headers)) {\n for (let index = 0; index + 1 < headers.length; index += 2) {\n res.setHeader(String(headers[index]), headers[index + 1]);\n }\n return;\n }\n\n if (!headers || typeof headers !== \"object\") return;\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) res.setHeader(key, value);\n }\n}\n\nexport function createHeadersPlugin(\n headers: HeaderConfig[],\n {\n beforeRequest: overrideBeforeRequest,\n afterResponse: overrideAfterResponse,\n i18n,\n }: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n i18n?: ResolvedFarmI18nConfig;\n } = {},\n): FarmPlugin {\n const compiledHeaders = headers.map((config) => ({\n config,\n pattern: compileConfigRoutePattern(config.source),\n }));\n\n return {\n name: \"farm:headers\",\n enforce: \"pre\",\n\n async beforeRequest(req, res, context) {\n if (overrideBeforeRequest) {\n await overrideBeforeRequest(req, res, context);\n }\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n const pathname = resolveConfigRoutePathname(url.pathname, i18n).pathname;\n const matchedHeaders = compiledHeaders\n .filter(({ pattern }) => pattern.regex.test(pathname))\n .map(({ config }) => config.headers);\n\n applyResponseHeaders(res, matchedHeaders);\n\n const responseWithFinalizer = res as FarmResponse & {\n [FARM_CONFIG_HEADERS_FINALIZER]?: boolean;\n };\n if (matchedHeaders.length === 0 || responseWithFinalizer[FARM_CONFIG_HEADERS_FINALIZER]) {\n return;\n }\n\n responseWithFinalizer[FARM_CONFIG_HEADERS_FINALIZER] = true;\n const writeHead = res.writeHead;\n res.writeHead = ((statusCode: number, ...args: unknown[]) => {\n const statusMessage = typeof args[0] === \"string\" ? (args.shift() as string) : undefined;\n applyWriteHeadHeaders(res, args[0]);\n applyResponseHeaders(res, matchedHeaders);\n return statusMessage === undefined\n ? (writeHead as any).call(res, statusCode)\n : (writeHead as any).call(res, statusCode, statusMessage);\n }) as FarmResponse[\"writeHead\"];\n },\n\n async afterResponse(req, res, context) {\n if (overrideAfterResponse) {\n await overrideAfterResponse(req, res, context);\n }\n },\n };\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { RewriteConfig } from \"../config\";\nimport type { FarmRequest, FarmResponse } from \"../types\";\nimport type { ResolvedFarmI18nConfig } from \"../i18n/types\";\nimport {\n compileConfigRoutePattern,\n interpolateConfigRouteDestination,\n localizeConfigRouteDestination,\n resolveConfigRoutePathname,\n} from \"./route-pattern\";\n\nexport const FARM_CONFIG_REWRITES_PLUGIN_NAME = \"farm:rewrites\";\n\nexport function createRewritesPlugin(\n rewrites: RewriteConfig[],\n {\n beforeRequest: overrideBeforeRequest,\n afterResponse: overrideAfterResponse,\n i18n,\n }: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n i18n?: ResolvedFarmI18nConfig;\n } = {},\n): FarmPlugin {\n const compiledRewrites = rewrites.map((rewrite) => ({\n rewrite,\n pattern: compileConfigRoutePattern(rewrite.source),\n }));\n\n return {\n name: FARM_CONFIG_REWRITES_PLUGIN_NAME,\n enforce: \"pre\",\n\n async beforeRequest(req, res, context) {\n if (overrideBeforeRequest) {\n await overrideBeforeRequest(req, res, context);\n }\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n const routePath = resolveConfigRoutePathname(url.pathname, i18n);\n const pathname = routePath.pathname;\n\n for (const { rewrite, pattern } of compiledRewrites) {\n const match = pathname.match(pattern.regex);\n if (match) {\n const newPath = localizeConfigRouteDestination(\n interpolateConfigRouteDestination(rewrite.destination, match, pattern.tokens),\n routePath.locale,\n i18n,\n );\n const destinationUrl = new URL(newPath, url);\n if (!destinationUrl.search && url.search) {\n destinationUrl.search = url.search;\n }\n req.url =\n destinationUrl.origin === url.origin\n ? destinationUrl.pathname + destinationUrl.search\n : destinationUrl.href;\n break;\n }\n }\n },\n\n async afterResponse(req, res, context) {\n if (overrideAfterResponse) {\n await overrideAfterResponse(req, res, context);\n }\n },\n };\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { FarmRequest, FarmResponse } from \"../types\";\n\nexport function createEnvPlugin(\n env: Record<string, string>,\n {\n beforeRequest: overrideBeforeRequest,\n afterResponse: overrideAfterResponse,\n }: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n } = {},\n): FarmPlugin {\n return {\n name: \"farm:env\",\n\n configResolved(config, context) {\n for (const [key, value] of Object.entries(env)) {\n process.env[key] = value;\n }\n },\n\n async beforeRequest(req, res, context) {\n if (overrideBeforeRequest) {\n await overrideBeforeRequest(req, res, context);\n }\n },\n\n async afterResponse(req, res, context) {\n if (overrideAfterResponse) {\n await overrideAfterResponse(req, res, context);\n }\n },\n };\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { FarmRequest, FarmResponse } from \"../types\";\nimport { Readable } from \"node:stream\";\nimport { constants, createBrotliCompress, createGzip } from \"node:zlib\";\n\ntype SupportedEncoding = \"br\" | \"gzip\";\n\nconst Q_VALUE = /^(?:0(?:\\.\\d{0,3})?|1(?:\\.0{0,3})?)$/;\n\nfunction parseEncodingQuality(header: string): Map<string, number> {\n const qualities = new Map<string, number>();\n\n for (const item of header.split(\",\")) {\n const [rawEncoding, ...parameters] = item.trim().split(\";\");\n const encoding = rawEncoding?.trim().toLowerCase();\n if (!encoding) continue;\n\n let quality = 1;\n for (const parameter of parameters) {\n const [rawName, rawValue] = parameter.split(\"=\", 2);\n if (rawName?.trim().toLowerCase() !== \"q\") continue;\n const value = rawValue?.trim() ?? \"\";\n quality = Q_VALUE.test(value) ? Number(value) : 0;\n }\n\n qualities.set(encoding, Math.max(qualities.get(encoding) ?? 0, quality));\n }\n\n return qualities;\n}\n\nfunction selectEncoding(header: string): SupportedEncoding | undefined {\n if (!header.trim()) return undefined;\n\n const qualities = parseEncodingQuality(header);\n const wildcard = qualities.get(\"*\") ?? 0;\n const quality = (encoding: SupportedEncoding) => qualities.get(encoding) ?? wildcard;\n const brotli = quality(\"br\");\n const gzip = quality(\"gzip\");\n\n if (brotli <= 0 && gzip <= 0) return undefined;\n return brotli >= gzip ? \"br\" : \"gzip\";\n}\n\nfunction canCompress(request: Request, response: Response): boolean {\n if (\n request.method === \"HEAD\" ||\n !response.body ||\n response.status === 204 ||\n response.status === 205 ||\n response.status === 304 ||\n response.headers.has(\"content-encoding\") ||\n response.headers.has(\"content-range\") ||\n response.headers.get(\"cache-control\")?.toLowerCase().includes(\"no-transform\")\n ) {\n return false;\n }\n\n const contentType = response.headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n return contentType !== \"text/event-stream\";\n}\n\nfunction appendVary(headers: Headers, value: string): void {\n const current = headers.get(\"vary\");\n const values = current\n ? current\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean)\n : [];\n if (!values.some((item) => item.toLowerCase() === value.toLowerCase())) {\n values.push(value);\n }\n headers.set(\"vary\", values.join(\", \"));\n}\n\nfunction compressResponse(response: Response, encoding: SupportedEncoding): Response {\n const compressor =\n encoding === \"br\" ? createBrotliCompress() : createGzip({ flush: constants.Z_SYNC_FLUSH });\n const input = Readable.fromWeb(response.body as any);\n const output = input.pipe(compressor);\n const headers = new Headers(response.headers);\n\n headers.set(\"content-encoding\", encoding);\n headers.delete(\"content-length\");\n appendVary(headers, \"Accept-Encoding\");\n\n const etag = headers.get(\"etag\");\n if (etag && !etag.startsWith(\"W/\")) {\n headers.set(\"etag\", `W/${etag}`);\n }\n\n return new Response(Readable.toWeb(output) as ReadableStream<Uint8Array>, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n\nexport function createCompressionPlugin({\n beforeRequest: overrideBeforeRequest,\n afterResponse: overrideAfterResponse,\n}: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n} = {}): FarmPlugin {\n return {\n name: \"farm:compression\",\n enforce: \"post\",\n\n runtime: {\n after({ request, response, isProd }) {\n if (!isProd || !canCompress(request, response)) return;\n\n const encoding = selectEncoding(request.headers.get(\"accept-encoding\") ?? \"\");\n if (!encoding) return;\n return compressResponse(response, encoding);\n },\n },\n\n async beforeRequest(req, res, context) {\n if (overrideBeforeRequest) {\n await overrideBeforeRequest(req, res, context);\n }\n },\n\n async afterResponse(req, res, context) {\n if (overrideAfterResponse) {\n await overrideAfterResponse(req, res, context);\n }\n },\n };\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { FarmResponse } from \"../types\";\nimport type { FarmRequest } from \"../types\";\n\nexport function createLoggerPlugin({\n beforeRequest,\n afterResponse,\n}: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n} = {}): FarmPlugin {\n const plugin: FarmPlugin = {\n name: \"farm:logger\",\n enforce: \"post\",\n };\n\n if (beforeRequest) {\n plugin.beforeRequest = async (req, res, context) => {\n await beforeRequest(req, res, context);\n };\n }\n if (afterResponse) {\n plugin.afterResponse = async (req, res, context) => {\n await afterResponse(req, res, context);\n };\n }\n\n return plugin;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/server-plugins.ts","../src/navigation-errors.ts","../src/i18n/routing.ts","../src/plugins/route-pattern.ts","../src/plugins/redirects.ts","../src/plugins/headers.ts","../src/plugins/rewrites.ts","../src/plugins/env.ts","../src/plugins/compression.ts","../src/plugins/logger.ts"],"sourcesContent":["// Server-side plugins export\nexport {\n createRedirectsPlugin,\n createHeadersPlugin,\n createRewritesPlugin,\n createEnvPlugin,\n createCompressionPlugin,\n createLoggerPlugin,\n} from \"./plugins\";\n\nexport type {\n FarmPlugin,\n FarmPluginContext,\n FarmPluginLifecycle,\n FarmRequestPluginContext,\n FarmRequestStore,\n} from \"./plugin\";\nexport type { RedirectConfig, HeaderConfig, RewriteConfig } from \"./config\";\n","export type FarmRedirectStatus = 301 | 302 | 303 | 307 | 308;\n\nexport interface FarmRedirectSignal {\n url: string;\n status: FarmRedirectStatus;\n}\n\nconst REDIRECT_ERROR_CODE = \"FARM_REDIRECT\";\nconst NOT_FOUND_ERROR_CODE = \"FARM_NOT_FOUND\";\nconst REDIRECT_ERROR_SYMBOL = Symbol.for(\"farm.navigation.redirect\");\nconst NOT_FOUND_ERROR_SYMBOL = Symbol.for(\"farm.navigation.notFound\");\n\ntype FarmNavigationError = Error & {\n digest: string;\n [REDIRECT_ERROR_SYMBOL]?: FarmRedirectSignal;\n [NOT_FOUND_ERROR_SYMBOL]?: true;\n};\n\nexport function redirect(url: string, status: FarmRedirectStatus = 307): never {\n throw createRedirectError(url, status);\n}\n\nexport function permanentRedirect(url: string): never {\n redirect(url, 308);\n}\n\nexport function notFound(): never {\n const error = new Error(NOT_FOUND_ERROR_CODE) as FarmNavigationError;\n error.digest = NOT_FOUND_ERROR_CODE;\n error[NOT_FOUND_ERROR_SYMBOL] = true;\n throw error;\n}\n\nexport function isFarmRedirectError(error: unknown): boolean {\n return Boolean(getFarmRedirectError(error));\n}\n\nexport function getFarmRedirectError(error: unknown): FarmRedirectSignal | null {\n if (!error || typeof error !== \"object\") return null;\n const candidate = error as Partial<FarmNavigationError>;\n if (candidate[REDIRECT_ERROR_SYMBOL]) {\n return candidate[REDIRECT_ERROR_SYMBOL] as FarmRedirectSignal;\n }\n\n if (\n typeof candidate.digest === \"string\" &&\n candidate.digest.startsWith(`${REDIRECT_ERROR_CODE};`)\n ) {\n const [, status, ...urlParts] = candidate.digest.split(\";\");\n const parsedStatus = Number(status);\n if (isFarmRedirectStatus(parsedStatus)) {\n return {\n status: parsedStatus,\n url: urlParts.join(\";\"),\n };\n }\n }\n\n return null;\n}\n\nexport function isFarmNotFoundError(error: unknown): boolean {\n if (!error || typeof error !== \"object\") return false;\n const candidate = error as Partial<FarmNavigationError>;\n return Boolean(candidate[NOT_FOUND_ERROR_SYMBOL] || candidate.digest === NOT_FOUND_ERROR_CODE);\n}\n\nfunction createRedirectError(url: string, status: FarmRedirectStatus): FarmNavigationError {\n const error = new Error(`${REDIRECT_ERROR_CODE};${status};${url}`) as FarmNavigationError;\n error.digest = `${REDIRECT_ERROR_CODE};${status};${url}`;\n error[REDIRECT_ERROR_SYMBOL] = { url, status };\n return error;\n}\n\nexport function isFarmRedirectStatus(status: unknown): status is FarmRedirectStatus {\n return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;\n}\n","import type { FarmI18nDirection, FarmI18nRouting, ResolvedFarmI18nConfig } from \"./types\";\n\nexport interface FarmLocalePathConfig {\n locales: readonly string[];\n defaultLocale: string;\n routing: FarmI18nRouting;\n}\n\nexport interface FarmLocalePathMatch {\n locale?: string;\n pathname: string;\n explicit: boolean;\n}\n\nconst RTL_LANGUAGES = new Set([\n \"ar\",\n \"arc\",\n \"ckb\",\n \"dv\",\n \"fa\",\n \"he\",\n \"ku\",\n \"nqo\",\n \"ps\",\n \"sd\",\n \"syr\",\n \"ug\",\n \"ur\",\n \"yi\",\n]);\n\nexport function resolveFarmLocalePath(\n pathname: string,\n config: FarmLocalePathConfig,\n): FarmLocalePathMatch {\n const normalized = normalizePathname(pathname);\n if (config.routing === \"none\") {\n return { pathname: normalized, explicit: false };\n }\n\n const segments = normalized.split(\"/\").filter(Boolean);\n const firstSegment = segments[0];\n const locale = config.locales.find(\n (candidate) => candidate.toLowerCase() === firstSegment?.toLowerCase(),\n );\n if (!locale) {\n return { pathname: normalized, explicit: false };\n }\n\n const remaining = segments.slice(1);\n return {\n locale,\n pathname: remaining.length > 0 ? `/${remaining.join(\"/\")}` : \"/\",\n explicit: true,\n };\n}\n\nexport function stripFarmLocaleFromPathname(\n pathname: string,\n config: FarmLocalePathConfig,\n): string {\n return resolveFarmLocalePath(pathname, config).pathname;\n}\n\nexport function localizeFarmPathname(\n pathname: string,\n locale: string,\n config: FarmLocalePathConfig,\n): string {\n const internalPathname = resolveFarmLocalePath(pathname, config).pathname;\n if (config.routing === \"none\") return internalPathname;\n if (config.routing === \"prefix-except-default\" && locale === config.defaultLocale) {\n return internalPathname;\n }\n return internalPathname === \"/\" ? `/${locale}` : `/${locale}${internalPathname}`;\n}\n\nexport function localizeFarmHref(\n href: string,\n locale: string,\n config: FarmLocalePathConfig,\n): string {\n if (!href.startsWith(\"/\") || href.startsWith(\"//\")) return href;\n const url = new URL(href, \"http://farm.local\");\n url.pathname = localizeFarmPathname(url.pathname, locale, config);\n return `${url.pathname}${url.search}${url.hash}`;\n}\n\nexport function getFarmLocaleDirection(\n locale: string,\n direction: ResolvedFarmI18nConfig[\"direction\"] | undefined,\n): FarmI18nDirection {\n const configured = direction?.[locale];\n if (configured) return configured;\n const language = locale.split(\"-\")[0]?.toLowerCase() || locale.toLowerCase();\n return RTL_LANGUAGES.has(language) ? \"rtl\" : \"ltr\";\n}\n\nfunction normalizePathname(pathname: string): string {\n const withLeadingSlash = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n if (withLeadingSlash === \"/\") return \"/\";\n return withLeadingSlash.replace(/\\/{2,}/g, \"/\").replace(/\\/$/, \"\") || \"/\";\n}\n","import { localizeFarmHref, resolveFarmLocalePath } from \"../i18n/routing\";\nimport type { ResolvedFarmI18nConfig } from \"../i18n/types\";\n\ntype ConfigRoutePatternToken =\n | { kind: \"param\"; name: string; captureIndex: number }\n | { kind: \"wildcard\"; captureIndex: number };\n\nexport interface CompiledConfigRoutePattern {\n regex: RegExp;\n tokens: ConfigRoutePatternToken[];\n}\n\nexport function resolveConfigRoutePathname(\n pathname: string,\n i18n?: ResolvedFarmI18nConfig,\n): { pathname: string; locale?: string } {\n if (!i18n?.enabled) return { pathname };\n const match = resolveFarmLocalePath(pathname, i18n);\n return { pathname: match.pathname, locale: match.locale };\n}\n\nexport function localizeConfigRouteDestination(\n destination: string,\n locale: string | undefined,\n i18n?: ResolvedFarmI18nConfig,\n): string {\n return locale && i18n?.enabled ? localizeFarmHref(destination, locale, i18n) : destination;\n}\n\nfunction escapeRegexCharacter(character: string): string {\n return /[\\\\^$.*+?()[\\]{}|]/.test(character) ? `\\\\${character}` : character;\n}\n\nexport function compileConfigRoutePattern(source: string): CompiledConfigRoutePattern {\n const tokens: ConfigRoutePatternToken[] = [];\n let pattern = \"\";\n let captureIndex = 1;\n\n for (let index = 0; index < source.length; ) {\n const rest = source.slice(index);\n const parameter = rest.match(/^:([A-Za-z0-9_]+)(\\*)?/);\n if (parameter) {\n tokens.push({ kind: \"param\", name: parameter[1], captureIndex });\n pattern += parameter[2] ? \"(.*)\" : \"([^/]+)\";\n captureIndex += 1;\n index += parameter[0].length;\n continue;\n }\n\n if (source[index] === \"*\") {\n tokens.push({ kind: \"wildcard\", captureIndex });\n pattern += \"(.*)\";\n captureIndex += 1;\n index += 1;\n continue;\n }\n\n pattern += escapeRegexCharacter(source[index]);\n index += 1;\n }\n\n return { regex: new RegExp(`^${pattern}$`), tokens };\n}\n\nexport function interpolateConfigRouteDestination(\n destination: string,\n match: RegExpMatchArray,\n tokens: readonly ConfigRoutePatternToken[],\n): string {\n const namedCaptures = new Map<string, string>();\n const wildcardCaptures: string[] = [];\n\n for (const token of tokens) {\n const value = match[token.captureIndex] || \"\";\n if (token.kind === \"param\") {\n namedCaptures.set(token.name, value);\n } else {\n wildcardCaptures.push(value);\n }\n }\n\n let result = \"\";\n let wildcardIndex = 0;\n for (let index = 0; index < destination.length; ) {\n const rest = destination.slice(index);\n const parameter = rest.match(/^:([A-Za-z0-9_]+)(\\*)?/);\n if (parameter) {\n const value = namedCaptures.get(parameter[1]);\n result += value === undefined ? parameter[0] : value;\n index += parameter[0].length;\n continue;\n }\n\n const capture = rest.match(/^\\$(\\d+)/);\n if (capture) {\n result += match[Number(capture[1])] || \"\";\n index += capture[0].length;\n continue;\n }\n\n if (destination[index] === \"*\") {\n result += wildcardCaptures[wildcardIndex] || \"\";\n wildcardIndex += 1;\n index += 1;\n continue;\n }\n\n result += destination[index];\n index += 1;\n }\n\n return result;\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { RedirectConfig } from \"../config\";\nimport type { FarmRequest, FarmResponse } from \"../types\";\nimport type { ResolvedFarmI18nConfig } from \"../i18n/types\";\nimport { isFarmRedirectStatus } from \"../navigation-errors\";\nimport {\n compileConfigRoutePattern,\n interpolateConfigRouteDestination,\n localizeConfigRouteDestination,\n resolveConfigRoutePathname,\n} from \"./route-pattern\";\n\nexport function createRedirectsPlugin(\n redirects: RedirectConfig[],\n {\n beforeRequest: overrideBeforeRequest,\n afterResponse: overrideAfterResponse,\n i18n,\n }: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n i18n?: ResolvedFarmI18nConfig;\n } = {},\n): FarmPlugin {\n for (const redirect of redirects) {\n if (redirect.statusCode !== undefined && !isFarmRedirectStatus(redirect.statusCode)) {\n throw new RangeError(\n `Redirect \"${redirect.source}\" statusCode must be one of 301, 302, 303, 307, or 308.`,\n );\n }\n }\n const compiledRedirects = redirects.map((redirect) => ({\n redirect,\n pattern: compileConfigRoutePattern(redirect.source),\n }));\n\n return {\n name: \"farm:redirects\",\n enforce: \"pre\",\n async beforeRequest(req, res, context) {\n if (overrideBeforeRequest) {\n await overrideBeforeRequest(req, res, context);\n }\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n const routePath = resolveConfigRoutePathname(url.pathname, i18n);\n const pathname = routePath.pathname;\n\n for (const { redirect, pattern } of compiledRedirects) {\n const match = pathname.match(pattern.regex);\n if (match) {\n const destination = localizeConfigRouteDestination(\n interpolateConfigRouteDestination(redirect.destination, match, pattern.tokens),\n routePath.locale,\n i18n,\n );\n\n const statusCode = redirect.statusCode ?? (redirect.permanent ? 308 : 307);\n\n res.writeHead(statusCode, {\n Location: destination,\n });\n res.end();\n return;\n }\n }\n },\n\n async afterResponse(req, res, context) {\n if (overrideAfterResponse) {\n await overrideAfterResponse(req, res, context);\n }\n },\n };\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { HeaderConfig } from \"../config\";\nimport type { FarmRequest, FarmResponse } from \"../types\";\nimport type { ResolvedFarmI18nConfig } from \"../i18n/types\";\nimport { compileConfigRoutePattern, resolveConfigRoutePathname } from \"./route-pattern\";\n\nconst FARM_CONFIG_HEADERS_FINALIZER = Symbol.for(\"farm.configHeadersFinalizer\");\n\nfunction appendConfiguredLinkHeader(res: FarmResponse, value: string): void {\n const current = res.getHeader(\"Link\");\n if (current === undefined) {\n res.setHeader(\"Link\", value);\n return;\n }\n\n const currentValue = Array.isArray(current) ? current.join(\", \") : String(current);\n if (currentValue !== value && !currentValue.endsWith(`, ${value}`)) {\n res.setHeader(\"Link\", `${currentValue}, ${value}`);\n }\n}\n\nfunction applyResponseHeaders(\n res: FarmResponse,\n matchedHeaders: readonly HeaderConfig[\"headers\"][],\n): void {\n for (const headers of matchedHeaders) {\n for (const header of headers) {\n if (header.key.toLowerCase() === \"link\") {\n appendConfiguredLinkHeader(res, header.value);\n } else {\n res.setHeader(header.key, header.value);\n }\n }\n }\n}\n\nfunction applyWriteHeadHeaders(res: FarmResponse, headers: unknown): void {\n if (Array.isArray(headers)) {\n for (let index = 0; index + 1 < headers.length; index += 2) {\n res.setHeader(String(headers[index]), headers[index + 1]);\n }\n return;\n }\n\n if (!headers || typeof headers !== \"object\") return;\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) res.setHeader(key, value);\n }\n}\n\nexport function createHeadersPlugin(\n headers: HeaderConfig[],\n {\n beforeRequest: overrideBeforeRequest,\n afterResponse: overrideAfterResponse,\n i18n,\n }: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n i18n?: ResolvedFarmI18nConfig;\n } = {},\n): FarmPlugin {\n const compiledHeaders = headers.map((config) => ({\n config,\n pattern: compileConfigRoutePattern(config.source),\n }));\n\n return {\n name: \"farm:headers\",\n enforce: \"pre\",\n\n async beforeRequest(req, res, context) {\n if (overrideBeforeRequest) {\n await overrideBeforeRequest(req, res, context);\n }\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n const pathname = resolveConfigRoutePathname(url.pathname, i18n).pathname;\n const matchedHeaders = compiledHeaders\n .filter(({ pattern }) => pattern.regex.test(pathname))\n .map(({ config }) => config.headers);\n\n applyResponseHeaders(res, matchedHeaders);\n\n const responseWithFinalizer = res as FarmResponse & {\n [FARM_CONFIG_HEADERS_FINALIZER]?: boolean;\n };\n if (matchedHeaders.length === 0 || responseWithFinalizer[FARM_CONFIG_HEADERS_FINALIZER]) {\n return;\n }\n\n responseWithFinalizer[FARM_CONFIG_HEADERS_FINALIZER] = true;\n const writeHead = res.writeHead;\n res.writeHead = ((statusCode: number, ...args: unknown[]) => {\n const statusMessage = typeof args[0] === \"string\" ? (args.shift() as string) : undefined;\n applyWriteHeadHeaders(res, args[0]);\n applyResponseHeaders(res, matchedHeaders);\n return statusMessage === undefined\n ? (writeHead as any).call(res, statusCode)\n : (writeHead as any).call(res, statusCode, statusMessage);\n }) as FarmResponse[\"writeHead\"];\n },\n\n async afterResponse(req, res, context) {\n if (overrideAfterResponse) {\n await overrideAfterResponse(req, res, context);\n }\n },\n };\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { RewriteConfig } from \"../config\";\nimport type { FarmRequest, FarmResponse } from \"../types\";\nimport type { ResolvedFarmI18nConfig } from \"../i18n/types\";\nimport {\n compileConfigRoutePattern,\n interpolateConfigRouteDestination,\n localizeConfigRouteDestination,\n resolveConfigRoutePathname,\n} from \"./route-pattern\";\n\nexport const FARM_CONFIG_REWRITES_PLUGIN_NAME = \"farm:rewrites\";\n\nexport function createRewritesPlugin(\n rewrites: RewriteConfig[],\n {\n beforeRequest: overrideBeforeRequest,\n afterResponse: overrideAfterResponse,\n i18n,\n }: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n i18n?: ResolvedFarmI18nConfig;\n } = {},\n): FarmPlugin {\n const compiledRewrites = rewrites.map((rewrite) => ({\n rewrite,\n pattern: compileConfigRoutePattern(rewrite.source),\n }));\n\n return {\n name: FARM_CONFIG_REWRITES_PLUGIN_NAME,\n enforce: \"pre\",\n\n async beforeRequest(req, res, context) {\n if (overrideBeforeRequest) {\n await overrideBeforeRequest(req, res, context);\n }\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n const routePath = resolveConfigRoutePathname(url.pathname, i18n);\n const pathname = routePath.pathname;\n\n for (const { rewrite, pattern } of compiledRewrites) {\n const match = pathname.match(pattern.regex);\n if (match) {\n const newPath = localizeConfigRouteDestination(\n interpolateConfigRouteDestination(rewrite.destination, match, pattern.tokens),\n routePath.locale,\n i18n,\n );\n const destinationUrl = new URL(newPath, url);\n if (!destinationUrl.search && url.search) {\n destinationUrl.search = url.search;\n }\n req.url =\n destinationUrl.origin === url.origin\n ? destinationUrl.pathname + destinationUrl.search\n : destinationUrl.href;\n break;\n }\n }\n },\n\n async afterResponse(req, res, context) {\n if (overrideAfterResponse) {\n await overrideAfterResponse(req, res, context);\n }\n },\n };\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { FarmRequest, FarmResponse } from \"../types\";\n\nexport function createEnvPlugin(\n env: Record<string, string>,\n {\n beforeRequest: overrideBeforeRequest,\n afterResponse: overrideAfterResponse,\n }: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n } = {},\n): FarmPlugin {\n return {\n name: \"farm:env\",\n\n configResolved(config, context) {\n for (const [key, value] of Object.entries(env)) {\n process.env[key] = value;\n }\n },\n\n async beforeRequest(req, res, context) {\n if (overrideBeforeRequest) {\n await overrideBeforeRequest(req, res, context);\n }\n },\n\n async afterResponse(req, res, context) {\n if (overrideAfterResponse) {\n await overrideAfterResponse(req, res, context);\n }\n },\n };\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { FarmRequest, FarmResponse } from \"../types\";\nimport { Readable } from \"node:stream\";\nimport { constants, createBrotliCompress, createGzip } from \"node:zlib\";\n\ntype SupportedEncoding = \"br\" | \"gzip\";\n\nconst Q_VALUE = /^(?:0(?:\\.\\d{0,3})?|1(?:\\.0{0,3})?)$/;\n\nfunction parseEncodingQuality(header: string): Map<string, number> {\n const qualities = new Map<string, number>();\n\n for (const item of header.split(\",\")) {\n const [rawEncoding, ...parameters] = item.trim().split(\";\");\n const encoding = rawEncoding?.trim().toLowerCase();\n if (!encoding) continue;\n\n let quality = 1;\n for (const parameter of parameters) {\n const [rawName, rawValue] = parameter.split(\"=\", 2);\n if (rawName?.trim().toLowerCase() !== \"q\") continue;\n const value = rawValue?.trim() ?? \"\";\n quality = Q_VALUE.test(value) ? Number(value) : 0;\n }\n\n qualities.set(encoding, Math.max(qualities.get(encoding) ?? 0, quality));\n }\n\n return qualities;\n}\n\nfunction selectEncoding(header: string): SupportedEncoding | undefined {\n if (!header.trim()) return undefined;\n\n const qualities = parseEncodingQuality(header);\n const wildcard = qualities.get(\"*\") ?? 0;\n const quality = (encoding: SupportedEncoding) => qualities.get(encoding) ?? wildcard;\n const brotli = quality(\"br\");\n const gzip = quality(\"gzip\");\n\n if (brotli <= 0 && gzip <= 0) return undefined;\n return brotli >= gzip ? \"br\" : \"gzip\";\n}\n\nfunction isCompressionEligible(request: Request, response: Response): boolean {\n if (\n (request.method !== \"HEAD\" && !response.body) ||\n response.status === 204 ||\n response.status === 205 ||\n response.status === 304 ||\n response.headers.has(\"content-encoding\") ||\n response.headers.has(\"content-range\") ||\n response.headers.get(\"cache-control\")?.toLowerCase().includes(\"no-transform\")\n ) {\n return false;\n }\n\n const contentType = response.headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n return contentType !== \"text/event-stream\";\n}\n\nfunction appendVary(headers: Headers, value: string): void {\n const current = headers.get(\"vary\");\n const values = current\n ? current\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean)\n : [];\n if (values.includes(\"*\")) return;\n if (!values.some((item) => item.toLowerCase() === value.toLowerCase())) {\n values.push(value);\n }\n headers.set(\"vary\", values.join(\", \"));\n}\n\nfunction compressResponse(response: Response, encoding: SupportedEncoding): Response {\n const compressor =\n encoding === \"br\" ? createBrotliCompress() : createGzip({ flush: constants.Z_SYNC_FLUSH });\n const input = Readable.fromWeb(response.body as any);\n const output = input.pipe(compressor);\n const headers = new Headers(response.headers);\n\n headers.set(\"content-encoding\", encoding);\n headers.delete(\"content-length\");\n appendVary(headers, \"Accept-Encoding\");\n\n const etag = headers.get(\"etag\");\n if (etag && !etag.startsWith(\"W/\")) {\n headers.set(\"etag\", `W/${etag}`);\n }\n\n return new Response(Readable.toWeb(output) as ReadableStream<Uint8Array>, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n\nfunction varyIdentityResponse(response: Response): Response {\n const headers = new Headers(response.headers);\n appendVary(headers, \"Accept-Encoding\");\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n\nexport function createCompressionPlugin({\n beforeRequest: overrideBeforeRequest,\n afterResponse: overrideAfterResponse,\n}: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n} = {}): FarmPlugin {\n return {\n name: \"farm:compression\",\n enforce: \"post\",\n\n runtime: {\n after({ request, response, isProd }) {\n if (!isProd || !isCompressionEligible(request, response)) return;\n\n if (request.method === \"HEAD\") return varyIdentityResponse(response);\n\n const encoding = selectEncoding(request.headers.get(\"accept-encoding\") ?? \"\");\n if (!encoding) return varyIdentityResponse(response);\n return compressResponse(response, encoding);\n },\n },\n\n async beforeRequest(req, res, context) {\n if (overrideBeforeRequest) {\n await overrideBeforeRequest(req, res, context);\n }\n },\n\n async afterResponse(req, res, context) {\n if (overrideAfterResponse) {\n await overrideAfterResponse(req, res, context);\n }\n },\n };\n}\n","import type { FarmPlugin, FarmPluginContext } from \"../plugin\";\nimport type { FarmResponse } from \"../types\";\nimport type { FarmRequest } from \"../types\";\n\nexport function createLoggerPlugin({\n beforeRequest,\n afterResponse,\n}: {\n beforeRequest?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n afterResponse?: (\n req: FarmRequest,\n res: FarmResponse,\n context: FarmPluginContext,\n ) => void | Promise<void>;\n} = {}): FarmPlugin {\n const plugin: FarmPlugin = {\n name: \"farm:logger\",\n enforce: \"post\",\n };\n\n if (beforeRequest) {\n plugin.beforeRequest = async (req, res, context) => {\n await beforeRequest(req, res, context);\n };\n }\n if (afterResponse) {\n plugin.afterResponse = async (req, res, context) => {\n await afterResponse(req, res, context);\n };\n }\n\n return plugin;\n}\n"],"mappings":"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","names":[]}
@@ -4,7 +4,7 @@ import {
4
4
  createHeadersPlugin,
5
5
  createLoggerPlugin,
6
6
  createRedirectsPlugin
7
- } from "./chunk-UGK7TW2V.mjs";
7
+ } from "./chunk-P57PPOQL.mjs";
8
8
  import {
9
9
  createRewritesPlugin
10
10
  } from "./chunk-I3FMD5VJ.mjs";
package/dist/server.cjs CHANGED
@@ -18702,6 +18702,25 @@ var fs4 = __toESM(require("fs"));
18702
18702
  var path12 = __toESM(require("path"));
18703
18703
  init_utils();
18704
18704
 
18705
+ // src/server/dev-styles.ts
18706
+ var STYLE_EXTENSIONS = /\.(css|scss|sass|less|styl|stylus)(?:$|\?)/;
18707
+ var ALREADY_LINKED = "/src/app/globals.css";
18708
+ function collectDevStylesheetUrls(modules) {
18709
+ const seen = /* @__PURE__ */ new Set();
18710
+ for (const mod of modules) {
18711
+ const id = mod.id ?? mod.file ?? "";
18712
+ if (!STYLE_EXTENSIONS.test(id)) continue;
18713
+ if (id.includes(".module.")) continue;
18714
+ const url = mod.url;
18715
+ if (!url || !url.startsWith("/")) continue;
18716
+ const bare = url.split("?")[0];
18717
+ if (bare === ALREADY_LINKED) continue;
18718
+ seen.add(bare);
18719
+ }
18720
+ return [...seen].sort();
18721
+ }
18722
+ __name(collectDevStylesheetUrls, "collectDevStylesheetUrls");
18723
+
18705
18724
  // src/server/full-document.ts
18706
18725
  function stripContentsWrappers(markup) {
18707
18726
  let out = markup.replace(/^\s+/, "");
@@ -22177,7 +22196,7 @@ _setFarmThemeServerSnapshotResolver(() => {
22177
22196
  });
22178
22197
 
22179
22198
  // package.json
22180
- var version = "0.1.0-beta.75";
22199
+ var version = "0.1.0-beta.76";
22181
22200
 
22182
22201
  // src/version.ts
22183
22202
  var FARM_VERSION = version;
@@ -22548,6 +22567,18 @@ var _ServerRenderer = class _ServerRenderer {
22548
22567
  pageElement
22549
22568
  );
22550
22569
  }
22570
+ /// Stylesheets the app imported through JS (fontsource packages, component
22571
+ /// CSS) that the document must link alongside globals.css — see #658.
22572
+ collectDevStyleHrefs() {
22573
+ const graph = this.viteServer?.moduleGraph;
22574
+ if (!graph) return [];
22575
+ return collectDevStylesheetUrls(graph.idToModuleMap.values());
22576
+ }
22577
+ collectDevStyleLinks() {
22578
+ return this.collectDevStyleHrefs().map(
22579
+ (href) => `<link rel="stylesheet" href="${escapeHtmlAttribute2(href)}">`
22580
+ );
22581
+ }
22551
22582
  createLayoutBoundary(pattern, layoutElement) {
22552
22583
  return this.rendererRuntime.createElement(
22553
22584
  "div",
@@ -23762,7 +23793,11 @@ var _ServerRenderer = class _ServerRenderer {
23762
23793
  {
23763
23794
  tag: "link",
23764
23795
  attrs: { rel: "stylesheet", href: "/src/app/globals.css" }
23765
- }
23796
+ },
23797
+ ...this.collectDevStyleHrefs().map((href) => ({
23798
+ tag: "link",
23799
+ attrs: { rel: "stylesheet", href }
23800
+ }))
23766
23801
  ]
23767
23802
  };
23768
23803
  for (const routeEntry of manifest.routes) {
@@ -23858,6 +23893,7 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
23858
23893
  rendererHead,
23859
23894
  renderFarmFontDevHead(this.config.root || process.cwd()),
23860
23895
  `<link rel="stylesheet" href="/src/app/globals.css">`,
23896
+ ...this.collectDevStyleLinks(),
23861
23897
  `<script type="module" src="/@vite/client"></script>`,
23862
23898
  rendererHydrationScript,
23863
23899
  bootstrapScript
@@ -23876,7 +23912,8 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
23876
23912
  ${documentTitleTag}${metaTags}${alternateTags}${rendererHead ? `
23877
23913
  ${rendererHead}` : ""}
23878
23914
  ${renderFarmFontDevHead(this.config.root || process.cwd())}
23879
- <link rel="stylesheet" href="/src/app/globals.css">
23915
+ <link rel="stylesheet" href="/src/app/globals.css">${this.collectDevStyleLinks().map((l) => `
23916
+ ${l}`).join("")}
23880
23917
  <script type="module" src="/@vite/client"></script>
23881
23918
  ${rendererHydrationScript}
23882
23919
  ${bootstrapScript}
@@ -23941,7 +23978,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
23941
23978
  {
23942
23979
  tag: "link",
23943
23980
  attrs: { rel: "stylesheet", href: "/src/app/globals.css" }
23944
- }
23981
+ },
23982
+ ...this.collectDevStyleHrefs().map((href) => ({
23983
+ tag: "link",
23984
+ attrs: { rel: "stylesheet", href }
23985
+ }))
23945
23986
  ]
23946
23987
  };
23947
23988
  for (const routeEntry of manifest.routes) {
@@ -24028,6 +24069,7 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
24028
24069
  { style: { display: "contents" } },
24029
24070
  element
24030
24071
  );
24072
+ const devStyleLinks = this.collectDevStyleLinks();
24031
24073
  const { pipe } = renderToPipeableStream2(streamRoot, {
24032
24074
  onShellReady() {
24033
24075
  const shellReadyMs = Date.now() - streamStartTime;
@@ -24049,7 +24091,8 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
24049
24091
  ${hasFavicon ? "" : '<link rel="icon" href="data:,">'}
24050
24092
  <title>${title}</title>${metaTags}${i18nAlternateTags}
24051
24093
  ${fontHead}
24052
- <link rel="stylesheet" href="/src/app/globals.css" />
24094
+ <link rel="stylesheet" href="/src/app/globals.css" />${devStyleLinks.map((l) => `
24095
+ ${l}`).join("")}
24053
24096
  <script type="module" src="/@vite/client"></script>
24054
24097
  ${propsScript}
24055
24098
  ${hydrationClickQueueScript}
@@ -24276,6 +24319,7 @@ ${i18nSnapshot ? `window.__FARM_I18N__ = ${serializeInlineValue2(i18nSnapshot)};
24276
24319
  alternateLinks,
24277
24320
  fontHead,
24278
24321
  `<link rel="stylesheet" href="/src/app/globals.css" />`,
24322
+ ...this.collectDevStyleLinks(),
24279
24323
  `<script type="module" src="/@vite/client"></script>`,
24280
24324
  rendererHydrationScript,
24281
24325
  integrationManifestScript
@@ -24293,7 +24337,8 @@ ${i18nSnapshot ? `window.__FARM_I18N__ = ${serializeInlineValue2(i18nSnapshot)};
24293
24337
  <link rel="icon" href="data:,">
24294
24338
  <title>${escapeHtmlAttribute2(documentTitle)}</title>${alternateLinks}
24295
24339
  ${fontHead}
24296
- <link rel="stylesheet" href="/src/app/globals.css" />
24340
+ <link rel="stylesheet" href="/src/app/globals.css" />${this.collectDevStyleLinks().map((l) => `
24341
+ ${l}`).join("")}
24297
24342
  <script type="module" src="/@vite/client"></script>
24298
24343
  ${rendererHydrationScript}
24299
24344
  ${integrationManifestScript}
@@ -33510,17 +33555,18 @@ function selectEncoding(header) {
33510
33555
  return brotli >= gzip ? "br" : "gzip";
33511
33556
  }
33512
33557
  __name(selectEncoding, "selectEncoding");
33513
- function canCompress(request, response) {
33514
- if (request.method === "HEAD" || !response.body || response.status === 204 || response.status === 205 || response.status === 304 || response.headers.has("content-encoding") || response.headers.has("content-range") || response.headers.get("cache-control")?.toLowerCase().includes("no-transform")) {
33558
+ function isCompressionEligible(request, response) {
33559
+ if (request.method !== "HEAD" && !response.body || response.status === 204 || response.status === 205 || response.status === 304 || response.headers.has("content-encoding") || response.headers.has("content-range") || response.headers.get("cache-control")?.toLowerCase().includes("no-transform")) {
33515
33560
  return false;
33516
33561
  }
33517
33562
  const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
33518
33563
  return contentType !== "text/event-stream";
33519
33564
  }
33520
- __name(canCompress, "canCompress");
33565
+ __name(isCompressionEligible, "isCompressionEligible");
33521
33566
  function appendVary(headers, value) {
33522
33567
  const current = headers.get("vary");
33523
33568
  const values = current ? current.split(",").map((item) => item.trim()).filter(Boolean) : [];
33569
+ if (values.includes("*")) return;
33524
33570
  if (!values.some((item) => item.toLowerCase() === value.toLowerCase())) {
33525
33571
  values.push(value);
33526
33572
  }
@@ -33546,6 +33592,16 @@ function compressResponse(response, encoding) {
33546
33592
  });
33547
33593
  }
33548
33594
  __name(compressResponse, "compressResponse");
33595
+ function varyIdentityResponse(response) {
33596
+ const headers = new Headers(response.headers);
33597
+ appendVary(headers, "Accept-Encoding");
33598
+ return new Response(response.body, {
33599
+ status: response.status,
33600
+ statusText: response.statusText,
33601
+ headers
33602
+ });
33603
+ }
33604
+ __name(varyIdentityResponse, "varyIdentityResponse");
33549
33605
  function createCompressionPlugin({
33550
33606
  beforeRequest: overrideBeforeRequest,
33551
33607
  afterResponse: overrideAfterResponse
@@ -33555,9 +33611,10 @@ function createCompressionPlugin({
33555
33611
  enforce: "post",
33556
33612
  runtime: {
33557
33613
  after({ request, response, isProd }) {
33558
- if (!isProd || !canCompress(request, response)) return;
33614
+ if (!isProd || !isCompressionEligible(request, response)) return;
33615
+ if (request.method === "HEAD") return varyIdentityResponse(response);
33559
33616
  const encoding = selectEncoding(request.headers.get("accept-encoding") ?? "");
33560
- if (!encoding) return;
33617
+ if (!encoding) return varyIdentityResponse(response);
33561
33618
  return compressResponse(response, encoding);
33562
33619
  }
33563
33620
  },