@tileflow/core 0.1.0-alpha.17 → 0.1.0-alpha.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -272,6 +272,19 @@ their frame scheduler plus MapLibre event and marker operations after mount. The
272
272
  is documented in
273
273
  [`docs/contracts/framework-browser-runtime.md`](../../docs/contracts/framework-browser-runtime.md).
274
274
 
275
+ ## Hosted session authorization
276
+
277
+ The framework adapters use `createTileflowSessionController` to obtain one short-lived session
278
+ grant before eligible hosted style, tile, font, or sprite requests. Concurrent resources share the
279
+ same preflight, grants are attached only to reviewed HTTP(S) origins, and an expired unused session
280
+ rotates once when the server requests it. A controller also rotates after six hours or 10,000
281
+ eligible requests. The preflight has a 10-second client timeout by default; direct controller users
282
+ can set `grantTimeoutMs` from 1 to 120,000 milliseconds.
283
+
284
+ `analytics.enabled: false` disables only the optional analytics beacon. It does not disable this
285
+ server-owned commercial authorization. User `transformRequest` callbacks still run first; Tileflow
286
+ then decorates only the resulting eligible URL and preserves the other request options.
287
+
275
288
  Other subpaths under `src/`, `themes/`, `templates/`, and `modules/` are internal implementation
276
289
  details and can change during alpha releases.
277
290
 
package/dist/browser.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { T as TileflowAnalytics } from './runtime-BMZ_tuba.js';
1
+ import { T as TileflowAnalytics, a as TileflowSessionController } from './runtime-Bg--GMWM.js';
2
2
  import 'zod';
3
3
 
4
4
  type TileflowMapReadinessState = 'error' | 'idle' | 'loading';
@@ -35,6 +35,7 @@ type TileflowMarkerController<TMap, TDefinition, TMarker> = {
35
35
  replace: (map: TMap, definitions: readonly TDefinition[]) => void;
36
36
  };
37
37
  declare function createTileflowSessionStarter(options: {
38
+ getSessionId?: () => string;
38
39
  sessionId: string;
39
40
  source: string;
40
41
  startSession?: TileflowSessionSender;
@@ -55,6 +56,7 @@ declare function createTileflowTransformRequest<TRequest extends TileflowTransfo
55
56
  always: true;
56
57
  asyncAnalyticsTiming?: TileflowAsyncAnalyticsTiming;
57
58
  getAnalytics: () => TileflowAnalytics | undefined;
59
+ sessionController?: TileflowSessionController;
58
60
  sessionId: string;
59
61
  transformRequest?: TileflowUserTransformRequest<TRequest, TResourceType>;
60
62
  }): TileflowTransformRequest<TRequest, TResourceType>;
@@ -62,6 +64,7 @@ declare function createTileflowTransformRequest<TRequest extends TileflowTransfo
62
64
  always?: boolean;
63
65
  asyncAnalyticsTiming?: TileflowAsyncAnalyticsTiming;
64
66
  getAnalytics: () => TileflowAnalytics | undefined;
67
+ sessionController?: TileflowSessionController;
65
68
  sessionId: string;
66
69
  transformRequest?: TileflowUserTransformRequest<TRequest, TResourceType>;
67
70
  }): TileflowTransformRequest<TRequest, TResourceType> | undefined;
package/dist/browser.js CHANGED
@@ -26,7 +26,7 @@ function startTileflowSession(analytics, input) {
26
26
  if (!analytics || analytics.enabled === false || !input.mapId || typeof navigator === "undefined") {
27
27
  return;
28
28
  }
29
- const endpoint = analytics.endpoint ?? `${getTileflowAnalyticsApiUrl(analytics)}/v1/sessions/start`;
29
+ const endpoint = analytics.endpoint ?? `${getTileflowAnalyticsApiUrl(analytics)}/v1/sessions/analytics`;
30
30
  const payload = JSON.stringify({
31
31
  mapId: input.mapId,
32
32
  metadata: analytics.metadata,
@@ -49,7 +49,7 @@ function startTileflowSession(analytics, input) {
49
49
  },
50
50
  keepalive: true,
51
51
  method: "POST"
52
- });
52
+ }).catch(() => void 0);
53
53
  }
