signalk-charts-provider-simple 2.9.0 → 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.
Files changed (162) hide show
  1. package/README.md +5 -3
  2. package/dist/catalog/build-merged-catalog.d.ts +2 -24
  3. package/dist/catalog/build-merged-catalog.d.ts.map +1 -1
  4. package/dist/catalog/build-merged-catalog.js +185 -289
  5. package/dist/catalog/build-merged-catalog.js.map +1 -1
  6. package/dist/catalog/chartcatalogs-xml.d.ts +1 -6
  7. package/dist/catalog/chartcatalogs-xml.js +39 -47
  8. package/dist/catalog/chartcatalogs-xml.js.map +1 -1
  9. package/dist/catalog/merged-catalog-schema.d.ts +245 -376
  10. package/dist/catalog/merged-catalog-schema.d.ts.map +1 -1
  11. package/dist/catalog/merged-catalog-schema.js +327 -412
  12. package/dist/catalog/merged-catalog-schema.js.map +1 -1
  13. package/dist/charts-loader.d.ts +4 -2
  14. package/dist/charts-loader.d.ts.map +1 -1
  15. package/dist/charts-loader.js +337 -388
  16. package/dist/charts-loader.js.map +1 -1
  17. package/dist/index.d.ts +10 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +3368 -4028
  20. package/dist/index.js.map +1 -1
  21. package/dist/package.js +132 -0
  22. package/dist/package.js.map +1 -0
  23. package/dist/types.d.ts +16 -20
  24. package/dist/types.d.ts.map +1 -1
  25. package/dist/types.js +0 -1
  26. package/dist/utils/catalog-manager.d.ts +2 -3
  27. package/dist/utils/catalog-manager.d.ts.map +1 -1
  28. package/dist/utils/catalog-manager.js +562 -727
  29. package/dist/utils/catalog-manager.js.map +1 -1
  30. package/dist/utils/catalog-schemas.d.ts +49 -55
  31. package/dist/utils/catalog-schemas.d.ts.map +1 -1
  32. package/dist/utils/catalog-schemas.js +53 -63
  33. package/dist/utils/catalog-schemas.js.map +1 -1
  34. package/dist/utils/catalog-title.js +63 -79
  35. package/dist/utils/catalog-title.js.map +1 -1
  36. package/dist/utils/chart-state.d.ts +1 -1
  37. package/dist/utils/chart-state.js +33 -33
  38. package/dist/utils/chart-state.js.map +1 -1
  39. package/dist/utils/concurrency.js +35 -44
  40. package/dist/utils/concurrency.js.map +1 -1
  41. package/dist/utils/container-fs.js +62 -66
  42. package/dist/utils/container-fs.js.map +1 -1
  43. package/dist/utils/container-images.js +24 -20
  44. package/dist/utils/container-images.js.map +1 -1
  45. package/dist/utils/container-jobs.d.ts +1 -20
  46. package/dist/utils/container-jobs.js +99 -102
  47. package/dist/utils/container-jobs.js.map +1 -1
  48. package/dist/utils/container-manager.js +54 -86
  49. package/dist/utils/container-manager.js.map +1 -1
  50. package/dist/utils/conversion-log.js +21 -17
  51. package/dist/utils/conversion-log.js.map +1 -1
  52. package/dist/utils/conversion-progress.d.ts +1 -1
  53. package/dist/utils/conversion-progress.js +15 -19
  54. package/dist/utils/conversion-progress.js.map +1 -1
  55. package/dist/utils/custom-catalog-download.js +96 -102
  56. package/dist/utils/custom-catalog-download.js.map +1 -1
  57. package/dist/utils/custom-catalog-manager.d.ts +2 -18
  58. package/dist/utils/custom-catalog-manager.js +282 -373
  59. package/dist/utils/custom-catalog-manager.js.map +1 -1
  60. package/dist/utils/download-manager.d.ts +1 -1
  61. package/dist/utils/download-manager.d.ts.map +1 -1
  62. package/dist/utils/download-manager.js +436 -614
  63. package/dist/utils/download-manager.js.map +1 -1
  64. package/dist/utils/file-scanner.d.ts +1 -1
  65. package/dist/utils/file-scanner.d.ts.map +1 -1
  66. package/dist/utils/file-scanner.js +131 -133
  67. package/dist/utils/file-scanner.js.map +1 -1
  68. package/dist/utils/folder-state.d.ts +1 -1
  69. package/dist/utils/folder-state.js +59 -80
  70. package/dist/utils/folder-state.js.map +1 -1
  71. package/dist/utils/job-failure.js +29 -35
  72. package/dist/utils/job-failure.js.map +1 -1
  73. package/dist/utils/mbtiles-metadata.js +473 -515
  74. package/dist/utils/mbtiles-metadata.js.map +1 -1
  75. package/dist/utils/mbtiles-reader.d.ts +1 -1
  76. package/dist/utils/mbtiles-reader.js +280 -410
  77. package/dist/utils/mbtiles-reader.js.map +1 -1
  78. package/dist/utils/noaa-enc-footprints.d.ts +1 -20
  79. package/dist/utils/noaa-enc-footprints.js +285 -347
  80. package/dist/utils/noaa-enc-footprints.js.map +1 -1
  81. package/dist/utils/online-area-download.d.ts +56 -0
  82. package/dist/utils/online-area-download.d.ts.map +1 -0
  83. package/dist/utils/online-area-download.js +159 -0
  84. package/dist/utils/online-area-download.js.map +1 -0
  85. package/dist/utils/online-charts.d.ts +63 -21
  86. package/dist/utils/online-charts.d.ts.map +1 -1
  87. package/dist/utils/online-charts.js +336 -188
  88. package/dist/utils/online-charts.js.map +1 -1
  89. package/dist/utils/online-tile-cache.d.ts +139 -0
  90. package/dist/utils/online-tile-cache.d.ts.map +1 -0
  91. package/dist/utils/online-tile-cache.js +517 -0
  92. package/dist/utils/online-tile-cache.js.map +1 -0
  93. package/dist/utils/path-marker.js +37 -55
  94. package/dist/utils/path-marker.js.map +1 -1
  95. package/dist/utils/path-safety.js +118 -117
  96. package/dist/utils/path-safety.js.map +1 -1
  97. package/dist/utils/plugin-config-schema.d.ts +7 -6
  98. package/dist/utils/plugin-config-schema.d.ts.map +1 -1
  99. package/dist/utils/plugin-config-schema.js +23 -30
  100. package/dist/utils/plugin-config-schema.js.map +1 -1
  101. package/dist/utils/pmtiles-reader.d.ts +46 -0
  102. package/dist/utils/pmtiles-reader.d.ts.map +1 -0
  103. package/dist/utils/pmtiles-reader.js +187 -0
  104. package/dist/utils/pmtiles-reader.js.map +1 -0
  105. package/dist/utils/quarantine.js +199 -246
  106. package/dist/utils/quarantine.js.map +1 -1
  107. package/dist/utils/raster-compose.js +123 -110
  108. package/dist/utils/raster-compose.js.map +1 -1
  109. package/dist/utils/raster-overzoom-worker.d.ts +1 -1
  110. package/dist/utils/raster-overzoom-worker.js +18 -13
  111. package/dist/utils/raster-overzoom-worker.js.map +1 -1
  112. package/dist/utils/raster-overzoom.d.ts +2 -22
  113. package/dist/utils/raster-overzoom.js +161 -170
  114. package/dist/utils/raster-overzoom.js.map +1 -1
  115. package/dist/utils/raster-tile-cache.d.ts +1 -28
  116. package/dist/utils/raster-tile-cache.js +258 -314
  117. package/dist/utils/raster-tile-cache.js.map +1 -1
  118. package/dist/utils/rest-validation.d.ts +3 -18
  119. package/dist/utils/rest-validation.d.ts.map +1 -1
  120. package/dist/utils/rest-validation.js +33 -50
  121. package/dist/utils/rest-validation.js.map +1 -1
  122. package/dist/utils/rnc-converter.d.ts +1 -1
  123. package/dist/utils/rnc-converter.js +373 -468
  124. package/dist/utils/rnc-converter.js.map +1 -1
  125. package/dist/utils/s57-band.js +148 -153
  126. package/dist/utils/s57-band.js.map +1 -1
  127. package/dist/utils/s57-converter.d.ts +2 -2
  128. package/dist/utils/s57-converter.js +1211 -1635
  129. package/dist/utils/s57-converter.js.map +1 -1
  130. package/dist/utils/serial-queue.d.ts +6 -0
  131. package/dist/utils/serial-queue.d.ts.map +1 -0
  132. package/dist/utils/serial-queue.js +17 -0
  133. package/dist/utils/serial-queue.js.map +1 -0
  134. package/dist/utils/tile-overzoom.d.ts +1 -25
  135. package/dist/utils/tile-overzoom.js +225 -298
  136. package/dist/utils/tile-overzoom.js.map +1 -1
  137. package/dist/utils/time-dimension.js +147 -209
  138. package/dist/utils/time-dimension.js.map +1 -1
  139. package/dist/utils/time-poller.d.ts +1 -13
  140. package/dist/utils/time-poller.js +136 -164
  141. package/dist/utils/time-poller.js.map +1 -1
  142. package/dist/utils/vessel-position.js +17 -19
  143. package/dist/utils/vessel-position.js.map +1 -1
  144. package/dist/utils/zip-extract.js +169 -193
  145. package/dist/utils/zip-extract.js.map +1 -1
  146. package/package.json +16 -11
  147. package/public/css/chart-catalog.css +5 -0
  148. package/public/css/manage-charts.css +85 -0
  149. package/public/index.html +2 -1
  150. package/public/js/chart-catalog.js +6 -32
  151. package/public/js/chart-catalog.ts +8 -45
  152. package/public/js/download-simple.js +184 -1
  153. package/public/js/download-simple.ts +196 -1
  154. package/public/js/globals.d.ts +15 -0
  155. package/public/js/main.js +5 -0
  156. package/public/js/main.ts +4 -0
  157. package/public/js/manage-charts-enhanced.js +58 -16
  158. package/public/js/manage-charts-enhanced.ts +89 -16
  159. package/public/js/online-area-download.js +347 -0
  160. package/public/js/online-area-download.ts +413 -0
  161. package/dist/types.js.map +0 -1
  162. 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. They stream from their provider, so they need an internet connection to display. They go into an "Online Charts" folder by default, so you can switch them all off at once when you're offline
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
- * Pure assembly of the merged chart catalog. File and network access live
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 MergedTileset, type OnlineChartsSourceFile, type TilesetFeed } 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 {
@@ -12,14 +7,6 @@ export interface ChartcatalogsFile {
12
7
  /** `null` when the file exists upstream but could not be parsed. */
13
8
  parsed: ParsedChartcatalogsXml | null;
14
9
  }
15
- /** A tileset feed as listed in the repo, with the document fetched for it. */
16
- export interface TilesetFeedInput {
17
- feed: TilesetFeed;
18
- /** The parsed feed document, or `null` when it could not be fetched or parsed. */
19
- document: unknown;
20
- /** File sizes in bytes by URL, where the publisher's server reported them. */
21
- sizes?: Map<string, number>;
22
- }
23
10
  export interface BuildInputs {
24
11
  chartcatalogs: ChartcatalogsFile[];
25
12
  chartcatalogsCommit?: string;
@@ -29,9 +16,6 @@ export interface BuildInputs {
29
16
  repo: string;
30
17
  /** chartcatalogs entries from the catalog currently published, by file. */
31
18
  previous?: Map<string, MergedChartcatalogsCatalog>;
32
- tilesetFeeds?: TilesetFeedInput[];
33
- /** Tilesets in the catalog currently published, for carrying a failed feed forward. */
34
- previousTilesets?: MergedTileset[];
35
19
  now: Date;
36
20
  }
37
21
  export interface BuildResult {
@@ -52,10 +36,6 @@ export interface BuildResult {
52
36
  * guard tolerates a few, so each one is reported.
53
37
  */
54
38
  removedUpstream: string[];
55
- /** Feeds that could not be read, whose published regions were kept. */
56
- carriedForwardFeeds: string[];
57
- /** Feed regions left out, each with the reason. */
58
- skippedTilesets: string[];
59
39
  }
60
40
  /**
61
41
  * Hash of what a user would actually see. chartcatalogs re-commits every
@@ -72,8 +52,6 @@ export interface PublishedState {
72
52
  chartCount: number;
73
53
  /** chartcatalogs entries that still validate, for carrying forward. */
74
54
  chartcatalogs: Map<string, MergedChartcatalogsCatalog>;
75
- /** Tilesets that still validate, for carrying a failed feed forward. */
76
- tilesets: MergedTileset[];
77
55
  /** The published `catalog.schema.json` text, or null if absent. */
78
56
  schemaText: string | null;
79
57
  }
@@ -1 +1 @@
1
- {"version":3,"file":"build-merged-catalog.d.ts","sourceRoot":"","sources":["../../src/catalog/build-merged-catalog.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build-merged-catalog.d.ts","sourceRoot":"","sources":["../../src/catalog/build-merged-catalog.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAQL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAElB,KAAK,0BAA0B,EAE/B,KAAK,sBAAsB,EAC5B,MAAM,4BAA4B,CAAC;AAEpC,eAAO,MAAM,sBAAsB,qCAAqC,CAAC;AACzE,eAAO,MAAM,oBAAoB,qDAAqD,CAAC;AAEvF,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,MAAM,EAAE,sBAAsB,CAAC;IAC/B,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;IACnD,GAAG,EAAE,IAAI,CAAC;CACX;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,aAAa,CAAC;IACvB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,4DAA4D;IAC5D,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,sFAAsF;IACtF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4EAA4E;IAC5E,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB;;;;OAIG;IACH,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAkED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,GAAG,MAAM,CAYtF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAoEnE;AAED,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;IACvD,mEAAmE;IACnE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,CAmB1F;AAMD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,GAAG,IAAI,GAAG,MAAM,EAAE,CAuB/F;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,aAAa,EACnB,SAAS,EAAE,cAAc,GAAG,IAAI,EAChC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,OAAO,GACb,eAAe,CAKjB"}
@@ -1,311 +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
- * Pure assembly of the merged chart catalog. File and network access live
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 { createHash } from 'node:crypto';
7
- import { Value } from '@sinclair/typebox/value';
8
- import { MERGED_CATALOG_SCHEMA_VERSION, MergedCatalogSchema, MergedChartcatalogsCatalogSchema, MergedTilesetSchema, TilesetFeedDocumentSchema, TilesetFeedFileSchema, 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
- return a < b ? -1 : a > b ? 1 : 0;
13
+ return a < b ? -1 : a > b ? 1 : 0;
13
14
  }
14
15
  function mergeChartcatalogsFile(file, parsed, index, droppedCharts) {
15
- const id = file.replace(/\.xml$/i, '');
16
- // One odd upstream URL must not fail the whole build (the schema would
17
- // reject the catalog), so bad charts are dropped here and reported.
18
- const charts = parsed.charts.filter((chart) => {
19
- const ok = /^https?:\/\//.test(chart.zipfile_location);
20
- if (!ok) {
21
- droppedCharts.push(`${file} ${chart.number}`);
22
- }
23
- return ok;
24
- });
25
- const base = { id, file, use: 'download', header: parsed.header, charts };
26
- const facets = index[file];
27
- if (facets) {
28
- return { ...base, ...facets, indexed: true };
29
- }
30
- // Nearly every chartcatalogs catalog is a navigation chart set, so that is
31
- // the safest default until someone adds the file to the index.
32
- return { ...base, label: parsed.header.title || id, category: 'navigation', indexed: false };
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
- const problems = [];
36
- const seen = new Set();
37
- for (const entry of online.charts) {
38
- if (seen.has(entry.id)) {
39
- problems.push(`${entry.id}: duplicate id`);
40
- }
41
- seen.add(entry.id);
42
- problems.push(...checkOnlineChartEntry(entry));
43
- }
44
- if (problems.length > 0) {
45
- throw new Error(`Invalid online chart entries:\n ${problems.join('\n ')}`);
46
- }
47
- return online.charts.map((entry) => ({
48
- ...entry,
49
- use: 'stream',
50
- format: formatForChartType(entry.chart.type)
51
- }));
52
- }
53
- function isIsoTime(value) {
54
- return value !== undefined && /^\d{4}-\d{2}-\d{2}T/.test(value) && !isNaN(Date.parse(value));
55
- }
56
- /** Why a feed region can't be published, or null when it can. */
57
- function regionProblem(raw, updated, seen) {
58
- if (seen.has(raw.id)) {
59
- return 'duplicate id';
60
- }
61
- if (raw.bounds[1] >= raw.bounds[3]) {
62
- return 'bounds south must be below north';
63
- }
64
- if (raw.minzoom !== undefined && raw.maxzoom !== undefined && raw.minzoom > raw.maxzoom) {
65
- return 'minzoom is greater than maxzoom';
66
- }
67
- if (!isIsoTime(updated)) {
68
- return 'no date to tell an update by';
69
- }
70
- return null;
71
- }
72
- /**
73
- * The regions of every tileset feed. A feed that cannot be read, or that
74
- * suddenly lists nothing usable, keeps its published regions: one failed
75
- * fetch must not remove a chart from every boat's catalog, or turn off the
76
- * update notice for boats that installed it.
77
- */
78
- function mergeTilesets(inputs, previous, repo, carriedForwardFeeds, skippedTilesets) {
79
- const ids = new Set();
80
- for (const { feed } of inputs) {
81
- if (ids.has(feed.id)) {
82
- throw new Error(`Invalid tileset feeds:\n ${feed.id}: duplicate id`);
83
- }
84
- ids.add(feed.id);
85
- }
86
- const tilesets = [];
87
- for (const { feed, document, sizes } of inputs) {
88
- const regions = [];
89
- if (Value.Check(TilesetFeedDocumentSchema, document)) {
90
- const seen = new Set();
91
- for (const raw of document.files) {
92
- const rawId = raw?.id;
93
- const label = `${feed.id} ${typeof rawId === 'string' ? rawId : '<no id>'}`;
94
- if (!Value.Check(TilesetFeedFileSchema, raw)) {
95
- skippedTilesets.push(`${label}: ${schemaErrors(TilesetFeedFileSchema, raw)[0] ?? 'invalid'}`);
96
- continue;
97
- }
98
- const updated = isIsoTime(raw.newestArrival) ? raw.newestArrival : document.generated;
99
- const problem = regionProblem(raw, updated, seen);
100
- if (problem !== null || !isIsoTime(updated)) {
101
- skippedTilesets.push(`${label}: ${problem ?? 'invalid'}`);
102
- continue;
103
- }
104
- seen.add(raw.id);
105
- const size = sizes?.get(raw.url);
106
- regions.push({
107
- id: `${feed.id}-${raw.id}`,
108
- feed: feed.id,
109
- name: raw.name,
110
- description: feed.description,
111
- use: 'download',
112
- category: feed.category,
113
- format: 'mbtiles',
114
- bbox: raw.bounds,
115
- provider: feed.provider,
116
- attribution: feed.attribution,
117
- license: feed.license,
118
- licenseUrl: feed.licenseUrl,
119
- homepage: feed.homepage,
120
- issues: feed.issues ?? `https://github.com/${repo}/issues/new?template=catalog-problem.yml`,
121
- ...(feed.notForNavigation !== undefined
122
- ? { notForNavigation: feed.notForNavigation }
123
- : {}),
124
- folder: feed.folder,
125
- file: {
126
- url: raw.url,
127
- updated,
128
- ...(size !== undefined ? { size } : {}),
129
- ...(raw.minzoom !== undefined ? { minzoom: raw.minzoom } : {}),
130
- ...(raw.maxzoom !== undefined ? { maxzoom: raw.maxzoom } : {})
131
- }
132
- });
133
- }
134
- }
135
- if (regions.length === 0) {
136
- const kept = previous.filter((t) => t.feed === feed.id);
137
- if (kept.length > 0) {
138
- carriedForwardFeeds.push(feed.id);
139
- }
140
- regions.push(...kept);
141
- }
142
- tilesets.push(...regions);
143
- }
144
- return tilesets.sort((a, b) => compareStrings(a.id, b.id));
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
+ }));
145
56
  }
146
57
  /** JSON with object keys sorted, so key order never changes the hash. */
147
58
  function canonicalJson(value) {
148
- if (Array.isArray(value)) {
149
- return `[${value.map(canonicalJson).join(',')}]`;
150
- }
151
- if (value !== null && typeof value === 'object') {
152
- const entries = Object.entries(value)
153
- .filter(([, v]) => v !== undefined)
154
- .sort(([a], [b]) => compareStrings(a, b));
155
- return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;
156
- }
157
- 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);
158
62
  }
