@signalk/tracks-plugin 3.1.0 → 3.2.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/README.md +63 -0
- package/dist/asyncTrackStore.d.ts +24 -1
- package/dist/bin.js +285 -0
- package/dist/e2e.test-utils.d.ts +16 -2
- package/dist/gpx.d.ts +11 -2
- package/dist/gpxExport.test.d.ts +1 -0
- package/dist/gpxImport.test.d.ts +1 -0
- package/dist/harness.test-utils.d.ts +11 -0
- package/dist/import.js +98 -0
- package/dist/importedTracks.d.ts +54 -0
- package/dist/importedTracks.test.d.ts +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.html +52 -0
- package/dist/index.js +802 -97
- package/dist/index.js.map +1 -1
- package/dist/reconcile.d.ts +11 -0
- package/dist/recycleBin.d.ts +20 -0
- package/dist/recycleBin.test.d.ts +1 -0
- package/dist/recycleBinApi.test.d.ts +1 -0
- package/dist/sqliteStore.d.ts +58 -1
- package/dist/store.d.ts +38 -1
- package/dist/style.css +78 -0
- package/dist/trackApi.d.ts +61 -5
- package/dist/trackProvider.d.ts +4 -0
- package/dist/trackStoreProtocol.d.ts +1 -1
- package/dist/trackStoreWorker.js +371 -1
- package/dist/trackStoreWorker.js.map +1 -1
- package/dist/tracks.js +132 -13
- package/dist/types.d.ts +74 -0
- package/dist/upload.d.ts +19 -0
- package/dist/{utils-CGIGp4Xf.js → utils-C0VkiidM.js} +32 -2
- package/dist/{utils-CGIGp4Xf.js.map → utils-C0VkiidM.js.map} +1 -1
- package/dist/utils.d.ts +14 -0
- package/docs/history-and-storage.md +33 -0
- package/package.json +1 -1
- package/public/bin.js +285 -0
- package/public/import.js +98 -0
- package/public/index.html +52 -0
- package/public/style.css +78 -0
- package/public/tracks.js +132 -13
package/dist/reconcile.d.ts
CHANGED
|
@@ -14,3 +14,14 @@ export interface ReconcileResult {
|
|
|
14
14
|
* bucket.
|
|
15
15
|
*/
|
|
16
16
|
export declare function reconcile(history: TimedPosition[], stored: TimedPosition[], resolution: number): ReconcileResult;
|
|
17
|
+
/**
|
|
18
|
+
* Merge history that is coarser than the store into it, the store first.
|
|
19
|
+
*
|
|
20
|
+
* The rule above holds while history is the finer source. A history read
|
|
21
|
+
* widened to stay within its bucket budget is not: one of its buckets spans
|
|
22
|
+
* several stored fixes, and letting it win would replace them all with one
|
|
23
|
+
* point. So here the store keeps every bucket it has a point in, and history
|
|
24
|
+
* fills only the buckets the store has nothing for — where the provider
|
|
25
|
+
* recorded before this plugin was installed, or while it was off.
|
|
26
|
+
*/
|
|
27
|
+
export declare function fillFromHistory(history: TimedPosition[], stored: TimedPosition[], resolution: number): ReconcileResult;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DeletedSpan, TimedPosition } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Prefix of a recorded track's id. The rest is the vessel's context: a vessel
|
|
4
|
+
* has one recording, so its context is all an id needs, and a provider may
|
|
5
|
+
* derive the id from it, as the server's Track API foresees.
|
|
6
|
+
*/
|
|
7
|
+
export declare const RECORDED_PREFIX = "recorded:";
|
|
8
|
+
export declare const recordedId: (context: string) => string;
|
|
9
|
+
/** The context a recorded track's id names, or undefined for any other id. */
|
|
10
|
+
export declare const recordedContext: (id: string) => string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* The points outside every deleted span.
|
|
13
|
+
*
|
|
14
|
+
* Applied to what a history provider returns, which this plugin cannot delete
|
|
15
|
+
* from: without it the next query would fill a deleted span back in. A
|
|
16
|
+
* history point stands for a bucket starting at its timestamp, and the fix it
|
|
17
|
+
* carries can lie anywhere in that bucket, so a bucket that overlaps a span at
|
|
18
|
+
* all is dropped; the store's own points answer for the rest of it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function outsideSpans(points: TimedPosition[], spans: readonly DeletedSpan[]): TimedPosition[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/sqliteStore.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TrackQuery } from './timeWindow.js';
|
|
2
2
|
import { TrackStore } from './store.js';
|
|
3
|
-
import { Context, Debug, LatLngTuple, TimedPosition, TimedTrackCollection, TimeWindow, TrackCollection, TrackParams } from './types.js';
|
|
3
|
+
import { BinEntry, Context, Debug, ImportedTrack, ImportFilter, LatLngTuple, RecordedSpan, RestoreResult, TimedPosition, TimedTrackCollection, TimeWindow, TrackCollection, TrackParams } from './types.js';
|
|
4
4
|
export declare class TransactionStateError extends Error {
|
|
5
5
|
}
|
|
6
6
|
export interface SqliteStoreConfig {
|
|
@@ -17,6 +17,8 @@ export interface SqliteStoreConfig {
|
|
|
17
17
|
resolution?: number;
|
|
18
18
|
/** Drop positions older than this many ms. 0 keeps everything. */
|
|
19
19
|
retention?: number;
|
|
20
|
+
/** Purge a recycle bin entry this many ms after its delete. 0 never purges. */
|
|
21
|
+
binRetention?: number;
|
|
20
22
|
/** Cells per bounding box covering. */
|
|
21
23
|
maxCells?: number;
|
|
22
24
|
/** A gap longer than this ms starts a new track segment. */
|
|
@@ -38,9 +40,11 @@ export declare class SqliteTrackStore implements TrackStore {
|
|
|
38
40
|
private readonly insert;
|
|
39
41
|
private readonly insertName;
|
|
40
42
|
private readonly selectName;
|
|
43
|
+
private readonly selectImportedPoints;
|
|
41
44
|
private readonly maxCells;
|
|
42
45
|
private readonly segmentGap;
|
|
43
46
|
private readonly retention;
|
|
47
|
+
private readonly binRetention;
|
|
44
48
|
private readonly resolution;
|
|
45
49
|
/** See CLIP_STRETCH_GAP; widened so a coarse write resolution does not split every fix. */
|
|
46
50
|
private readonly stretchGap;
|
|
@@ -123,6 +127,59 @@ export declare class SqliteTrackStore implements TrackStore {
|
|
|
123
127
|
recordName(context: Context, name: string, timestamp?: number): void;
|
|
124
128
|
nameFor(context: Context): string | undefined;
|
|
125
129
|
prune(maxAge: number, keep?: Context, now?: number): void;
|
|
130
|
+
/**
|
|
131
|
+
* Keep an imported track. The caller mints the id; a second track under the
|
|
132
|
+
* same id fails rather than replacing the first.
|
|
133
|
+
*/
|
|
134
|
+
storeImport(track: ImportedTrack): void;
|
|
135
|
+
/**
|
|
136
|
+
* Move a vessel's positions recorded within `[from, to]` into the recycle
|
|
137
|
+
* bin, resolving to the entry's id and whether the store held the vessel at
|
|
138
|
+
* all.
|
|
139
|
+
*
|
|
140
|
+
* An entry is made even when the store holds nothing there if `keepEmpty`
|
|
141
|
+
* says another source does: the span still has to hide what a history
|
|
142
|
+
* provider holds. Without one, an empty delete leaves no entry behind.
|
|
143
|
+
*/
|
|
144
|
+
binRecorded(context: Context, from: number | undefined, to: number, deletedAt: number, keepEmpty: boolean): {
|
|
145
|
+
id?: number;
|
|
146
|
+
known: boolean;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Move an imported track into the recycle bin, or with a span only its
|
|
150
|
+
* points within it, resolving to the entry's id; undefined when there is no
|
|
151
|
+
* such import, or the span holds none of its points.
|
|
152
|
+
*/
|
|
153
|
+
binImport(importId: string, span: {
|
|
154
|
+
from?: number;
|
|
155
|
+
to?: number;
|
|
156
|
+
} | undefined, deletedAt: number): number | undefined;
|
|
157
|
+
private newBinEntry;
|
|
158
|
+
/**
|
|
159
|
+
* Put a recycle bin entry's rows back where they were deleted from.
|
|
160
|
+
*
|
|
161
|
+
* A span of an import whose whole track has since gone to the bin as well
|
|
162
|
+
* is a conflict: its points have no track to return to until that is
|
|
163
|
+
* restored first.
|
|
164
|
+
*/
|
|
165
|
+
restoreFromBin(id: number): RestoreResult;
|
|
166
|
+
/** Purge one recycle bin entry now, resolving whether it was in the bin. */
|
|
167
|
+
purgeFromBin(id: number): boolean;
|
|
168
|
+
/** Entries still in the recycle bin, newest delete first. */
|
|
169
|
+
binEntries(): BinEntry[];
|
|
170
|
+
/** Every deleted span of a recorded track, purged or not: what history must not show. */
|
|
171
|
+
deletedSpans(): RecordedSpan[];
|
|
172
|
+
private liveEntry;
|
|
173
|
+
/**
|
|
174
|
+
* Remove an entry's rows for good. A recorded span stays as a tombstone, so
|
|
175
|
+
* history cannot refill it; a whole import takes the spans deleted from it
|
|
176
|
+
* earlier with it, since they have no track left to be restored into.
|
|
177
|
+
*/
|
|
178
|
+
private purge;
|
|
179
|
+
getImport(id: string): ImportedTrack | undefined;
|
|
180
|
+
/** Imported tracks the filter lets through, in the order they were stored. */
|
|
181
|
+
findImports(filter?: ImportFilter): ImportedTrack[];
|
|
182
|
+
private importFrom;
|
|
126
183
|
/** Worker startup snapshot; queries and name lookups must not open a second database. */
|
|
127
184
|
storedNames(): {
|
|
128
185
|
context: string;
|
package/dist/store.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context, Debug, LatLngTuple, TimedPosition, TimedTrackCollection, TimeWindow, TrackCollection, TrackParams } from './types.js';
|
|
1
|
+
import { BinEntry, Context, Debug, ImportedTrack, ImportFilter, LatLngTuple, RecordedSpan, RestoreResult, TimedPosition, TimedTrackCollection, TimeWindow, TrackCollection, TrackParams } from './types.js';
|
|
2
2
|
import { TrackQuery } from './timeWindow.js';
|
|
3
3
|
/**
|
|
4
4
|
* A track store: somewhere positions are accumulated and queried back.
|
|
@@ -86,6 +86,43 @@ export interface TrackStore {
|
|
|
86
86
|
* keeps it out of a real configuration.
|
|
87
87
|
*/
|
|
88
88
|
prune(maxAge: number, keep?: Context): void;
|
|
89
|
+
/**
|
|
90
|
+
* Keep a track a client imported. Optional: a store that only records what
|
|
91
|
+
* it observes leaves the four import methods out, and the Track API then
|
|
92
|
+
* answers that storing tracks is not supported.
|
|
93
|
+
*
|
|
94
|
+
* Resolves once the track is durably stored, unlike the recording writes
|
|
95
|
+
* above: the caller hands its id to a client, which must be able to read it
|
|
96
|
+
* back at once.
|
|
97
|
+
*/
|
|
98
|
+
storeImport?(track: ImportedTrack): void | Promise<void>;
|
|
99
|
+
getImport?(id: string): ImportedTrack | undefined | Promise<ImportedTrack | undefined>;
|
|
100
|
+
findImports?(filter?: ImportFilter): ImportedTrack[] | Promise<ImportedTrack[]>;
|
|
101
|
+
/**
|
|
102
|
+
* The recycle bin. Optional like the import methods: a store without it
|
|
103
|
+
* cannot delete. A delete moves rows into the bin rather than removing them,
|
|
104
|
+
* and each of these resolves once committed, like `storeImport`.
|
|
105
|
+
*
|
|
106
|
+
* `binRecorded` moves a vessel's positions recorded within `[from, to]`;
|
|
107
|
+
* `binImport` moves an imported track, or only its points within a span.
|
|
108
|
+
*/
|
|
109
|
+
binRecorded?(context: Context, from: number | undefined, to: number, deletedAt: number, keepEmpty: boolean): {
|
|
110
|
+
id?: number;
|
|
111
|
+
known: boolean;
|
|
112
|
+
} | Promise<{
|
|
113
|
+
id?: number;
|
|
114
|
+
known: boolean;
|
|
115
|
+
}>;
|
|
116
|
+
binImport?(importId: string, span: {
|
|
117
|
+
from?: number;
|
|
118
|
+
to?: number;
|
|
119
|
+
} | undefined, deletedAt: number): number | undefined | Promise<number | undefined>;
|
|
120
|
+
restoreFromBin?(id: number): RestoreResult | Promise<RestoreResult>;
|
|
121
|
+
/** Purge an entry now, resolving whether it was in the bin. */
|
|
122
|
+
purgeFromBin?(id: number): boolean | Promise<boolean>;
|
|
123
|
+
binEntries?(): BinEntry[] | Promise<BinEntry[]>;
|
|
124
|
+
/** Every deleted span of a recorded track, purged or not: what history must not show. */
|
|
125
|
+
deletedSpans?(): RecordedSpan[] | Promise<RecordedSpan[]>;
|
|
89
126
|
/**
|
|
90
127
|
* Release any resources held by the store.
|
|
91
128
|
*
|
package/dist/style.css
CHANGED
|
@@ -47,3 +47,81 @@ tbody tr:hover {
|
|
|
47
47
|
.self {
|
|
48
48
|
font-weight: 600;
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
.sr-only {
|
|
52
|
+
position: absolute;
|
|
53
|
+
width: 1px;
|
|
54
|
+
height: 1px;
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
clip-path: inset(50%);
|
|
57
|
+
white-space: nowrap;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
h2 {
|
|
61
|
+
font-size: 1.1rem;
|
|
62
|
+
margin: 2rem 0 0.25rem;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#import-form {
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-wrap: wrap;
|
|
68
|
+
align-items: center;
|
|
69
|
+
gap: 0.5rem 1rem;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#bin-status,
|
|
73
|
+
#import-status {
|
|
74
|
+
color: GrayText;
|
|
75
|
+
margin: 0 0 1rem;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.error,
|
|
79
|
+
#bin-status[data-state='error'],
|
|
80
|
+
#import-status[data-state='error'] {
|
|
81
|
+
color: crimson;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.imported {
|
|
85
|
+
color: GrayText;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.actions {
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-wrap: wrap;
|
|
91
|
+
gap: 0.5rem;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.danger {
|
|
95
|
+
color: crimson;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
dialog {
|
|
99
|
+
max-width: min(32rem, calc(100vw - 2rem));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
dialog h2 {
|
|
103
|
+
margin-top: 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
dialog fieldset {
|
|
107
|
+
display: grid;
|
|
108
|
+
gap: 0.5rem;
|
|
109
|
+
border: none;
|
|
110
|
+
padding: 0;
|
|
111
|
+
margin: 0 0 1rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
dialog .span {
|
|
115
|
+
display: flex;
|
|
116
|
+
flex-wrap: wrap;
|
|
117
|
+
gap: 0.5rem 1rem;
|
|
118
|
+
margin-left: 1.5rem;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
dialog menu {
|
|
122
|
+
display: flex;
|
|
123
|
+
justify-content: flex-end;
|
|
124
|
+
gap: 0.5rem;
|
|
125
|
+
padding: 0;
|
|
126
|
+
margin: 0;
|
|
127
|
+
}
|
package/dist/trackApi.d.ts
CHANGED
|
@@ -8,7 +8,10 @@ import { Temporal } from '@js-temporal/polyfill';
|
|
|
8
8
|
* imports when it is.
|
|
9
9
|
*
|
|
10
10
|
* Specified in https://github.com/SignalK/signalk-server/issues/2504 and
|
|
11
|
-
* implemented in https://github.com/SignalK/signalk-server/pull/2995
|
|
11
|
+
* implemented in https://github.com/SignalK/signalk-server/pull/2995; storing,
|
|
12
|
+
* fetching and deleting a track by id in
|
|
13
|
+
* https://github.com/SignalK/signalk-server/pull/3038, and deleting part of
|
|
14
|
+
* one in the server's `deleteTrackSpan`.
|
|
12
15
|
*/
|
|
13
16
|
/** `[west, south, east, north]` — GeoJSON coordinate order. */
|
|
14
17
|
export type TrackBoundingBox = [number, number, number, number];
|
|
@@ -47,11 +50,18 @@ export interface TracksRequest {
|
|
|
47
50
|
geometry?: boolean;
|
|
48
51
|
}
|
|
49
52
|
export interface TrackProperties {
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
/** Identifies a stored track within this provider; recorded tracks carry none. */
|
|
54
|
+
id?: string;
|
|
55
|
+
/** Absent for an imported track that names no vessel. */
|
|
56
|
+
context?: string;
|
|
57
|
+
/** Absent along with `context`. */
|
|
58
|
+
isSelf?: boolean;
|
|
52
59
|
contextName?: string;
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
/** The track's own name, as an imported GPX `<trk><name>` carries. */
|
|
61
|
+
name?: string;
|
|
62
|
+
/** Absent, with `to`, for a track whose points carry no times. */
|
|
63
|
+
from?: string;
|
|
64
|
+
to?: string;
|
|
55
65
|
bbox?: TrackBoundingBox;
|
|
56
66
|
pointCount: number;
|
|
57
67
|
resolution?: string;
|
|
@@ -59,6 +69,8 @@ export interface TrackProperties {
|
|
|
59
69
|
coordTimes?: string[][];
|
|
60
70
|
appliedProperties?: string[];
|
|
61
71
|
values?: Record<string, (number | string | null)[][]>;
|
|
72
|
+
/** Whatever else an imported track was posted with, returned as posted. */
|
|
73
|
+
[key: string]: unknown;
|
|
62
74
|
}
|
|
63
75
|
export interface TrackFeature {
|
|
64
76
|
type: 'Feature';
|
|
@@ -76,6 +88,50 @@ export interface TracksResponse {
|
|
|
76
88
|
export interface TrackApi {
|
|
77
89
|
getTracks(query: TracksRequest): Promise<TracksResponse>;
|
|
78
90
|
getTrackContexts(query: TracksRequest): Promise<string[]>;
|
|
91
|
+
/** One stored track by id, or undefined when there is none. */
|
|
92
|
+
getTrack?(id: string): Promise<TrackFeature | undefined>;
|
|
93
|
+
/** Keep a track a client supplied, resolving to the id it was given. */
|
|
94
|
+
storeTrack?(track: TrackImport): Promise<string>;
|
|
95
|
+
/** Delete a stored track by id, resolving false when there is none. */
|
|
96
|
+
deleteTrack?(id: string): Promise<boolean>;
|
|
97
|
+
/** Delete the part of a stored track recorded within `span`, resolving false when there is no such track. */
|
|
98
|
+
deleteTrackSpan?(id: string, span: TrackSpan): Promise<boolean>;
|
|
99
|
+
}
|
|
100
|
+
/** Every point recorded at or after `from` and at or before `to`; an absent bound is open. */
|
|
101
|
+
export interface TrackSpan {
|
|
102
|
+
from?: Temporal.Instant;
|
|
103
|
+
to?: Temporal.Instant;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* A track offered for storage, in the canonical form the server reduces every
|
|
107
|
+
* import to: a MultiLineString, `coordTimes` nested to match it and in UTC, a
|
|
108
|
+
* qualified context, and none of the properties a provider derives itself.
|
|
109
|
+
*/
|
|
110
|
+
export interface TrackImport {
|
|
111
|
+
type: 'Feature';
|
|
112
|
+
geometry: {
|
|
113
|
+
type: 'MultiLineString';
|
|
114
|
+
coordinates: [number, number][][];
|
|
115
|
+
};
|
|
116
|
+
properties: TrackImportProperties;
|
|
117
|
+
}
|
|
118
|
+
export interface TrackImportProperties {
|
|
119
|
+
coordTimes?: string[][];
|
|
120
|
+
name?: string;
|
|
121
|
+
context?: string;
|
|
122
|
+
/** The client's own metadata, stored as posted. */
|
|
123
|
+
[key: string]: unknown;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Thrown by `storeTrack` when this provider will not keep the track, which
|
|
127
|
+
* the server answers with a 400 rather than a 500.
|
|
128
|
+
*
|
|
129
|
+
* The server recognises it by `isTrackRejected` rather than by class, since
|
|
130
|
+
* this is a copy of its own.
|
|
131
|
+
*/
|
|
132
|
+
export declare class TrackRejectedError extends Error {
|
|
133
|
+
readonly isTrackRejected: true;
|
|
134
|
+
constructor(message?: string);
|
|
79
135
|
}
|
|
80
136
|
/** Present on servers that carry the Track API; absent on older ones. */
|
|
81
137
|
export interface WithTrackApi {
|
package/dist/trackProvider.d.ts
CHANGED
|
@@ -49,6 +49,10 @@ export interface TrackProviderDeps {
|
|
|
49
49
|
* only before this plugin was installed is known to history alone.
|
|
50
50
|
*/
|
|
51
51
|
historyContexts: (window: TimeWindow | undefined) => Promise<ReadonlySet<string>>;
|
|
52
|
+
/** Whether a history provider holds anything at all for a context. */
|
|
53
|
+
historyKnows: (context: string) => Promise<boolean>;
|
|
54
|
+
/** Called after a delete, so whatever hides deleted spans from history reloads them. */
|
|
55
|
+
deleted: () => void;
|
|
52
56
|
reconcileWithHistory: (context: string, stored: TimedPosition[], window: TimeWindow | undefined, resolutionMs: number | undefined) => Promise<TimedPosition[]>;
|
|
53
57
|
}
|
|
54
58
|
export declare function createTrackProvider(deps: TrackProviderDeps): TrackApi;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SqliteTrackStore } from './sqliteStore.js';
|
|
2
|
-
export type StoreMethod = 'newPosition' | 'recordName' | 'initialTrack' | 'prune' | 'get' | 'getTimed' | 'getAllTracks' | 'getFilteredTracks' | 'getFilteredTimedTracks' | 'close';
|
|
2
|
+
export type StoreMethod = 'newPosition' | 'recordName' | 'initialTrack' | 'prune' | 'get' | 'getTimed' | 'getAllTracks' | 'getFilteredTracks' | 'getFilteredTimedTracks' | 'storeImport' | 'getImport' | 'findImports' | 'binRecorded' | 'binImport' | 'restoreFromBin' | 'purgeFromBin' | 'binEntries' | 'deletedSpans' | 'close';
|
|
3
3
|
export type Operation = {
|
|
4
4
|
[K in StoreMethod]: {
|
|
5
5
|
id: number;
|