signalk-questdb 2.3.2 → 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.
- 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 +121 -9
- 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/container-env.js +14 -0
- package/dist/container-env.js.map +1 -1
- package/dist/drained.d.ts +2 -0
- package/dist/drained.js +22 -0
- package/dist/drained.js.map +1 -0
- package/dist/ilp-writer.d.ts +2 -0
- package/dist/ilp-writer.js +48 -13
- package/dist/ilp-writer.js.map +1 -1
- package/dist/image-refresh.d.ts +35 -0
- package/dist/image-refresh.js +118 -0
- package/dist/image-refresh.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +502 -149
- 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/query-client.d.ts +12 -1
- package/dist/query-client.js +37 -3
- package/dist/query-client.js.map +1 -1
- package/dist/questdb-ready.d.ts +21 -0
- package/dist/questdb-ready.js +35 -0
- package/dist/questdb-ready.js.map +1 -0
- package/dist/questdb-watchdog.d.ts +73 -0
- package/dist/questdb-watchdog.js +308 -0
- package/dist/questdb-watchdog.js.map +1 -0
- package/dist/watchdog-checks.d.ts +50 -0
- package/dist/watchdog-checks.js +279 -0
- package/dist/watchdog-checks.js.map +1 -0
- package/package.json +5 -5
- package/public/assets/AppPanel-D_HLFa10.js +1 -0
- package/public/assets/PluginConfigurationPanel-CAj6IDmY.js +13 -0
- package/public/assets/_virtual_mf-localSharedImportMap___mfe_internal__signalk_questdb__mf_owner__1-CTTydXPl.js +1 -0
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__97224418320974__loadShare__shared-BgRwx7LZ.js +1 -0
- package/public/assets/{_virtual_mf___mfe_internal__signalk_questdb__mf_owner__196689844091135__prebuild__react__prebuild__-eQjVI-Oj.js → _virtual_mf___mfe_internal__signalk_questdb__mf_owner__97224418320974__prebuild__react__prebuild__-CTXDWPi7.js} +1 -1
- package/public/assets/{dist-C5M3fhBw.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-D2iWq8nf.js → index-j5iNctbW.js} +2 -2
- package/public/assets/pendingShares-BHVf8sM9.js +2 -0
- package/public/assets/rolldown-runtime-XKyaHtl9.js +1 -0
- package/public/assets/{utils-v9GZlbdk.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/AppPanel-Fvhb8xTK.js +0 -1
- package/public/assets/PluginConfigurationPanel-DR-y5e3d.js +0 -11
- package/public/assets/_virtual_mf-localSharedImportMap___mfe_internal__signalk_questdb__mf_owner__1-BUtmcsA-.js +0 -1
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__196689844091135__loadShare__shared-BRI06Lxu.js +0 -8
- package/public/assets/helpers-oIxDNnZJ.js +0 -1
- package/public/assets/hostInit-3qWTwrQQ.js +0 -1
- package/public/assets/pendingShares-PvyVL1Z8.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) |
|
|
@@ -132,6 +133,51 @@ rootless Podman.
|
|
|
132
133
|
| Container network | `sk-network` | Shared Podman/Docker network QuestDB joins in managed mode, so the companion signalk-grafana reaches it by container DNS; host-port publication is separate ("Bind to 0.0.0.0") |
|
|
133
134
|
| Bind to 0.0.0.0 | `false` | Publish QuestDB's ports on all interfaces (see Connectivity) |
|
|
134
135
|
|
|
136
|
+
### QuestDB updates
|
|
137
|
+
|
|
138
|
+
The panel's **update check** compares the running QuestDB with its latest stable
|
|
139
|
+
release. **Update** pulls that release, recreates the container on it and saves
|
|
140
|
+
it as the image version.
|
|
141
|
+
|
|
142
|
+
After the plugin itself was updated, and at its very first start, the plugin
|
|
143
|
+
also checks once on its own, in the background. It does so only in managed mode,
|
|
144
|
+
and only where the image version is a tag that moves, such as the default
|
|
145
|
+
`latest`. If QuestDB runs an older release than the latest stable one, the
|
|
146
|
+
plugin pulls the tag again and recreates the container on it. The image version
|
|
147
|
+
stays `latest`. Without this check, QuestDB on `latest` would stay on whatever
|
|
148
|
+
release the first install pulled, because signalk-container pulls an image only
|
|
149
|
+
when it is missing. An image version that names one release, such as `9.4.3`,
|
|
150
|
+
stays as it is, and so does one that **Update** saved while the check was
|
|
151
|
+
waiting. QuestDB is down while it starts again, as with **Update**. Progress
|
|
152
|
+
goes to the debug log, and failures go to the server log. A recreate that fails
|
|
153
|
+
half way and leaves QuestDB stopped or without a container is followed by a
|
|
154
|
+
start, as the plugin's own start would do. Only if that fails too does the
|
|
155
|
+
status line say so.
|
|
156
|
+
|
|
157
|
+
With [signalk-internet](https://github.com/meri-imperiumi/signalk-internet)
|
|
158
|
+
enabled, the check waits for a free link. On a metered, captive or offline link
|
|
159
|
+
it asks again from time to time, for as long as the plugin runs. Without
|
|
160
|
+
signalk-internet, the link counts as free. Telling the two apart needs
|
|
161
|
+
signalk-container 1.38.0 or later; with an older one, the check does not run. A
|
|
162
|
+
failure to read the running or the latest version is also tried again later.
|
|
163
|
+
|
|
164
|
+
The check is done once QuestDB runs the latest release, or once its container
|
|
165
|
+
runs the image the pull brought, even if QuestDB still reports an older release
|
|
166
|
+
afterwards. It then waits for the next plugin update. The next start checks
|
|
167
|
+
again wherever the container keeps its old image, or where the new one cannot
|
|
168
|
+
be told from the old:
|
|
169
|
+
|
|
170
|
+
- the plugin stopped first;
|
|
171
|
+
- the download failed, or something else failed before the recreate;
|
|
172
|
+
- an InfluxDB import, a GPX import or an export was running;
|
|
173
|
+
- the string table was being rebuilt;
|
|
174
|
+
- the container was stopped, which the check leaves as it is;
|
|
175
|
+
- the container was wedged;
|
|
176
|
+
- the tag on Docker Hub did not name the latest release yet.
|
|
177
|
+
|
|
178
|
+
A tag that never reaches the latest release, such as `9` while the latest is
|
|
179
|
+
`10.1.0`, therefore costs a pull and a look at GitHub at every start.
|
|
180
|
+
|
|
135
181
|
## Recording
|
|
136
182
|
|
|
137
183
|
### Schema
|
|
@@ -191,9 +237,27 @@ canboat WASM decoder do not produce the marker at all.
|
|
|
191
237
|
|
|
192
238
|
**Sampling.** Each path is written at most once per **Default sampling rate**
|
|
193
239
|
(2000 ms), with per-path overrides by glob for the few paths that need finer
|
|
194
|
-
resolution and the many that need less.
|
|
195
|
-
|
|
196
|
-
|
|
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.
|
|
197
261
|
|
|
198
262
|
**Change-only recording.** Most paths on a boat do not change from one sample
|
|
199
263
|
to the next: a switch that flips twice a day, an energy counter at rest, a tank
|
|
@@ -225,8 +289,9 @@ data really stopped:
|
|
|
225
289
|
age**. Keep the heartbeat below that, or a vessel lying still may come back
|
|
226
290
|
with its position but without its speed, course and heading.
|
|
227
291
|
|
|
228
|
-
`navigation.position` is
|
|
229
|
-
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.
|
|
230
295
|
|
|
231
296
|
**Batching.** Samples are buffered and committed every **Write batch interval**
|
|
232
297
|
(5 s), one WAL transaction per table per commit. The buffer holds 100 000 lines;
|
|
@@ -804,12 +869,59 @@ shown on the status card as **WAL suspended**. Two causes need two remedies:
|
|
|
804
869
|
shows those numbers and offers **Skip**; if the stuck segment is the newest
|
|
805
870
|
one, skipping drops the entire pending backlog, and the panel says so.
|
|
806
871
|
|
|
872
|
+
### Out of memory or not answering
|
|
873
|
+
|
|
874
|
+
QuestDB caps the native memory it tracks below the container's memory limit
|
|
875
|
+
and fails any allocation past the cap with `global RSS memory limit exceeded`.
|
|
876
|
+
When something holds memory at the cap, queries fail, the WAL apply job then
|
|
877
|
+
suspends the tables, and in the end QuestDB stops accepting HTTP connections. The JVM stays up throughout, so the
|
|
878
|
+
container's restart policy never acts.
|
|
879
|
+
|
|
880
|
+
In managed mode the plugin checks for this every minute while it runs, and
|
|
881
|
+
restarts the QuestDB container when:
|
|
882
|
+
|
|
883
|
+
- QuestDB recently reported its global memory limit, and over consecutive
|
|
884
|
+
checks its native memory is still held at that limit, it fails even the
|
|
885
|
+
health check with it, or it has stopped answering. A single large query
|
|
886
|
+
frees what it held when it ends, so it does not count, nor does a client that
|
|
887
|
+
keeps asking for one.
|
|
888
|
+
- the container is running but QuestDB has answered nothing for several
|
|
889
|
+
minutes, over consecutive checks. A QuestDB busy with a long statement still
|
|
890
|
+
answers it in the end, and no check counts while a parquet export streams
|
|
891
|
+
from it.
|
|
892
|
+
|
|
893
|
+
Before the restart the plugin writes QuestDB's message and its last error lines
|
|
894
|
+
to the server log, shows the reason on the status card, and raises the Signal K
|
|
895
|
+
notification `notifications.signalk-questdb.watchdog`. After the restart it
|
|
896
|
+
logs the outcome. Once QuestDB answers again the notification returns to normal;
|
|
897
|
+
when it answered right after the restart, the notification keeps the time and
|
|
898
|
+
the reason. Restarts are rate limited, spaced apart and capped per few hours of
|
|
899
|
+
the plugin running; past that the plugin logs that the restart is held back and
|
|
900
|
+
keeps the alert up. A restart that fails, because stopping or starting the
|
|
901
|
+
container failed, counts against none of these limits: the plugin tries again a
|
|
902
|
+
few minutes later, since the container may be left stopped. It reports the
|
|
903
|
+
first failure; while the retries keep failing, they go to the debug log only.
|
|
904
|
+
|
|
905
|
+
The plugin leaves a container alone if someone else stopped it, and starts one
|
|
906
|
+
that its own restart left stopped. One exception: a container stopped by hand
|
|
907
|
+
while a stop of the plugin's is still pending, or right after a restart of the
|
|
908
|
+
plugin's before the plugin has seen it running again, may be started again too.
|
|
909
|
+
While a stop of the plugin's hangs, it sends no further stop. An external
|
|
910
|
+
QuestDB is never restarted. A
|
|
911
|
+
QuestDB that is already stuck when the plugin starts, as after a Signal K
|
|
912
|
+
restart while it was stuck, is not covered: restarting the plugin stops the
|
|
913
|
+
container and starts it afresh.
|
|
914
|
+
|
|
915
|
+
The managed container runs with QuestDB's query caches off: history reads put
|
|
916
|
+
their time range into the SQL, so the caches never got a hit, and on older
|
|
917
|
+
QuestDB releases they held memory.
|
|
918
|
+
|
|
807
919
|
### Performance (Pi and other low-power hosts)
|
|
808
920
|
|
|
809
921
|
- **Default sampling rate** of 2000 ms limits each path to one write per two
|
|
810
922
|
seconds, keeping write volume modest on busy NMEA 2000 buses; per-path
|
|
811
|
-
overrides allow faster rates for critical paths
|
|
812
|
-
|
|
923
|
+
overrides allow faster rates, or wire speed with 0, for critical paths while
|
|
924
|
+
slow-changing ones stay throttled.
|
|
813
925
|
- **Change-only recording** skips a value that has not changed since it was last
|
|
814
926
|
written until the heartbeat is due. Paths that hold steady — which is
|
|
815
927
|
most of them — cost one row per heartbeat instead of one per sampling
|
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":"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,EAAE,MAAe;QACnD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,MAAM,UAAU,GAAG,GAAG,IAAI,GAAG,CAAC;QAC9B,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;YAC/B,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC;gBAAE,SAAS;YACpE,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC7D,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBAChB,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;YAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvD,CAAC;IAED,KAAK;QACH,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IACjB,CAAC;IAEO,KAAK,CAAC,GAAW;QACvB,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC9C,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;gBAC/B,IAAI,GAAG,GAAG,KAAK,CAAC,EAAE,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;oBACvC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;oBAChB,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;gBAClB,CAAC;YACH,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;gBAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACvD,CAAC;QACD,wEAAwE;QACxE,wEAAwE;QACxE,gDAAgD;QAChD,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,UAAU,GAAG,IAAI;YAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IACzD,CAAC;CACF;AAED,8EAA8E;AAC9E,6DAA6D;AAC7D,SAAS,GAAG,CAAC,IAAY,EAAE,MAA0B;IACnD,OAAO,GAAG,IAAI,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;AACnC,CAAC"}
|
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/container-env.js
CHANGED
|
@@ -134,6 +134,20 @@ export function buildContainerEnv(config) {
|
|
|
134
134
|
// apply memory-pressure backoff that precedes a suspension, so raise this
|
|
135
135
|
// back to INFO when diagnosing a database that has stopped recording.
|
|
136
136
|
QDB_LOG_W_STDOUT_LEVEL: "ERROR",
|
|
137
|
+
// The HTTP endpoint keeps up to 128 compiled queries (32 blocks of 4) for
|
|
138
|
+
// reuse by identical SQL text, and pg-wire keeps another such cache. The
|
|
139
|
+
// plugin's history reads put their time range into the SQL as literals,
|
|
140
|
+
// and so do Grafana's macros, so neither cache gets a hit — they only
|
|
141
|
+
// fill. On QuestDB before 10.0.0 every cached parallel GROUP BY / SAMPLE
|
|
142
|
+
// BY also keeps its native buffers at their peak size (fixed upstream in
|
|
143
|
+
// questdb#7267): about 7 MiB each over a month of data, so a full cache
|
|
144
|
+
// holds more than the 691 MiB the 768m cap leaves QuestDB, and it then
|
|
145
|
+
// fails every allocation with "global RSS memory limit exceeded". Seen
|
|
146
|
+
// twice on 9.4.3 within a day: 20 distinct reads added 150 MiB that only
|
|
147
|
+
// flush_query_cache() or a restart gave back. Without the caches, each
|
|
148
|
+
// read frees its buffers when it ends.
|
|
149
|
+
QDB_HTTP_QUERY_CACHE_ENABLED: "false",
|
|
150
|
+
QDB_PG_SELECT_CACHE_ENABLED: "false",
|
|
137
151
|
QDB_CAIRO_O3_COLUMN_MEMORY_SIZE: "262144",
|
|
138
152
|
...(config.compression && config.compression !== "none"
|
|
139
153
|
? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container-env.js","sourceRoot":"","sources":["../src/container-env.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"container-env.js","sourceRoot":"","sources":["../src/container-env.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,wEAAwE;AACxE,8EAA8E;AAC9E,6EAA6E;AAC7E,0EAA0E;AAC1E,6EAA6E;AAC7E,yBAAyB;AAIzB,kEAAkE;AAClE,6EAA6E;AAC7E,0EAA0E;AAC1E,0EAA0E;AAC1E,yEAAyE;AACzE,0EAA0E;AAC1E,6EAA6E;AAC7E,2EAA2E;AAC3E,0EAA0E;AAC1E,QAAQ;AACR,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAE1C,0EAA0E;AAC1E,2EAA2E;AAC3E,8EAA8E;AAC9E,qBAAqB;AACrB,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAErC,4EAA4E;AAC5E,gBAAgB;AAChB,EAAE;AACF,4EAA4E;AAC5E,6EAA6E;AAC7E,0EAA0E;AAC1E,0EAA0E;AAC1E,2EAA2E;AAC3E,8EAA8E;AAC9E,uEAAuE;AACvE,EAAE;AACF,6EAA6E;AAC7E,sEAAsE;AACtE,8EAA8E;AAC9E,2EAA2E;AAC3E,uEAAuE;AACvE,uEAAuE;AACvE,wEAAwE;AACxE,MAAM,UAAU,eAAe,CAAC,MAAuB;IACrD,MAAM,KAAK,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC9C,IAAI,MAAM,CAAC,kBAAkB,EAAE,IAAI,EAAE,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAAuB;IAEvB,OAAO;QACL,qBAAqB,EAAE,OAAO;QAC9B,gBAAgB,EAAE,MAAM;QACxB,oBAAoB,EAAE,MAAM;QAC5B,qBAAqB,EAAE,mBAAmB;QAC1C,WAAW,EAAE,eAAe,CAAC,MAAM,CAAC;QACpC,yEAAyE;QACzE,sEAAsE;QACtE,yEAAyE;QACzE,yEAAyE;QACzE,wEAAwE;QACxE,iEAAiE;QACjE,uEAAuE;QACvE,wEAAwE;QACxE,oEAAoE;QACpE,gDAAgD;QAChD,gBAAgB,EAAE,GAAG;QAErB,yEAAyE;QACzE,EAAE;QACF,wEAAwE;QACxE,wEAAwE;QACxE,uEAAuE;QACvE,0EAA0E;QAC1E,4CAA4C;QAC5C,EAAE;QACF,oEAAoE;QACpE,gEAAgE;QAChE,uEAAuE;QACvE,2DAA2D;QAC3D,EAAE;QACF,qEAAqE;QACrE,iCAAiC;QACjC,EAAE;QACF,mEAAmE;QACnE,kEAAkE;QAClE,uEAAuE;QACvE,uBAAuB;QACvB,EAAE;QACF,yEAAyE;QACzE,yEAAyE;QACzE,iBAAiB;QACjB,0BAA0B,EAAE,GAAG;QAC/B,uBAAuB,EAAE,GAAG;QAC5B,gCAAgC,EAAE,GAAG;QACrC,wEAAwE;QACxE,yEAAyE;QACzE,yEAAyE;QACzE,yEAAyE;QACzE,wCAAwC;QACxC,4BAA4B,EAAE,GAAG;QACjC,uEAAuE;QACvE,wEAAwE;QACxE,8BAA8B,EAAE,GAAG;QACnC,iCAAiC,EAAE,GAAG;QACtC,kCAAkC,EAAE,GAAG;QACvC,uBAAuB,EAAE,GAAG;QAE5B,0EAA0E;QAC1E,kEAAkE;QAClE,iCAAiC,EAAE,sBAAsB;QACzD,uEAAuE;QACvE,yEAAyE;QACzE,uEAAuE;QACvE,yEAAyE;QACzE,uEAAuE;QACvE,8CAA8C;QAC9C,+BAA+B,EAAE,sBAAsB;QACvD,6BAA6B,EAAE,sBAAsB;QACrD,oCAAoC,EAAE,sBAAsB;QAC5D,sCAAsC,EAAE,sBAAsB;QAC9D,0CAA0C,EAAE,sBAAsB;QAClE,wCAAwC,EAAE,sBAAsB;QAChE,2CAA2C,EAAE,sBAAsB;QACnE,4CAA4C,EAAE,sBAAsB;QACpE,iCAAiC,EAAE,sBAAsB;QAEzD,wEAAwE;QACxE,0EAA0E;QAC1E,mEAAmE;QACnE,mEAAmE;QACnE,uEAAuE;QACvE,0EAA0E;QAC1E,sEAAsE;QACtE,sBAAsB,EAAE,OAAO;QAE/B,0EAA0E;QAC1E,yEAAyE;QACzE,wEAAwE;QACxE,sEAAsE;QACtE,yEAAyE;QACzE,yEAAyE;QACzE,wEAAwE;QACxE,uEAAuE;QACvE,uEAAuE;QACvE,yEAAyE;QACzE,uEAAuE;QACvE,uCAAuC;QACvC,4BAA4B,EAAE,OAAO;QACrC,2BAA2B,EAAE,OAAO;QAEpC,+BAA+B,EAAE,QAAQ;QACzC,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,KAAK,MAAM;YACrD,CAAC,CAAC;gBACE,uCAAuC,EACrC,MAAM,CAAC,WAAW,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;gBAChD,GAAG,CAAC,MAAM,CAAC,WAAW,KAAK,MAAM,IAAI,MAAM,CAAC,gBAAgB;oBAC1D,CAAC,CAAC;wBACE,uCAAuC,EAAE,MAAM,CAC7C,MAAM,CAAC,gBAAgB,CACxB;qBACF;oBACH,CAAC,CAAC,EAAE,CAAC;aACR;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC"}
|
package/dist/drained.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// After a write the response refused, waits until it takes more; false once
|
|
2
|
+
// the client is gone, or once `signal` (the export's deadline) aborts. A
|
|
3
|
+
// response whose client disconnected never drains, nor does one whose client
|
|
4
|
+
// stays connected but stops reading, and a stream loop waiting for it would
|
|
5
|
+
// hold its QuestDB export open for good, and with it the watchdog's export
|
|
6
|
+
// count (questdbExportsRunning in index.ts).
|
|
7
|
+
export function drained(res, signal) {
|
|
8
|
+
if (res.destroyed || signal?.aborted)
|
|
9
|
+
return Promise.resolve(false);
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
const settle = () => {
|
|
12
|
+
res.off("drain", settle);
|
|
13
|
+
res.off("close", settle);
|
|
14
|
+
signal?.removeEventListener("abort", settle);
|
|
15
|
+
resolve(!res.destroyed && !signal?.aborted);
|
|
16
|
+
};
|
|
17
|
+
res.once("drain", settle);
|
|
18
|
+
res.once("close", settle);
|
|
19
|
+
signal?.addEventListener("abort", settle, { once: true });
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=drained.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drained.js","sourceRoot":"","sources":["../src/drained.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,yEAAyE;AACzE,6EAA6E;AAC7E,4EAA4E;AAC5E,2EAA2E;AAC3E,6CAA6C;AAC7C,MAAM,UAAU,OAAO,CACrB,GAAmB,EACnB,MAAoB;IAEpB,IAAI,GAAG,CAAC,SAAS,IAAI,MAAM,EAAE,OAAO;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACpE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACzB,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACzB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC7C,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC,CAAC;QACF,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1B,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1B,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/ilp-writer.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export declare class ILPWriter {
|
|
|
32
32
|
private readonly maxReconnectDelay;
|
|
33
33
|
private readonly stableConnectionMs;
|
|
34
34
|
private readonly unhealthyAfterFlaps;
|
|
35
|
+
private readonly disconnectTimeoutMs;
|
|
35
36
|
private readonly flushIntervalMs;
|
|
36
37
|
constructor(host: string, port: number, debug?: (msg: string) => void, callbacks?: {
|
|
37
38
|
onUnhealthy?: (msg: string) => void;
|
|
@@ -43,6 +44,7 @@ export declare class ILPWriter {
|
|
|
43
44
|
maxReconnectDelay?: number;
|
|
44
45
|
stableConnectionMs?: number;
|
|
45
46
|
unhealthyAfterFlaps?: number;
|
|
47
|
+
disconnectTimeoutMs?: number;
|
|
46
48
|
};
|
|
47
49
|
});
|
|
48
50
|
connect(): Promise<void>;
|