@alxia/react-router 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +370 -20
  2. package/dist/chunks/index-5rt6hnhn.js +42 -0
  3. package/dist/chunks/index-5rt6hnhn.js.map +11 -0
  4. package/dist/chunks/{index-mppz0806.js → index-6jf7zpxz.js} +5 -5
  5. package/dist/chunks/{index-mppz0806.js.map → index-6jf7zpxz.js.map} +1 -1
  6. package/dist/chunks/index-y6vpnjr3.js +123 -0
  7. package/dist/chunks/index-y6vpnjr3.js.map +14 -0
  8. package/dist/context.d.ts +0 -7
  9. package/dist/context.d.ts.map +1 -1
  10. package/dist/document/index.d.ts +11 -0
  11. package/dist/document/index.d.ts.map +1 -0
  12. package/dist/document/index.js +19 -0
  13. package/dist/document/index.js.map +9 -0
  14. package/dist/document/inline-script.d.ts +27 -0
  15. package/dist/document/inline-script.d.ts.map +1 -0
  16. package/dist/document/language-store.d.ts +8 -0
  17. package/dist/document/language-store.d.ts.map +1 -0
  18. package/dist/document/nonce.d.ts +34 -0
  19. package/dist/document/nonce.d.ts.map +1 -0
  20. package/dist/document/page-language.d.ts +39 -0
  21. package/dist/document/page-language.d.ts.map +1 -0
  22. package/dist/document/text-direction.d.ts +10 -0
  23. package/dist/document/text-direction.d.ts.map +1 -0
  24. package/dist/entry/abort.d.ts +21 -0
  25. package/dist/entry/abort.d.ts.map +1 -0
  26. package/dist/entry/create-entry.d.ts +32 -0
  27. package/dist/entry/create-entry.d.ts.map +1 -0
  28. package/dist/entry/index.d.ts +8 -0
  29. package/dist/entry/index.d.ts.map +1 -0
  30. package/dist/entry/index.js +128 -0
  31. package/dist/entry/index.js.map +12 -0
  32. package/dist/entry/whole-page.d.ts +10 -0
  33. package/dist/entry/whole-page.d.ts.map +1 -0
  34. package/dist/errors/api-error.d.ts +86 -0
  35. package/dist/errors/api-error.d.ts.map +1 -0
  36. package/dist/errors/api-errors.d.ts +96 -0
  37. package/dist/errors/api-errors.d.ts.map +1 -0
  38. package/dist/errors/call-failure.d.ts +45 -0
  39. package/dist/errors/call-failure.d.ts.map +1 -0
  40. package/dist/errors/catalogue-check.d.ts +24 -0
  41. package/dist/errors/catalogue-check.d.ts.map +1 -0
  42. package/dist/errors/catalogue-read.d.ts +33 -0
  43. package/dist/errors/catalogue-read.d.ts.map +1 -0
  44. package/dist/errors/catalogue.d.ts +59 -0
  45. package/dist/errors/catalogue.d.ts.map +1 -0
  46. package/dist/errors/code-resolver.d.ts +44 -0
  47. package/dist/errors/code-resolver.d.ts.map +1 -0
  48. package/dist/errors/describe-refusal.d.ts +6 -0
  49. package/dist/errors/describe-refusal.d.ts.map +1 -0
  50. package/dist/errors/describe.d.ts +3 -0
  51. package/dist/errors/describe.d.ts.map +1 -0
  52. package/dist/errors/fetcher-result.d.ts +21 -0
  53. package/dist/errors/fetcher-result.d.ts.map +1 -0
  54. package/dist/errors/form-errors.d.ts +8 -0
  55. package/dist/errors/form-errors.d.ts.map +1 -0
  56. package/dist/errors/index.d.ts +15 -0
  57. package/dist/errors/index.d.ts.map +1 -0
  58. package/dist/errors/index.js +602 -0
  59. package/dist/errors/index.js.map +29 -0
  60. package/dist/errors/json-actions.d.ts +38 -0
  61. package/dist/errors/json-actions.d.ts.map +1 -0
  62. package/dist/errors/log.d.ts +8 -0
  63. package/dist/errors/log.d.ts.map +1 -0
  64. package/dist/errors/message-values.d.ts +11 -0
  65. package/dist/errors/message-values.d.ts.map +1 -0
  66. package/dist/errors/messages.d.ts +63 -0
  67. package/dist/errors/messages.d.ts.map +1 -0
  68. package/dist/errors/page-path.d.ts +9 -0
  69. package/dist/errors/page-path.d.ts.map +1 -0
  70. package/dist/errors/provider.d.ts +20 -0
  71. package/dist/errors/provider.d.ts.map +1 -0
  72. package/dist/errors/request-id.d.ts +5 -0
  73. package/dist/errors/request-id.d.ts.map +1 -0
  74. package/dist/errors/route-error.d.ts +31 -0
  75. package/dist/errors/route-error.d.ts.map +1 -0
  76. package/dist/errors/standard-schema.d.ts +34 -0
  77. package/dist/errors/standard-schema.d.ts.map +1 -0
  78. package/dist/errors/submit-form.d.ts +16 -0
  79. package/dist/errors/submit-form.d.ts.map +1 -0
  80. package/dist/errors/use-json-fetcher.d.ts +18 -0
  81. package/dist/errors/use-json-fetcher.d.ts.map +1 -0
  82. package/dist/index.js +13 -34
  83. package/dist/index.js.map +3 -5
  84. package/dist/pwa/build/index.js +1 -1
  85. package/dist/pwa/index.js +6 -6
  86. package/dist/pwa/index.js.map +1 -1
  87. package/dist/vite/index.d.ts.map +1 -1
  88. package/dist/vite/index.js +29 -2
  89. package/dist/vite/index.js.map +5 -4
  90. package/dist/vite/inline.d.ts +18 -0
  91. package/dist/vite/inline.d.ts.map +1 -0
  92. package/docs/README.md +3 -1
  93. package/docs/guide/csp-nonce.md +35 -3
  94. package/docs/guide/document.md +310 -0
  95. package/docs/guide/errors.md +838 -0
  96. package/docs/roadmap.md +36 -1
  97. package/docs/troubleshooting.md +712 -1
  98. package/package.json +33 -3
