@cogenta/render 0.1.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 (205) hide show
  1. package/dist/astro/integration.d.ts +57 -0
  2. package/dist/astro/integration.d.ts.map +1 -0
  3. package/dist/astro/integration.js +72 -0
  4. package/dist/astro/integration.js.map +1 -0
  5. package/dist/build/index.d.ts +7 -0
  6. package/dist/build/index.d.ts.map +1 -0
  7. package/dist/build/index.js +7 -0
  8. package/dist/build/index.js.map +1 -0
  9. package/dist/build/integration.d.ts +25 -0
  10. package/dist/build/integration.d.ts.map +1 -0
  11. package/dist/build/integration.js +41 -0
  12. package/dist/build/integration.js.map +1 -0
  13. package/dist/build/plan.d.ts +41 -0
  14. package/dist/build/plan.d.ts.map +1 -0
  15. package/dist/build/plan.js +48 -0
  16. package/dist/build/plan.js.map +1 -0
  17. package/dist/build/refusal.d.ts +14 -0
  18. package/dist/build/refusal.d.ts.map +1 -0
  19. package/dist/build/refusal.js +110 -0
  20. package/dist/build/refusal.js.map +1 -0
  21. package/dist/build/requirements.d.ts +59 -0
  22. package/dist/build/requirements.d.ts.map +1 -0
  23. package/dist/build/requirements.js +74 -0
  24. package/dist/build/requirements.js.map +1 -0
  25. package/dist/build/run-build.d.ts +43 -0
  26. package/dist/build/run-build.d.ts.map +1 -0
  27. package/dist/build/run-build.js +73 -0
  28. package/dist/build/run-build.js.map +1 -0
  29. package/dist/build/targets.d.ts +47 -0
  30. package/dist/build/targets.d.ts.map +1 -0
  31. package/dist/build/targets.js +69 -0
  32. package/dist/build/targets.js.map +1 -0
  33. package/dist/cache/index.d.ts +7 -0
  34. package/dist/cache/index.d.ts.map +1 -0
  35. package/dist/cache/index.js +4 -0
  36. package/dist/cache/index.js.map +1 -0
  37. package/dist/cache/page-cache.d.ts +64 -0
  38. package/dist/cache/page-cache.d.ts.map +1 -0
  39. package/dist/cache/page-cache.js +128 -0
  40. package/dist/cache/page-cache.js.map +1 -0
  41. package/dist/cache/recorder.d.ts +49 -0
  42. package/dist/cache/recorder.d.ts.map +1 -0
  43. package/dist/cache/recorder.js +77 -0
  44. package/dist/cache/recorder.js.map +1 -0
  45. package/dist/cache/tags.d.ts +55 -0
  46. package/dist/cache/tags.d.ts.map +1 -0
  47. package/dist/cache/tags.js +62 -0
  48. package/dist/cache/tags.js.map +1 -0
  49. package/dist/config.d.ts +48 -0
  50. package/dist/config.d.ts.map +1 -0
  51. package/dist/config.js +44 -0
  52. package/dist/config.js.map +1 -0
  53. package/dist/content/client.d.ts +35 -0
  54. package/dist/content/client.d.ts.map +1 -0
  55. package/dist/content/client.js +155 -0
  56. package/dist/content/client.js.map +1 -0
  57. package/dist/content/types.d.ts +104 -0
  58. package/dist/content/types.d.ts.map +1 -0
  59. package/dist/content/types.js +16 -0
  60. package/dist/content/types.js.map +1 -0
  61. package/dist/context/render-context.d.ts +31 -0
  62. package/dist/context/render-context.d.ts.map +1 -0
  63. package/dist/context/render-context.js +152 -0
  64. package/dist/context/render-context.js.map +1 -0
  65. package/dist/context/types.d.ts +49 -0
  66. package/dist/context/types.d.ts.map +1 -0
  67. package/dist/context/types.js +2 -0
  68. package/dist/context/types.js.map +1 -0
  69. package/dist/images/errors.d.ts +20 -0
  70. package/dist/images/errors.d.ts.map +1 -0
  71. package/dist/images/errors.js +58 -0
  72. package/dist/images/errors.js.map +1 -0
  73. package/dist/images/format.d.ts +18 -0
  74. package/dist/images/format.d.ts.map +1 -0
  75. package/dist/images/format.js +34 -0
  76. package/dist/images/format.js.map +1 -0
  77. package/dist/images/geometry.d.ts +46 -0
  78. package/dist/images/geometry.d.ts.map +1 -0
  79. package/dist/images/geometry.js +127 -0
  80. package/dist/images/geometry.js.map +1 -0
  81. package/dist/images/index.d.ts +29 -0
  82. package/dist/images/index.d.ts.map +1 -0
  83. package/dist/images/index.js +30 -0
  84. package/dist/images/index.js.map +1 -0
  85. package/dist/images/pipeline.d.ts +51 -0
  86. package/dist/images/pipeline.d.ts.map +1 -0
  87. package/dist/images/pipeline.js +105 -0
  88. package/dist/images/pipeline.js.map +1 -0
  89. package/dist/images/sharp.d.ts +57 -0
  90. package/dist/images/sharp.d.ts.map +1 -0
  91. package/dist/images/sharp.js +139 -0
  92. package/dist/images/sharp.js.map +1 -0
  93. package/dist/images/srcset.d.ts +33 -0
  94. package/dist/images/srcset.d.ts.map +1 -0
  95. package/dist/images/srcset.js +94 -0
  96. package/dist/images/srcset.js.map +1 -0
  97. package/dist/images/store.d.ts +32 -0
  98. package/dist/images/store.d.ts.map +1 -0
  99. package/dist/images/store.js +54 -0
  100. package/dist/images/store.js.map +1 -0
  101. package/dist/images/types.d.ts +128 -0
  102. package/dist/images/types.d.ts.map +1 -0
  103. package/dist/images/types.js +22 -0
  104. package/dist/images/types.js.map +1 -0
  105. package/dist/images/wasm.d.ts +43 -0
  106. package/dist/images/wasm.d.ts.map +1 -0
  107. package/dist/images/wasm.js +128 -0
  108. package/dist/images/wasm.js.map +1 -0
  109. package/dist/index.d.ts +29 -0
  110. package/dist/index.d.ts.map +1 -0
  111. package/dist/index.js +29 -0
  112. package/dist/index.js.map +1 -0
  113. package/dist/pwa/build.d.ts +39 -0
  114. package/dist/pwa/build.d.ts.map +1 -0
  115. package/dist/pwa/build.js +38 -0
  116. package/dist/pwa/build.js.map +1 -0
  117. package/dist/pwa/client.d.ts +90 -0
  118. package/dist/pwa/client.d.ts.map +1 -0
  119. package/dist/pwa/client.js +76 -0
  120. package/dist/pwa/client.js.map +1 -0
  121. package/dist/pwa/index.d.ts +29 -0
  122. package/dist/pwa/index.d.ts.map +1 -0
  123. package/dist/pwa/index.js +22 -0
  124. package/dist/pwa/index.js.map +1 -0
  125. package/dist/pwa/manifest.d.ts +69 -0
  126. package/dist/pwa/manifest.d.ts.map +1 -0
  127. package/dist/pwa/manifest.js +108 -0
  128. package/dist/pwa/manifest.js.map +1 -0
  129. package/dist/pwa/offline.d.ts +44 -0
  130. package/dist/pwa/offline.d.ts.map +1 -0
  131. package/dist/pwa/offline.js +94 -0
  132. package/dist/pwa/offline.js.map +1 -0
  133. package/dist/pwa/service-worker.d.ts +71 -0
  134. package/dist/pwa/service-worker.d.ts.map +1 -0
  135. package/dist/pwa/service-worker.js +119 -0
  136. package/dist/pwa/service-worker.js.map +1 -0
  137. package/dist/pwa/strategy.d.ts +36 -0
  138. package/dist/pwa/strategy.d.ts.map +1 -0
  139. package/dist/pwa/strategy.js +180 -0
  140. package/dist/pwa/strategy.js.map +1 -0
  141. package/dist/pwa/sw-runtime.d.ts +109 -0
  142. package/dist/pwa/sw-runtime.d.ts.map +1 -0
  143. package/dist/pwa/sw-runtime.js +214 -0
  144. package/dist/pwa/sw-runtime.js.map +1 -0
  145. package/dist/pwa/types.d.ts +107 -0
  146. package/dist/pwa/types.d.ts.map +1 -0
  147. package/dist/pwa/types.js +18 -0
  148. package/dist/pwa/types.js.map +1 -0
  149. package/dist/pwa/version.d.ts +45 -0
  150. package/dist/pwa/version.d.ts.map +1 -0
  151. package/dist/pwa/version.js +89 -0
  152. package/dist/pwa/version.js.map +1 -0
  153. package/dist/skin/color.d.ts +50 -0
  154. package/dist/skin/color.d.ts.map +1 -0
  155. package/dist/skin/color.js +176 -0
  156. package/dist/skin/color.js.map +1 -0
  157. package/dist/skin/css.d.ts +19 -0
  158. package/dist/skin/css.d.ts.map +1 -0
  159. package/dist/skin/css.js +69 -0
  160. package/dist/skin/css.js.map +1 -0
  161. package/dist/skin/errors.d.ts +22 -0
  162. package/dist/skin/errors.d.ts.map +1 -0
  163. package/dist/skin/errors.js +59 -0
  164. package/dist/skin/errors.js.map +1 -0
  165. package/dist/skin/index.d.ts +15 -0
  166. package/dist/skin/index.d.ts.map +1 -0
  167. package/dist/skin/index.js +11 -0
  168. package/dist/skin/index.js.map +1 -0
  169. package/dist/skin/sheet.d.ts +42 -0
  170. package/dist/skin/sheet.d.ts.map +1 -0
  171. package/dist/skin/sheet.js +36 -0
  172. package/dist/skin/sheet.js.map +1 -0
  173. package/dist/skin/tokens.d.ts +90 -0
  174. package/dist/skin/tokens.d.ts.map +1 -0
  175. package/dist/skin/tokens.js +56 -0
  176. package/dist/skin/tokens.js.map +1 -0
  177. package/dist/skin/validate.d.ts +14 -0
  178. package/dist/skin/validate.d.ts.map +1 -0
  179. package/dist/skin/validate.js +272 -0
  180. package/dist/skin/validate.js.map +1 -0
  181. package/dist/theme/load-theme.d.ts +37 -0
  182. package/dist/theme/load-theme.d.ts.map +1 -0
  183. package/dist/theme/load-theme.js +102 -0
  184. package/dist/theme/load-theme.js.map +1 -0
  185. package/dist/theme/manifest.d.ts +48 -0
  186. package/dist/theme/manifest.d.ts.map +1 -0
  187. package/dist/theme/manifest.js +36 -0
  188. package/dist/theme/manifest.js.map +1 -0
  189. package/dist/theme/verify/forbidden.d.ts +39 -0
  190. package/dist/theme/verify/forbidden.d.ts.map +1 -0
  191. package/dist/theme/verify/forbidden.js +107 -0
  192. package/dist/theme/verify/forbidden.js.map +1 -0
  193. package/dist/theme/verify/scan-file.d.ts +15 -0
  194. package/dist/theme/verify/scan-file.d.ts.map +1 -0
  195. package/dist/theme/verify/scan-file.js +151 -0
  196. package/dist/theme/verify/scan-file.js.map +1 -0
  197. package/dist/theme/verify/scanner.d.ts +52 -0
  198. package/dist/theme/verify/scanner.d.ts.map +1 -0
  199. package/dist/theme/verify/scanner.js +304 -0
  200. package/dist/theme/verify/scanner.js.map +1 -0
  201. package/dist/theme/verify/verify-theme.d.ts +44 -0
  202. package/dist/theme/verify/verify-theme.d.ts.map +1 -0
  203. package/dist/theme/verify/verify-theme.js +179 -0
  204. package/dist/theme/verify/verify-theme.js.map +1 -0
  205. package/package.json +56 -0
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The page side of the PWA: registration, and the way out.
3
+ *
4
+ * These functions take the browser objects as arguments instead of reaching for
5
+ * `navigator` and `caches`. That is what lets them be unit-tested at all — the
6
+ * package compiles without the DOM library — and it keeps the surface the code
7
+ * depends on down to the six methods declared here. A caller in the admin (L2)
8
+ * passes `navigator.serviceWorker` and `caches`; the real objects satisfy these
9
+ * interfaces structurally, with no cast.
10
+ */
11
+ export const DEFAULT_SERVICE_WORKER_URL = '/sw.js';
12
+ /**
13
+ * Registers the worker at the root scope.
14
+ *
15
+ * Returns `null` rather than throwing when the browser has no service worker
16
+ * support, or when registration fails: a site that renders on the server does
17
+ * not stop working because an offline nicety could not be installed (rule R1
18
+ * applied to the browser).
19
+ */
20
+ export async function registerServiceWorker(container, options = {}) {
21
+ if (container === undefined)
22
+ return null;
23
+ try {
24
+ return await container.register(options.scriptUrl ?? DEFAULT_SERVICE_WORKER_URL, {
25
+ scope: options.scope ?? '/',
26
+ updateViaCache: 'none',
27
+ });
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ /**
34
+ * Deletes the caches this site owns. Names outside `cachePrefix` are left
35
+ * alone: another application may share the origin, and deleting a neighbour's
36
+ * cache is a worse incident than the stale content this is fixing.
37
+ */
38
+ export async function purgeSiteCaches(cacheStorage, cachePrefix) {
39
+ if (cacheStorage === undefined)
40
+ return [];
41
+ const names = await cacheStorage.keys();
42
+ const mine = names.filter((name) => name.startsWith(`${cachePrefix}:`));
43
+ await Promise.all(mine.map((name) => cacheStorage.delete(name)));
44
+ return mine;
45
+ }
46
+ /**
47
+ * The "clear client cache" button of the admin, as a function.
48
+ *
49
+ * The order is deliberate. The active worker is asked to purge and unregister
50
+ * itself first, because it is the only party that can empty its caches while it
51
+ * is still running. Then the registrations are unregistered from the page side,
52
+ * which also covers a worker that is broken, hung, or from a build that never
53
+ * understood the message. Only then are the remaining caches deleted directly —
54
+ * doing that first would simply let the still-running worker refill them.
55
+ *
56
+ * Even after all of this the current page is still controlled by the old
57
+ * worker, which is why the report insists on a reload.
58
+ */
59
+ export async function resetPwaClient(environment, cachePrefix) {
60
+ const container = environment.serviceWorker;
61
+ const cacheStorage = environment.caches;
62
+ const supported = container !== undefined && cacheStorage !== undefined;
63
+ const controlled = container?.controller != null;
64
+ let registrationsRemoved = 0;
65
+ if (container !== undefined) {
66
+ const registrations = await container.getRegistrations();
67
+ for (const registration of registrations) {
68
+ registration.active?.postMessage({ type: 'cogenta:unregister' });
69
+ }
70
+ const results = await Promise.all(registrations.map((registration) => registration.unregister().catch(() => false)));
71
+ registrationsRemoved = results.filter((removed) => removed).length;
72
+ }
73
+ const cachesDeleted = await purgeSiteCaches(cacheStorage, cachePrefix);
74
+ return { registrationsRemoved, cachesDeleted, reloadRequired: controlled, supported };
75
+ }
76
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/pwa/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAwCH,MAAM,CAAC,MAAM,0BAA0B,GAAG,QAAQ,CAAA;AAElD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,SAAmD,EACnD,OAAO,GAA6D,EAAE;IAEtE,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACxC,IAAI,CAAC;QACH,OAAO,MAAM,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,IAAI,0BAA0B,EAAE;YAC/E,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG;YAC3B,cAAc,EAAE,MAAM;SACvB,CAAC,CAAA;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAiBD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,YAA4C,EAC5C,WAAmB;IAEnB,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,EAAE,CAAA;IACzC,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,CAAA;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC,CAAA;IACvE,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAChE,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,WAAiC,EACjC,WAAmB;IAEnB,MAAM,SAAS,GAAG,WAAW,CAAC,aAAa,CAAA;IAC3C,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,CAAA;IACvC,MAAM,SAAS,GAAG,SAAS,KAAK,SAAS,IAAI,YAAY,KAAK,SAAS,CAAA;IACvE,MAAM,UAAU,GAAG,SAAS,EAAE,UAAU,IAAI,IAAI,CAAA;IAEhD,IAAI,oBAAoB,GAAG,CAAC,CAAA;IAC5B,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,aAAa,GAAG,MAAM,SAAS,CAAC,gBAAgB,EAAE,CAAA;QACxD,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;YACzC,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC,CAAA;QAClE,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,aAAa,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAClF,CAAA;QACD,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,CAAA;IACpE,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,eAAe,CAAC,YAAY,EAAE,WAAW,CAAC,CAAA;IAEtE,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,CAAA;AACvF,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * PWA layer: manifest, service worker, per-resource cache strategies, offline
3
+ * page, and — the part that has to exist before the first deploy, not after the
4
+ * first incident — a working way to purge the caches and remove the worker.
5
+ *
6
+ * Deliberately dependency-free. Workbox would cover most of this, but it is a
7
+ * large dependency for code we would still have to understand line by line the
8
+ * day a visitor reports stale content, and rule R9 asks for zero dependencies
9
+ * before a small one. What is here is a route table, three caching strategies
10
+ * and a generational purge; the value Workbox adds over that is a build plugin
11
+ * we do not want and a precache manifest we deliberately keep tiny.
12
+ */
13
+ export type { PwaArtefacts, PwaBuildInput } from './build.js';
14
+ export { buildPwaAssets, DEFAULT_OFFLINE_URL } from './build.js';
15
+ export type { ClientCacheStorage, ClientRegisterOptions, ClientRegistration, ClientServiceWorkerContainer, ClientWorker, PwaClientEnvironment, PwaResetReport, } from './client.js';
16
+ export { DEFAULT_SERVICE_WORKER_URL, purgeSiteCaches, registerServiceWorker, resetPwaClient, } from './client.js';
17
+ export type { DisplayMode, InstallabilityProblem, ManifestIcon, ManifestInput, WebAppManifest, } from './manifest.js';
18
+ export { assertInstallable, buildManifest, checkInstallability, MANIFEST_CONTENT_TYPE, renderManifest, } from './manifest.js';
19
+ export type { OfflinePageOptions } from './offline.js';
20
+ export { OFFLINE_PAGE_SCRIPT, renderOfflinePage } from './offline.js';
21
+ export { renderServiceWorker, renderTombstoneServiceWorker, SERVICE_WORKER_HEADERS, } from './service-worker.js';
22
+ export { chooseStrategy, DEFAULT_ROUTES, IMMUTABLE_ASSET_PATTERN } from './strategy.js';
23
+ export type { ServiceWorkerScope } from './sw-runtime.js';
24
+ export { SW_MESSAGE_ACTIVATED, SW_MESSAGE_PURGE, SW_MESSAGE_UNREGISTER, SW_MESSAGE_UNREGISTERED, serviceWorkerMain, tombstoneMain, } from './sw-runtime.js';
25
+ export type { CacheBucket, CacheStrategy, PwaConfig, RequestDescriptor, RouteRule, StrategyDecision, } from './types.js';
26
+ export { CACHE_BUCKETS } from './types.js';
27
+ export type { ParsedCacheName } from './version.js';
28
+ export { bucketCacheName, cacheNameFor, cachesToPurge, computeCacheVersion, DEFAULT_CACHE_PREFIX, parseCacheName, } from './version.js';
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pwa/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAChE,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,4BAA4B,EAC5B,YAAY,EACZ,oBAAoB,EACpB,cAAc,GACf,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,0BAA0B,EAC1B,eAAe,EACf,qBAAqB,EACrB,cAAc,GACf,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,WAAW,EACX,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AACrE,OAAO,EACL,mBAAmB,EACnB,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACvF,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,aAAa,GACd,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,WAAW,EACX,aAAa,EACb,SAAS,EACT,iBAAiB,EACjB,SAAS,EACT,gBAAgB,GACjB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EACL,eAAe,EACf,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,GACf,MAAM,cAAc,CAAA"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * PWA layer: manifest, service worker, per-resource cache strategies, offline
3
+ * page, and — the part that has to exist before the first deploy, not after the
4
+ * first incident — a working way to purge the caches and remove the worker.
5
+ *
6
+ * Deliberately dependency-free. Workbox would cover most of this, but it is a
7
+ * large dependency for code we would still have to understand line by line the
8
+ * day a visitor reports stale content, and rule R9 asks for zero dependencies
9
+ * before a small one. What is here is a route table, three caching strategies
10
+ * and a generational purge; the value Workbox adds over that is a build plugin
11
+ * we do not want and a precache manifest we deliberately keep tiny.
12
+ */
13
+ export { buildPwaAssets, DEFAULT_OFFLINE_URL } from './build.js';
14
+ export { DEFAULT_SERVICE_WORKER_URL, purgeSiteCaches, registerServiceWorker, resetPwaClient, } from './client.js';
15
+ export { assertInstallable, buildManifest, checkInstallability, MANIFEST_CONTENT_TYPE, renderManifest, } from './manifest.js';
16
+ export { OFFLINE_PAGE_SCRIPT, renderOfflinePage } from './offline.js';
17
+ export { renderServiceWorker, renderTombstoneServiceWorker, SERVICE_WORKER_HEADERS, } from './service-worker.js';
18
+ export { chooseStrategy, DEFAULT_ROUTES, IMMUTABLE_ASSET_PATTERN } from './strategy.js';
19
+ export { SW_MESSAGE_ACTIVATED, SW_MESSAGE_PURGE, SW_MESSAGE_UNREGISTER, SW_MESSAGE_UNREGISTERED, serviceWorkerMain, tombstoneMain, } from './sw-runtime.js';
20
+ export { CACHE_BUCKETS } from './types.js';
21
+ export { bucketCacheName, cacheNameFor, cachesToPurge, computeCacheVersion, DEFAULT_CACHE_PREFIX, parseCacheName, } from './version.js';
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pwa/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAUhE,OAAO,EACL,0BAA0B,EAC1B,eAAe,EACf,qBAAqB,EACrB,cAAc,GACf,MAAM,aAAa,CAAA;AAQpB,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,GACf,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AACrE,OAAO,EACL,mBAAmB,EACnB,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAEvF,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,aAAa,GACd,MAAM,iBAAiB,CAAA;AASxB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,OAAO,EACL,eAAe,EACf,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,GACf,MAAM,cAAc,CAAA"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The web application manifest, and the installability check that goes with it.
3
+ *
4
+ * Installability is not a property of the manifest alone — the browser also
5
+ * requires a registered service worker that answers navigations offline, which
6
+ * is what the document route and the precached offline page provide. What this
7
+ * module can check is the half that is data, and it checks it *before* the
8
+ * build ships rather than leaving the author to discover in a Lighthouse report
9
+ * that the install button never appeared.
10
+ */
11
+ export type DisplayMode = 'fullscreen' | 'standalone' | 'minimal-ui' | 'browser';
12
+ export interface ManifestIcon {
13
+ readonly src: string;
14
+ /** `"192x192"`, or `"any"` for a vector. */
15
+ readonly sizes: string;
16
+ readonly type: string;
17
+ /**
18
+ * `any` is drawn as-is; `maskable` fills a platform-defined shape. A single
19
+ * icon may declare both, but then it must survive being cropped to a circle.
20
+ */
21
+ readonly purpose?: 'any' | 'maskable' | 'any maskable';
22
+ }
23
+ export interface WebAppManifest {
24
+ /** Stable identity of the application. Changing it installs a *new* app. */
25
+ readonly id: string;
26
+ readonly name: string;
27
+ readonly short_name: string;
28
+ readonly description?: string;
29
+ readonly start_url: string;
30
+ readonly scope: string;
31
+ readonly display: DisplayMode;
32
+ readonly background_color: string;
33
+ readonly theme_color: string;
34
+ readonly lang: string;
35
+ readonly dir: 'ltr' | 'rtl' | 'auto';
36
+ readonly icons: readonly ManifestIcon[];
37
+ }
38
+ export interface ManifestInput {
39
+ readonly name: string;
40
+ readonly shortName?: string;
41
+ readonly description?: string;
42
+ /** Absolute path within the origin. Where the installed app opens. */
43
+ readonly startUrl?: string;
44
+ readonly scope?: string;
45
+ readonly display?: DisplayMode;
46
+ readonly backgroundColor: string;
47
+ readonly themeColor: string;
48
+ readonly lang: string;
49
+ readonly dir?: 'ltr' | 'rtl' | 'auto';
50
+ readonly icons: readonly ManifestIcon[];
51
+ }
52
+ export declare function buildManifest(input: ManifestInput): WebAppManifest;
53
+ export interface InstallabilityProblem {
54
+ readonly field: string;
55
+ readonly problem: string;
56
+ readonly fix: string;
57
+ }
58
+ /**
59
+ * Reports everything that would stop a browser offering installation, in one
60
+ * pass. Reporting one problem per run is the loop that makes an author give up
61
+ * on the third iteration.
62
+ */
63
+ export declare function checkInstallability(manifest: WebAppManifest): InstallabilityProblem[];
64
+ /** Throws a message an author can act on, listing every problem at once. */
65
+ export declare function assertInstallable(manifest: WebAppManifest): void;
66
+ /** Serialised manifest, ready to write to `manifest.webmanifest`. */
67
+ export declare function renderManifest(manifest: WebAppManifest): string;
68
+ export declare const MANIFEST_CONTENT_TYPE = "application/manifest+json";
69
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/pwa/manifest.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,CAAA;AAEhF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,GAAG,UAAU,GAAG,cAAc,CAAA;CACvD;AAED,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAA;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;CACxC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAA;IAC9B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAA;IACrC,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;CACxC;AAQD,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAsBlE;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAQD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,qBAAqB,EAAE,CA4DrF;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAWhE;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAE/D;AAED,eAAO,MAAM,qBAAqB,8BAA8B,CAAA"}
@@ -0,0 +1,108 @@
1
+ import { CogentaError } from '@cogenta/core';
2
+ /**
3
+ * Android home-screen labels are truncated around twelve characters, so a long
4
+ * `short_name` is silently mangled rather than rejected.
5
+ */
6
+ const SHORT_NAME_MAX = 12;
7
+ export function buildManifest(input) {
8
+ const scope = input.scope ?? '/';
9
+ const shortName = input.shortName ?? input.name.slice(0, SHORT_NAME_MAX);
10
+ const manifest = {
11
+ // `id` defaults to `start_url` in browsers, which means changing the start
12
+ // URL orphans the installed app. Pinning it to the scope makes the identity
13
+ // survive a routing change.
14
+ id: scope,
15
+ name: input.name,
16
+ short_name: shortName,
17
+ start_url: input.startUrl ?? scope,
18
+ scope,
19
+ display: input.display ?? 'standalone',
20
+ background_color: input.backgroundColor,
21
+ theme_color: input.themeColor,
22
+ lang: input.lang,
23
+ dir: input.dir ?? 'ltr',
24
+ icons: input.icons,
25
+ };
26
+ return input.description === undefined
27
+ ? manifest
28
+ : { ...manifest, description: input.description };
29
+ }
30
+ function hasSquareIcon(icons, edge) {
31
+ return icons.some((icon) => icon.sizes.split(/\s+/).some((size) => size.toLowerCase() === `${edge}x${edge}`));
32
+ }
33
+ /**
34
+ * Reports everything that would stop a browser offering installation, in one
35
+ * pass. Reporting one problem per run is the loop that makes an author give up
36
+ * on the third iteration.
37
+ */
38
+ export function checkInstallability(manifest) {
39
+ const problems = [];
40
+ if (manifest.name.trim() === '') {
41
+ problems.push({
42
+ field: 'name',
43
+ problem: 'the manifest has no name',
44
+ fix: 'Set the site name: it is the label of the install prompt.',
45
+ });
46
+ }
47
+ if (manifest.short_name.length > SHORT_NAME_MAX) {
48
+ problems.push({
49
+ field: 'short_name',
50
+ problem: `short_name is ${manifest.short_name.length} characters`,
51
+ fix: `Keep it to ${SHORT_NAME_MAX} characters or fewer, or the home screen truncates it.`,
52
+ });
53
+ }
54
+ if (manifest.display === 'browser') {
55
+ problems.push({
56
+ field: 'display',
57
+ problem: 'display is "browser", which no browser treats as installable',
58
+ fix: 'Use "standalone", "minimal-ui" or "fullscreen".',
59
+ });
60
+ }
61
+ if (!manifest.start_url.startsWith(manifest.scope)) {
62
+ problems.push({
63
+ field: 'start_url',
64
+ problem: `start_url "${manifest.start_url}" is outside scope "${manifest.scope}"`,
65
+ fix: 'The start URL must be inside the scope, or the installed app opens outside itself.',
66
+ });
67
+ }
68
+ if (!hasSquareIcon(manifest.icons, 192)) {
69
+ problems.push({
70
+ field: 'icons',
71
+ problem: 'no 192x192 icon',
72
+ fix: 'Add a 192x192 PNG: it is the minimum Android requires for the install prompt.',
73
+ });
74
+ }
75
+ if (!hasSquareIcon(manifest.icons, 512)) {
76
+ problems.push({
77
+ field: 'icons',
78
+ problem: 'no 512x512 icon',
79
+ fix: 'Add a 512x512 PNG: it is used for the splash screen.',
80
+ });
81
+ }
82
+ if (!manifest.icons.some((icon) => icon.purpose?.includes('maskable') === true)) {
83
+ problems.push({
84
+ field: 'icons',
85
+ problem: 'no maskable icon',
86
+ fix: 'Add an icon with purpose "maskable" and 20% safe padding, or Android draws your square icon inside a white circle.',
87
+ });
88
+ }
89
+ return problems;
90
+ }
91
+ /** Throws a message an author can act on, listing every problem at once. */
92
+ export function assertInstallable(manifest) {
93
+ const problems = checkInstallability(manifest);
94
+ if (problems.length === 0)
95
+ return;
96
+ throw new CogentaError({
97
+ code: 'CONFIG_INVALID',
98
+ message: `The web app manifest is not installable (${problems.length} problem${problems.length === 1 ? '' : 's'}):\n${problems.map((p) => ` ${p.field}: ${p.problem} — ${p.fix}`).join('\n')}`,
99
+ hint: 'A browser offers installation only when the manifest is complete, the display mode is not "browser", and a service worker answers navigations offline.',
100
+ details: { problems },
101
+ });
102
+ }
103
+ /** Serialised manifest, ready to write to `manifest.webmanifest`. */
104
+ export function renderManifest(manifest) {
105
+ return `${JSON.stringify(manifest, null, 2)}\n`;
106
+ }
107
+ export const MANIFEST_CONTENT_TYPE = 'application/manifest+json';
108
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/pwa/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The offline page.
3
+ *
4
+ * It has one job and one honesty requirement: say that the *network* is
5
+ * missing, not that the site is broken, and never render an empty shell. A
6
+ * blank page is indistinguishable from a crashed site, so a visitor reloads,
7
+ * blames the site, and leaves.
8
+ *
9
+ * It is fully self-contained — inline styles, no font, no image, no external
10
+ * stylesheet — because every external reference is one more thing that must
11
+ * have been cached for the page to render at the exact moment nothing can be
12
+ * fetched. It styles itself from the skin custom properties when they happen to
13
+ * be defined, and falls back to literal values when they are not.
14
+ */
15
+ export interface OfflinePageOptions {
16
+ readonly siteName: string;
17
+ /** BCP 47 tag. Drives the `lang` attribute, so screen readers pronounce it. */
18
+ readonly lang: string;
19
+ readonly dir?: 'ltr' | 'rtl' | 'auto';
20
+ /** Heading. Must state that the visitor is offline. */
21
+ readonly title?: string;
22
+ readonly message?: string;
23
+ readonly retryLabel?: string;
24
+ /** Shown once the browser reports the connection is back. */
25
+ readonly restoredLabel?: string;
26
+ /**
27
+ * Whether to include the retry script. Disable it on a site whose CSP has no
28
+ * hash or nonce for inline scripts; the page still renders and still tells
29
+ * the truth, it just cannot retry by itself.
30
+ */
31
+ readonly interactive?: boolean;
32
+ }
33
+ /**
34
+ * The inline script, exported so a strict CSP can hash it
35
+ * (`script-src 'sha256-…'`) instead of loosening to `unsafe-inline`.
36
+ *
37
+ * It does two things and nothing else: reload on demand, and notice when the
38
+ * connection returns. `navigator.onLine` is famously optimistic — it reports a
39
+ * live network adapter, not a reachable site — so it is used only to *offer* a
40
+ * reload, never to claim the site is back.
41
+ */
42
+ export declare const OFFLINE_PAGE_SCRIPT: string;
43
+ export declare function renderOfflinePage(options: OfflinePageOptions): string;
44
+ //# sourceMappingURL=offline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline.d.ts","sourceRoot":"","sources":["../../src/pwa/offline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAcH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAA;IACrC,uDAAuD;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,6DAA6D;IAC7D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,QAOpB,CAAA;AAkBZ,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAuCrE"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The offline page.
3
+ *
4
+ * It has one job and one honesty requirement: say that the *network* is
5
+ * missing, not that the site is broken, and never render an empty shell. A
6
+ * blank page is indistinguishable from a crashed site, so a visitor reloads,
7
+ * blames the site, and leaves.
8
+ *
9
+ * It is fully self-contained — inline styles, no font, no image, no external
10
+ * stylesheet — because every external reference is one more thing that must
11
+ * have been cached for the page to render at the exact moment nothing can be
12
+ * fetched. It styles itself from the skin custom properties when they happen to
13
+ * be defined, and falls back to literal values when they are not.
14
+ */
15
+ const ESCAPES = {
16
+ '&': '&',
17
+ '<': '&lt;',
18
+ '>': '&gt;',
19
+ '"': '&quot;',
20
+ "'": '&#39;',
21
+ };
22
+ function escapeHtml(value) {
23
+ return value.replace(/[&<>"']/g, (char) => ESCAPES[char] ?? char);
24
+ }
25
+ /**
26
+ * The inline script, exported so a strict CSP can hash it
27
+ * (`script-src 'sha256-…'`) instead of loosening to `unsafe-inline`.
28
+ *
29
+ * It does two things and nothing else: reload on demand, and notice when the
30
+ * connection returns. `navigator.onLine` is famously optimistic — it reports a
31
+ * live network adapter, not a reachable site — so it is used only to *offer* a
32
+ * reload, never to claim the site is back.
33
+ */
34
+ export const OFFLINE_PAGE_SCRIPT = [
35
+ '(function () {',
36
+ ' var button = document.getElementById("cogenta-offline-retry")',
37
+ ' var status = document.getElementById("cogenta-offline-status")',
38
+ ' if (button) button.addEventListener("click", function () { location.reload() })',
39
+ ' addEventListener("online", function () { if (status) status.hidden = false })',
40
+ '})()',
41
+ ].join('\n');
42
+ const STYLE = [
43
+ ':root{color-scheme:light dark}',
44
+ 'body{margin:0;min-height:100vh;display:grid;place-items:center;',
45
+ 'padding:var(--cogenta-space-unit,1rem);',
46
+ 'background:var(--cogenta-color-bg,#fff);color:var(--cogenta-color-fg,#111);',
47
+ 'font-family:var(--cogenta-font-sans,system-ui,sans-serif);line-height:1.5}',
48
+ 'main{max-width:34rem;text-align:center}',
49
+ 'h1{font-size:var(--cogenta-font-size-xl,1.75rem);margin:0 0 .5rem}',
50
+ 'p{margin:0 0 1.5rem;color:var(--cogenta-color-muted-fg,#555)}',
51
+ 'button{font:inherit;cursor:pointer;padding:.6rem 1.2rem;',
52
+ 'border:1px solid var(--cogenta-color-border,#ccc);',
53
+ 'border-radius:var(--cogenta-radius-md,.375rem);',
54
+ 'background:var(--cogenta-color-accent,#111);color:var(--cogenta-color-accent-fg,#fff)}',
55
+ '[hidden]{display:none}',
56
+ ].join('');
57
+ export function renderOfflinePage(options) {
58
+ const lang = escapeHtml(options.lang);
59
+ const dir = options.dir ?? 'ltr';
60
+ const siteName = escapeHtml(options.siteName);
61
+ const title = escapeHtml(options.title ?? 'You are offline');
62
+ const message = escapeHtml(options.message ??
63
+ 'This page has not been downloaded yet, and there is no connection to fetch it. Everything you have already visited is still available.');
64
+ const retryLabel = escapeHtml(options.retryLabel ?? 'Try again');
65
+ const restoredLabel = escapeHtml(options.restoredLabel ?? 'The connection is back.');
66
+ const interactive = options.interactive ?? true;
67
+ return [
68
+ '<!doctype html>',
69
+ `<html lang="${lang}" dir="${dir}">`,
70
+ '<head>',
71
+ '<meta charset="utf-8">',
72
+ '<meta name="viewport" content="width=device-width, initial-scale=1">',
73
+ // Not indexable, and never a canonical answer for a real URL.
74
+ '<meta name="robots" content="noindex">',
75
+ `<title>${title} · ${siteName}</title>`,
76
+ `<style>${STYLE}</style>`,
77
+ '</head>',
78
+ '<body>',
79
+ '<main>',
80
+ `<h1>${title}</h1>`,
81
+ `<p>${message}</p>`,
82
+ interactive
83
+ ? `<p id="cogenta-offline-status" hidden>${restoredLabel}</p><button id="cogenta-offline-retry" type="button">${retryLabel}</button>`
84
+ : '',
85
+ '</main>',
86
+ interactive ? `<script>${OFFLINE_PAGE_SCRIPT}</script>` : '',
87
+ '</body>',
88
+ '</html>',
89
+ '',
90
+ ]
91
+ .filter((line) => line !== '')
92
+ .join('\n');
93
+ }
94
+ //# sourceMappingURL=offline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline.js","sourceRoot":"","sources":["../../src/pwa/offline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,OAAO,GAAqC;IAChD,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,OAAO;CACb,CAAA;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAA;AACnE,CAAC;AAqBD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,gBAAgB;IAChB,iEAAiE;IACjE,kEAAkE;IAClE,mFAAmF;IACnF,iFAAiF;IACjF,MAAM;CACP,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAEZ,MAAM,KAAK,GAAG;IACZ,gCAAgC;IAChC,iEAAiE;IACjE,yCAAyC;IACzC,6EAA6E;IAC7E,4EAA4E;IAC5E,yCAAyC;IACzC,oEAAoE;IACpE,+DAA+D;IAC/D,0DAA0D;IAC1D,oDAAoD;IACpD,iDAAiD;IACjD,wFAAwF;IACxF,wBAAwB;CACzB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAEV,MAAM,UAAU,iBAAiB,CAAC,OAA2B;IAC3D,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,KAAK,CAAA;IAChC,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC7C,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,IAAI,iBAAiB,CAAC,CAAA;IAC5D,MAAM,OAAO,GAAG,UAAU,CACxB,OAAO,CAAC,OAAO;QACb,wIAAwI,CAC3I,CAAA;IACD,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,UAAU,IAAI,WAAW,CAAC,CAAA;IAChE,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,CAAC,aAAa,IAAI,yBAAyB,CAAC,CAAA;IACpF,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAA;IAE/C,OAAO;QACL,iBAAiB;QACjB,eAAe,IAAI,UAAU,GAAG,IAAI;QACpC,QAAQ;QACR,wBAAwB;QACxB,sEAAsE;QACtE,8DAA8D;QAC9D,wCAAwC;QACxC,UAAU,KAAK,MAAM,QAAQ,UAAU;QACvC,UAAU,KAAK,UAAU;QACzB,SAAS;QACT,QAAQ;QACR,QAAQ;QACR,OAAO,KAAK,OAAO;QACnB,MAAM,OAAO,MAAM;QACnB,WAAW;YACT,CAAC,CAAC,yCAAyC,aAAa,wDAAwD,UAAU,WAAW;YACrI,CAAC,CAAC,EAAE;QACN,SAAS;QACT,WAAW,CAAC,CAAC,CAAC,WAAW,mBAAmB,WAAW,CAAC,CAAC,CAAC,EAAE;QAC5D,SAAS;QACT,SAAS;QACT,EAAE;KACH;SACE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;SAC7B,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC"}
@@ -0,0 +1,71 @@
1
+ import type { PwaConfig } from './types.js';
2
+ /**
3
+ * Generation of the service worker script.
4
+ *
5
+ * ## The update strategy, stated in full
6
+ *
7
+ * `skipWaiting()` and `clients.claim()` are two calls; the strategy is the four
8
+ * properties that make those two calls safe.
9
+ *
10
+ * **1. Every generation owns its caches.** Cache names embed a version derived
11
+ * from the offline page, the precache list and the route table
12
+ * (`computeCacheVersion`). A new deploy therefore writes into new caches and
13
+ * cannot mix generations, which is what makes immediate activation safe: there
14
+ * is no window in which half the entries are old.
15
+ *
16
+ * **2. Activation purges the previous generations.** `cachesToPurge` returns
17
+ * every cache of ours whose version is not the current one, and activation
18
+ * deletes them before claiming clients. Storage does not accumulate one
19
+ * generation per deploy, and — the part that matters — yesterday's HTML is
20
+ * *gone*, not merely lower priority.
21
+ *
22
+ * **3. Documents are never served from cache while the network works.** Even a
23
+ * worker that somehow survived a bad deploy cannot pin an old page: the
24
+ * document strategy is network-first, so the cache answers only when the
25
+ * network has already failed. This is the property that keeps a mistake
26
+ * recoverable by deploying a fix, rather than requiring every visitor to clear
27
+ * their site data.
28
+ *
29
+ * **4. There is an exit.** The worker answers a message by purging every cache
30
+ * it owns and calling `registration.unregister()` — that is the admin's "clear
31
+ * client cache" button. For clients we can no longer run code on,
32
+ * `renderTombstoneServiceWorker` produces a worker that unregisters itself on
33
+ * activation; deploying it at the same URL removes the PWA from every browser
34
+ * that re-fetches the script, which browsers do at least daily.
35
+ *
36
+ * ## What is deliberately *not* done
37
+ *
38
+ * The worker does not reload open pages when it activates. A forced reload
39
+ * destroys scroll position, half-typed comments and video playback, and it
40
+ * turns one bad deploy into a reload loop. It posts `cogenta:activated`
41
+ * instead, and the page decides — the registration helper in `client.ts` turns
42
+ * that into an "a new version is available" prompt.
43
+ *
44
+ * The worker is a *classic* script, not an ES module: module workers are still
45
+ * unevenly supported, and there is nothing to import — the script is
46
+ * self-contained by construction.
47
+ */
48
+ /**
49
+ * Headers the server must send for the service worker script.
50
+ *
51
+ * This is not decoration. A worker script cached by the HTTP cache for a year
52
+ * is the canonical unkillable zombie: the browser's daily update check reads
53
+ * the stale copy from disk, sees no change, and keeps the old worker alive —
54
+ * including after the tombstone has been deployed. `no-cache` (revalidate, do
55
+ * not blindly reuse) is the minimum, and the scope header lets the script live
56
+ * anywhere while controlling the whole origin.
57
+ */
58
+ export declare const SERVICE_WORKER_HEADERS: Readonly<Record<string, string>>;
59
+ export declare function renderServiceWorker(config: PwaConfig): string;
60
+ /**
61
+ * The kill switch, as a deployable artefact.
62
+ *
63
+ * Deploy this at the URL the real worker was served from. It caches nothing,
64
+ * intercepts nothing, deletes every cache under `cachePrefix` and unregisters
65
+ * itself. Keep it deployed for at least one update cycle — twenty-four hours
66
+ * covers browsers that only check the script once a day — before removing the
67
+ * route entirely, because a 404 on the worker URL also unregisters the worker
68
+ * but tells the caches nothing.
69
+ */
70
+ export declare function renderTombstoneServiceWorker(cachePrefix: string): string;
71
+ //# sourceMappingURL=service-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-worker.d.ts","sourceRoot":"","sources":["../../src/pwa/service-worker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAG3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAInE,CAAA;AA0BD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAiB7D;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CASxE"}