signalk-questdb 2.4.0 → 2.5.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 (34) hide show
  1. package/.coderabbit.yaml +17 -2
  2. package/.release-please-manifest.json +1 -1
  3. package/AGENTS.md +80 -0
  4. package/CLAUDE.md +1 -79
  5. package/README.md +29 -9
  6. package/dist/change-gate.d.ts +6 -6
  7. package/dist/change-gate.js +8 -6
  8. package/dist/change-gate.js.map +1 -1
  9. package/dist/config/schema.d.ts +1 -0
  10. package/dist/config/schema.js +6 -1
  11. package/dist/config/schema.js.map +1 -1
  12. package/dist/index.d.ts +4 -0
  13. package/dist/index.js +38 -13
  14. package/dist/index.js.map +1 -1
  15. package/dist/path-matcher.d.ts +5 -4
  16. package/dist/path-matcher.js +17 -10
  17. package/dist/path-matcher.js.map +1 -1
  18. package/package.json +4 -4
  19. package/public/assets/{AppPanel-C293yEHl.js → AppPanel-D_HLFa10.js} +1 -1
  20. package/public/assets/PluginConfigurationPanel-CAj6IDmY.js +13 -0
  21. package/public/assets/{_virtual_mf-localSharedImportMap___mfe_internal__signalk_questdb__mf_owner__1-BH9mY6vj.js → _virtual_mf-localSharedImportMap___mfe_internal__signalk_questdb__mf_owner__1-CTTydXPl.js} +1 -1
  22. package/public/assets/{_virtual_mf___mfe_internal__signalk_questdb__mf_owner__97224418320974__loadShare__shared-H1lSL0nN.js → _virtual_mf___mfe_internal__signalk_questdb__mf_owner__97224418320974__loadShare__shared-BgRwx7LZ.js} +1 -1
  23. package/public/assets/{dist-BhVCbinB.js → dist-B4My1YVh.js} +1 -1
  24. package/public/assets/helpers-PTLizuYm.js +1 -0
  25. package/public/assets/hostInit-Dvn0aCSd.js +1 -0
  26. package/public/assets/{index-QJC86Uvi.js → index-j5iNctbW.js} +2 -2
  27. package/public/assets/pendingShares-BHVf8sM9.js +2 -0
  28. package/public/assets/{utils-D62kKhzq.js → utils-BesU7hIa.js} +1 -1
  29. package/public/assets/{vite-preload-helper-CRzLMJPS.js → vite-preload-helper-DclECHGd.js} +1 -1
  30. package/public/remoteEntry.js +2 -2
  31. package/public/assets/PluginConfigurationPanel-7qfy8fIR.js +0 -11
  32. package/public/assets/helpers-CvnX61Ju.js +0 -1
  33. package/public/assets/hostInit-Ds8KD_fS.js +0 -1
  34. package/public/assets/pendingShares-DemvETkM.js +0 -2
package/.coderabbit.yaml CHANGED
@@ -136,7 +136,7 @@ reviews:
136
136
  must narrow `unknown` and avoid `any`.
137
137
  - The configpanel is TypeScript too, with its own `tsconfig.json`
138
138
  (browser libs, `noEmit`), checked by `npm run typecheck:panel`.
