@alxia/react-router 0.8.1 → 0.10.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 (94) hide show
  1. package/README.md +222 -10
  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-9xv2a8et.js +108 -0
  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/pwa/build/build-worker.d.ts +41 -0
  15. package/dist/pwa/build/build-worker.d.ts.map +1 -0
  16. package/dist/pwa/build/index.d.ts +9 -0
  17. package/dist/pwa/build/index.d.ts.map +1 -0
  18. package/dist/pwa/build/index.js +9 -0
  19. package/dist/pwa/build/index.js.map +9 -0
  20. package/dist/pwa/build/precache.d.ts +17 -0
  21. package/dist/pwa/build/precache.d.ts.map +1 -0
  22. package/dist/pwa/index.d.ts +14 -0
  23. package/dist/pwa/index.d.ts.map +1 -0
  24. package/dist/pwa/index.js +65 -0
  25. package/dist/pwa/index.js.map +10 -0
  26. package/dist/pwa/model/caching.d.ts +46 -0
  27. package/dist/pwa/model/caching.d.ts.map +1 -0
  28. package/dist/pwa/model/clear-app-caches.d.ts +9 -0
  29. package/dist/pwa/model/clear-app-caches.d.ts.map +1 -0
  30. package/dist/pwa/model/manifest.d.ts +80 -0
  31. package/dist/pwa/model/manifest.d.ts.map +1 -0
  32. package/dist/pwa/model/precache-list.d.ts +40 -0
  33. package/dist/pwa/model/precache-list.d.ts.map +1 -0
  34. package/dist/pwa/model/registration.d.ts +39 -0
  35. package/dist/pwa/model/registration.d.ts.map +1 -0
  36. package/dist/pwa/model/start-service-worker.d.ts +24 -0
  37. package/dist/pwa/model/start-service-worker.d.ts.map +1 -0
  38. package/dist/pwa/model/storable.d.ts +8 -0
  39. package/dist/pwa/model/storable.d.ts.map +1 -0
  40. package/dist/pwa/model/update-notice.d.ts +38 -0
  41. package/dist/pwa/model/update-notice.d.ts.map +1 -0
  42. package/dist/pwa/model/worker-contract.d.ts +61 -0
  43. package/dist/pwa/model/worker-contract.d.ts.map +1 -0
  44. package/dist/pwa/react/index.d.ts +9 -0
  45. package/dist/pwa/react/index.d.ts.map +1 -0
  46. package/dist/pwa/react/index.js +75 -0
  47. package/dist/pwa/react/index.js.map +13 -0
  48. package/dist/pwa/react/use-online.d.ts +6 -0
  49. package/dist/pwa/react/use-online.d.ts.map +1 -0
  50. package/dist/pwa/react/use-service-worker.d.ts +33 -0
  51. package/dist/pwa/react/use-service-worker.d.ts.map +1 -0
  52. package/dist/pwa/react/use-update-notice.d.ts +14 -0
  53. package/dist/pwa/react/use-update-notice.d.ts.map +1 -0
  54. package/dist/pwa/server/index.d.ts +7 -0
  55. package/dist/pwa/server/index.d.ts.map +1 -0
  56. package/dist/pwa/server/index.js +18 -0
  57. package/dist/pwa/server/index.js.map +10 -0
  58. package/dist/pwa/server/no-cache.d.ts +22 -0
  59. package/dist/pwa/server/no-cache.d.ts.map +1 -0
  60. package/dist/pwa/worker/index.d.ts +9 -0
  61. package/dist/pwa/worker/index.d.ts.map +1 -0
  62. package/dist/pwa/worker/index.js +144 -0
  63. package/dist/pwa/worker/index.js.map +12 -0
  64. package/dist/pwa/worker/injected.d.ts +15 -0
  65. package/dist/pwa/worker/injected.d.ts.map +1 -0
  66. package/dist/pwa/worker/plan.d.ts +65 -0
  67. package/dist/pwa/worker/plan.d.ts.map +1 -0
  68. package/dist/pwa/worker/scope.d.ts +18 -0
  69. package/dist/pwa/worker/scope.d.ts.map +1 -0
  70. package/dist/pwa/worker/start-worker.d.ts +17 -0
  71. package/dist/pwa/worker/start-worker.d.ts.map +1 -0
  72. package/dist/vite/index.d.ts +10 -0
  73. package/dist/vite/index.d.ts.map +1 -1
  74. package/dist/vite/index.js +49 -3
  75. package/dist/vite/index.js.map +6 -5
  76. package/dist/vite/runtime.d.ts +1 -1
  77. package/dist/vite/runtime.d.ts.map +1 -1
  78. package/dist/vite/worker.d.ts +16 -0
  79. package/dist/vite/worker.d.ts.map +1 -0
  80. package/docs/README.md +12 -3
  81. package/docs/guide/beside-the-pages.md +178 -0
  82. package/docs/guide/csp-nonce.md +100 -0
  83. package/docs/guide/customising-the-server.md +292 -0
  84. package/docs/guide/deploying.md +99 -0
  85. package/docs/guide/getting-started.md +112 -0
  86. package/docs/guide/how-it-works.md +268 -0
  87. package/docs/guide/openapi.md +51 -0
  88. package/docs/guide/pwa.md +636 -0
  89. package/docs/guide/testing.md +42 -0
  90. package/docs/guide/typing-the-loaders.md +311 -0
  91. package/docs/guide.md +27 -1391
  92. package/docs/roadmap.md +39 -1
  93. package/docs/troubleshooting.md +402 -11
  94. package/package.json +70 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"precache-list.d.ts","sourceRoot":"","sources":["../../../src/pwa/model/precache-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,wEAAwE;AACxE,KAAK,cAAc,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE9E,mEAAmE;AACnE,MAAM,MAAM,eAAe,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACvC,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAKrE,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CASpE;AAED,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,aAAa,EAAE,CAcvE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC5B,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,OAAO,EAAE,MAAM,EACf,IAAI,SAAa,GACf,aAAa,EAAE,CAQjB"}
