@abide/abide 0.116.0 → 0.116.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @abide/abide
2
2
 
3
+ ## 0.116.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 7dedb9a: the offline worker keeps an answer marked `cache-control: public` — a public file, a font a stylesheet draws with — through a sign-in, a sign-out and a form submitted, which drop only what was saved for the caller
8
+ - 6a69393: a copy the offline worker kept stands in for a year where the network fails, rather than a week — a page, a GET's answer and a public file carry `stale-if-error=31536000`
9
+
3
10
  ## 0.116.0
4
11
 
5
12
  ### Minor Changes
package/docs/REGISTRY.md CHANGED
@@ -291,8 +291,8 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
291
291
  | `cache-control: no-store` | `/__abide/openapi.json`, `/__abide/mcp` | Refuses caching for an answer about the app's surfaces. | 21.4 |
292
292
  | `cache-control: public, max-age=31536000, immutable` | the built bundle | A chunk addressed by its own content hash. | 21.5 |
293
293
  | `cache-control: public, max-age=0, must-revalidate` | a file under a `public` directory | A file at an address the build never chose. | 21.12 |
294
- | `cache-control: private, stale-while-revalidate=604800, stale-if-error=604800` | a page, a `GET`'s answer, where `ABIDE_OFFLINE` registers its worker | The default that lets a copy kept on the device stand in for the network. | 21.22, 21.11, 21.17 |
295
- | `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=604800` | a file under a `public` directory, where `ABIDE_OFFLINE` registers its worker | The same, for a file at an address the build never chose. | 21.23 |
294
+ | `cache-control: private, stale-while-revalidate=604800, stale-if-error=31536000` | a page, a `GET`'s answer, where `ABIDE_OFFLINE` registers its worker | The default that lets a copy kept on the device stand in for the network. | 21.22, 21.11, 21.17 |
295
+ | `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=31536000` | a file under a `public` directory, where `ABIDE_OFFLINE` registers its worker | The same, for a file at an address the build never chose. | 21.23 |
296
296
  | `clear-site-data: "cache"` | an answer carrying `abide-principal`, where pages are kept offline | Drops what the browser's own cache kept for the caller signed in before. | 21.24 |
297
297
  | `etag` | a file under a `public` directory | The answered bytes, addressed by their content. | 21.13, 21.14 |
298
298
  | `referrer-policy: strict-origin-when-cross-origin` | a page | The browsers' own default, written down. | 21.6 |
package/docs/RULEBOOK.md CHANGED
@@ -1448,11 +1448,11 @@ time those rungs took apart from what they wrapped.
1448
1448
 
1449
1449
  21.22 Where 45.1 has a page register the worker `ABIDE_OFFLINE` turns on, a page, and the answer of
1450
1450
  a `GET` of the app's own that is neither a refusal nor a stream 16.32 frames, MUST default to `cache-control: private,
1451
- stale-while-revalidate=604800, stale-if-error=604800`.
1451
+ stale-while-revalidate=604800, stale-if-error=31536000`.
1452
1452
 
1453
1453
  21.23 Where 45.1 has a page register the worker `ABIDE_OFFLINE` turns on, a file under a `public`
1454
1454
  directory MUST answer `cache-control: public, max-age=0, stale-while-revalidate=604800,
1455
- stale-if-error=604800`.
1455
+ stale-if-error=31536000`.
1456
1456
 
1457
1457
  21.24 Where 45.1 has a page register the worker `ABIDE_OFFLINE` turns on, an answer carrying the
1458
1458
  `abide-principal` header 26.24 has it carry MUST carry `clear-site-data: "cache"`.
@@ -3301,8 +3301,9 @@ rather than one a redirect led to, that is neither a stream 16.32 frames nor a b
3301
3301
  45.6 The worker `ABIDE_OFFLINE` turns on MUST answer a file of the build's bundle from what it
3302
3302
  saved.
3303
3303
 
3304
- 45.7 The worker `ABIDE_OFFLINE` turns on MUST drop everything it saved where an answer it is handed
3305
- carries `abide-principal`, where `principal` changes in any tab, and where a form is submitted.
3304
+ 45.7 The worker `ABIDE_OFFLINE` turns on MUST drop everything it saved, save an answer whose
3305
+ `cache-control` is `public`, where an answer it is handed carries `abide-principal`, where
3306
+ `principal` changes in any tab, and where a form is submitted.
3306
3307
 
3307
3308
  45.8 The worker `ABIDE_OFFLINE` turns on, of a new build, MUST drop everything the one before it
3308
3309
  saved, and every file of the bundle the new build does not name.
package/docs/abide.md CHANGED
@@ -6541,12 +6541,12 @@ abide start
6541
6541
  ABIDE_OFFLINE=0 abide start
6542
6542
  ```
6543
6543
 