139
- - Node `>= 22` per `engines` (uses Node's built-in test runner).
139
+ - Node `>= 24` per `engines` (uses Node's built-in test runner).
140
140
 
141
141
  ## Tests
142
142
 
@@ -145,7 +145,13 @@ reviews:
145
145
  (`npm run build:all`). Flag changes that add tests but don't
146
146
  match the existing pattern.
147
147
 
148
- ## Echo comments
148
+ ## Comments
149
+
150
+ Code explains itself: names, types and small functions say what
151
+ it does. Do not ask for docstrings or JSDoc, and do not flag a
152
+ function for lacking one. A comment is there for the *why* the
153
+ code cannot show: a constraint, a trap, a choice against the
154
+ obvious alternative.
149
155
 
150
156
  Flag any comment that merely restates what the code already says.
151
157
  Examples to flag:
@@ -222,3 +228,12 @@ reviews:
222
228
  `ContainerResourceLimits` (memory + cpus) — the defaults of
223
229
  `768m` / `1.5 cpus` are deliberate for the typical Signal K
224
230
  host stack (questdb + grafana + mayara + backup co-resident).
231
+
232
+ # Docstrings are not wanted for their own sake; see "Comments" above.
233
+ finishing_touches:
234
+ docstrings:
235
+ enabled: false
236
+
237
+ pre_merge_checks:
238
+ docstrings:
239
+ mode: "off"
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "2.4.0"
2
+ ".": "2.5.0"
3
3
  }
package/AGENTS.md ADDED
@@ -0,0 +1,80 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for AI coding agents working in this repository. `CLAUDE.md` imports it.
4
+
5
+ ## Commands
6
+
7
+ - `npm run build` — TypeScript compile (`tsc`) + panel typecheck + Vite bundle of the React config panel into `public/`
8
+ - `npm run build:config` — Vite only (rebuild the config panel without recompiling TS)
9
+ - `npm run typecheck:panel` — typecheck the config panel (`tsc -p src/configpanel --noEmit`); Vite only strips its types, so this is the only thing that checks them
10
+ - `npm run watch` — `tsc --watch` for the plugin source
11
+ - `npm test` — runs the Node built-in test runner against compiled output (`node --test 'dist/test/**/*.test.js'`). Tests must be built first; use `npm run build:all` to build then test.
12
+ - `npm run format` — prettier write + eslint --fix
13
+ - `npm run ci-lint` — eslint + prettier --check (used in CI)
14
+ - Run a single test file: `node --test dist/test/history-v2.test.js` (after `npm run build`)
15
+
16
+ Node ≥ 24 is required (uses the built-in test runner).
17
+
18
+ ## Architecture
19
+
20
+ This is a Signal K **server plugin** that ingests vessel data into a managed **QuestDB** container and serves it back via Signal K's history APIs. It is intended as a drop-in replacement for `signalk-to-influxdb`.
21
+
22
+ ### Two surfaces
23
+
24
+ 1. **Plugin runtime** (`src/index.ts` and siblings) — compiled by `tsc` to `dist/`, loaded by Signal K server as `main` (`dist/index.js`). The package is **ESM** (`"type": "module"`): `src/index.ts` ends in `export default`, and Signal K's `importOrRequire()` unwraps it. Node ≥ 20.19 can `require()` an ESM module, which every Node version the plugin supports does.
25
+ 2. **React config panel** (`src/configpanel/`) — TypeScript + JSX, bundled by **Vite** (`vite.config.ts`) via **Module Federation** into `public/remoteEntry.js`, exposed to the Signal K Admin UI as `./PluginConfigurationPanel`. React 19 is shared as a singleton with the host UI. It has its own `tsconfig.json` (browser libs, classic JSX, `noEmit`) so DOM types never reach the Node compile.
26
+
27
+ Because the package is `"type": "module"`, the server injects the panel's script tag with `type="module"`, and the Admin UI then loads it as an **ESM remote**: `await import(remoteEntry.js)` expecting exported `get`/`init` (see the admin UI's `views/Webapps/dynamicutilities.ts`). Vite's federation output provides exactly that.
28
+
29
+ Both build outputs are shipped in the npm package; `prepublishOnly` rebuilds them.
30
+
31
+ ### Data flow
32
+
33
+ - Subscribes to the Signal K **streambundle** for all paths and writes filtered/throttled deltas to QuestDB via **ILP over raw TCP** (`src/ilp-writer.ts`) — no QuestDB client library.
34
+ - Three tables (`signalk`, `signalk_str`, `signalk_position`) defined and queried in `src/query-client.ts`. All use WAL mode + daily partitioning + dedup. The archives `signalk_archive` and `signalk_position_archive` are defined there too, with their base tables' columns and dedup keys and monthly partitions.
35
+ - Reads come from two registered providers:
36
+ - **v2** (`src/history-v2.ts`) — `app.registerHistoryApiProvider()`, REST under `/signalk/v2/api/history/`. Aggregates map directly to QuestDB SQL except `sma`/`ema` (computed client-side). Where the archive covers the range, each table is sampled on its own and the buckets merged in the plugin.
37
+ - **v1** (`src/history-v1.ts`) — `app.registerHistoryProvider()`, WebSocket playback with chunked reads.
38
+ - Retention (`src/retention.ts`) drops old daily partitions on a timer. Before a partition of `signalk_position` or `signalk` goes, the own vessel's rows the config keeps are copied, thinned, into `signalk_position_archive` / `signalk_archive` (`src/archive.ts`), and the drop waits for that copy to be applied. Reads add an archive table only for a range it covers (`ArchiveSpans`), so a recent window stays on the base table alone. v1 snapshots read it as one more per-table `LATEST ON` branch, and v2's sampled and raw reads of the own vessel read each table on its own and merge the buckets or rows in the plugin. Their union (`readFrom` in `src/archive.ts`) sorts every row in range, so only reads that need one order across both tables, such as `middle_index`, or that a short window bounds still use it.
39
+ - GPX import (`src/gpx-import.ts`, `src/gpx-import-session.ts`) streams a sliced upload through saxes into `signalk_position_archive` under source `import.gpx`, thinned by stamping points on interval starts and letting dedup keep one per interval. The caller names the tracks to import: an export mixes the own track with AIS targets and other boats' passages, and nothing in GPX tells them apart.
40
+ - Startup restore (`src/restore.ts`) optionally replays each vessel's last recorded position back into the live model after a server restart.
41
+ - Console proxy (`src/console-proxy.ts`) reverse-proxies QuestDB's own web console so the embeddable webapp (`public/index.html`) can host it in the admin UI.
42
+
43
+ ### Container integration
44
+
45
+ The plugin **does not run QuestDB itself**. It names `signalk-container` in `package.json`'s `signalk.requires`, with no npm dependency on it, and looks up that plugin's API at runtime (`ContainerManagerApi` in `src/index.ts`). When `managedContainer` is true, it calls `ensureRunning` / `ensureNetwork` / `pullImage` / `stop` to control a QuestDB container on the shared `sk-network` Podman/Docker network. The plugin lifecycle (`start`/`stop`) drives container lifecycle.
46
+
47
+ When `managedContainer` is false, the plugin connects to an external QuestDB at the configured host/ports.
48
+
49
+ ### Config schema
50
+
51
+ `src/config/schema.ts` uses **TypeBox** — the schema is the single source of truth for the JSON-schema Signal K shows in the Admin UI **and** for the `Config` TS type. Add new options there.
52
+
53
+ The package is the unscoped `typebox` (1.x), not `@sinclair/typebox` (which stopped at 0.34). TypeBox 1 was published under a new name rather than a major bump, so `npm outdated` will never point at it.
54
+
55
+ ### REST endpoints
56
+
57
+ All extra plugin endpoints live under `/plugins/signalk-questdb/api/` and are wired in `src/index.ts` via the `IRouter` Signal K passes to `registerWithRouter`. The `/query` endpoint is gated by `isReadOnlySQL` in `src/query-client.ts` — DDL/DML must remain blocked.
58
+
59
+ ⚠️ **`/console` is deliberately NOT gated that way.** It proxies QuestDB's own console, which is a full SQL client and can drop tables. It is safe only because signalk-server gives plugin routes an **admin-only default**, and routes are downgraded to readwrite/readonly _only_ by registering them through `router.access(...)`. Never wrap the console mount in `.access()` — doing so hands non-admins the ability to delete recorded history. The mount is also gated on the `enableConsole` config flag, so turning it off removes the route rather than leaving it to refuse.
60
+
61
+ Their wire shapes live in `src/api-contract.ts` and are shared by both surfaces: handlers assert responses with `satisfies`, and the panel casts fetch results to the same types. Add or rename a response field there, not inline — that is what stops the server and panel drifting apart silently.
62
+
63
+ ### Tests
64
+
65
+ `src/test/` contains `node:test` suites for ILP encoding, time range parsing, query building, and v2 history. They run against the **compiled** JS in `dist/test/`, so always build before testing.
66
+
67
+ `src/test/history-live.test.ts` runs the v2 provider against a real QuestDB. It drops the plugin's tables, so run it only against a throwaway instance; its header says how.
68
+
69
+ ## Conventions
70
+
71
+ - TypeScript strict mode everywhere, panel included; do not loosen either `tsconfig.json`. In particular never add `jsx` or a DOM `lib` to the root one — server code referencing `document` would then typecheck clean.
72
+ - **Relative imports in the runtime need the `.js` extension** (`./query-client.js`, even though the source is `.ts`). That is Node's ESM rule, enforced by `moduleResolution: "nodenext"`. Omitting it fails the build, which is the point — switching to `"bundler"` resolution would silently emit unresolvable specifiers instead.
73
+ - **`.npmignore` excludes config files by exact filename** — rename one and the stale entry silently publishes it. `eslint.config.ts` is loaded through `jiti` (eslint 10 needs it for TypeScript configs); it is a devDependency and never ships.
74
+ - **The three ignore files serve opposite purposes for build output.** `public/assets/` is gitignored and prettier-ignored, but must NEVER be added to `.npmignore`: `remoteEntry.js` imports those chunks by name, so excluding them publishes a panel that 404s on load. Only genuinely local artifacts (`.mf/`) belong in all three.
75
+ - Prettier + eslint flat config (`eslint.config.ts`); run `npm run format` before committing.
76
+ - The config panel uses React 19 with Module Federation — keep the federation `shared` block in sync with `package.json`'s React version.
77
+ - **Classic JSX is load-bearing.** `jsxRuntime: "classic"` (vite.config.ts) and `"jsx": "react"` (panel tsconfig) must agree. The automatic runtime imports `react/jsx-runtime`, which is not in the federation `shared` scope, so the remote would carry its own copy of React's jsx runtime — a second React instance whose dispatcher is not the host's, so `useState` reads null and the panel fails to mount. At runtime, with no build error.
78
+ - **Imports leaving `src/configpanel/` must be `import type`.** A value import pulls the module's runtime dependencies into the browser bundle (typebox via `config/schema`, `fs/promises` via `host-limits`, saxes via `gpx-import`). After changing panel imports, confirm with `grep -rl "typebox\|fs/promises\|saxes" public/` — it must print nothing.
79
+ - `signalk.appIcon` and `signalk.displayName` in `package.json` control how the plugin appears in the Admin UI.
80
+ - **Code explains itself.** Names, types and small functions say what the code does; a comment says why — a constraint, a trap, a choice against the obvious alternative. Don't write docstrings for their own sake or comments that restate the code. CodeRabbit's docstring check is off for the same reason.
package/CLAUDE.md CHANGED
@@ -1,79 +1 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Commands
6
-
7
- - `npm run build` — TypeScript compile (`tsc`) + panel typecheck + Vite bundle of the React config panel into `public/`
8
- - `npm run build:config` — Vite only (rebuild the config panel without recompiling TS)
9
- - `npm run typecheck:panel` — typecheck the config panel (`tsc -p src/configpanel --noEmit`); Vite only strips its types, so this is the only thing that checks them
10
- - `npm run watch` — `tsc --watch` for the plugin source
11
- - `npm test` — runs the Node built-in test runner against compiled output (`node --test 'dist/test/**/*.test.js'`). Tests must be built first; use `npm run build:all` to build then test.
12
- - `npm run format` — prettier write + eslint --fix
13
- - `npm run ci-lint` — eslint + prettier --check (used in CI)
14
- - Run a single test file: `node --test dist/test/history-v2.test.js` (after `npm run build`)
15
-
16
- Node ≥ 22 is required (uses the built-in test runner).
17
-
18
- ## Architecture
19
-
20
- This is a Signal K **server plugin** that ingests vessel data into a managed **QuestDB** container and serves it back via Signal K's history APIs. It is intended as a drop-in replacement for `signalk-to-influxdb`.
21
-
22
- ### Two surfaces
23
-
24
- 1. **Plugin runtime** (`src/index.ts` and siblings) — compiled by `tsc` to `dist/`, loaded by Signal K server as `main` (`dist/index.js`). The package is **ESM** (`"type": "module"`): `src/index.ts` ends in `export default`, and Signal K's `importOrRequire()` unwraps it. Node ≥ 20.19 can `require()` an ESM module, so this works on both supported Node versions.
25
- 2. **React config panel** (`src/configpanel/`) — TypeScript + JSX, bundled by **Vite** (`vite.config.ts`) via **Module Federation** into `public/remoteEntry.js`, exposed to the Signal K Admin UI as `./PluginConfigurationPanel`. React 19 is shared as a singleton with the host UI. It has its own `tsconfig.json` (browser libs, classic JSX, `noEmit`) so DOM types never reach the Node compile.
26
-
27
- Because the package is `"type": "module"`, the server injects the panel's script tag with `type="module"`, and the Admin UI then loads it as an **ESM remote**: `await import(remoteEntry.js)` expecting exported `get`/`init` (see the admin UI's `views/Webapps/dynamicutilities.ts`). Vite's federation output provides exactly that.
28
-
29
- Both build outputs are shipped in the npm package; `prepublishOnly` rebuilds them.
30
-
31
- ### Data flow
32
-
33
- - Subscribes to the Signal K **streambundle** for all paths and writes filtered/throttled deltas to QuestDB via **ILP over raw TCP** (`src/ilp-writer.ts`) — no QuestDB client library.
34
- - Three tables (`signalk`, `signalk_str`, `signalk_position`) defined and queried in `src/query-client.ts`. All use WAL mode + daily partitioning + dedup. The archives `signalk_archive` and `signalk_position_archive` are defined there too, with their base tables' columns and dedup keys and monthly partitions.
35
- - Reads come from two registered providers:
36
- - **v2** (`src/history-v2.ts`) — `app.registerHistoryApiProvider()`, REST under `/signalk/v2/api/history/`. Aggregates map directly to QuestDB SQL except `sma`/`ema` (computed client-side). Where the archive covers the range, each table is sampled on its own and the buckets merged in the plugin.
37
- - **v1** (`src/history-v1.ts`) — `app.registerHistoryProvider()`, WebSocket playback with chunked reads.
38
- - Retention (`src/retention.ts`) drops old daily partitions on a timer. Before a partition of `signalk_position` or `signalk` goes, the own vessel's rows the config keeps are copied, thinned, into `signalk_position_archive` / `signalk_archive` (`src/archive.ts`), and the drop waits for that copy to be applied. Reads add an archive table only for a range it covers (`ArchiveSpans`), so a recent window stays on the base table alone. v1 snapshots read it as one more per-table `LATEST ON` branch, and v2's sampled and raw reads of the own vessel read each table on its own and merge the buckets or rows in the plugin. Their union (`readFrom` in `src/archive.ts`) sorts every row in range, so only reads that need one order across both tables, such as `middle_index`, or that a short window bounds still use it.
39
- - GPX import (`src/gpx-import.ts`, `src/gpx-import-session.ts`) streams a sliced upload through saxes into `signalk_position_archive` under source `import.gpx`, thinned by stamping points on interval starts and letting dedup keep one per interval. The caller names the tracks to import: an export mixes the own track with AIS targets and other boats' passages, and nothing in GPX tells them apart.
40
- - Startup restore (`src/restore.ts`) optionally replays each vessel's last recorded position back into the live model after a server restart.
41
- - Console proxy (`src/console-proxy.ts`) reverse-proxies QuestDB's own web console so the embeddable webapp (`public/index.html`) can host it in the admin UI.
42
-
43
- ### Container integration
44
-
45
- The plugin **does not run QuestDB itself**. It declares an optional peer dep on `signalk-container` and looks up that plugin's API at runtime (`ContainerManagerApi` in `src/index.ts`). When `managedContainer` is true, it calls `ensureRunning` / `ensureNetwork` / `pullImage` / `stop` to control a QuestDB container on the shared `sk-network` Podman/Docker network. The plugin lifecycle (`start`/`stop`) drives container lifecycle.
46
-
47
- When `managedContainer` is false, the plugin connects to an external QuestDB at the configured host/ports.
48
-
49
- ### Config schema
50
-
51
- `src/config/schema.ts` uses **TypeBox** — the schema is the single source of truth for the JSON-schema Signal K shows in the Admin UI **and** for the `Config` TS type. Add new options there.
52
-
53
- The package is the unscoped `typebox` (1.x), not `@sinclair/typebox` (which stopped at 0.34). TypeBox 1 was published under a new name rather than a major bump, so `npm outdated` will never point at it.
54
-
55
- ### REST endpoints
56
-
57
- All extra plugin endpoints live under `/plugins/signalk-questdb/api/` and are wired in `src/index.ts` via the `IRouter` Signal K passes to `registerWithRouter`. The `/query` endpoint is gated by `isReadOnlySQL` in `src/query-client.ts` — DDL/DML must remain blocked.
58
-
59
- ⚠️ **`/console` is deliberately NOT gated that way.** It proxies QuestDB's own console, which is a full SQL client and can drop tables. It is safe only because signalk-server gives plugin routes an **admin-only default**, and routes are downgraded to readwrite/readonly _only_ by registering them through `router.access(...)`. Never wrap the console mount in `.access()` — doing so hands non-admins the ability to delete recorded history. The mount is also gated on the `enableConsole` config flag, so turning it off removes the route rather than leaving it to refuse.
60
-
61
- Their wire shapes live in `src/api-contract.ts` and are shared by both surfaces: handlers assert responses with `satisfies`, and the panel casts fetch results to the same types. Add or rename a response field there, not inline — that is what stops the server and panel drifting apart silently.
62
-
63
- ### Tests
64
-
65
- `src/test/` contains `node:test` suites for ILP encoding, time range parsing, query building, and v2 history. They run against the **compiled** JS in `dist/test/`, so always build before testing.
66
-
67
- `src/test/history-live.test.ts` runs the v2 provider against a real QuestDB. It drops the plugin's tables, so run it only against a throwaway instance; its header says how.
68
-
69
- ## Conventions
70
-
71
- - TypeScript strict mode everywhere, panel included; do not loosen either `tsconfig.json`. In particular never add `jsx` or a DOM `lib` to the root one — server code referencing `document` would then typecheck clean.
72
- - **Relative imports in the runtime need the `.js` extension** (`./query-client.js`, even though the source is `.ts`). That is Node's ESM rule, enforced by `moduleResolution: "nodenext"`. Omitting it fails the build, which is the point — switching to `"bundler"` resolution would silently emit unresolvable specifiers instead.
73
- - **`.npmignore` excludes config files by exact filename** — rename one and the stale entry silently publishes it. `eslint.config.ts` is loaded through `jiti` (eslint 10 needs it for TypeScript configs); it is a devDependency and never ships.
74
- - **The three ignore files serve opposite purposes for build output.** `public/assets/` is gitignored and prettier-ignored, but must NEVER be added to `.npmignore`: `remoteEntry.js` imports those chunks by name, so excluding them publishes a panel that 404s on load. Only genuinely local artifacts (`.mf/`) belong in all three.
75
- - Prettier + eslint flat config (`eslint.config.ts`); run `npm run format` before committing.
76
- - The config panel uses React 19 with Module Federation — keep the federation `shared` block in sync with `package.json`'s React version.
77
- - **Classic JSX is load-bearing.** `jsxRuntime: "classic"` (vite.config.ts) and `"jsx": "react"` (panel tsconfig) must agree. The automatic runtime imports `react/jsx-runtime`, which is not in the federation `shared` scope, so the remote would carry its own copy of React's jsx runtime — a second React instance whose dispatcher is not the host's, so `useState` reads null and the panel fails to mount. At runtime, with no build error.
78
- - **Imports leaving `src/configpanel/` must be `import type`.** A value import pulls the module's runtime dependencies into the browser bundle (typebox via `config/schema`, `fs/promises` via `host-limits`, saxes via `gpx-import`). After changing panel imports, confirm with `grep -rl "typebox\|fs/promises\|saxes" public/` — it must print nothing.
79
- - `signalk.appIcon` and `signalk.displayName` in `package.json` control how the plugin appears in the Admin UI.
1
+ @AGENTS.md
package/README.md CHANGED
@@ -43,7 +43,7 @@ InfluxDB history can be imported.
43
43
 
