@greendrake/vue-api 0.6.0 → 0.7.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 (68) hide show
  1. package/README.md +15 -9
  2. package/catalogue/af.yaml +7 -0
  3. package/catalogue/ar.yaml +7 -0
  4. package/catalogue/az.yaml +7 -0
  5. package/catalogue/be.yaml +7 -0
  6. package/catalogue/bg.yaml +7 -0
  7. package/catalogue/bn.yaml +7 -0
  8. package/catalogue/bs.yaml +7 -0
  9. package/catalogue/cs.yaml +7 -0
  10. package/catalogue/da.yaml +7 -0
  11. package/catalogue/de.yaml +7 -0
  12. package/catalogue/en-GB.yaml +14 -0
  13. package/catalogue/en-US.yaml +7 -0
  14. package/catalogue/es-419.yaml +7 -0
  15. package/catalogue/es-ES.yaml +7 -0
  16. package/catalogue/fa.yaml +7 -0
  17. package/catalogue/fi.yaml +7 -0
  18. package/catalogue/fil.yaml +7 -0
  19. package/catalogue/fr.yaml +7 -0
  20. package/catalogue/gl.yaml +7 -0
  21. package/catalogue/gu.yaml +7 -0
  22. package/catalogue/he.yaml +7 -0
  23. package/catalogue/hi.yaml +7 -0
  24. package/catalogue/id.yaml +7 -0
  25. package/catalogue/it.yaml +7 -0
  26. package/catalogue/ja.yaml +7 -0
  27. package/catalogue/jv.yaml +7 -0
  28. package/catalogue/kk.yaml +7 -0
  29. package/catalogue/ko.yaml +7 -0
  30. package/catalogue/lb.yaml +7 -0
  31. package/catalogue/lt.yaml +7 -0
  32. package/catalogue/mk.yaml +7 -0
  33. package/catalogue/mn.yaml +7 -0
  34. package/catalogue/mr.yaml +7 -0
  35. package/catalogue/ms.yaml +7 -0
  36. package/catalogue/my.yaml +7 -0
  37. package/catalogue/nb.yaml +7 -0
  38. package/catalogue/ne.yaml +7 -0
  39. package/catalogue/nl.yaml +7 -0
  40. package/catalogue/pa.yaml +7 -0
  41. package/catalogue/pl.yaml +7 -0
  42. package/catalogue/pt-BR.yaml +7 -0
  43. package/catalogue/pt-PT.yaml +7 -0
  44. package/catalogue/ro.yaml +7 -0
  45. package/catalogue/ru.yaml +7 -0
  46. package/catalogue/sk.yaml +7 -0
  47. package/catalogue/sm.yaml +7 -0
  48. package/catalogue/sq.yaml +7 -0
  49. package/catalogue/sr.yaml +7 -0
  50. package/catalogue/su.yaml +7 -0
  51. package/catalogue/sv.yaml +7 -0
  52. package/catalogue/sw.yaml +7 -0
  53. package/catalogue/te.yaml +7 -0
  54. package/catalogue/th.yaml +7 -0
  55. package/catalogue/to.yaml +7 -0
  56. package/catalogue/tr.yaml +7 -0
  57. package/catalogue/uk.yaml +7 -0
  58. package/catalogue/uz.yaml +7 -0
  59. package/catalogue/vi.yaml +7 -0
  60. package/catalogue/yo.yaml +7 -0
  61. package/catalogue/zh-Hans.yaml +7 -0
  62. package/catalogue/zh-Hant.yaml +7 -0
  63. package/package.json +9 -7
  64. package/src/ApiClient.ts +28 -15
  65. package/src/connectivity.ts +12 -1
  66. package/src/keepConnected.ts +68 -0
  67. package/src/live.ts +1 -1
  68. package/src/main.ts +3 -2
package/README.md CHANGED
@@ -25,7 +25,7 @@ export interface Methods {
25
25
  'host.get': (id: number) => Host
26
26
  'host.update': (record: { id: number } & Partial<Host>) => Host
27
27
  'host.delete': (args: { id: number }) => void
28
- 'vlan.list': (query: Record<string, never>) => Vlan[]
28
+ 'vlan.list': () => Vlan[]
29
29
  }
30
30
 
