Skip to content

Commit ccea6fe

Browse files
committed
fix(tracking): snapshot canonical doc on /tracking load so stale-save detection covers fresh installs
ensureCanonicalDocExists creates the canonical doc via CAS when the /tracking UI first loads settings. This guarantees expectedRaw !== null by save time, so the field-by-field stale check always runs — including on upgrades and fresh installs where no /tracking save has ever occurred.
1 parent 10f18e2 commit ccea6fe

2 files changed

Lines changed: 20 additions & 0 deletions

File tree

src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { validateLicense, CACHE_KEY } from './lib/licensing';
44
import { buildPageFragments } from './frontend/injector';
55
import {
66
documentToApiResponse,
7+
ensureCanonicalDocExists,
78
loadTrackingSettingsDocument,
89
saveTrackingSettings,
910
type TrackingSaveBody,
@@ -262,6 +263,9 @@ export function createPlugin() {
262263
'tracking/settings': {
263264
handler: async (ctx) => {
264265
const doc = await loadTrackingSettingsDocument(ctx);
266+
// Snapshot current state so saveTrackingSettings can detect races
267+
// with the settings-schema form even on first-ever /tracking load.
268+
await ensureCanonicalDocExists(ctx, doc);
265269
return documentToApiResponse(doc);
266270
}
267271
},

src/lib/trackingSettingsDocument.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,22 @@ export async function loadTrackingSettingsDocument(
125125
return loadLegacyTrackingDocument(ctx);
126126
}
127127

128+
/**
129+
* Ensure the canonical doc exists so saveTrackingSettings always has a snapshot
130+
* for conflict detection. Called when the /tracking UI loads settings — by save
131+
* time the doc exists and the field-by-field stale check runs.
132+
*/
133+
export async function ensureCanonicalDocExists(
134+
ctx: Pick<PluginContext, 'kv'>,
135+
doc: TrackingSettingsDocument,
136+
): Promise<void> {
137+
const kv = asKvWithCas(ctx.kv);
138+
const existing = await kv.getRaw(TRACKING_SETTINGS_DOC_KEY);
139+
if (existing === null) {
140+
await kv.commitIfValueUnchanged(TRACKING_SETTINGS_DOC_KEY, null, doc);
141+
}
142+
}
143+
128144
export async function mirrorTrackingDocumentToSettingsKeys(
129145
ctx: Pick<PluginContext, 'kv'>,
130146
doc: TrackingSettingsDocument,

0 commit comments

Comments
 (0)