mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-12 06:12:18 +00:00
Layer state keeps only layers the application registered
An application built with fewer layers no longer restores, stores or shares a layer it does not have when a link or saved state from a build that has it names that layer. LayerStateCoordinator drops unregistered enabled-layer ids before normalizing, so dependent options (a tracked selection) go with them. An application with every layer registered is unchanged.
This commit is contained in:
1 parent
7ee7dc26b8
commit
e9346cef19
3 files changed
+98
-9
No files matched your search
@@ -5627,6 +5627,13 @@ and unreachable upstream (502/504) separately from road geometry.
|
|||||||
explicit-empty form); unknown option tokens are ignored. Restoration
|
explicit-empty form); unknown option tokens are ignored. Restoration
|
||||||
settles independently per layer so one failed or unavailable source cannot
|
settles independently per layer so one failed or unavailable source cannot
|
||||||
block its siblings.
|
block its siblings.
|
||||||
|
- An application that registers fewer layers (an embedder with a smaller
|
||||||
|
catalog) keeps only its own: `LayerStateCoordinator` drops enabled-layer ids
|
||||||
|
the application did not register before it normalizes a share, saved state or
|
||||||
|
explicit change, so such a layer is never restored, written to local storage
|
||||||
|
or carried into a link this application shares. Options that depend on a
|
||||||
|
dropped layer (a tracked selection) go with it. An application with every
|
||||||
|
layer registered is unchanged.
|
||||||
- Layer tokens are permanent public compatibility identifiers. The reservation
|
- Layer tokens are permanent public compatibility identifiers. The reservation
|
||||||
JSON ledger at `src/data/layerStateTokenReservations.json` pins all existing
|
JSON ledger at `src/data/layerStateTokenReservations.json` pins all existing
|
||||||
one-character assignments (all letters plus `0`, `1` and
|
one-character assignments (all letters plus `0`, `1` and
|
||||||
|
|||||||
@@ -3121,3 +3121,70 @@ test('Active Fires: the codec accepts whole megawatts in bounds and the three co
|
|||||||
confidence: 'nominal',
|
confidence: 'nominal',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// An application built without a layer (a smaller catalog) receives a link or
|
||||||
|
// saved state from a build that has it: the layer is neither restored nor
|
||||||
|
// stored nor carried into the links this application shares.
|
||||||
|
function managerWithout(withheld) {
|
||||||
|
const manager = new DataLayerManager({});
|
||||||
|
for (const id of REGISTERED_LAYER_IDS)
|
||||||
|
if (!withheld.includes(id)) manager.register(fakeLayer(id));
|
||||||
|
manager.finalizeRegistrations(
|
||||||
|
LAYER_STATE_REGISTRY.filter(({ id }) => !withheld.includes(id)),
|
||||||
|
);
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('layers the application did not register are never restored, stored or shared', async () => {
|
||||||
|
const manager = managerWithout(['earthquakes']);
|
||||||
|
const storage = memoryStorage();
|
||||||
|
const share = shareSink();
|
||||||
|
const coordinator = new LayerStateCoordinator(manager, share, { storage });
|
||||||
|
const incoming = createDefaultLayerState();
|
||||||
|
incoming.enabledLayerIds = ['earthquakes', 'traffic'];
|
||||||
|
await coordinator.start({ shareLayerState: incoming });
|
||||||
|
|
||||||
|
assert.deepEqual(coordinator.getDurableState().enabledLayerIds, ['traffic']);
|
||||||
|
assert.deepEqual(share.provider().enabledLayerIds, ['traffic']);
|
||||||
|
assert.equal(manager.layers.has('earthquakes'), false);
|
||||||
|
|
||||||
|
await manager.setEnabled('cctv', true, { origin: 'user' });
|
||||||
|
const [[, written]] = storage.writes;
|
||||||
|
assert.deepEqual(parseStoredLayerState(written).enabledLayerIds, [
|
||||||
|
'cctv',
|
||||||
|
'traffic',
|
||||||
|
]);
|
||||||
|
assert.deepEqual(share.provider().enabledLayerIds, ['cctv', 'traffic']);
|
||||||
|
|
||||||
|
await coordinator.applyShareLayerState({ shareLayerState: incoming });
|
||||||
|
assert.deepEqual(coordinator.getDurableState().enabledLayerIds, ['traffic']);
|
||||||
|
coordinator.destroy();
|
||||||
|
|
||||||
|
const saved = createDefaultLayerState();
|
||||||
|
saved.enabledLayerIds = ['earthquakes', 'traffic'];
|
||||||
|
const local = new LayerStateCoordinator(
|
||||||
|
managerWithout(['earthquakes']),
|
||||||
|
shareSink(),
|
||||||
|
{ storage: memoryStorage(serializeStoredLayerState(saved)) },
|
||||||
|
);
|
||||||
|
await local.start();
|
||||||
|
assert.equal(local.source, 'local');
|
||||||
|
assert.deepEqual(local.getDurableState().enabledLayerIds, ['traffic']);
|
||||||
|
local.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('an application with every layer registered keeps every requested layer', async () => {
|
||||||
|
const coordinator = new LayerStateCoordinator(
|
||||||
|
productionManager(),
|
||||||
|
shareSink(),
|
||||||
|
{ storage: memoryStorage() },
|
||||||
|
);
|
||||||
|
const incoming = createDefaultLayerState();
|
||||||
|
incoming.enabledLayerIds = ['earthquakes', 'traffic'];
|
||||||
|
await coordinator.start({ shareLayerState: incoming });
|
||||||
|
assert.deepEqual(coordinator.getDurableState().enabledLayerIds, [
|
||||||
|
'earthquakes',
|
||||||
|
'traffic',
|
||||||
|
]);
|
||||||
|
coordinator.destroy();
|
||||||
|
});
|
||||||
@@ -106,9 +106,7 @@ export class LayerStateCoordinator {
|
|||||||
} = {}) {
|
} = {}) {
|
||||||
if (this._destroyed)
|
if (this._destroyed)
|
||||||
throw new Error('Layer-state coordinator is destroyed');
|
throw new Error('Layer-state coordinator is destroyed');
|
||||||
let selected = shareLayerState
|
let selected = shareLayerState ? this._normalize(shareLayerState) : null;
|
||||||
? normalizeLayerState(shareLayerState)
|
|
||||||
: null;
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
this._source = 'share';
|
this._source = 'share';
|
||||||
this._shareCreatedAtMs = Number.isFinite(shareCreatedAtMs)
|
this._shareCreatedAtMs = Number.isFinite(shareCreatedAtMs)
|
||||||
@@ -124,7 +122,7 @@ export class LayerStateCoordinator {
|
|||||||
/* best effort */
|
/* best effort */
|
||||||
}
|
}
|
||||||
if (stored) {
|
if (stored) {
|
||||||
selected = stored;
|
selected = this._normalize(stored);
|
||||||
this._source = 'local';
|
this._source = 'local';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -163,9 +161,7 @@ export class LayerStateCoordinator {
|
|||||||
for (const controller of this._restoreControllers.values())
|
for (const controller of this._restoreControllers.values())
|
||||||
controller.abort('superseded-by-share-navigation');
|
controller.abort('superseded-by-share-navigation');
|
||||||
this._revokePendingTrackingWatch('superseded-by-share-navigation');
|
this._revokePendingTrackingWatch('superseded-by-share-navigation');
|
||||||
const selected = shareLayerState
|
const selected = shareLayerState ? this._normalize(shareLayerState) : null;
|
||||||
? normalizeLayerState(shareLayerState)
|
|
||||||
: null;
|
|
||||||
this._source = selected ? 'share' : 'legacy-share';
|
this._source = selected ? 'share' : 'legacy-share';
|
||||||
this._shareCreatedAtMs =
|
this._shareCreatedAtMs =
|
||||||
selected && Number.isFinite(shareCreatedAtMs) ? shareCreatedAtMs : null;
|
selected && Number.isFinite(shareCreatedAtMs) ? shareCreatedAtMs : null;
|
||||||
@@ -182,6 +178,25 @@ export class LayerStateCoordinator {
|
|||||||
return this._source;
|
return this._source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canonical layer state holding only the layers this application
|
||||||
|
* registered. An application built without a layer never restores, stores
|
||||||
|
* or shares it, even when a link or saved state from a build that has it
|
||||||
|
* names it. Filtering before normalizing lets normalization drop options
|
||||||
|
* that depend on a layer that is not here (a tracked selection).
|
||||||
|
*/
|
||||||
|
_normalize(candidate) {
|
||||||
|
const input = candidate && typeof candidate === 'object' ? candidate : {};
|
||||||
|
return normalizeLayerState({
|
||||||
|
...input,
|
||||||
|
enabledLayerIds: Array.isArray(input.enabledLayerIds)
|
||||||
|
? input.enabledLayerIds.filter((id) =>
|
||||||
|
this.dataManager.layers.has(String(id)),
|
||||||
|
)
|
||||||
|
: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getDurableState() {
|
getDurableState() {
|
||||||
return cloneLayerState(this._durableState);
|
return cloneLayerState(this._durableState);
|
||||||
}
|
}
|
||||||
@@ -345,7 +360,7 @@ export class LayerStateCoordinator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_commitExplicit(candidate) {
|
_commitExplicit(candidate) {
|
||||||
this._durableState = normalizeLayerState(candidate);
|
this._durableState = this._normalize(candidate);
|
||||||
const serialized = serializeStoredLayerState(this._durableState);
|
const serialized = serializeStoredLayerState(this._durableState);
|
||||||
try {
|
try {
|
||||||
if (this.storage?.getItem?.(LAYER_STATE_STORAGE_KEY) !== serialized) {
|
if (this.storage?.getItem?.(LAYER_STATE_STORAGE_KEY) !== serialized) {
|
||||||
@@ -478,7 +493,7 @@ export class LayerStateCoordinator {
|
|||||||
...this._durableState.options[selected.optionOwner],
|
...this._durableState.options[selected.optionOwner],
|
||||||
[selected.optionKey]: null,
|
[selected.optionKey]: null,
|
||||||
};
|
};
|
||||||
this._durableState = normalizeLayerState({
|
this._durableState = this._normalize({
|
||||||
...this._durableState,
|
...this._durableState,
|
||||||
options: {
|
options: {
|
||||||
...this._durableState.options,
|
...this._durableState.options,
|
||||||
|
|||||||
Reference in new issue
Block a user