159
63
  /**
160
- * Hash of what a user would actually see. chartcatalogs re-commits every
161
- * hour just to refresh its header dates, so those dates, the upstream
162
- * commit id and our own `generatedAt` are excluded by name; anything else,
163
- * including fields added later, is hashed.
164
- */
165
- export function computeContentHash(catalog) {
166
- const { generatedAt: _generatedAt, ...content } = catalog;
167
- const { commit: _commit, ...chartcatalogsSource } = content.sources.chartcatalogs;
168
- const hashed = {
169
- ...content,
170
- sources: { ...content.sources, chartcatalogs: chartcatalogsSource },
171
- chartcatalogs: content.chartcatalogs.map((c) => {
172
- const { dateCreated: _created, dateValid: _valid, ...header } = c.header;
173
- return { ...c, header };
174
- })
175
- };
176
- return createHash('sha256').update(canonicalJson(hashed)).digest('hex');
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");
177
87
  }
178
- export function buildMergedCatalog(inputs) {
179
- const indexProblems = checkChartcatalogsIndex(inputs.index);
180
- if (indexProblems.length > 0) {
181
- throw new Error(`Invalid chartcatalogs index:\n ${indexProblems.join('\n ')}`);
182
- }
183
- const files = [...inputs.chartcatalogs].sort((a, b) => compareStrings(a.file, b.file));
184
- const carriedForward = [];
185
- const skipped = [];
186
- const droppedCharts = [];
187
- const chartcatalogs = [];
188
- for (const { file, parsed } of files) {
189
- const previous = inputs.previous?.get(file);
190
- // A file that still exists upstream but is broken or suddenly empty is
191
- // far more likely an upstream mistake than a withdrawn catalog, so keep
192
- // what users can see today rather than wiping it from every plugin.
193
- if (previous && (!parsed || parsed.charts.length === 0)) {
194
- const facets = inputs.index[file];
195
- chartcatalogs.push(facets ? { ...previous, ...facets, indexed: true } : previous);
196
- carriedForward.push(file);
197
- continue;
198
- }
199
- if (!parsed) {
200
- skipped.push(file);
201
- continue;
202
- }
203
- chartcatalogs.push(mergeChartcatalogsFile(file, parsed, inputs.index, droppedCharts));
204
- }
205
- const carriedForwardFeeds = [];
206
- const skippedTilesets = [];
207
- const tilesets = mergeTilesets(inputs.tilesetFeeds ?? [], inputs.previousTilesets ?? [], inputs.repo, carriedForwardFeeds, skippedTilesets);
208
- const fileNames = new Set(files.map((f) => f.file));
209
- const withoutHash = {
210
- schemaVersion: MERGED_CATALOG_SCHEMA_VERSION,
211
- generatedAt: inputs.now.toISOString(),
212
- sources: {
213
- chartcatalogs: {
214
- homepage: CHARTCATALOGS_HOMEPAGE,
215
- issues: CHARTCATALOGS_ISSUES,
216
- license: 'CC0-1.0',
217
- ...(inputs.chartcatalogsCommit ? { commit: inputs.chartcatalogsCommit } : {})
218
- },
219
- online: {
220
- homepage: `https://github.com/${inputs.repo}`,
221
- issues: `https://github.com/${inputs.repo}/issues/new?template=catalog-problem.yml`
222
- }
223
- },
224
- chartcatalogs,
225
- online: mergeOnlineCharts(inputs.online),
226
- tilesets
227
- };
228
- const catalog = { ...withoutHash, contentHash: computeContentHash(withoutHash) };
229
- if (!Value.Check(MergedCatalogSchema, catalog)) {
230
- throw new Error(`Merged catalog failed validation:\n ${schemaErrors(MergedCatalogSchema, catalog).join('\n ')}`);
231
- }
232
- return {
233
- catalog,
234
- unindexed: chartcatalogs.filter((c) => !c.indexed).map((c) => c.file),
235
- staleIndexEntries: Object.keys(inputs.index).filter((f) => !fileNames.has(f)),
236
- carriedForward,
237
- skipped,
238
- droppedCharts,
239
- removedUpstream: [...(inputs.previous?.keys() ?? [])]
240
- .filter((f) => !fileNames.has(f))
241
- .sort(compareStrings),
242
- carriedForwardFeeds,
243
- skippedTilesets
244
- };
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
+ };
245
147
  }
