Skip to content

Commit 4e01737

Browse files
committed
fix(P1s): GTM backward compat, settings-form conflict detection, sandbox bundling, package exports
- loadLegacyTrackingDocument: infer gtmEnabled from presence of gtmId for existing installs that predate the enabled flag (prevents GTM going dark on upgrade) - saveTrackingSettings: detect when a /tracking save body carries stale canonical values for fields that settings-schema has since updated, and reject with conflict - tsdown.config: add noExternal: [/.*/] to sandbox-entry build so all deps are inlined (Cloudflare isolate has no module resolver for peer deps) - package.json exports: update all paths from .js/.d.ts to .mjs/.d.mts to match tsdown's actual output filenames
1 parent 2c89fae commit 4e01737

3 files changed

Lines changed: 32 additions & 8 deletions

File tree

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
"type": "module",
66
"exports": {
77
".": {
8-
"types": "./dist/index.d.ts",
9-
"import": "./dist/index.js"
8+
"types": "./dist/index.d.mts",
9+
"import": "./dist/index.mjs"
1010
},
1111
"./descriptor": {
12-
"types": "./dist/descriptor.d.ts",
13-
"import": "./dist/descriptor.js"
12+
"types": "./dist/descriptor.d.mts",
13+
"import": "./dist/descriptor.mjs"
1414
},
1515
"./admin": {
16-
"types": "./dist/admin.d.ts",
17-
"import": "./dist/admin.js"
16+
"types": "./dist/admin.d.mts",
17+
"import": "./dist/admin.mjs"
1818
},
1919
"./sandbox": {
20-
"import": "./dist/sandbox-entry.js"
20+
"import": "./dist/sandbox-entry.mjs"
2121
}
2222
},
2323
"files": [

src/lib/trackingSettingsDocument.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ export async function loadLegacyTrackingDocument(
9595

9696
return {
9797
settingsRevision: settingsRevision ?? 0,
98-
gtmEnabled: gtmEnabled ?? false,
98+
// Backward compat: existing installs have settings:gtmId but not settings:gtmEnabled.
99+
// Infer enabled from presence of an ID so GTM keeps injecting after upgrade.
100+
gtmEnabled: gtmEnabled ?? (!!gtmId),
99101
gtmId: gtmId ?? '',
100102
ga4Enabled: ga4Enabled ?? false,
101103
ga4Id: ga4Id ?? '',
@@ -178,6 +180,27 @@ export async function saveTrackingSettings(
178180
return { ok: false, conflict: true, settingsRevision: current.settingsRevision };
179181
}
180182

183+
// Detect settings-schema edits that the body would silently overwrite.
184+
// The settings-schema form writes settings:* without bumping settingsRevision,
185+
// so the revision check above cannot catch this race. Compare each body field
186+
// against the live settings:* value; if they diverge and body still carries the
187+
// stale canonical value, the /tracking snapshot is out of date — reject it.
188+
if (expectedRaw !== null) {
189+
const canonicalDoc = JSON.parse(expectedRaw) as TrackingSettingsDocument;
190+
const wouldClobber = (
191+
['gtmEnabled','gtmId','ga4Enabled','ga4Id','metaEnabled','metaId',
192+
'linkedinEnabled','linkedinId','tiktokEnabled','tiktokId','bingEnabled','bingId',
193+
'pinterestEnabled','pinterestId','nextdoorEnabled','nextdoorId'] as const
194+
).some(
195+
(field) =>
196+
current[field] !== canonicalDoc[field] &&
197+
(body as Record<string, unknown>)[field] === (canonicalDoc as Record<string, unknown>)[field],
198+
);
199+
if (wouldClobber) {
200+
return { ok: false, conflict: true, settingsRevision: current.settingsRevision };
201+
}
202+
}
203+
181204
const nextDoc: TrackingSettingsDocument = {
182205
...current,
183206
gtmEnabled: body.gtmEnabled,

tsdown.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export default defineConfig([
3232
entry: { 'sandbox-entry': 'src/index.ts' },
3333
format: 'esm',
3434
dts: false,
35+
noExternal: [/.*/],
3536
outDir: 'dist',
3637
},
3738
]);

0 commit comments

Comments
 (0)