signalk-questdb 2.4.0 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.coderabbit.yaml +17 -2
- package/.release-please-manifest.json +1 -1
- package/AGENTS.md +80 -0
- package/CLAUDE.md +1 -79
- package/README.md +34 -12
- package/dist/api-contract.d.ts +1 -1
- package/dist/change-gate.d.ts +6 -6
- package/dist/change-gate.js +8 -6
- package/dist/change-gate.js.map +1 -1
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.js +6 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +42 -54
- package/dist/index.js.map +1 -1
- package/dist/path-matcher.d.ts +5 -4
- package/dist/path-matcher.js +17 -10
- package/dist/path-matcher.js.map +1 -1
- package/dist/restore.d.ts +10 -6
- package/dist/restore.js +22 -8
- package/dist/restore.js.map +1 -1
- package/package.json +4 -4
- package/public/assets/{AppPanel-C293yEHl.js → AppPanel-D_HLFa10.js} +1 -1
- package/public/assets/PluginConfigurationPanel-DhWC94G3.js +13 -0
- 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
- 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
- package/public/assets/{dist-BhVCbinB.js → dist-B4My1YVh.js} +1 -1
- package/public/assets/helpers-PTLizuYm.js +1 -0
- package/public/assets/hostInit-Dvn0aCSd.js +1 -0
- package/public/assets/{index-QJC86Uvi.js → index-j5iNctbW.js} +2 -2
- package/public/assets/pendingShares-BHVf8sM9.js +2 -0
- package/public/assets/{utils-D62kKhzq.js → utils-BesU7hIa.js} +1 -1
- package/public/assets/{vite-preload-helper-CRzLMJPS.js → vite-preload-helper-DclECHGd.js} +1 -1
- package/public/remoteEntry.js +2 -2
- package/public/assets/PluginConfigurationPanel-7qfy8fIR.js +0 -11
- package/public/assets/helpers-CvnX61Ju.js +0 -1
- package/public/assets/hostInit-Ds8KD_fS.js +0 -1
- 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 `>=
|
|
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
|
-
##
|
|
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"
|
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
|
-
|
|
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
|
|
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,
|
|
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.
|
|
240
|
-
|
|
241
|
-
|
|
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
|
|
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;
|
|
@@ -515,9 +535,11 @@ collision-avoidance setting, not a convenience one.
|
|
|
515
535
|
|
|
516
536
|
Only navigation and identity paths are replayed, so stale tank levels and engine
|
|
517
537
|
temperatures are not resurrected as though they were current readings. A vessel
|
|
518
|
-
with no position in the window is skipped
|
|
519
|
-
|
|
520
|
-
|
|
538
|
+
with no position in the window is skipped. Nothing the Signal K model already
|
|
539
|
+
holds is replaced: a vessel heard while QuestDB was still starting keeps its
|
|
540
|
+
newer position, and after a plugin restart every vessel keeps the one it has.
|
|
541
|
+
Restore honours the recording toggles: with **Record AIS targets** off, no AIS
|
|
542
|
+
target is restored.
|
|
521
543
|
|
|
522
544
|
The config panel reports the outcome once startup finishes: **Vessels Restored**
|
|
523
545
|
with the count, or **Restore failed** if the replay could not run. Neither
|
|
@@ -900,8 +922,8 @@ QuestDB releases they held memory.
|
|
|
900
922
|
|
|
901
923
|
- **Default sampling rate** of 2000 ms limits each path to one write per two
|
|
902
924
|
seconds, keeping write volume modest on busy NMEA 2000 buses; per-path
|
|
903
|
-
overrides allow faster rates for critical paths
|
|
904
|
-
|
|
925
|
+
overrides allow faster rates, or wire speed with 0, for critical paths while
|
|
926
|
+
slow-changing ones stay throttled.
|
|
905
927
|
- **Change-only recording** skips a value that has not changed since it was last
|
|
906
928
|
written until the heartbeat is due. Paths that hold steady — which is
|
|
907
929
|
most of them — cost one row per heartbeat instead of one per sampling
|
package/dist/api-contract.d.ts
CHANGED
|
@@ -76,7 +76,7 @@ export interface UnstorableStatus {
|
|
|
76
76
|
export interface RestoreStatus {
|
|
77
77
|
/** Vessels replayed into the Signal K model. */
|
|
78
78
|
contexts: number;
|
|
79
|
-
/** Contexts skipped because the
|
|
79
|
+
/** Contexts skipped because the Signal K model already held their position. */
|
|
80
80
|
skippedLive: number;
|
|
81
81
|
/** True when the restore threw; `contexts` is then not meaningful. */
|
|
82
82
|
failed: boolean;
|
package/dist/change-gate.d.ts
CHANGED
|
@@ -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
|
|
18
|
-
* was flattened into. Signal K reports a
|
|
19
|
-
* without this, the reading that comes
|
|
20
|
-
*
|
|
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
|
}
|
package/dist/change-gate.js
CHANGED
|
@@ -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
|
|
72
|
-
* was flattened into. Signal K reports a
|
|
73
|
-
* without this, the reading that comes
|
|
74
|
-
*
|
|
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;
|
package/dist/change-gate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-gate.js","sourceRoot":"","sources":["../src/change-gate.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,uEAAuE;AACvE,EAAE;AACF,4EAA4E;AAC5E,uEAAuE;AACvE,0EAA0E;AAC1E,4EAA4E;AAC5E,sEAAsE;AACtE,0EAA0E;AAC1E,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,2DAA2D;AAQ3D,MAAM,OAAO,UAAU;IAYF;IACA;IAZnB,0EAA0E;IAC1E,yEAAyE;IACxD,SAAS,GAAG,IAAI,GAAG,EAAgC,CAAC;IAC7D,KAAK,GAAG,CAAC,CAAC;IAElB,4EAA4E;IAC5E,yEAAyE;IACzE,0EAA0E;IAC1E,uEAAuE;IACvE,gBAAgB;IAChB,YACmB,WAAmB,EACnB,aAAa,MAAM;QADnB,gBAAW,GAAX,WAAW,CAAQ;QACnB,eAAU,GAAV,UAAU,CAAS;IACnC,CAAC;IAEJ,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;;OAIG;IACH,QAAQ,CACN,IAAY,EACZ,OAAe,EACf,MAA0B,EAC1B,KAAc,EACd,GAAW;QAEX,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAClE,OAAO,CACL,KAAK,KAAK,SAAS;YACnB,KAAK,CAAC,KAAK,KAAK,KAAK;YACrB,GAAG,GAAG,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,WAAW,CAClC,CAAC;IACJ,CAAC;IAED,2EAA2E;IAC3E,KAAK,CACH,IAAY,EACZ,OAAe,EACf,MAA0B,EAC1B,KAAc,EACd,GAAW;QAEX,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC;YAAE,OAAO;QAClC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5B,IAAI,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACnB,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBAClC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAChB,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACtC,CAAC;YACD,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;gBAClB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACrC,CAAC;YACD,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;QAClB,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACzC,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,IAAY,EAAE,OAAe;
|
|
1
|
+
{"version":3,"file":"change-gate.js","sourceRoot":"","sources":["../src/change-gate.ts"],"names":[],"mappings":"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"}
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -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;
|
package/dist/config/schema.js
CHANGED
|
@@ -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.*":
|
|
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":"AAAA,OAAO,EAAE,IAAI,EAAU,MAAM,SAAS,CAAC;AAEvC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;IACtC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC;QACvB,OAAO,EAAE,WAAW;QACpB,KAAK,EAAE,cAAc;QACrB,WAAW,EACT,oHAAoH;KACvH,CAAC;IACF,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC;QAC1B,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EACT,4HAA4H;KAC/H,CAAC;IACF,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC;QAC3B,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EACT,4HAA4H;KAC/H,CAAC;IACF,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC;QACzB,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EACT,uHAAuH;KAC1H,CAAC;IACF,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC;QAC1B,OAAO,EAAE,QAAQ;QACjB,KAAK,EAAE,uBAAuB;KAC/B,CAAC;IAEF,gBAAgB,EAAE,IAAI,CAAC,OAAO,CAAC;QAC7B,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,gDAAgD;QACvD,WAAW,EAAE,oDAAoD;KAClE,CAAC;IAEF,kBAAkB,EAAE,IAAI,CAAC,MAAM,CAAC;QAC9B,OAAO,EAAE,MAAM;QACf,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EACT,2JAA2J;KAC9J,CAAC;IAEF,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC;QAC3B,OAAO,EAAE,GAAG;QACZ,KAAK,EAAE,2BAA2B;QAClC,WAAW,EACT,sEAAsE;KACzE,CAAC;IAEF,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;QACtB,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE;YACnE,OAAO,EAAE,SAAS;YAClB,KAAK,EAAE,aAAa;SACrB,CAAC;QACF,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE;YAC/B,OAAO,EAAE,EAAE;YACX,KAAK,EAAE,gCAAgC;YACvC,WAAW,EAAE,8CAA8C;SAC5D,CAAC;KACH,CAAC;IAEF,mBAAmB,EAAE,IAAI,CAAC,MAAM,CAAC;QAC/B,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,4BAA4B;QACnC,WAAW,EACT,gMAAgM;KACnM,CAAC;IAEF,kBAAkB,EAAE,IAAI,CAAC,MAAM,CAAC;QAC9B,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,GAAG;QACZ,OAAO,EAAE,MAAM;QACf,KAAK,EAAE,2BAA2B;QAClC,WAAW,EACT,wbAAwb;KAC3b,CAAC;IAEF,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE;QACvD,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,8BAA8B;QACrC,WAAW,EACT,
|
|
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;
|