6544
- Each page the CRM opens is kept on the device, with the answers its handlers gave, the public files it loaded and the bundle it runs, until `ABIDE_OFFLINE=0` turns that off where the CRM is served. A kept page opens at once, however weak the signal, and the network is asked for the next copy behind it. With the network gone, a page opened in the last week loads again with its data and works. One never opened says the device is offline, and a link to it leaves the page you are on standing.
6544
+ Each page the CRM opens is kept on the device, with the answers its handlers gave, the public files it loaded and the bundle it runs, until `ABIDE_OFFLINE=0` turns that off where the CRM is served. A kept page opens at once, however weak the signal, and the network is asked for the next copy behind it. With the network gone, a page opened in the last year loads again with its data and works. One never opened says the device is offline, and a link to it leaves the page you are on standing.
6545
6545
 
6546
6546
  | Name | What it does for the CRM |
6547
6547
  | --- | --- |
6548
- | `cache-control: private, stale-while-revalidate=604800, stale-if-error=604800` | what a page and a handler's answer carry, so the device shows what it kept while it asks again |
6549
- | `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=604800` | the same, for a public file |
6548
+ | `cache-control: private, stale-while-revalidate=604800, stale-if-error=31536000` | what a page and a handler's answer carry, so the device shows what it kept while it asks again |
6549
+ | `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=31536000` | the same, for a public file |
6550
6550
  | `clear-site-data: "cache"` | what a sign-in or a sign-out answers, so the browser forgets what it kept for the last person |
6551
6551
 
6552
6552
  What is kept is what those headers allow, and nothing else decides it. A handler that writes `cache-control: no-store` through `response` is never kept, and an answer with a `ttl` is shown from the device until its `max-age` runs out. A refresh always asks the network.
@@ -7681,8 +7681,8 @@ Transports: what is written in place rather than imported.
7681
7681
  | `cache-control: no-store` | `/__abide/openapi.json`, `/__abide/mcp` | Refuses caching for an answer about the app's surfaces. | [21.4](/rulebook#21.4) |
