Skip to content

Commit 9986e57

Browse files
committed
fix(plugins): match a pending PMTiles name to its archive so a panel add cannot take it
1 parent 76aee52 commit 9986e57

2 files changed

Lines changed: 29 additions & 13 deletions

File tree

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,7 +1811,7 @@ async function addPMTilesLayerNow(
18111811
// The control emits `layeradd` synchronously while adding, so the handler has already spent this
18121812
// name by the time the await returns; the disposer is for the archive that throws before it.
18131813
const clearPendingName =
1814-
options.name === undefined ? undefined : setPendingPMTilesName(options.name);
1814+
options.name === undefined ? undefined : setPendingPMTilesName(url, options.name);
18151815
try {
18161816
await pmtilesControl.addLayer(url);
18171817
} finally {
@@ -4800,25 +4800,28 @@ function createZarrLayerAddHandler(): ZarrLayerEventHandler {
48004800

48014801
// A name handed to addPMTilesLayerFromUrl, waiting for the `layeradd` it belongs to. The control's
48024802
// own `addLayer(url)` takes no name, so a caller with a better one than the file name (a STAC item
4803-
// and its asset, say) leaves it here. Adds are serialized, so only one is ever pending.
4804-
let pendingPMTilesName: string | null = null;
4803+
// and its asset, say) leaves it here. Adds through this module are serialized, so only one is ever
4804+
// pending; it carries its archive so a layer the control's own panel adds meanwhile cannot take it.
4805+
let pendingPMTiles: { url: string; name: string } | null = null;
48054806

48064807
/**
48074808
* @internal Exported for tests. Returns a disposer, so an add that never reaches `layeradd` (a
48084809
* broken archive) leaves nothing behind it.
48094810
*/
4810-
export function setPendingPMTilesName(name: string): () => void {
4811-
pendingPMTilesName = name;
4811+
export function setPendingPMTilesName(url: string, name: string): () => void {
4812+
pendingPMTiles = { url, name };
48124813
return () => {
4813-
pendingPMTilesName = null;
4814+
pendingPMTiles = null;
48144815
};
48154816
}
48164817

48174818
/** @internal Spends the pending name, falling back to the control's own and then the file name. */
48184819
export function resolvePMTilesLayerName(layerInfo: PMTilesLayerInfo, id: string): string {
4819-
const pending = pendingPMTilesName;
4820-
pendingPMTilesName = null;
4821-
return pending || layerInfo.name || layerNameFromUrl(layerInfo.url, id);
4820+
const fallback = layerInfo.name || layerNameFromUrl(layerInfo.url, id);
4821+
if (pendingPMTiles?.url !== layerInfo.url) return fallback;
4822+
const { name } = pendingPMTiles;
4823+
pendingPMTiles = null;
4824+
return name || fallback;
48224825
}
48234826

48244827
function createPMTilesLayerAddHandler(): PMTilesLayerEventHandler {

tests/pmtiles-layer-name.test.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,34 +22,47 @@ function layerInfo(patch: Partial<PMTilesLayerInfo> = {}): PMTilesLayerInfo {
2222

2323
describe("naming a PMTiles layer a caller asked for", () => {
2424
it("uses the name the caller left, not the file name", () => {
25-
setPendingPMTilesName("item-1 — PMTiles vector tiles");
25+
setPendingPMTilesName(layerInfo().url, "item-1 — PMTiles vector tiles");
2626

2727
assert.equal(resolvePMTilesLayerName(layerInfo(), "layer-1"), "item-1 — PMTiles vector tiles");
2828
});
2929

3030
it("spends the name once, so a layer the panel adds next is named for itself", () => {
3131
const info = layerInfo();
32-
setPendingPMTilesName("item-1 — PMTiles vector tiles");
32+
setPendingPMTilesName(layerInfo().url, "item-1 — PMTiles vector tiles");
3333
resolvePMTilesLayerName(info, "layer-1");
3434

3535
assert.equal(resolvePMTilesLayerName(info, "layer-2"), "units");
3636
});
3737

3838
it("drops a name whose add never produced a layer", () => {
3939
const info = layerInfo();
40-
const clear = setPendingPMTilesName("item-1 — PMTiles vector tiles");
40+
const clear = setPendingPMTilesName(info.url, "item-1 — PMTiles vector tiles");
4141
clear();
4242

4343
assert.equal(resolvePMTilesLayerName(info, "layer-1"), "units");
4444
});
4545

4646
it("falls back when a caller queues an empty name, rather than naming the layer nothing", () => {
4747
const info = layerInfo({ name: "Named by the panel" });
48-
setPendingPMTilesName("");
48+
setPendingPMTilesName(info.url, "");
4949

5050
assert.equal(resolvePMTilesLayerName(info, "layer-1"), "Named by the panel");
5151
});
5252

53+
it("leaves a name alone for a layer the control's own panel added meanwhile", () => {
54+
setPendingPMTilesName(
55+
"https://example.org/warehouse/units.pmtiles",
56+
"item-1 — PMTiles vector tiles",
57+
);
58+
59+
// A panel add for a different archive must not take the name queued for ours.
60+
const other = layerInfo({ url: "https://example.org/somebody-elses.pmtiles", name: "" });
61+
assert.equal(resolvePMTilesLayerName(other, "layer-9"), "somebody-elses");
62+
// And ours still gets it when it arrives.
63+
assert.equal(resolvePMTilesLayerName(layerInfo(), "layer-1"), "item-1 — PMTiles vector tiles");
64+
});
65+
5366
it("keeps the control's own name when the caller supplied none", () => {
5467
assert.equal(
5568
resolvePMTilesLayerName(layerInfo({ name: "Named by the panel" }), "layer-1"),

0 commit comments

Comments
 (0)