signalk-forecast-dataset-provider 0.1.0 → 0.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/CHANGELOG.md +24 -0
- package/README.md +4 -4
- package/RELEASING.md +24 -9
- package/dist/http.js +12 -12
- package/dist/openapi.js +1 -1
- package/docs/sandbox-verification.md +21 -0
- package/docs/upgrading.md +5 -1
- package/package.json +10 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 — 2026-10-31
|
|
4
|
+
|
|
5
|
+
- Require Node.js 22 or later, dropping Node.js 20 support. Align Node.js
|
|
6
|
+
development types with version 22 and test on Node.js 22/24.
|
|
7
|
+
- Upgrade Express to 5 and validate named route parameters explicitly. Cover
|
|
8
|
+
the HTTP lifecycle against both Express 4 hosts and Express 5 hosts.
|
|
9
|
+
- Upgrade to TypeScript 7 with Node16 module resolution, preserving CommonJS
|
|
10
|
+
output for Signal K plugin loading.
|
|
11
|
+
- Update Signal K development API types to 2.35 and the OpenAPI validator to 13.
|
|
12
|
+
Development checks require Node.js 22.19+; installed runtime code supports 22+.
|
|
13
|
+
- Separate major dependency migrations from routine Dependabot updates; keep
|
|
14
|
+
Express and its types together and Node.js types on the supported baseline.
|
|
15
|
+
- Keep storage, settings, dataset identities and HTTP contract 0.1 unchanged.
|
|
16
|
+
- Verify Signal K 2.34.0/2.35.1 integration and the existing sandbox deployment.
|
|
17
|
+
|
|
18
|
+
## 0.1.1 — 2026-10-31
|
|
19
|
+
|
|
20
|
+
- Enable Forecast Dataset Provider by default on fresh installations, so the
|
|
21
|
+
catalogue is available without a separate activation step.
|
|
22
|
+
- Preserve existing explicit enabled/disabled settings through Signal K's
|
|
23
|
+
standard activation behaviour. Already-disabled installations still require
|
|
24
|
+
enabling the provider once in Plugin Config.
|
|
25
|
+
- Keep storage, permissions and HTTP contract 0.1 unchanged.
|
|
26
|
+
|
|
3
27
|
## 0.1.0 — 2026-10-30
|
|
4
28
|
|
|
5
29
|
- First stable catalogue release, promoting the tested streaming publication,
|
package/README.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Signal K Resource Provider for **`forecastDatasets`**: a shared catalogue of persistent, immutable forecast content. Downloaders and receivers publish original bytes; routing, weather and navigation clients discover and retrieve them without depending on private directories.
|
|
4
4
|
|
|
5
|
-
**Version 0.
|
|
5
|
+
**Version 0.2.0 requires Node.js 22 or later.** It implements staged multi-asset publication (including unknown-size streams), live publication progress, SHA-256 content versions, resumable publication after restart, verified-inventory enrichment, filtered discovery, HTTP Range/HEAD/ETag access, deletion coordination and v2 resource deltas. No GRIB/Zarr decoder is bundled. Formats are declared, not detected or validated meteorologically.
|
|
6
6
|
|
|
7
7
|
The architecture and examples come from the [project outline](https://github.com/macjl/signalk-grib-resources/blob/c289876e4d4e5faadbb6779d865d7aa21a0d9822/docs/project-outline.md). The wire contract selected for this prototype is documented in [contract 0.1](docs/contract-v0.1.md); it is experimental, not an adopted Signal K specification.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
|
-
Requires **Signal K server 2.34 or later within 2.x** and Node.js
|
|
11
|
+
Requires **Signal K server 2.34 or later within 2.x** and Node.js 22 or later for this plugin. Dependency migration integration was also verified against Signal K 2.35.1.
|
|
12
12
|
|
|
13
13
|
See the [coordinated upgrade guide](docs/upgrading.md) before upgrading the downloader and Weather Provider from local-file releases. The complete chain requires Node.js 24+.
|
|
14
14
|
|
|
@@ -18,7 +18,7 @@ Install the stable release from the Signal K App Store or from its configuration
|
|
|
18
18
|
npm install signalk-forecast-dataset-provider@latest
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Restart the server
|
|
21
|
+
Restart the server. From version **0.1.1**, fresh installations enable **Forecast Dataset Provider** automatically. Signal K preserves an existing explicit disabled setting: for an already installed but disabled provider, enable it once in Plugin Config and save. To build a local archive from source:
|
|
22
22
|
|
|
23
23
|
```sh
|
|
24
24
|
npm ci
|
|
@@ -26,7 +26,7 @@ npm run check
|
|
|
26
26
|
npm pack
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
Install the resulting archive using the server's normal plugin installation workflow, for example `npm install /path/to/signalk-forecast-dataset-provider-0.
|
|
29
|
+
Install the resulting archive using the server's normal plugin installation workflow, for example `npm install /path/to/signalk-forecast-dataset-provider-0.2.0.tgz` from its configuration directory. No npm release is required for local testing.
|
|
30
30
|
|
|
31
31
|
The default storage location is `<plugin data directory>/catalogue`. An optional absolute `storageDirectory` can be configured. Use a local filesystem supporting hard links and atomic rename, with a single catalogue writer. Limits are configurable for asset bytes, dataset bytes, asset count and unfinished publications. They are per-publication limits, not an automatic total-disk-quota policy. Unfinished publications persist until resumed or explicitly aborted.
|
|
32
32
|
|
package/RELEASING.md
CHANGED
|
@@ -5,15 +5,16 @@ GRIB Downloader and History Explorer): publish a GitHub Release, or manually run
|
|
|
5
5
|
`.github/workflows/publish.yml`. Publication uses npm trusted publishing (OIDC)
|
|
6
6
|
with provenance; no `NPM_TOKEN` GitHub secret is used.
|
|
7
7
|
|
|
8
|
-
The package and trusted publisher already exist.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
snapshots
|
|
8
|
+
The package and trusted publisher already exist. Version **0.2.0** is a stable
|
|
9
|
+
release published to `latest`. It requires Node.js 22+ and keeps HTTP contract
|
|
10
|
+
0.1. Version 0.1.0 was the first stable release, followed by 0.1.1. The dated
|
|
11
|
+
prototype entries under “Development snapshots” in the changelog were never
|
|
12
|
+
published. See [the coordinated upgrade guide](docs/upgrading.md).
|
|
12
13
|
|
|
13
14
|
## First publication setup
|
|
14
15
|
|
|
15
16
|
The package must exist before an npm trusted publisher can be configured. The
|
|
16
|
-
existing `0.0.
|
|
17
|
+
existing `0.0.1` bootstrap already satisfies this: **do not repeat the
|
|
17
18
|
bootstrap for this package**. For a new package, as with History Explorer,
|
|
18
19
|
bootstrap an **empty** placeholder outside its checkout.
|
|
19
20
|
These commands publish publicly and must be run deliberately by the maintainer:
|
|
@@ -59,8 +60,8 @@ and [npm trust prerequisites](https://docs.npmjs.com/cli/v11/commands/npm-trust/
|
|
|
59
60
|
|
|
60
61
|
## Prepare the real plugin version
|
|
61
62
|
|
|
62
|
-
The
|
|
63
|
-
subsequent
|
|
63
|
+
The release prepared here is **0.2.0**. Decide the new version before tagging
|
|
64
|
+
each subsequent release.
|
|
64
65
|
Keep `package.json`, `package-lock.json`, the OpenAPI info version and changelog
|
|
65
66
|
consistent if changing it. The resource contract stays `0.1` independently of
|
|
66
67
|
the npm package version.
|
|
@@ -77,7 +78,9 @@ helpers, local configuration and credentials must not be included. Compilation
|
|
|
77
78
|
runs during `prepack`, so the installed App Store package needs no install/build
|
|
78
79
|
script. `prepublishOnly` reruns the full checks before a real publish.
|
|
79
80
|
|
|
80
|
-
|
|
81
|
+
Development and release checks require Node 22.19 or later (the OpenAPI
|
|
82
|
+
validator requires this version baseline); installed runtime code supports Node 22+.
|
|
83
|
+
The local CI checks Node 22/24. The separate Signal K reusable CI follows the
|
|
81
84
|
other plugins and adds Linux/ARM64/macOS/Windows plus real-server integration.
|
|
82
85
|
Those hosted checks only run after pushing; local success does not claim they
|
|
83
86
|
have already passed. Publication uses Node 24 on a GitHub-hosted runner.
|
|
@@ -99,7 +102,19 @@ provenance. The OIDC path cannot be fully validated by a local dry run: npm must
|
|
|
99
102
|
recognise the configured GitHub workflow. Tighten npm publishing access only
|
|
100
103
|
after that first successful OIDC publication, as in the other plugins.
|
|
101
104
|
|
|
102
|
-
##
|
|
105
|
+
## Validation for 0.2.0
|
|
106
|
+
|
|
107
|
+
The migration passed 29 top-level tests on Node.js 22/24, including an additional
|
|
108
|
+
execution of the HTTP suite with an Express 4 host and Express 5 middleware.
|
|
109
|
+
Hosted CI passed on Linux x64/ARM64, macOS and Windows, with integration against
|
|
110
|
+
Signal K 2.34.0 and 2.35.1. The existing sandbox deployment also passed the
|
|
111
|
+
publication, recovery, inventory, permissions and original-content transport
|
|
112
|
+
checks, and the maintainer confirmed acceptance. Production dependency audit
|
|
113
|
+
reported zero vulnerabilities. The release commit is checked again before
|
|
114
|
+
publication; inspect its hosted checks rather than assuming earlier results
|
|
115
|
+
apply to later changes.
|
|
116
|
+
|
|
117
|
+
## Historical preparation checks (2026-10-21)
|
|
103
118
|
|
|
104
119
|
The 27 local tests, formatting and TypeScript checks passed. `npm publish --dry-run`
|
|
105
120
|
completed without publishing; the inspected archive contains 24 files, including
|
package/dist/http.js
CHANGED
|
@@ -106,15 +106,15 @@ function registerRoutes(router, getStore, report) {
|
|
|
106
106
|
read.get('/datasets', wrap((req, res) => json(req, res, (0, discovery_1.listResources)(getStore(), req.query))));
|
|
107
107
|
read.get('/datasets/:id', wrap((req, res) => {
|
|
108
108
|
const store = getStore();
|
|
109
|
-
json(req, res, store.resource(store.get(req.params.id)));
|
|
109
|
+
json(req, res, store.resource(store.get((0, validation_1.identifier)(req.params.id))));
|
|
110
110
|
}));
|
|
111
111
|
read.get('/datasets/:id/versions/:version', wrap((req, res) => {
|
|
112
112
|
const store = getStore();
|
|
113
|
-
json(req, res, store.resource(store.get(req.params.id, req.params.version)));
|
|
113
|
+
json(req, res, store.resource(store.get((0, validation_1.identifier)(req.params.id), (0, validation_1.identifier)(req.params.version))));
|
|
114
114
|
}));
|
|
115
115
|
read.get('/datasets/:id/versions/:version/index', wrap(async (req, res) => {
|
|
116
116
|
const store = getStore();
|
|
117
|
-
const { record, inventory } = await store.readInventory(req.params.id, req.params.version);
|
|
117
|
+
const { record, inventory } = await store.readInventory((0, validation_1.identifier)(req.params.id), (0, validation_1.identifier)(req.params.version));
|
|
118
118
|
json(req, res, {
|
|
119
119
|
datasetId: record.id,
|
|
120
120
|
version: record.version,
|
|
@@ -128,7 +128,7 @@ function registerRoutes(router, getStore, report) {
|
|
|
128
128
|
});
|
|
129
129
|
}));
|
|
130
130
|
read.get('/datasets/:id/versions/:version/assets/:asset', wrap(async (req, res) => {
|
|
131
|
-
const opened = await getStore().openAsset(req.params.id, req.params.version, req.params.asset);
|
|
131
|
+
const opened = await getStore().openAsset((0, validation_1.identifier)(req.params.id), (0, validation_1.identifier)(req.params.version), (0, validation_1.identifier)(req.params.asset));
|
|
132
132
|
try {
|
|
133
133
|
const { asset, handle } = opened;
|
|
134
134
|
const tag = `"sha256-${asset.sha256}"`;
|
|
@@ -171,7 +171,7 @@ function registerRoutes(router, getStore, report) {
|
|
|
171
171
|
}
|
|
172
172
|
}));
|
|
173
173
|
const begin = wrap(async (req, res) => {
|
|
174
|
-
const stage = await getStore().begin(req.body, req.params.id);
|
|
174
|
+
const stage = await getStore().begin(req.body, req.params.id === undefined ? undefined : (0, validation_1.identifier)(req.params.id));
|
|
175
175
|
res.status(201).location(`${types_1.API_ROOT}/publications/${stage.id}`).json(stage);
|
|
176
176
|
});
|
|
177
177
|
write.post('/publications', body, begin);
|
|
@@ -182,14 +182,14 @@ function registerRoutes(router, getStore, report) {
|
|
|
182
182
|
write.get('/publications/:publication', wrap((req, res) => {
|
|
183
183
|
res
|
|
184
184
|
.set('Cache-Control', 'no-store')
|
|
185
|
-
.json(getStore().publication(req.params.publication));
|
|
185
|
+
.json(getStore().publication((0, validation_1.identifier)(req.params.publication)));
|
|
186
186
|
}));
|
|
187
187
|
write.put('/publications/:publication/assets/:asset', wrap(async (req, res) => {
|
|
188
188
|
if (req.is('application/octet-stream') !== 'application/octet-stream' ||
|
|
189
189
|
(req.get('Content-Encoding') &&
|
|
190
190
|
req.get('Content-Encoding') !== 'identity'))
|
|
191
191
|
throw new validation_1.ApiError(415, 'RAW_UPLOAD_REQUIRED', 'Upload unencoded application/octet-stream bytes');
|
|
192
|
-
const stage = getStore().publication(req.params.publication);
|
|
192
|
+
const stage = getStore().publication((0, validation_1.identifier)(req.params.publication));
|
|
193
193
|
const expected = stage.declaration.assets.find((a) => a.id === req.params.asset);
|
|
194
194
|
if (!expected)
|
|
195
195
|
throw new validation_1.ApiError(404, 'ASSET_NOT_DECLARED', 'Asset was not declared');
|
|
@@ -201,23 +201,23 @@ function registerRoutes(router, getStore, report) {
|
|
|
201
201
|
throw new validation_1.ApiError(413, 'SIZE_LIMIT_EXCEEDED', 'Content-Length exceeds configured asset size limit');
|
|
202
202
|
}
|
|
203
203
|
req.setTimeout(120000, () => req.destroy(new Error('Upload timed out')));
|
|
204
|
-
const asset = await getStore().upload(req.params.publication, req.params.asset, req);
|
|
204
|
+
const asset = await getStore().upload((0, validation_1.identifier)(req.params.publication), (0, validation_1.identifier)(req.params.asset), req);
|
|
205
205
|
res.status(201).json(asset);
|
|
206
206
|
}));
|
|
207
207
|
write.post('/publications/:publication/commit', wrap(async (req, res) => {
|
|
208
208
|
const store = getStore();
|
|
209
|
-
const record = await store.commit(req.params.publication);
|
|
209
|
+
const record = await store.commit((0, validation_1.identifier)(req.params.publication));
|
|
210
210
|
res
|
|
211
211
|
.status(201)
|
|
212
212
|
.location(`${types_1.API_ROOT}/datasets/${record.id}/versions/${record.version}`)
|
|
213
213
|
.json(store.resource(record));
|
|
214
214
|
}));
|
|
215
215
|
write.delete('/publications/:publication', wrap(async (req, res) => {
|
|
216
|
-
await getStore().abort(req.params.publication);
|
|
216
|
+
await getStore().abort((0, validation_1.identifier)(req.params.publication));
|
|
217
217
|
res.status(204).end();
|
|
218
218
|
}));
|
|
219
219
|
write.delete('/datasets/:id', wrap(async (req, res) => {
|
|
220
|
-
await getStore().remove(req.params.id);
|
|
220
|
+
await getStore().remove((0, validation_1.identifier)(req.params.id));
|
|
221
221
|
res.status(204).end();
|
|
222
222
|
}));
|
|
223
223
|
// Verified inventory is an explicit assertion from an administrator/indexer,
|
|
@@ -227,7 +227,7 @@ function registerRoutes(router, getStore, report) {
|
|
|
227
227
|
if (!value)
|
|
228
228
|
throw new validation_1.ApiError(428, 'REVISION_REQUIRED', 'If-Match with the current dataset ETag is required');
|
|
229
229
|
const store = getStore();
|
|
230
|
-
const record = store.get(req.params.id, req.params.version);
|
|
230
|
+
const record = store.get((0, validation_1.identifier)(req.params.id), (0, validation_1.identifier)(req.params.version));
|
|
231
231
|
if (value !== etag(store.resource(record)))
|
|
232
232
|
throw new validation_1.ApiError(412, 'REVISION_CHANGED', 'Dataset metadata has changed');
|
|
233
233
|
const updated = await store.enrich(record.id, record.version, req.body, record.metadataRevision);
|
package/dist/openapi.js
CHANGED
|
@@ -124,7 +124,7 @@ exports.openApi = {
|
|
|
124
124
|
openapi: '3.0.3',
|
|
125
125
|
info: {
|
|
126
126
|
title: 'Forecast Dataset Provider',
|
|
127
|
-
version: '0.
|
|
127
|
+
version: '0.2.0',
|
|
128
128
|
description: 'Experimental contract 0.1. Original-content catalogue; no GRIB/Zarr decoding or subset processor is bundled.'
|
|
129
129
|
},
|
|
130
130
|
servers: [{ url: types_1.API_ROOT }],
|
|
@@ -54,3 +54,24 @@ Update/rollback note: committed contract 0.1 datasets retain the same storage an
|
|
|
54
54
|
Plugin Config now exposes `maxAssetMiB: 2048` and `maxDatasetMiB: 8192`, with explicit MiB labels (1,048,576 bytes). Effective default byte limits and byte-based HTTP/index/progress semantics are unchanged. No configuration migration or compatibility aliases are included for the unpublished prototype. Only this sandbox plugin's settings were manually adjusted through its configuration API.
|
|
55
55
|
|
|
56
56
|
The updated 0.1.2 archive was installed with a backup at `local-packages/forecast-provider-before-update-2026-10-21T18-45-50-384Z`. The actual `/plugins` schema and saved settings were verified. The 45 other plugin configurations, enable/debug flags and both existing dataset content versions remained unchanged. The provider's streaming/recovery/transport integration helper passed again and removed its synthetic data. Local checks pass 27 tests, including unit conversion, invalid configuration and enforcement of a MiB-configured limit during an upload.
|
|
57
|
+
|
|
58
|
+
## Dependency migration verification — 2026-10-31
|
|
59
|
+
|
|
60
|
+
The provider now requires Node.js 22+. Local checks passed on Node.js 22.23.4
|
|
61
|
+
and 24.22.2 with TypeScript 7, Express 5 and the updated OpenAPI validator.
|
|
62
|
+
The 29 top-level tests include a second execution of the HTTP suite using an
|
|
63
|
+
Express 4 host with the provider's Express 5 JSON middleware.
|
|
64
|
+
|
|
65
|
+
A separate Signal K 2.35.1 instance used Node.js 24.22.1 and the server's
|
|
66
|
+
Express 4.22.3. It ran on a free port with its own temporary configuration,
|
|
67
|
+
catalogue and test accounts, using the packed provider with production-only
|
|
68
|
+
dependencies. Fresh-install activation, reader/write permissions, anonymous
|
|
69
|
+
access rejection, unknown-size streaming, commit, core Resource API discovery
|
|
70
|
+
and filtering, Range, HEAD, ETag and deletion passed. The instance was stopped
|
|
71
|
+
and its temporary configuration removed. The existing sandbox configuration,
|
|
72
|
+
installed plugins and datasets were not changed.
|
|
73
|
+
|
|
74
|
+
The targeted sandbox install/update helpers require Express 5 to be available
|
|
75
|
+
for the provider. They deliberately do not resolve dependencies; do not use
|
|
76
|
+
them to reuse the server's Express 4 installation during this migration. Use
|
|
77
|
+
a normal package installation that installs the provider's dependencies.
|
package/docs/upgrading.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# Coordinated forecast-plugin upgrade
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The first coordinated stable migration used: Forecast Dataset Provider **0.1.0**, GRIB Downloader **0.4.0**,
|
|
4
4
|
GRIB Weather Provider **0.7.0**, Forecast Dataset Browser **0.1.0**.
|
|
5
5
|
The catalogue HTTP contract remains **0.1**; it is not an adopted Signal K standard.
|
|
6
6
|
|
|
7
7
|
## Requirements and upgrade from local-file releases
|
|
8
8
|
|
|
9
|
+
Provider 0.2.0 requires Node.js 22+ and preserves the same catalogue storage and
|
|
10
|
+
HTTP contract. It does not require a dataset or configuration migration. Use the
|
|
11
|
+
latest compatible releases when applying the historical migration steps below.
|
|
12
|
+
|
|
9
13
|
The complete chain requires **Node.js 24+** (the Weather Provider's ecCodes
|
|
10
14
|
WebAssembly decoder needs memory64) and **Signal K 2.34+ within 2.x**.
|
|
11
15
|
Downloader 0.3.x and Weather Provider 0.6.x shared local files. Upgrade both
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "signalk-forecast-dataset-provider",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Signal K Resource Provider for immutable forecast datasets, original assets and verified inventories",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
"url": "https://github.com/macjl/signalk-forecast-dataset-provider/issues"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
|
-
"node": ">=
|
|
49
|
+
"node": ">=22"
|
|
50
50
|
},
|
|
51
|
-
"signalk-plugin-enabled-by-default":
|
|
51
|
+
"signalk-plugin-enabled-by-default": true,
|
|
52
52
|
"signalk": {
|
|
53
53
|
"displayName": "Forecast Dataset Provider",
|
|
54
54
|
"appIcon": "icon.svg"
|
|
@@ -57,14 +57,15 @@
|
|
|
57
57
|
"@signalk/server-api": ">=2.34.0 <3"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@apidevtools/swagger-parser": "^
|
|
61
|
-
"@signalk/server-api": "2.
|
|
62
|
-
"@types/express": "^
|
|
63
|
-
"@types/node": "^20.
|
|
60
|
+
"@apidevtools/swagger-parser": "^13.1.0",
|
|
61
|
+
"@signalk/server-api": "2.35.0",
|
|
62
|
+
"@types/express": "^5.0.6",
|
|
63
|
+
"@types/node": "^22.20.5",
|
|
64
|
+
"express4": "npm:express@^4.22.3",
|
|
64
65
|
"prettier": "^3.6.2",
|
|
65
|
-
"typescript": "
|
|
66
|
+
"typescript": "7.0.2"
|
|
66
67
|
},
|
|
67
68
|
"dependencies": {
|
|
68
|
-
"express": "^
|
|
69
|
+
"express": "^5.3.0"
|
|
69
70
|
}
|
|
70
71
|
}
|