Skip to content

Commit 3969cfe

Browse files
committed
fix(stac): fail a PMTiles add without an app, and queue pending layer names per archive
1 parent 1db5118 commit 3969cfe

3 files changed

Lines changed: 35 additions & 11 deletions

File tree

packages/plugins/src/plugins/maplibre-components.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4781,29 +4781,34 @@ function createZarrLayerAddHandler(): ZarrLayerEventHandler {
47814781
};
47824782
}
47834783

4784-
// Names handed to addPMTilesLayerFromUrl, keyed by archive URL. The control's own `addLayer(url)`
4784+
// Names handed to addPMTilesLayerFromUrl, queued per archive URL. The control's own `addLayer(url)`
47854785
// takes no name, so a caller that has a better one than the file name (a STAC item and its asset,
4786-
// say) leaves it here for the `layeradd` that follows.
4787-
const pendingPMTilesNames = new Map<string, string>();
4786+
// say) leaves it here for the `layeradd` that follows. A queue rather than a single entry because
4787+
// two adds of the same archive would otherwise take each other's name.
4788+
const pendingPMTilesNames = new Map<string, { name: string }[]>();
47884789

47894790
/**
47904791
* @internal Exported only so the programmatic-name handoff can be unit-tested. Returns a disposer,
47914792
* so an add that never reaches `layeradd` (a broken archive) leaves nothing behind.
47924793
*/
47934794
export function setPendingPMTilesName(url: string, name: string): () => void {
4794-
pendingPMTilesNames.set(url, name);
4795+
const queue = pendingPMTilesNames.get(url) ?? [];
4796+
const pending = { name };
4797+
queue.push(pending);
4798+
pendingPMTilesNames.set(url, queue);
47954799
return () => {
4796-
pendingPMTilesNames.delete(url);
4800+
const index = queue.indexOf(pending);
4801+
if (index >= 0) queue.splice(index, 1);
4802+
if (queue.length === 0) pendingPMTilesNames.delete(url);
47974803
};
47984804
}
47994805

48004806
/** @internal Exported only so the programmatic-name handoff can be unit-tested. */
48014807
export function resolvePMTilesLayerName(layerInfo: PMTilesLayerInfo, id: string): string {
4802-
const pending = pendingPMTilesNames.get(layerInfo.url);
4803-
if (pending !== undefined) {
4804-
pendingPMTilesNames.delete(layerInfo.url);
4805-
return pending;
4806-
}
4808+
const queue = pendingPMTilesNames.get(layerInfo.url);
4809+
const pending = queue?.shift();
4810+
if (queue?.length === 0) pendingPMTilesNames.delete(layerInfo.url);
4811+
if (pending) return pending.name;
48074812
return layerInfo.name || layerNameFromUrl(layerInfo.url, id);
48084813
}
48094814

packages/plugins/src/plugins/maplibre-stac.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,8 +510,9 @@ async function visualizeAsset(
510510
const format = assetFormat(asset);
511511
switch (format) {
512512
case "pmtiles": {
513+
if (!appRef) throw new Error(labels.addFailed);
513514
// The same door the Source Cooperative browser uses, so an archive reaches the map one way.
514-
if (appRef) await addPMTilesLayerFromUrl(appRef, asset.href, { fit: false, name });
515+
await addPMTilesLayerFromUrl(appRef, asset.href, { fit: false, name });
515516
return;
516517
}
517518
case "geojson": {

tests/pmtiles-layer-name.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,24 @@ describe("naming a PMTiles layer a caller asked for", () => {
4343
assert.equal(resolvePMTilesLayerName(info, "layer-1"), "units");
4444
});
4545

46+
it("keeps each concurrent add's own name when both are for the same archive", () => {
47+
const info = layerInfo();
48+
setPendingPMTilesName(info.url, "first item");
49+
setPendingPMTilesName(info.url, "second item");
50+
51+
assert.equal(resolvePMTilesLayerName(info, "layer-1"), "first item");
52+
assert.equal(resolvePMTilesLayerName(info, "layer-2"), "second item");
53+
});
54+
55+
it("drops only the cancelled add's name, leaving the other in the queue", () => {
56+
const info = layerInfo();
57+
const clearFirst = setPendingPMTilesName(info.url, "first item");
58+
setPendingPMTilesName(info.url, "second item");
59+
clearFirst();
60+
61+
assert.equal(resolvePMTilesLayerName(info, "layer-1"), "second item");
62+
});
63+
4664
it("keeps the control's own name when the caller supplied none", () => {
4765
assert.equal(
4866
resolvePMTilesLayerName(layerInfo({ name: "Named by the panel" }), "layer-1"),

0 commit comments

Comments
 (0)