31
31
  export const api = new ApiClient<Methods>({
@@ -83,15 +83,15 @@ Calls from components and stores:
83
83
  import { ref } from 'vue'
84
84
  import { api } from '@/api'
85
85
 
86
- const host = await api.call('host.get', [42]) // Host; the global spinner shows while pending
86
+ const host = await api.call('host.get', 42) // Host; the global spinner shows while pending
87
87
 
88
88
  const saving = ref(false)
89
- await api.call('host.update', [{ id: 42, name: 'nas' }], { pending: saving }) // this ref instead
89
+ await api.with({ pending: saving }).call('host.update', { id: 42, name: 'nas' }) // this ref instead
90
90
 
91
- const vlans = await api.call('vlan.list', [{}], { silent: true }) // no spinner, no toast; errors reject
91
+ const vlans = await api.with({ silent: true }).call('vlan.list') // no spinner, no toast; errors reject
92
92
  ```
93
93
 
94
- `call(method, args, options?)` takes the arguments as a tuple (`Parameters<M[K]>`), unlike `RpcClient.call`'s variadic form.
94
+ `call(method, ...args)` takes a method's own arguments, as `RpcClient.call` does (`@greendrake/rpc`'s `Caller`), and a method that takes none is called with none. `with(options)` is the calls made under `CallOptions`: a separate entry point, because nothing at runtime tells trailing options from a last argument. `ApiCaller<M>` is the two together, for a library typing the client it is handed.
95
95
 
96
96
  ## ApiClient
97
97
 
@@ -109,7 +109,7 @@ const vlans = await api.call('vlan.list', [{}], { silent: true }) // no spinner,
109
109
  - `silent: true` — no loading indication and no error UX; errors reject straight to the caller. For background calls and callers owning their own error presentation.
110
110
  - `cache: false` — reads past the cache and does not store the answer. Cannot opt an unlisted method in and leaves the nonce rule alone.
111
111
 
112
- Cache access on the client: `invalidateCache(method, ...args)` (with args, that entry; without, the whole method), `clearCache()`, `primeCache(method, args, data)`.
112
+ Cache access on the client: `invalidateCache(method, ...args)` (with args, that entry; without, the whole method), `clearCache()`.
113
113
 
114
114
  `setDebugDelay(method, ms)` — dev builds only: holds a call pending for `ms` so its loading state can be inspected; `0` clears. Inert in production.
115
115
 
@@ -158,6 +158,12 @@ import { retryWhenOnline } from '@greendrake/vue-api'
158
158
  const strings = await retryWhenOnline(() => fetchJson<Strings>(`/locales/${locale}.json`))
159
159
  ```
160
160
 
161
+ `keepConnected<Methods>(transport, method)` keeps a `WsTransport` (any `Revivable`: `probe` and `pipe.reconnectNow`) connected for the page's life and returns the teardown. The window's `online` event collapses a waiting reconnect's delay. While the page is visible — on becoming visible, then every 60 s — it reconnects a socket waiting out a backoff and probes with `method`, so a socket that died silently (a backgrounded app, an expired NAT mapping) is abandoned and reconnected. Nothing runs while the page is hidden. `method` is a key of the app's method map: a probe takes any answer, a refusal included, as proof of life, so a method the server lacks would report every socket alive.
162
+
163
+ ```ts
164
+ keepConnected<Methods>(transport, 'app.ping')
165
+ ```
166
+
161
167
  ## Live updates
162
168
 
163
169
  `bindLive(transport, options)` is `@greendrake/rpc`'s `liveBinding` under this package's UX policy, and `useLive` is the component-scoped form that releases on unmount.
@@ -171,19 +177,19 @@ useLive(transport, {
171
177
  'thread.message': { when: pushed => pushed.thread_id === props.threadId }
172
178
  },
173
179
  resync: async () => {
174
- thread.value = await api.call('thread.get', [props.threadId], backgroundCall(true))
180
+ thread.value = await api.with(backgroundCall(true)).call('thread.get', props.threadId)
175
181
  }
176
182
  })
177
183
  ```
178
184
 
179
185
  The mechanism — relevance, `apply` versus `resync`, the reconnect re-read, coalescing, release — is documented in `@greendrake/rpc`. What this package adds is what happens around a `resync`:
180
186
 
181
- - It runs unseen. `backgroundCall(background)` is the one place that decides what that means: `{ silent: true }`, which bypasses both the loading counter and the error-UX pipeline. `pending: false` is not enough — it suppresses the spinner and still raises the blocking retry toast over a screen the user is reading.
187
+ - It runs unseen. `backgroundCall(background)` is the one place that decides what that means: `{ silent: true }` (and `{}` for a load somebody asked for), which bypasses both the loading counter and the error-UX pipeline. `pending: false` is not enough — it suppresses the spinner and still raises the blocking retry toast over a screen the user is reading.
182
188
  - Its failure neither interrupts nor vanishes. A `NotFoundError` is dropped (the entity went away while the socket was down; the next navigation says so), a `NetworkError` is dropped (the socket went again mid-read, and the binding's own reconnect rule re-reads when it returns), and anything else goes to the `errorSink` configured through `configureApiUX` — reported, never toasted.
183
189
  - It must read past a response cache the missed pushes would have invalidated: invalidate the entries the read would hit, then read, so the stale entry is repaired rather than stepped around.
184
190
 
185
191
  `backgroundCall` also serves any loader told whether anybody asked for its load — a table re-reading after a push, say, which is where a panel's re-read actually calls the API:
186
192
 
187
193
  ```ts
188
- const load = (args: ListArgs, background: boolean) => api.call('admin.thread.list', [args], backgroundCall(background))
194
+ const load = (args: ListArgs, background: boolean) => api.with(backgroundCall(background)).call('admin.thread.list', args)
189
195
  ```
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Dit lyk of jy nie aanlyn is nie — wag vir jou konneksie…
7
+ unreachable: Kan nie @:brand.name bereik nie — wag vir die konneksie…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: يبدو أنك غير متصل بالإنترنت — جاري انتظار اتصالك…
7
+ unreachable: تعذر الوصول إلى @:brand.name — جاري انتظار الاتصال…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Görünür, internet bağlantınız yoxdur — bağlantı gözlənilir…
7
+ unreachable: '@:brand.name ilə əlaqə qurmaq mümkün olmadı — bağlantı gözlənilir…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Вы ўзяліся ў аўтаномны рэжым — чакаем падключэння…
7
+ unreachable: Не удаецца звязацца з @:brand.name — чакаем падключэння…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Изглежда, че сте офлайн — очакване на връзка…
7
+ unreachable: Няма връзка с @:brand.name — очакване на връзка…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: মনে হচ্ছে আপনি অফলাইনে আছেন — সংযোগের জন্য অপেক্ষা করা হচ্ছে…
7
+ unreachable: '@:brand.name-এর সাথে সংযোগ করা যাচ্ছে না — সংযোগের জন্য অপেক্ষা করা হচ্ছে…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Izgleda da niste povezani na internet — čekanje povezivanja…
7
+ unreachable: Ne može se uspostaviti veza sa @:brand.name — čekanje povezivanja…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Vypadá to, že jste odpojeni — čekáme na připojení…
7
+ unreachable: Nelze připojit k @:brand.name — čekáme na připojení…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Det ser ud til, at du er offline — venter på forbindelse…
7
+ unreachable: Kan ikke få forbindelse til @:brand.name — venter på forbindelse…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Du scheinst offline zu sein – warte auf deine Verbindung…
7
+ unreachable: '@:brand.name ist nicht erreichbar – warte auf die Verbindung…'
@@ -0,0 +1,14 @@
1
+ # This package's strings (en-GB), the master every other locale is rendered
2
+ # from by fe/lib/translate.yaml. A host serves them merged under its own
3
+ # catalogue (@greendrake/i18n), and may reword one by defining its key.
4
+ #
5
+ # vue-i18n syntax. @:brand.name is the host's name for itself, which every
6
+ # host supplies (setupI18n's constants).
7
+
8
+ connection:
9
+ # Two readings of the same fact — calls cannot get through. Only the device
10
+ # signal tells them apart, which is the whole of what it is trusted for.
11
+ # A linked message must be closed by whitespace: '@:brand.name.' would read
12
+ # the trailing period as part of the key and render the key verbatim.
13
+ device_offline: 'You appear to be offline — waiting for your connection…'
14
+ unreachable: 'Cannot reach @:brand.name — waiting for the connection…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: You appear to be offline — waiting for your connection…
7
+ unreachable: Cannot reach @:brand.name — waiting for the connection…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Parece que estás sin conexión — esperando tu conexión…
7
+ unreachable: No se puede alcanzar @:brand.name — esperando la conexión…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Parece que estás sin conexión — esperando a que se restablezca la conexión…
7
+ unreachable: No se puede alcanzar @:brand.name — esperando la conexión…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: به نظر می‌رسد آفلاین هستید — در انتظار اتصال…
7
+ unreachable: اتصال به @:brand.name برقرار نمی‌شود — در انتظار اتصال…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Näytät olevan offline-tilassa – odotetaan yhteyttä…
7
+ unreachable: '@:brand.name ei tavoita – odotetaan yhteyttä…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Mukhang offline ka — naghihintay ng iyong koneksyon…
7
+ unreachable: Hindi maabot ang @:brand.name — naghihintay ng koneksyon…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Vous semblez être hors ligne — attente de votre connexion…
7
+ unreachable: Impossible de joindre @:brand.name — attente de la connexion…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Parece que estás sen conexión — agardando a que conectes…
7
+ unreachable: Non se pode conectar con @:brand.name — agardando a conexión…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: તમે ઓફલાઇન હોવ તેમ લાગે છે — તમારા કનેક્શનની રાહ જોઈ રહ્યા છીએ…
7
+ unreachable: '@:brand.name સુધી પહોંચી શકાય તેમ નથી — કનેક્શનની રાહ જોઈ રહ્યા છીએ…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: נראה שאתה לא מחובר — ממתין לחיבור שלך...
7
+ unreachable: לא ניתן להגיע ל-@:brand.name — ממתין לחיבור...
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: ऐसा लगता है कि आप ऑफलाइन हैं — आपके कनेक्शन का इंतज़ार है…
7
+ unreachable: '@:brand.name से संपर्क नहीं हो पा रहा है — कनेक्शन का इंतज़ार है…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Anda tampaknya sedang luring — menunggu koneksi Anda…
7
+ unreachable: Tidak dapat menjangkau @:brand.name — menunggu koneksi…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Sembra che tu sia offline — in attesa della connessione…
7
+ unreachable: Impossibile raggiungere @:brand.name — in attesa della connessione…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: オフラインのようです — 接続を待機しています…
7
+ unreachable: '@:brand.name に接続できません — 接続を待機しています…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Sampeyan ketoke offline — ngenteni koneksi…
7
+ unreachable: Ora bisa nyambung menyang @:brand.name — ngenteni koneksi…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Сіз офлайн көрінесіз — байланыс күтілуде…
7
+ unreachable: '@:brand.name сервисіне қосылу мүмкін емес — байланыс күтілуде…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: 오프라인 상태인 것 같습니다 — 연결을 기다리는 중입니다…
7
+ unreachable: '@:brand.name에 연결할 수 없습니다 — 연결을 기다리는 중입니다…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Dir schéngt offline ze sinn — waart op Är Verbindung…
7
+ unreachable: '@:brand.name kann net erreecht ginn — waart op d''Verbindung…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Atrodo, kad esate be interneto – laukiam ryšio...
7
+ unreachable: Nepavyko prisijungti prie @:brand.name – laukiam ryšio...
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Изгледа дека сте офлајн — се чека поврзување…
7
+ unreachable: Не може да се поврзе со @:brand.name — се чека поврзување…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Та интернэтгүй байгаа бололтой — холболтыг хүлээж байна…
7
+ unreachable: '@:brand.name-тэй холбогдож чадсангүй — холболтыг хүлээж байна…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: तुम्ही ऑफलाइन असल्याचे दिसते — कनेक्शनची प्रतीक्षा करत आहे…
7
+ unreachable: '@:brand.name शी संपर्क होऊ शकत नाही — कनेक्शनची प्रतीक्षा करत आहे…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Anda kelihatan tidak berada dalam talian — menunggu sambungan anda…
7
+ unreachable: Tidak dapat menghubungi @:brand.name — menunggu sambungan…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: သင်သည် အင်တာနက်လိုင်း ပြတ်တောက်နေပုံရသည် — ချိတ်ဆက်မှုကို စောင့်ဆိုင်းနေသည်…
7
+ unreachable: '@:brand.name သို့ ချိတ်ဆက်၍မရပါ — ချိတ်ဆက်မှုကို စောင့်ဆိုင်းနေသည်…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Det ser ut til at du er frakoblet – venter på tilkobling…
7
+ unreachable: Kan ikke nå @:brand.name – venter på tilkobling…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: तपाईं अफलाइन हुनुहुन्छ जस्तो देखिन्छ — जडानको प्रतीक्षा गर्दै…
7
+ unreachable: '@:brand.name सँग सम्पर्क हुन सकेन — जडानको प्रतीक्षा गर्दै…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Je lijkt offline te zijn — wachten op verbinding…
7
+ unreachable: '@:brand.name is niet bereikbaar — wachten op verbinding…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: ਲੱਗਦਾ ਹੈ ਕਿ ਤੁਸੀਂ ਆਫਲਾਈਨ ਹੋ — ਤੁਹਾਡੇ ਕਨੈਕਸ਼ਨ ਦੀ ਉਡੀਕ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…
7
+ unreachable: '@:brand.name ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ — ਕਨੈਕਸ਼ਨ ਦੀ ਉਡੀਕ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Wygląda na to, że jesteś offline — czekamy na połączenie…
7
+ unreachable: Nie można połączyć się z @:brand.name — czekamy na połączenie…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Você parece estar offline — aguardando sua conexão…
7
+ unreachable: Não é possível alcançar o @:brand.name — aguardando a conexão…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Parece que está offline — a aguardar a ligação…
7
+ unreachable: Não é possível aceder ao @:brand.name — a aguardar a ligação…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Se pare că ești offline — se așteaptă după conexiune…
7
+ unreachable: Nu se poate ajunge la @:brand.name — se așteaptă după conexiune…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Похоже, вы не в сети — ожидаем подключения…
7
+ unreachable: Не удалось связаться с @:brand.name — ожидаем подключения…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Zjavuje sa, že ste odpojení — čakáme na pripojenie…
7
+ unreachable: Nie je možné nájsť @:brand.name — čakáme na pripojenie…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: E foliga mai e le o oe i luga o le initaneti — o lo'o fa'atali mo lau feso'ota'iga…
7
+ unreachable: E le mafai ona fesootai ma le @:brand.name — o lo'o fa'atali mo le feso'ota'iga…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Duket se je offline — po pritet lidhja…
7
+ unreachable: Nuk mund të lidhem me @:brand.name — po pritet lidhja…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Izgleda da niste povezani na internet — čekanje veze…
7
+ unreachable: Ne može se uspostaviti veza sa @:brand.name — čekanje veze…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Anjeun sigana nuju offline — ngantosan koneksi…
7
+ unreachable: Teu tiasa ngahubungi @:brand.name — ngantosan koneksi…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Det ser ut som att du är offline – väntar på anslutning…
7
+ unreachable: Kan inte nå @:brand.name – väntar på anslutning…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Inaonekana hauko mtandaoni — tunasubiri muunganisho wako…
7
+ unreachable: Hatuwezi kuifikia @:brand.name — tunasubiri muunganisho…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: మీరు ఆఫ్‌లైన్‌లో ఉన్నట్లు కనిపిస్తోంది — మీ కనెక్షన్ కోసం వేచి ఉంది…
7
+ unreachable: '@:brand.nameని సంప్రదించలేకపోతున్నాము — కనెక్షన్ కోసం వేచి ఉంది…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: ดูเหมือนว่าคุณจะไม่ได้เชื่อมต่ออินเทอร์เน็ต — กำลังรอการเชื่อมต่อ…
7
+ unreachable: ไม่สามารถเชื่อมต่อกับ @:brand.name ได้ — กำลังรอการเชื่อมต่อ…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: ‘Oku hā mai ‘oku ‘ikai ke koe faka‘amanatu‘i ki he initaneti — ‘oku tatali ki ho faka‘amanatu‘i…
7
+ unreachable: ‘Oku ‘ikai ke lava ‘o faka‘amanatu‘i ki he @:brand.name — ‘oku tatali ki he faka‘amanatu‘i…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Çevrimdışı görünüyorsunuz — bağlantınız bekleniyor…
7
+ unreachable: '@:brand.name adresine ulaşılamıyor — bağlantı bekleniyor…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Схоже, ви офлайн — чекаємо на з'єднання…
7
+ unreachable: Не вдається зв'язатися з @:brand.name — чекаємо на з'єднання…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Sizda internet aloqasi yo‘qdek — ulanish kutilmoqda…
7
+ unreachable: '@:brand.name bilan bog‘lanib bo‘lmadi — ulanish kutilmoqda…'
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Có vẻ như bạn đang ngoại tuyến — đang chờ kết nối…
7
+ unreachable: Không thể kết nối với @:brand.name — đang chờ kết nối…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: Ó dàbí ẹni pé o kò lọ́wọ́ sí ẹ̀rọ ayélujára — a ń dúró fún ìsopọ̀ rẹ…
7
+ unreachable: A kò le kan @:brand.name — a ń dúró fún ìsopọ̀…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: 你似乎已离线 — 正在等待连接…
7
+ unreachable: 无法连接到 @:brand.name — 正在等待连接…
@@ -0,0 +1,7 @@
1
+ # GENERATED by bin/l1 translate from the en-GB master. Do not add or remove keys.
2
+ # An edit to a value here is kept until its source string, this locale's
3
+ # rendering of the terms (translation/glossary/, beside translate.yaml) or the
4
+ # translation policy changes, at which point the string is rendered again.
5
+ connection:
6
+ device_offline: 你似乎已離線 — 正在等待連線…
7
+ unreachable: 無法連線至 @:brand.name — 正在等待連線…
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greendrake/vue-api",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Vue binding of @greendrake/rpc: loading-state wiring, response cache, error-UX pipeline, silent calls, live bindings to server push",
5
5
  "license": "MIT",
6
6
  "author": "Eugene Greendrake <eugene@greendrake.nz>",
@@ -23,8 +23,10 @@
23
23
  }
24
24
  },
25
25
  "files": [
26
- "src"
26
+ "src",
27
+ "catalogue"
27
28
  ],
29
+ "catalogue": "catalogue",
28
30
  "sideEffects": false,
29
31
  "publishConfig": {
30
32
  "access": "public"
@@ -34,17 +36,17 @@
34
36
  },
35
37
  "prettier": "@greendrake/dev-config/prettier",
36
38
  "dependencies": {
37
- "@greendrake/rpc": "0.6.0",
38
- "@greendrake/ui": "0.6.0",
39
- "@greendrake/util": "0.6.0",
40
- "@greendrake/vue-kit": "0.6.0"
39
+ "@greendrake/rpc": "0.7.0",
40
+ "@greendrake/ui": "0.7.0",
41
+ "@greendrake/util": "0.7.0",
42
+ "@greendrake/vue-kit": "0.7.0"
41
43
  },
42
44
  "peerDependencies": {
43
45
  "pinia": "^3.0.4",
44
46
  "vue": "^3.5.34"
45
47
  },
46
48
  "devDependencies": {
47
- "@greendrake/dev-config": "0.6.0",
49
+ "@greendrake/dev-config": "0.7.0",
48
50
  "pinia": "^3.0.4",
49
51
  "typescript": "6.0.3",
50
52
  "vue": "^3.5.34",
package/src/ApiClient.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { isRef, type Ref } from 'vue'
2
2
  import { createDeferred, NetworkError, TimeOutPromise } from '@greendrake/util'
3
3
  import { ResponseCache, WebSocketClosedError, codeOf, packRequest } from '@greendrake/rpc'
4
- import type { CacheConfig, MethodMap, MethodMapConstraint, Transport } from '@greendrake/rpc'
4
+ import type { CacheConfig, Caller, MethodMap, MethodMapConstraint, Transport } from '@greendrake/rpc'
5
5
  import { useAppState } from '@greendrake/vue-kit'
6
6
  import { useConnectivity } from './connectivity'
7
7
  import { useExceptionState } from './exceptionState'
@@ -56,9 +56,18 @@ export interface ApiClientOptions {
56
56
  locale?: () => string | undefined
57
57
  }
58
58
 
59
+ // What calls the methods of M as ApiClient does: its own calls, under no options,
60
+ // and those made under some.
61
+ export interface ApiCaller<M extends MethodMapConstraint<M>> extends Caller<M> {
62
+ // The calls made under options. A separate entry point rather than a trailing
63
+ // parameter: nothing at runtime tells trailing options from a last argument.
64
+ // api.with({ pending: false }).call('listing.get', { listing_id })
65
+ with(options: CallOptions): Caller<M>
66
+ }
67
+
59
68
  // The Vue-facing API client: typed calls with loading-state wiring, response
60
69
  // caching, and the blocking-toast retry pipeline.
61
- export class ApiClient<M extends MethodMapConstraint<M> = MethodMap> {
70
+ export class ApiClient<M extends MethodMapConstraint<M> = MethodMap> implements ApiCaller<M> {
62
71
  readonly transport: Transport
63
72
  readonly #cache?: ResponseCache
64
73
  readonly #errorHandlers?: Record<string, (e: Error) => void>
@@ -103,7 +112,17 @@ export class ApiClient<M extends MethodMapConstraint<M> = MethodMap> {
103
112
  }
104
113
  }
105
114
 
106
- // The one way to invoke a method. Whether the request carries a nonce is
115
+ call<K extends keyof M & string>(method: K, ...args: Parameters<M[K]>): Promise<Awaited<ReturnType<M[K]>>> {
116
+ return this.#call({}, method, args)
117
+ }
118
+
119
+ with(options: CallOptions): Caller<M> {
120
+ return {
121
+ call: <K extends keyof M & string>(method: K, ...args: Parameters<M[K]>) => this.#call(options, method, args)
122
+ }
123
+ }
124
+
125
+ // What every call comes down to. Whether the request carries a nonce is
107
126
  // not a per-call-site choice: it follows from the cache config, which is
108
127
  // the single declaration of "this answer may be reused".
109
128
  //
@@ -126,7 +145,7 @@ export class ApiClient<M extends MethodMapConstraint<M> = MethodMap> {
126
145
  // server-side response caching is built (there is none today, so nonce-less
127
146
  // buys nothing extra), or if `Request.Abort` (which cancels by nonce) gets
128
147
  // wired up on a cacheable method.
129
- async call<K extends keyof M & string>(method: K, args: Parameters<M[K]>, options?: CallOptions): Promise<Awaited<ReturnType<M[K]>>> {
148
+ async #call<K extends keyof M & string>(options: CallOptions, method: K, args: Parameters<M[K]>): Promise<Awaited<ReturnType<M[K]>>> {
130
149
  const cacheable = this.#cache?.ttlFor(method) !== false && this.#cache !== undefined
131
150
  // Refusing the cache is about this answer, not about the method: it
132
151
  // suppresses the lookup and the store, and deliberately does NOT touch
@@ -135,7 +154,7 @@ export class ApiClient<M extends MethodMapConstraint<M> = MethodMap> {
135
154
  // and keeps what nonce-lessness buys it, which is not only the Redis
136
155
  // write it saves but offline parking: a nonce-less read dropped in
137
156
  // flight waits out the outage instead of asking the user to retry it.
138
- const useCache = cacheable && options?.cache !== false
157
+ const useCache = cacheable && options.cache !== false
139
158
  if (useCache) {
140
159
  const cached = this.#cache!.get(method, args)
141
160
  if (cached.hit) {
@@ -158,16 +177,10 @@ export class ApiClient<M extends MethodMapConstraint<M> = MethodMap> {
158
177
  this.#cache?.clear()
159
178
  }
160
179
 
161
- // Seed the cache from a server push already carrying the full DTO a read
162
- // would return. `args` must match the read's args to hit the same key.
163
- primeCache(method: keyof M & string, args: unknown[], data: unknown): void {
164
- this.#cache?.prime(method, args, data)
165
- }
166
-
167
- async #run<K extends keyof M & string>(nonce: boolean, store: boolean, method: K, args: Parameters<M[K]>, options?: CallOptions): Promise<Awaited<ReturnType<M[K]>>> {
180
+ async #run<K extends keyof M & string>(nonce: boolean, store: boolean, method: K, args: Parameters<M[K]>, options: CallOptions): Promise<Awaited<ReturnType<M[K]>>> {
168
181
  const send = async (): Promise<Awaited<ReturnType<M[K]>>> => {
169
182
  const result = await this.transport.send(packRequest(method, args, nonce, this.#locale?.()))
170
- if (options?.validate) {
183
+ if (options.validate) {
171
184
  const verdict = options.validate(result)
172
185
  if (verdict instanceof Error) {
173
186
  throw verdict
@@ -186,14 +199,14 @@ export class ApiClient<M extends MethodMapConstraint<M> = MethodMap> {
186
199
  return result as Awaited<ReturnType<M[K]>>
187
200
  }
188
201
 
189
- if (options?.silent) {
202
+ if (options.silent) {
190
203
  return send()
191
204
  }
192
205
 
193
206
  const appState = useAppState()
194
207
  const exceptionState = useExceptionState()
195
208
  const connectivity = useConnectivity()
196
- const pending = options?.pending
209
+ const pending = options.pending
197
210
  const switchLoading = (on: boolean): void => {
198
211
  if (isRef(pending)) {
199
212
  pending.value = on
@@ -1,4 +1,4 @@
1
- import { ref } from 'vue'
1
+ import { computed, ref, type ComputedRef } from 'vue'
2
2
  import { defineStore } from 'pinia'
3
3
  import { createDeferred } from '@greendrake/util'
4
4
  import { getConnectionSource } from './uxConfig'
@@ -76,3 +76,14 @@ export const useConnectivity = defineStore('connectivity', () => {
76
76
  whenOnline
77
77
  }
78
78
  })
79
+
80
+ // The catalogue key of the offline message to show (this package's
81
+ // `connection` namespace). The device's own claim picks between them: "your
82
+ // connection is gone" and "the site cannot be reached" are materially
83
+ // different things to tell someone, and the device signal — useless as a
84
+ // verdict on whether calls can succeed — is the only thing that distinguishes
85
+ // them.
86
+ export const useConnectionMessageKey = (): ComputedRef<string> => {
87
+ const connectivity = useConnectivity()
88
+ return computed(() => (connectivity.deviceOffline ? 'connection.device_offline' : 'connection.unreachable'))
89
+ }
@@ -0,0 +1,68 @@
1
+ import type { WebSocketPipe, WsTransport } from '@greendrake/rpc'
2
+
3
+ // What keeping a socket connected asks of it — @greendrake/rpc's WsTransport
4
+ // as-is.
5
+ export type Revivable = Pick<WsTransport, 'probe'> & { readonly pipe: Pick<WebSocketPipe, 'reconnectNow'> }
6
+
7
+ // How often a visible page asks whether its socket is still alive.
8
+ const BEAT_MS = 60_000
9
+
10
+ // Keeps the socket carrying the app's calls connected for as long as the page
11
+ // lives; returns the teardown.
12
+ //
13
+ // The device coming back is a hint, not a verdict — it only collapses a
14
+ // waiting reconnect's remaining delay so the socket re-attempts at once
15
+ // instead of sitting out a timer that started before connectivity returned.
16
+ // A device that reports wrongly (a WKWebView, a captive portal) costs nothing
17
+ // here: the attempt either opens the socket or it does not.
18
+ //
19
+ // A socket can also die without saying so: a backgrounded mobile app, an idle
20
+ // NAT or VPN mapping expiring under a tab that never lost focus. Nothing fires
21
+ // then — no close, no online, no visibility change — so the only way to learn
22
+ // it is to ask, with `method`, the cheapest question the API answers. Silence
23
+ // past the transport's deadline abandons the socket and reconnects, which puts
24
+ // everything hanging off the reconnect back in charge.
25
+ //
26
+ // Asked at the two moments it can matter: when the page comes back to the
27
+ // user, and — because a visible tab can be sitting on a dead socket just as
28
+ // easily — on a slow beat while it stays visible. Nothing runs while hidden: a
29
+ // hidden tab has no reader to be stale for, and its timers are throttled
30
+ // anyway.
31
+ //
32
+ // `method` is typed against the app's method map (`keepConnected<Methods>`)
33
+ // rather than taken as a bare string: a probe reads any answer as proof of
34
+ // life, a refusal included, so a method the server does not have would report
35
+ // every socket alive forever. This way a rename is a compile error.
36
+ export const keepConnected = <M>(transport: Revivable, method: keyof M & string): (() => void) => {
37
+ let beat: ReturnType<typeof setInterval> | undefined
38
+
39
+ const reconnect = (): void => transport.pipe.reconnectNow()
40
+
41
+ const probe = (): void => {
42
+ void transport.probe(method)
43
+ }
44
+
45
+ const onVisibility = (): void => {
46
+ clearInterval(beat)
47
+ beat = undefined
48
+ if (document.visibilityState !== 'visible') {
49
+ return
50
+ }
51
+ // A socket that DID close while hidden is waiting out a backoff whose
52
+ // timer was throttled with the tab; collapse it rather than make the
53
+ // reader wait.
54
+ reconnect()
55
+ probe()
56
+ beat = setInterval(probe, BEAT_MS)
57
+ }
58
+
59
+ addEventListener('online', reconnect)
60
+ addEventListener('visibilitychange', onVisibility)
61
+ onVisibility()
62
+
63
+ return () => {
64
+ removeEventListener('online', reconnect)
65
+ removeEventListener('visibilitychange', onVisibility)
66
+ clearInterval(beat)
67
+ }
68
+ }
package/src/live.ts CHANGED
@@ -10,7 +10,7 @@ import { getErrorSink } from './uxConfig'
10
10
  // is the option that matters: it bypasses the loading counter AND the
11
11
  // error-UX pipeline, whereas `pending: false` suppresses only the spinner and
12
12
  // would still put the blocking retry toast over a screen the user is reading.
13
- export const backgroundCall = (background: boolean): CallOptions | undefined => (background ? { silent: true } : undefined)
13
+ export const backgroundCall = (background: boolean): CallOptions => (background ? { silent: true } : {})
14
14
 
15
15
  // @greendrake/rpc's liveBinding under this package's UX policy: a resync runs
16
16
  // unseen, and its failure neither interrupts the user nor vanishes.
package/src/main.ts CHANGED
@@ -1,7 +1,8 @@
1
- export { ApiClient, UnacceptableResultError, type ApiClientOptions, type CallOptions } from './ApiClient'
1
+ export { ApiClient, UnacceptableResultError, type ApiCaller, type ApiClientOptions, type CallOptions } from './ApiClient'
2
2
  export { useExceptionState, classifyException, type ExceptionKind, type FailOptions } from './exceptionState'
3
3
  export { configureApiUX, getGoHome, type ConnectionSource, type ErrorSink } from './uxConfig'
4
4
  export { apiUXHooks } from './apiUXHooks'
5
- export { useConnectivity } from './connectivity'
5
+ export { useConnectivity, useConnectionMessageKey } from './connectivity'
6
6
  export { retryWhenOnline } from './retryWhenOnline'
7
+ export { keepConnected, type Revivable } from './keepConnected'
7
8
  export { backgroundCall, bindLive, useLive } from './live'