44
44
  ### Requirements
45
45
 
46
- - Node.js 22 or newer
46
+ - Node.js 24 or newer
47
47
  - QuestDB 8.1 or newer (resolution queries use `SAMPLE BY … FROM … TO`). This
48
48
  applies to an external QuestDB and to a pinned **QuestDB image version** in
49
49
  managed mode; `latest` always qualifies
@@ -112,11 +112,12 @@ rootless Podman.
112
112
  | QuestDB memory limit | `768m` | Hard cgroup cap on the container (empty = unlimited); the JVM heap gets half of it, so heap plus off-heap stays inside |
113
113
  | QuestDB CPU limit (cores) | `1.5` | Fractional cores (0 = unlimited) |
114
114
  | Default sampling rate (ms) | `2000` | Minimum ms between writes for any one path (0 = every update) |
115
- | Per-path sampling rates (ms) | _(empty)_ | Glob → ms overrides, e.g. `{ "environment.wind.*": 200, "tanks.*": 10000 }` |
115
+ | Per-path sampling rates (ms) | _(empty)_ | Glob → ms overrides, one `pattern ms` per line in the panel (0 = wire speed, see Sampling) |
116
116
  | Unchanged-value heartbeat (s) | `300` | A value equal to the last one recorded is written again only this often; changes are written at the sampling rate (0 = every sample, see Change-only recording) |