@@ -0,0 +1,29 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/errors/api-error.ts", "../src/errors/code-resolver.ts", "../src/errors/describe-refusal.ts", "../src/errors/fetcher-result.ts", "../src/errors/route-error.ts", "../src/errors/request-id.ts", "../src/errors/submit-form.ts", "../src/errors/describe.ts", "../src/errors/api-errors.ts", "../src/errors/log.ts", "../src/errors/call-failure.ts", "../src/errors/catalogue-check.ts", "../src/errors/catalogue-read.ts", "../src/errors/catalogue.ts", "../src/errors/json-actions.ts", "../src/errors/page-path.ts", "../src/errors/message-values.ts", "../src/errors/messages.ts", "../src/errors/provider.ts", "../src/errors/use-json-fetcher.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * The shape of an API's refusal, as `@nxgt/graphql-client`'s `ApiError`\n * carries it, declared structurally: `/errors` never imports the client, so\n * an app needs it only if it calls the API through it. `api-error.spec.ts`\n * holds the two in step.\n */\n\n/** One refused input field: `extensions.fields` of a validation refusal. */\nexport interface ApiFieldErrorLike {\n\treadonly path: string;\n\treadonly code: string;\n\treadonly message: string;\n\t/** The values the field's message interpolates. */\n\treadonly params?: Readonly<Record<string, unknown>>;\n}\n\n/** The values a message interpolates; a throttle's wait is `minutes` or `retryAfter`. */\nexport interface ApiErrorParamsLike {\n\treadonly minutes?: unknown;\n\treadonly retryAfter?: unknown;\n\treadonly [key: string]: unknown;\n}\n\n/**\n * One GraphQL error's `extensions`. `code` is the API's own\n * `<module>.errors.<slug>`; the rest is what its messages name.\n */\nexport interface ApiErrorExtensionsLike {\n\treadonly code?: string;\n\t/** The values the message interpolates, when the API sends them. */\n\treadonly params?: ApiErrorParamsLike;\n\t/** A validation refusal: every field at fault, by input path. */\n\treadonly fields?: readonly ApiFieldErrorLike[];\n\t/** Any other refusal about one input field, by its path. */\n\treadonly field?: string;\n\t/** A throttle's wait, in minutes (`waitMinutes` coerces a string). */\n\treadonly minutes?: unknown;\n\t/** A throttle's wait, in seconds. */\n\treadonly retryAfter?: unknown;\n\treadonly [key: string]: unknown;\n}\n\n/** One error of a response's `errors`. */\nexport interface ApiErrorEntryLike {\n\treadonly message: string;\n\treadonly extensions?: ApiErrorExtensionsLike;\n}\n\n/** A refusal read from a GraphQL API: what `ApiError` has, and the request id. */\nexport interface ApiErrorLike extends Error {\n\treadonly errors: readonly ApiErrorEntryLike[];\n\t/** The first error's `extensions.code`. */\n\treadonly code: string | undefined;\n\t/** The HTTP status to answer with. */\n\treadonly status: number;\n\t/** The id the API logged the request under, when the caller kept it. */\n\treadonly requestId?: string;\n}\n\n/** Whether `error` is an `Error` of this `name`: the client's errors are told by it, never `instanceof`. */\nexport const isNamed = (error: unknown, name: string): error is Error =>\n\terror instanceof Error && error.name === name;\n\n/**\n * Whether `error` is an `ApiError` of `@nxgt/graphql-client`, by the test\n * that client's `isApiError` makes (an `Error` named `ApiError` with an\n * `errors` array), plus a numeric `status`, which `ApiErrorLike` promises.\n * It holds across two copies of the client.\n */\nexport function isApiErrorLike(error: unknown): error is ApiErrorLike {\n\treturn (\n\t\tisNamed(error, 'ApiError') &&\n\t\tArray.isArray((error as { errors?: unknown }).errors) &&\n\t\ttypeof (error as { status?: unknown }).status === 'number'\n\t);\n}\n\n/**\n * The minutes a wait lasts, as the API words it: `minutes` (at least one, in\n * `extensions.params` or beside it), else the `retryAfter` seconds rounded\n * up, else one.\n */\nexport function waitMinutes(\n\textensions: ApiErrorExtensionsLike | undefined,\n): number {\n\tconst minutes = extensions?.params?.minutes ?? extensions?.minutes;\n\tif (minutes !== undefined) return Math.max(1, Number(minutes) || 1);\n\tconst seconds = extensions?.params?.retryAfter ?? extensions?.retryAfter;\n\treturn Math.max(1, Math.ceil(Number(seconds) / 60) || 1);\n}\n\n/**\n * The values a message names: `extensions.params` over the extensions\n * themselves, where older errors and the extras of a few sit.\n */\nexport function valuesOf(\n\textensions: ApiErrorExtensionsLike | undefined,\n): Record<string, unknown> | undefined {\n\tif (!extensions) return undefined;\n\treturn extensions.params\n\t\t? { ...extensions, ...extensions.params }\n\t\t: extensions;\n}\n\n/** `@nxgt/graphql-client`'s `ApiStatusError`, as far as the failure reads it. */\nexport interface StatusErrorLike extends Error {\n\tstatus: number;\n\tstatusText?: string | undefined;\n\tbody?: unknown;\n}\n\n/** `@nxgt/graphql-client`'s `ApiUnavailableError`, as far as the failure reads it. */\nexport interface UnavailableErrorLike extends Error {\n\treason: string;\n}\n\nexport const isStatusError = (error: unknown): error is StatusErrorLike =>\n\tisNamed(error, 'ApiStatusError') &&\n\ttypeof (error as { status?: unknown }).status === 'number';\n\nexport const isUnavailableError = (\n\terror: unknown,\n): error is UnavailableErrorLike =>\n\tisNamed(error, 'ApiUnavailableError') &&\n\ttypeof (error as { reason?: unknown }).reason === 'string';\n",
6
+ "import type { ApiMessages } from './messages';\n\n/** The part of a zod issue a code reads, by shape (`@nxgt/material-core`'s `CodeIssue`). */\nexport type CodeIssueLike = {\n\treadonly code: string;\n\t/** A custom issue's params: `.refine(…, { params: { code } })`. */\n\treadonly params?: Record<string, unknown> | undefined;\n\treadonly input?: unknown;\n\treadonly origin?: string | undefined;\n\treadonly minimum?: unknown;\n\treadonly maximum?: unknown;\n\treadonly expected?: unknown;\n\treadonly message?: string | undefined;\n};\n\n/** An API error code (`<module>.errors.<slug>`), as opposed to a GraphQL one (`BAD_USER_INPUT`). */\nexport const API_CODE = /^[a-z-]+\\.errors\\.[a-z0-9-]+$/;\n\nexport interface ApiCodeResolverOptions<L extends string> {\n\t/**\n\t * The app's translator, for every key that is not an API code. Method\n\t * syntax: the app's own `Translate<LocaleKey>` is accepted.\n\t */\n\ttranslate(\n\t\tkey: string,\n\t\tcontext?: Readonly<Record<string, unknown>>,\n\t\tlanguage?: L | (() => L),\n\t): string;\n\t/** The page's language, when a call names none. */\n\tlanguage(): L;\n\t/** Which code an issue carries; the issue's `params.code` by default. */\n\tcodeOf?(issue: CodeIssueLike): string | undefined;\n\t/** Whether a key is an API code; `<module>.errors.<slug>` by default. */\n\tisApiCode?(key: string): boolean;\n}\n\n/** What `@nxgt/material`'s `useCodeResolver(schema, resolver)` takes. */\nexport interface ApiCodeResolver<L extends string> {\n\ttranslate: (\n\t\tkey: string,\n\t\tcontext?: Readonly<Record<string, unknown>>,\n\t\tlanguage?: L | (() => L),\n\t) => string;\n\tcodeOf: (issue: CodeIssueLike) => string | undefined;\n}\n\nfunction paramsCode(issue: CodeIssueLike) {\n\tconst code = (issue.params as { code?: unknown } | undefined)?.code;\n\treturn typeof code === 'string' ? code : undefined;\n}\n\n/**\n * The words of what a form's checks ask for: a refinement carrying an API\n * code in `params.code` reads exactly as the API words it (from the\n * catalogue `messages` holds), any other key takes the app's translator, and\n * a code the catalogue lacks comes back as it is, so the form library words\n * the issue with its own locale instead. Both functions are stable: a\n * resolver is made once per schema and language, so build this once, at\n * module scope.\n */\nexport function apiCodeResolver<L extends string>(\n\tmessages: ApiMessages<L>,\n\toptions: ApiCodeResolverOptions<L>,\n): ApiCodeResolver<L> {\n\tconst isApiCode = (key: string) =>\n\t\toptions.isApiCode ? options.isApiCode(key) : API_CODE.test(key);\n\treturn {\n\t\ttranslate(key, context, provideLanguage) {\n\t\t\tif (!isApiCode(key))\n\t\t\t\treturn options.translate(key, context, provideLanguage);\n\t\t\tconst language =\n\t\t\t\ttypeof provideLanguage === 'function'\n\t\t\t\t\t? provideLanguage()\n\t\t\t\t\t: (provideLanguage ?? options.language());\n\t\t\treturn messages.apiMessage(key, context, language) ?? key;\n\t\t},\n\t\tcodeOf(issue) {\n\t\t\treturn options.codeOf ? options.codeOf(issue) : paramsCode(issue);\n\t\t},\n\t};\n}\n",
7
+ "import { isApiErrorLike } from './api-error';\nimport type { ApiErrors } from './api-errors';\n\n/** The API's refusal of `error`, or `undefined` when it is not one. */\nexport function refusalOf<L extends string>(\n\terrors: Pick<ApiErrors<L>, 'apiErrorMessage' | 'unavailableMessage'>,\n\terror: unknown,\n\tlanguage: L,\n): ReturnType<ApiErrors<L>['apiRefusal']> {\n\tif (isApiErrorLike(error)) {\n\t\treturn {\n\t\t\tmessage: errors.apiErrorMessage(error, language),\n\t\t\tstatus: error.status,\n\t\t};\n\t}\n\tif (error instanceof Response && error.status === 503) {\n\t\treturn { message: errors.unavailableMessage(language), status: 503 };\n\t}\n\treturn undefined;\n}\n\n/** The worded message of each field an API error names, by field path. */\nexport function fieldErrorsOf<L extends string>(\n\terrors: Pick<ApiErrors<L>, 'apiErrorMessage' | 'translateCode'>,\n\terror: Parameters<ApiErrors<L>['fieldErrors']>[0],\n\tlanguage: L,\n): ReturnType<ApiErrors<L>['fieldErrors']> {\n\tconst extensions = error.errors[0]?.extensions;\n\tconst fields = Array.isArray(extensions?.fields) ? extensions.fields : [];\n\tif (!fields.length && extensions?.field) {\n\t\treturn { [extensions.field]: errors.apiErrorMessage(error, language) };\n\t}\n\treturn Object.fromEntries(\n\t\tfields.map(({ path, code, message, params }) => [\n\t\t\tpath,\n\t\t\terrors.translateCode(code, message, language, params),\n\t\t]),\n\t);\n}\n",
8
+ "import { data } from 'react-router';\nimport { isApiErrorLike } from './api-error';\nimport type { ApiErrors, ApiRefusal } from './api-errors';\nimport { type RouteErrorOptions, routeError } from './route-error';\n\n/**\n * A fetcher's refusal: the API's (`apiRefusal`), an unreachable API's too,\n * so the page stays where it was. A 401 (the session caller redirects\n * first) or any other failure is thrown, for the error page.\n */\nexport function fetcherRefusal<L extends string>(\n\terrors: Pick<ApiErrors<L>, 'apiRefusal'>,\n\terror: unknown,\n\tlanguage: L,\n\troute?: RouteErrorOptions,\n): ApiRefusal {\n\tconst refusal =\n\t\tisApiErrorLike(error) && error.status === 401\n\t\t\t? undefined\n\t\t\t: errors.apiRefusal(error, language);\n\tif (!refusal) throw routeError(error, route);\n\treturn refusal;\n}\n\n/**\n * Runs the mutation of an action a fetcher posted to: `{ ok: true }` with\n * `about` and the mutation's value, or the refusal (`fetcherRefusal`) as a\n * worded `error`, with its HTTP status, for a toast or a form message.\n */\nexport async function fetcherResult<\n\tL extends string,\n\tAbout extends object,\n\tValue extends object = Record<never, never>,\n>(\n\terrors: Pick<ApiErrors<L>, 'apiRefusal'>,\n\tlanguage: L,\n\tabout: About,\n\trun: () => Promise<Value>,\n\troute?: RouteErrorOptions,\n) {\n\ttry {\n\t\treturn { ok: true as const, ...about, ...(await run()) };\n\t} catch (error) {\n\t\tconst { message, status } = fetcherRefusal(errors, error, language, route);\n\t\treturn data({ ok: false as const, ...about, error: message }, { status });\n\t}\n}\n",
9
+ "import { data } from 'react-router';\nimport { isApiErrorLike } from './api-error';\nimport { requestIdOf } from './request-id';\n\n/** The statuses an error page words: unauthenticated, forbidden, missing, unavailable. */\nconst ROUTED: ReadonlySet<number> = new Set([401, 403, 404, 503]);\n\n/**\n * What a route error of the API carries to the error page: the API's code\n * and message when it gave a code, and the `X-Request-Id` of the call as the\n * support reference (`errorRequestId`).\n */\nexport type RouteErrorData = {\n\tcode?: string | undefined;\n\tmessage?: string | undefined;\n\trequestId?: string | undefined;\n};\n\nexport interface RouteErrorOptions {\n\t/** The statuses that become a route error of their own; 401, 403, 404 and 503 by default. */\n\tstatuses?: ReadonlySet<number>;\n}\n\n/**\n * What a loader throws for an error from the API: an `ApiError` whose\n * status is routed (401, 403, 404 or 503) becomes a route error response of\n * that status, which the root `ErrorBoundary` renders as its page, with the\n * call's request id when the API gave one. A thrown routed `Response` that\n * carries a request id becomes one too (a bare `Response` hands the browser\n * its body only). Any other `ApiError` that has a request id becomes a 500\n * with just that id, so the generic error page can show it. Anything else\n * is returned as it is.\n */\nexport function routeError(\n\terror: unknown,\n\t{ statuses = ROUTED }: RouteErrorOptions = {},\n): unknown {\n\tif (isApiErrorLike(error)) {\n\t\tconst { requestId } = error;\n\t\tif (statuses.has(error.status)) {\n\t\t\treturn data<RouteErrorData>(\n\t\t\t\t{ code: error.code, message: error.message, requestId },\n\t\t\t\t{ status: error.status },\n\t\t\t);\n\t\t}\n\t\treturn requestId\n\t\t\t? data<RouteErrorData>({ requestId }, { status: 500 })\n\t\t\t: error;\n\t}\n\tif (error instanceof Response && statuses.has(error.status)) {\n\t\tconst requestId = requestIdOf(error);\n\t\tif (requestId) {\n\t\t\treturn data<RouteErrorData>({ requestId }, { status: error.status });\n\t\t}\n\t}\n\treturn error;\n}\n\n/**\n * The support reference an error page shows: the API's `X-Request-Id` a\n * route error carries (`routeError`), if it carries one.\n */\nexport function errorRequestId(error: unknown): string | undefined {\n\tif (typeof error !== 'object' || error === null) return undefined;\n\tconst { data: body } = error as { data?: unknown };\n\tif (typeof body !== 'object' || body === null) return undefined;\n\tconst { requestId } = body as { requestId?: unknown };\n\treturn typeof requestId === 'string' && requestId ? requestId : undefined;\n}\n",
10
+ "/** The header an API echoes on every response: the id its logs carry. */\nexport const REQUEST_ID_HEADER = 'x-request-id';\n\n/** The request id a response carries, if the API sent one. */\nexport function requestIdOf(response: Response): string | undefined {\n\treturn response.headers.get(REQUEST_ID_HEADER) || undefined;\n}\n",
11
+ "import { data } from 'react-router';\nimport { isApiErrorLike } from './api-error';\nimport type { ApiErrors } from './api-errors';\nimport type { FormErrors } from './form-errors';\nimport { type RouteErrorOptions, routeError } from './route-error';\n\n/**\n * Runs a form's mutation. An `ApiError` becomes the form's errors: each\n * refused field (`extensions.fields`, or the one `extensions.field` names)\n * under the form field `fieldOf` names (else on the form), and any other\n * code as the form's message. A 404 about none of the form's fields (the\n * record itself is gone), a 401 and other failures go to the error page.\n */\nexport async function submitForm<L extends string, T>(\n\terrors: Pick<ApiErrors<L>, 'apiErrorMessage' | 'fieldErrors'>,\n\tlanguage: L,\n\trun: () => Promise<T>,\n\tfieldOf: (path: string) => string | undefined,\n\troute?: RouteErrorOptions,\n): Promise<{ ok: true; value: T } | ReturnType<typeof data<FormErrors>>> {\n\ttry {\n\t\treturn { ok: true, value: await run() };\n\t} catch (error) {\n\t\tif (!isApiErrorLike(error) || error.status === 401) {\n\t\t\tthrow routeError(error, route);\n\t\t}\n\t\tconst field = error.errors[0]?.extensions?.field;\n\t\tif (error.status === 404 && !(field && fieldOf(field))) {\n\t\t\tthrow routeError(error, route);\n\t\t}\n\t\tconst byField: Record<string, string> = {};\n\t\tconst unplaced: string[] = [];\n\t\tfor (const [path, message] of Object.entries(\n\t\t\terrors.fieldErrors(error, language),\n\t\t)) {\n\t\t\tconst field = fieldOf(path);\n\t\t\tif (field) byField[field] = message;\n\t\t\telse unplaced.push(message);\n\t\t}\n\t\tconst formError =\n\t\t\tObject.keys(byField).length && !unplaced.length\n\t\t\t\t? undefined\n\t\t\t\t: [errors.apiErrorMessage(error, language), ...unplaced].join(' ');\n\t\treturn data({ fieldErrors: byField, formError }, { status: error.status });\n\t}\n}\n",
12
+ "import { valuesOf } from './api-error';\nimport type { ApiErrors, ApiErrorsOptions } from './api-errors';\nimport { API_CODE } from './code-resolver';\nimport { fieldErrorsOf, refusalOf } from './describe-refusal';\nimport { fetcherRefusal, fetcherResult } from './fetcher-result';\nimport { submitForm } from './submit-form';\n\nexport function describeApiErrors<L extends string>({\n\tmessages,\n\tunavailable,\n\trefused,\n\tbyCode = {},\n\tisApiCode = (code) => API_CODE.test(code),\n}: ApiErrorsOptions<L>): ApiErrors<L> {\n\tconst translateCode: ApiErrors<L>['translateCode'] = (\n\t\tcode,\n\t\tfallback,\n\t\tlanguage,\n\t\tvalues,\n\t) => {\n\t\tif (!code) return fallback;\n\t\treturn messages.apiMessage(code, values, language) ?? fallback;\n\t};\n\n\tconst apiErrorMessage: ApiErrors<L>['apiErrorMessage'] = (\n\t\terror,\n\t\tlanguage,\n\t) => {\n\t\tconst code = error.code;\n\t\tif (refused && (!code || !isApiCode(code))) return refused(language);\n\t\tconst own = code && Object.hasOwn(byCode, code) ? byCode[code] : undefined;\n\t\tif (own) return own(error, language);\n\t\treturn translateCode(\n\t\t\tcode,\n\t\t\terror.message,\n\t\t\tlanguage,\n\t\t\tvaluesOf(error.errors[0]?.extensions),\n\t\t);\n\t};\n\n\tconst unavailableMessage = (language: L) => unavailable(language);\n\n\tconst apiRefusal: ApiErrors<L>['apiRefusal'] = (error, language) =>\n\t\trefusalOf({ apiErrorMessage, unavailableMessage }, error, language);\n\n\tconst fieldErrors: ApiErrors<L>['fieldErrors'] = (error, language) =>\n\t\tfieldErrorsOf({ apiErrorMessage, translateCode }, error, language);\n\n\tconst parts = { apiRefusal, apiErrorMessage, fieldErrors };\n\n\treturn {\n\t\ttranslateCode,\n\t\tapiErrorMessage,\n\t\tunavailableMessage,\n\t\tfieldErrors,\n\t\tapiRefusal,\n\t\tfetcherRefusal: (error, language, route) =>\n\t\t\tfetcherRefusal(parts, error, language, route),\n\t\tfetcherResult: (language, about, run, route) =>\n\t\t\tfetcherResult(parts, language, about, run, route),\n\t\tsubmitForm: (language, run, fieldOf, route) =>\n\t\t\tsubmitForm(parts, language, run, fieldOf, route),\n\t};\n}\n",
13
+ "import type { data } from 'react-router';\nimport type { ApiErrorLike } from './api-error';\nimport { describeApiErrors } from './describe';\nimport type { FormErrors } from './form-errors';\nimport type { ApiMessages } from './messages';\nimport type { RouteErrorOptions } from './route-error';\n\n/** A refused call, as a page says it: the message, and the HTTP status to answer. */\nexport type ApiRefusal = { message: string; status: number };\n\nexport interface ApiErrorsOptions<L extends string> {\n\t/** The catalogue the API's codes are worded from. */\n\tmessages: ApiMessages<L>;\n\t/**\n\t * The app's own word for an API it cannot reach (a 503 `Response`): not\n\t * an API error, so never one of the catalogue's codes.\n\t */\n\tunavailable(language: L): string;\n\t/**\n\t * The app's generic refusal, said for an error with no API code (a raw\n\t * `BAD_USER_INPUT` before the API's validation layer), never the raw\n\t * message. Without it, such an error says the message the API sent.\n\t */\n\trefused?(language: L): string;\n\t/**\n\t * An app's own wording for given codes, such as the API's request limits\n\t * (depth, cost, introspection), whose params differ and name no field.\n\t * Checked after `refused`, before the catalogue.\n\t */\n\tbyCode?: Readonly<\n\t\tRecord<string, (error: ApiErrorLike, language: L) => string>\n\t>;\n\t/** Whether a code is an API code; `<module>.errors.<slug>` by default. */\n\tisApiCode?(code: string): boolean;\n}\n\n/** What a page says of an API's refusals, in a language. */\nexport interface ApiErrors<L extends string> {\n\t/**\n\t * The catalogue's text of an API code in `language`, else `fallback` (the\n\t * message the API already worded). `values` are the message's arguments\n\t * (`extensions.params`).\n\t */\n\ttranslateCode(\n\t\tcode: string | undefined,\n\t\tfallback: string,\n\t\tlanguage: L,\n\t\tvalues?: Readonly<Record<string, unknown>>,\n\t): string;\n\t/**\n\t * What to show for an API error: `refused` for an error with no API code,\n\t * when given; else `byCode`'s wording of its code; else the catalogue's\n\t * text; else the message the API already worded.\n\t */\n\tapiErrorMessage(error: ApiErrorLike, language: L): string;\n\t/**\n\t * The message of each refused field, by input path, ready for a form: the\n\t * entries of `extensions.fields`, else the one field `extensions.field`\n\t * names, with the error's own message. Empty for neither.\n\t */\n\tfieldErrors(error: ApiErrorLike, language: L): Record<string, string>;\n\t/** `unavailable`, in `language`. */\n\tunavailableMessage(language: L): string;\n\t/**\n\t * What a failed call says to the user: an API refusal's message and its\n\t * status, or an API it cannot reach (a 503 `Response`: the unavailable\n\t * word). Undefined for anything else, which the caller rethrows.\n\t */\n\tapiRefusal(error: unknown, language: L): ApiRefusal | undefined;\n\t/**\n\t * A fetcher's refusal: `apiRefusal`'s, so the page stays where it was. A\n\t * 401 (the session caller redirects first) or any other failure is\n\t * thrown, through `routeError`, for the error page.\n\t */\n\tfetcherRefusal(\n\t\terror: unknown,\n\t\tlanguage: L,\n\t\troute?: RouteErrorOptions,\n\t): ApiRefusal;\n\t/**\n\t * Runs the mutation of an action a fetcher posted to: `{ ok: true }` with\n\t * `about` and the mutation's value, or `{ ok: false, error }` with `about`\n\t * and the refusal's message, answered with its HTTP status.\n\t */\n\tfetcherResult<\n\t\tAbout extends object,\n\t\tValue extends object = Record<never, never>,\n\t>(\n\t\tlanguage: L,\n\t\tabout: About,\n\t\trun: () => Promise<Value>,\n\t\troute?: RouteErrorOptions,\n\t): Promise<\n\t\t| ({ ok: true } & About & Value)\n\t\t| ReturnType<typeof data<{ ok: false; error: string } & About>>\n\t>;\n\t/**\n\t * Runs a form's mutation: `{ ok: true, value }`, or the form's errors\n\t * (`fieldErrors` under the form field `fieldOf` names an input path by,\n\t * and a `formError` for the rest) with the refusal's status. A 404 about\n\t * none of the form's fields, a 401 and other failures are thrown for the\n\t * error page, through `routeError` (with `route`).\n\t */\n\tsubmitForm<T>(\n\t\tlanguage: L,\n\t\trun: () => Promise<T>,\n\t\tfieldOf: (path: string) => string | undefined,\n\t\troute?: RouteErrorOptions,\n\t): Promise<{ ok: true; value: T } | ReturnType<typeof data<FormErrors>>>;\n}\n\n/**\n * The wording of an API's refusals for the languages `L`, from the\n * catalogue `messages` holds. Make one per app, beside its `createApiMessages`.\n */\nexport function createApiErrors<L extends string>(\n\toptions: ApiErrorsOptions<L>,\n): ApiErrors<L> {\n\treturn describeApiErrors(options);\n}\n",
14
+ "/** The methods of a `console` a report goes through. */\nexport type LogMethod = 'warn' | 'error';\n\n/**\n * A log whose methods can throw without breaking what reports through\n * them: a broken app log must not turn a handled failure into a rejection.\n */\nexport function quietLog<M extends LogMethod>(\n\tlog: Pick<Console, M>,\n\tmethods: readonly M[],\n): Pick<Console, M> {\n\tconst quiet = {} as Pick<Console, M>;\n\tfor (const method of methods) {\n\t\tquiet[method] = ((...args: unknown[]) => {\n\t\t\ttry {\n\t\t\t\t(log[method] as (...args: unknown[]) => void)(...args);\n\t\t\t} catch {\n\t\t\t\t// Swallowed on purpose: see above.\n\t\t\t}\n\t\t}) as Pick<Console, M>[M];\n\t}\n\treturn quiet;\n}\n",
15
+ "import { isApiErrorLike, isStatusError, isUnavailableError } from './api-error';\nimport { quietLog } from './log';\nimport { REQUEST_ID_HEADER } from './request-id';\n\n/** The statuses an API's gateway or auth layer may answer without a GraphQL body. */\nconst PASSED_THROUGH: ReadonlySet<number> = new Set([401, 403, 404]);\n\n/** The statuses a `Response` cannot carry a body with. */\nconst NO_BODY: ReadonlySet<number> = new Set([101, 204, 205, 304]);\n\nexport interface CallFailureOptions {\n\t/** What a log line names the API by, such as `my-api https://api.example.com/graphql`. */\n\tapi: string;\n\t/** The operation's name, for the log (`operationName(document)`). */\n\toperation?: string;\n\t/** The `x-request-id` the API answered, when it did (`requestIdOf(response)`). */\n\trequestId?: string;\n\t/** The incoming request's signal: an aborted call is answered 503. */\n\tsignal?: AbortSignal;\n\t/** The statuses without a GraphQL body that become a `Response` of their own; 401, 403 and 404 by default. */\n\tpassedThrough?: ReadonlySet<number>;\n\t/** Where the lines go; `console` by default. */\n\tlog?: Pick<Console, 'warn' | 'error'>;\n}\n\n/**\n * What a call became once it failed, for a loader or an action to throw: the\n * `ApiError` as it is (carrying `requestId`), a `Response` of the API's own\n * status for a 401, 403 or 404 without a GraphQL body, a 503 `Response` (with no body; the reason is in the log) for an\n * unreachable, slow, garbled or aborted API, and any other error as it is.\n * Every case it recognises is logged with the request id, when the API gave one, and never\n * the headers, the variables nor a token; the id rides on the `ApiError` as\n * `requestId` and on a `Response` as `x-request-id`, where `routeError` reads\n * it.\n *\n * It recognises `@nxgt/graphql-client`'s errors by their `name`, never with\n * `instanceof`, so it needs no import of the client and holds across two\n * copies of it.\n */\nexport function apiCallFailure(\n\terror: unknown,\n\t{\n\t\tapi,\n\t\toperation = 'operation',\n\t\trequestId,\n\t\tsignal,\n\t\tpassedThrough = PASSED_THROUGH,\n\t\tlog: appLog = console,\n\t}: CallFailureOptions,\n): unknown {\n\tconst log = quietLog(appLog, ['warn', 'error']);\n\tconst call = `${api}: ${operation}${requestId ? ` (request ${requestId})` : ''}`;\n\tconst headers: Record<string, string> = requestId\n\t\t? { [REQUEST_ID_HEADER]: requestId }\n\t\t: {};\n\tconst unavailable = (reason: string, cause?: unknown): Response => {\n\t\tlog.error(`${call} ${reason}`, cause ?? '');\n\t\treturn new Response(null, { status: 503, headers });\n\t};\n\tif (isApiErrorLike(error)) {\n\t\t// The client's `ApiError.requestId` is writable, though the structural type marks it readonly.\n\t\tif (requestId) (error as { requestId?: string }).requestId = requestId;\n\t\tconst line = `${call} answered ${error.status}${error.code ? ` ${error.code}` : ''}`;\n\t\tif (error.status >= 500) log.error(line);\n\t\telse log.warn(line);\n\t\treturn error;\n\t}\n\tif (isStatusError(error)) {\n\t\tif (passedThrough.has(error.status)) {\n\t\t\tlog.warn(`${call} answered ${error.status}`);\n\t\t\tconst statusText =\n\t\t\t\ttypeof error.statusText === 'string' ? error.statusText : '';\n\t\t\tif (NO_BODY.has(error.status)) {\n\t\t\t\treturn new Response(null, {\n\t\t\t\t\tstatus: error.status,\n\t\t\t\t\tstatusText,\n\t\t\t\t\theaders,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn new Response(statusText, {\n\t\t\t\tstatus: error.status,\n\t\t\t\tstatusText,\n\t\t\t\theaders,\n\t\t\t});\n\t\t}\n\t\treturn unavailable(`answered ${error.status} without GraphQL`, error.body);\n\t}\n\tif (isUnavailableError(error)) return unavailable(error.reason, error.cause);\n\tif (signal?.aborted) return unavailable('aborted', error);\n\treturn error;\n}\n\n/** A document, as far as `operationName` reads it: `graphql`'s `DocumentNode` fits. */\nexport interface DocumentLike {\n\treadonly definitions: readonly {\n\t\treadonly kind: string;\n\t\treadonly operation?: string | undefined;\n\t\treadonly name?: { readonly value: string } | undefined;\n\t}[];\n}\n\n/**\n * The name of the operation a document holds, for a log line: its name, else\n * its kind (`query`, `mutation`, `subscription`), else `operation`.\n */\nexport function operationName(document: DocumentLike): string {\n\tfor (const definition of document.definitions) {\n\t\tif (definition.kind === 'OperationDefinition') {\n\t\t\treturn definition.name?.value ?? definition.operation ?? 'operation';\n\t\t}\n\t}\n\treturn 'operation';\n}\n",
16
+ "import type { Held } from './catalogue-read';\n\n/** What the TTL check needs: the reader's state and the app's own calls. */\nexport interface CheckContext<L extends string> {\n\treader: {\n\t\theld: Map<L, Held>;\n\t\tload(language: L, now: number): Promise<void>;\n\t};\n\tversions(): Promise<Partial<Record<L, string>>>;\n\tttl(): number;\n\tretryMs: number;\n\tlog: Pick<Console, 'error'>;\n}\n\n/**\n * Asks the versions in one call and reads again the messages of each\n * language that moved. An API that cannot be reached keeps the messages\n * held (stale, still worded) and is asked again after `retryMs`.\n */\nexport async function checkVersions<L extends string>(\n\t{ reader, versions, ttl, retryMs, log }: CheckContext<L>,\n\tnow: number,\n) {\n\ttry {\n\t\tconst latest = await versions();\n\t\tfor (const [language, entry] of [...reader.held]) {\n\t\t\tif (latest[language] !== entry.version) {\n\t\t\t\tawait reader.load(language, now);\n\t\t\t} else {\n\t\t\t\tentry.checkedAt = now;\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tlog.error(\n\t\t\t'alxia-react-router: the error catalogue was not checked, keeping what is held',\n\t\t\terror,\n\t\t);\n\t\tfor (const entry of reader.held.values()) {\n\t\t\tentry.checkedAt = now - ttl() + retryMs;\n\t\t}\n\t}\n}\n\n/**\n * The TTL, whose getter is the app's: one that throws, or that is not a\n * finite number of at least 0, is logged and `fallback` is used.\n */\nexport function safeTtl(\n\tttlMs: number | (() => number),\n\tfallback: number,\n\tlog: Pick<Console, 'error'>,\n) {\n\treturn () => {\n\t\ttry {\n\t\t\tconst ttl = typeof ttlMs === 'function' ? ttlMs() : ttlMs;\n\t\t\tif (typeof ttl === 'number' && Number.isFinite(ttl) && ttl >= 0) {\n\t\t\t\treturn ttl;\n\t\t\t}\n\t\t\tlog.error('alxia-react-router: ttlMs is not a duration, using retryMs');\n\t\t} catch (error) {\n\t\t\tlog.error('alxia-react-router: ttlMs threw, using retryMs', error);\n\t\t}\n\t\treturn fallback;\n\t};\n}\n",
17
+ "import type { ApiMessages, CatalogueMessage } from './messages';\n\n/** What the API answers for one language: its version and every message. */\nexport type CatalogueReply = {\n\tversion: string;\n\tmessages: ReadonlyArray<CatalogueMessage & { readonly code: string }>;\n};\n\n/** A language's catalogue as held, and when its version was last confirmed. */\nexport type Held = { version: string; checkedAt: number };\n\nexport interface CatalogueReaderOptions<L extends string> {\n\tmessages: ApiMessages<L>;\n\tread(language: L): Promise<CatalogueReply>;\n\tretryMs: number;\n\tfirstReadWaitMs: number;\n\tlog: Pick<Console, 'error'>;\n}\n\n/**\n * Waits for the read, until `firstReadWaitMs` after it started (not after\n * this request came: a hung API holds a request at most that long in\n * all); the read goes on either way.\n */\nasync function boundedWait(\n\tpending: { read: Promise<void>; started: number },\n\tfirstReadWaitMs: number,\n) {\n\tconst left = firstReadWaitMs - (performance.now() - pending.started);\n\tif (left <= 0) return;\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tconst limit = new Promise<void>((resolve) => {\n\t\ttimer = setTimeout(resolve, left);\n\t});\n\tawait Promise.race([pending.read, limit]);\n\tclearTimeout(timer);\n}\n\n/**\n * The reading half of the catalogue: the first read of each language (one in\n * flight, a bounded wait, a retry delay after a failure) and the version\n * each one holds. `createErrorCatalogue` adds the TTL check on top.\n */\nexport function createCatalogueReader<L extends string>({\n\tmessages,\n\tread,\n\tretryMs,\n\tfirstReadWaitMs,\n\tlog,\n}: CatalogueReaderOptions<L>) {\n\tconst held = new Map<L, Held>();\n\tconst loading = new Map<L, { read: Promise<void>; started: number }>();\n\tconst failedAt = new Map<L, number>();\n\n\t/** Reads one language's messages and holds them. */\n\tasync function load(language: L, now: number) {\n\t\tconst reply = await read(language);\n\t\tconst entries: Record<string, CatalogueMessage> = Object.create(null);\n\t\tfor (const { code, message, params } of reply.messages) {\n\t\t\tentries[code] = { message, params };\n\t\t}\n\t\tmessages.set(language, entries);\n\t\theld.set(language, { version: reply.version, checkedAt: now });\n\t}\n\n\t/** The first read of a language, once however many pages ask at the same time. */\n\tfunction loadOnce(language: L, now: number) {\n\t\tlet pending = loading.get(language);\n\t\tif (!pending) {\n\t\t\tconst started = performance.now();\n\t\t\tconst reading = load(language, now)\n\t\t\t\t.then(() => {\n\t\t\t\t\tfailedAt.delete(language);\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\t// When it failed, not when it started: a read that hung for\n\t\t\t\t\t// the wait is not retried at once by the next request.\n\t\t\t\t\tfailedAt.set(language, now + (performance.now() - started));\n\t\t\t\t\tlog.error(\n\t\t\t\t\t\t`alxia-react-router: the error catalogue (${language}) was not read`,\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.finally(() => {\n\t\t\t\t\tloading.delete(language);\n\t\t\t\t});\n\t\t\tpending = { read: reading, started };\n\t\t\tloading.set(language, pending);\n\t\t}\n\t\treturn pending;\n\t}\n\n\treturn {\n\t\theld,\n\t\tload,\n\t\t/** Reads a language that is not held yet, unless a failure is still fresh. */\n\t\tasync first(language: L, now: number) {\n\t\t\tconst failed = failedAt.get(language);\n\t\t\tif (failed === undefined || now - failed >= retryMs) {\n\t\t\t\tawait boundedWait(loadOnce(language, now), firstReadWaitMs);\n\t\t\t}\n\t\t},\n\t\treset() {\n\t\t\theld.clear();\n\t\t\tloading.clear();\n\t\t\tfailedAt.clear();\n\t\t},\n\t};\n}\n",
18
+ "import { checkVersions, safeTtl } from './catalogue-check';\nimport { type CatalogueReply, createCatalogueReader } from './catalogue-read';\nimport { quietLog } from './log';\nimport type { ApiMessages, CatalogueMessages } from './messages';\n\nexport type { CatalogueReply } from './catalogue-read';\n\nexport interface ErrorCatalogueOptions<L extends string> {\n\t/** The store the page words refusals from (`createApiMessages`). */\n\tmessages: ApiMessages<L>;\n\t/** The languages to hold, each read once and kept fresh. */\n\tlanguages: readonly L[];\n\t/** Reads one language's catalogue from the API. */\n\tread(language: L): Promise<CatalogueReply>;\n\t/** Asks the API for the version of every language, in one call. */\n\tversions(): Promise<Partial<Record<L, string>>>;\n\t/** How long a held catalogue is served before its version is checked. */\n\tttlMs: number | (() => number);\n\t/** How soon a check or a first read that failed is tried again. Default 5000. */\n\tretryMs?: number;\n\t/**\n\t * How long a request waits for a language's first read; past it the\n\t * request goes on and the read carries on in the background. Default 1500.\n\t */\n\tfirstReadWaitMs?: number;\n\t/** The codes the browser words itself (`clientMessages`). */\n\tclientCodes: readonly string[];\n\t/** Where failures are reported. Default `console`. */\n\tlog?: Pick<Console, 'error'>;\n}\n\n/** The API's error catalogue, kept fresh for the server. */\nexport interface ErrorCatalogue<L extends string> {\n\t/**\n\t * The catalogue of `language`, ready to word a refusal. Only a language\n\t * with no catalogue yet is awaited, for at most `firstReadWaitMs`. A held\n\t * one is served as it is, and once older than the TTL its version is\n\t * checked in the background, one check at a time.\n\t */\n\tfresh(language: L, now?: number): Promise<void>;\n\t/** Every language, fresh: what the root middleware awaits. */\n\tfreshAll(now?: number): Promise<void>;\n\t/** At startup: reads every language without holding the boot back. */\n\twarm(): void;\n\t/** The messages the browser words itself, by language, for the root's loader. */\n\tclientMessages(): Record<L, CatalogueMessages>;\n\t/**\n\t * Forgets everything held and in flight. For use between tests, with\n\t * nothing in flight: a read or check still running when it is called\n\t * finishes and holds its result again.\n\t */\n\treset(): void;\n\t/** The version held for a language, if any. */\n\theldVersion(language: L): string | undefined;\n\t/** Waits for the check in flight, if any (tests). */\n\tsettled(): Promise<void>;\n}\n\n/** The messages the browser words itself, by language. */\nfunction clientMessages<L extends string>(\n\tmessages: ApiMessages<L>,\n\tlanguages: readonly L[],\n\tclientCodes: readonly string[],\n) {\n\tconst byLanguage = {} as Record<L, CatalogueMessages>;\n\tfor (const language of languages) {\n\t\tconst entries: Record<string, CatalogueMessages[string]> =\n\t\t\tObject.create(null);\n\t\tfor (const code of clientCodes) {\n\t\t\tconst entry = messages.messageOf(language, code);\n\t\t\tif (entry) entries[code] = entry;\n\t\t}\n\t\tbyLanguage[language] = entries;\n\t}\n\treturn byLanguage;\n}\n\n/**\n * Keeps the API's error catalogue in `messages`: read on first use, checked\n * against the API's versions once per TTL, and re-read only for the\n * languages whose version moved. It is an instance, not module state.\n */\nexport function createErrorCatalogue<L extends string>({\n\tmessages,\n\tlanguages,\n\tread,\n\tversions,\n\tttlMs,\n\tretryMs = 5_000,\n\tfirstReadWaitMs = 1_500,\n\tclientCodes,\n\tlog: appLog = console,\n}: ErrorCatalogueOptions<L>): ErrorCatalogue<L> {\n\tif (languages.length === 0) {\n\t\tthrow new TypeError('createErrorCatalogue(): needs at least one language.');\n\t}\n\tconst log = quietLog(appLog, ['error']);\n\tconst ttl = safeTtl(ttlMs, retryMs, log);\n\tconst reader = createCatalogueReader({\n\t\tmessages,\n\t\tread,\n\t\tretryMs,\n\t\tfirstReadWaitMs,\n\t\tlog,\n\t});\n\tconst context = { reader, versions, ttl, retryMs, log };\n\tlet checking: Promise<void> | undefined;\n\n\tasync function fresh(language: L, now: number = Date.now()) {\n\t\tconst entry = reader.held.get(language);\n\t\tif (!entry) return reader.first(language, now);\n\t\tif (now - entry.checkedAt >= ttl() && !checking) {\n\t\t\tchecking = checkVersions(context, now)\n\t\t\t\t.finally(() => {\n\t\t\t\t\tchecking = undefined;\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t}\n\t}\n\n\tconst freshAll = async (now: number = Date.now()) => {\n\t\tawait Promise.all(languages.map((language) => fresh(language, now)));\n\t};\n\n\treturn {\n\t\tfresh,\n\t\tfreshAll,\n\t\twarm() {\n\t\t\tfreshAll().catch((error) => {\n\t\t\t\tlog.error(\n\t\t\t\t\t'alxia-react-router: the error catalogue was not warmed',\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t});\n\t\t},\n\t\tclientMessages: () => clientMessages(messages, languages, clientCodes),\n\t\treset() {\n\t\t\treader.reset();\n\t\t\tchecking = undefined;\n\t\t\tmessages.clear();\n\t\t},\n\t\theldVersion: (language) => reader.held.get(language)?.version,\n\t\tsettled: () => checking ?? Promise.resolve(),\n\t};\n}\n",
19
+ "import { data, redirect } from 'react-router';\nimport { nextOf } from './page-path';\nimport type { InferOutput, StandardSchemaV1 } from './standard-schema';\n\nexport interface JsonActionsOptions {\n\t/**\n\t * The origin a request must come from, such as `https://app.example.com`:\n\t * required behind a TLS-terminating proxy, where the request's own URL\n\t * says `http:`. A getter is read at each request. Else the request's own\n\t * origin.\n\t */\n\torigin?: string | (() => string | undefined);\n}\n\n/** The actions' JSON helpers, bound to one app's origin. */\nexport interface JsonActions {\n\t/**\n\t * The JSON body an action was posted, refused unless it comes from this\n\t * site: 403 for another origin or a cross-site fetch, 415 for a body that\n\t * is not `application/json`, 400 for malformed JSON.\n\t */\n\treadJson(request: Request): Promise<unknown>;\n\t/**\n\t * For the actions a form posts to: a native post (before hydration) goes\n\t * back to its page, which then posts JSON. A JSON post goes on.\n\t */\n\trequireJsonPost(request: Request): void;\n\t/**\n\t * The `input` of the JSON body `{ input }`, checked against a Standard\n\t * Schema (Zod 4, Valibot, ArkType), whose validation may be async. A\n\t * refused input is a 400.\n\t */\n\treadInput<S extends StandardSchemaV1>(\n\t\trequest: Request,\n\t\tschema: S,\n\t): Promise<InferOutput<S>>;\n}\n\n/**\n * Every action takes JSON: a cross-site page can only post a form or\n * `text/plain` without a preflight, so requiring `application/json` and a\n * same-origin `Origin` (or `Sec-Fetch-Site`) stops CSRF before the session\n * cookie's `SameSite` is even consulted.\n */\nexport function jsonActions(options: JsonActionsOptions = {}): JsonActions {\n\tconst ownOrigin = (request: Request) => {\n\t\tconst { origin } = options;\n\t\tconst set = typeof origin === 'function' ? origin() : origin;\n\t\treturn set ?? new URL(request.url).origin;\n\t};\n\tconst isJson = (request: Request) =>\n\t\trequest.headers.get('content-type')?.startsWith('application/json');\n\n\tasync function readJson(request: Request): Promise<unknown> {\n\t\tconst origin = request.headers.get('origin');\n\t\tconst site = request.headers.get('sec-fetch-site');\n\t\tif (\n\t\t\t(origin && origin !== ownOrigin(request)) ||\n\t\t\t(site && site !== 'same-origin' && site !== 'none')\n\t\t) {\n\t\t\tthrow data('Cross-site request refused', { status: 403 });\n\t\t}\n\t\tif (!isJson(request)) {\n\t\t\tthrow data('Expected a JSON body', { status: 415 });\n\t\t}\n\t\treturn request.json().catch(() => {\n\t\t\tthrow data('Malformed JSON body', { status: 400 });\n\t\t});\n\t}\n\n\treturn {\n\t\treadJson,\n\t\trequireJsonPost(request) {\n\t\t\tif (!isJson(request)) throw redirect(nextOf(request.url));\n\t\t},\n\t\tasync readInput(request, schema) {\n\t\t\tconst body = (await readJson(request)) as { input?: unknown } | null;\n\t\t\tconst result = await schema['~standard'].validate(body?.input);\n\t\t\tif (result.issues) {\n\t\t\t\tthrow data('The form sent an input the API would refuse', {\n\t\t\t\t\tstatus: 400,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn result.value as InferOutput<typeof schema>;\n\t\t},\n\t};\n}\n",
20
+ "/**\n * The page a request is for, as `next` carries it back after sign-in: its\n * path and search, without React Router's single-fetch details. A client\n * navigation's `request.url` is `/todos/demo/list.data?q=a&_routes=…`\n * (`/_.data` for `/`, `?index` for an index action), as the server\n * runtime's own `normalizeUrl` strips them.\n */\nexport function nextOf(url: string): string {\n\tconst { pathname, searchParams } = new URL(url);\n\tconst trimmed = pathname.endsWith('/_.data')\n\t\t? pathname.slice(0, -'_.data'.length)\n\t\t: pathname.replace(/\\.data$/, '');\n\t// `new URL('http://h/\\\\evil.com')` has the path `//evil.com`: never let a\n\t// `next` start with two slashes, which a redirect reads as another site.\n\tconst path = trimmed.replace(/^\\/{2,}/, '/');\n\tconst params = new URLSearchParams(searchParams);\n\tparams.delete('_routes');\n\t// An empty `index` names the index route; a valued one is the page's own.\n\tfor (const value of params.getAll('index')) {\n\t\tif (!value) params.delete('index', value);\n\t}\n\tconst search = params.toString();\n\treturn search ? `${path}?${search}` : path;\n}\n",
21
+ "import type { CatalogueMessage } from './messages';\n\n/** The values a formatter takes: what ICU arguments can be. */\nexport type MessageValues = Readonly<\n\tRecord<string, string | number | boolean | object>\n>;\n\nconst DAY = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\n\n/** A local `Date` built from the calendar parts of a `YYYY-MM-DD`: no time-zone shift. */\nfunction dayOf(value: string): Date | undefined {\n\tconst parts = DAY.exec(value);\n\tif (!parts) return undefined;\n\treturn new Date(Number(parts[1]), Number(parts[2]) - 1, Number(parts[3]));\n}\n\n/**\n * The values as the message formats them: a `YYYY-MM-DD` the message names\n * as a date becomes a local `Date` (by calendar parts, so the day is the\n * same in every time zone), a count or number given as a numeric string a\n * number. Everything else passes as it is.\n */\nexport function valuesFor(\n\tparams: CatalogueMessage['params'],\n\tvalues: Readonly<Record<string, unknown>>,\n): MessageValues {\n\tconst worded: Record<string, unknown> = { ...values };\n\tfor (const { name, kind } of params) {\n\t\tconst value = values[name];\n\t\tif (typeof value !== 'string') continue;\n\t\tif (kind === 'DATE') {\n\t\t\tconst day = dayOf(value);\n\t\t\tif (day) worded[name] = day;\n\t\t} else if (\n\t\t\t(kind === 'NUMBER' || kind === 'PLURAL') &&\n\t\t\tvalue.trim() !== '' &&\n\t\t\t!Number.isNaN(Number(value))\n\t\t) {\n\t\t\tworded[name] = Number(value);\n\t\t}\n\t}\n\treturn worded as MessageValues;\n}\n",
22
+ "import { type MessageValues, valuesFor } from './message-values';\n\nexport type { MessageValues } from './message-values';\n\n/** How a message uses an argument (the API's `ErrorParamKind`). */\nexport type ParamKind = 'TEXT' | 'NUMBER' | 'DATE' | 'PLURAL' | 'SELECT';\n\n/**\n * One text of the API's error catalogue: the uninterpolated ICU message and\n * the arguments it names, which say what to supply as a date or a number\n * without parsing the message.\n */\nexport type CatalogueMessage = {\n\tmessage: string;\n\tparams: ReadonlyArray<{ name: string; kind: ParamKind }>;\n};\n\n/** A language's messages by API code (`<module>.errors.<slug>`). */\nexport type CatalogueMessages = Readonly<Record<string, CatalogueMessage>>;\n\n/**\n * Formats an ICU message for a language: plurals, numbers and dates of that\n * language. `@nxgt/material`'s `formatMessage` is one, as it is. It reports\n * a message it could not format (a missing value, a stray brace) to\n * `onError` instead of throwing.\n */\nexport type MessageFormat<L extends string> = (\n\tmessage: string,\n\tlanguage: L,\n\tvalues: MessageValues,\n\toptions: { onError(error: unknown): void },\n) => string;\n\nexport interface ApiMessagesOptions<L extends string> {\n\t/** The app's ICU formatter: the same engine as its translator, so both word alike. */\n\tformat: MessageFormat<L>;\n}\n\n/** The catalogue the page words refusals with, by language. */\nexport interface ApiMessages<L extends string> {\n\t/** Replaces a language's messages (a server's whole catalogue). */\n\tset(language: L, messages: CatalogueMessages): void;\n\t/** Adds messages to a language's (a browser's, from the root's data). */\n\tadd(language: L, messages: CatalogueMessages): void;\n\t/** Forgets every message (tests). */\n\tclear(): void;\n\t/** The message the catalogue holds for a code in a language, if any. */\n\tmessageOf(language: L, code: string): CatalogueMessage | undefined;\n\t/**\n\t * A message formatted for `language` with `values`. Undefined when it\n\t * cannot be formatted with those values (a missing argument, a day it\n\t * cannot read) or does not parse.\n\t */\n\tformat(\n\t\tentry: CatalogueMessage,\n\t\tvalues: Readonly<Record<string, unknown>> | undefined,\n\t\tlanguage: L,\n\t): string | undefined;\n\t/**\n\t * The catalogue's text of an API code in `language`, with `values`\n\t * (`extensions.params`) filled in. Undefined when the catalogue has no\n\t * such code or the message cannot be formatted, so the caller falls back\n\t * to the message the API already worded.\n\t */\n\tapiMessage(\n\t\tcode: string,\n\t\tvalues: Readonly<Record<string, unknown>> | undefined,\n\t\tlanguage: L,\n\t): string | undefined;\n}\n\n/**\n * A catalogue store for the languages `L`. It is an instance, not module\n * state: two copies of the package never split it, and a spec makes its own.\n * Make one per app, in a module of its own, and share it with\n * `apiCodeResolver` and `ApiMessagesProvider`.\n */\nexport function createApiMessages<L extends string>({\n\tformat,\n}: ApiMessagesOptions<L>): ApiMessages<L> {\n\tconst catalogues = new Map<L, Map<string, CatalogueMessage>>();\n\n\tconst messageOf = (language: L, code: string) =>\n\t\tcatalogues.get(language)?.get(code);\n\n\tfunction formatEntry(\n\t\tentry: CatalogueMessage,\n\t\tvalues: Readonly<Record<string, unknown>> | undefined,\n\t\tlanguage: L,\n\t) {\n\t\tconst worded = valuesFor(entry.params, values ?? {});\n\t\t// A message that did not format is reported here, not logged: the\n\t\t// caller falls back.\n\t\tlet failed = false;\n\t\ttry {\n\t\t\tconst text = format(entry.message, language, worded, {\n\t\t\t\tonError: () => {\n\t\t\t\t\tfailed = true;\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn failed ? undefined : text;\n\t\t} catch {\n\t\t\t// An app's formatter that throws is a message that did not format.\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\treturn {\n\t\tset(language, messages) {\n\t\t\tcatalogues.set(language, new Map(Object.entries(messages)));\n\t\t},\n\t\tadd(language, messages) {\n\t\t\tconst held = catalogues.get(language) ?? new Map();\n\t\t\tfor (const [code, message] of Object.entries(messages)) {\n\t\t\t\theld.set(code, message);\n\t\t\t}\n\t\t\tcatalogues.set(language, held);\n\t\t},\n\t\tclear() {\n\t\t\tcatalogues.clear();\n\t\t},\n\t\tmessageOf,\n\t\tformat: formatEntry,\n\t\tapiMessage(code, values, language) {\n\t\t\tconst entry = messageOf(language, code);\n\t\t\treturn entry ? formatEntry(entry, values, language) : undefined;\n\t\t},\n\t};\n}\n",
23
+ "import type { ReactNode } from 'react';\nimport type { ApiMessages, CatalogueMessages } from './messages';\n\nexport interface ApiMessagesProviderProps<L extends string> {\n\t/** The store `apiCodeResolver` reads. */\n\tmessages: ApiMessages<L>;\n\t/** The messages the root's data carries, by language. */\n\tvalue: Readonly<Record<L, CatalogueMessages>> | undefined;\n\tchildren: ReactNode;\n}\n\n/**\n * Fills the browser's catalogue with the messages the root's data carries\n * (the few its forms word), before the children render: `apiCodeResolver`\n * reads them synchronously. A server render writes nothing: the server's\n * store holds the whole, fresher catalogue, and a render's data may be older\n * than it. It is a plain function (no JSX, no React runtime): use it as\n * `ApiMessagesProvider({ messages, value, children })` from a component, or\n * as an element's type.\n */\nexport function ApiMessagesProvider<L extends string>({\n\tmessages,\n\tvalue,\n\tchildren,\n}: ApiMessagesProviderProps<L>): ReactNode {\n\tconst inBrowser =\n\t\ttypeof (globalThis as { document?: unknown }).document !== 'undefined';\n\tif (value && inBrowser) {\n\t\tfor (const [language, held] of Object.entries<CatalogueMessages>(value)) {\n\t\t\tmessages.add(language as L, held);\n\t\t}\n\t}\n\treturn children;\n}\n",
24
+ "import { type FetcherWithComponents, useFetcher } from 'react-router';\n\n/** What a fetcher can post as JSON. */\nexport type JsonBody = Parameters<FetcherWithComponents<unknown>['submit']>[0];\n\n/**\n * A fetcher posting JSON to its page's action (which reads it with\n * `readJson`, and answers `fetcherResult` or the like): `post` sends a\n * body, `busy` while it is in flight. `key` shares the fetcher between the\n * form that posts and the component that reads the answer; `action` posts to\n * another route's action instead (a resource route's, such as a\n * `notifications/poll`). `post` takes the target of one call as its second\n * argument (a list's rows each post to their own record's action).\n */\nexport function useJsonFetcher<Action, Body extends JsonBody = JsonBody>(\n\tkey?: string,\n\taction?: string,\n): {\n\tfetcher: ReturnType<typeof useFetcher<Action>>;\n\tpost(body: Body, to?: string): Promise<void>;\n\tbusy: boolean;\n} {\n\tconst fetcher = useFetcher<Action>(key ? { key } : undefined);\n\tconst post = (body: Body, to = action) =>\n\t\tfetcher.submit(body, {\n\t\t\tmethod: 'post',\n\t\t\tencType: 'application/json',\n\t\t\t...(to ? { action: to } : {}),\n\t\t});\n\treturn { fetcher, post, busy: fetcher.state !== 'idle' };\n}\n"
25
+ ],
26
+ "mappings": "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",
27
+ "debugId": "0AF5BB2424365EAA64756E2164756E21",
28
+ "names": []
29
+ }
@@ -0,0 +1,38 @@
1
+ import type { InferOutput, StandardSchemaV1 } from './standard-schema';
2
+ export interface JsonActionsOptions {
3
+ /**
4
+ * The origin a request must come from, such as `https://app.example.com`:
5
+ * required behind a TLS-terminating proxy, where the request's own URL
6
+ * says `http:`. A getter is read at each request. Else the request's own
7
+ * origin.
8
+ */
9
+ origin?: string | (() => string | undefined);
10
+ }
11
+ /** The actions' JSON helpers, bound to one app's origin. */
12
+ export interface JsonActions {
13
+ /**
14
+ * The JSON body an action was posted, refused unless it comes from this
15
+ * site: 403 for another origin or a cross-site fetch, 415 for a body that
16
+ * is not `application/json`, 400 for malformed JSON.
17
+ */
18
+ readJson(request: Request): Promise<unknown>;
19
+ /**
20
+ * For the actions a form posts to: a native post (before hydration) goes
21
+ * back to its page, which then posts JSON. A JSON post goes on.
22
+ */
23
+ requireJsonPost(request: Request): void;
24
+ /**
25
+ * The `input` of the JSON body `{ input }`, checked against a Standard
26
+ * Schema (Zod 4, Valibot, ArkType), whose validation may be async. A
27
+ * refused input is a 400.
28
+ */
29
+ readInput<S extends StandardSchemaV1>(request: Request, schema: S): Promise<InferOutput<S>>;
30
+ }
31
+ /**
32
+ * Every action takes JSON: a cross-site page can only post a form or
33
+ * `text/plain` without a preflight, so requiring `application/json` and a
34
+ * same-origin `Origin` (or `Sec-Fetch-Site`) stops CSRF before the session
35
+ * cookie's `SameSite` is even consulted.
36
+ */
37
+ export declare function jsonActions(options?: JsonActionsOptions): JsonActions;
38
+ //# sourceMappingURL=json-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-actions.d.ts","sourceRoot":"","sources":["../../src/errors/json-actions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEvE,MAAM,WAAW,kBAAkB;IAClC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC;CAC7C;AAED,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC;;;;OAIG;IACH,SAAS,CAAC,CAAC,SAAS,gBAAgB,EACnC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,CAAC,GACP,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,WAAW,CA0CzE"}
@@ -0,0 +1,8 @@
1
+ /** The methods of a `console` a report goes through. */
2
+ export type LogMethod = 'warn' | 'error';
3
+ /**
4
+ * A log whose methods can throw without breaking what reports through
5
+ * them: a broken app log must not turn a handled failure into a rejection.
6
+ */
7
+ export declare function quietLog<M extends LogMethod>(log: Pick<Console, M>, methods: readonly M[]): Pick<Console, M>;
8
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/errors/log.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzC;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,SAAS,EAC3C,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,EACrB,OAAO,EAAE,SAAS,CAAC,EAAE,GACnB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAYlB"}
@@ -0,0 +1,11 @@
1
+ import type { CatalogueMessage } from './messages';
2
+ /** The values a formatter takes: what ICU arguments can be. */
3
+ export type MessageValues = Readonly<Record<string, string | number | boolean | object>>;
4
+ /**
5
+ * The values as the message formats them: a `YYYY-MM-DD` the message names
6
+ * as a date becomes a local `Date` (by calendar parts, so the day is the
7
+ * same in every time zone), a count or number given as a numeric string a
8
+ * number. Everything else passes as it is.
9
+ */
10
+ export declare function valuesFor(params: CatalogueMessage['params'], values: Readonly<Record<string, unknown>>): MessageValues;
11
+ //# sourceMappingURL=message-values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-values.d.ts","sourceRoot":"","sources":["../../src/errors/message-values.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,+DAA+D;AAC/D,MAAM,MAAM,aAAa,GAAG,QAAQ,CACnC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAClD,CAAC;AAWF;;;;;GAKG;AACH,wBAAgB,SAAS,CACxB,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAClC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,aAAa,CAiBf"}
@@ -0,0 +1,63 @@
1
+ import { type MessageValues } from './message-values';
2
+ export type { MessageValues } from './message-values';
3
+ /** How a message uses an argument (the API's `ErrorParamKind`). */
4
+ export type ParamKind = 'TEXT' | 'NUMBER' | 'DATE' | 'PLURAL' | 'SELECT';
5
+ /**
6
+ * One text of the API's error catalogue: the uninterpolated ICU message and
7
+ * the arguments it names, which say what to supply as a date or a number
8
+ * without parsing the message.
9
+ */
10
+ export type CatalogueMessage = {
11
+ message: string;
12
+ params: ReadonlyArray<{
13
+ name: string;
14
+ kind: ParamKind;
15
+ }>;
16
+ };
17
+ /** A language's messages by API code (`<module>.errors.<slug>`). */
18
+ export type CatalogueMessages = Readonly<Record<string, CatalogueMessage>>;
19
+ /**
20
+ * Formats an ICU message for a language: plurals, numbers and dates of that
21
+ * language. `@nxgt/material`'s `formatMessage` is one, as it is. It reports
22
+ * a message it could not format (a missing value, a stray brace) to
23
+ * `onError` instead of throwing.
24
+ */
25
+ export type MessageFormat<L extends string> = (message: string, language: L, values: MessageValues, options: {
26
+ onError(error: unknown): void;
27
+ }) => string;
28
+ export interface ApiMessagesOptions<L extends string> {
29
+ /** The app's ICU formatter: the same engine as its translator, so both word alike. */
30
+ format: MessageFormat<L>;
31
+ }
32
+ /** The catalogue the page words refusals with, by language. */
33
+ export interface ApiMessages<L extends string> {
34
+ /** Replaces a language's messages (a server's whole catalogue). */
35
+ set(language: L, messages: CatalogueMessages): void;
36
+ /** Adds messages to a language's (a browser's, from the root's data). */
37
+ add(language: L, messages: CatalogueMessages): void;
38
+ /** Forgets every message (tests). */
39
+ clear(): void;
40
+ /** The message the catalogue holds for a code in a language, if any. */
41
+ messageOf(language: L, code: string): CatalogueMessage | undefined;
42
+ /**
43
+ * A message formatted for `language` with `values`. Undefined when it
44
+ * cannot be formatted with those values (a missing argument, a day it
45
+ * cannot read) or does not parse.
46
+ */
47
+ format(entry: CatalogueMessage, values: Readonly<Record<string, unknown>> | undefined, language: L): string | undefined;
48
+ /**
49
+ * The catalogue's text of an API code in `language`, with `values`
50
+ * (`extensions.params`) filled in. Undefined when the catalogue has no
51
+ * such code or the message cannot be formatted, so the caller falls back
52
+ * to the message the API already worded.
53
+ */
54
+ apiMessage(code: string, values: Readonly<Record<string, unknown>> | undefined, language: L): string | undefined;
55
+ }
56
+ /**
57
+ * A catalogue store for the languages `L`. It is an instance, not module
58
+ * state: two copies of the package never split it, and a spec makes its own.
59
+ * Make one per app, in a module of its own, and share it with
60
+ * `apiCodeResolver` and `ApiMessagesProvider`.
61
+ */
62
+ export declare function createApiMessages<L extends string>({ format, }: ApiMessagesOptions<L>): ApiMessages<L>;
63
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/errors/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAa,MAAM,kBAAkB,CAAC;AAEjE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,mEAAmE;AACnE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEzE;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;CACzD,CAAC;AAEF,oEAAoE;AACpE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAE3E;;;;;GAKG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,IAAI,CAC7C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,CAAC,EACX,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE;IAAE,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,KACtC,MAAM,CAAC;AAEZ,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,MAAM;IACnD,sFAAsF;IACtF,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;CACzB;AAED,+DAA+D;AAC/D,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,MAAM;IAC5C,mEAAmE;IACnE,GAAG,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACpD,yEAAyE;IACzE,GAAG,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACpD,qCAAqC;IACrC,KAAK,IAAI,IAAI,CAAC;IACd,wEAAwE;IACxE,SAAS,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAAC;IACnE;;;;OAIG;IACH,MAAM,CACL,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,EACrD,QAAQ,EAAE,CAAC,GACT,MAAM,GAAG,SAAS,CAAC;IACtB;;;;;OAKG;IACH,UAAU,CACT,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,EACrD,QAAQ,EAAE,CAAC,GACT,MAAM,GAAG,SAAS,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,EACnD,MAAM,GACN,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAiDxC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The page a request is for, as `next` carries it back after sign-in: its
3
+ * path and search, without React Router's single-fetch details. A client
4
+ * navigation's `request.url` is `/todos/demo/list.data?q=a&_routes=…`
5
+ * (`/_.data` for `/`, `?index` for an index action), as the server
6
+ * runtime's own `normalizeUrl` strips them.
7
+ */
8
+ export declare function nextOf(url: string): string;
9
+ //# sourceMappingURL=page-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-path.d.ts","sourceRoot":"","sources":["../../src/errors/page-path.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgB1C"}
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { ApiMessages, CatalogueMessages } from './messages';
3
+ export interface ApiMessagesProviderProps<L extends string> {
4
+ /** The store `apiCodeResolver` reads. */
5
+ messages: ApiMessages<L>;
6
+ /** The messages the root's data carries, by language. */
7
+ value: Readonly<Record<L, CatalogueMessages>> | undefined;
8
+ children: ReactNode;
9
+ }
10
+ /**
11
+ * Fills the browser's catalogue with the messages the root's data carries
12
+ * (the few its forms word), before the children render: `apiCodeResolver`
13
+ * reads them synchronously. A server render writes nothing: the server's
14
+ * store holds the whole, fresher catalogue, and a render's data may be older
15
+ * than it. It is a plain function (no JSX, no React runtime): use it as
16
+ * `ApiMessagesProvider({ messages, value, children })` from a component, or
17
+ * as an element's type.
18
+ */
19
+ export declare function ApiMessagesProvider<L extends string>({ messages, value, children, }: ApiMessagesProviderProps<L>): ReactNode;
20
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/errors/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,wBAAwB,CAAC,CAAC,SAAS,MAAM;IACzD,yCAAyC;IACzC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACzB,yDAAyD;IACzD,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,GAAG,SAAS,CAAC;IAC1D,QAAQ,EAAE,SAAS,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,EAAE,EACrD,QAAQ,EACR,KAAK,EACL,QAAQ,GACR,EAAE,wBAAwB,CAAC,CAAC,CAAC,GAAG,SAAS,CASzC"}
@@ -0,0 +1,5 @@
1
+ /** The header an API echoes on every response: the id its logs carry. */
2
+ export declare const REQUEST_ID_HEADER = "x-request-id";
3
+ /** The request id a response carries, if the API sent one. */
4
+ export declare function requestIdOf(response: Response): string | undefined;
5
+ //# sourceMappingURL=request-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-id.d.ts","sourceRoot":"","sources":["../../src/errors/request-id.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD,8DAA8D;AAC9D,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAElE"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * What a route error of the API carries to the error page: the API's code
3
+ * and message when it gave a code, and the `X-Request-Id` of the call as the
4
+ * support reference (`errorRequestId`).
5
+ */
6
+ export type RouteErrorData = {
7
+ code?: string | undefined;
8
+ message?: string | undefined;
9
+ requestId?: string | undefined;
10
+ };
11
+ export interface RouteErrorOptions {
12
+ /** The statuses that become a route error of their own; 401, 403, 404 and 503 by default. */
13
+ statuses?: ReadonlySet<number>;
14
+ }
15
+ /**
16
+ * What a loader throws for an error from the API: an `ApiError` whose
17
+ * status is routed (401, 403, 404 or 503) becomes a route error response of
18
+ * that status, which the root `ErrorBoundary` renders as its page, with the
19
+ * call's request id when the API gave one. A thrown routed `Response` that
20
+ * carries a request id becomes one too (a bare `Response` hands the browser
21
+ * its body only). Any other `ApiError` that has a request id becomes a 500
22
+ * with just that id, so the generic error page can show it. Anything else
23
+ * is returned as it is.
24
+ */
25
+ export declare function routeError(error: unknown, { statuses }?: RouteErrorOptions): unknown;
26
+ /**
27
+ * The support reference an error page shows: the API's `X-Request-Id` a
28
+ * route error carries (`routeError`), if it carries one.
29
+ */
30
+ export declare function errorRequestId(error: unknown): string | undefined;
31
+ //# sourceMappingURL=route-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-error.d.ts","sourceRoot":"","sources":["../../src/errors/route-error.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B,CAAC;AAEF,MAAM,WAAW,iBAAiB;IACjC,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACzB,KAAK,EAAE,OAAO,EACd,EAAE,QAAiB,EAAE,GAAE,iBAAsB,GAC3C,OAAO,CAoBT;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMjE"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The part of [Standard Schema](https://standardschema.dev) v1 that `readInput`
3
+ * calls, declared here so `/errors` names no other package, not even as a
4
+ * type. It is structurally the specification's interface: a Zod 4, Valibot
5
+ * or ArkType schema, or `@alxia/core`'s `StandardSchemaV1`, is accepted as is.
6
+ */
7
+ export interface StandardSchemaV1<Input = unknown, Output = Input> {
8
+ readonly '~standard': {
9
+ readonly version: 1;
10
+ readonly vendor: string;
11
+ readonly validate: (value: unknown) => StandardResult<Output> | Promise<StandardResult<Output>>;
12
+ readonly types?: {
13
+ readonly input: Input;
14
+ readonly output: Output;
15
+ } | undefined;
16
+ };
17
+ }
18
+ /** What a schema's validation answers: the value, or the issues. */
19
+ export type StandardResult<Output> = {
20
+ readonly value: Output;
21
+ readonly issues?: undefined;
22
+ } | {
23
+ readonly issues: readonly StandardIssue[];
24
+ };
25
+ /** One thing a schema refused. */
26
+ export interface StandardIssue {
27
+ readonly message: string;
28
+ readonly path?: readonly (PropertyKey | {
29
+ readonly key: PropertyKey;
30
+ })[] | undefined;
31
+ }
32
+ /** What a schema gives back once the value passed. */
33
+ export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
34
+ //# sourceMappingURL=standard-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standard-schema.d.ts","sourceRoot":"","sources":["../../src/errors/standard-schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,GAAG,KAAK;IAChE,QAAQ,CAAC,WAAW,EAAE;QACrB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,EAAE,CAClB,KAAK,EAAE,OAAO,KACV,cAAc,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9D,QAAQ,CAAC,KAAK,CAAC,EACZ;YAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GAClD,SAAS,CAAC;KACb,CAAC;CACF;AAED,oEAAoE;AACpE,MAAM,MAAM,cAAc,CAAC,MAAM,IAC9B;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAA;CAAE,CAAC;AAEjD,kCAAkC;AAClC,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EACX,SAAS,CAAC,WAAW,GAAG;QAAE,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAA;KAAE,CAAC,EAAE,GACxD,SAAS,CAAC;CACb;AAED,sDAAsD;AACtD,MAAM,MAAM,WAAW,CAAC,MAAM,SAAS,gBAAgB,IAAI,WAAW,CACrE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAC5B,CAAC,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { data } from 'react-router';
2
+ import type { ApiErrors } from './api-errors';
3
+ import type { FormErrors } from './form-errors';
4
+ import { type RouteErrorOptions } from './route-error';
5
+ /**
6
+ * Runs a form's mutation. An `ApiError` becomes the form's errors: each
7
+ * refused field (`extensions.fields`, or the one `extensions.field` names)
8
+ * under the form field `fieldOf` names (else on the form), and any other
9
+ * code as the form's message. A 404 about none of the form's fields (the
10
+ * record itself is gone), a 401 and other failures go to the error page.
11
+ */
12
+ export declare function submitForm<L extends string, T>(errors: Pick<ApiErrors<L>, 'apiErrorMessage' | 'fieldErrors'>, language: L, run: () => Promise<T>, fieldOf: (path: string) => string | undefined, route?: RouteErrorOptions): Promise<{
13
+ ok: true;
14
+ value: T;
15
+ } | ReturnType<typeof data<FormErrors>>>;
16
+ //# sourceMappingURL=submit-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"submit-form.d.ts","sourceRoot":"","sources":["../../src/errors/submit-form.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,eAAe,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACnD,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,iBAAiB,GAAG,aAAa,CAAC,EAC7D,QAAQ,EAAE,CAAC,EACX,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACrB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,EAC7C,KAAK,CAAC,EAAE,iBAAiB,GACvB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CA0BvE"}
@@ -0,0 +1,18 @@
1
+ import { type FetcherWithComponents, useFetcher } from 'react-router';
2
+ /** What a fetcher can post as JSON. */
3
+ export type JsonBody = Parameters<FetcherWithComponents<unknown>['submit']>[0];
4
+ /**
5
+ * A fetcher posting JSON to its page's action (which reads it with
6
+ * `readJson`, and answers `fetcherResult` or the like): `post` sends a
7
+ * body, `busy` while it is in flight. `key` shares the fetcher between the
8
+ * form that posts and the component that reads the answer; `action` posts to
9
+ * another route's action instead (a resource route's, such as a
10
+ * `notifications/poll`). `post` takes the target of one call as its second
11
+ * argument (a list's rows each post to their own record's action).
12
+ */
13
+ export declare function useJsonFetcher<Action, Body extends JsonBody = JsonBody>(key?: string, action?: string): {
14
+ fetcher: ReturnType<typeof useFetcher<Action>>;
15
+ post(body: Body, to?: string): Promise<void>;
16
+ busy: boolean;
17
+ };
18
+ //# sourceMappingURL=use-json-fetcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-json-fetcher.d.ts","sourceRoot":"","sources":["../../src/errors/use-json-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEtE,uCAAuC;AACvC,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/E;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,IAAI,SAAS,QAAQ,GAAG,QAAQ,EACtE,GAAG,CAAC,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GACb;IACF,OAAO,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/C,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,EAAE,OAAO,CAAC;CACd,CASA"}
package/dist/index.js CHANGED
@@ -1,29 +1,8 @@
1
- // src/context.ts
2
- import { createContext, RouterContextProvider } from "react-router";
3
- var MISSING = Symbol("alxia context missing");
4
- var alxiaContext = createContext(MISSING);
5
- function alxiaOf(context) {
6
- const value = context.get(alxiaContext);
7
- if (value === MISSING) {
8
- throw new Error("alxiaOf(): this request has no alxia context. Serve the React Router app through alxia: add alxia() from @alxia/react-router/vite to vite.config.ts's plugins, or, with a server of your own, serve the build through reactRouter() from @alxia/react-router.");
9
- }
10
- return value;
11
- }
12
- Object.defineProperty(RouterContextProvider.prototype, "alxia", {
13
- configurable: true,
14
- enumerable: false,
15
- get() {
16
- return alxiaOf(this);
17
- }
18
- });
19
- // src/nonce.ts
20
- function nonceOf(context) {
21
- const value = context.get(alxiaContext);
22
- if (typeof value !== "object" || value === null || !("nonce" in value)) {
23
- return;
24
- }
25
- return typeof value.nonce === "string" ? value.nonce : undefined;
26
- }
1
+ import {
2
+ alxiaContext2,
3
+ alxiaOf2,
4
+ nonceOf2
5
+ } from "./chunks/index-5rt6hnhn.js";
27
6
  // src/react-router.ts
