Skip to content

Commit 91c59c2

Browse files
mournergithub-actions[bot]
authored andcommitted
Identify bundle loads from Mapbox CDN vs bundled
GitOrigin-RevId: c3d005241ac6e36dbffe71beef7fa57ac4ab263c
1 parent edb5343 commit 91c59c2

8 files changed

Lines changed: 72 additions & 7 deletions

File tree

build/rollup_plugins.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import minifyStyleSpec from './rollup_plugin_minify_style_spec.js';
1616
*
1717
* @param {Object} options
1818
* @param {string | 'dev' | 'production'} [options.mode] - build mode
19-
* @param {string | 'esm' | 'umd'} [options.format] - output format
19+
* @param {'umd' | 'csp' | 'esm'} [options.format] - bundle format, reported as `bundleFormat` in telemetry
2020
* @param {boolean} [options.minified] - whether to minify the output
2121
* @param {boolean} [options.production] - whether this is a production build
2222
* @param {boolean} [options.test] - whether this is a test build
@@ -31,7 +31,7 @@ export const plugins = ({mode, format, minified, production, test, keepClassName
3131
sourceMap: true,
3232
tsconfig: './tsconfig.browser.json',
3333
define: {
34-
'import.meta.env': JSON.stringify({mode}),
34+
'import.meta.env': JSON.stringify({mode, format}),
3535
}
3636
}),
3737
json({

rollup.config.csp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const config = (input: InputOption, file: string, format: ModuleFormat): RollupO
1717
banner
1818
},
1919
treeshake: {preset: 'recommended', moduleSideEffects: (id) => !id.endsWith('devtools.ts')},
20-
plugins: plugins({minified: true, production: true, keepClassNames: true, test: false, mode: 'production'})
20+
plugins: plugins({minified: true, production: true, keepClassNames: true, test: false, mode: 'production', format: 'csp'})
2121
});
2222