117
117
  | Write batch interval (ms) | `5000` | How often buffered samples are committed — one WAL transaction per table per commit (see Performance) |
118
118
  | Record own vessel | `true` | Record the `self` context |
119
119
  | Record AIS targets | `true` | Record other vessels |
120
+ | Record all sources | `false` | Record every source of a path, not only the one the source priorities prefer (see Source priorities) |
120
121
  | Restore vessels on startup | `false` | Replay each vessel's last recorded position after a restart (see Startup restore) |
121
122
  | Restore max age (minutes) | `9` | Only replay values recorded within this window |
122
123
  | Allow `sourcePolicy=all` | `false` | Let History API callers split a path into one column per recording source (see History API) |
@@ -236,9 +237,27 @@ canboat WASM decoder do not produce the marker at all.
236
237
 
237
238
  **Sampling.** Each path is written at most once per **Default sampling rate**
238
239
  (2000 ms), with per-path overrides by glob for the few paths that need finer
239
- resolution and the many that need less. **Record own vessel** and **Record AIS
240
- targets** switch the two kinds of context independently, and **Path patterns**
241
- exclude (or include only) paths by glob.
240
+ resolution and the many that need less. The first value after a window opens
241
+ is written and the rest of the window is dropped, not averaged, so a gust
242
+ between two samples is not in the history. A rate of 0, as default or per
243
+ path, removes the window: `environment.wind.* 0` records every change of the
244
+ wind at wire speed while everything else stays at the default. An unchanged
245
+ value is still written only once per **Unchanged-value heartbeat** (see
246
+ Change-only recording), except `navigation.position`, which is written even
247
+ when unchanged; set the heartbeat to 0 as well to record every update. Wire
248
+ speed costs rows: a wind sensor at 10 Hz writes about 1.7 million a day for
249
+ angle and speed, and every path of a busy NMEA 2000 boat at wire speed can be
250
+ a few hundred rows a second. **Record own vessel** and **Record AIS targets**
251
+ switch the two kinds of context independently, and **Path patterns** exclude
252
+ (or include only) paths by glob.
253
+
254
+ **Source priorities.** When Signal K's source priorities rank the sources of a
255
+ path, only the preferred source is recorded, the same value instruments show.
256
+ Paths without a priority are recorded from every source. **Record all sources**
257
+ records the sources a priority hides as well, for calibrating a sensor or
258
+ comparing two GPS receivers. Each source is then sampled on its own, so a path
259
+ with three sources writes up to three times the rows; with the option off, the
260
+ sources of a path share one sampling window.
242
261
 