246
148
  /**
247
- * Read the published catalog loosely. It may predate the current schema
248
- * (that is exactly when a build change is riskiest), so the counts the drop
249
- * guard relies on are taken from the raw arrays rather than a full parse.
250
- */
251
- export function readPublishedState(raw, schemaText) {
252
- const obj = raw !== null && typeof raw === 'object' ? raw : {};
253
- const rawCatalogs = Array.isArray(obj.chartcatalogs) ? obj.chartcatalogs : [];
254
- const chartcatalogs = new Map();
255
- let chartCount = 0;
256
- for (const entry of rawCatalogs) {
257
- const charts = entry?.charts;
258
- chartCount += Array.isArray(charts) ? charts.length : 0;
259
- if (Value.Check(MergedChartcatalogsCatalogSchema, entry)) {
260
- chartcatalogs.set(entry.file, entry);
261
- }
262
- }
263
- const tilesets = (Array.isArray(obj.tilesets) ? obj.tilesets : []).filter((t) => Value.Check(MergedTilesetSchema, t));
264
- return {
265
- contentHash: typeof obj.contentHash === 'string' ? obj.contentHash : null,
266
- catalogCount: rawCatalogs.length,
267
- chartCount,
268
- chartcatalogs,
269
- tilesets,
270
- schemaText
271
- };
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
+ };
272
170
  }
