@alxia/react-router 0.9.0 → 0.11.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 (100) hide show
  1. package/README.md +301 -13
  2. package/dist/chunks/index-59vhfj6n.js +10 -0
  3. package/dist/chunks/index-59vhfj6n.js.map +10 -0
  4. package/dist/chunks/index-5gqcpyyg.js +34 -0
  5. package/dist/chunks/index-5gqcpyyg.js.map +10 -0
  6. package/dist/chunks/{index-zqa30gr7.js → index-9xv2a8et.js} +7 -38
  7. package/dist/chunks/index-9xv2a8et.js.map +11 -0
  8. package/dist/chunks/index-mppz0806.js +119 -0
  9. package/dist/chunks/index-mppz0806.js.map +11 -0
  10. package/dist/chunks/index-rfr02vg4.js +40 -0
  11. package/dist/chunks/index-rfr02vg4.js.map +10 -0
  12. package/dist/chunks/index-zdbqzcjp.js +97 -0
  13. package/dist/chunks/index-zdbqzcjp.js.map +12 -0
  14. package/dist/errors/api-error.d.ts +86 -0
  15. package/dist/errors/api-error.d.ts.map +1 -0
  16. package/dist/errors/api-errors.d.ts +96 -0
  17. package/dist/errors/api-errors.d.ts.map +1 -0
  18. package/dist/errors/call-failure.d.ts +45 -0
  19. package/dist/errors/call-failure.d.ts.map +1 -0
  20. package/dist/errors/catalogue-check.d.ts +24 -0
  21. package/dist/errors/catalogue-check.d.ts.map +1 -0
  22. package/dist/errors/catalogue-read.d.ts +33 -0
  23. package/dist/errors/catalogue-read.d.ts.map +1 -0
  24. package/dist/errors/catalogue.d.ts +59 -0
  25. package/dist/errors/catalogue.d.ts.map +1 -0
  26. package/dist/errors/code-resolver.d.ts +44 -0
  27. package/dist/errors/code-resolver.d.ts.map +1 -0
  28. package/dist/errors/describe-refusal.d.ts +6 -0
  29. package/dist/errors/describe-refusal.d.ts.map +1 -0
  30. package/dist/errors/describe.d.ts +3 -0
  31. package/dist/errors/describe.d.ts.map +1 -0
  32. package/dist/errors/fetcher-result.d.ts +21 -0
  33. package/dist/errors/fetcher-result.d.ts.map +1 -0
  34. package/dist/errors/form-errors.d.ts +8 -0
  35. package/dist/errors/form-errors.d.ts.map +1 -0
  36. package/dist/errors/index.d.ts +15 -0
  37. package/dist/errors/index.d.ts.map +1 -0
  38. package/dist/errors/index.js +602 -0
  39. package/dist/errors/index.js.map +29 -0
  40. package/dist/errors/json-actions.d.ts +38 -0
  41. package/dist/errors/json-actions.d.ts.map +1 -0
  42. package/dist/errors/log.d.ts +8 -0
  43. package/dist/errors/log.d.ts.map +1 -0
  44. package/dist/errors/message-values.d.ts +11 -0
  45. package/dist/errors/message-values.d.ts.map +1 -0
  46. package/dist/errors/messages.d.ts +63 -0
  47. package/dist/errors/messages.d.ts.map +1 -0
  48. package/dist/errors/page-path.d.ts +9 -0
  49. package/dist/errors/page-path.d.ts.map +1 -0
  50. package/dist/errors/provider.d.ts +20 -0
  51. package/dist/errors/provider.d.ts.map +1 -0
  52. package/dist/errors/request-id.d.ts +5 -0
  53. package/dist/errors/request-id.d.ts.map +1 -0
  54. package/dist/errors/route-error.d.ts +31 -0
  55. package/dist/errors/route-error.d.ts.map +1 -0
  56. package/dist/errors/standard-schema.d.ts +34 -0
  57. package/dist/errors/standard-schema.d.ts.map +1 -0
  58. package/dist/errors/submit-form.d.ts +16 -0
  59. package/dist/errors/submit-form.d.ts.map +1 -0
  60. package/dist/errors/use-json-fetcher.d.ts +18 -0
  61. package/dist/errors/use-json-fetcher.d.ts.map +1 -0
  62. package/dist/pwa/build/build-worker.d.ts +41 -0
  63. package/dist/pwa/build/build-worker.d.ts.map +1 -0
  64. package/dist/pwa/build/index.d.ts +9 -0
  65. package/dist/pwa/build/index.d.ts.map +1 -0
  66. package/dist/pwa/build/index.js +9 -0
  67. package/dist/pwa/build/index.js.map +9 -0
  68. package/dist/pwa/build/precache.d.ts +17 -0
  69. package/dist/pwa/build/precache.d.ts.map +1 -0
  70. package/dist/pwa/index.js +34 -134
  71. package/dist/pwa/index.js.map +4 -8
  72. package/dist/pwa/react/index.js +1 -1
  73. package/dist/pwa/worker/index.d.ts +9 -0
  74. package/dist/pwa/worker/index.d.ts.map +1 -0
  75. package/dist/pwa/worker/index.js +144 -0
  76. package/dist/pwa/worker/index.js.map +12 -0
  77. package/dist/pwa/worker/injected.d.ts +15 -0
  78. package/dist/pwa/worker/injected.d.ts.map +1 -0
  79. package/dist/pwa/worker/plan.d.ts +65 -0
  80. package/dist/pwa/worker/plan.d.ts.map +1 -0
  81. package/dist/pwa/worker/scope.d.ts +18 -0
  82. package/dist/pwa/worker/scope.d.ts.map +1 -0
  83. package/dist/pwa/worker/start-worker.d.ts +17 -0
  84. package/dist/pwa/worker/start-worker.d.ts.map +1 -0
  85. package/dist/vite/index.d.ts +10 -0
  86. package/dist/vite/index.d.ts.map +1 -1
  87. package/dist/vite/index.js +49 -3
  88. package/dist/vite/index.js.map +6 -5
  89. package/dist/vite/runtime.d.ts +1 -1
  90. package/dist/vite/runtime.d.ts.map +1 -1
  91. package/dist/vite/worker.d.ts +16 -0
  92. package/dist/vite/worker.d.ts.map +1 -0
  93. package/docs/README.md +3 -2
  94. package/docs/guide/errors.md +838 -0
  95. package/docs/guide/how-it-works.md +7 -1
  96. package/docs/guide/pwa.md +372 -211
  97. package/docs/roadmap.md +38 -5
  98. package/docs/troubleshooting.md +689 -3
  99. package/package.json +56 -4
  100. package/dist/chunks/index-zqa30gr7.js.map +0 -12