243
262
  **Change-only recording.** Most paths on a boat do not change from one sample
244
263
  to the next: a switch that flips twice a day, an energy counter at rest, a tank
@@ -270,8 +289,9 @@ data really stopped:
270
289
  age**. Keep the heartbeat below that, or a vessel lying still may come back
271
290
  with its position but without its speed, course and heading.
272
291
 
273
- `navigation.position` is never skipped. Set the heartbeat to 0 to record every
274
- sample.
292
+ `navigation.position` is written even when unchanged. A heartbeat of 0 does the
293
+ same for every path: each sample the sampling rate lets through is written. With
294
+ a sampling rate of 0 as well, that is every update.
275
295
 
276
296
  **Batching.** Samples are buffered and committed every **Write batch interval**
277
297
  (5 s), one WAL transaction per table per commit. The buffer holds 100 000 lines;
@@ -900,8 +920,8 @@ QuestDB releases they held memory.
900
920
 
901
921
  - **Default sampling rate** of 2000 ms limits each path to one write per two
902
922
  seconds, keeping write volume modest on busy NMEA 2000 buses; per-path
903
- overrides allow faster rates for critical paths
904
- (`{ "environment.wind.*": 200 }`) while slow-changing ones stay throttled.
923
+ overrides allow faster rates, or wire speed with 0, for critical paths while
924
+ slow-changing ones stay throttled.
905
925
  - **Change-only recording** skips a value that has not changed since it was last
