sailkick-boat 0.14.4 → 0.14.5
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/README.md +13 -4
- package/lib/proxy/index.js +21 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,10 +63,19 @@ comes from the cloud **announcing bakes**, not from a clock:
|
|
|
63
63
|
`X-Sailkick-Cache` reports `HIT` / `MISS` / `UPDATED` / `STALE` / `LIVE` per response.
|
|
64
64
|
|
|
65
65
|
**Static vs dynamic — two strategies.** Tiles and app assets are **cache-first**
|
|
66
|
-
(pinned, offline forever). Dynamic `/api/*` data (AIS, weather) is
|
|
67
|
-
fetched live every time online (so
|
|
68
|
-
response kept only as an **offline fallback** (`STALE`). Local `/api/history` and
|
|
69
|
-
patched `/api/config` are served specially (above).
|
|
66
|
+
(pinned, offline forever). Dynamic `/api/*` data (AIS, weather, lightning) is
|
|
67
|
+
**network-first**: fetched live every time online (so it never goes stale), with the
|
|
68
|
+
last response kept only as an **offline fallback** (`STALE`). Local `/api/history` and
|
|
69
|
+
the patched `/api/config` are served specially (above).
|
|
70
|
+
|
|
71
|
+
**Exception — velocity tiles are pinned.** The app serves its wind/current field from
|
|
72
|
+
`/api/velocity/tiles/<layer>/<runId>/<z>/<x>/<y>/<hour>.f32`. The forecast **run id is
|
|
73
|
+
in the path**, so a URL's bytes never change — a new run means new URLs. Those are
|
|
74
|
+
cache-first like map tiles: each tile downloads once instead of on every pan, and the
|
|
75
|
+
wind particles + storm field keep rendering **offline**. The velocity *manifest*
|
|
76
|
+
(`?run=latest`) stays network-first, or the boat would pin itself to a stale run
|
|
77
|
+
forever. Nothing prefetches velocity tiles yet, so offline wind covers only what you
|
|
78
|
+
have panned over, and old runs are not pruned.
|
|
70
79
|
|
|
71
80
|
**Offline circuit breaker.** Some boat routers return errors (or hang) for outbound
|
|
72
81
|
requests when the uplink is down, instead of failing cleanly. Once a fetch fails, the
|
package/lib/proxy/index.js
CHANGED
|
@@ -18,6 +18,20 @@ const { countBboxTiles, bboxTiles, boxAround } = require('./tiles')
|
|
|
18
18
|
// get its live data from the boat's local SignalK and its charts/weather cached.
|
|
19
19
|
// Also exposes the auth'd /plugins/sailkick-boat/p/* router handlers.
|
|
20
20
|
|
|
21
|
+
// Everything under /api/ is dynamic and network-first — EXCEPT paths whose URL is
|
|
22
|
+
// immutable by construction. Velocity tiles embed the forecast run id
|
|
23
|
+
// (/api/velocity/tiles/<layer>/<runId>/<z>/<x>/<y>/<hour>.f32, see the app's
|
|
24
|
+
// public/engine/velocity-client.js), so a given URL's bytes never change: a new
|
|
25
|
+
// forecast run produces new URLs. Treating them as network-first re-downloaded the
|
|
26
|
+
// whole wind field on every pan while online, and left nothing usable offline —
|
|
27
|
+
// pinning them is what makes wind particles and the storm field work with no uplink.
|
|
28
|
+
//
|
|
29
|
+
// The velocity *manifest* is deliberately NOT in here: it resolves `run=latest` and
|
|
30
|
+
// must stay live, otherwise the boat pins itself to a stale forecast run forever.
|
|
31
|
+
const IMMUTABLE_API_PREFIXES = ['/api/velocity/tiles/']
|
|
32
|
+
const isImmutableApi = (p) => IMMUTABLE_API_PREFIXES.some((x) => p.startsWith(x))
|
|
33
|
+
const isNetworkFirst = (p) => p.startsWith('/api/') && !isImmutableApi(p)
|
|
34
|
+
|
|
21
35
|
function createProxy (app, options) {
|
|
22
36
|
const log = (m) => (app.debug ? app.debug('[proxy] ' + m) : console.log('[sailkick-boat:proxy]', m))
|
|
23
37
|
let cfg = null
|
|
@@ -138,8 +152,10 @@ function createProxy (app, options) {
|
|
|
138
152
|
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
139
153
|
const { buffer, contentType, cacheState } = await getResource({
|
|
140
154
|
storeDir: cfg.storeDir, upstream: cfg.upstream, reqPath: req.url, timeoutMs: cfg.timeoutMs,
|
|
141
|
-
|
|
142
|
-
|
|
155
|
+
// An immutable URL needs no invalidation: a bake announcement can't change
|
|
156
|
+
// bytes that are keyed by run id, so don't let an app rebuild churn them.
|
|
157
|
+
invalidatedAt: isImmutableApi(req.url) ? 0 : (manifest ? manifest.invalidatedAtFor(req.url) : 0),
|
|
158
|
+
networkFirst: isNetworkFirst(req.url), // live data (AIS, weather, lightning): fresh online, cache-fallback offline
|
|
143
159
|
health
|
|
144
160
|
})
|
|
145
161
|
res.setHeader('Content-Type', contentType)
|
|
@@ -242,8 +258,8 @@ function createProxy (app, options) {
|
|
|
242
258
|
try {
|
|
243
259
|
const { buffer, contentType, cacheState } = await getResource({
|
|
244
260
|
storeDir: cfg.storeDir, upstream: cfg.upstream, reqPath, timeoutMs: cfg.timeoutMs,
|
|
245
|
-
invalidatedAt: manifest ? manifest.invalidatedAtFor(reqPath) : 0,
|
|
246
|
-
networkFirst: reqPath
|
|
261
|
+
invalidatedAt: isImmutableApi(reqPath) ? 0 : (manifest ? manifest.invalidatedAtFor(reqPath) : 0),
|
|
262
|
+
networkFirst: isNetworkFirst(reqPath), health
|
|
247
263
|
})
|
|
248
264
|
res.set('Content-Type', contentType)
|
|
249
265
|
res.set('X-Sailkick-Cache', cacheState)
|
|
@@ -390,4 +406,4 @@ function createProxy (app, options) {
|
|
|
390
406
|
return { start, stop, status, handleGet, handlePrefetch, handlePrefetchRegion, handleClear, _serveMirror: serveMirror, _serveConfig: serveConfig, _manifest: () => manifest, _seeder: () => seeder, _startAreaPrefetch: startAreaPrefetch, _area: () => area }
|
|
391
407
|
}
|
|
392
408
|
|
|
393
|
-
module.exports = { createProxy }
|
|
409
|
+
module.exports = { createProxy, isNetworkFirst, isImmutableApi }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sailkick-boat",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.5",
|
|
4
4
|
"description": "EARLY ALPHA — cloud telemetry + offline maps for sailkick boats (www.sailkick.io; register on the web, paste the write token). Gapless boat→cloud telemetry sync to InfluxDB, and a local proxy that keeps the sailkick app and its charts/maps working fully offline on board.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|