@@ -0,0 +1,119 @@
1
+ import {
2
+ parseBrowserManifest2,
3
+ precacheList2,
4
+ assetPrecache2
5
+ } from "./index-5gqcpyyg.js";
6
+ import {
7
+ PRECACHE_DEFINE
8
+ } from "./index-59vhfj6n.js";
9
+ import {
10
+ SERVICE_WORKER_URL2,
11
+ OFFLINE_URL2
12
+ } from "./index-rfr02vg4.js";
13
+
14
+ // src/pwa/build/build-worker.ts
15
+ import { existsSync } from "node:fs";
16
+ import { readdir, readFile, stat } from "node:fs/promises";
17
+ import { basename, dirname, join, resolve, sep } from "node:path";
18
+
19
+ // src/pwa/build/precache.ts
20
+ import { createHash } from "node:crypto";
21
+ function browserManifestFile(files) {
22
+ const found = files.filter((file) => /^manifest-[\w-]+\.js$/.test(file));
23
+ const [only] = found;
24
+ if (found.length !== 1 || only === undefined) {
25
+ throw new Error(`buildWorker(): expected one React Router browser manifest, build/client/assets/manifest-*.js, found ${found.length}. Run react-router build first.`);
26
+ }
27
+ return only;
28
+ }
29
+ function revisionOf(bytes) {
30
+ return createHash("sha256").update(bytes).digest("hex").slice(0, 16);
31
+ }
32
+ function workerPrecache(input) {
33
+ const all = [
34
+ ...input.offline ? [input.offline] : [],
35
+ ...precacheList2(parseBrowserManifest2(input.manifestSource)),
36
+ ...input.include ? assetPrecache2(input.assetFiles, input.include) : []
37
+ ];
38
+ const seen = new Set;
39
+ return all.filter(({ url }) => !seen.has(url) && seen.add(url));
40
+ }
41
+
42
+ // src/pwa/build/build-worker.ts
43
+ function assertBun(onBun = globalThis.Bun !== undefined) {
44
+ if (onBun)
45
+ return;
46
+ throw new Error("buildWorker() runs on Bun: it is running on Node. Run it with bun, or run react-router build with bun --bun.");
47
+ }
48
+ function urlInClient(client, url) {
49
+ const path = join(client, url);
50
+ if (!url.startsWith("/") || /^[a-z][a-z0-9+.-]*:|^\/\//i.test(url) || !path.startsWith(client + sep) || path.endsWith(sep)) {
51
+ throw new Error(`buildWorker(): ${url} is outside ${client}.`);
52
+ }
53
+ return path;
54
+ }
55
+ async function buildWorker2(options) {
56
+ assertBun();
57
+ const root = resolve(options.root ?? process.cwd());
58
+ const client = resolve(root, options.client ?? "build/client");
59
+ const source = resolve(root, options.file);
60
+ const workerUrl = options.contract?.serviceWorkerUrl ?? SERVICE_WORKER_URL2;
61
+ const offlineUrl = options.contract?.offlineUrl ?? OFFLINE_URL2;
62
+ if (!existsSync(source)) {
63
+ throw new Error(`buildWorker(): ${options.file} does not exist.`);
64
+ }
65
+ const assets = join(client, "assets");
66
+ const files = await readdir(assets).catch(() => []);
67
+ const manifest = browserManifestFile(files);
68
+ let offline;
69
+ if (options.offline !== false) {
70
+ const page = urlInClient(client, offlineUrl);
71
+ if (!existsSync(page)) {
72
+ throw new Error(`buildWorker(): ${options.client ?? "build/client"}${offlineUrl} does not exist. Add public${offlineUrl}, or pass offline: false (\`worker: { offline: false }\` in alxia()), or pass the app's contract (\`worker: { contract }\` in alxia()).`);
73
+ }
74
+ offline = { url: offlineUrl, revision: revisionOf(await readFile(page)) };
75
+ }
76
+ const precache = workerPrecache({
77
+ manifestSource: await readFile(join(assets, manifest), "utf8"),
78
+ assetFiles: files,
79
+ offline,
80
+ include: options.precacheAssets
81
+ });
82
+ const out = urlInClient(client, workerUrl);
83
+ const result = await Bun.build({
84
+ entrypoints: [source],
85
+ outdir: dirname(out),
86
+ naming: basename(out),
87
+ target: "browser",
88
+ format: "iife",
89
+ minify: options.minify ?? true,
90
+ define: {
91
+ "process.env.NODE_ENV": '"production"',
92
+ [PRECACHE_DEFINE]: JSON.stringify(precache)
93
+ }
94
+ }).catch((error) => {
95
+ const detail = error instanceof AggregateError ? error.errors.map(String).join(`
96
+ `) : String(error);
97
+ throw new Error(`buildWorker(): Bun.build failed:
98
+ ${detail}`);
99
+ });
100
+ if (!result.success) {
101
+ throw new Error(`buildWorker(): Bun.build failed:
102
+ ${result.logs.map(String).join(`
103
+ `)}`);
104
+ }
105
+ let precacheBytes = 0;
106
+ for (const { url } of precache) {
107
+ precacheBytes += (await stat(join(client, url))).size;
108
+ }
109
+ return {
110
+ file: out,
111
+ precache,
112
+ precacheBytes,
113
+ bytes: (await stat(out)).size
114
+ };
115
+ }
116
+ export { buildWorker2 };
117
+
118
+ //# debugId=763F0D0F0869B4B964756E2164756E21
119
+ //# sourceMappingURL=index-mppz0806.js.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/pwa/build/build-worker.ts", "../src/pwa/build/precache.ts"],
4
+ "sourcesContent": [
5
+ "import { existsSync } from 'node:fs';\nimport { readdir, readFile, stat } from 'node:fs/promises';\nimport { basename, dirname, join, resolve, sep } from 'node:path';\nimport type { PrecacheEntry } from '../model/precache-list';\nimport {\n\tOFFLINE_URL,\n\tSERVICE_WORKER_URL,\n\ttype WorkerContract,\n} from '../model/worker-contract';\nimport { PRECACHE_DEFINE } from '../worker/injected';\nimport { browserManifestFile, revisionOf, workerPrecache } from './precache';\n\nexport interface BuildWorkerOptions {\n\t/**\n\t * The worker's source, relative to `root`: `app/service-worker.ts`. Named\n\t * `file`, not `entry`: `reveal` reads `alxia({ entry })`.\n\t */\n\tfile: string;\n\t/** `process.cwd()` by default. */\n\troot?: string;\n\t/** The client build, relative to `root`: `build/client` by default. */\n\tclient?: string;\n\t/** Where the worker is written and which offline page is precached. */\n\tcontract?: Pick<WorkerContract, 'serviceWorkerUrl' | 'offlineUrl'>;\n\t/** Precache the offline page, which must then exist. `true` by default. */\n\toffline?: boolean;\n\t/** More files of `assets/` to precache, such as fonts: the app's regex. */\n\tprecacheAssets?: RegExp;\n\t/** `true` by default. */\n\tminify?: boolean;\n}\n\nexport interface WorkerBuild {\n\t/** The absolute path of the worker written. */\n\tfile: string;\n\tprecache: PrecacheEntry[];\n\t/** The precached files' size, before compression. */\n\tprecacheBytes: number;\n\t/** The worker's own size. */\n\tbytes: number;\n}\n\n/** Not `requireBun` of `vite/`: `/pwa/build` imports Node's built-ins only. */\nexport function assertBun(\n\tonBun = (globalThis as { Bun?: unknown }).Bun !== undefined,\n): void {\n\tif (onBun) return;\n\tthrow new Error(\n\t\t'buildWorker() runs on Bun: it is running on Node. Run it with bun, or run react-router build with bun --bun.',\n\t);\n}\n\n/**\n * The file a root-relative URL names inside `client`. It fails closed: a URL\n * that is absolute, protocol-relative, not rooted at `/`, or that resolves to\n * `client` itself or outside it, is refused.\n */\nfunction urlInClient(client: string, url: string): string {\n\tconst path = join(client, url);\n\tif (\n\t\t!url.startsWith('/') ||\n\t\t/^[a-z][a-z0-9+.-]*:|^\\/\\//i.test(url) ||\n\t\t!path.startsWith(client + sep) ||\n\t\tpath.endsWith(sep)\n\t) {\n\t\tthrow new Error(`buildWorker(): ${url} is outside ${client}.`);\n\t}\n\treturn path;\n}\n\n/**\n * Bundles the worker (Workbox included) into the client build, after\n * `react-router build`, with the precache list written in: the offline page\n * by its content, every route's code and `precacheAssets`. A new build\n * changes the list, so the worker's bytes, so the browser installs it.\n * Runs on Bun; the only imports of this module are Node's built-ins.\n */\nexport async function buildWorker(\n\toptions: BuildWorkerOptions,\n): Promise<WorkerBuild> {\n\tassertBun();\n\tconst root = resolve(options.root ?? process.cwd());\n\tconst client = resolve(root, options.client ?? 'build/client');\n\tconst source = resolve(root, options.file);\n\tconst workerUrl = options.contract?.serviceWorkerUrl ?? SERVICE_WORKER_URL;\n\tconst offlineUrl = options.contract?.offlineUrl ?? OFFLINE_URL;\n\tif (!existsSync(source)) {\n\t\tthrow new Error(`buildWorker(): ${options.file} does not exist.`);\n\t}\n\tconst assets = join(client, 'assets');\n\tconst files = await readdir(assets).catch(() => []);\n\tconst manifest = browserManifestFile(files);\n\tlet offline: PrecacheEntry | undefined;\n\tif (options.offline !== false) {\n\t\tconst page = urlInClient(client, offlineUrl);\n\t\tif (!existsSync(page)) {\n\t\t\tthrow new Error(\n\t\t\t\t`buildWorker(): ${options.client ?? 'build/client'}${offlineUrl} does not exist. Add public${offlineUrl}, or pass offline: false (\\`worker: { offline: false }\\` in alxia()), or pass the app's contract (\\`worker: { contract }\\` in alxia()).`,\n\t\t\t);\n\t\t}\n\t\toffline = { url: offlineUrl, revision: revisionOf(await readFile(page)) };\n\t}\n\tconst precache = workerPrecache({\n\t\tmanifestSource: await readFile(join(assets, manifest), 'utf8'),\n\t\tassetFiles: files,\n\t\toffline,\n\t\tinclude: options.precacheAssets,\n\t});\n\tconst out = urlInClient(client, workerUrl);\n\tconst result = await Bun.build({\n\t\tentrypoints: [source],\n\t\toutdir: dirname(out),\n\t\tnaming: basename(out),\n\t\ttarget: 'browser',\n\t\tformat: 'iife',\n\t\tminify: options.minify ?? true,\n\t\tdefine: {\n\t\t\t// Workbox's production build: no logging, no dev assertions.\n\t\t\t'process.env.NODE_ENV': '\"production\"',\n\t\t\t[PRECACHE_DEFINE]: JSON.stringify(precache),\n\t\t},\n\t}).catch((error: unknown) => {\n\t\tconst detail =\n\t\t\terror instanceof AggregateError\n\t\t\t\t? error.errors.map(String).join('\\n')\n\t\t\t\t: String(error);\n\t\tthrow new Error(`buildWorker(): Bun.build failed:\\n${detail}`);\n\t});\n\tif (!result.success) {\n\t\tthrow new Error(\n\t\t\t`buildWorker(): Bun.build failed:\\n${result.logs.map(String).join('\\n')}`,\n\t\t);\n\t}\n\tlet precacheBytes = 0;\n\tfor (const { url } of precache) {\n\t\tprecacheBytes += (await stat(join(client, url))).size;\n\t}\n\treturn {\n\t\tfile: out,\n\t\tprecache,\n\t\tprecacheBytes,\n\t\tbytes: (await stat(out)).size,\n\t};\n}\n",
6
+ "import { createHash } from 'node:crypto';\nimport {\n\tassetPrecache,\n\ttype PrecacheEntry,\n\tparseBrowserManifest,\n\tprecacheList,\n} from '../model/precache-list';\n\n/** The one React Router browser manifest among the names in build/client/assets. */\nexport function browserManifestFile(files: readonly string[]): string {\n\tconst found = files.filter((file) => /^manifest-[\\w-]+\\.js$/.test(file));\n\tconst [only] = found;\n\tif (found.length !== 1 || only === undefined) {\n\t\tthrow new Error(\n\t\t\t`buildWorker(): expected one React Router browser manifest, build/client/assets/manifest-*.js, found ${found.length}. Run react-router build first.`,\n\t\t);\n\t}\n\treturn only;\n}\n\n/** A file's revision: the first 16 hex characters of its sha256. */\nexport function revisionOf(bytes: Uint8Array): string {\n\treturn createHash('sha256').update(bytes).digest('hex').slice(0, 16);\n}\n\n/**\n * The worker's precache list: the offline page (revisioned by its content),\n * every route's code, then the asset files `include` matches. Each URL once,\n * the first kept.\n */\nexport function workerPrecache(input: {\n\tmanifestSource: string;\n\tassetFiles: readonly string[];\n\toffline?: PrecacheEntry | undefined;\n\tinclude?: RegExp | undefined;\n}): PrecacheEntry[] {\n\tconst all = [\n\t\t...(input.offline ? [input.offline] : []),\n\t\t...precacheList(parseBrowserManifest(input.manifestSource)),\n\t\t...(input.include ? assetPrecache(input.assetFiles, input.include) : []),\n\t];\n\tconst seen = new Set<string>();\n\treturn all.filter(({ url }) => !seen.has(url) && seen.add(url));\n}\n"
7
+ ],
8
+ "mappings": "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",
9
+ "debugId": "763F0D0F0869B4B964756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,40 @@
1
+ // src/pwa/model/worker-contract.ts
2
+ var SERVICE_WORKER_URL2 = "/service-worker.js";
3
+ var OFFLINE_URL2 = "/offline.html";
4
+ var SKIP_WAITING2 = "SKIP_WAITING";
5
+ function defineWorkerContract2(options) {
6
+ const { cachePrefix } = options;
7
+ if (typeof cachePrefix !== "string" || cachePrefix === "") {
8
+ throw new TypeError(`defineWorkerContract(): cachePrefix must be a non-empty name, such as 'my-app'; got '${String(cachePrefix)}'.`);
9
+ }
10
+ if (cachePrefix.endsWith("-")) {
11
+ throw new TypeError(`defineWorkerContract(): cachePrefix must not end with '-', which the contract adds; got '${cachePrefix}'. Use '${cachePrefix.replace(/-+$/, "")}'.`);
12
+ }
13
+ const cacheVersion = options.cacheVersion ?? "v1";
14
+ if (typeof cacheVersion !== "string" || cacheVersion === "" || cacheVersion.includes("-")) {
15
+ throw new TypeError(`defineWorkerContract(): cacheVersion must be a non-empty name without '-', such as 'v2'; got '${String(cacheVersion)}'.`);
16
+ }
17
+ const cacheNames = Object.freeze({
18
+ precache: `${cachePrefix}-precache-${cacheVersion}`,
19
+ assets: `${cachePrefix}-assets-${cacheVersion}`,
20
+ static: `${cachePrefix}-static-${cacheVersion}`
21
+ });
22
+ const current = new Set(Object.values(cacheNames));
23
+ const shape = new RegExp(`^${cachePrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}-[a-z0-9]+-[^-]+$`);
24
+ const owned = (name) => shape.test(name);
25
+ const contract = {
26
+ cachePrefix,
27
+ cacheVersion,
28
+ serviceWorkerUrl: options.serviceWorkerUrl ?? SERVICE_WORKER_URL2,
29
+ offlineUrl: options.offlineUrl ?? OFFLINE_URL2,
30
+ cacheNames,
31
+ obsoleteCaches: (names) => names.filter((name) => owned(name) && !current.has(name)),
32
+ clearableCaches: (names) => names.filter((name) => owned(name) && name !== cacheNames.precache)
33
+ };
34
+ return Object.freeze(contract);
35
+ }
36
+
37
+ export { SERVICE_WORKER_URL2, OFFLINE_URL2, SKIP_WAITING2, defineWorkerContract2 };
38
+
39
+ //# debugId=5CED7E20833AD98364756E2164756E21
40
+ //# sourceMappingURL=index-rfr02vg4.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/pwa/model/worker-contract.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * What the service worker and the page agree on: the worker's address, the\n * offline page, the message that applies an update, and the names of the\n * caches the worker owns. Dependency-free: the worker bundles it, with no\n * DOM and no React.\n */\n\n/** Where the build writes the worker; registered with scope `/`. */\nexport const SERVICE_WORKER_URL = '/service-worker.js';\n\n/** The page served when a navigation fails for want of a network. */\nexport const OFFLINE_URL = '/offline.html';\n\n/** The message a page posts to a waiting worker to make it take over. */\nexport const SKIP_WAITING = 'SKIP_WAITING';\n\nexport interface WorkerContractOptions {\n\t/**\n\t * Every Cache Storage cache the worker owns is named\n\t * `<cachePrefix>-<kind>-<version>` (kind: letters and digits; version:\n\t * no `-`); a cache of another shape is never swept.\n\t * Required: two apps on one origin must not share it.\n\t */\n\tcachePrefix: string;\n\t/**\n\t * Bumped when a cache's content changes shape: the next worker deletes\n\t * every cache of the prefix that has another version when it activates.\n\t * `'v1'` by default; not empty, and without a `-`.\n\t */\n\tcacheVersion?: string;\n\t/** `SERVICE_WORKER_URL` by default. */\n\tserviceWorkerUrl?: string;\n\t/** `OFFLINE_URL` by default. */\n\tofflineUrl?: string;\n}\n\nexport type CacheNames = {\n\t/**\n\t * The offline page and the hashed scripts and stylesheets of every route.\n\t * `<prefix>-precache-<version>` is the name Workbox builds from\n\t * `setCacheNameDetails({ prefix, precache: 'precache', suffix: version })`.\n\t */\n\treadonly precache: string;\n\t/** `/assets/*` bundles: hashed names, so cache-first. */\n\treadonly assets: string;\n\t/** Icons, sprites, the logo: stale-while-revalidate. */\n\treadonly static: string;\n};\n\nexport interface WorkerContract {\n\treadonly cachePrefix: string;\n\treadonly cacheVersion: string;\n\treadonly serviceWorkerUrl: string;\n\treadonly offlineUrl: string;\n\treadonly cacheNames: CacheNames;\n\t/** The caches a worker deletes on activate: the prefix's, of another version. */\n\tobsoleteCaches(names: readonly string[]): string[];\n\t/**\n\t * The caches `clearAppCaches()` deletes: all of the prefix's but the\n\t * current precache, which holds the offline page and the app's code, never\n\t * a user's data.\n\t */\n\tclearableCaches(names: readonly string[]): string[];\n}\n\n/** Builds the contract of one app from its cache prefix. */\nexport function defineWorkerContract(\n\toptions: WorkerContractOptions,\n): WorkerContract {\n\tconst { cachePrefix } = options;\n\tif (typeof cachePrefix !== 'string' || cachePrefix === '') {\n\t\tthrow new TypeError(\n\t\t\t`defineWorkerContract(): cachePrefix must be a non-empty name, such as 'my-app'; got '${String(cachePrefix)}'.`,\n\t\t);\n\t}\n\tif (cachePrefix.endsWith('-')) {\n\t\tthrow new TypeError(\n\t\t\t`defineWorkerContract(): cachePrefix must not end with '-', which the contract adds; got '${cachePrefix}'. Use '${cachePrefix.replace(/-+$/, '')}'.`,\n\t\t);\n\t}\n\tconst cacheVersion = options.cacheVersion ?? 'v1';\n\tif (\n\t\ttypeof cacheVersion !== 'string' ||\n\t\tcacheVersion === '' ||\n\t\tcacheVersion.includes('-')\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`defineWorkerContract(): cacheVersion must be a non-empty name without '-', such as 'v2'; got '${String(cacheVersion)}'.`,\n\t\t);\n\t}\n\tconst cacheNames: CacheNames = Object.freeze({\n\t\tprecache: `${cachePrefix}-precache-${cacheVersion}`,\n\t\tassets: `${cachePrefix}-assets-${cacheVersion}`,\n\t\tstatic: `${cachePrefix}-static-${cacheVersion}`,\n\t});\n\tconst current = new Set<string>(Object.values(cacheNames));\n\t// Exactly `<prefix>-<kind>-<version>`: the prefix 'app' must not claim\n\t// 'app-admin-assets-v1' (one segment too many), but still owns a retired\n\t// kind such as 'app-pages-v0'.\n\tconst shape = new RegExp(\n\t\t`^${cachePrefix.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}-[a-z0-9]+-[^-]+$`,\n\t);\n\tconst owned = (name: string) => shape.test(name);\n\tconst contract: WorkerContract = {\n\t\tcachePrefix,\n\t\tcacheVersion,\n\t\tserviceWorkerUrl: options.serviceWorkerUrl ?? SERVICE_WORKER_URL,\n\t\tofflineUrl: options.offlineUrl ?? OFFLINE_URL,\n\t\tcacheNames,\n\t\tobsoleteCaches: (names) =>\n\t\t\tnames.filter((name) => owned(name) && !current.has(name)),\n\t\tclearableCaches: (names) =>\n\t\t\tnames.filter((name) => owned(name) && name !== cacheNames.precache),\n\t};\n\treturn Object.freeze(contract);\n}\n"
6
+ ],
7
+ "mappings": ";AAQO,IAAM,sBAAqB;AAG3B,IAAM,eAAc;AAGpB,IAAM,gBAAe;AAoDrB,SAAS,qBAAoB,CACnC,SACiB;AAAA,EACjB,QAAQ,gBAAgB;AAAA,EACxB,IAAI,OAAO,gBAAgB,YAAY,gBAAgB,IAAI;AAAA,IAC1D,MAAM,IAAI,UACT,wFAAwF,OAAO,WAAW,KAC3G;AAAA,EACD;AAAA,EACA,IAAI,YAAY,SAAS,GAAG,GAAG;AAAA,IAC9B,MAAM,IAAI,UACT,4FAA4F,sBAAsB,YAAY,QAAQ,OAAO,EAAE,KAChJ;AAAA,EACD;AAAA,EACA,MAAM,eAAe,QAAQ,gBAAgB;AAAA,EAC7C,IACC,OAAO,iBAAiB,YACxB,iBAAiB,MACjB,aAAa,SAAS,GAAG,GACxB;AAAA,IACD,MAAM,IAAI,UACT,iGAAiG,OAAO,YAAY,KACrH;AAAA,EACD;AAAA,EACA,MAAM,aAAyB,OAAO,OAAO;AAAA,IAC5C,UAAU,GAAG,wBAAwB;AAAA,IACrC,QAAQ,GAAG,sBAAsB;AAAA,IACjC,QAAQ,GAAG,sBAAsB;AAAA,EAClC,CAAC;AAAA,EACD,MAAM,UAAU,IAAI,IAAY,OAAO,OAAO,UAAU,CAAC;AAAA,EAIzD,MAAM,QAAQ,IAAI,OACjB,IAAI,YAAY,QAAQ,uBAAuB,MAAM,oBACtD;AAAA,EACA,MAAM,QAAQ,CAAC,SAAiB,MAAM,KAAK,IAAI;AAAA,EAC/C,MAAM,WAA2B;AAAA,IAChC;AAAA,IACA;AAAA,IACA,kBAAkB,QAAQ,oBAAoB;AAAA,IAC9C,YAAY,QAAQ,cAAc;AAAA,IAClC;AAAA,IACA,gBAAgB,CAAC,UAChB,MAAM,OAAO,CAAC,SAAS,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC;AAAA,IACzD,iBAAiB,CAAC,UACjB,MAAM,OAAO,CAAC,SAAS,MAAM,IAAI,KAAK,SAAS,WAAW,QAAQ;AAAA,EACpE;AAAA,EACA,OAAO,OAAO,OAAO,QAAQ;AAAA;",
8
+ "debugId": "5CED7E20833AD98364756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,97 @@
1
+ import {
2
+ SERVICE_WORKER_URL2
3
+ } from "./index-rfr02vg4.js";
4
+
5
+ // src/pwa/model/manifest.ts
6
+ var MANIFEST_PATH2 = "/manifest.webmanifest";
7
+ var MANIFEST_HEADERS2 = {
8
+ "content-type": "application/manifest+json; charset=utf-8",
9
+ "cache-control": "public, max-age=3600",
10
+ vary: "Accept-Language",
11
+ "content-security-policy": "default-src 'none'; frame-ancestors 'none'"
12
+ };
13
+ function manifestHref2(language, path = MANIFEST_PATH2) {
14
+ return `${path}?lang=${encodeURIComponent(language)}`;
15
+ }
16
+ function manifestLanguage2(url, supported, fallback) {
17
+ const requested = url.searchParams.get("lang");
18
+ return supported.find((language) => language === requested) ?? fallback;
19
+ }
20
+ var MINIMAL_2023_ICONS2 = Object.freeze([
21
+ { src: "/pwa-64x64.png", sizes: "64x64", type: "image/png" },
22
+ { src: "/pwa-192x192.png", sizes: "192x192", type: "image/png" },
23
+ { src: "/pwa-512x512.png", sizes: "512x512", type: "image/png" },
24
+ {
25
+ src: "/maskable-icon-512x512.png",
26
+ sizes: "512x512",
27
+ type: "image/png",
28
+ purpose: "maskable"
29
+ }
30
+ ].map((icon) => Object.freeze(icon)));
31
+ function webAppManifest2(options) {
32
+ return {
33
+ id: options.id ?? "/",
34
+ name: options.name,
35
+ short_name: options.shortName ?? options.name,
36
+ description: options.description,
37
+ lang: options.lang,
38
+ dir: options.dir ?? "ltr",
39
+ start_url: options.startUrl ?? "/",
40
+ scope: options.scope ?? "/",
41
+ display: options.display ?? "standalone",
42
+ theme_color: options.themeColor,
43
+ background_color: options.backgroundColor,
44
+ ...options.categories && { categories: [...options.categories] },
45
+ icons: (options.icons ?? MINIMAL_2023_ICONS2).map((icon) => ({ ...icon }))
46
+ };
47
+ }
48
+
49
+ // src/pwa/model/caching.ts
50
+ var STATIC_EXTENSION = /\.(?:js|mjs|css|woff2?|ttf|otf|svg|png|jpe?g|webp|avif|gif|ico)$/i;
51
+ var underAssets = (path) => path.startsWith("/assets/");
52
+ function cachingFor2(request, origin, options = {}) {
53
+ if (request.method !== "GET")
54
+ return "none";
55
+ if (request.mode === "navigate")
56
+ return "navigation";
57
+ const url = new URL(request.url);
58
+ if (url.origin !== origin)
59
+ return "none";
60
+ if (request.headers.has("authorization"))
61
+ return "none";
62
+ if (url.search)
63
+ return "none";
64
+ const path = url.pathname;
65
+ if (path.endsWith(".data") || path === "/__manifest" || path === (options.serviceWorkerUrl ?? options.contract?.serviceWorkerUrl ?? SERVICE_WORKER_URL2) || path === (options.manifestPath ?? MANIFEST_PATH2) || options.never?.(path)) {
66
+ return "none";
67
+ }
68
+ if (request.destination === "document" || request.destination === "iframe")
69
+ return "none";
70
+ if (!STATIC_EXTENSION.test(path))
71
+ return "none";
72
+ return (options.immutable ?? underAssets)(path) ? "immutable" : "static";
73
+ }
74
+
75
+ // src/pwa/model/storable.ts
76
+ var STATIC_CONTENT_TYPE = /^(?:(?:text|application)\/(?:javascript|x-javascript|ecmascript)|text\/css|font\/[\w.+-]+|application\/(?:font-woff2?|x-font-[\w.+-]+|vnd\.ms-fontobject)|image\/[\w.+-]+)$/i;
77
+ function isStorable2(response) {
78
+ if (response.status !== 200)
79
+ return false;
80
+ if (response.type !== "basic" && response.type !== "default")
81
+ return false;
82
+ const contentType = (response.headers.get("content-type") ?? "").split(";")[0]?.trim();
83
+ if (!contentType || !STATIC_CONTENT_TYPE.test(contentType))
84
+ return false;
85
+ const cacheControl = response.headers.get("cache-control") ?? "";
86
+ if (!/\bpublic\b/i.test(cacheControl))
87
+ return false;
88
+ if (/\b(?:no-store|private)\b/i.test(cacheControl))
89
+ return false;
90
+ const vary = response.headers.get("vary") ?? "";
91
+ return !/(?:^|,)\s*(?:\*|cookie|authorization)\s*(?:,|$)/i.test(vary);
92
+ }
93
+
94
+ export { MANIFEST_PATH2, MANIFEST_HEADERS2, manifestHref2, manifestLanguage2, MINIMAL_2023_ICONS2, webAppManifest2, cachingFor2, isStorable2 };
95
+
96
+ //# debugId=E5DD035F8EB2F4B164756E2164756E21
97
+ //# sourceMappingURL=index-zdbqzcjp.js.map
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/pwa/model/manifest.ts", "../src/pwa/model/caching.ts", "../src/pwa/model/storable.ts"],
4
+ "sourcesContent": [
5
+ "/** Where the server serves the manifest, and the document head links it. */\nexport const MANIFEST_PATH = '/manifest.webmanifest';\n\n/**\n * Cacheable for an hour, like the icons; the language travels in `?lang=`.\n * Its own policy, which loads nothing: the pages' one carries a nonce, and\n * a nonce never goes in a response a cache may keep.\n */\nexport const MANIFEST_HEADERS = {\n\t'content-type': 'application/manifest+json; charset=utf-8',\n\t'cache-control': 'public, max-age=3600',\n\tvary: 'Accept-Language',\n\t'content-security-policy': \"default-src 'none'; frame-ancestors 'none'\",\n} as const;\n\n/**\n * The manifest's URL for a page in `language`. A browser fetches the\n * manifest without cookies, so a `language` cookie would not reach it: the\n * page names its language in the query instead.\n */\nexport function manifestHref(\n\tlanguage: string,\n\tpath: string = MANIFEST_PATH,\n): string {\n\treturn `${path}?lang=${encodeURIComponent(language)}`;\n}\n\n/**\n * `?lang=` when it names one of `supported`, else `fallback`: the request's\n * Accept-Language, never its cookie, so `Vary: Accept-Language` holds.\n */\nexport function manifestLanguage<L extends string>(\n\turl: URL,\n\tsupported: readonly L[],\n\tfallback: L,\n): L {\n\tconst requested = url.searchParams.get('lang');\n\treturn supported.find((language) => language === requested) ?? fallback;\n}\n\nexport type ManifestIcon = {\n\tsrc: string;\n\tsizes: string;\n\ttype: string;\n\tpurpose?: 'any' | 'maskable' | 'monochrome';\n};\n\n/**\n * The four icons `@vite-pwa/assets-generator`'s `minimal2023Preset` writes\n * to `public/`: 64, 192 and 512 pixels, and a maskable 512.\n */\nexport const MINIMAL_2023_ICONS: readonly ManifestIcon[] = Object.freeze(\n\t[\n\t\t{ src: '/pwa-64x64.png', sizes: '64x64', type: 'image/png' },\n\t\t{ src: '/pwa-192x192.png', sizes: '192x192', type: 'image/png' },\n\t\t{ src: '/pwa-512x512.png', sizes: '512x512', type: 'image/png' },\n\t\t{\n\t\t\tsrc: '/maskable-icon-512x512.png',\n\t\t\tsizes: '512x512',\n\t\t\ttype: 'image/png',\n\t\t\tpurpose: 'maskable' as const,\n\t\t},\n\t].map((icon) => Object.freeze(icon)),\n);\n\nexport interface WebAppManifestOptions {\n\tname: string;\n\t/** `name` by default. */\n\tshortName?: string;\n\tdescription: string;\n\t/** The language of `name` and `description`. */\n\tlang: string;\n\t/** `'ltr'` by default. */\n\tdir?: 'ltr' | 'rtl' | 'auto';\n\tthemeColor: string;\n\tbackgroundColor: string;\n\tcategories?: readonly string[];\n\t/** `MINIMAL_2023_ICONS` by default. */\n\ticons?: readonly ManifestIcon[];\n\t/** `'/'` by default: the same in every language, so a language switch never makes a second app. */\n\tid?: string;\n\t/** `'/'` by default. */\n\tstartUrl?: string;\n\t/** `'/'` by default. */\n\tscope?: string;\n\t/** `'standalone'` by default. */\n\tdisplay?: 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser';\n}\n\n/** The web app manifest's JSON, with its snake_case keys. */\nexport type WebAppManifest = {\n\tid: string;\n\tname: string;\n\tshort_name: string;\n\tdescription: string;\n\tlang: string;\n\tdir: 'ltr' | 'rtl' | 'auto';\n\tstart_url: string;\n\tscope: string;\n\tdisplay: 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser';\n\ttheme_color: string;\n\tbackground_color: string;\n\tcategories?: string[];\n\ticons: ManifestIcon[];\n};\n\n/**\n * A web app manifest from the app's own (translated) texts and colours.\n * `id`, `start_url` and `scope` default to `'/'` whatever the language.\n */\nexport function webAppManifest(options: WebAppManifestOptions): WebAppManifest {\n\treturn {\n\t\tid: options.id ?? '/',\n\t\tname: options.name,\n\t\tshort_name: options.shortName ?? options.name,\n\t\tdescription: options.description,\n\t\tlang: options.lang,\n\t\tdir: options.dir ?? 'ltr',\n\t\tstart_url: options.startUrl ?? '/',\n\t\tscope: options.scope ?? '/',\n\t\tdisplay: options.display ?? 'standalone',\n\t\ttheme_color: options.themeColor,\n\t\tbackground_color: options.backgroundColor,\n\t\t...(options.categories && { categories: [...options.categories] }),\n\t\ticons: (options.icons ?? MINIMAL_2023_ICONS).map((icon) => ({ ...icon })),\n\t};\n}\n",
6
+ "import { MANIFEST_PATH } from './manifest';\nimport { SERVICE_WORKER_URL, type WorkerContract } from './worker-contract';\n\n/**\n * Which requests the service worker caches, and how. Pure, so that what is\n * never cached is tested, not hoped for.\n *\n * - `navigation`: a page load. Always from the network, never stored; the\n * offline page answers only when the network fails.\n * - `immutable`: a hashed bundle: cache-first.\n * - `static`: another same-origin static file (icon, sprite, logo):\n * stale-while-revalidate.\n * - `none`: left to the browser, untouched: every non-GET, any other\n * origin, React Router's `.data` and `/__manifest`, the worker and the\n * manifest themselves, the app's own `never` paths, and anything carrying\n * credentials.\n */\nexport type Caching = 'navigation' | 'immutable' | 'static' | 'none';\n\n/** What a `Request` exposes that the decision reads. */\nexport interface RequestFacts {\n\treadonly method: string;\n\treadonly url: string;\n\treadonly mode: string;\n\treadonly destination: string;\n\treadonly headers: Pick<Headers, 'has'>;\n}\n\nexport interface CachingOptions {\n\t/**\n\t * The worker's own path, never cached. Wins over `contract`;\n\t * `SERVICE_WORKER_URL` when neither is given.\n\t */\n\tserviceWorkerUrl?: string;\n\t/** The app's contract, whose `serviceWorkerUrl` is never cached. */\n\tcontract?: Pick<WorkerContract, 'serviceWorkerUrl'>;\n\t/** The web app manifest's path, never cached. `MANIFEST_PATH` by default. */\n\tmanifestPath?: string;\n\t/** Paths of the app answered fresh every time, such as `(p) => p.startsWith('/api/')`. */\n\tnever?: (path: string) => boolean;\n\t/**\n\t * Which static paths are immutable (cache-first) rather than static.\n\t * `(path) => path.startsWith('/assets/')` by default, as `serveClient`\n\t * serves them.\n\t */\n\timmutable?: (path: string) => boolean;\n}\n\nconst STATIC_EXTENSION =\n\t/\\.(?:js|mjs|css|woff2?|ttf|otf|svg|png|jpe?g|webp|avif|gif|ico)$/i;\n\nconst underAssets = (path: string) => path.startsWith('/assets/');\n\n/** How the worker answers `request`, for an app served at `origin`. */\nexport function cachingFor(\n\trequest: RequestFacts,\n\torigin: string,\n\toptions: CachingOptions = {},\n): Caching {\n\tif (request.method !== 'GET') return 'none';\n\tif (request.mode === 'navigate') return 'navigation';\n\tconst url = new URL(request.url);\n\tif (url.origin !== origin) return 'none';\n\tif (request.headers.has('authorization')) return 'none';\n\tif (url.search) return 'none';\n\tconst path = url.pathname;\n\tif (\n\t\tpath.endsWith('.data') || // React Router single fetch: loader data\n\t\tpath === '/__manifest' || // React Router route discovery\n\t\tpath ===\n\t\t\t(options.serviceWorkerUrl ??\n\t\t\t\toptions.contract?.serviceWorkerUrl ??\n\t\t\t\tSERVICE_WORKER_URL) ||\n\t\tpath === (options.manifestPath ?? MANIFEST_PATH) ||\n\t\toptions.never?.(path)\n\t) {\n\t\treturn 'none';\n\t}\n\tif (request.destination === 'document' || request.destination === 'iframe')\n\t\treturn 'none';\n\tif (!STATIC_EXTENSION.test(path)) return 'none';\n\treturn (options.immutable ?? underAssets)(path) ? 'immutable' : 'static';\n}\n",
7
+ "/**\n * The content types of the static extensions: scripts, styles, fonts and\n * images (SVG and icons included). A page, JSON or anything else served\n * under a static-looking path is refused all the same.\n */\nconst STATIC_CONTENT_TYPE =\n\t/^(?:(?:text|application)\\/(?:javascript|x-javascript|ecmascript)|text\\/css|font\\/[\\w.+-]+|application\\/(?:font-woff2?|x-font-[\\w.+-]+|vnd\\.ms-fontobject)|image\\/[\\w.+-]+)$/i;\n\n/**\n * Whether a response may be stored: a full, same-origin 200 of a static\n * content type, that the server marked `public` (alxia does on every\n * static file it serves; a route answering an image, a user's photo, does\n * not), not private nor no-store, and that does not vary on who asks.\n */\nexport function isStorable(response: Response): boolean {\n\tif (response.status !== 200) return false;\n\tif (response.type !== 'basic' && response.type !== 'default') return false;\n\tconst contentType = (response.headers.get('content-type') ?? '')\n\t\t.split(';')[0]\n\t\t?.trim();\n\tif (!contentType || !STATIC_CONTENT_TYPE.test(contentType)) return false;\n\tconst cacheControl = response.headers.get('cache-control') ?? '';\n\tif (!/\\bpublic\\b/i.test(cacheControl)) return false;\n\tif (/\\b(?:no-store|private)\\b/i.test(cacheControl)) return false;\n\tconst vary = response.headers.get('vary') ?? '';\n\treturn !/(?:^|,)\\s*(?:\\*|cookie|authorization)\\s*(?:,|$)/i.test(vary);\n}\n"
8
+ ],
9
+ "mappings": "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",
10
+ "debugId": "E5DD035F8EB2F4B164756E2164756E21",
11
+ "names": []
12
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The shape of an API's refusal, as `@nxgt/graphql-client`'s `ApiError`
3
+ * carries it, declared structurally: `/errors` never imports the client, so
4
+ * an app needs it only if it calls the API through it. `api-error.spec.ts`
5
+ * holds the two in step.
6
+ */
7
+ /** One refused input field: `extensions.fields` of a validation refusal. */
8
+ export interface ApiFieldErrorLike {
9
+ readonly path: string;
10
+ readonly code: string;
11
+ readonly message: string;
12
+ /** The values the field's message interpolates. */
13
+ readonly params?: Readonly<Record<string, unknown>>;
14
+ }
15
+ /** The values a message interpolates; a throttle's wait is `minutes` or `retryAfter`. */
16
+ export interface ApiErrorParamsLike {
17
+ readonly minutes?: unknown;
18
+ readonly retryAfter?: unknown;
19
+ readonly [key: string]: unknown;
20
+ }
21
+ /**
22
+ * One GraphQL error's `extensions`. `code` is the API's own
23
+ * `<module>.errors.<slug>`; the rest is what its messages name.
24
+ */
25
+ export interface ApiErrorExtensionsLike {
26
+ readonly code?: string;
27
+ /** The values the message interpolates, when the API sends them. */
28
+ readonly params?: ApiErrorParamsLike;
29
+ /** A validation refusal: every field at fault, by input path. */
30
+ readonly fields?: readonly ApiFieldErrorLike[];
31
+ /** Any other refusal about one input field, by its path. */
32
+ readonly field?: string;
33
+ /** A throttle's wait, in minutes (`waitMinutes` coerces a string). */
34
+ readonly minutes?: unknown;
35
+ /** A throttle's wait, in seconds. */
36
+ readonly retryAfter?: unknown;
37
+ readonly [key: string]: unknown;
38
+ }
39
+ /** One error of a response's `errors`. */
40
+ export interface ApiErrorEntryLike {
41
+ readonly message: string;
42
+ readonly extensions?: ApiErrorExtensionsLike;
43
+ }
44
+ /** A refusal read from a GraphQL API: what `ApiError` has, and the request id. */
45
+ export interface ApiErrorLike extends Error {
46
+ readonly errors: readonly ApiErrorEntryLike[];
47
+ /** The first error's `extensions.code`. */
48
+ readonly code: string | undefined;
49
+ /** The HTTP status to answer with. */
50
+ readonly status: number;
51
+ /** The id the API logged the request under, when the caller kept it. */
52
+ readonly requestId?: string;
53
+ }
54
+ /** Whether `error` is an `Error` of this `name`: the client's errors are told by it, never `instanceof`. */
55
+ export declare const isNamed: (error: unknown, name: string) => error is Error;
56
+ /**
57
+ * Whether `error` is an `ApiError` of `@nxgt/graphql-client`, by the test
58
+ * that client's `isApiError` makes (an `Error` named `ApiError` with an
59
+ * `errors` array), plus a numeric `status`, which `ApiErrorLike` promises.
60
+ * It holds across two copies of the client.
61
+ */
62
+ export declare function isApiErrorLike(error: unknown): error is ApiErrorLike;
63
+ /**
64
+ * The minutes a wait lasts, as the API words it: `minutes` (at least one, in
65
+ * `extensions.params` or beside it), else the `retryAfter` seconds rounded
66
+ * up, else one.
67
+ */
68
+ export declare function waitMinutes(extensions: ApiErrorExtensionsLike | undefined): number;
69
+ /**
70
+ * The values a message names: `extensions.params` over the extensions
71
+ * themselves, where older errors and the extras of a few sit.
72
+ */
73
+ export declare function valuesOf(extensions: ApiErrorExtensionsLike | undefined): Record<string, unknown> | undefined;
74
+ /** `@nxgt/graphql-client`'s `ApiStatusError`, as far as the failure reads it. */
75
+ export interface StatusErrorLike extends Error {
76
+ status: number;
77
+ statusText?: string | undefined;
78
+ body?: unknown;
79
+ }
80
+ /** `@nxgt/graphql-client`'s `ApiUnavailableError`, as far as the failure reads it. */
81
+ export interface UnavailableErrorLike extends Error {
82
+ reason: string;
83
+ }
84
+ export declare const isStatusError: (error: unknown) => error is StatusErrorLike;
85
+ export declare const isUnavailableError: (error: unknown) => error is UnavailableErrorLike;
86
+ //# sourceMappingURL=api-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-error.d.ts","sourceRoot":"","sources":["../../src/errors/api-error.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED,yFAAyF;AACzF,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IACrC,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC/C,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,qCAAqC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC;AAED,0CAA0C;AAC1C,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,sBAAsB,CAAC;CAC7C;AAED,kFAAkF;AAClF,MAAM,WAAW,YAAa,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC9C,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,sCAAsC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,4GAA4G;AAC5G,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,EAAE,MAAM,MAAM,KAAG,KAAK,IAAI,KAClB,CAAC;AAE/C;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAMpE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAC1B,UAAU,EAAE,sBAAsB,GAAG,SAAS,GAC5C,MAAM,CAKR;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACvB,UAAU,EAAE,sBAAsB,GAAG,SAAS,GAC5C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAKrC;AAED,iFAAiF;AACjF,MAAM,WAAW,eAAgB,SAAQ,KAAK;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAED,sFAAsF;AACtF,MAAM,WAAW,oBAAqB,SAAQ,KAAK;IAClD,MAAM,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,eAEG,CAAC;AAE5D,eAAO,MAAM,kBAAkB,GAC9B,OAAO,OAAO,KACZ,KAAK,IAAI,oBAE+C,CAAC"}
@@ -0,0 +1,96 @@
1
+ import type { data } from 'react-router';
2
+ import type { ApiErrorLike } from './api-error';
3
+ import type { FormErrors } from './form-errors';
4
+ import type { ApiMessages } from './messages';
5
+ import type { RouteErrorOptions } from './route-error';
6
+ /** A refused call, as a page says it: the message, and the HTTP status to answer. */
7
+ export type ApiRefusal = {
8
+ message: string;
9
+ status: number;
10
+ };
11
+ export interface ApiErrorsOptions<L extends string> {
12
+ /** The catalogue the API's codes are worded from. */
13
+ messages: ApiMessages<L>;
14
+ /**
15
+ * The app's own word for an API it cannot reach (a 503 `Response`): not
16
+ * an API error, so never one of the catalogue's codes.
17
+ */
18
+ unavailable(language: L): string;
19
+ /**
20
+ * The app's generic refusal, said for an error with no API code (a raw
21
+ * `BAD_USER_INPUT` before the API's validation layer), never the raw
22
+ * message. Without it, such an error says the message the API sent.
23
+ */
24
+ refused?(language: L): string;
25
+ /**
26
+ * An app's own wording for given codes, such as the API's request limits
27
+ * (depth, cost, introspection), whose params differ and name no field.
28
+ * Checked after `refused`, before the catalogue.
29
+ */
30
+ byCode?: Readonly<Record<string, (error: ApiErrorLike, language: L) => string>>;
31
+ /** Whether a code is an API code; `<module>.errors.<slug>` by default. */
32
+ isApiCode?(code: string): boolean;
33
+ }
34
+ /** What a page says of an API's refusals, in a language. */
35
+ export interface ApiErrors<L extends string> {
36
+ /**
37
+ * The catalogue's text of an API code in `language`, else `fallback` (the
38
+ * message the API already worded). `values` are the message's arguments
39
+ * (`extensions.params`).
40
+ */
41
+ translateCode(code: string | undefined, fallback: string, language: L, values?: Readonly<Record<string, unknown>>): string;
42
+ /**
43
+ * What to show for an API error: `refused` for an error with no API code,
44
+ * when given; else `byCode`'s wording of its code; else the catalogue's
45
+ * text; else the message the API already worded.
46
+ */
47
+ apiErrorMessage(error: ApiErrorLike, language: L): string;
48
+ /**
49
+ * The message of each refused field, by input path, ready for a form: the
50
+ * entries of `extensions.fields`, else the one field `extensions.field`
51
+ * names, with the error's own message. Empty for neither.
52
+ */
53
+ fieldErrors(error: ApiErrorLike, language: L): Record<string, string>;
54
+ /** `unavailable`, in `language`. */
55
+ unavailableMessage(language: L): string;
56
+ /**
57
+ * What a failed call says to the user: an API refusal's message and its
58
+ * status, or an API it cannot reach (a 503 `Response`: the unavailable
59
+ * word). Undefined for anything else, which the caller rethrows.
60
+ */
61
+ apiRefusal(error: unknown, language: L): ApiRefusal | undefined;
62
+ /**
63
+ * A fetcher's refusal: `apiRefusal`'s, so the page stays where it was. A
64
+ * 401 (the session caller redirects first) or any other failure is
65
+ * thrown, through `routeError`, for the error page.
66
+ */
67
+ fetcherRefusal(error: unknown, language: L, route?: RouteErrorOptions): ApiRefusal;
68
+ /**
69
+ * Runs the mutation of an action a fetcher posted to: `{ ok: true }` with
70
+ * `about` and the mutation's value, or `{ ok: false, error }` with `about`
71
+ * and the refusal's message, answered with its HTTP status.
72
+ */
73
+ fetcherResult<About extends object, Value extends object = Record<never, never>>(language: L, about: About, run: () => Promise<Value>, route?: RouteErrorOptions): Promise<({
74
+ ok: true;
75
+ } & About & Value) | ReturnType<typeof data<{
76
+ ok: false;
77
+ error: string;
78
+ } & About>>>;
79
+ /**
80
+ * Runs a form's mutation: `{ ok: true, value }`, or the form's errors
81
+ * (`fieldErrors` under the form field `fieldOf` names an input path by,
82
+ * and a `formError` for the rest) with the refusal's status. A 404 about
83
+ * none of the form's fields, a 401 and other failures are thrown for the
84
+ * error page, through `routeError` (with `route`).
85
+ */
86
+ submitForm<T>(language: L, run: () => Promise<T>, fieldOf: (path: string) => string | undefined, route?: RouteErrorOptions): Promise<{
87
+ ok: true;
88
+ value: T;
89
+ } | ReturnType<typeof data<FormErrors>>>;
90
+ }
91
+ /**
92
+ * The wording of an API's refusals for the languages `L`, from the
93
+ * catalogue `messages` holds. Make one per app, beside its `createApiMessages`.
94
+ */
95
+ export declare function createApiErrors<L extends string>(options: ApiErrorsOptions<L>): ApiErrors<L>;
96
+ //# sourceMappingURL=api-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-errors.d.ts","sourceRoot":"","sources":["../../src/errors/api-errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ export interface CallFailureOptions {
2
+ /** What a log line names the API by, such as `my-api https://api.example.com/graphql`. */
3
+ api: string;
4
+ /** The operation's name, for the log (`operationName(document)`). */
5
+ operation?: string;
6
+ /** The `x-request-id` the API answered, when it did (`requestIdOf(response)`). */
7
+ requestId?: string;
8
+ /** The incoming request's signal: an aborted call is answered 503. */
9
+ signal?: AbortSignal;
10
+ /** The statuses without a GraphQL body that become a `Response` of their own; 401, 403 and 404 by default. */
11
+ passedThrough?: ReadonlySet<number>;
12
+ /** Where the lines go; `console` by default. */
13
+ log?: Pick<Console, 'warn' | 'error'>;
14
+ }
15
+ /**
16
+ * What a call became once it failed, for a loader or an action to throw: the
17
+ * `ApiError` as it is (carrying `requestId`), a `Response` of the API's own
18
+ * 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
19
+ * unreachable, slow, garbled or aborted API, and any other error as it is.
20
+ * Every case it recognises is logged with the request id, when the API gave one, and never
21
+ * the headers, the variables nor a token; the id rides on the `ApiError` as
22
+ * `requestId` and on a `Response` as `x-request-id`, where `routeError` reads
23
+ * it.
24
+ *
25
+ * It recognises `@nxgt/graphql-client`'s errors by their `name`, never with
26
+ * `instanceof`, so it needs no import of the client and holds across two
27
+ * copies of it.
28
+ */
29
+ export declare function apiCallFailure(error: unknown, { api, operation, requestId, signal, passedThrough, log: appLog, }: CallFailureOptions): unknown;
30
+ /** A document, as far as `operationName` reads it: `graphql`'s `DocumentNode` fits. */
31
+ export interface DocumentLike {
32
+ readonly definitions: readonly {
33
+ readonly kind: string;
34
+ readonly operation?: string | undefined;
35
+ readonly name?: {
36
+ readonly value: string;
37
+ } | undefined;
38
+ }[];
39
+ }
40
+ /**
41
+ * The name of the operation a document holds, for a log line: its name, else
42
+ * its kind (`query`, `mutation`, `subscription`), else `operation`.
43
+ */
44
+ export declare function operationName(document: DocumentLike): string;
45
+ //# sourceMappingURL=call-failure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-failure.d.ts","sourceRoot":"","sources":["../../src/errors/call-failure.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,kBAAkB;IAClC,0FAA0F;IAC1F,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,8GAA8G;IAC9G,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,gDAAgD;IAChD,GAAG,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC7B,KAAK,EAAE,OAAO,EACd,EACC,GAAG,EACH,SAAuB,EACvB,SAAS,EACT,MAAM,EACN,aAA8B,EAC9B,GAAG,EAAE,MAAgB,GACrB,EAAE,kBAAkB,GACnB,OAAO,CAyCT;AAED,uFAAuF;AACvF,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,WAAW,EAAE,SAAS;QAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxC,QAAQ,CAAC,IAAI,CAAC,EAAE;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;SAAE,GAAG,SAAS,CAAC;KACvD,EAAE,CAAC;CACJ;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAO5D"}
@@ -0,0 +1,24 @@
1
+ import type { Held } from './catalogue-read';
2
+ /** What the TTL check needs: the reader's state and the app's own calls. */
3
+ export interface CheckContext<L extends string> {
4
+ reader: {
5
+ held: Map<L, Held>;
6
+ load(language: L, now: number): Promise<void>;
7
+ };
8
+ versions(): Promise<Partial<Record<L, string>>>;
9
+ ttl(): number;
10
+ retryMs: number;
11
+ log: Pick<Console, 'error'>;
12
+ }
13
+ /**
14
+ * Asks the versions in one call and reads again the messages of each
15
+ * language that moved. An API that cannot be reached keeps the messages
16
+ * held (stale, still worded) and is asked again after `retryMs`.
17
+ */
18
+ export declare function checkVersions<L extends string>({ reader, versions, ttl, retryMs, log }: CheckContext<L>, now: number): Promise<void>;
19
+ /**
20
+ * The TTL, whose getter is the app's: one that throws, or that is not a
21
+ * finite number of at least 0, is logged and `fallback` is used.
22
+ */
23
+ export declare function safeTtl(ttlMs: number | (() => number), fallback: number, log: Pick<Console, 'error'>): () => number;
24
+ //# sourceMappingURL=catalogue-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogue-check.d.ts","sourceRoot":"","sources":["../../src/errors/catalogue-check.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,4EAA4E;AAC5E,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM;IAC7C,MAAM,EAAE;QACP,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACnB,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC9C,CAAC;IACF,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAChD,GAAG,IAAI,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,MAAM,EACnD,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,EACxD,GAAG,EAAE,MAAM,iBAoBX;AAED;;;GAGG;AACH,wBAAgB,OAAO,CACtB,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,EAC9B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,gBAc3B"}