906
926
  written until the heartbeat is due. Paths that hold steady — which is
907
927
  most of them — cost one row per heartbeat instead of one per sampling
@@ -14,13 +14,13 @@ export declare class ChangeGate {
14
14
  /** Records that `value` was written, so later equal values are repeats. */
15
15
  wrote(path: string, context: string, source: string | undefined, value: unknown, now: number): void;
16
16
  /**
17
- * Forgets a path in a context, for every source, along with any leaves it
18
- * was flattened into. Signal K reports a path going stale as a null value;
19
- * without this, the reading that comes back after the gap would be skipped
20
- * whenever it equals the one before it, and the gap would read as a value
21
- * held straight through it.
17
+ * Forgets a path in a context, for every source or only for `source` when
18
+ * given, along with any leaves it was flattened into. Signal K reports a
19
+ * path going stale as a null value; without this, the reading that comes
20
+ * back after the gap would be skipped whenever it equals the one before
21
+ * it, and the gap would read as a value held straight through it.
22
22
  */
23
- forget(path: string, context: string): void;
23
+ forget(path: string, context: string, source?: string): void;
24
24
  clear(): void;
25
25
  private evict;
26
26
  }
@@ -68,18 +68,20 @@ export class ChangeGate {
68
68
  paths.set(k, { path, value, at: now });
69
69
  }