7682
7682
  | `cache-control: public, max-age=31536000, immutable` | the built bundle | A chunk addressed by its own content hash. | [21.5](/rulebook#21.5) |
7683
7683
  | `cache-control: public, max-age=0, must-revalidate` | a file under a `public` directory | A file at an address the build never chose. | [21.12](/rulebook#21.12) |
7684
- | `cache-control: private, stale-while-revalidate=604800, stale-if-error=604800` | a page, a `GET`'s answer, where `ABIDE_OFFLINE` registers its worker | The default that lets a copy kept on the device stand in for the network. | [21.22](/rulebook#21.22), [21.11](/rulebook#21.11), [21.17](/rulebook#21.17) |
7685
- | `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=604800` | a file under a `public` directory, where `ABIDE_OFFLINE` registers its worker | The same, for a file at an address the build never chose. | [21.23](/rulebook#21.23) |
7684
+ | `cache-control: private, stale-while-revalidate=604800, stale-if-error=31536000` | a page, a `GET`'s answer, where `ABIDE_OFFLINE` registers its worker | The default that lets a copy kept on the device stand in for the network. | [21.22](/rulebook#21.22), [21.11](/rulebook#21.11), [21.17](/rulebook#21.17) |
7685
+ | `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=31536000` | a file under a `public` directory, where `ABIDE_OFFLINE` registers its worker | The same, for a file at an address the build never chose. | [21.23](/rulebook#21.23) |
7686
7686
  | `clear-site-data: "cache"` | an answer carrying `abide-principal`, where pages are kept offline | Drops what the browser's own cache kept for the caller signed in before. | [21.24](/rulebook#21.24) |
7687
7687
  | `etag` | a file under a `public` directory | The answered bytes, addressed by their content. | [21.13](/rulebook#21.13), [21.14](/rulebook#21.14) |
7688
7688
  | `referrer-policy: strict-origin-when-cross-origin` | a page | The browsers' own default, written down. | [21.6](/rulebook#21.6) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abide/abide",
3
- "version": "0.116.0",
3
+ "version": "0.116.1",
4
4
  "type": "module",
5
5
  "description": "Isomorphic type-safe framework for reactive async interfaces for humans and machines",
6
6
  "license": "MIT",
@@ -1,10 +1,11 @@
1
1
  // 21.22, 21.23 — HOW LONG A COPY KEPT ON THE DEVICE STAYS SERVABLE once stale, where `ABIDE_OFFLINE`
2
- // registers its worker: a week, while the network answers or fails to — as long as Safari keeps an
3
- // unvisited site's storage.
2
+ // registers its worker: a week while the network answers and the copy is asked for again behind it,
3
+ // and a year where the network fails — a copy of any age beats the page saying nothing was saved, and
4
+ // what may evict it sooner (Safari's week for a site not visited) is the browser's, not this header's.
4
5
  //
5
6
  // NO `max-age` BESIDE IT on a page or a `GET`: a copy reads `max-age` as its own life (18.14), and
6
7
  // a followed copy stale from the moment it landed would reload on every read. With no lifetime and
7
8
  // no `Last-Modified`, a cache holds the answer stale at once, which is all the two windows need.
8
9
 
9
10
  export const OFFLINE_STALE =
10
- 'stale-while-revalidate=604800, stale-if-error=604800'
11
+ 'stale-while-revalidate=604800, stale-if-error=31536000'
@@ -15,6 +15,7 @@ import {
15
15
  OFFLINE_BUNDLE,
16
16
  OFFLINE_CACHE_PREFIX,
17
17
  OFFLINE_SAVED,
18
+ OFFLINE_SHARED,
18
19
  } from '#shared/OFFLINE_CACHES.ts'
19
20
  import { PRINCIPAL_HEADER } from '#shared/PRINCIPAL_HEADER.ts'
20
21
  import { REACHED_MESSAGE } from '#shared/REACHED_MESSAGE.ts'
@@ -31,6 +32,7 @@ const STREAMED_TYPES = [
31
32
 
32
33
  const CACHES = `const OURS = ${JSON.stringify(OFFLINE_CACHE_PREFIX)}
33
34
  const SAVED = ${JSON.stringify(OFFLINE_SAVED)}
35
+ const SHARED = ${JSON.stringify(OFFLINE_SHARED)}
34
36
  const BUNDLE = ${JSON.stringify(OFFLINE_BUNDLE)}
35
37
 
36
38
  async function dropOurs(keeping) {
@@ -112,7 +114,7 @@ async function answer(event, page) {
112
114
  request.cache === 'no-store')
113
115
  const saved = bypass
114
116
  ? undefined
115
- : await caches.match(request, { cacheName: SAVED, ignoreVary: true })
117
+ : await savedCopy(request)
116
118
  const life = saved === undefined ? undefined : lifeOf(saved)
117
119
  if (life !== undefined) {
118
120
  if (life.age < life.fresh) return marked(saved)
@@ -197,7 +199,7 @@ async function keepReached(url) {
197
199
  if (reaching.has(url)) return
198
200
  reaching.add(url)
199
201
  try {
200
- const saved = await caches.match(url, { cacheName: SAVED, ignoreVary: true })
202
+ const saved = await savedCopy(url)
201
203
  if (saved === undefined || !standsIn(lifeOf(saved))) await keepPage(url)
202
204
  } finally {
203
205
  reaching.delete(url)
@@ -247,9 +249,19 @@ function kept(response) {
247
249
  return !STREAMED.some((streamed) => type.startsWith(streamed))
248
250
  }
249
251
 
252
+ // The copy saved of an address, a caller's own ahead of one any caller may be answered.
253
+ async function savedCopy(request) {
254
+ return (
255
+ (await caches.match(request, { cacheName: SAVED, ignoreVary: true })) ??
256
+ (await caches.match(request, { cacheName: SHARED, ignoreVary: true }))
257
+ )
258
+ }
259
+
250
260
  // 45.7 — EVERY PAGE SAVED DROPPED, and with them every copy asked for before the drop: one asked under
251
261
  // the cookies of then would land after it holding the caller signed out since. The drop an answer's
252
- // own abide-principal makes is the exception, since that answer is the new caller's.
262
+ // own abide-principal makes is the exception, since that answer is the new caller's. What any caller
263
+ // may be answered is kept apart and left standing: it was never the caller's to drop, and a font
264
+ // thrown away at sign-in is a page drawn without it offline.
253
265
  let drops = 0
254
266
 
255
267
  function dropSaved() {
@@ -259,15 +271,19 @@ function dropSaved() {
259
271
 
260
272
  // Saves a copy 45.3 and 45.4 let through where no drop has come since its request was asked — after
261
273
  // the drop its own answer makes where that answer rotated the principal — and cancels one where one has.
274
+ // One any caller may be answered is saved whenever it was asked, since no caller's cookies made it.
262
275
  async function keep(request, copy, since, rotated) {
263
276
  if (rotated) {
264
277
  await dropSaved()
265
278
  since = drops
266
279
  }
267
280
  if (copy === undefined) return
268
- if (since !== drops) return copy.body?.cancel()
269
- const cache = await caches.open(SAVED)
270
- await cache.put(request, copy)
281
+ const shared = directivesOf(copy).has('public')
282
+ if (!shared && since !== drops) return copy.body?.cancel()
283
+ // An address whose answer went public or private since leaves no copy behind for savedCopy to
284
+ // prefer over this one.
285
+ await (await caches.open(shared ? SAVED : SHARED)).delete(request)
286
+ await (await caches.open(shared ? SHARED : SAVED)).put(request, copy)
271
287
  }
272
288
 
273
289
  function offline() {
@@ -5,8 +5,11 @@
5
5
  /** What every cache the worker keeps is named under, so a cache an earlier worker named is found. */
6
6
  export const OFFLINE_CACHE_PREFIX = 'abide:'
7
7
 
8
- /** The pages, the handlers' answers and the public files the worker saved, by address. */
8
+ /** The pages and the handlers' answers the worker saved, by address. */
9
9
  export const OFFLINE_SAVED = `${OFFLINE_CACHE_PREFIX}saved`
10
10
 
11
+ /** What the worker saved that any caller may be answered — `cache-control: public` — by address. */
12
+ export const OFFLINE_SHARED = `${OFFLINE_CACHE_PREFIX}shared`
13
+
11
14
  /** The build's bundle, saved as the worker installs. */
12
15
  export const OFFLINE_BUNDLE = `${OFFLINE_CACHE_PREFIX}bundle`