54
54
  function getTileflowAnalyticsApiUrl(analytics) {
55
55
  if (analytics.apiUrl) {
@@ -94,17 +94,18 @@ function createTileflowSessionStarter(options) {
94
94
  },
95
95
  start(analytics, styleId) {
96
96
  const mapId = analytics?.mapId;
97
+ const sessionId = options.getSessionId?.() ?? options.sessionId;
97
98
  if (!mapId) {
98
99
  return false;
99
100
  }
100
- const key = `${options.sessionId}:${mapId}:${styleId ?? ""}`;
101
+ const key = `${sessionId}:${mapId}:${styleId ?? ""}`;
101
102
  if (starts.has(key)) {
102
103
  return false;
103
104
  }
104
105
  starts.add(key);
105
106
  send(analytics, {
106
107
  mapId,
107
- sessionId: options.sessionId,
108
+ sessionId,
108
109
  source: options.source,
109
110
  styleId
110
111
  });
@@ -216,7 +217,8 @@ function attachTileflowMapLifecycle(options) {
216
217
  function createTileflowTransformRequest(options) {
217
218
  if (!options.always && !options.transformRequest) {
218
219
  const analytics = options.getAnalytics();
219
- if (!analytics || analytics.enabled === false) {
220
+ const requiresCommercialSession = Boolean(options.sessionController && analytics?.mapId);
221
+ if (!requiresCommercialSession && (!analytics || analytics.enabled === false)) {
220
222
  return void 0;
221
223
  }
222
224
  }
@@ -226,19 +228,21 @@ function createTileflowTransformRequest(options) {
226
228
  const analyticsAtRequest = asyncAnalyticsTiming === "request" ? options.getAnalytics() : void 0;
227
229
  if (isPromiseLike(request)) {
228
230
  return request.then(
229
- (resolvedRequest) => applyTileflowAnalyticsRequest(
231
+ (resolvedRequest) => applyTileflowRequest(
230
232
  url,
231
233
  resolvedRequest,
232
234
  asyncAnalyticsTiming === "resolution" ? options.getAnalytics() : analyticsAtRequest,
233
- options.sessionId
235
+ options.sessionId,
236
+ options.sessionController
234
237
  ) ?? resolvedRequest
235
238
  );
236
239
  }
237
- return applyTileflowAnalyticsRequest(
240
+ return applyTileflowRequest(
238
241
  url,
239
242
  request,
240
243
  asyncAnalyticsTiming === "resolution" ? options.getAnalytics() : analyticsAtRequest,
241
- options.sessionId
244
+ options.sessionId,
245
+ options.sessionController
242
246
  );
243
247
  };
244
248
  }
@@ -272,10 +276,23 @@ function createTileflowMarkerController(options) {
272
276
  }
273
277
  };
274
278
  }
275
- function applyTileflowAnalyticsRequest(url, request, analytics, sessionId) {
276
- const nextUrl = resolveTileflowAnalyticsRequestUrl(request?.url ?? url, analytics, sessionId);
279
+ function applyTileflowRequest(url, request, analytics, sessionId, sessionController) {
280
+ const requestUrl = request?.url ?? url;
281
+ if (sessionController) {
282
+ return sessionController.resolveRequestUrl(requestUrl, analytics).then(
283
+ (nextUrl) => composeTileflowRequest(url, request, nextUrl, true) ?? { url }
284
+ );
285
+ }
286
+ return composeTileflowRequest(
287
+ url,
288
+ request,
289
+ resolveTileflowAnalyticsRequestUrl(requestUrl, analytics, sessionId),
290
+ false
291
+ );
292
+ }
293
+ function composeTileflowRequest(originalUrl, request, nextUrl, ensureRequest) {
277
294
  if (!nextUrl) {
278
- return request;
295
+ return request ?? (ensureRequest ? { url: originalUrl } : void 0);
279
296
  }
280
297
  return request ? { ...request, url: nextUrl } : { url: nextUrl };
281
298
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as TileflowConfig, b as TileflowProjectConfig, V as ValidationResult, c as TileflowOsmBasemapOptions, d as TileflowOsmBasemapConfig, e as TileflowRoadsModuleOptions, f as TileflowRoadsModuleConfig, g as TileflowLabelsModuleOptions, h as TileflowLabelsModuleConfig, i as TileflowPoiModuleOptions, j as TileflowPoiModuleConfig, k as TileflowStyleOverrideOptions, l as TileflowStyleOverrideModuleConfig } from './runtime-BMZ_tuba.js';
2
- export { M as MapLibreSprite, m as MapLibreStyle, N as NormalizedTileflowCaptureScene, R as ResolvedTileflowTypography, n as TileflowAdvancedColorConfig, T as TileflowAnalytics, o as TileflowBaseColors, p as TileflowBasemapConfig, q as TileflowBoundaryColorConfig, r as TileflowBuildingColorConfig, s as TileflowBuildingStyleConfig, t as TileflowBuildings, u as TileflowCaptureApplicationTarget, v as TileflowCaptureBoundsCamera, w as TileflowCaptureCamera, x as TileflowCaptureCenterCamera, y as TileflowCaptureMapTarget, z as TileflowCaptureScene, A as TileflowCaptureTarget, B as TileflowCaptureViewport, C as TileflowColor, D as TileflowColorConfig, E as TileflowColors, F as TileflowDensity, G as TileflowFontWeight, H as TileflowFonts, I as TileflowHostedNotoSansWeight, J as TileflowHydroColorConfig, K as TileflowIconSet, L as TileflowIconSetConfig, O as TileflowLabelColorConfig, P as TileflowLabelDetail, Q as TileflowLabelLanguage, S as TileflowLabels, U as TileflowLandcoverColorConfig, W as TileflowLanduseColorConfig, X as TileflowManifest, Y as TileflowManifestLoadOptions, Z as TileflowMapConfig, _ as TileflowMapMarker, $ as TileflowMapMode, a0 as TileflowMapModeOptions, a1 as TileflowModuleConfig, a2 as TileflowPoi, a3 as TileflowPoiCategory, a4 as TileflowPoiClassMapping, a5 as TileflowPoiColorConfig, a6 as TileflowPoiColorMode, a7 as TileflowPoiDensity, a8 as TileflowPoiIcons, a9 as TileflowPoiLabels, aa as TileflowProjectIconSets, ab as TileflowProjectThemes, ac as TileflowRenderer, ad as TileflowRoadClass, ae as TileflowRoadColorConfig, af as TileflowRoadDetail, ag as TileflowRoadExtras, ah as TileflowRoadHierarchy, ai as TileflowRoadLabelDetail, aj as TileflowRoadOutline, ak as TileflowRoadWeight, al as TileflowRoads, am as TileflowRuntimeManifest, an as TileflowRuntimeManifestMap, ao as TileflowRuntimeManifestMapEntry, ap as TileflowRuntimeStyle, aq as TileflowRuntimeStyleOptions, ar as TileflowSourceLayers, as as TileflowStyleLayerOverride, at as TileflowStyleOptions, au as TileflowTerrain, av as TileflowTerrainConfig, aw as TileflowTerrainEncoding, ax as TileflowTerrainMode, ay as TileflowTheme, az as TileflowThemeConfig, aA as TileflowThemeMode, aB as TileflowThemeModulesConfig, aC as TileflowThemeName, aD as TileflowTiles, aE as TileflowTilesetConfig, aF as TileflowTypography, aG as TileflowTypographyDomain, aH as TileflowTypographyStyle, aI as TileflowViewConfig, aJ as ValidationMessage, aK as createManifest, aL as createStyle, aM as createStyleFromProject, aN as createTileflowSessionId, aO as defaultTileflowManifestUrl, aP as defaultTileflowStyleBaseUrl, aQ as getDefaultColors, aR as getDefaultTileflowStyleBaseUrl, aS as getTileflowAnalyticsApiUrl, aT as inferTileflowAnalyticsFromStyleUrl, aU as isTileflowLocalDevHost, aV as loadTileflowManifest, aW as mergeTileflowAnalytics, aX as normalizeTileflowCaptureId, aY as normalizeTileflowCaptureScene, aZ as normalizeTileflowStaticImageSize, a_ as normalizeTileflowUrl, a$ as resolveTileflowAnalyticsRequestUrl, b0 as resolveTileflowManifestMap, b1 as resolveTileflowMapMode, b2 as resolveTileflowRuntimeStyle, b3 as resolveTileflowStaticImageUrl, b4 as resolveTileflowStyleUrl, b5 as shouldLoadTileflowManifest, b6 as startTileflowSession, b7 as tileflowCaptureApplicationTargetSchema, b8 as tileflowCaptureBoundsCameraSchema, b9 as tileflowCaptureCameraSchema, ba as tileflowCaptureCenterCameraSchema, bb as tileflowCaptureIdSchema, bc as tileflowCaptureMapTargetSchema, bd as tileflowCaptureSceneLimits, be as tileflowCaptureSceneNameSchema, bf as tileflowCaptureSceneSchema, bg as tileflowCaptureSceneSchemaVersion, bh as tileflowCaptureTargetSchema, bi as tileflowCaptureViewportSchema, bj as tileflowHostedNotoSansWeights } from './runtime-BMZ_tuba.js';
1
+ import { b as TileflowConfig, c as TileflowProjectConfig, V as ValidationResult, d as TileflowOsmBasemapOptions, e as TileflowOsmBasemapConfig, f as TileflowRoadsModuleOptions, g as TileflowRoadsModuleConfig, h as TileflowLabelsModuleOptions, i as TileflowLabelsModuleConfig, j as TileflowPoiModuleOptions, k as TileflowPoiModuleConfig, l as TileflowStyleOverrideOptions, m as TileflowStyleOverrideModuleConfig } from './runtime-Bg--GMWM.js';
2
+ export { M as MapLibreSprite, n as MapLibreStyle, N as NormalizedTileflowCaptureScene, R as ResolvedTileflowTypography, o as TileflowAdvancedColorConfig, T as TileflowAnalytics, p as TileflowBaseColors, q as TileflowBasemapConfig, r as TileflowBoundaryColorConfig, s as TileflowBuildingColorConfig, t as TileflowBuildingStyleConfig, u as TileflowBuildings, v as TileflowCaptureApplicationTarget, w as TileflowCaptureBoundsCamera, x as TileflowCaptureCamera, y as TileflowCaptureCenterCamera, z as TileflowCaptureMapTarget, A as TileflowCaptureScene, B as TileflowCaptureTarget, C as TileflowCaptureViewport, D as TileflowColor, E as TileflowColorConfig, F as TileflowColors, G as TileflowDensity, H as TileflowFontWeight, I as TileflowFonts, J as TileflowHostedNotoSansWeight, K as TileflowHydroColorConfig, L as TileflowIconSet, O as TileflowIconSetConfig, P as TileflowLabelColorConfig, Q as TileflowLabelDetail, S as TileflowLabelLanguage, U as TileflowLabels, W as TileflowLandcoverColorConfig, X as TileflowLanduseColorConfig, Y as TileflowManifest, Z as TileflowManifestLoadOptions, _ as TileflowMapConfig, $ as TileflowMapMarker, a0 as TileflowMapMode, a1 as TileflowMapModeOptions, a2 as TileflowModuleConfig, a3 as TileflowPoi, a4 as TileflowPoiCategory, a5 as TileflowPoiClassMapping, a6 as TileflowPoiColorConfig, a7 as TileflowPoiColorMode, a8 as TileflowPoiDensity, a9 as TileflowPoiIcons, aa as TileflowPoiLabels, ab as TileflowProjectIconSets, ac as TileflowProjectThemes, ad as TileflowRenderer, ae as TileflowRoadClass, af as TileflowRoadColorConfig, ag as TileflowRoadDetail, ah as TileflowRoadExtras, ai as TileflowRoadHierarchy, aj as TileflowRoadLabelDetail, ak as TileflowRoadOutline, al as TileflowRoadWeight, am as TileflowRoads, an as TileflowRuntimeManifest, ao as TileflowRuntimeManifestMap, ap as TileflowRuntimeManifestMapEntry, aq as TileflowRuntimeStyle, ar as TileflowRuntimeStyleOptions, a as TileflowSessionController, as as TileflowSessionGrantResponse, at as TileflowSourceLayers, au as TileflowStyleLayerOverride, av as TileflowStyleOptions, aw as TileflowTerrain, ax as TileflowTerrainConfig, ay as TileflowTerrainEncoding, az as TileflowTerrainMode, aA as TileflowTheme, aB as TileflowThemeConfig, aC as TileflowThemeMode, aD as TileflowThemeModulesConfig, aE as TileflowThemeName, aF as TileflowTiles, aG as TileflowTilesetConfig, aH as TileflowTypography, aI as TileflowTypographyDomain, aJ as TileflowTypographyStyle, aK as TileflowViewConfig, aL as ValidationMessage, aM as createManifest, aN as createStyle, aO as createStyleFromProject, aP as createTileflowSessionController, aQ as createTileflowSessionId, aR as defaultTileflowManifestUrl, aS as defaultTileflowStyleBaseUrl, aT as getDefaultColors, aU as getDefaultTileflowStyleBaseUrl, aV as getTileflowAnalyticsApiUrl, aW as inferTileflowAnalyticsFromStyleUrl, aX as isTileflowLocalDevHost, aY as loadTileflowManifest, aZ as mergeTileflowAnalytics, a_ as normalizeTileflowCaptureId, a$ as normalizeTileflowCaptureScene, b0 as normalizeTileflowStaticImageSize, b1 as normalizeTileflowUrl, b2 as resolveTileflowAnalyticsRequestUrl, b3 as resolveTileflowManifestMap, b4 as resolveTileflowMapMode, b5 as resolveTileflowRuntimeStyle, b6 as resolveTileflowStaticImageUrl, b7 as resolveTileflowStyleUrl, b8 as shouldLoadTileflowManifest, b9 as startTileflowSession, ba as tileflowCaptureApplicationTargetSchema, bb as tileflowCaptureBoundsCameraSchema, bc as tileflowCaptureCameraSchema, bd as tileflowCaptureCenterCameraSchema, be as tileflowCaptureIdSchema, bf as tileflowCaptureMapTargetSchema, bg as tileflowCaptureSceneLimits, bh as tileflowCaptureSceneNameSchema, bi as tileflowCaptureSceneSchema, bj as tileflowCaptureSceneSchemaVersion, bk as tileflowCaptureTargetSchema, bl as tileflowCaptureViewportSchema, bm as tileflowHostedNotoSansWeights } from './runtime-Bg--GMWM.js';
3
3
  import { z } from 'zod';
4
4
 
5
5
  declare const tileflowThemeSchema: z.ZodObject<{
package/dist/index.js CHANGED
@@ -6821,6 +6821,250 @@ function createTileflowSessionId() {
6821
6821
  }
6822
6822
  return `ses_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
6823
6823
  }
6824
+ function createTileflowSessionController(input) {
6825
+ const fetchGrant = input.fetch ?? globalThis.fetch;
6826
+ const grantTimeoutMs = validateSessionGrantTimeout(input.grantTimeoutMs);
6827
+ const now = input.now ?? (() => /* @__PURE__ */ new Date());
6828
+ const sessionIdFactory = input.sessionIdFactory ?? createTileflowSessionId;
6829
+ const createSession = (startedAt) => ({
6830
+ binding: null,
6831
+ grant: null,
6832
+ grantPromise: null,
6833
+ pendingGrantRequests: 0,
6834
+ requestCount: 0,
6835
+ sessionId: sessionIdFactory(),
6836
+ startedAt
6837
+ });
6838
+ let activeSession = createSession(now().getTime());
6839
+ async function resolveGrant(session, analytics, at) {
6840
+ if (session.grant && session.grant.expiresAt - at > 3e4) {
6841
+ return { grant: session.grant, session };
6842
+ }
6843
+ session.pendingGrantRequests += 1;
6844
+ try {
6845
+ if (!session.grantPromise) {
6846
+ const pending = (async () => {
6847
+ try {
6848
+ const nextGrant = await requestSessionGrant({
6849
+ analytics,
6850
+ at,
6851
+ fetchGrant,
6852
+ timeoutMs: grantTimeoutMs,
6853
+ sessionId: session.sessionId,
6854
+ source: input.source
6855
+ });
6856
+ session.grant = nextGrant;
6857
+ return { grant: nextGrant, session };
6858
+ } catch (error) {
6859
+ if (!(error instanceof TileflowSessionRestartError)) throw error;
6860
+ const replacement = createSession(at);
6861
+ replacement.binding = session.binding;
6862
+ replacement.requestCount = session.pendingGrantRequests;
6863
+ if (activeSession === session) activeSession = replacement;
6864
+ const replacementPending = requestSessionGrant({
6865
+ analytics,
6866
+ at,
6867
+ fetchGrant,
6868
+ timeoutMs: grantTimeoutMs,
6869
+ sessionId: replacement.sessionId,
6870
+ source: input.source
6871
+ }).then((nextGrant) => {
6872
+ replacement.grant = nextGrant;
6873
+ return { grant: nextGrant, session: replacement };
6874
+ });
6875
+ replacement.grantPromise = replacementPending;
6876
+ try {
6877
+ return await replacementPending;
6878
+ } finally {
6879
+ if (replacement.grantPromise === replacementPending) {
6880
+ replacement.grantPromise = null;
6881
+ }
6882
+ }
6883
+ } finally {
6884
+ session.grantPromise = null;
6885
+ }
6886
+ })();
6887
+ session.grantPromise = pending;
6888
+ }
6889
+ return await session.grantPromise;
6890
+ } finally {
6891
+ session.pendingGrantRequests -= 1;
6892
+ }
6893
+ }
6894
+ return {
6895
+ get sessionId() {
6896
+ return activeSession.sessionId;
6897
+ },
6898
+ async resolveRequestUrl(url, analytics) {
6899
+ if (!analytics || !analytics.mapId) return void 0;
6900
+ const at = now().getTime();
6901
+ if (at - activeSession.startedAt >= 6 * 60 * 60 * 1e3 || activeSession.requestCount >= 1e4) {
6902
+ activeSession = createSession(at);
6903
+ }
6904
+ const apiOrigin = originOf(getTileflowAnalyticsApiUrl(analytics));
6905
+ const binding = JSON.stringify([getTileflowAnalyticsApiUrl(analytics), analytics.mapId]);
6906
+ let session = activeSession;
6907
+ const grantedOrigins = session.binding === binding ? session.grant?.resourceOrigins : void 0;
6908
+ if (!isEligibleResource(url, analytics.mapId, apiOrigin, grantedOrigins)) {
6909
+ return void 0;
6910
+ }
6911
+ if (session.binding !== null && session.binding !== binding) {
6912
+ session = createSession(at);
6913
+ activeSession = session;
6914
+ }
6915
+ session.binding = binding;
6916
+ session.requestCount += 1;
6917
+ const resolved = await resolveGrant(session, analytics, at);
6918
+ const nextUrl = new URL(url);
6919
+ nextUrl.searchParams.set("session", resolved.session.sessionId);
6920
+ nextUrl.searchParams.set("map", analytics.mapId);
6921
+ if (analytics.styleId) nextUrl.searchParams.set("styleId", analytics.styleId);
6922
+ if (resolved.grant.token) nextUrl.searchParams.set("grant", resolved.grant.token);
6923
+ return nextUrl.toString();
6924
+ }
6925
+ };
6926
+ }
6927
+ async function requestSessionGrant(input) {
6928
+ return runWithinSessionGrantTimeout(input.timeoutMs, async (signal) => {
6929
+ const apiUrl = getTileflowAnalyticsApiUrl(input.analytics);
6930
+ const response = await input.fetchGrant(`${apiUrl}/v1/sessions/start`, {
6931
+ body: JSON.stringify({
6932
+ mapId: input.analytics.mapId,
6933
+ sdkVersion: input.analytics.sdkVersion,
6934
+ sessionId: input.sessionId,
6935
+ source: input.analytics.source ?? input.source,
6936
+ styleId: input.analytics.styleId
6937
+ }),
6938
+ credentials: "omit",
6939
+ headers: { "Content-Type": "application/json" },
6940
+ method: "POST",
6941
+ signal
6942
+ });
6943
+ throwIfSessionGrantAborted(signal);
6944
+ const body = await response.json().catch(() => null);
6945
+ throwIfSessionGrantAborted(signal);
6946
+ if (response.status === 409 && isRecord6(body) && body.code === "COMMERCIAL_SESSION_RESTART_REQUIRED" && body.ok === false && body.retryWithNewSession === true && body.sessionId === input.sessionId) {
6947
+ throw new TileflowSessionRestartError();
6948
+ }
6949
+ if (!response.ok) {
6950
+ throw new Error(`Tileflow session grant failed: ${response.status}`);
6951
+ }
6952
+ const parsed = parseSessionGrantResponse(body, input.sessionId);
6953
+ if (!parsed) {
6954
+ throw new Error("Tileflow session grant response was invalid.");
6955
+ }
6956
+ const expiresAt = parsed.grant ? Date.parse(parsed.expiresAt) : input.at + 6e4;
6957
+ if (response.status === 201 && (!parsed.grant || !parsed.expiresAt) || parsed.grant && (!Number.isFinite(expiresAt) || expiresAt <= input.at)) {
6958
+ throw new Error("Tileflow session grant response was invalid.");
6959
+ }
6960
+ return {
6961
+ expiresAt,
6962
+ resourceOrigins: new Set(
6963
+ [originOf(apiUrl), ...(parsed.resourceOrigins ?? []).map(originOf)].filter(
6964
+ (value) => Boolean(value)
6965
+ )
6966
+ ),
6967
+ token: parsed.grant ?? null
6968
+ };
6969
+ });
6970
+ }
6971
+ function validateSessionGrantTimeout(value) {
6972
+ const resolved = value ?? 1e4;
6973
+ if (!Number.isSafeInteger(resolved) || resolved < 1 || resolved > 12e4) {
6974
+ throw new Error("Tileflow session grantTimeoutMs must be an integer from 1 to 120000");
6975
+ }
6976
+ return resolved;
6977
+ }
6978
+ async function runWithinSessionGrantTimeout(timeoutMs, operation) {
6979
+ const controller = new AbortController();
6980
+ const timeoutError = new Error(`Tileflow session grant timed out after ${timeoutMs}ms`);
6981
+ const timeout = setTimeout(() => controller.abort(timeoutError), timeoutMs);
6982
+ try {
6983
+ return await raceSessionGrantWithAbort(operation(controller.signal), controller.signal);
6984
+ } finally {
6985
+ clearTimeout(timeout);
6986
+ }
6987
+ }
6988
+ function raceSessionGrantWithAbort(operation, signal) {
6989
+ if (signal.aborted) return Promise.reject(sessionGrantAbortReason(signal));
6990
+ return new Promise((resolve, reject) => {
6991
+ const onAbort = () => {
6992
+ cleanup();
6993
+ reject(sessionGrantAbortReason(signal));
6994
+ };
6995
+ const cleanup = () => signal.removeEventListener("abort", onAbort);
6996
+ signal.addEventListener("abort", onAbort, { once: true });
6997
+ operation.then(
6998
+ (value) => {
6999
+ cleanup();
7000
+ resolve(value);
7001
+ },
7002
+ (error) => {
7003
+ cleanup();
7004
+ reject(error);
7005
+ }
7006
+ );
7007
+ });
7008
+ }
7009
+ function throwIfSessionGrantAborted(signal) {
7010
+ if (signal.aborted) throw sessionGrantAbortReason(signal);
7011
+ }
7012
+ function sessionGrantAbortReason(signal) {
7013
+ if (signal.reason instanceof Error) return signal.reason;
7014
+ const error = new Error("Tileflow session grant was aborted");
7015
+ error.name = "AbortError";
7016
+ return error;
7017
+ }
7018
+ function parseSessionGrantResponse(value, expectedSessionId) {
7019
+ if (!isRecord6(value) || value.ok !== true || value.sessionId !== expectedSessionId) return null;
7020
+ if (value.usageMode !== void 0 && value.usageMode !== "session") return null;
7021
+ if (value.billingEnabled !== void 0 && value.billingEnabled !== false) return null;
7022
+ if (value.disposition !== void 0 && value.disposition !== "ordinary" && value.disposition !== "unbilled_fail_open") {
7023
+ return null;
7024
+ }
7025
+ if (value.meterMode !== void 0 && value.meterMode !== "disabled" && value.meterMode !== "shadow" && value.meterMode !== "enforced") {
7026
+ return null;
7027
+ }
7028
+ if (value.grant !== void 0 && (typeof value.grant !== "string" || value.grant.length === 0 || value.grant.length > 8192)) {
7029
+ return null;
7030
+ }
7031
+ if (value.expiresAt !== void 0 && typeof value.expiresAt !== "string") return null;
7032
+ if (value.grant !== void 0 && value.expiresAt === void 0) return null;
7033
+ if (value.resourceOrigins !== void 0 && (!Array.isArray(value.resourceOrigins) || value.resourceOrigins.length > 32 || value.resourceOrigins.some((origin) => !originOf(origin)))) {
7034
+ return null;
7035
+ }
7036
+ return value;
7037
+ }
7038
+ function isRecord6(value) {
7039
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
7040
+ }
7041
+ var TileflowSessionRestartError = class extends Error {
7042
+ constructor() {
7043
+ super("Tileflow session must restart");
7044
+ this.name = "TileflowSessionRestartError";
7045
+ }
7046
+ };
7047
+ function isEligibleResource(value, mapId, apiOrigin, grantedOrigins) {
7048
+ try {
7049
+ const url = new URL(value);
7050
+ if (url.username || url.password) return false;
7051
+ const originAllowed = url.origin === apiOrigin || grantedOrigins?.has(url.origin) || apiOrigin === "https://api.tileflow.dev" && url.origin === "https://cdn.tileflow.dev";
7052
+ if (!originAllowed) return false;
7053
+ return url.pathname === `/maps/${encodeURIComponent(mapId)}/style.json` || url.pathname.startsWith("/tiles/") || url.pathname.startsWith("/v1/tiles/") || url.pathname.startsWith("/fonts/") || url.pathname.startsWith("/sprites/");
7054
+ } catch {
7055
+ return false;
7056
+ }
7057
+ }
7058
+ function originOf(value) {
7059
+ if (typeof value !== "string") return null;
7060
+ try {
7061
+ const url = new URL(value);
7062
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) return null;
7063
+ return url.origin;
7064
+ } catch {
7065
+ return null;
7066
+ }
7067
+ }
6824
7068
  function resolveTileflowAnalyticsRequestUrl(url, analytics, sessionId) {
6825
7069
  if (!analytics || analytics.enabled === false) {
6826
7070
  return void 0;
@@ -6843,7 +7087,7 @@ function startTileflowSession(analytics, input) {
6843
7087
  if (!analytics || analytics.enabled === false || !input.mapId || typeof navigator === "undefined") {
6844
7088
  return;
6845
7089
  }
6846
- const endpoint = analytics.endpoint ?? `${getTileflowAnalyticsApiUrl(analytics)}/v1/sessions/start`;
7090
+ const endpoint = analytics.endpoint ?? `${getTileflowAnalyticsApiUrl(analytics)}/v1/sessions/analytics`;
6847
7091
  const payload = JSON.stringify({
6848
7092
  mapId: input.mapId,
6849
7093
  metadata: analytics.metadata,
@@ -6866,7 +7110,7 @@ function startTileflowSession(analytics, input) {
6866
7110
  },
6867
7111
  keepalive: true,
6868
7112
  method: "POST"
6869
- });
7113
+ }).catch(() => void 0);
6870
7114
  }
6871
7115
  function getTileflowAnalyticsApiUrl(analytics) {
6872
7116
  if (analytics.apiUrl) {
@@ -6883,9 +7127,6 @@ function getTileflowAnalyticsApiUrl(analytics) {
6883
7127
  return "https://api.tileflow.dev";
6884
7128
  }
6885
7129
  function mergeTileflowAnalytics(userAnalytics, runtimeAnalytics) {
6886
- if (userAnalytics?.enabled === false) {
6887
- return userAnalytics;
6888
- }
6889
7130
  if (!userAnalytics && !runtimeAnalytics?.mapId) {
6890
7131
  return void 0;
6891
7132
  }
@@ -6994,7 +7235,7 @@ function normalizeTileflowStaticImageSize(input) {
6994
7235
  function inferTileflowAnalyticsFromStyleUrl(styleUrl) {
6995
7236
  try {
6996
7237
  const url = new URL(styleUrl, getTileflowLocationOrigin());
6997
- const match = url.pathname.match(/\/(?:v1\/)?maps\/([^/]+)\/style\.json$/);
7238
+ const match = url.pathname.match(/^\/maps\/([^/]+)\/style\.json$/);
6998
7239
  if (!match?.[1]) {
6999
7240
  return void 0;
7000
7241
  }
@@ -7037,6 +7278,7 @@ export {
7037
7278
  createManifest,
7038
7279
  createStyle,
7039
7280
  createStyleFromProject,
7281
+ createTileflowSessionController,
7040
7282
  createTileflowSessionId,
7041
7283
  defaultTileflowManifestUrl,
7042
7284
  defaultTileflowStyleBaseUrl,
@@ -558,6 +558,23 @@ type TileflowAnalytics = {
558
558
  source?: string;
559
559
  styleId?: string;
560
560
  };
561
+ type TileflowSessionGrantResponse = {
562
+ billingEnabled?: false;
563
+ disposition?: 'ordinary' | 'unbilled_fail_open';
564
+ expiresAt?: string;
565
+ grant?: string;
566
+ meterMode?: 'disabled' | 'shadow' | 'enforced';
567
+ ok: boolean;
568
+ resourceOrigins?: string[];
569
+ sessionId: string;
570
+ usageMode?: 'session';
571
+ code?: string;
572
+ retryWithNewSession?: boolean;
573
+ };
574
+ type TileflowSessionController = {
575
+ readonly sessionId: string;
576
+ resolveRequestUrl(url: string, analytics: TileflowAnalytics | undefined): Promise<string | undefined>;
577
+ };
561
578
  type TileflowMapMarker = {
562
579
  id: string;
563
580
  coordinates: [number, number];
@@ -622,6 +639,13 @@ declare function resolveTileflowStyleUrl(mapName: string, styleBaseUrl?: string)
622
639
  declare function getDefaultTileflowStyleBaseUrl(): string;
623
640
  declare function normalizeTileflowUrl(value: string): string;
624
641
  declare function createTileflowSessionId(): string;
642
+ declare function createTileflowSessionController(input: {
643
+ fetch?: typeof fetch;
644
+ grantTimeoutMs?: number;
645
+ now?: () => Date;
646
+ sessionIdFactory?: () => string;
647
+ source: string;
648
+ }): TileflowSessionController;
625
649
  declare function resolveTileflowAnalyticsRequestUrl(url: string, analytics: TileflowAnalytics | undefined, sessionId: string): string | undefined;
626
650
  declare function startTileflowSession(analytics: TileflowAnalytics | undefined, input: {
627
651
  mapId?: string;
@@ -653,4 +677,4 @@ declare function normalizeTileflowStaticImageSize(input: {
653
677
  };
654
678
  declare function inferTileflowAnalyticsFromStyleUrl(styleUrl: string): TileflowAnalytics | undefined;
655
679
 
656
- export { type TileflowMapMode as $, type TileflowCaptureTarget as A, type TileflowCaptureViewport as B, type TileflowColor as C, type TileflowColorConfig as D, type TileflowColors as E, type TileflowDensity as F, type TileflowFontWeight as G, type TileflowFonts as H, type TileflowHostedNotoSansWeight as I, type TileflowHydroColorConfig as J, type TileflowIconSet as K, type TileflowIconSetConfig as L, type MapLibreSprite as M, type NormalizedTileflowCaptureScene as N, type TileflowLabelColorConfig as O, type TileflowLabelDetail as P, type TileflowLabelLanguage as Q, type ResolvedTileflowTypography as R, type TileflowLabels as S, type TileflowAnalytics as T, type TileflowLandcoverColorConfig as U, type ValidationResult as V, type TileflowLanduseColorConfig as W, type TileflowManifest as X, type TileflowManifestLoadOptions as Y, type TileflowMapConfig as Z, type TileflowMapMarker as _, type TileflowConfig as a, resolveTileflowAnalyticsRequestUrl as a$, type TileflowMapModeOptions as a0, type TileflowModuleConfig as a1, type TileflowPoi as a2, type TileflowPoiCategory as a3, type TileflowPoiClassMapping as a4, type TileflowPoiColorConfig as a5, type TileflowPoiColorMode as a6, type TileflowPoiDensity as a7, type TileflowPoiIcons as a8, type TileflowPoiLabels as a9, type TileflowThemeMode as aA, type TileflowThemeModulesConfig as aB, type TileflowThemeName as aC, type TileflowTiles as aD, type TileflowTilesetConfig as aE, type TileflowTypography as aF, type TileflowTypographyDomain as aG, type TileflowTypographyStyle as aH, type TileflowViewConfig as aI, type ValidationMessage as aJ, createManifest as aK, createStyle as aL, createStyleFromProject as aM, createTileflowSessionId as aN, defaultTileflowManifestUrl as aO, defaultTileflowStyleBaseUrl as aP, getDefaultColors as aQ, getDefaultTileflowStyleBaseUrl as aR, getTileflowAnalyticsApiUrl as aS, inferTileflowAnalyticsFromStyleUrl as aT, isTileflowLocalDevHost as aU, loadTileflowManifest as aV, mergeTileflowAnalytics as aW, normalizeTileflowCaptureId as aX, normalizeTileflowCaptureScene as aY, normalizeTileflowStaticImageSize as aZ, normalizeTileflowUrl as a_, type TileflowProjectIconSets as aa, type TileflowProjectThemes as ab, type TileflowRenderer as ac, type TileflowRoadClass as ad, type TileflowRoadColorConfig as ae, type TileflowRoadDetail as af, type TileflowRoadExtras as ag, type TileflowRoadHierarchy as ah, type TileflowRoadLabelDetail as ai, type TileflowRoadOutline as aj, type TileflowRoadWeight as ak, type TileflowRoads as al, type TileflowRuntimeManifest as am, type TileflowRuntimeManifestMap as an, type TileflowRuntimeManifestMapEntry as ao, type TileflowRuntimeStyle as ap, type TileflowRuntimeStyleOptions as aq, type TileflowSourceLayers as ar, type TileflowStyleLayerOverride as as, type TileflowStyleOptions as at, type TileflowTerrain as au, type TileflowTerrainConfig as av, type TileflowTerrainEncoding as aw, type TileflowTerrainMode as ax, type TileflowTheme as ay, type TileflowThemeConfig as az, type TileflowProjectConfig as b, resolveTileflowManifestMap as b0, resolveTileflowMapMode as b1, resolveTileflowRuntimeStyle as b2, resolveTileflowStaticImageUrl as b3, resolveTileflowStyleUrl as b4, shouldLoadTileflowManifest as b5, startTileflowSession as b6, tileflowCaptureApplicationTargetSchema as b7, tileflowCaptureBoundsCameraSchema as b8, tileflowCaptureCameraSchema as b9, tileflowCaptureCenterCameraSchema as ba, tileflowCaptureIdSchema as bb, tileflowCaptureMapTargetSchema as bc, tileflowCaptureSceneLimits as bd, tileflowCaptureSceneNameSchema as be, tileflowCaptureSceneSchema as bf, tileflowCaptureSceneSchemaVersion as bg, tileflowCaptureTargetSchema as bh, tileflowCaptureViewportSchema as bi, tileflowHostedNotoSansWeights as bj, type TileflowOsmBasemapOptions as c, type TileflowOsmBasemapConfig as d, type TileflowRoadsModuleOptions as e, type TileflowRoadsModuleConfig as f, type TileflowLabelsModuleOptions as g, type TileflowLabelsModuleConfig as h, type TileflowPoiModuleOptions as i, type TileflowPoiModuleConfig as j, type TileflowStyleOverrideOptions as k, type TileflowStyleOverrideModuleConfig as l, type MapLibreStyle as m, type TileflowAdvancedColorConfig as n, type TileflowBaseColors as o, type TileflowBasemapConfig as p, type TileflowBoundaryColorConfig as q, type TileflowBuildingColorConfig as r, type TileflowBuildingStyleConfig as s, type TileflowBuildings as t, type TileflowCaptureApplicationTarget as u, type TileflowCaptureBoundsCamera as v, type TileflowCaptureCamera as w, type TileflowCaptureCenterCamera as x, type TileflowCaptureMapTarget as y, type TileflowCaptureScene as z };
680
+ export { type TileflowMapMarker as $, type TileflowCaptureScene as A, type TileflowCaptureTarget as B, type TileflowCaptureViewport as C, type TileflowColor as D, type TileflowColorConfig as E, type TileflowColors as F, type TileflowDensity as G, type TileflowFontWeight as H, type TileflowFonts as I, type TileflowHostedNotoSansWeight as J, type TileflowHydroColorConfig as K, type TileflowIconSet as L, type MapLibreSprite as M, type NormalizedTileflowCaptureScene as N, type TileflowIconSetConfig as O, type TileflowLabelColorConfig as P, type TileflowLabelDetail as Q, type ResolvedTileflowTypography as R, type TileflowLabelLanguage as S, type TileflowAnalytics as T, type TileflowLabels as U, type ValidationResult as V, type TileflowLandcoverColorConfig as W, type TileflowLanduseColorConfig as X, type TileflowManifest as Y, type TileflowManifestLoadOptions as Z, type TileflowMapConfig as _, type TileflowSessionController as a, normalizeTileflowCaptureScene as a$, type TileflowMapMode as a0, type TileflowMapModeOptions as a1, type TileflowModuleConfig as a2, type TileflowPoi as a3, type TileflowPoiCategory as a4, type TileflowPoiClassMapping as a5, type TileflowPoiColorConfig as a6, type TileflowPoiColorMode as a7, type TileflowPoiDensity as a8, type TileflowPoiIcons as a9, type TileflowTheme as aA, type TileflowThemeConfig as aB, type TileflowThemeMode as aC, type TileflowThemeModulesConfig as aD, type TileflowThemeName as aE, type TileflowTiles as aF, type TileflowTilesetConfig as aG, type TileflowTypography as aH, type TileflowTypographyDomain as aI, type TileflowTypographyStyle as aJ, type TileflowViewConfig as aK, type ValidationMessage as aL, createManifest as aM, createStyle as aN, createStyleFromProject as aO, createTileflowSessionController as aP, createTileflowSessionId as aQ, defaultTileflowManifestUrl as aR, defaultTileflowStyleBaseUrl as aS, getDefaultColors as aT, getDefaultTileflowStyleBaseUrl as aU, getTileflowAnalyticsApiUrl as aV, inferTileflowAnalyticsFromStyleUrl as aW, isTileflowLocalDevHost as aX, loadTileflowManifest as aY, mergeTileflowAnalytics as aZ, normalizeTileflowCaptureId as a_, type TileflowPoiLabels as aa, type TileflowProjectIconSets as ab, type TileflowProjectThemes as ac, type TileflowRenderer as ad, type TileflowRoadClass as ae, type TileflowRoadColorConfig as af, type TileflowRoadDetail as ag, type TileflowRoadExtras as ah, type TileflowRoadHierarchy as ai, type TileflowRoadLabelDetail as aj, type TileflowRoadOutline as ak, type TileflowRoadWeight as al, type TileflowRoads as am, type TileflowRuntimeManifest as an, type TileflowRuntimeManifestMap as ao, type TileflowRuntimeManifestMapEntry as ap, type TileflowRuntimeStyle as aq, type TileflowRuntimeStyleOptions as ar, type TileflowSessionGrantResponse as as, type TileflowSourceLayers as at, type TileflowStyleLayerOverride as au, type TileflowStyleOptions as av, type TileflowTerrain as aw, type TileflowTerrainConfig as ax, type TileflowTerrainEncoding as ay, type TileflowTerrainMode as az, type TileflowConfig as b, normalizeTileflowStaticImageSize as b0, normalizeTileflowUrl as b1, resolveTileflowAnalyticsRequestUrl as b2, resolveTileflowManifestMap as b3, resolveTileflowMapMode as b4, resolveTileflowRuntimeStyle as b5, resolveTileflowStaticImageUrl as b6, resolveTileflowStyleUrl as b7, shouldLoadTileflowManifest as b8, startTileflowSession as b9, tileflowCaptureApplicationTargetSchema as ba, tileflowCaptureBoundsCameraSchema as bb, tileflowCaptureCameraSchema as bc, tileflowCaptureCenterCameraSchema as bd, tileflowCaptureIdSchema as be, tileflowCaptureMapTargetSchema as bf, tileflowCaptureSceneLimits as bg, tileflowCaptureSceneNameSchema as bh, tileflowCaptureSceneSchema as bi, tileflowCaptureSceneSchemaVersion as bj, tileflowCaptureTargetSchema as bk, tileflowCaptureViewportSchema as bl, tileflowHostedNotoSansWeights as bm, type TileflowProjectConfig as c, type TileflowOsmBasemapOptions as d, type TileflowOsmBasemapConfig as e, type TileflowRoadsModuleOptions as f, type TileflowRoadsModuleConfig as g, type TileflowLabelsModuleOptions as h, type TileflowLabelsModuleConfig as i, type TileflowPoiModuleOptions as j, type TileflowPoiModuleConfig as k, type TileflowStyleOverrideOptions as l, type TileflowStyleOverrideModuleConfig as m, type MapLibreStyle as n, type TileflowAdvancedColorConfig as o, type TileflowBaseColors as p, type TileflowBasemapConfig as q, type TileflowBoundaryColorConfig as r, type TileflowBuildingColorConfig as s, type TileflowBuildingStyleConfig as t, type TileflowBuildings as u, type TileflowCaptureApplicationTarget as v, type TileflowCaptureBoundsCamera as w, type TileflowCaptureCamera as x, type TileflowCaptureCenterCamera as y, type TileflowCaptureMapTarget as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/core",
3
- "version": "0.1.0-alpha.17",
3
+ "version": "0.1.0-alpha.18",
4
4
  "description": "Typed Tileflow config and MapLibre style compiler.",
5
5
  "type": "module",
6
6
  "sideEffects": false,