signalk-questdb 2.1.4 → 2.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -27
- package/dist/api-contract.d.ts +45 -0
- package/dist/configpanel/responses.d.ts +28 -1
- package/dist/configpanel/responses.js +74 -0
- package/dist/configpanel/responses.js.map +1 -1
- package/dist/ilp-writer.d.ts +52 -0
- package/dist/ilp-writer.js +88 -7
- package/dist/ilp-writer.js.map +1 -1
- package/dist/index.js +477 -45
- package/dist/index.js.map +1 -1
- package/dist/migration-checkpoint.d.ts +170 -0
- package/dist/migration-checkpoint.js +276 -0
- package/dist/migration-checkpoint.js.map +1 -0
- package/dist/migration-cleanup.d.ts +48 -0
- package/dist/migration-cleanup.js +155 -0
- package/dist/migration-cleanup.js.map +1 -0
- package/dist/migration.d.ts +134 -17
- package/dist/migration.js +694 -158
- package/dist/migration.js.map +1 -1
- package/dist/questdb-mount.d.ts +106 -0
- package/dist/questdb-mount.js +173 -0
- package/dist/questdb-mount.js.map +1 -0
- package/dist/restore.js +44 -8
- package/dist/restore.js.map +1 -1
- package/package.json +2 -2
- package/public/assets/AppPanel-p27aQlBS.js +1 -0
- package/public/assets/PluginConfigurationPanel-BO_4OxBo.js +9 -0
- package/public/assets/_virtual_mf-localSharedImportMap___mfe_internal__signalk_questdb__mf_owner__1-BiBqFhVp.js +1 -0
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__275086164343916__loadShare__react__loadShare__.js-BWO9MGdR.js +1 -0
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__275086164343916__prebuild__react__prebuild__-D2g10OtU.js +1 -0
- package/public/assets/dist-BK97MX24.js +1 -0
- package/public/assets/helpers-Bizl8Raj.js +1 -0
- package/public/assets/hostInit-BFZIDSUI.js +1 -0
- package/public/assets/index-bERRzqQZ.js +2 -0
- package/public/assets/pendingShares-BS4kO2ec.js +2 -0
- package/public/assets/{dist-BgxWXJ5y.js → utils-jR9fY54i.js} +2 -2
- package/public/assets/{vite-preload-helper-HclGiUj8.js → vite-preload-helper-uBIymjUX.js} +1 -1
- package/public/remoteEntry.js +2 -2
- package/public/assets/AppPanel-CNFtJpeq.js +0 -1
- package/public/assets/PluginConfigurationPanel-CR4mD9IH.js +0 -9
- package/public/assets/_virtual_mf-localSharedImportMap___mfe_internal__signalk_questdb__mf_owner__1-7tb3Q-Bx.js +0 -1
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__1__loadShare__react__loadShare__.js-DzhF4wXp.js +0 -1
- package/public/assets/hostInit-CyS9_M1c.js +0 -1
- package/public/assets/index-Dh-t5VMR.js +0 -1
- package/public/assets/virtualExposes-BBIkbTtD.js +0 -1
package/dist/migration.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { ILPWriter } from "./ilp-writer.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { CheckpointStore, ConfirmStored, MigrationCheckpoint } from "./migration-checkpoint.js";
|
|
3
|
+
import type { MigrationBucket, MigrationMeasurement, MigrationProgress, MigrationResumePoint, MigrationRunState } from "./api-contract.js";
|
|
3
4
|
/**
|
|
4
|
-
* How much time one
|
|
5
|
-
* not
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* How much time one query covers. Large enough that a sparse measurement does
|
|
6
|
+
* not need thousands of round trips, small enough that one query stays cheap
|
|
7
|
+
* for InfluxDB and progress moves in visible steps. It does not bound memory:
|
|
8
|
+
* a window is streamed in batches, however many points it holds.
|
|
8
9
|
*/
|
|
9
10
|
export declare const DEFAULT_WINDOW_MS: number;
|
|
10
11
|
export interface InfluxAuth {
|
|
@@ -25,8 +26,18 @@ export interface MigrationRequest {
|
|
|
25
26
|
/** ISO instants bounding the import. */
|
|
26
27
|
from: string;
|
|
27
28
|
to: string;
|
|
28
|
-
/** Signal K context to write
|
|
29
|
+
/** Signal K context to write the source's own vessel under. */
|
|
29
30
|
context: string;
|
|
31
|
+
/**
|
|
32
|
+
* The `context` tag value the source uses for its own vessel. Rows tagged
|
|
33
|
+
* with it are written under `context`; rows tagged otherwise are other
|
|
34
|
+
* vessels and keep their tag (see `others`); untagged rows are the own
|
|
35
|
+
* vessel too. Absent: every row is the own vessel.
|
|
36
|
+
*/
|
|
37
|
+
sourceSelfContext?: string;
|
|
38
|
+
/** What to do with rows of other vessels: keep them under their own
|
|
39
|
+
* context (the default), or leave them out. */
|
|
40
|
+
others?: "keep" | "skip";
|
|
30
41
|
/** Restrict to these measurements; empty/absent means all of them. */
|
|
31
42
|
measurements?: string[];
|
|
32
43
|
/** `source` tag written on every imported row, so it is distinguishable. */
|
|
@@ -47,18 +58,70 @@ export declare function listMeasurements(req: {
|
|
|
47
58
|
auth?: InfluxAuth;
|
|
48
59
|
}, fetchImpl?: typeof fetch): Promise<MigrationMeasurement[]>;
|
|
49
60
|
/**
|
|
50
|
-
*
|
|
61
|
+
* The `context` tag values in a bucket/database: the vessels the source holds.
|
|
62
|
+
* Both Signal K InfluxDB writers tag every point with the delta's context, so
|
|
63
|
+
* this is how the source's own vessel is told from the AIS targets it recorded.
|
|
64
|
+
* A source with no such tag — a non-Signal K schema — returns none.
|
|
65
|
+
*/
|
|
66
|
+
export declare function listContexts(req: {
|
|
67
|
+
url: string;
|
|
68
|
+
type: string;
|
|
69
|
+
bucket: string;
|
|
70
|
+
auth?: InfluxAuth;
|
|
71
|
+
}, fetchImpl?: typeof fetch): Promise<string[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Whether a string is a Signal K context this plugin will file history under:
|
|
74
|
+
* a vessel, aton, aircraft or SAR target with a well-formed identifier.
|
|
75
|
+
*/
|
|
76
|
+
export declare function isSignalKContext(context: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Incremental annotated-CSV reader for Flux responses.
|
|
79
|
+
*
|
|
80
|
+
* Fed one physical line at a time, so a response is never held whole: the
|
|
81
|
+
* caller streams the body through it and sees each record as it completes.
|
|
51
82
|
*
|
|
52
83
|
* Flux emits one or more *tables*, each preceded by annotation lines starting
|
|
53
84
|
* with `#` and then a header row. A response can therefore change its column
|
|
54
85
|
* layout partway through, which is why the header is tracked per block rather
|
|
55
86
|
* than read once at the top.
|
|
56
87
|
*/
|
|
88
|
+
export declare class AnnotatedCsvReader {
|
|
89
|
+
private header;
|
|
90
|
+
private datatypes;
|
|
91
|
+
private groups;
|
|
92
|
+
private types;
|
|
93
|
+
private grouped;
|
|
94
|
+
private pending;
|
|
95
|
+
/**
|
|
96
|
+
* Feed one physical line, without its `\n`. Returns the record it
|
|
97
|
+
* completes, or null for annotations, headers, blanks and partial records.
|
|
98
|
+
*
|
|
99
|
+
* A quoted CSV value may contain a newline — verified against a live 2.9.1,
|
|
100
|
+
* which returns `"line1\r\nline2"` for a string value holding one. Splitting
|
|
101
|
+
* on newlines alone would tear that record in half and produce two garbage
|
|
102
|
+
* rows, so physical lines are joined until the quotes balance ("" inside a
|
|
103
|
+
* quoted value is an escaped quote and does not change the balance).
|
|
104
|
+
* \r is stripped per PHYSICAL line before joining: a quoted value spanning
|
|
105
|
+
* two CRLF lines would otherwise keep the \r of the first inside the joined
|
|
106
|
+
* value, so the imported string silently carries a stray carriage return.
|
|
107
|
+
*/
|
|
108
|
+
push(physicalLine: string): AnnotatedRecord | null;
|
|
109
|
+
/** Call once after the last line. */
|
|
110
|
+
end(): AnnotatedRecord | null;
|
|
111
|
+
private logicalLine;
|
|
112
|
+
}
|
|
113
|
+
/** Parse a whole annotated-CSV response. For small results only. */
|
|
57
114
|
export declare function parseAnnotatedCsv(text: string): AnnotatedRecord[];
|
|
58
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* One Flux CSV record, plus what the table's annotations say about its columns.
|
|
117
|
+
* `types` and `grouped` belong to the table and are shared by all its records.
|
|
118
|
+
*/
|
|
59
119
|
export interface AnnotatedRecord {
|
|
60
120
|
values: Record<string, string>;
|
|
61
|
-
|
|
121
|
+
/** `#datatype` per column. */
|
|
122
|
+
types: Readonly<Record<string, string>>;
|
|
123
|
+
/** Columns in the table's group key (`#group` true): tags, never fields. */
|
|
124
|
+
grouped: ReadonlySet<string>;
|
|
62
125
|
}
|
|
63
126
|
/**
|
|
64
127
|
* Parse an RFC3339 instant into nanoseconds since the epoch, preserving digits
|
|
@@ -73,12 +136,27 @@ export declare function rfc3339ToNanos(value: string): bigint | null;
|
|
|
73
136
|
/**
|
|
74
137
|
* The Signal K path a measurement+field pair represents.
|
|
75
138
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* fields) is a non-Signal K schema, so the field name is appended to keep
|
|
79
|
-
* two apart instead of overwriting one with the other.
|
|
139
|
+
* The Signal K InfluxDB plugins write the path as the measurement name and the
|
|
140
|
+
* value in one of PATH_VALUE_FIELDS. Anything else (a measurement with several
|
|
141
|
+
* named fields) is a non-Signal K schema, so the field name is appended to keep
|
|
142
|
+
* the two apart instead of overwriting one with the other.
|
|
80
143
|
*/
|
|
81
144
|
export declare function toSignalKPath(measurement: string, field: string): string;
|
|
145
|
+
/**
|
|
146
|
+
* Decode `jsonValue` fields back into the values they encode.
|
|
147
|
+
*
|
|
148
|
+
* Left as the string it arrives as, a position would be recorded in
|
|
149
|
+
* `signalk_str` as text and `signalk_position` would stay empty. Decoded, it
|
|
150
|
+
* routes exactly like the live value did: a position to the position table,
|
|
151
|
+
* any other object to its scalar leaves.
|
|
152
|
+
*
|
|
153
|
+
* A value that does not parse cannot be represented, so it is dropped — and
|
|
154
|
+
* reported, so it lands in the run's skipped total.
|
|
155
|
+
*/
|
|
156
|
+
export declare function decodeJsonValueRows(rows: SourceRow[]): {
|
|
157
|
+
rows: SourceRow[];
|
|
158
|
+
dropped: number;
|
|
159
|
+
};
|
|
82
160
|
/** Coerce an InfluxDB field value (always text over CSV) to a JS value. */
|
|
83
161
|
export declare function coerceValue(raw: string, dataType?: string): unknown;
|
|
84
162
|
export interface MigrationRunHandle {
|
|
@@ -90,6 +168,7 @@ export interface MigrationRunHandle {
|
|
|
90
168
|
finishedAt?: string;
|
|
91
169
|
progress: MigrationProgress;
|
|
92
170
|
error?: string;
|
|
171
|
+
resumedFrom?: MigrationResumePoint;
|
|
93
172
|
cancel(): void;
|
|
94
173
|
}
|
|
95
174
|
/**
|
|
@@ -104,6 +183,7 @@ export declare class MigrationRun implements MigrationRunHandle {
|
|
|
104
183
|
startedAt: string;
|
|
105
184
|
finishedAt?: string;
|
|
106
185
|
error?: string;
|
|
186
|
+
resumedFrom?: MigrationResumePoint;
|
|
107
187
|
progress: MigrationProgress;
|
|
108
188
|
private cancelled;
|
|
109
189
|
constructor(id: string, url: string, bucket: string);
|
|
@@ -120,26 +200,63 @@ export declare class MigrationRun implements MigrationRunHandle {
|
|
|
120
200
|
*/
|
|
121
201
|
export declare function runMigration(req: MigrationRequest, writer: Pick<ILPWriter, "writeAtNanos" | "writeStringAtNanos" | "writePositionAtNanos" | "pendingLines"> & {
|
|
122
202
|
readonly droppedLineCount?: number;
|
|
203
|
+
readonly enqueuedLineCount?: number;
|
|
204
|
+
readonly settledLineCount?: number;
|
|
205
|
+
readonly atCapacity?: boolean;
|
|
123
206
|
}, run: MigrationRun, deps?: {
|
|
124
207
|
fetchImpl?: typeof fetch;
|
|
125
208
|
debug?: (msg: string) => void;
|
|
126
209
|
/** Injected so tests don't wait on real backpressure sleeps. */
|
|
127
210
|
sleep?: (ms: number) => Promise<void>;
|
|
211
|
+
/** Injected so tests don't wait out a real stalled read. */
|
|
212
|
+
readIdleTimeoutMs?: number;
|
|
213
|
+
/** Where the import's position is kept, so a later run can resume it. */
|
|
214
|
+
checkpoints?: CheckpointStore;
|
|
215
|
+
/**
|
|
216
|
+
* A checkpoint to continue from. Only honoured if it belongs to this same
|
|
217
|
+
* import — anything else is ignored and the run starts from the beginning.
|
|
218
|
+
*/
|
|
219
|
+
resumeFrom?: MigrationCheckpoint;
|
|
220
|
+
/** Injected so tests don't wait out the real checkpoint lag. */
|
|
221
|
+
checkpointLagMs?: number;
|
|
222
|
+
now?: () => number;
|
|
223
|
+
/**
|
|
224
|
+
* QuestDB's confirmation that rows can be read back, consulted before a
|
|
225
|
+
* position is saved. Without it a position rests on the writer's counters
|
|
226
|
+
* and the lag alone.
|
|
227
|
+
*/
|
|
228
|
+
confirmStored?: ConfirmStored;
|
|
128
229
|
}): Promise<void>;
|
|
230
|
+
/**
|
|
231
|
+
* The Signal K context a row is written under, or null for a row that is left
|
|
232
|
+
* out — counted as skipped by the caller.
|
|
233
|
+
*
|
|
234
|
+
* The source's own vessel becomes `self`, whether the point was tagged with
|
|
235
|
+
* the source's own context, with the literal `vessels.self`, or not at all.
|
|
236
|
+
* Any other tag is another vessel: kept under its own context if it is a
|
|
237
|
+
* well-formed Signal K one, since that is what the history API is queried by,
|
|
238
|
+
* or left out when other vessels are not wanted. With no `sourceSelf` given,
|
|
239
|
+
* every row is the own vessel — what a single-vessel source amounts to.
|
|
240
|
+
*/
|
|
241
|
+
export declare function targetContext(rowContext: string | undefined, sourceSelf: string | undefined, self: string, others: "keep" | "skip"): string | null;
|
|
129
242
|
/** One point read out of InfluxDB, normalised across the two dialects. */
|
|
130
243
|
export interface SourceRow {
|
|
131
244
|
tsNanos: bigint;
|
|
132
245
|
field: string;
|
|
133
246
|
value: unknown;
|
|
247
|
+
/** The source's `context` tag, when the point carried one. */
|
|
248
|
+
context?: string;
|
|
134
249
|
}
|
|
135
250
|
/**
|
|
136
251
|
* Recombine latitude/longitude fields of a position measurement into one
|
|
137
252
|
* object value at the same instant.
|
|
138
253
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* two numeric paths and the position history would be unusable
|
|
142
|
-
* positions in their own table.
|
|
254
|
+
* signalk-to-influxdb2 stores navigation.position as two fields (`lat`/`lon`),
|
|
255
|
+
* as does signalk-to-influxdb 1.x with `separateLatLon` on. Left as-is they
|
|
256
|
+
* would import as two numeric paths and the position history would be unusable
|
|
257
|
+
* — QuestDB keeps positions in their own table. The 1.x default encoding, a
|
|
258
|
+
* single `jsonValue`, is already whole by the time it gets here (see
|
|
259
|
+
* decodeJsonValueRows).
|
|
143
260
|
*/
|
|
144
261
|
export declare function mergePositionRows(measurement: string, rows: SourceRow[]): {
|
|
145
262
|
rows: SourceRow[];
|