70
70
  /**
71
- * Forgets a path in a context, for every source, along with any leaves it
72
- * was flattened into. Signal K reports a path going stale as a null value;
73
- * without this, the reading that comes back after the gap would be skipped
74
- * whenever it equals the one before it, and the gap would read as a value
75
- * held straight through it.
71
+ * Forgets a path in a context, for every source or only for `source` when
72
+ * given, along with any leaves it was flattened into. Signal K reports a
73
+ * path going stale as a null value; without this, the reading that comes
74
+ * back after the gap would be skipped whenever it equals the one before
75
+ * it, and the gap would read as a value held straight through it.
76
76
  */
77
- forget(path, context) {
77
+ forget(path, context, source) {
78
78
  const paths = this.byContext.get(context);
79
79
  if (!paths)
80
80
  return;
81
81
  const leafPrefix = `${path}.`;
82
82
  for (const [k, entry] of paths) {
83
+ if (source !== undefined && k !== key(entry.path, source))
84
+ continue;
83
85
  if (entry.path === path || entry.path.startsWith(leafPrefix)) {
84
86
  paths.delete(k);
85
87
  this.count -= 1;
@@ -1 +1 @@
1
- {"version":3,"file":"change-gate.js","sourceRoot":"","sources":["../src/change-gate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"change-gate.js","sourceRoot":"","sources":["../src/change-gate.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,7 @@ export declare const ConfigSchema: Type.TObject<{
18
18
  unchangedHeartbeatSeconds: Type.TNumber;
19
19
  recordSelf: Type.TBoolean;
20
20
  recordOthers: Type.TBoolean;
21
+ recordAllSources: Type.TBoolean;
21
22
  restoreOnStart: Type.TBoolean;
22
23
  restoreMaxAgeMinutes: Type.TNumber;
23
24
  historySourcePolicyAll: Type.TBoolean;
@@ -65,7 +65,7 @@ export const ConfigSchema = Type.Object({
65
65
  samplingRates: Type.Record(Type.String(), Type.Number(), {
66
66
  default: {},
67
67
  title: "Per-path sampling rates (ms)",
68
- description: 'Override default rate for specific paths. e.g. { "environment.wind.*": 200, "tanks.*": 10000 }',
68
+ description: 'Override default rate for specific paths (0 = wire speed: every change, while an unchanged value still waits for the heartbeat; navigation.position is written even when unchanged, so 0 writes every position update). e.g. { "environment.wind.*": 0, "tanks.*": 10000 }',
69
69
  }),
70
70
  unchangedHeartbeatSeconds: Type.Number({
71
71
  default: 300,
@@ -81,6 +81,11 @@ export const ConfigSchema = Type.Object({
81
81
  default: true,
82
82
  title: "Record AIS targets",
83
83
  }),
84
+ recordAllSources: Type.Boolean({
85
+ default: false,
86
+ title: "Record all sources",
87
+ description: "Record every source of a path, not only the one Signal K's source priorities prefer. Off by default: priorities pick the better sensor, and its value is what history normally wants. Turn it on to keep the readings a priority hides, e.g. to calibrate a sensor or compare two GPS receivers. Each source is then sampled on its own, so a path with three sources writes up to three times the rows.",
88
+ }),
84
89
  restoreOnStart: Type.Boolean({
85
90
  default: false,
86
91
  title: "Restore vessels on startup",
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -13,6 +13,9 @@ interface App {
13
13
  getBus: (path?: string) => {
14
14
  onValue: (cb: (delta: unknown) => void) => () => void;
15
15
  };
16
+ getUnfilteredBus: (path?: string) => {
17
+ onValue: (cb: (delta: unknown) => void) => () => void;
18
+ };
16
19
  };
17
20
  registerHistoryProvider: (provider: unknown) => void;
18
21
  registerHistoryApiProvider: (provider: HistoryApi.HistoryProvider) => void;
@@ -46,6 +49,7 @@ declare const _default: (app: App) => {
46
49
  unchangedHeartbeatSeconds: import("typebox").TNumber;
47
50
  recordSelf: import("typebox").TBoolean;
48
51
  recordOthers: import("typebox").TBoolean;
52
+ recordAllSources: import("typebox").TBoolean;
49
53
  restoreOnStart: import("typebox").TBoolean;
50
54
  restoreMaxAgeMinutes: import("typebox").TNumber;
51
55
  historySourcePolicyAll: import("typebox").TBoolean;
package/dist/index.js CHANGED
@@ -556,10 +556,10 @@ export default (app) => {
556
556
  const matches = matcher.matches(path);
557
557
  return mode === "exclude" ? !matches : matches;
558
558
  }
559
- function isThrottled(path, context, defaultRate) {
559
+ function isThrottled(path, context, defaultRate, source) {
560
560
  // Per-path override wins when one matches; otherwise the default rate.
561
561
  const overrideRate = rateMatcher?.rateFor(path) ?? null;
562
- return throttle.shouldDrop(path, context, overrideRate ?? defaultRate, Date.now());
562
+ return throttle.shouldDrop(path, context, overrideRate ?? defaultRate, Date.now(), source);
563
563
  }
564
564
  const WAL_NOTIFICATION_PATH = "notifications.signalk-questdb.walSuspended";
565
565
  // Dedupe key of the last emitted notification. The monitor reports every
@@ -1193,7 +1193,14 @@ export default (app) => {
1193
1193
  // restore sees every vessel that transmitted during startup.
1194
1194
  liveContexts.clear();
1195
1195
  trackLiveContexts = config.restoreOnStart;
1196
- const bus = app.streambundle.getBus();
1196
+ // getBus() carries only the source that Signal K's source priorities
1197
+ // prefer on each path, the value instruments show. recordAllSources
1198
+ // reads the unfiltered bus instead, so a source that loses the ranking
1199
+ // is recorded as well: the raw paddlewheel next to a corrected STW
1200
+ // (issue #235).
1201
+ const bus = config.recordAllSources
1202
+ ? app.streambundle.getUnfilteredBus()
1203
+ : app.streambundle.getBus();
1197
1204
  const unsub = bus.onValue((delta) => {
1198
1205
  if (!writer)
1199
1206
  return;
@@ -1212,9 +1219,26 @@ export default (app) => {
1212
1219
  // syntax. Undefined when the delta carries none — the column stays
1213
1220
  // null, same as rows written before it existed.
1214
1221
  const source = typeof delta.$source === "string" ? delta.$source : undefined;
1222
+ // Recording every source keeps each one apart. It gets its own
1223
+ // sampling window: the shared one would keep whichever source arrives
1224
+ // first, so the fastest receiver would crowd out the ones recorded to
1225
+ // compare against it. And its nulls forget only its own last values:
1226
+ // a gateway sending "not available" several times a second would
1227
+ // otherwise make every other source's unchanged values due again at
1228
+ // each sampling window instead of each heartbeat.
1229
+ const perSource = config.recordAllSources ? source : undefined;
1230
+ // Meta updates ride the same bus as values, flagged isMeta. They
1231
+ // describe a path (units, description, displayName), they are not a
1232
+ // reading of it. Routed like a value object, their keys were stored
1233
+ // as rows such as `….units` and `….displayName`, again each time a
1234
+ // plugin republished its meta. Nor do they show that a vessel is
1235
+ // transmitting: a local plugin publishes its meta on its own start,
1236
+ // and counting that would cost the restore a vessel's saved position.
1237
+ if (delta.isMeta)
1238
+ return;
1215
1239
  // Note the context BEFORE any early return or filter: identity-only
1216
1240
  // deltas return below, and path filters and throttles decide what gets
1217
- // STORED — but any delta at all proves the vessel is transmitting now,
1241
+ // STORED — but any value at all proves the vessel is transmitting now,
1218
1242
  // which is what makes its stored position obsolete for the restore.
1219
1243
  if (trackLiveContexts) {
1220
1244
  liveContexts.add(context === app.selfContext ? "self" : context);
@@ -1257,7 +1281,7 @@ export default (app) => {
1257
1281
  // the change gate must forget the path, so the first reading after the
1258
1282
  // gap is recorded even when it equals the last one before it.
1259
1283
  if (path && value === null) {
1260
- changeGate?.forget(path, context === app.selfContext ? "self" : context);
1284
+ changeGate?.forget(path, context === app.selfContext ? "self" : context, perSource);
1261
1285
  }
1262
1286
  if (!path || value === undefined || value === null)
1263
1287
  return;
@@ -1304,13 +1328,14 @@ export default (app) => {
1304
1328
  return;
1305
1329
  // Throttled per path AND context: the sampling rate bounds each
1306
1330
  // vessel's stream, not the fleet's (issue #93). The stored context
1307
- // ("self" vs raw) is the key, matching what the rows carry. The key
1308
- // deliberately does NOT include source: the sampling rate bounds the
1309
- // per-path row volume, and keying per source would multiply it by the
1310
- // number of receivers. With two sources alternating, each window
1311
- // keeps whichever arrived first — a source-filtered read then sees
1312
- // roughly every other sample, which the rate already allows for.
1313
- if (isThrottled(path, isSelf ? "self" : context, config.defaultSamplingRate ?? 2000))
1331
+ // ("self" vs raw) is the key, matching what the rows carry. Unless
1332
+ // recordAllSources asks for every source, the key deliberately does
1333
+ // NOT include source: the sampling rate bounds the per-path row
1334
+ // volume, and keying per source would multiply it by the number of
1335
+ // receivers. With two sources alternating, each window keeps
1336
+ // whichever arrived first — a source-filtered read then sees roughly
1337
+ // every other sample, which the rate already allows for.
1338
+ if (isThrottled(path, isSelf ? "self" : context, config.defaultSamplingRate ?? 2000, perSource))
1314
1339
  return;
1315
1340
  }
1316
1341
  // Rows are stamped with the server receive time, deliberately NOT the
@@ -1365,7 +1390,7 @@ export default (app) => {
1365
1390
  continue;
1366
1391
  if (changeGate?.isRepeat(leaf.path, ctx, source, leaf.value, now))
1367
1392
  continue;
1368
- if (isThrottled(leaf.path, ctx, config.defaultSamplingRate ?? 2000))
1393
+ if (isThrottled(leaf.path, ctx, config.defaultSamplingRate ?? 2000, perSource))
1369
1394
  continue;
1370
1395
  changeGate?.wrote(leaf.path, ctx, source, leaf.value, now);
1371
1396
  if (typeof leaf.value === "number") {