2323
export default [

rollup.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export default ({watch}: {watch: boolean}): RollupOptions[] => {
6363
},
6464
onwarn: production ? onwarn : undefined,
6565
treeshake: production ? {preset: 'recommended', moduleSideEffects: (id) => !id.endsWith('devtools.ts')} : false,
66-
plugins: [preserveDynamicImport(), ...plugins({minified, production, test: false, keepClassNames: false, mode: BUILD})]
66+
plugins: [preserveDynamicImport(), ...plugins({minified, production, test: false, keepClassNames: false, mode: BUILD, format: 'umd'})]
6767
}, {
6868
// Next, bundle together the three "chunks" produced in the previous pass
6969
// into a single, final bundle. See rollup/bundle_prelude.js and

src/index.esm.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import assert from './style-spec/util/assert';
22
import _Point from '@mapbox/point-geometry';
33
import {version as _version} from '../package.json';
4+
import {setBundleDistribution} from './util/mapbox';
5+
import {isMapboxHTTPCDNURL} from './util/mapbox_url';
6+
7+
// Detect whether this ESM bundle was served from the Mapbox CDN (telemetry only).
8+
// `import.meta.url` is the module URL; bundlers rewrite it to a local URL, yielding 'other'.
9+
// eslint-disable-next-line no-restricted-syntax -- this file is the ESM-only entry, never bundled as UMD
10+
setBundleDistribution(isMapboxHTTPCDNURL(import.meta.url) ? 'cdn' : 'other');
411

512
// Source class instance types — returned by map.getSource()
613
export type * from './source/source_types';

src/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,24 @@ import MercatorCoordinate from './geo/mercator_coordinate';
1717
import {Evented} from './util/evented';
1818
import config, {setAccessToken, setBaseApiUrl, setMaxParallelImageRequests, getDracoUrl, setDracoUrl, getMeshoptUrl, setMeshoptUrl, getBuildingGenUrl, setBuildingGenUrl} from './util/config';
1919
import {setRTLTextPlugin, getRTLTextPluginStatus} from './source/rtl_text_plugin';
20-
import {setSdkInfo} from './util/mapbox';
2120
import {addTileProvider} from './source/tile_provider';
2221
import {getWorkerCount, setWorkerCount} from './util/worker_pool';
2322
import WorkerClass from './util/worker_class';
2423
import {prewarm, clearPrewarmedResources} from './util/worker_pool_factory';
2524
import {clearTileCache} from './util/tile_request_cache';
2625
import {FreeCameraOptions} from './ui/free_camera';
2726
import browser from './util/browser';
27+
import {isMapboxHTTPCDNURL} from './util/mapbox_url';
28+
import {setSdkInfo, setBundleDistribution} from './util/mapbox';
2829

2930
import type {Class} from './types/class';
3031

32+
// Detect whether this UMD/CSP bundle was served from the Mapbox CDN (telemetry only).
33+
// Classic scripts expose their URL via `document.currentScript`; the `typeof document`
34+
// guard keeps the bundle importable in Node/SSR.
35+
const currentScript = typeof document !== 'undefined' ? document.currentScript as HTMLScriptElement | null : null;
36+
setBundleDistribution(currentScript && currentScript.src && isMapboxHTTPCDNURL(currentScript.src) ? 'cdn' : 'other');
37+
3138
// Explicit type re-exports
3239
export type * from './ui/events';
3340
export type * from './style-spec/types';

src/types/import-meta.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
interface ImportMeta {
22
env: {
33
mode?: 'dev' | 'production';
4+
format?: 'umd' | 'csp' | 'esm';
45
[key: string]: unknown;
56
};
67
}

src/util/mapbox.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,16 @@ export function setSdkInfo(info: string) {
343343
sdkInfo = info;
344344
}
345345

346+
// Whether the running bundle was served from the Mapbox CDN (vs. self-hosted or bundled into
347+
// the consumer's app), reported as the `bundleDistribution` telemetry field. The entry files
348+
// (index.ts / index.esm.ts) inspect their own script URL locally and pass only the resulting
349+
// enum here at import time — no URL is ever retained or transmitted.
350+
let bundleDistribution: 'cdn' | 'other' = 'other';
351+
352+
export function setBundleDistribution(distribution: 'cdn' | 'other') {
353+
bundleDistribution = distribution;
354+
}
355+
346356
type TelemetryEventType = 'appUserTurnstile' | 'map.load' | 'map.auth' | 'gljs.performance' | 'style.load' | 'metrics';
347357

348358
export class TelemetryEvent {
@@ -578,7 +588,9 @@ export class MapLoadEvent extends TelemetryEvent {
578588
sdkVersion,
579589
skuId: SKU_ID,
580590
skuToken: this.skuToken,
581-
userId: this.anonId
591+
userId: this.anonId,
592+
bundleFormat: import.meta.env.format || 'unknown',
593+
bundleDistribution
582594
};
583595

584596
if (sdkInfo) additionalPayload.sdkInfo = sdkInfo;
@@ -878,7 +890,9 @@ export class TurnstileEvent extends TelemetryEvent {
878890
sdkVersion,
879891
skuId: SKU_ID,
880892
"enabled.telemetry": false,
881-
userId: this.anonId
893+
userId: this.anonId,
894+
bundleFormat: import.meta.env.format || 'unknown',
895+
bundleDistribution
882896
};
883897

884898
if (sdkInfo) additionalPayload.sdkInfo = sdkInfo;

test/unit/util/mapbox.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,10 @@ describe("mapbox", () => {
783783
expect(!!mapLoadEvent.userId).toBeTruthy();
784784
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
785785
expect(!!mapLoadEvent.created).toBeTruthy();
786+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
787+
expect(mapLoadEvent.bundleFormat).toEqual('unknown');
788+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
789+
expect(mapLoadEvent.bundleDistribution).toEqual('other');
786790
});
787791

788792
test('does not POST when mapboxgl.ACCESS_TOKEN is not set', () => {
@@ -1212,6 +1216,10 @@ describe("mapbox", () => {
12121216
expect(mapLoadEvent.version).toEqual('2.2');
12131217
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
12141218
expect(mapLoadEvent.sdkInfo).toBeUndefined();
1219+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1220+
expect(mapLoadEvent.bundleFormat).toEqual('unknown');
1221+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1222+
expect(mapLoadEvent.bundleDistribution).toEqual('other');
12151223
});
12161224

12171225
test('setSdkInfo ignores invalid values', async () => {
@@ -1667,4 +1675,32 @@ describe("mapbox", () => {
16671675
expect(mapbox.parseAccessToken('a.!!!.c')).toBeNull();
16681676
});
16691677
});
1678+
1679+
describe('setBundleDistribution', () => {
1680+
let event: any;
1681+
const skuToken = '1234567890123';
1682+
beforeEach(() => {
1683+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
1684+
window.useFakeXMLHttpRequest();
1685+
event = new mapbox.MapLoadEvent();
1686+
});
1687+
afterEach(() => {
1688+
// Reset the module-level state set by these tests (default is 'other').
1689+
mapbox.setBundleDistribution('other');
1690+
});
1691+
1692+
async function getBundleDistribution(): Promise<string> {
1693+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
1694+
const reqBody = await window.server.requests[0].requestBody;
1695+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
1696+
return JSON.parse(reqBody.slice(1, reqBody.length - 1)).bundleDistribution;
1697+
}
1698+
1699+
test('payload reflects the bundleDistribution set via setBundleDistribution', async () => {
1700+
mapbox.setBundleDistribution('cdn');
1701+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
1702+
event.postMapLoadEvent(1, skuToken);
1703+
expect(await getBundleDistribution()).toEqual('cdn');
1704+
});
1705+
});
16701706
});

0 commit comments

Comments
 (0)