28
7
  import { fileURLToPath } from "node:url";
29
8
  import {
@@ -31,7 +10,7 @@ import {
31
10
  } from "@alxia/core";
32
11
  import {
33
12
  createRequestHandler,
34
- RouterContextProvider as RouterContextProvider2
13
+ RouterContextProvider
35
14
  } from "react-router";
36
15
 
37
16
  // src/assets.ts
@@ -78,8 +57,8 @@ function reactRouter(app, options) {
78
57
  const handle = requestHandler(options.build, mode);
79
58
  const getLoadContext = options.getLoadContext;
80
59
  const handler = async (ctx) => {
81
- const context = new RouterContextProvider2;
82
- context.set(alxiaContext, ctx);
60
+ const context = new RouterContextProvider;
61
+ context.set(alxiaContext2, ctx);
83
62
  await getLoadContext?.(ctx, context);
84
63
  const response = await handle(routerRequest(ctx), context);
85
64
  return ctx.reply(response.status, response.body ?? undefined, { headers: response.headers });
@@ -181,17 +160,17 @@ function announce({ dev, table, url }) {
181
160
  }
182
161
  // src/with-alxia.ts
183
162
  function withAlxia(fn) {
184
- return (args, ...rest) => fn({ ...args, alxia: alxiaOf(args.context) }, ...rest);
163
+ return (args, ...rest) => fn({ ...args, alxia: alxiaOf2(args.context) }, ...rest);
185
164
  }
186
165
  export {
187
- alxiaContext,
188
- alxiaOf,
166
+ alxiaContext2 as alxiaContext,
167
+ alxiaOf2 as alxiaOf,
189
168
  createServer,
190
169
  isReactRouterRoute,
191
- nonceOf,
170
+ nonceOf2 as nonceOf,
192
171
  reactRouter,
193
172
  withAlxia
194
173
  };
195
174
 
196
- //# debugId=348D809D943AF31864756E2164756E21
175
+ //# debugId=121DFAE14BF4BF9464756E2164756E21
197
176
  //# sourceMappingURL=index.js.map