273
171
  function chartCount(catalog) {
274
- return catalog.chartcatalogs.reduce((n, c) => n + c.charts.length, 0);
172
+ return catalog.chartcatalogs.reduce((n, c) => n + c.charts.length, 0);
275
173
  }
276
174
  /**
277
- * Reasons not to replace the published catalog with `next`. Per-file
278
- * breakage is absorbed by carrying entries forward; these thresholds catch
279
- * whole-source failures such as a truncated clone.
280
- */
281
- export function publishBlockers(next, published) {
282
- const blockers = [];
283
- if (next.chartcatalogs.length === 0) {
284
- blockers.push('no chartcatalogs catalogs were read');
285
- }
286
- if (next.online.length === 0) {
287
- blockers.push('the online chart list is empty');
288
- }
289
- if (published) {
290
- const after = next.chartcatalogs.length;
291
- if (after < Math.floor(published.catalogCount * 0.8)) {
292
- blockers.push(`chartcatalogs catalogs dropped from ${String(published.catalogCount)} to ${String(after)}`);
293
- }
294
- const chartsAfter = chartCount(next);
295
- if (chartsAfter < Math.floor(published.chartCount * 0.5)) {
296
- blockers.push(`chartcatalogs charts dropped from ${String(published.chartCount)} to ${String(chartsAfter)}`);
297
- }
298
- }
299
- 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;
300
190
  }
301
191
  /**
302
- * Whether to deploy. The site also carries the JSON Schema, so a schema
303
- * change with identical content must still deploy, or third parties would
304
- * keep validating against a stale schema.
305
- */
306
- export function decidePublish(next, published, schemaText, force) {
307
- const blockers = publishBlockers(next, published);
308
- const changed = !published || published.contentHash !== next.contentHash || published.schemaText !== schemaText;
309
- return { publish: force || (changed && blockers.length === 0), blockers };
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
+ };
310
203
  }
204
+ //#endregion
205
+ export { CHARTCATALOGS_HOMEPAGE, CHARTCATALOGS_ISSUES, buildMergedCatalog, computeContentHash, decidePublish, publishBlockers, readPublishedState };
206
+
311
207
  //# sourceMappingURL=build-merged-catalog.js.map