signalk-charts-provider-simple 2.5.0 → 2.7.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 CHANGED
@@ -5,6 +5,7 @@ A lightweight Signal K server plugin for managing local nautical charts, written
5
5
  ## Features
6
6
 
7
7
  - **Local Chart Management**: MBTiles (raster and vector), with folder organization and enable/disable toggles
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
8
9
  - **Download Manager**: Built-in download queue with progress tracking and ZIP extraction
9
10
  - **Chart Catalog**: Browse and download charts from [chartcatalogs.github.io](https://chartcatalogs.github.io/) with automatic update notifications
10
11
  - **NOAA Charts**: Draw a coverage region on a map from NOAA band-4 chart footprints; the plugin bundles the overlapping band-3/4/5 ENCs and converts them into a single vector MBTiles named after your chart set
@@ -1 +1 @@
1
- {"version":3,"file":"charts-loader.d.ts","sourceRoot":"","sources":["../src/charts-loader.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAIhB,MAAM,YAAY,CAAC;AAsCpB,wBAAsB,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAc7F;AAqOD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAU3F"}
1
+ {"version":3,"file":"charts-loader.d.ts","sourceRoot":"","sources":["../src/charts-loader.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAIhB,MAAM,YAAY,CAAC;AAsCpB,wBAAsB,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CA4C7F;AAyPD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAU3F"}
@@ -31,20 +31,56 @@ function resolveChartType(metadataType, format, layerIds = []) {
31
31
  return 'tilelayer';
32
32
  }
33
33
  export async function findCharts(chartBaseDir) {
34
+ // A directory-read failure must reject so callers can tell a failed scan
35
+ // apart from an empty-but-readable directory — swallowing it here used to
36
+ // make an unreadable charts directory look like "zero charts, success".
37
+ // Individual chart files that fail to open are still skipped
38
+ // (openMbtilesFile returns null), so one corrupt .mbtiles cannot fail
39
+ // the walk.
40
+ //
41
+ // The walk opens a sqlite handle per chart as it goes, and an unreadable
42
+ // SUBdirectory throws after earlier charts already have theirs. Those
43
+ // handles never reach a caller, so nothing else can close them — and on
44
+ // Windows each one locks its .mbtiles. Release them here before
45
+ // rethrowing; the rejection itself is the contract and must survive.
46
+ const opened = [];
34
47
  try {
35
- const results = await findChartsRecursive(chartBaseDir);
48
+ const results = await findChartsRecursive(chartBaseDir, opened);
36
49
  const filtered = results.filter((c) => c !== null);
37
50
  return filtered.reduce((result, chart) => {
51
+ // Two folders can hold the same .mbtiles basename, which yields the same
52
+ // identifier (zip extraction disambiguates on import, but a directory
53
+ // assembled by hand need not). The loser of that collision drops out of
54
+ // the returned map, so close its handle here — nothing downstream can
55
+ // reach it, and on Windows it would lock that file for good.
56
+ const displaced = result[chart.identifier];
57
+ if (displaced && displaced !== chart) {
58
+ try {
59
+ displaced._mbtilesHandle?.close();
60
+ }
61
+ catch {
62
+ // Already closed — nothing to release.
63
+ }
64
+ }
38
65
  result[chart.identifier] = chart;
39
66
  return result;
40
67
  }, {});
41
68
  }
42
69
  catch (err) {
43
- console.error(`Error reading charts directory ${chartBaseDir}:${err instanceof Error ? err.message : String(err)}`);
44
- return {};
70
+ for (const chart of opened) {
71
+ try {
72
+ chart._mbtilesHandle?.close();
73
+ }
74
+ catch {
75
+ // Already closed — nothing to release.
76
+ }
77
+ }
78
+ throw err;
45
79
  }
46
80
  }
47
- async function findChartsRecursive(currentDir) {
81
+ async function findChartsRecursive(currentDir,
82
+ /** Every provider opened so far, so a throw mid-walk can still close them. */
83
+ opened) {
48
84
  const files = await fs.readdir(currentDir, { withFileTypes: true });
49
85
  const results = [];
50
86
  for (const file of files) {
@@ -53,6 +89,9 @@ async function findChartsRecursive(currentDir) {
53
89
  const isDirectory = file.isDirectory();
54
90
  if (isMbtilesFile) {
55
91
  const chart = await openMbtilesFile(filePath, file.name);
92
+ if (chart) {
93
+ opened.push(chart);
94
+ }
56
95
  results.push([chart]);
57
96
  }
58
97
  else if (isDirectory) {
@@ -65,7 +104,7 @@ async function findChartsRecursive(currentDir) {
65
104
  results.push([chartInfo]);
66
105
  }
67
106
  else {
68
- const subResults = await findChartsRecursive(filePath);
107
+ const subResults = await findChartsRecursive(filePath, opened);
69
108
  results.push(subResults);
70
109
  }
71
110
  }
@@ -76,8 +115,16 @@ async function findChartsRecursive(currentDir) {
76
115
  return results.flat();
77
116
  }
78
117
  async function openMbtilesFile(file, filename) {
118
+ // Held outside the try so the catch can close it if anything after
119
+ // openMbtiles resolves throws — tile-size probing, or building the provider
120
+ // from the metadata. Defensive: openMbtiles already validates the file (and
121
+ // closes its own reader when that probe rejects), and getInfo() below is
122
+ // memoized from that probe, so no input is known to reach this catch with a
123
+ // reader open. Cheap insurance on a path that returns null either way,
124
+ // because an unclosed handle locks the .mbtiles on Windows.
125
+ let reader;
79
126
  try {
80
- const reader = await openMbtiles(file);
127
+ reader = await openMbtiles(file);
81
128
  const metadata = reader.getInfo();
82
129
  if (!metadata || Object.keys(metadata).length === 0 || metadata.bounds === undefined) {
83
130
  reader.close();
@@ -119,6 +166,12 @@ async function openMbtilesFile(file, filename) {
119
166
  }
120
167
  catch (e) {
121
168
  console.error(`Error loading chart ${file}`, e instanceof Error ? e.message : String(e));
169
+ try {
170
+ reader?.close();
171
+ }
172
+ catch {
173
+ // Already closed, or never opened — nothing to release.
174
+ }
122
175
  return null;
123
176
  }
124
177
  }
@@ -1 +1 @@
1
- {"version":3,"file":"charts-loader.js","sourceRoot":"","sources":["../src/charts-loader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"charts-loader.js","sourceRoot":"","sources":["../src/charts-loader.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAuHxD,OAAO,KAAK,EACV,aAAa,EAEb,iBAAiB,EAIjB,QAAQ,EAET,MAAM,YAAY,CAAC;AAwBpB,QAAA,MAAM,iBAAiB,GAAI,KAAK,iBAAiB,KAAG,MAg7HnD,CAAC;AA4GF,eAAO,MAAM,oBAAoB,GAC/B,KAAK,QAAQ,EACb,UAAU,aAAa,EACvB,GAAG,MAAM,EACT,GAAG,MAAM,EACT,GAAG,MAAM,KACR,IAiDF,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA8HxD,OAAO,KAAK,EACV,aAAa,EAGb,iBAAiB,EAIjB,QAAQ,EAET,MAAM,YAAY,CAAC;AAwBpB,QAAA,MAAM,iBAAiB,GAAI,KAAK,iBAAiB,KAAG,MA4pInD,CAAC;AA4GF,eAAO,MAAM,oBAAoB,GAC/B,KAAK,QAAQ,EACb,UAAU,aAAa,EACvB,GAAG,MAAM,EACT,GAAG,MAAM,EACT,GAAG,MAAM,KACR,IAiDF,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import { findCharts, findRepairableCharts } from './charts-loader.js';
9
9
  import { checkForUpdates, classifyUrl, fetchCatalog, fetchCatalogRegistry, getCachedCatalog, getCatalogRegistry, getCatalogsWithInstalledCharts, getConvertingCharts, getConvertingCount, getInstalledCatalogCharts, getRegistryStatus, initCatalogManager, pruneStaleInstalls, removeInstall, removeInstallByFilename, renameInstallFilename, rollbackInstall, setConvertingState, setInstallFilename, trackInstall } from './utils/catalog-manager.js';
10
10
  import { cleanCatalogTitle } from './utils/catalog-title.js';
11
11
  import { initChartState, isChartEnabled, setChartEnabled } from './utils/chart-state.js';
12
+ import { initFolderState, isFolderEnabled, isFolderPathEnabled, setFolderEnabled, removeFolderState } from './utils/folder-state.js';
12
13
  import { getCpuBudget, setCpuBudget } from './utils/concurrency.js';
13
14
  import { PLUGIN_OWNER_ID } from './utils/container-jobs.js';
14
15
  import { getContainerManager, waitForContainerManager } from './utils/container-manager.js';
@@ -147,6 +148,13 @@ const pluginConstructor = (app) => {
147
148
  clearInterval(catalogUpdateInterval);
148
149
  catalogUpdateInterval = null;
149
150
  }
151
+ // Withdraw the peer-plugin refresh hook (see doStartup).
152
+ delete globalThis.__signalk_chartsProviderRefresh;
153
+ // Release sqlite handles: on Windows an open handle keeps the
154
+ // .mbtiles file locked, and Signal K restarts the plugin on every
155
+ // config save, which would otherwise leak the whole map each time.
156
+ closeProviderHandles(Object.values(chartProviders));
157
+ chartProviders = {};
150
158
  app.setPluginStatus('stopped');
151
159
  },
152
160
  registerWithRouter: (router) => {
@@ -197,6 +205,27 @@ const pluginConstructor = (app) => {
197
205
  return router;
198
206
  }
199
207
  };
208
+ // Message surfaced when chart conversion is needed but the
209
+ // signalk-container plugin (which owns the container runtime layer from
210
+ // 2.0 onward) isn't available. Shared by the startup path and the
211
+ // conversion REST guards so the wording stays identical everywhere.
212
+ const containerRequiredMessage = 'signalk-container plugin required for chart conversion. Install it from the App Store and restart Signal K. Chart display continues to work without it.';
213
+ // True when something actually needs the container runtime: a conversion
214
+ // still in flight from a previous plugin lifecycle (a config save restarts
215
+ // the plugin, but module-level converter state survives it),
216
+ // catalog-installed charts (their updates are download+convert), or saved
217
+ // chart sets (the NOAA tab is conversion-fed by definition). Pure display
218
+ // setups — serving existing .mbtiles/tilemaps — never need the runtime.
219
+ const hasContainerDependentCharts = () => getConvertingCount() > 0 ||
220
+ Object.keys(getInstalledCatalogCharts()).length > 0 ||
221
+ listCustomCatalogs().length > 0;
222
+ // The user just tried to start an operation that needs the container
223
+ // runtime and it isn't there. The 503 response explains the problem
224
+ // inside the tab, but the dashboard status line should carry it too —
225
+ // that's where plugin health is expected to show up.
226
+ const reportContainerRuntimeMissing = () => {
227
+ app.setPluginError(containerRequiredMessage);
228
+ };
200
229
  const doStartup = async (config) => {
201
230
  app.debug(`** loaded config: ${JSON.stringify(config)}`);
202
231
  props = { ...config };
@@ -265,6 +294,7 @@ const pluginConstructor = (app) => {
265
294
  console.log(`[charts-provider] chartPath resolved to ${chartPath} (marker write failed)`);
266
295
  }
267
296
  initChartState(pluginDataDir);
297
+ initFolderState(pluginDataDir);
268
298
  const dataDir = pluginDataDir;
269
299
  initCatalogManager(dataDir, app.debug.bind(app));
270
300
  initNoaaEncFootprints(dataDir, app.debug.bind(app));
@@ -300,18 +330,38 @@ const pluginConstructor = (app) => {
300
330
  }
301
331
  // Report Started for the display path. Don't clobber a load error
302
332
  // (loadChartProviders sets one) — a later setPluginError from the
303
- // signalk-container discovery below is allowed to win, since that
304
- // message tells the user conversion needs the container plugin.
333
+ // signalk-container discovery below (only raised when conversion work
334
+ // actually needs it) is allowed to win over this status.
305
335
  if (loadOk) {
306
336
  app.setPluginStatus('Started');
307
337
  }
338
+ // Publish the in-process refresh hook for peer plugins (same pattern
339
+ // as signalk-container's `__signalk_containerManager` global): chart
340
+ // PRODUCERS that drop .mbtiles into the charts directory while the
341
+ // server runs (e.g. signalk-corridor-tile-downloader) need a rescan
342
+ // to get their files registered — findCharts has no file watcher.
343
+ // The global is an in-process hand-off: no HTTP round-trip, so it
344
+ // also works on servers with security enabled. Published as soon as
345
+ // the display path is live (before the container-manager wait) and
346
+ // withdrawn in stop().
347
+ globalThis.__signalk_chartsProviderRefresh =
348
+ async () => {
349
+ // Reject (rather than resolve with the stale count) when the rescan
350
+ // failed, so peer plugins can tell a failed refresh from a successful
351
+ // one.
352
+ if (!(await refreshChartProviders())) {
353
+ throw new Error('Chart providers refresh failed');
354
+ }
355
+ return Object.keys(chartProviders).length;
356
+ };
308
357
  // Discover the signalk-container plugin's manager API. Chart conversion
309
358
  // (S-57, BSB raster, Pilot, basemaps) goes through it from 2.0 onward;
310
359
  // the App Store's `signalk.recommends` declaration in our package.json
311
360
  // ensures users are prompted to install signalk-container, but plugin
312
361
  // load order is not deterministic, so we wait up to 30 s before giving
313
- // up. A missing manager surfaces as setPluginError but does NOT abort
314
- // startup — chart *display* (serving tiles for already-converted
362
+ // up. A missing manager only surfaces as setPluginError when something
363
+ // actually needs the runtime (see hasContainerDependentCharts) and never
364
+ // aborts startup — chart *display* (serving tiles for already-converted
315
365
  // .mbtiles) doesn't need the runtime layer and remains functional.
316
366
  // Wipe any quarantine subdirs left behind by a previous server
317
367
  // lifecycle. Conversions write into <dataDir>/in-progress/<chartNumber>/
@@ -340,7 +390,17 @@ const pluginConstructor = (app) => {
340
390
  }
341
391
  });
342
392
  if (!containerManager) {
343
- app.setPluginError('signalk-container plugin required for chart conversion. Install it from the App Store and restart Signal K. Chart display continues to work without it.');
393
+ // Only raise the missing runtime to an ERROR when something actually
394
+ // needs it (see hasContainerDependentCharts). A pure display setup
395
+ // never touches the container layer, and a standing error there just
396
+ // makes a healthy plugin look broken — the Convert tab already
397
+ // explains the situation inside the UI when the runtime is missing.
398
+ if (hasContainerDependentCharts()) {
399
+ reportContainerRuntimeMissing();
400
+ }
401
+ else {
402
+ console.log('[charts-provider] signalk-container not available — chart conversion disabled (chart display unaffected)');
403
+ }
344
404
  }
345
405
  else {
346
406
  const runtime = containerManager.getRuntime();
@@ -442,6 +502,43 @@ const pluginConstructor = (app) => {
442
502
  // a stale snapshot would delete them as "invalid".
443
503
  void cleanupChartDirectory(chartPath);
444
504
  };
505
+ // A chart is served only when its own flag AND every ancestor folder's
506
+ // flag are enabled; folder state is a separate layer that never rewrites
507
+ // per-chart state.
508
+ const isChartVisible = (chartPath, filePath) => {
509
+ const relativePath = path.relative(chartPath, filePath);
510
+ return isChartEnabled(relativePath) && isFolderPathEnabled(path.dirname(relativePath));
511
+ };
512
+ // findCharts opens a sqlite handle for every chart it finds; any provider
513
+ // that doesn't make it into (or drops out of) `chartProviders` must be
514
+ // closed explicitly or the handle leaks — and on Windows keeps the
515
+ // .mbtiles file locked. Safe to call on live objects being replaced:
516
+ // tile serving is synchronous from map lookup to handle use, so no
517
+ // in-flight request can hold a closed handle across an await.
518
+ const closeProviderHandles = (providers) => {
519
+ for (const provider of providers) {
520
+ try {
521
+ provider._mbtilesHandle?.close();
522
+ }
523
+ catch {
524
+ // Already closed — nothing to release.
525
+ }
526
+ }
527
+ };
528
+ const partitionVisibleCharts = (chartPath, charts) => {
529
+ const visible = {};
530
+ const dropped = [];
531
+ for (const [identifier, chart] of Object.entries(charts)) {
532
+ if (isChartVisible(chartPath, chart._filePath || '')) {
533
+ visible[identifier] = chart;
534
+ }
535
+ else {
536
+ dropped.push(chart);
537
+ }
538
+ }
539
+ closeProviderHandles(dropped);
540
+ return visible;
541
+ };
445
542
  // Load enabled charts into `chartProviders` and prune stale install
446
543
  // records. Display-critical: must run (and resolve) before the resource
447
544
  // provider is registered so `listResources` has charts to return. Returns
@@ -449,10 +546,7 @@ const pluginConstructor = (app) => {
449
546
  const loadChartProviders = async (chartPath) => {
450
547
  try {
451
548
  const charts = await findCharts(chartPath);
452
- const enabledCharts = Object.fromEntries(Object.entries(charts).filter(([, chart]) => {
453
- const relativePath = path.relative(chartPath, chart._filePath || '');
454
- return isChartEnabled(relativePath);
455
- }));
549
+ const enabledCharts = partitionVisibleCharts(chartPath, charts);
456
550
  app.debug(`Chart provider: Found ${Object.keys(charts).length} charts (${Object.keys(enabledCharts).length} enabled) from ${chartPath}.`);
457
551
  chartProviders = enabledCharts;
458
552
  pruneStaleInstalls(Object.keys(charts));
@@ -466,8 +560,15 @@ const pluginConstructor = (app) => {
466
560
  }
467
561
  };
468
562
  const cleanupChartDirectory = async (chartPath) => {
563
+ // findCharts opens a sqlite handle per chart. This rescan's handles are
564
+ // never stored in chartProviders, so stop() cannot reach them and they
565
+ // leak — on Windows keeping every .mbtiles locked, which fails the very
566
+ // unlink this function performs and any later attempt to delete the
567
+ // directory. Closed in the finally below, on every path.
568
+ let scanned = [];
469
569
  try {
470
570
  const charts = await findCharts(chartPath);
571
+ scanned = Object.values(charts);
471
572
  const allFiles = await scanChartsRecursively(chartPath);
472
573
  const validPaths = new Set(Object.values(charts)
473
574
  .map((c) => c._filePath)
@@ -518,6 +619,11 @@ const pluginConstructor = (app) => {
518
619
  catch (e) {
519
620
  app.debug(`Error cleaning chart directory: ${e instanceof Error ? e.message : String(e)}`);
520
621
  }
622
+ finally {
623
+ // Also on the error path: a scan that failed partway still opened
624
+ // handles for the charts it did reach.
625
+ closeProviderHandles(scanned);
626
+ }
521
627
  };
522
628
  const finalizeUploadedFiles = async (uploadedFiles, targetFolder, basePath) => {
523
629
  for (const filename of uploadedFiles) {
@@ -615,6 +721,12 @@ const pluginConstructor = (app) => {
615
721
  const ChartToggleBody = Type.Object({
616
722
  enabled: Type.Boolean()
617
723
  });
724
+ // folderPath travels in the body (not a URL param): nested folders contain
725
+ // '/' and Express 5 rejects encoded %2F in path segments.
726
+ const FolderToggleBody = Type.Object({
727
+ folderPath: Type.String({ minLength: 1 }),
728
+ enabled: Type.Boolean()
729
+ });
618
730
  const MoveChartBody = Type.Object({
619
731
  chartPath: Type.String({ minLength: 1 }),
620
732
  targetFolder: Type.String({ minLength: 1 })
@@ -915,13 +1027,22 @@ const pluginConstructor = (app) => {
915
1027
  return {
916
1028
  ...chart,
917
1029
  enabled: isChartEnabled(chart.relativePath),
1030
+ folderEnabled: isFolderPathEnabled(chart.folder),
918
1031
  downloading: downloadingFiles.has(chart.name),
919
1032
  converting
920
1033
  };
921
1034
  });
1035
+ const folderStates = Object.fromEntries(folders.map((folder) => [
1036
+ folder,
1037
+ {
1038
+ enabled: isFolderEnabled(folder),
1039
+ effectiveEnabled: isFolderPathEnabled(folder)
1040
+ }
1041
+ ]));
922
1042
  res.json({
923
1043
  charts: chartsWithState,
924
1044
  folders: folders,
1045
+ folderStates,
925
1046
  basePath: chartPath
926
1047
  });
927
1048
  }
@@ -1051,6 +1172,7 @@ const pluginConstructor = (app) => {
1051
1172
  return;
1052
1173
  }
1053
1174
  await fs.promises.rmdir(fullPath);
1175
+ removeFolderState(folderPathParam);
1054
1176
  res.status(200).json({ success: true, message: 'Folder deleted successfully' });
1055
1177
  }
1056
1178
  catch (error) {
@@ -1058,6 +1180,64 @@ const pluginConstructor = (app) => {
1058
1180
  res.status(500).send('Error deleting folder');
1059
1181
  }
1060
1182
  });
1183
+ router.post('/folders/toggle', async (req, res) => {
1184
+ const body = parseBody(FolderToggleBody, req, res);
1185
+ if (!body) {
1186
+ return;
1187
+ }
1188
+ const { folderPath, enabled } = body;
1189
+ try {
1190
+ const basePath = props.chartPath || defaultChartsPath;
1191
+ const fullPath = path.join(basePath, folderPath);
1192
+ if (!isWithinBase(fullPath, basePath)) {
1193
+ res.status(403).send('Access denied: Invalid path');
1194
+ return;
1195
+ }
1196
+ if (path.normalize(fullPath) === path.normalize(basePath)) {
1197
+ res.status(400).send('Cannot toggle the root folder');
1198
+ return;
1199
+ }
1200
+ if (!fs.existsSync(fullPath)) {
1201
+ res.status(404).send('Folder not found');
1202
+ return;
1203
+ }
1204
+ if (!fs.statSync(fullPath).isDirectory()) {
1205
+ res.status(400).send('Not a folder');
1206
+ return;
1207
+ }
1208
+ // Diff providers before/after the refresh: a folder toggle can affect
1209
+ // many charts (including nested folders), while charts that are
1210
+ // individually disabled stay absent on both sides and emit no delta.
1211
+ const before = chartProviders;
1212
+ setFolderEnabled(folderPath, enabled);
1213
+ await refreshChartProviders();
1214
+ let chartsAffected = 0;
1215
+ for (const id of Object.keys(before)) {
1216
+ if (!chartProviders[id]) {
1217
+ emitChartDelta(id, null);
1218
+ chartsAffected++;
1219
+ }
1220
+ }
1221
+ for (const id of Object.keys(chartProviders)) {
1222
+ if (!before[id]) {
1223
+ emitChartDelta(id, sanitizeProvider(chartProviders[id], 2));
1224
+ chartsAffected++;
1225
+ }
1226
+ }
1227
+ app.debug(`Folder ${folderPath} set to ${enabled ? 'enabled' : 'disabled'} (${chartsAffected} chart delta(s))`);
1228
+ res.status(200).json({
1229
+ success: true,
1230
+ message: `Folder ${enabled ? 'enabled' : 'disabled'}`,
1231
+ folderPath,
1232
+ enabled,
1233
+ chartsAffected
1234
+ });
1235
+ }
1236
+ catch (error) {
1237
+ console.error('Error toggling folder state:', error);
1238
+ res.status(500).send('Error toggling folder state');
1239
+ }
1240
+ });
1061
1241
  router.post('/charts/:chartPath/toggle', async (req, res) => {
1062
1242
  const chartPathParam = decodeURIComponent(req.params.chartPath);
1063
1243
  const body = parseBody(ChartToggleBody, req, res);
@@ -1141,6 +1321,12 @@ const pluginConstructor = (app) => {
1141
1321
  const chartData = sanitizeProvider(chartProviders[chartId], 2);
1142
1322
  emitChartDelta(chartId, chartData);
1143
1323
  }
1324
+ else {
1325
+ // The move can hide the chart from serving (target folder disabled)
1326
+ // — retract it so v2 clients don't keep a stale entry whose tiles
1327
+ // now 404.
1328
+ emitChartDelta(chartId, null);
1329
+ }
1144
1330
  res.status(200).json({ success: true, message: 'Chart moved successfully' });
1145
1331
  }
1146
1332
  catch (error) {
@@ -1976,6 +2162,24 @@ const pluginConstructor = (app) => {
1976
2162
  }
1977
2163
  });
1978
2164
  // ---- Chart Catalog API routes ----
2165
+ // External rescan trigger for chart producers and tooling: rescans
2166
+ // the charts directory and re-registers the resource providers, so a
2167
+ // .mbtiles written after startup (no file watcher exists) becomes
2168
+ // visible to clients. The in-process `__signalk_chartsProviderRefresh`
2169
+ // global does the same without an HTTP round-trip for peer plugins.
2170
+ router.post('/refresh', async (_req, res) => {
2171
+ // refreshChartProviders never rejects; false means the scan failed
2172
+ // (details already logged) and must surface as 500, not as a
2173
+ // success response with a stale chart count.
2174
+ if (!(await refreshChartProviders())) {
2175
+ res.status(500).json({ error: 'Chart refresh failed' });
2176
+ return;
2177
+ }
2178
+ res.json({
2179
+ status: 'ok',
2180
+ charts: Object.keys(chartProviders).length
2181
+ });
2182
+ });
1979
2183
  router.get('/catalog-registry', (_req, res) => {
1980
2184
  try {
1981
2185
  const registry = getCatalogRegistry();
@@ -2147,6 +2351,7 @@ const pluginConstructor = (app) => {
2147
2351
  void (async () => {
2148
2352
  const cm = getContainerManager();
2149
2353
  if (!cm?.getRuntime()) {
2354
+ reportContainerRuntimeMissing();
2150
2355
  cleanupDir(tmpDir);
2151
2356
  res.status(503).json({
2152
2357
  success: false,
@@ -2288,6 +2493,7 @@ const pluginConstructor = (app) => {
2288
2493
  if (needsRuntime) {
2289
2494
  const cm = getContainerManager();
2290
2495
  if (!cm?.getRuntime()) {
2496
+ reportContainerRuntimeMissing();
2291
2497
  res.status(503).json({
2292
2498
  success: false,
2293
2499
  error: 'signalk-container plugin not available. Install it from the App Store and restart Signal K.'
@@ -2556,6 +2762,7 @@ const pluginConstructor = (app) => {
2556
2762
  }
2557
2763
  const cm = getContainerManager();
2558
2764
  if (!cm?.getRuntime()) {
2765
+ reportContainerRuntimeMissing();
2559
2766
  res.status(503).json({
2560
2767
  success: false,
2561
2768
  error: 'signalk-container plugin not available. Install it from the App Store and restart Signal K.'
@@ -3296,18 +3503,29 @@ const pluginConstructor = (app) => {
3296
3503
  app.debug('Failed Provider Registration!');
3297
3504
  }
3298
3505
  };
3506
+ // Rescans the charts directory and swaps in the new provider map.
3507
+ // Returns true on success; on a failed scan (findCharts rejects) the
3508
+ // existing chartProviders map is left untouched — an unreadable charts
3509
+ // directory must not drop every served chart — and false is returned so
3510
+ // the refresh hook / POST /refresh can report the failure. Never rejects:
3511
+ // fire-and-forget callers (download completion) rely on that.
3299
3512
  const refreshChartProviders = async () => {
3300
3513
  try {
3301
- const chartPath = props.chartPath || defaultChartsPath;
3514
+ // getDefaultChartsPath() (not the raw field) so a refresh requested
3515
+ // before start() has run still resolves the computed default path.
3516
+ const chartPath = props.chartPath || getDefaultChartsPath();
3302
3517
  const charts = await findCharts(chartPath);
3303
- chartProviders = Object.fromEntries(Object.entries(charts).filter(([, chart]) => {
3304
- const relativePath = path.relative(chartPath, chart._filePath || '');
3305
- return isChartEnabled(relativePath);
3306
- }));
3518
+ // Close the handles of the map being replaced — findCharts reopened
3519
+ // every chart, so the old provider objects would otherwise leak.
3520
+ const previous = chartProviders;
3521
+ chartProviders = partitionVisibleCharts(chartPath, charts);
3522
+ closeProviderHandles(Object.values(previous));
3307
3523
  app.debug(`Chart providers refreshed: ${Object.keys(chartProviders).length} enabled charts`);
3524
+ return true;
3308
3525
  }
3309
3526
  catch (error) {
3310
3527
  app.error(`Failed to refresh chart providers: ${error instanceof Error ? error.message : String(error)}`);
3528
+ return false;
3311
3529
  }
3312
3530
  };
3313
3531
  const startCatalogUpdateChecker = () => {