@@ -0,0 +1,39 @@
1
+ /** What the registration reads of `navigator.serviceWorker`. */
2
+ export type ServiceWorkerContainerLike = Pick<ServiceWorkerContainer, 'controller' | 'addEventListener' | 'register'>;
3
+ /**
4
+ * Calls `onWaiting` with each new version that finished installing while an
5
+ * older one controls the page: the one waiting now, and each found later.
6
+ * A first install controls nothing yet, so it offers nothing.
7
+ */
8
+ export declare function watchForUpdates(registration: ServiceWorkerRegistration, container: Pick<ServiceWorkerContainerLike, 'controller'>, onWaiting: (worker: ServiceWorker) => void): void;
9
+ /** Moves this page onto a waiting worker: `apply(worker)`. */
10
+ export type ApplyUpdate = (worker: Pick<ServiceWorker, 'postMessage' | 'state'>) => void;
11
+ /**
12
+ * The one update path of a page, created once: `apply(worker)` activates
13
+ * the waiting worker and reloads once, when it takes control, so the page
14
+ * runs the version its assets came from.
15
+ *
16
+ * - Several tabs: another tab may have activated the worker already. Its
17
+ * `controllerchange` reached this page before anyone asked, so this page
18
+ * did not reload (a form may be half filled); its "Reload" then reloads
19
+ * at once, the worker being active.
20
+ * - Clicked twice, or a `controllerchange` after a direct reload: one
21
+ * reload, never two.
22
+ */
23
+ export declare function createUpdater(container: Pick<ServiceWorkerContainerLike, 'controller' | 'addEventListener'>, reload?: () => void): ApplyUpdate;
24
+ /** Runs `run` once the page has loaded: registering never competes with it. */
25
+ export declare function afterLoad(run: () => void): void;
26
+ export interface RegisterOptions {
27
+ /** The worker's path. `SERVICE_WORKER_URL` by default. */
28
+ url?: string;
29
+ /** The scope it controls. `'/'` by default. */
30
+ scope?: string;
31
+ }
32
+ /**
33
+ * Registers the worker and watches for updates, handing each waiting worker
34
+ * to `onWaiting` with the page's one `apply` (`createUpdater`, made on the
35
+ * first call: one per page). A tab coming back to the foreground checks for
36
+ * an update, as a long-lived page otherwise would only on its next full load.
37
+ */
38
+ export declare function registerServiceWorker(container: ServiceWorkerContainerLike, onWaiting: (worker: ServiceWorker, apply: ApplyUpdate) => void, options?: RegisterOptions): Promise<ServiceWorkerRegistration>;
39
+ //# sourceMappingURL=registration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../../src/pwa/model/registration.ts"],"names":[],"mappings":"AAEA,gEAAgE;AAChE,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC5C,sBAAsB,EACtB,YAAY,GAAG,kBAAkB,GAAG,UAAU,CAC9C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,eAAe,CAC9B,YAAY,EAAE,yBAAyB,EACvC,SAAS,EAAE,IAAI,CAAC,0BAA0B,EAAE,YAAY,CAAC,EACzD,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,GACxC,IAAI,CAWN;AAED,8DAA8D;AAC9D,MAAM,MAAM,WAAW,GAAG,CACzB,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,KAChD,IAAI,CAAC;AAEV;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC5B,SAAS,EAAE,IAAI,CACd,0BAA0B,EAC1B,YAAY,GAAG,kBAAkB,CACjC,EACD,MAAM,GAAE,MAAM,IAAqC,GACjD,WAAW,CAoBb;AAED,+EAA+E;AAC/E,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,IAAI,GAAG,IAAI,CAG/C;AAED,MAAM,WAAW,eAAe;IAC/B,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAID;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAC1C,SAAS,EAAE,0BAA0B,EACrC,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,EAC9D,OAAO,GAAE,eAAoB,GAC3B,OAAO,CAAC,yBAAyB,CAAC,CAiBpC"}
@@ -0,0 +1,24 @@
1
+ import type { ServiceWorkerContainerLike } from './registration';
2
+ import type { UpdateNoticeStore } from './update-notice';
3
+ /** What starting reads of the browser: `navigator`, which may have no worker. */
4
+ export interface ServiceWorkerEnvironment {
5
+ navigator: object & {
6
+ serviceWorker?: ServiceWorkerContainerLike;
7
+ };
8
+ }
9
+ export interface StartServiceWorkerOptions {
10
+ enabled: boolean;
11
+ url?: string;
12
+ scope?: string;
13
+ notice: Pick<UpdateNoticeStore, 'offer'>;
14
+ onError?: (error: unknown) => void;
15
+ }
16
+ /**
17
+ * The body of `useServiceWorker`'s effect, a plain function so that it is
18
+ * tested without React: registers after `load` when `enabled` and the
19
+ * browser has a worker container, offers each waiting version to `notice`,
20
+ * and hands a failed registration to `onError`. The listeners it adds live
21
+ * for the page.
22
+ */
23
+ export declare function startServiceWorker(environment: ServiceWorkerEnvironment, options: StartServiceWorkerOptions): void;
24
+ //# sourceMappingURL=start-service-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-service-worker.d.ts","sourceRoot":"","sources":["../../../src/pwa/model/start-service-worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAEjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,iFAAiF;AACjF,MAAM,WAAW,wBAAwB;IACxC,SAAS,EAAE,MAAM,GAAG;QAAE,aAAa,CAAC,EAAE,0BAA0B,CAAA;KAAE,CAAC;CACnE;AAED,MAAM,WAAW,yBAAyB;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,WAAW,EAAE,wBAAwB,EACrC,OAAO,EAAE,yBAAyB,GAChC,IAAI,CAgBN"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Whether a response may be stored: a full, same-origin 200 of a static
3
+ * content type, that the server marked `public` (alxia does on every
4
+ * static file it serves; a route answering an image, a user's photo, does
5
+ * not), not private nor no-store, and that does not vary on who asks.
6
+ */
7
+ export declare function isStorable(response: Response): boolean;
8
+ //# sourceMappingURL=storable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storable.d.ts","sourceRoot":"","sources":["../../../src/pwa/model/storable.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAYtD"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The session's "Later": the notice stays hidden until the next visit.
3
+ * `sessionStorage` is per origin, so one key serves every app; a second
4
+ * argument of `createUpdateNotice` names another.
5
+ */
6
+ export declare const UPDATE_LATER_KEY = "alxia.update-later";
7
+ /** The part of `Storage` the notice reads and writes. */
8
+ export type NoticeStorage = {
9
+ getItem(key: string): string | null;
10
+ setItem(key: string, value: string): void;
11
+ };
12
+ /** What the notice offers while a new version waits. */
13
+ export type UpdateNotice = {
14
+ /** Moves the page onto the new version: the worker's `apply`. */
15
+ reload: () => void;
16
+ /** Hides the notice until the next visit (the tab's session). */
17
+ later: () => void;
18
+ };
19
+ /** What `createUpdateNotice` returns: the shape `useSyncExternalStore` takes. */
20
+ export interface UpdateNoticeStore {
21
+ /** A new version waits: `apply` moves the page onto it. */
22
+ offer(apply: () => void): void;
23
+ /** The notice to show, if any (the same object until it changes). */
24
+ current(): UpdateNotice | undefined;
25
+ /** Calls `listener` on each change; returns the function that stops it. */
26
+ subscribe(listener: () => void): () => void;
27
+ }
28
+ /**
29
+ * The update notice's state: offered by the worker's registration, read by
30
+ * the banners, hidden for the session by "Later". `storage` is the session's
31
+ * (`sessionStorage`), which may be missing or throw (private mode, blocked
32
+ * storage): the notice then hides for this page only. It is a function so
33
+ * that nothing reads the global at load.
34
+ */
35
+ export declare function createUpdateNotice(storage: () => NoticeStorage | undefined, key?: string): UpdateNoticeStore;
36
+ /** `sessionStorage`, or nothing where there is none (the server). */
37
+ export declare const sessionStore: () => NoticeStorage | undefined;
38
+ //# sourceMappingURL=update-notice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-notice.d.ts","sourceRoot":"","sources":["../../../src/pwa/model/update-notice.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,uBAAuB,CAAC;AAErD,yDAAyD;AACzD,MAAM,MAAM,aAAa,GAAG;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1C,CAAC;AAEF,wDAAwD;AACxD,MAAM,MAAM,YAAY,GAAG;IAC1B,iEAAiE;IACjE,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,iEAAiE;IACjE,KAAK,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAEF,iFAAiF;AACjF,MAAM,WAAW,iBAAiB;IACjC,2DAA2D;IAC3D,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC/B,qEAAqE;IACrE,OAAO,IAAI,YAAY,GAAG,SAAS,CAAC;IACpC,2EAA2E;IAC3E,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC5C;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,MAAM,aAAa,GAAG,SAAS,EACxC,GAAG,GAAE,MAAyB,GAC5B,iBAAiB,CAyCnB;AAED,qEAAqE;AACrE,eAAO,MAAM,YAAY,QAAO,aAAa,GAAG,SACmB,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * What the service worker and the page agree on: the worker's address, the
3
+ * offline page, the message that applies an update, and the names of the
4
+ * caches the worker owns. Dependency-free: the worker bundles it, with no
5
+ * DOM and no React.
6
+ */
7
+ /** Where the build writes the worker; registered with scope `/`. */
8
+ export declare const SERVICE_WORKER_URL = "/service-worker.js";
9
+ /** The page served when a navigation fails for want of a network. */
10
+ export declare const OFFLINE_URL = "/offline.html";
11
+ /** The message a page posts to a waiting worker to make it take over. */
12
+ export declare const SKIP_WAITING = "SKIP_WAITING";
13
+ export interface WorkerContractOptions {
14
+ /**
15
+ * Every Cache Storage cache the worker owns is named
16
+ * `<cachePrefix>-<kind>-<version>` (kind: letters and digits; version:
17
+ * no `-`); a cache of another shape is never swept.
18
+ * Required: two apps on one origin must not share it.
19
+ */
20
+ cachePrefix: string;
21
+ /**
22
+ * Bumped when a cache's content changes shape: the next worker deletes
23
+ * every cache of the prefix that has another version when it activates.
24
+ * `'v1'` by default; not empty, and without a `-`.
25
+ */
26
+ cacheVersion?: string;
27
+ /** `SERVICE_WORKER_URL` by default. */
28
+ serviceWorkerUrl?: string;
29
+ /** `OFFLINE_URL` by default. */
30
+ offlineUrl?: string;
31
+ }
32
+ export type CacheNames = {
33
+ /**
34
+ * The offline page and the hashed scripts and stylesheets of every route.
35
+ * `<prefix>-precache-<version>` is the name Workbox builds from
36
+ * `setCacheNameDetails({ prefix, precache: 'precache', suffix: version })`.
37
+ */
38
+ readonly precache: string;
39
+ /** `/assets/*` bundles: hashed names, so cache-first. */
40
+ readonly assets: string;
41
+ /** Icons, sprites, the logo: stale-while-revalidate. */
42
+ readonly static: string;
43
+ };
44
+ export interface WorkerContract {
45
+ readonly cachePrefix: string;
46
+ readonly cacheVersion: string;
47
+ readonly serviceWorkerUrl: string;
48
+ readonly offlineUrl: string;
49
+ readonly cacheNames: CacheNames;
50
+ /** The caches a worker deletes on activate: the prefix's, of another version. */
51
+ obsoleteCaches(names: readonly string[]): string[];
52
+ /**
53
+ * The caches `clearAppCaches()` deletes: all of the prefix's but the
54
+ * current precache, which holds the offline page and the app's code, never
55
+ * a user's data.
56
+ */
57
+ clearableCaches(names: readonly string[]): string[];
58
+ }
59
+ /** Builds the contract of one app from its cache prefix. */
60
+ export declare function defineWorkerContract(options: WorkerContractOptions): WorkerContract;
61
+ //# sourceMappingURL=worker-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-contract.d.ts","sourceRoot":"","sources":["../../../src/pwa/model/worker-contract.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAEvD,qEAAqE;AACrE,eAAO,MAAM,WAAW,kBAAkB,CAAC;AAE3C,yEAAyE;AACzE,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C,MAAM,WAAW,qBAAqB;IACrC;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,GAAG;IACxB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,iFAAiF;IACjF,cAAc,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IACnD;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;CACpD;AAED,4DAA4D;AAC5D,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,qBAAqB,GAC5B,cAAc,CA+ChB"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `@alxia/react-router/pwa/react`: the hooks of an installable app's page.
3
+ * `react` is its only import (an optional peer of the package), and what it
4
+ * shares with `/pwa` is a chunk of that entry.
5
+ */
6
+ export { useOnline } from './use-online';
7
+ export { type ServiceWorkerOptions, useServiceWorker, } from './use-service-worker';
8
+ export { updateNotice, useUpdateNotice } from './use-update-notice';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pwa/react/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACN,KAAK,oBAAoB,EACzB,gBAAgB,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,75 @@
1
+ import {
2
+ afterLoad2,
3
+ registerServiceWorker2,
4
+ createUpdateNotice2,
5
+ sessionStore2
6
+ } from "../../chunks/index-9xv2a8et.js";
7
+
8
+ // src/pwa/react/use-online.ts
9
+ import { useSyncExternalStore } from "react";
10
+ function subscribe(onChange) {
11
+ window.addEventListener("online", onChange);
12
+ window.addEventListener("offline", onChange);
13
+ return () => {
14
+ window.removeEventListener("online", onChange);
15
+ window.removeEventListener("offline", onChange);
16
+ };
17
+ }
18
+ function useOnline() {
19
+ return useSyncExternalStore(subscribe, () => navigator.onLine, () => true);
20
+ }
21
+ // src/pwa/react/use-service-worker.ts
22
+ import { useEffect, useRef } from "react";
23
+
24
+ // src/pwa/model/start-service-worker.ts
25
+ function startServiceWorker(environment, options) {
26
+ const { navigator: navigator2 } = environment;
27
+ if (!options.enabled || !("serviceWorker" in navigator2))
28
+ return;
29
+ const container = navigator2.serviceWorker;
30
+ if (!container)
31
+ return;
32
+ const { url, scope, notice, onError } = options;
33
+ afterLoad2(() => {
34
+ registerServiceWorker2(container, (worker, apply) => notice.offer(() => apply(worker)), {
35
+ ...url !== undefined && { url },
36
+ ...scope !== undefined && { scope }
37
+ }).catch((error) => onError?.(error));
38
+ });
39
+ }
40
+
41
+ // src/pwa/react/use-update-notice.ts
42
+ import { useSyncExternalStore as useSyncExternalStore2 } from "react";
43
+ var updateNotice = createUpdateNotice2(sessionStore2);
44
+ function useUpdateNotice(notice = updateNotice) {
45
+ return useSyncExternalStore2((listener) => notice.subscribe(listener), () => notice.current(), () => {
46
+ return;
47
+ });
48
+ }
49
+
50
+ // src/pwa/react/use-service-worker.ts
51
+ function useServiceWorker(options) {
52
+ const { enabled, url, scope, notice = updateNotice } = options;
53
+ const onError = useRef(options.onError);
54
+ useEffect(() => {
55
+ onError.current = options.onError;
56
+ });
57
+ useEffect(() => {
58
+ startServiceWorker({ navigator }, {
59
+ enabled,
60
+ notice,
61
+ onError: (error) => onError.current?.(error),
62
+ ...url !== undefined && { url },
63
+ ...scope !== undefined && { scope }
64
+ });
65
+ }, [enabled, url, scope, notice]);
66
+ }
67
+ export {
68
+ updateNotice,
69
+ useOnline,
70
+ useServiceWorker,
71
+ useUpdateNotice
72
+ };
73
+
74
+ //# debugId=DC45CFDED1D0414264756E2164756E21
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/pwa/react/use-online.ts", "../src/pwa/react/use-service-worker.ts", "../src/pwa/model/start-service-worker.ts", "../src/pwa/react/use-update-notice.ts"],
4
+ "sourcesContent": [
5
+ "import { useSyncExternalStore } from 'react';\n\nfunction subscribe(onChange: () => void) {\n\twindow.addEventListener('online', onChange);\n\twindow.addEventListener('offline', onChange);\n\treturn () => {\n\t\twindow.removeEventListener('online', onChange);\n\t\twindow.removeEventListener('offline', onChange);\n\t};\n}\n\n/**\n * Whether the browser says it is online. Online on the server and during\n * hydration, so the first client render matches the server's.\n */\nexport function useOnline(): boolean {\n\treturn useSyncExternalStore(\n\t\tsubscribe,\n\t\t() => navigator.onLine,\n\t\t() => true,\n\t);\n}\n",
6
+ "import { useEffect, useRef } from 'react';\nimport { startServiceWorker } from '../model/start-service-worker';\nimport type { UpdateNoticeStore } from '../model/update-notice';\nimport { updateNotice } from './use-update-notice';\n\nexport interface ServiceWorkerOptions {\n\t/**\n\t * Whether to register at all. The app decides, typically\n\t * `import.meta.env.PROD`: a worker under a dev server caches the code being\n\t * edited. This package never reads `import.meta.env` itself.\n\t */\n\tenabled: boolean;\n\t/** The worker's path. `SERVICE_WORKER_URL` by default. */\n\turl?: string;\n\t/** The scope it controls. `'/'` by default. */\n\tscope?: string;\n\t/** Where a waiting version is offered. The page's `updateNotice` by default. */\n\tnotice?: UpdateNoticeStore;\n\t/**\n\t * Called when registering fails (private mode, a blocked script). The app\n\t * works without a worker, so by default the error is dropped. An `onError`\n\t * that throws becomes an unhandled rejection.\n\t */\n\tonError?: (error: unknown) => void;\n}\n\n/**\n * Registers the service worker after `load` when `enabled`, in a browser\n * that has one. When a new version waits, `notice` offers it (`useUpdateNotice`\n * reads it); nothing activates it unasked. Does nothing on the server.\n *\n * The options are read when the effect runs, which is on mount and when\n * `enabled`, `url`, `scope` or `notice` change; `onError` is always the\n * latest. The listeners it adds (the update check, the controller change)\n * stay for the page's life: there is no cleanup, a page has one worker.\n */\nexport function useServiceWorker(options: ServiceWorkerOptions): void {\n\tconst { enabled, url, scope, notice = updateNotice } = options;\n\tconst onError = useRef(options.onError);\n\tuseEffect(() => {\n\t\tonError.current = options.onError;\n\t});\n\tuseEffect(() => {\n\t\tstartServiceWorker(\n\t\t\t{ navigator },\n\t\t\t{\n\t\t\t\tenabled,\n\t\t\t\tnotice,\n\t\t\t\tonError: (error) => onError.current?.(error),\n\t\t\t\t...(url !== undefined && { url }),\n\t\t\t\t...(scope !== undefined && { scope }),\n\t\t\t},\n\t\t);\n\t}, [enabled, url, scope, notice]);\n}\n",
7
+ "import type { ServiceWorkerContainerLike } from './registration';\nimport { afterLoad, registerServiceWorker } from './registration';\nimport type { UpdateNoticeStore } from './update-notice';\n\n/** What starting reads of the browser: `navigator`, which may have no worker. */\nexport interface ServiceWorkerEnvironment {\n\tnavigator: object & { serviceWorker?: ServiceWorkerContainerLike };\n}\n\nexport interface StartServiceWorkerOptions {\n\tenabled: boolean;\n\turl?: string;\n\tscope?: string;\n\tnotice: Pick<UpdateNoticeStore, 'offer'>;\n\tonError?: (error: unknown) => void;\n}\n\n/**\n * The body of `useServiceWorker`'s effect, a plain function so that it is\n * tested without React: registers after `load` when `enabled` and the\n * browser has a worker container, offers each waiting version to `notice`,\n * and hands a failed registration to `onError`. The listeners it adds live\n * for the page.\n */\nexport function startServiceWorker(\n\tenvironment: ServiceWorkerEnvironment,\n\toptions: StartServiceWorkerOptions,\n): void {\n\tconst { navigator } = environment;\n\tif (!options.enabled || !('serviceWorker' in navigator)) return;\n\tconst container = navigator.serviceWorker;\n\tif (!container) return;\n\tconst { url, scope, notice, onError } = options;\n\tafterLoad(() => {\n\t\tregisterServiceWorker(\n\t\t\tcontainer,\n\t\t\t(worker, apply) => notice.offer(() => apply(worker)),\n\t\t\t{\n\t\t\t\t...(url !== undefined && { url }),\n\t\t\t\t...(scope !== undefined && { scope }),\n\t\t\t},\n\t\t).catch((error: unknown) => onError?.(error));\n\t});\n}\n",
8
+ "import { useSyncExternalStore } from 'react';\nimport {\n\tcreateUpdateNotice,\n\tsessionStore,\n\ttype UpdateNotice,\n\ttype UpdateNoticeStore,\n} from '../model/update-notice';\n\n/**\n * The page's one notice, which `useServiceWorker` offers and\n * `useUpdateNotice` reads. It lives here, not in `/pwa`, so that the model a\n * worker bundle imports makes nothing at load.\n */\nexport const updateNotice: UpdateNoticeStore = createUpdateNotice(sessionStore);\n\n/**\n * The update notice to show: a new version waits and \"Later\" was not asked.\n * None on the server. `notice` is the page's `updateNotice` unless the app\n * made a store of its own.\n */\nexport function useUpdateNotice(\n\tnotice: UpdateNoticeStore = updateNotice,\n): UpdateNotice | undefined {\n\treturn useSyncExternalStore(\n\t\t(listener) => notice.subscribe(listener),\n\t\t() => notice.current(),\n\t\t() => undefined,\n\t);\n}\n"
9
+ ],
10
+ "mappings": ";;;;;;;;AAAA;AAEA,SAAS,SAAS,CAAC,UAAsB;AAAA,EACxC,OAAO,iBAAiB,UAAU,QAAQ;AAAA,EAC1C,OAAO,iBAAiB,WAAW,QAAQ;AAAA,EAC3C,OAAO,MAAM;AAAA,IACZ,OAAO,oBAAoB,UAAU,QAAQ;AAAA,IAC7C,OAAO,oBAAoB,WAAW,QAAQ;AAAA;AAAA;AAQzC,SAAS,SAAS,GAAY;AAAA,EACpC,OAAO,qBACN,WACA,MAAM,UAAU,QAChB,MAAM,IACP;AAAA;;ACpBD;;;ACwBO,SAAS,kBAAkB,CACjC,aACA,SACO;AAAA,EACP,QAAQ,0BAAc;AAAA,EACtB,IAAI,CAAC,QAAQ,WAAW,EAAE,mBAAmB;AAAA,IAAY;AAAA,EACzD,MAAM,YAAY,WAAU;AAAA,EAC5B,IAAI,CAAC;AAAA,IAAW;AAAA,EAChB,QAAQ,KAAK,OAAO,QAAQ,YAAY;AAAA,EACxC,WAAU,MAAM;AAAA,IACf,uBACC,WACA,CAAC,QAAQ,UAAU,OAAO,MAAM,MAAM,MAAM,MAAM,CAAC,GACnD;AAAA,SACK,QAAQ,aAAa,EAAE,IAAI;AAAA,SAC3B,UAAU,aAAa,EAAE,MAAM;AAAA,IACpC,CACD,EAAE,MAAM,CAAC,UAAmB,UAAU,KAAK,CAAC;AAAA,GAC5C;AAAA;;;AC1CF,iCAAS;AAaF,IAAM,eAAkC,oBAAmB,aAAY;AAOvE,SAAS,eAAe,CAC9B,SAA4B,cACD;AAAA,EAC3B,OAAO,sBACN,CAAC,aAAa,OAAO,UAAU,QAAQ,GACvC,MAAM,OAAO,QAAQ,GACrB,MAAG;AAAA,IAAG;AAAA,GACP;AAAA;;;AFSM,SAAS,gBAAgB,CAAC,SAAqC;AAAA,EACrE,QAAQ,SAAS,KAAK,OAAO,SAAS,iBAAiB;AAAA,EACvD,MAAM,UAAU,OAAO,QAAQ,OAAO;AAAA,EACtC,UAAU,MAAM;AAAA,IACf,QAAQ,UAAU,QAAQ;AAAA,GAC1B;AAAA,EACD,UAAU,MAAM;AAAA,IACf,mBACC,EAAE,UAAU,GACZ;AAAA,MACC;AAAA,MACA;AAAA,MACA,SAAS,CAAC,UAAU,QAAQ,UAAU,KAAK;AAAA,SACvC,QAAQ,aAAa,EAAE,IAAI;AAAA,SAC3B,UAAU,aAAa,EAAE,MAAM;AAAA,IACpC,CACD;AAAA,KACE,CAAC,SAAS,KAAK,OAAO,MAAM,CAAC;AAAA;",
11
+ "debugId": "DC45CFDED1D0414264756E2164756E21",
12
+ "names": []
13
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Whether the browser says it is online. Online on the server and during
3
+ * hydration, so the first client render matches the server's.
4
+ */
5
+ export declare function useOnline(): boolean;
6
+ //# sourceMappingURL=use-online.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-online.d.ts","sourceRoot":"","sources":["../../../src/pwa/react/use-online.ts"],"names":[],"mappings":"AAWA;;;GAGG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAMnC"}
@@ -0,0 +1,33 @@
1
+ import type { UpdateNoticeStore } from '../model/update-notice';
2
+ export interface ServiceWorkerOptions {
3
+ /**
4
+ * Whether to register at all. The app decides, typically
5
+ * `import.meta.env.PROD`: a worker under a dev server caches the code being
6
+ * edited. This package never reads `import.meta.env` itself.
7
+ */
8
+ enabled: boolean;
9
+ /** The worker's path. `SERVICE_WORKER_URL` by default. */
10
+ url?: string;
11
+ /** The scope it controls. `'/'` by default. */
12
+ scope?: string;
13
+ /** Where a waiting version is offered. The page's `updateNotice` by default. */
14
+ notice?: UpdateNoticeStore;
15
+ /**
16
+ * Called when registering fails (private mode, a blocked script). The app
17
+ * works without a worker, so by default the error is dropped. An `onError`
18
+ * that throws becomes an unhandled rejection.
19
+ */
20
+ onError?: (error: unknown) => void;
21
+ }
22
+ /**
23
+ * Registers the service worker after `load` when `enabled`, in a browser
24
+ * that has one. When a new version waits, `notice` offers it (`useUpdateNotice`
25
+ * reads it); nothing activates it unasked. Does nothing on the server.
26
+ *
27
+ * The options are read when the effect runs, which is on mount and when
28
+ * `enabled`, `url`, `scope` or `notice` change; `onError` is always the
29
+ * latest. The listeners it adds (the update check, the controller change)
30
+ * stay for the page's life: there is no cleanup, a page has one worker.
31
+ */
32
+ export declare function useServiceWorker(options: ServiceWorkerOptions): void;
33
+ //# sourceMappingURL=use-service-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-service-worker.d.ts","sourceRoot":"","sources":["../../../src/pwa/react/use-service-worker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,MAAM,WAAW,oBAAoB;IACpC;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAkBpE"}
@@ -0,0 +1,14 @@
1
+ import { type UpdateNotice, type UpdateNoticeStore } from '../model/update-notice';
2
+ /**
3
+ * The page's one notice, which `useServiceWorker` offers and
4
+ * `useUpdateNotice` reads. It lives here, not in `/pwa`, so that the model a
5
+ * worker bundle imports makes nothing at load.
6
+ */
7
+ export declare const updateNotice: UpdateNoticeStore;
8
+ /**
9
+ * The update notice to show: a new version waits and "Later" was not asked.
10
+ * None on the server. `notice` is the page's `updateNotice` unless the app
11
+ * made a store of its own.
12
+ */
13
+ export declare function useUpdateNotice(notice?: UpdateNoticeStore): UpdateNotice | undefined;
14
+ //# sourceMappingURL=use-update-notice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-update-notice.d.ts","sourceRoot":"","sources":["../../../src/pwa/react/use-update-notice.ts"],"names":[],"mappings":"AACA,OAAO,EAGN,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,MAAM,wBAAwB,CAAC;AAEhC;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,iBAAoD,CAAC;AAEhF;;;;GAIG;AACH,wBAAgB,eAAe,CAC9B,MAAM,GAAE,iBAAgC,GACtC,YAAY,GAAG,SAAS,CAM1B"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `@alxia/react-router/pwa/server`: the server's side of an installable
3
+ * app, `noCache` for the worker's script. `@alxia/core` is its only import,
4
+ * so keep it out of the page and the worker.
5
+ */
6
+ export { noCache, revalidated, WORKER_POLICY } from './no-cache';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pwa/server/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,18 @@
1
+ // src/pwa/server/no-cache.ts
2
+ import { defineMiddleware, withHeaders } from "@alxia/core";
3
+ var WORKER_POLICY = "default-src 'none'; connect-src 'self'";
4
+ function revalidated(response, policy = WORKER_POLICY) {
5
+ return withHeaders(response, (headers) => {
6
+ headers.set("cache-control", "no-cache");
7
+ headers.set("content-security-policy", policy);
8
+ });
9
+ }
10
+ var noCache = defineMiddleware(async (_ctx, next) => revalidated(await next()));
11
+ export {
12
+ WORKER_POLICY,
13
+ noCache,
14
+ revalidated
15
+ };
16
+
17
+ //# debugId=D042AA38FD15BC2064756E2164756E21
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/pwa/server/no-cache.ts"],
4
+ "sourcesContent": [
5
+ "import { defineMiddleware, withHeaders } from '@alxia/core';\n\n/**\n * The service worker's own policy: a worker takes the policy its script\n * came with, not the pages'. It runs no other script and fetches this site\n * only (the precache, navigations); its routes never fetch another origin.\n */\nexport const WORKER_POLICY = \"default-src 'none'; connect-src 'self'\";\n\n/**\n * The worker's script with `Cache-Control: no-cache`: the client's\n * top-level files are served for an hour, and the worker's script must be\n * revalidated on every update check, or an update waits that long. And with\n * the worker's policy (`WORKER_POLICY`, or your own `policy`), which no\n * other static file needs: none of them is a document but an offline page,\n * which carries its own in a `<meta>`.\n */\nexport function revalidated(\n\tresponse: Response,\n\tpolicy: string = WORKER_POLICY,\n): Response {\n\treturn withHeaders(response, (headers) => {\n\t\theaders.set('cache-control', 'no-cache');\n\t\theaders.set('content-security-policy', policy);\n\t});\n}\n\n/**\n * For `use(SERVICE_WORKER_URL, noCache)` in `createServer({ beforeAll })`,\n * before the client's files: they answer without calling `next` for a\n * middleware declared after them.\n */\nexport const noCache = defineMiddleware(async (_ctx, next) =>\n\trevalidated(await next()),\n);\n"
6
+ ],
7
+ "mappings": ";AAAA;AAOO,IAAM,gBAAgB;AAUtB,SAAS,WAAW,CAC1B,UACA,SAAiB,eACN;AAAA,EACX,OAAO,YAAY,UAAU,CAAC,YAAY;AAAA,IACzC,QAAQ,IAAI,iBAAiB,UAAU;AAAA,IACvC,QAAQ,IAAI,2BAA2B,MAAM;AAAA,GAC7C;AAAA;AAQK,IAAM,UAAU,iBAAiB,OAAO,MAAM,SACpD,YAAY,MAAM,KAAK,CAAC,CACzB;",
8
+ "debugId": "D042AA38FD15BC2064756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The service worker's own policy: a worker takes the policy its script
3
+ * came with, not the pages'. It runs no other script and fetches this site
4
+ * only (the precache, navigations); its routes never fetch another origin.
5
+ */
6
+ export declare const WORKER_POLICY = "default-src 'none'; connect-src 'self'";
7
+ /**
8
+ * The worker's script with `Cache-Control: no-cache`: the client's
9
+ * top-level files are served for an hour, and the worker's script must be
10
+ * revalidated on every update check, or an update waits that long. And with
11
+ * the worker's policy (`WORKER_POLICY`, or your own `policy`), which no
12
+ * other static file needs: none of them is a document but an offline page,
13
+ * which carries its own in a `<meta>`.
14
+ */
15
+ export declare function revalidated(response: Response, policy?: string): Response;
16
+ /**
17
+ * For `use(SERVICE_WORKER_URL, noCache)` in `createServer({ beforeAll })`,
18
+ * before the client's files: they answer without calling `next` for a
19
+ * middleware declared after them.
20
+ */
21
+ export declare const noCache: NoInfer<import("@alxia/core").Middleware<import("@alxia/core").Empty, Promise<Response>>>;
22
+ //# sourceMappingURL=no-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-cache.d.ts","sourceRoot":"","sources":["../../../src/pwa/server/no-cache.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,aAAa,2CAA2C,CAAC;AAEtE;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAC1B,QAAQ,EAAE,QAAQ,EAClB,MAAM,GAAE,MAAsB,GAC5B,QAAQ,CAKV;AAED;;;;GAIG;AACH,eAAO,MAAM,OAAO,2FAEnB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `@alxia/react-router/pwa/worker`: the service worker on Workbox. Import it
3
+ * from a service worker bundle alone, never from a page. The six `workbox-*`
4
+ * packages (`core`, `expiration`, `navigation-preload`, `precaching`,
5
+ * `routing`, `strategies`, optional peers) are its only package imports.
6
+ */
7
+ export type { ExpirationOptions, WorkerOptions } from './plan';
8
+ export { startWorker } from './start-worker';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pwa/worker/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,144 @@
1
+ import {
2
+ cachingFor2,
3
+ isStorable2
4
+ } from "../../chunks/index-zdbqzcjp.js";
5
+ import {
6
+ injectedPrecache
7
+ } from "../../chunks/index-59vhfj6n.js";
8
+ import {
9
+ SKIP_WAITING2
10
+ } from "../../chunks/index-rfr02vg4.js";
11
+
12
+ // src/pwa/worker/start-worker.ts
13
+ import {
14
+ clientsClaim,
15
+ setCacheNameDetails
16
+ } from "workbox-core";
17
+ import { ExpirationPlugin } from "workbox-expiration";
18
+ import * as navigationPreload from "workbox-navigation-preload";
19
+ import { matchPrecache, precacheAndRoute } from "workbox-precaching";
20
+ import { registerRoute } from "workbox-routing";
21
+ import {
22
+ CacheFirst,
23
+ NetworkOnly,
24
+ StaleWhileRevalidate
25
+ } from "workbox-strategies";
26
+
27
+ // src/pwa/worker/plan.ts
28
+ var THIRTY_DAYS = 30 * 24 * 60 * 60;
29
+ function workerPlan(options, injected) {
30
+ const { contract } = options;
31
+ if (!contract) {
32
+ throw new TypeError("startWorker(): contract is required: pass defineWorkerContract({ cachePrefix }) from @alxia/react-router/pwa.");
33
+ }
34
+ const precache = options.precache ?? injected;
35
+ if (!precache) {
36
+ throw new Error("startWorker(): no precache list. Build this worker with alxia() from @alxia/react-router/vite (app/service-worker.ts) or buildWorker() from @alxia/react-router/pwa/build, which write it in.");
37
+ }
38
+ const offlineUrl = contract.offlineUrl;
39
+ const hasOffline = precache.some((entry) => entry.url.split("?")[0] === offlineUrl);
40
+ return {
41
+ cacheNameDetails: {
42
+ prefix: contract.cachePrefix,
43
+ precache: "precache",
44
+ suffix: contract.cacheVersion
45
+ },
46
+ precache,
47
+ routes: [
48
+ {
49
+ caching: "navigation",
50
+ strategy: "network-only",
51
+ fallback: hasOffline ? offlineUrl : undefined
52
+ },
53
+ {
54
+ caching: "immutable",
55
+ strategy: "cache-first",
56
+ cacheName: contract.cacheNames.assets,
57
+ expiration: { maxEntries: 200, ...options.assets }
58
+ },
59
+ {
60
+ caching: "static",
61
+ strategy: "stale-while-revalidate",
62
+ cacheName: contract.cacheNames.static,
63
+ expiration: {
64
+ maxEntries: 100,
65
+ maxAgeSeconds: THIRTY_DAYS,
66
+ ...options.static
67
+ }
68
+ }
69
+ ],
70
+ navigationPreload: options.navigationPreload ?? true
71
+ };
72
+ }
73
+ function matcher(caching, origin, options) {
74
+ return ({ request }) => cachingFor2(request, origin(), {
75
+ ...options.caching,
76
+ contract: options.contract
77
+ }) === caching;
78
+ }
79
+ var storableOnly = {
80
+ async cacheWillUpdate({
81
+ response
82
+ }) {
83
+ return isStorable2(response) ? response : null;
84
+ }
85
+ };
86
+ function isSkipWaiting(data) {
87
+ return data?.type === SKIP_WAITING2;
88
+ }
89
+ async function deleteObsoleteCaches(contract, storage) {
90
+ const names = await storage.keys();
91
+ await Promise.all(contract.obsoleteCaches(names).map((name) => storage.delete(name)));
92
+ }
93
+
94
+ // src/pwa/worker/scope.ts
95
+ var workerScope = () => globalThis;
96
+
97
+ // src/pwa/worker/start-worker.ts
98
+ function startWorker(options) {
99
+ const plan = workerPlan(options, injectedPrecache());
100
+ const scope = workerScope();
101
+ const { contract } = options;
102
+ setCacheNameDetails(plan.cacheNameDetails);
103
+ precacheAndRoute([...plan.precache], {
104
+ cleanURLs: false,
105
+ directoryIndex: ""
106
+ });
107
+ if (plan.navigationPreload)
108
+ navigationPreload.enable();
109
+ for (const route of plan.routes) {
110
+ const match = matcher(route.caching, () => scope.location.origin, options);
111
+ if (route.strategy === "network-only") {
112
+ const { fallback } = route;
113
+ registerRoute(match, new NetworkOnly({
114
+ plugins: fallback ? [{ handlerDidError: async () => matchPrecache(fallback) }] : []
115
+ }));
116
+ continue;
117
+ }
118
+ const Strategy = route.strategy === "cache-first" ? CacheFirst : StaleWhileRevalidate;
119
+ registerRoute(match, new Strategy({
120
+ cacheName: route.cacheName,
121
+ plugins: [
122
+ storableOnly,
123
+ new ExpirationPlugin({
124
+ ...route.expiration,
125
+ purgeOnQuotaError: true
126
+ })
127
+ ]
128
+ }));
129
+ }
130
+ clientsClaim();
131
+ scope.addEventListener("activate", (event) => {
132
+ event.waitUntil(deleteObsoleteCaches(contract, caches));
133
+ });
134
+ scope.addEventListener("message", (event) => {
135
+ if (isSkipWaiting(event.data))
136
+ scope.skipWaiting();
137
+ });
138
+ }
139
+ export {
140
+ startWorker
141
+ };
142
+
143
+ //# debugId=52EB15D1D020B66D64756E2164756E21
144
+ //# sourceMappingURL=index.js.map