signalk-charts-provider-simple 2.8.1 → 2.10.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 +5 -3
- package/dist/catalog/build-merged-catalog.d.ts +2 -7
- package/dist/catalog/build-merged-catalog.js +185 -188
- package/dist/catalog/build-merged-catalog.js.map +1 -1
- package/dist/catalog/chartcatalogs-xml.d.ts +1 -6
- package/dist/catalog/chartcatalogs-xml.js +39 -47
- package/dist/catalog/chartcatalogs-xml.js.map +1 -1
- package/dist/catalog/merged-catalog-schema.d.ts +245 -244
- package/dist/catalog/merged-catalog-schema.d.ts.map +1 -1
- package/dist/catalog/merged-catalog-schema.js +327 -300
- package/dist/catalog/merged-catalog-schema.js.map +1 -1
- package/dist/charts-loader.d.ts +4 -2
- package/dist/charts-loader.d.ts.map +1 -1
- package/dist/charts-loader.js +337 -388
- package/dist/charts-loader.js.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3368 -4015
- package/dist/index.js.map +1 -1
- package/dist/package.js +132 -0
- package/dist/package.js.map +1 -0
- package/dist/types.d.ts +16 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -1
- package/dist/utils/catalog-manager.d.ts +2 -2
- package/dist/utils/catalog-manager.js +562 -688
- package/dist/utils/catalog-manager.js.map +1 -1
- package/dist/utils/catalog-schemas.d.ts +49 -55
- package/dist/utils/catalog-schemas.d.ts.map +1 -1
- package/dist/utils/catalog-schemas.js +53 -63
- package/dist/utils/catalog-schemas.js.map +1 -1
- package/dist/utils/catalog-title.js +63 -79
- package/dist/utils/catalog-title.js.map +1 -1
- package/dist/utils/chart-state.d.ts +1 -1
- package/dist/utils/chart-state.js +33 -33
- package/dist/utils/chart-state.js.map +1 -1
- package/dist/utils/concurrency.js +35 -44
- package/dist/utils/concurrency.js.map +1 -1
- package/dist/utils/container-fs.js +62 -66
- package/dist/utils/container-fs.js.map +1 -1
- package/dist/utils/container-images.js +24 -20
- package/dist/utils/container-images.js.map +1 -1
- package/dist/utils/container-jobs.d.ts +1 -20
- package/dist/utils/container-jobs.js +99 -102
- package/dist/utils/container-jobs.js.map +1 -1
- package/dist/utils/container-manager.js +54 -86
- package/dist/utils/container-manager.js.map +1 -1
- package/dist/utils/conversion-log.js +21 -17
- package/dist/utils/conversion-log.js.map +1 -1
- package/dist/utils/conversion-progress.d.ts +1 -1
- package/dist/utils/conversion-progress.js +15 -19
- package/dist/utils/conversion-progress.js.map +1 -1
- package/dist/utils/custom-catalog-download.js +96 -102
- package/dist/utils/custom-catalog-download.js.map +1 -1
- package/dist/utils/custom-catalog-manager.d.ts +2 -18
- package/dist/utils/custom-catalog-manager.js +282 -373
- package/dist/utils/custom-catalog-manager.js.map +1 -1
- package/dist/utils/download-manager.d.ts +1 -1
- package/dist/utils/download-manager.d.ts.map +1 -1
- package/dist/utils/download-manager.js +436 -614
- package/dist/utils/download-manager.js.map +1 -1
- package/dist/utils/file-scanner.d.ts +1 -1
- package/dist/utils/file-scanner.d.ts.map +1 -1
- package/dist/utils/file-scanner.js +131 -133
- package/dist/utils/file-scanner.js.map +1 -1
- package/dist/utils/folder-state.d.ts +1 -1
- package/dist/utils/folder-state.js +59 -80
- package/dist/utils/folder-state.js.map +1 -1
- package/dist/utils/job-failure.js +29 -35
- package/dist/utils/job-failure.js.map +1 -1
- package/dist/utils/mbtiles-metadata.js +473 -515
- package/dist/utils/mbtiles-metadata.js.map +1 -1
- package/dist/utils/mbtiles-reader.d.ts +2 -1
- package/dist/utils/mbtiles-reader.d.ts.map +1 -1
- package/dist/utils/mbtiles-reader.js +280 -407
- package/dist/utils/mbtiles-reader.js.map +1 -1
- package/dist/utils/noaa-enc-footprints.d.ts +1 -20
- package/dist/utils/noaa-enc-footprints.js +285 -347
- package/dist/utils/noaa-enc-footprints.js.map +1 -1
- package/dist/utils/online-area-download.d.ts +56 -0
- package/dist/utils/online-area-download.d.ts.map +1 -0
- package/dist/utils/online-area-download.js +159 -0
- package/dist/utils/online-area-download.js.map +1 -0
- package/dist/utils/online-charts.d.ts +63 -21
- package/dist/utils/online-charts.d.ts.map +1 -1
- package/dist/utils/online-charts.js +336 -188
- package/dist/utils/online-charts.js.map +1 -1
- package/dist/utils/online-tile-cache.d.ts +139 -0
- package/dist/utils/online-tile-cache.d.ts.map +1 -0
- package/dist/utils/online-tile-cache.js +517 -0
- package/dist/utils/online-tile-cache.js.map +1 -0
- package/dist/utils/path-marker.js +37 -55
- package/dist/utils/path-marker.js.map +1 -1
- package/dist/utils/path-safety.js +118 -117
- package/dist/utils/path-safety.js.map +1 -1
- package/dist/utils/plugin-config-schema.d.ts +7 -6
- package/dist/utils/plugin-config-schema.d.ts.map +1 -1
- package/dist/utils/plugin-config-schema.js +23 -30
- package/dist/utils/plugin-config-schema.js.map +1 -1
- package/dist/utils/pmtiles-reader.d.ts +46 -0
- package/dist/utils/pmtiles-reader.d.ts.map +1 -0
- package/dist/utils/pmtiles-reader.js +187 -0
- package/dist/utils/pmtiles-reader.js.map +1 -0
- package/dist/utils/quarantine.js +199 -246
- package/dist/utils/quarantine.js.map +1 -1
- package/dist/utils/raster-compose.js +123 -110
- package/dist/utils/raster-compose.js.map +1 -1
- package/dist/utils/raster-overzoom-worker.d.ts +1 -1
- package/dist/utils/raster-overzoom-worker.js +18 -13
- package/dist/utils/raster-overzoom-worker.js.map +1 -1
- package/dist/utils/raster-overzoom.d.ts +2 -20
- package/dist/utils/raster-overzoom.d.ts.map +1 -1
- package/dist/utils/raster-overzoom.js +163 -142
- package/dist/utils/raster-overzoom.js.map +1 -1
- package/dist/utils/raster-tile-cache.d.ts +31 -0
- package/dist/utils/raster-tile-cache.d.ts.map +1 -0
- package/dist/utils/raster-tile-cache.js +288 -0
- package/dist/utils/raster-tile-cache.js.map +1 -0
- package/dist/utils/rest-validation.d.ts +3 -18
- package/dist/utils/rest-validation.d.ts.map +1 -1
- package/dist/utils/rest-validation.js +33 -50
- package/dist/utils/rest-validation.js.map +1 -1
- package/dist/utils/rnc-converter.d.ts +1 -1
- package/dist/utils/rnc-converter.js +373 -468
- package/dist/utils/rnc-converter.js.map +1 -1
- package/dist/utils/s57-band.js +148 -153
- package/dist/utils/s57-band.js.map +1 -1
- package/dist/utils/s57-converter.d.ts +2 -2
- package/dist/utils/s57-converter.js +1211 -1635
- package/dist/utils/s57-converter.js.map +1 -1
- package/dist/utils/serial-queue.d.ts +6 -0
- package/dist/utils/serial-queue.d.ts.map +1 -0
- package/dist/utils/serial-queue.js +17 -0
- package/dist/utils/serial-queue.js.map +1 -0
- package/dist/utils/tile-overzoom.d.ts +1 -25
- package/dist/utils/tile-overzoom.js +225 -298
- package/dist/utils/tile-overzoom.js.map +1 -1
- package/dist/utils/time-dimension.js +147 -209
- package/dist/utils/time-dimension.js.map +1 -1
- package/dist/utils/time-poller.d.ts +1 -13
- package/dist/utils/time-poller.js +136 -164
- package/dist/utils/time-poller.js.map +1 -1
- package/dist/utils/vessel-position.js +17 -19
- package/dist/utils/vessel-position.js.map +1 -1
- package/dist/utils/zip-extract.js +169 -193
- package/dist/utils/zip-extract.js.map +1 -1
- package/package.json +16 -11
- package/public/css/chart-catalog.css +5 -0
- package/public/css/manage-charts.css +85 -0
- package/public/index.html +2 -1
- package/public/js/chart-catalog.js +3 -1
- package/public/js/chart-catalog.ts +5 -1
- package/public/js/download-simple.js +184 -1
- package/public/js/download-simple.ts +196 -1
- package/public/js/globals.d.ts +15 -0
- package/public/js/main.js +5 -0
- package/public/js/main.ts +4 -0
- package/public/js/manage-charts-enhanced.js +58 -16
- package/public/js/manage-charts-enhanced.ts +89 -16
- package/public/js/online-area-download.js +347 -0
- package/public/js/online-area-download.ts +413 -0
- package/dist/types.js.map +0 -1
- package/public/index.js +0 -5
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A lightweight Signal K server plugin for managing local nautical charts, written
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Local Chart Management**: MBTiles (raster and vector), with folder organization and enable/disable toggles
|
|
7
|
+
- **Local Chart Management**: MBTiles and PMTiles (raster and vector), with folder organization and enable/disable toggles. PMTiles archives are downloaded, uploaded or placed in the chart folder and served as they are; their metadata is read-only
|
|
8
8
|
- **Folder Groups**: Enable/disable whole chart folders at once (OpenCPN-style groups, including nested folders); clients like Freeboard-SK see the change live via `resources.charts` deltas
|
|
9
9
|
- **Download Manager**: Built-in download queue with progress tracking and ZIP extraction
|
|
10
10
|
- **Chart Catalog**: Browse and download charts from [chartcatalogs.github.io](https://chartcatalogs.github.io/) with automatic update notifications
|
|
@@ -79,8 +79,9 @@ The interface provides five tabs:
|
|
|
79
79
|
|
|
80
80
|
2. **Download from URL**:
|
|
81
81
|
- Download charts directly from any URL
|
|
82
|
-
- Supports `.mbtiles` and `.zip` archives
|
|
82
|
+
- Supports `.mbtiles` and `.pmtiles` files, and `.zip` archives of `.mbtiles`
|
|
83
83
|
- Download queue with progress tracking
|
|
84
|
+
- Add an online chart by URL: a `{z}/{x}/{y}` tile service (also `{-y}`, `{z-N}`, `{bbox}` and `{bbox_3857}`) or a WMS the Chart Catalog does not list. Tick **Keep tiles for offline use** only when the provider's terms allow it; the chart is then kept offline like a catalog chart
|
|
84
85
|
|
|
85
86
|
3. **Convert**:
|
|
86
87
|
- Upload ZIP files containing S-57 ENC (.000) or BSB raster (.kap) charts
|
|
@@ -90,7 +91,7 @@ The interface provides five tabs:
|
|
|
90
91
|
|
|
91
92
|
4. **Chart Catalog**:
|
|
92
93
|
- Downloadable charts from [chartcatalogs.github.io](https://chartcatalogs.github.io/) and a curated list of online charts, in one list, sectioned by what the charts show, with **Near me** narrowing each section to where you are
|
|
93
|
-
- **Online charts** (weather radar and satellite, online nautical charts, depths, base maps): press **Add** and the chart appears in Manage Charts and in your chart plotter.
|
|
94
|
+
- **Online charts** (weather radar and satellite, online nautical charts, depths, base maps): press **Add** and the chart appears in Manage Charts and in your chart plotter. Most stream from their provider and need an internet connection to display. Charts whose license allows it (NOAA, CHS, Kartverket, Traficom, GEBCO, EMODnet and USGS imagery) are marked **Kept offline**: they are served through your server, which keeps every tile it fetches, so the areas you have already viewed keep working without internet. To prepare for a passage, press **Offline area** on such a chart in Manage Charts, frame the area on the map, choose the zoom levels and download it ahead of time. Online charts go into an "Online Charts" folder by default, so you can switch them all off at once when you're offline
|
|
94
95
|
- Live weather layers (radar, satellite, forecasts) keep their recent images or forecast steps up to date, so chart plotters with a time slider (Freeboard-SK) can play them back
|
|
95
96
|
- One-click download for MBTiles charts (NOAA)
|
|
96
97
|
- Download & convert for S-57 ENC, BSB raster, Pilot Charts, and basemaps
|
|
@@ -111,6 +112,7 @@ The interface provides five tabs:
|
|
|
111
112
|
| Format | Source | Conversion | Output |
|
|
112
113
|
|--------|--------|-----------|--------|
|
|
113
114
|
| **MBTiles** | Direct download | None needed | Raster or vector tiles |
|
|
115
|
+
| **PMTiles** | Direct download, upload or the chart folder | None needed | Raster or vector tiles |
|
|
114
116
|
| **S-57 ENC** (.000) | IENC catalogs, custom upload | GDAL + tippecanoe (containerized) | Vector MBTiles with S-52 styling |
|
|
115
117
|
| **BSB Raster** (.kap) | RNC catalogs, custom upload | GDAL (containerized) | Raster MBTiles (PNG) |
|
|
116
118
|
| **Pilot Charts** (.kap in .tar.xz) | Pilot catalog | GDAL (containerized) | Raster MBTiles (PNG) |
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* in `publish-catalog-cli.ts`; everything here is deterministic so it can be
|
|
4
|
-
* unit-tested and so an unchanged input always yields the same content hash.
|
|
5
|
-
*/
|
|
6
|
-
import type { ParsedChartcatalogsXml } from './chartcatalogs-xml.js';
|
|
7
|
-
import { type ChartcatalogsIndex, type MergedCatalog, type MergedChartcatalogsCatalog, type OnlineChartsSourceFile } from './merged-catalog-schema.js';
|
|
1
|
+
import { ParsedChartcatalogsXml } from './chartcatalogs-xml.js';
|
|
2
|
+
import { ChartcatalogsIndex, MergedCatalog, MergedChartcatalogsCatalog, OnlineChartsSourceFile } from './merged-catalog-schema.js';
|
|
8
3
|
export declare const CHARTCATALOGS_HOMEPAGE = "https://chartcatalogs.github.io/";
|
|
9
4
|
export declare const CHARTCATALOGS_ISSUES = "https://github.com/chartcatalogs/catalogs/issues";
|
|
10
5
|
export interface ChartcatalogsFile {
|
|
@@ -1,210 +1,207 @@
|
|
|
1
|
+
import { MergedCatalogSchema, MergedChartcatalogsCatalogSchema, checkChartcatalogsIndex, checkOnlineChartEntry, formatForChartType, schemaErrors } from "./merged-catalog-schema.js";
|
|
2
|
+
import { Value } from "typebox/value";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
//#region src/catalog/build-merged-catalog.ts
|
|
1
5
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { MERGED_CATALOG_SCHEMA_VERSION, MergedCatalogSchema, MergedChartcatalogsCatalogSchema, checkChartcatalogsIndex, checkOnlineChartEntry, formatForChartType, schemaErrors } from './merged-catalog-schema.js';
|
|
9
|
-
export const CHARTCATALOGS_HOMEPAGE = 'https://chartcatalogs.github.io/';
|
|
10
|
-
export const CHARTCATALOGS_ISSUES = 'https://github.com/chartcatalogs/catalogs/issues';
|
|
6
|
+
* Pure assembly of the merged chart catalog. File and network access live
|
|
7
|
+
* in `publish-catalog-cli.ts`; everything here is deterministic so it can be
|
|
8
|
+
* unit-tested and so an unchanged input always yields the same content hash.
|
|
9
|
+
*/
|
|
10
|
+
var CHARTCATALOGS_HOMEPAGE = "https://chartcatalogs.github.io/";
|
|
11
|
+
var CHARTCATALOGS_ISSUES = "https://github.com/chartcatalogs/catalogs/issues";
|
|
11
12
|
function compareStrings(a, b) {
|
|
12
|
-
|
|
13
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
13
14
|
}
|
|
14
15
|
function mergeChartcatalogsFile(file, parsed, index, droppedCharts) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
16
|
+
const id = file.replace(/\.xml$/i, "");
|
|
17
|
+
const charts = parsed.charts.filter((chart) => {
|
|
18
|
+
const ok = /^https?:\/\//.test(chart.zipfile_location);
|
|
19
|
+
if (!ok) droppedCharts.push(`${file} ${chart.number}`);
|
|
20
|
+
return ok;
|
|
21
|
+
});
|
|
22
|
+
const base = {
|
|
23
|
+
id,
|
|
24
|
+
file,
|
|
25
|
+
use: "download",
|
|
26
|
+
header: parsed.header,
|
|
27
|
+
charts
|
|
28
|
+
};
|
|
29
|
+
const facets = index[file];
|
|
30
|
+
if (facets) return {
|
|
31
|
+
...base,
|
|
32
|
+
...facets,
|
|
33
|
+
indexed: true
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
...base,
|
|
37
|
+
label: parsed.header.title || id,
|
|
38
|
+
category: "navigation",
|
|
39
|
+
indexed: false
|
|
40
|
+
};
|
|
33
41
|
}
|
|
34
42
|
function mergeOnlineCharts(online) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
...entry,
|
|
49
|
-
use: 'stream',
|
|
50
|
-
format: formatForChartType(entry.chart.type)
|
|
51
|
-
}));
|
|
43
|
+
const problems = [];
|
|
44
|
+
const seen = /* @__PURE__ */ new Set();
|
|
45
|
+
for (const entry of online.charts) {
|
|
46
|
+
if (seen.has(entry.id)) problems.push(`${entry.id}: duplicate id`);
|
|
47
|
+
seen.add(entry.id);
|
|
48
|
+
problems.push(...checkOnlineChartEntry(entry));
|
|
49
|
+
}
|
|
50
|
+
if (problems.length > 0) throw new Error(`Invalid online chart entries:\n ${problems.join("\n ")}`);
|
|
51
|
+
return online.charts.map((entry) => ({
|
|
52
|
+
...entry,
|
|
53
|
+
use: "stream",
|
|
54
|
+
format: formatForChartType(entry.chart.type)
|
|
55
|
+
}));
|
|
52
56
|
}
|
|
53
57
|
/** JSON with object keys sorted, so key order never changes the hash. */
|
|
54
58
|
function canonicalJson(value) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (value !== null && typeof value === 'object') {
|
|
59
|
-
const entries = Object.entries(value)
|
|
60
|
-
.filter(([, v]) => v !== undefined)
|
|
61
|
-
.sort(([a], [b]) => compareStrings(a, b));
|
|
62
|
-
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;
|
|
63
|
-
}
|
|
64
|
-
return JSON.stringify(value);
|
|
59
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
60
|
+
if (value !== null && typeof value === "object") return `{${Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => compareStrings(a, b)).map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(",")}}`;
|
|
61
|
+
return JSON.stringify(value);
|
|
65
62
|
}
|
|
66
63
|
/**
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
64
|
+
* Hash of what a user would actually see. chartcatalogs re-commits every
|
|
65
|
+
* hour just to refresh its header dates, so those dates, the upstream
|
|
66
|
+
* commit id and our own `generatedAt` are excluded by name; anything else,
|
|
67
|
+
* including fields added later, is hashed.
|
|
68
|
+
*/
|
|
69
|
+
function computeContentHash(catalog) {
|
|
70
|
+
const { generatedAt: _generatedAt, ...content } = catalog;
|
|
71
|
+
const { commit: _commit, ...chartcatalogsSource } = content.sources.chartcatalogs;
|
|
72
|
+
const hashed = {
|
|
73
|
+
...content,
|
|
74
|
+
sources: {
|
|
75
|
+
...content.sources,
|
|
76
|
+
chartcatalogs: chartcatalogsSource
|
|
77
|
+
},
|
|
78
|
+
chartcatalogs: content.chartcatalogs.map((c) => {
|
|
79
|
+
const { dateCreated: _created, dateValid: _valid, ...header } = c.header;
|
|
80
|
+
return {
|
|
81
|
+
...c,
|
|
82
|
+
header
|
|
83
|
+
};
|
|
84
|
+
})
|
|
85
|
+
};
|
|
86
|
+
return createHash("sha256").update(canonicalJson(hashed)).digest("hex");
|
|
84
87
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
.sort(compareStrings)
|
|
145
|
-
};
|
|
88
|
+
function buildMergedCatalog(inputs) {
|
|
89
|
+
const indexProblems = checkChartcatalogsIndex(inputs.index);
|
|
90
|
+
if (indexProblems.length > 0) throw new Error(`Invalid chartcatalogs index:\n ${indexProblems.join("\n ")}`);
|
|
91
|
+
const files = [...inputs.chartcatalogs].sort((a, b) => compareStrings(a.file, b.file));
|
|
92
|
+
const carriedForward = [];
|
|
93
|
+
const skipped = [];
|
|
94
|
+
const droppedCharts = [];
|
|
95
|
+
const chartcatalogs = [];
|
|
96
|
+
for (const { file, parsed } of files) {
|
|
97
|
+
const previous = inputs.previous?.get(file);
|
|
98
|
+
if (previous && (!parsed || parsed.charts.length === 0)) {
|
|
99
|
+
const facets = inputs.index[file];
|
|
100
|
+
chartcatalogs.push(facets ? {
|
|
101
|
+
...previous,
|
|
102
|
+
...facets,
|
|
103
|
+
indexed: true
|
|
104
|
+
} : previous);
|
|
105
|
+
carriedForward.push(file);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (!parsed) {
|
|
109
|
+
skipped.push(file);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
chartcatalogs.push(mergeChartcatalogsFile(file, parsed, inputs.index, droppedCharts));
|
|
113
|
+
}
|
|
114
|
+
const fileNames = new Set(files.map((f) => f.file));
|
|
115
|
+
const withoutHash = {
|
|
116
|
+
schemaVersion: 1,
|
|
117
|
+
generatedAt: inputs.now.toISOString(),
|
|
118
|
+
sources: {
|
|
119
|
+
chartcatalogs: {
|
|
120
|
+
homepage: CHARTCATALOGS_HOMEPAGE,
|
|
121
|
+
issues: CHARTCATALOGS_ISSUES,
|
|
122
|
+
license: "CC0-1.0",
|
|
123
|
+
...inputs.chartcatalogsCommit ? { commit: inputs.chartcatalogsCommit } : {}
|
|
124
|
+
},
|
|
125
|
+
online: {
|
|
126
|
+
homepage: `https://github.com/${inputs.repo}`,
|
|
127
|
+
issues: `https://github.com/${inputs.repo}/issues/new?template=catalog-problem.yml`
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
chartcatalogs,
|
|
131
|
+
online: mergeOnlineCharts(inputs.online)
|
|
132
|
+
};
|
|
133
|
+
const catalog = {
|
|
134
|
+
...withoutHash,
|
|
135
|
+
contentHash: computeContentHash(withoutHash)
|
|
136
|
+
};
|
|
137
|
+
if (!Value.Check(MergedCatalogSchema, catalog)) throw new Error(`Merged catalog failed validation:\n ${schemaErrors(MergedCatalogSchema, catalog).join("\n ")}`);
|
|
138
|
+
return {
|
|
139
|
+
catalog,
|
|
140
|
+
unindexed: chartcatalogs.filter((c) => !c.indexed).map((c) => c.file),
|
|
141
|
+
staleIndexEntries: Object.keys(inputs.index).filter((f) => !fileNames.has(f)),
|
|
142
|
+
carriedForward,
|
|
143
|
+
skipped,
|
|
144
|
+
droppedCharts,
|
|
145
|
+
removedUpstream: [...inputs.previous?.keys() ?? []].filter((f) => !fileNames.has(f)).sort(compareStrings)
|
|
146
|
+
};
|
|
146
147
|
}
|
|
147
148
|
/**
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
schemaText
|
|
170
|
-
};
|
|
149
|
+
* Read the published catalog loosely. It may predate the current schema
|
|
150
|
+
* (that is exactly when a build change is riskiest), so the counts the drop
|
|
151
|
+
* guard relies on are taken from the raw arrays rather than a full parse.
|
|
152
|
+
*/
|
|
153
|
+
function readPublishedState(raw, schemaText) {
|
|
154
|
+
const obj = raw !== null && typeof raw === "object" ? raw : {};
|
|
155
|
+
const rawCatalogs = Array.isArray(obj.chartcatalogs) ? obj.chartcatalogs : [];
|
|
156
|
+
const chartcatalogs = /* @__PURE__ */ new Map();
|
|
157
|
+
let chartCount = 0;
|
|
158
|
+
for (const entry of rawCatalogs) {
|
|
159
|
+
const charts = entry?.charts;
|
|
160
|
+
chartCount += Array.isArray(charts) ? charts.length : 0;
|
|
161
|
+
if (Value.Check(MergedChartcatalogsCatalogSchema, entry)) chartcatalogs.set(entry.file, entry);
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
contentHash: typeof obj.contentHash === "string" ? obj.contentHash : null,
|
|
165
|
+
catalogCount: rawCatalogs.length,
|
|
166
|
+
chartCount,
|
|
167
|
+
chartcatalogs,
|
|
168
|
+
schemaText
|
|
169
|
+
};
|
|
171
170
|
}
|
|
172
171
|
function chartCount(catalog) {
|
|
173
|
-
|
|
172
|
+
return catalog.chartcatalogs.reduce((n, c) => n + c.charts.length, 0);
|
|
174
173
|
}
|
|
175
174
|
/**
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
blockers.push(`chartcatalogs catalogs dropped from ${String(published.catalogCount)} to ${String(after)}`);
|
|
192
|
-
}
|
|
193
|
-
const chartsAfter = chartCount(next);
|
|
194
|
-
if (chartsAfter < Math.floor(published.chartCount * 0.5)) {
|
|
195
|
-
blockers.push(`chartcatalogs charts dropped from ${String(published.chartCount)} to ${String(chartsAfter)}`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return blockers;
|
|
175
|
+
* Reasons not to replace the published catalog with `next`. Per-file
|
|
176
|
+
* breakage is absorbed by carrying entries forward; these thresholds catch
|
|
177
|
+
* whole-source failures such as a truncated clone.
|
|
178
|
+
*/
|
|
179
|
+
function publishBlockers(next, published) {
|
|
180
|
+
const blockers = [];
|
|
181
|
+
if (next.chartcatalogs.length === 0) blockers.push("no chartcatalogs catalogs were read");
|
|
182
|
+
if (next.online.length === 0) blockers.push("the online chart list is empty");
|
|
183
|
+
if (published) {
|
|
184
|
+
const after = next.chartcatalogs.length;
|
|
185
|
+
if (after < Math.floor(published.catalogCount * .8)) blockers.push(`chartcatalogs catalogs dropped from ${String(published.catalogCount)} to ${String(after)}`);
|
|
186
|
+
const chartsAfter = chartCount(next);
|
|
187
|
+
if (chartsAfter < Math.floor(published.chartCount * .5)) blockers.push(`chartcatalogs charts dropped from ${String(published.chartCount)} to ${String(chartsAfter)}`);
|
|
188
|
+
}
|
|
189
|
+
return blockers;
|
|
199
190
|
}
|
|
200
191
|
/**
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
192
|
+
* Whether to deploy. The site also carries the JSON Schema, so a schema
|
|
193
|
+
* change with identical content must still deploy, or third parties would
|
|
194
|
+
* keep validating against a stale schema.
|
|
195
|
+
*/
|
|
196
|
+
function decidePublish(next, published, schemaText, force) {
|
|
197
|
+
const blockers = publishBlockers(next, published);
|
|
198
|
+
const changed = !published || published.contentHash !== next.contentHash || published.schemaText !== schemaText;
|
|
199
|
+
return {
|
|
200
|
+
publish: force || changed && blockers.length === 0,
|
|
201
|
+
blockers
|
|
202
|
+
};
|
|
209
203
|
}
|
|
204
|
+
//#endregion
|
|
205
|
+
export { CHARTCATALOGS_HOMEPAGE, CHARTCATALOGS_ISSUES, buildMergedCatalog, computeContentHash, decidePublish, publishBlockers, readPublishedState };
|
|
206
|
+
|
|
210
207
|
//# sourceMappingURL=build-merged-catalog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-merged-catalog.js","sourceRoot":"","sources":["../../src/catalog/build-merged-catalog.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"build-merged-catalog.js","names":[],"sources":["../../src/catalog/build-merged-catalog.ts"],"sourcesContent":["/**\n * Pure assembly of the merged chart catalog. File and network access live\n * in `publish-catalog-cli.ts`; everything here is deterministic so it can be\n * unit-tested and so an unchanged input always yields the same content hash.\n */\n\nimport { createHash } from 'node:crypto';\nimport { Value } from 'typebox/value';\nimport type { ParsedChartcatalogsXml } from './chartcatalogs-xml.js';\nimport {\n MERGED_CATALOG_SCHEMA_VERSION,\n MergedCatalogSchema,\n MergedChartcatalogsCatalogSchema,\n checkChartcatalogsIndex,\n checkOnlineChartEntry,\n formatForChartType,\n schemaErrors,\n type ChartcatalogsIndex,\n type MergedCatalog,\n type MergedCatalogChart,\n type MergedChartcatalogsCatalog,\n type MergedOnlineChart,\n type OnlineChartsSourceFile\n} from './merged-catalog-schema.js';\n\nexport const CHARTCATALOGS_HOMEPAGE = 'https://chartcatalogs.github.io/';\nexport const CHARTCATALOGS_ISSUES = 'https://github.com/chartcatalogs/catalogs/issues';\n\nexport interface ChartcatalogsFile {\n file: string;\n /** `null` when the file exists upstream but could not be parsed. */\n parsed: ParsedChartcatalogsXml | null;\n}\n\nexport interface BuildInputs {\n chartcatalogs: ChartcatalogsFile[];\n chartcatalogsCommit?: string;\n index: ChartcatalogsIndex;\n online: OnlineChartsSourceFile;\n /** `owner/name` of the repo publishing the catalog; issue links point here. */\n repo: string;\n /** chartcatalogs entries from the catalog currently published, by file. */\n previous?: Map<string, MergedChartcatalogsCatalog>;\n now: Date;\n}\n\nexport interface BuildResult {\n catalog: MergedCatalog;\n /** chartcatalogs files with no entry in the index yet. */\n unindexed: string[];\n /** Index entries whose file chartcatalogs no longer has. */\n staleIndexEntries: string[];\n /** Files that failed to parse or came back empty, kept from the published catalog. */\n carriedForward: string[];\n /** Files that failed to parse and had nothing published to fall back on. */\n skipped: string[];\n /** Charts dropped because their download location is not an http(s) URL. */\n droppedCharts: string[];\n /**\n * Published catalogs whose file chartcatalogs no longer has. They leave\n * the catalog (the file may really have been withdrawn), but the drop\n * guard tolerates a few, so each one is reported.\n */\n removedUpstream: string[];\n}\n\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\nfunction mergeChartcatalogsFile(\n file: string,\n parsed: ParsedChartcatalogsXml,\n index: ChartcatalogsIndex,\n droppedCharts: string[]\n): MergedChartcatalogsCatalog {\n const id = file.replace(/\\.xml$/i, '');\n // One odd upstream URL must not fail the whole build (the schema would\n // reject the catalog), so bad charts are dropped here and reported.\n const charts: MergedCatalogChart[] = parsed.charts.filter((chart) => {\n const ok = /^https?:\\/\\//.test(chart.zipfile_location);\n if (!ok) {\n droppedCharts.push(`${file} ${chart.number}`);\n }\n return ok;\n });\n const base = { id, file, use: 'download' as const, header: parsed.header, charts };\n const facets = index[file];\n if (facets) {\n return { ...base, ...facets, indexed: true };\n }\n // Nearly every chartcatalogs catalog is a navigation chart set, so that is\n // the safest default until someone adds the file to the index.\n return { ...base, label: parsed.header.title || id, category: 'navigation', indexed: false };\n}\n\nfunction mergeOnlineCharts(online: OnlineChartsSourceFile): MergedOnlineChart[] {\n const problems: string[] = [];\n const seen = new Set<string>();\n for (const entry of online.charts) {\n if (seen.has(entry.id)) {\n problems.push(`${entry.id}: duplicate id`);\n }\n seen.add(entry.id);\n problems.push(...checkOnlineChartEntry(entry));\n }\n if (problems.length > 0) {\n throw new Error(`Invalid online chart entries:\\n ${problems.join('\\n ')}`);\n }\n return online.charts.map((entry) => ({\n ...entry,\n use: 'stream' as const,\n format: formatForChartType(entry.chart.type)\n }));\n}\n\n/** JSON with object keys sorted, so key order never changes the hash. */\nfunction canonicalJson(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map(canonicalJson).join(',')}]`;\n }\n if (value !== null && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => compareStrings(a, b));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;\n }\n return JSON.stringify(value);\n}\n\n/**\n * Hash of what a user would actually see. chartcatalogs re-commits every\n * hour just to refresh its header dates, so those dates, the upstream\n * commit id and our own `generatedAt` are excluded by name; anything else,\n * including fields added later, is hashed.\n */\nexport function computeContentHash(catalog: Omit<MergedCatalog, 'contentHash'>): string {\n const { generatedAt: _generatedAt, ...content } = catalog;\n const { commit: _commit, ...chartcatalogsSource } = content.sources.chartcatalogs;\n const hashed = {\n ...content,\n sources: { ...content.sources, chartcatalogs: chartcatalogsSource },\n chartcatalogs: content.chartcatalogs.map((c) => {\n const { dateCreated: _created, dateValid: _valid, ...header } = c.header;\n return { ...c, header };\n })\n };\n return createHash('sha256').update(canonicalJson(hashed)).digest('hex');\n}\n\nexport function buildMergedCatalog(inputs: BuildInputs): BuildResult {\n const indexProblems = checkChartcatalogsIndex(inputs.index);\n if (indexProblems.length > 0) {\n throw new Error(`Invalid chartcatalogs index:\\n ${indexProblems.join('\\n ')}`);\n }\n\n const files = [...inputs.chartcatalogs].sort((a, b) => compareStrings(a.file, b.file));\n const carriedForward: string[] = [];\n const skipped: string[] = [];\n const droppedCharts: string[] = [];\n const chartcatalogs: MergedChartcatalogsCatalog[] = [];\n\n for (const { file, parsed } of files) {\n const previous = inputs.previous?.get(file);\n // A file that still exists upstream but is broken or suddenly empty is\n // far more likely an upstream mistake than a withdrawn catalog, so keep\n // what users can see today rather than wiping it from every plugin.\n if (previous && (!parsed || parsed.charts.length === 0)) {\n const facets = inputs.index[file];\n chartcatalogs.push(facets ? { ...previous, ...facets, indexed: true } : previous);\n carriedForward.push(file);\n continue;\n }\n if (!parsed) {\n skipped.push(file);\n continue;\n }\n chartcatalogs.push(mergeChartcatalogsFile(file, parsed, inputs.index, droppedCharts));\n }\n\n const fileNames = new Set(files.map((f) => f.file));\n const withoutHash: Omit<MergedCatalog, 'contentHash'> = {\n schemaVersion: MERGED_CATALOG_SCHEMA_VERSION,\n generatedAt: inputs.now.toISOString(),\n sources: {\n chartcatalogs: {\n homepage: CHARTCATALOGS_HOMEPAGE,\n issues: CHARTCATALOGS_ISSUES,\n license: 'CC0-1.0',\n ...(inputs.chartcatalogsCommit ? { commit: inputs.chartcatalogsCommit } : {})\n },\n online: {\n homepage: `https://github.com/${inputs.repo}`,\n issues: `https://github.com/${inputs.repo}/issues/new?template=catalog-problem.yml`\n }\n },\n chartcatalogs,\n online: mergeOnlineCharts(inputs.online)\n };\n\n const catalog: MergedCatalog = { ...withoutHash, contentHash: computeContentHash(withoutHash) };\n if (!Value.Check(MergedCatalogSchema, catalog)) {\n throw new Error(\n `Merged catalog failed validation:\\n ${schemaErrors(MergedCatalogSchema, catalog).join('\\n ')}`\n );\n }\n\n return {\n catalog,\n unindexed: chartcatalogs.filter((c) => !c.indexed).map((c) => c.file),\n staleIndexEntries: Object.keys(inputs.index).filter((f) => !fileNames.has(f)),\n carriedForward,\n skipped,\n droppedCharts,\n removedUpstream: [...(inputs.previous?.keys() ?? [])]\n .filter((f) => !fileNames.has(f))\n .sort(compareStrings)\n };\n}\n\n/** What the drop guard and change detection need to know about the live catalog. */\nexport interface PublishedState {\n contentHash: string | null;\n catalogCount: number;\n chartCount: number;\n /** chartcatalogs entries that still validate, for carrying forward. */\n chartcatalogs: Map<string, MergedChartcatalogsCatalog>;\n /** The published `catalog.schema.json` text, or null if absent. */\n schemaText: string | null;\n}\n\n/**\n * Read the published catalog loosely. It may predate the current schema\n * (that is exactly when a build change is riskiest), so the counts the drop\n * guard relies on are taken from the raw arrays rather than a full parse.\n */\nexport function readPublishedState(raw: unknown, schemaText: string | null): PublishedState {\n const obj = raw !== null && typeof raw === 'object' ? (raw as Record<string, unknown>) : {};\n const rawCatalogs = Array.isArray(obj.chartcatalogs) ? (obj.chartcatalogs as unknown[]) : [];\n const chartcatalogs = new Map<string, MergedChartcatalogsCatalog>();\n let chartCount = 0;\n for (const entry of rawCatalogs) {\n const charts = (entry as { charts?: unknown } | null)?.charts;\n chartCount += Array.isArray(charts) ? charts.length : 0;\n if (Value.Check(MergedChartcatalogsCatalogSchema, entry)) {\n chartcatalogs.set(entry.file, entry);\n }\n }\n return {\n contentHash: typeof obj.contentHash === 'string' ? obj.contentHash : null,\n catalogCount: rawCatalogs.length,\n chartCount,\n chartcatalogs,\n schemaText\n };\n}\n\nfunction chartCount(catalog: MergedCatalog): number {\n return catalog.chartcatalogs.reduce((n, c) => n + c.charts.length, 0);\n}\n\n/**\n * Reasons not to replace the published catalog with `next`. Per-file\n * breakage is absorbed by carrying entries forward; these thresholds catch\n * whole-source failures such as a truncated clone.\n */\nexport function publishBlockers(next: MergedCatalog, published: PublishedState | null): string[] {\n const blockers: string[] = [];\n if (next.chartcatalogs.length === 0) {\n blockers.push('no chartcatalogs catalogs were read');\n }\n if (next.online.length === 0) {\n blockers.push('the online chart list is empty');\n }\n if (published) {\n const after = next.chartcatalogs.length;\n if (after < Math.floor(published.catalogCount * 0.8)) {\n blockers.push(\n `chartcatalogs catalogs dropped from ${String(published.catalogCount)} to ${String(after)}`\n );\n }\n const chartsAfter = chartCount(next);\n if (chartsAfter < Math.floor(published.chartCount * 0.5)) {\n blockers.push(\n `chartcatalogs charts dropped from ${String(published.chartCount)} to ${String(chartsAfter)}`\n );\n }\n }\n return blockers;\n}\n\nexport interface PublishDecision {\n publish: boolean;\n blockers: string[];\n}\n\n/**\n * Whether to deploy. The site also carries the JSON Schema, so a schema\n * change with identical content must still deploy, or third parties would\n * keep validating against a stale schema.\n */\nexport function decidePublish(\n next: MergedCatalog,\n published: PublishedState | null,\n schemaText: string,\n force: boolean\n): PublishDecision {\n const blockers = publishBlockers(next, published);\n const changed =\n !published || published.contentHash !== next.contentHash || published.schemaText !== schemaText;\n return { publish: force || (changed && blockers.length === 0), blockers };\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Parser for chartcatalogs.github.io catalog XML (the NOAA RNC product
|
|
3
|
-
* catalog subset used by OpenCPN). Shared by the plugin's catalog manager
|
|
4
|
-
* and the merged-catalog build so both read the files identically.
|
|
5
|
-
*/
|
|
6
|
-
import type { CatalogChart, CatalogHeader } from '../utils/catalog-schemas.js';
|
|
1
|
+
import { CatalogChart, CatalogHeader } from '../utils/catalog-schemas.js';
|
|
7
2
|
export interface ParsedChartcatalogsXml {
|
|
8
3
|
header: CatalogHeader;
|
|
9
4
|
charts: CatalogChart[];
|
|
@@ -1,53 +1,45 @@
|
|
|
1
|
+
import { parseStringPromise } from "xml2js";
|
|
2
|
+
//#region src/catalog/chartcatalogs-xml.ts
|
|
1
3
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { parseStringPromise } from 'xml2js';
|
|
7
|
-
// xml2js keeps element text verbatim, and some catalogs wrap values on
|
|
8
|
-
// their own lines (ACE_BUOY's zip URL is surrounded by newlines), which
|
|
9
|
-
// would otherwise reach the downloader as an invalid URL.
|
|
4
|
+
* Parser for chartcatalogs.github.io catalog XML (the NOAA RNC product
|
|
5
|
+
* catalog subset used by OpenCPN). Shared by the plugin's catalog manager
|
|
6
|
+
* and the merged-catalog build so both read the files identically.
|
|
7
|
+
*/
|
|
10
8
|
function text(node, ...names) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
return '';
|
|
9
|
+
for (const name of names) {
|
|
10
|
+
const value = node[name]?.[0];
|
|
11
|
+
if (typeof value === "string") return value.trim();
|
|
12
|
+
}
|
|
13
|
+
return "";
|
|
18
14
|
}
|
|
19
15
|
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
title: text(node, 'title', 'lname'),
|
|
46
|
-
format: text(node, 'format'),
|
|
47
|
-
zipfile_location: text(node, 'zipfile_location'),
|
|
48
|
-
zipfile_datetime_iso8601: text(node, 'zipfile_datetime_iso8601')
|
|
49
|
-
}))
|
|
50
|
-
.filter((c) => c.number !== '' && c.zipfile_location !== '');
|
|
51
|
-
return { header, charts };
|
|
16
|
+
* Parse one catalog file. Entries without a chart number or download
|
|
17
|
+
* location are dropped: they cannot be installed or tracked for updates.
|
|
18
|
+
* Throws when the document is not a chartcatalogs catalog at all.
|
|
19
|
+
*/
|
|
20
|
+
async function parseChartcatalogsXml(xmlData) {
|
|
21
|
+
const result = await parseStringPromise(xmlData);
|
|
22
|
+
if (typeof result !== "object" || result === null) throw new Error("Invalid XML parse result");
|
|
23
|
+
const parsed = result;
|
|
24
|
+
const root = parsed.RncProductCatalogChartCatalogs ?? parsed.EncProductCatalogcellCatalogs;
|
|
25
|
+
if (!root) throw new Error("Unexpected XML root element");
|
|
26
|
+
const headerNode = root.Header?.[0] ?? {};
|
|
27
|
+
return {
|
|
28
|
+
header: {
|
|
29
|
+
title: text(headerNode, "title"),
|
|
30
|
+
dateCreated: text(headerNode, "date_created"),
|
|
31
|
+
dateValid: text(headerNode, "date_valid")
|
|
32
|
+
},
|
|
33
|
+
charts: (root.chart ?? root.cell ?? []).map((node) => ({
|
|
34
|
+
number: text(node, "number", "name"),
|
|
35
|
+
title: text(node, "title", "lname"),
|
|
36
|
+
format: text(node, "format"),
|
|
37
|
+
zipfile_location: text(node, "zipfile_location"),
|
|
38
|
+
zipfile_datetime_iso8601: text(node, "zipfile_datetime_iso8601")
|
|
39
|
+
})).filter((c) => c.number !== "" && c.zipfile_location !== "")
|
|
40
|
+
};
|
|
52
41
|
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { parseChartcatalogsXml };
|
|
44
|
+
|
|
53
45
|
//# sourceMappingURL=chartcatalogs-xml.js.map
|