@tileflow/core 0.1.0-alpha.14 → 0.1.0-alpha.17
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 +119 -6
- package/dist/browser.d.ts +74 -0
- package/dist/browser.js +320 -0
- package/dist/index.d.ts +701 -464
- package/dist/index.js +1491 -282
- package/dist/runtime-BMZ_tuba.d.ts +656 -0
- package/package.json +13 -5
package/README.md
CHANGED
|
@@ -42,18 +42,31 @@ export default defineTileflow({
|
|
|
42
42
|
},
|
|
43
43
|
typography: {
|
|
44
44
|
font: 'Inter',
|
|
45
|
+
roads: {weight: 'bold'},
|
|
45
46
|
},
|
|
46
47
|
},
|
|
47
48
|
},
|
|
48
49
|
maps: {
|
|
49
50
|
madrid: {
|
|
50
|
-
basemap: osm(),
|
|
51
|
+
basemap: osm({version: '2026-06-07'}),
|
|
51
52
|
icons: 'brand',
|
|
52
53
|
theme: 'light',
|
|
53
54
|
modules: [
|
|
54
|
-
labels({roads: 'major'}),
|
|
55
|
-
poi({
|
|
56
|
-
|
|
55
|
+
labels({roads: 'major', roadClasses: ['motorway', 'trunk', 'primary']}),
|
|
56
|
+
poi({
|
|
57
|
+
categories: ['food', 'coffee', 'culture', 'transit'],
|
|
58
|
+
color: 'category',
|
|
59
|
+
density: 'balanced',
|
|
60
|
+
icons: 'essential',
|
|
61
|
+
}),
|
|
62
|
+
roads({
|
|
63
|
+
detail: 'streets',
|
|
64
|
+
hierarchy: 'clear',
|
|
65
|
+
oneWayMarkers: true,
|
|
66
|
+
outline: 'subtle',
|
|
67
|
+
weight: 'regular',
|
|
68
|
+
widthScale: {motorway: 1.15, minor: 0.9},
|
|
69
|
+
}),
|
|
57
70
|
],
|
|
58
71
|
layers: {
|
|
59
72
|
'highway-primary': {
|
|
@@ -71,6 +84,43 @@ export default defineTileflow({
|
|
|
71
84
|
});
|
|
72
85
|
```
|
|
73
86
|
|
|
87
|
+
## Capture scenes
|
|
88
|
+
|
|
89
|
+
Commit named visual scenes beside the maps they exercise. Scenes are strict, bounded test inputs;
|
|
90
|
+
they do not change style or manifest identity.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
export default defineTileflow({
|
|
94
|
+
maps: {
|
|
95
|
+
madrid: {basemap: osm(), theme: 'light'},
|
|
96
|
+
},
|
|
97
|
+
scenes: {
|
|
98
|
+
'madrid-desktop': {
|
|
99
|
+
map: 'madrid',
|
|
100
|
+
camera: {type: 'center', center: [-3.7038, 40.4168], zoom: 12},
|
|
101
|
+
viewport: {width: 1280, height: 800, dpr: 1},
|
|
102
|
+
},
|
|
103
|
+
'madrid-product': {
|
|
104
|
+
map: 'madrid',
|
|
105
|
+
camera: {type: 'center', center: [-3.7038, 40.4168], zoom: 12},
|
|
106
|
+
viewport: {width: 390, height: 844, dpr: 2},
|
|
107
|
+
target: {
|
|
108
|
+
kind: 'application',
|
|
109
|
+
path: '/maps/madrid',
|
|
110
|
+
captureId: 'product-map',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The default target renders autonomously with no server. An application target uses the app's
|
|
118
|
+
normal loopback development server and selects exactly one wrapper by `captureId`, explicit
|
|
119
|
+
selector, or map name. Cameras accept `center` or `bounds`; viewports are 64–4096 CSS pixels at DPR
|
|
120
|
+
1 or 2 within the public physical-pixel budget. Standalone capture applies the camera; an
|
|
121
|
+
application route must render it itself because capture does not mutate application component
|
|
122
|
+
state. Use `tileflow capture <scene>` or the `@tileflow/capture` Node package to render them.
|
|
123
|
+
|
|
74
124
|
## Style contract
|
|
75
125
|
|
|
76
126
|
Tileflow keeps these levels separate:
|
|
@@ -80,6 +130,8 @@ Tileflow keeps these levels separate:
|
|
|
80
130
|
- `theme.modules.<name>` contains advanced, module-specific tokens. These are granular: setting `theme.modules.roads.rail` does not recolor primary roads.
|
|
81
131
|
- `maps.<name>.icons` can point at a reusable root icon set, a hosted MapLibre sprite URL, or a local icon folder for Vite/build output.
|
|
82
132
|
- `maps.<name>.modules` enables semantic behavior such as `labels()`, `poi()`, and `roads()`. Road colors remain in `theme.modules.roads`; `roads()` controls detail, hierarchy, weight, outline, and optional transport extras.
|
|
133
|
+
- `typography` sets a global font/weight and optional `places`, `roads`, `water`, and `poi` overrides. The hosted Noto Sans endpoint guarantees `regular` and `bold`; custom glyph endpoints may provide the other typed weights.
|
|
134
|
+
- `buildingStyle` controls generated building fill opacity, outline opacity/width, and a height threshold that selects the theme's low/high-rise colors. Generated landuse and landcover layers consume their corresponding module colors.
|
|
83
135
|
- `maps.<name>.renderer` can be `auto`, `osm-bright`, or `generated`. `auto` preserves the default template when compatible; `generated` always uses Tileflow's generated layers; `osm-bright` throws if the map config cannot be rendered with the template.
|
|
84
136
|
- `layers` is the raw MapLibre escape hatch for exact layer patches.
|
|
85
137
|
|
|
@@ -95,6 +147,8 @@ roads({
|
|
|
95
147
|
hierarchy: 'strong',
|
|
96
148
|
weight: 'regular',
|
|
97
149
|
outline: 'strong',
|
|
150
|
+
oneWayMarkers: true,
|
|
151
|
+
widthScale: {motorway: 1.15, primary: 1.05, minor: 0.9},
|
|
98
152
|
extras: {
|
|
99
153
|
paths: false,
|
|
100
154
|
rail: false,
|
|
@@ -108,6 +162,8 @@ roads({
|
|
|
108
162
|
adds service roads and tracks. `hierarchy` accepts `subtle`, `clear`, or `strong`; `weight`
|
|
109
163
|
accepts `thin`, `regular`, or `bold`; and `outline` accepts `none`, `subtle`, or `strong`.
|
|
110
164
|
`extras.paths`, `extras.rail`, and `extras.ferry` are independent and default to `false`.
|
|
165
|
+
`widthScale` accepts bounded multipliers for selected road classes, and `oneWayMarkers` adds
|
|
166
|
+
direction markers to eligible one-way roads.
|
|
111
167
|
|
|
112
168
|
Without options, `roads()` uses `detail: 'streets'`, `hierarchy: 'clear'`,
|
|
113
169
|
`weight: 'regular'`, `outline: 'subtle'`, and all extras disabled. Named visual recipes belong
|
|
@@ -124,6 +180,7 @@ labels({
|
|
|
124
180
|
language: 'auto',
|
|
125
181
|
places: 'major',
|
|
126
182
|
roads: 'major',
|
|
183
|
+
roadClasses: ['motorway', 'trunk', 'primary', 'secondary'],
|
|
127
184
|
water: 'major',
|
|
128
185
|
});
|
|
129
186
|
```
|
|
@@ -140,12 +197,46 @@ are also clamped to the classes currently visible through `roads({detail})`, so
|
|
|
140
197
|
is the intersection of both modules. `language` accepts `auto`, `local`, or a language code such as
|
|
141
198
|
`en` or `es`.
|
|
142
199
|
|
|
200
|
+
`roadClasses` is an optional explicit eligibility list. It is intersected with both the label
|
|
201
|
+
detail and visible `roads()` classes, so it cannot label a road family that the map has hidden.
|
|
202
|
+
|
|
143
203
|
Without options, `labels()` uses `language: 'auto'` and `major` for all three label families.
|
|
144
204
|
Label colors remain in `theme.modules.labels`; font family and weight remain in `typography`.
|
|
145
205
|
|
|
206
|
+
## POI module
|
|
207
|
+
|
|
208
|
+
POI category policy is resolved once for filtering, icon selection, label color, and collision
|
|
209
|
+
priority:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
poi({
|
|
213
|
+
categories: ['food', 'culture', 'lodging'],
|
|
214
|
+
classMapping: {culture: ['planetarium']},
|
|
215
|
+
color: 'category',
|
|
216
|
+
density: 'balanced',
|
|
217
|
+
icons: 'full',
|
|
218
|
+
labels: 'full',
|
|
219
|
+
placement: {coupleIconAndLabel: true, iconPadding: 4, textPadding: 8},
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Built-in mappings inspect both `class` and `subclass`; `classMapping` adds source-specific values
|
|
224
|
+
without removing the defaults. `color: 'category'` uses `theme.modules.poi` colors end to end.
|
|
225
|
+
Named density is opt-in and adds bounded category-aware rank gates plus stable sort priority;
|
|
226
|
+
omitting it preserves legacy layer behavior. Raw source rank expressions remain available through
|
|
227
|
+
`styleOverride` when a particular dataset needs a bespoke rule.
|
|
228
|
+
|
|
229
|
+
## Tile-source revision
|
|
230
|
+
|
|
231
|
+
Set `version` on `osm({...})`, `maps.<name>.tiles`, or a named project tileset to pin an immutable
|
|
232
|
+
archive identifier. Map-level `tiles.version` wins over basemap and named-tileset defaults. Tileflow
|
|
233
|
+
adds it as `archiveVersion` to the TileJSON URL and records it as
|
|
234
|
+
`metadata['tileflow:tilesetVersion']`; it never infers stability from an unversioned response.
|
|
235
|
+
|
|
146
236
|
## Public API
|
|
147
237
|
|
|
148
|
-
|
|
238
|
+
Application config, compilation, runtime resolution, and capture-scene APIs come from the package
|
|
239
|
+
root:
|
|
149
240
|
|
|
150
241
|
```ts
|
|
151
242
|
import {
|
|
@@ -160,6 +251,28 @@ import {
|
|
|
160
251
|
} from '@tileflow/core';
|
|
161
252
|
```
|
|
162
253
|
|
|
163
|
-
|
|
254
|
+
Capture scene schemas, types, normalization, limits, and schema version are also exported from the
|
|
255
|
+
package root. See the [local visual capture contract](https://tileflow.dev/docs/agent-workflow) for
|
|
256
|
+
the CLI workflow and readiness boundary.
|
|
257
|
+
|
|
258
|
+
Framework adapters can import the browser-only lifecycle kernel explicitly:
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
import {
|
|
262
|
+
attachTileflowMapLifecycle,
|
|
263
|
+
createTileflowMarkerController,
|
|
264
|
+
createTileflowSessionStarter,
|
|
265
|
+
createTileflowTransformRequest,
|
|
266
|
+
} from '@tileflow/core/browser';
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The browser subpath is not re-exported from the root. It is safe to import during SSR, does not
|
|
270
|
+
depend on MapLibre, and reads no browser globals during module evaluation. Adapter authors inject
|
|
271
|
+
their frame scheduler plus MapLibre event and marker operations after mount. The package contract
|
|
272
|
+
is documented in
|
|
273
|
+
[`docs/contracts/framework-browser-runtime.md`](../../docs/contracts/framework-browser-runtime.md).
|
|
274
|
+
|
|
275
|
+
Other subpaths under `src/`, `themes/`, `templates/`, and `modules/` are internal implementation
|
|
276
|
+
details and can change during alpha releases.
|
|
164
277
|
|
|
165
278
|
Docs: https://tileflow.dev/docs
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { T as TileflowAnalytics } from './runtime-BMZ_tuba.js';
|
|
2
|
+
import 'zod';
|
|
3
|
+
|
|
4
|
+
type TileflowMapReadinessState = 'error' | 'idle' | 'loading';
|
|
5
|
+
type TileflowMapLifecycleEvent = 'dataloading' | 'error' | 'idle' | 'load' | 'styledataloading';
|
|
6
|
+
type TileflowFrameScheduler<TFrame> = {
|
|
7
|
+
cancelFrame: (frame: TFrame) => void;
|
|
8
|
+
requestFrame: (callback: () => void) => TFrame;
|
|
9
|
+
};
|
|
10
|
+
type TileflowMapLifecycleSubscriber<TMap> = (map: TMap, event: TileflowMapLifecycleEvent, listener: () => void) => () => void;
|
|
11
|
+
type TileflowSessionSender = (analytics: TileflowAnalytics | undefined, input: {
|
|
12
|
+
mapId?: string;
|
|
13
|
+
sessionId: string;
|
|
14
|
+
source: string;
|
|
15
|
+
styleId?: string;
|
|
16
|
+
}) => void;
|
|
17
|
+
type TileflowSessionStarter = {
|
|
18
|
+
clear: () => void;
|
|
19
|
+
start: (analytics: TileflowAnalytics | undefined, styleId?: string) => boolean;
|
|
20
|
+
};
|
|
21
|
+
type TileflowMapLifecycleAttachment = {
|
|
22
|
+
dispose: () => void;
|
|
23
|
+
invalidate: (state?: TileflowMapReadinessState) => void;
|
|
24
|
+
};
|
|
25
|
+
type TileflowTransformRequestParameters = {
|
|
26
|
+
url: string;
|
|
27
|
+
};
|
|
28
|
+
type TileflowComposedRequest<TRequest extends TileflowTransformRequestParameters> = Pick<TRequest, 'url'> & Partial<Omit<TRequest, 'url'>>;
|
|
29
|
+
type TileflowTransformRequest<TRequest extends TileflowTransformRequestParameters, TResourceType> = (url: string, resourceType?: TResourceType) => Promise<TileflowComposedRequest<TRequest>> | TileflowComposedRequest<TRequest> | undefined;
|
|
30
|
+
type TileflowUserTransformRequest<TRequest extends TileflowTransformRequestParameters, TResourceType> = (url: string, resourceType?: TResourceType) => Promise<TRequest> | TRequest | undefined;
|
|
31
|
+
type TileflowAsyncAnalyticsTiming = 'request' | 'resolution';
|
|
32
|
+
type TileflowMarkerController<TMap, TDefinition, TMarker> = {
|
|
33
|
+
clear: () => void;
|
|
34
|
+
dispose: () => void;
|
|
35
|
+
replace: (map: TMap, definitions: readonly TDefinition[]) => void;
|
|
36
|
+
};
|
|
37
|
+
declare function createTileflowSessionStarter(options: {
|
|
38
|
+
sessionId: string;
|
|
39
|
+
source: string;
|
|
40
|
+
startSession?: TileflowSessionSender;
|
|
41
|
+
}): TileflowSessionStarter;
|
|
42
|
+
declare function attachTileflowMapLifecycle<TMap, TFrame>(options: {
|
|
43
|
+
getSession?: () => {
|
|
44
|
+
analytics: TileflowAnalytics | undefined;
|
|
45
|
+
styleId?: string;
|
|
46
|
+
};
|
|
47
|
+
map: TMap;
|
|
48
|
+
onLoad?: (map: TMap) => void;
|
|
49
|
+
scheduler: TileflowFrameScheduler<TFrame>;
|
|
50
|
+
sessionStarter?: TileflowSessionStarter;
|
|
51
|
+
setState: (state: TileflowMapReadinessState) => void;
|
|
52
|
+
subscribe: TileflowMapLifecycleSubscriber<TMap>;
|
|
53
|
+
}): TileflowMapLifecycleAttachment;
|
|
54
|
+
declare function createTileflowTransformRequest<TRequest extends TileflowTransformRequestParameters = TileflowTransformRequestParameters, TResourceType = unknown>(options: {
|
|
55
|
+
always: true;
|
|
56
|
+
asyncAnalyticsTiming?: TileflowAsyncAnalyticsTiming;
|
|
57
|
+
getAnalytics: () => TileflowAnalytics | undefined;
|
|
58
|
+
sessionId: string;
|
|
59
|
+
transformRequest?: TileflowUserTransformRequest<TRequest, TResourceType>;
|
|
60
|
+
}): TileflowTransformRequest<TRequest, TResourceType>;
|
|
61
|
+
declare function createTileflowTransformRequest<TRequest extends TileflowTransformRequestParameters = TileflowTransformRequestParameters, TResourceType = unknown>(options: {
|
|
62
|
+
always?: boolean;
|
|
63
|
+
asyncAnalyticsTiming?: TileflowAsyncAnalyticsTiming;
|
|
64
|
+
getAnalytics: () => TileflowAnalytics | undefined;
|
|
65
|
+
sessionId: string;
|
|
66
|
+
transformRequest?: TileflowUserTransformRequest<TRequest, TResourceType>;
|
|
67
|
+
}): TileflowTransformRequest<TRequest, TResourceType> | undefined;
|
|
68
|
+
declare function createTileflowMarkerController<TMap, TDefinition, TMarker>(options: {
|
|
69
|
+
attach: (marker: TMarker, map: TMap, definition: TDefinition) => void;
|
|
70
|
+
create: (definition: TDefinition) => TMarker;
|
|
71
|
+
remove: (marker: TMarker) => void;
|
|
72
|
+
}): TileflowMarkerController<TMap, TDefinition, TMarker>;
|
|
73
|
+
|
|
74
|
+
export { type TileflowAsyncAnalyticsTiming, type TileflowComposedRequest, type TileflowFrameScheduler, type TileflowMapLifecycleAttachment, type TileflowMapLifecycleEvent, type TileflowMapLifecycleSubscriber, type TileflowMapReadinessState, type TileflowMarkerController, type TileflowSessionSender, type TileflowSessionStarter, type TileflowTransformRequest, type TileflowTransformRequestParameters, type TileflowUserTransformRequest, attachTileflowMapLifecycle, createTileflowMarkerController, createTileflowSessionStarter, createTileflowTransformRequest };
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// src/runtime.ts
|
|
2
|
+
var maxStaticImagePixels = 1280 * 1280;
|
|
3
|
+
var manifestCache = new globalThis.Map();
|
|
4
|
+
function normalizeTileflowUrl(value) {
|
|
5
|
+
return value.replace(/\/+$/, "");
|
|
6
|
+
}
|
|
7
|
+
function resolveTileflowAnalyticsRequestUrl(url, analytics, sessionId) {
|
|
8
|
+
if (!analytics || analytics.enabled === false) {
|
|
9
|
+
return void 0;
|
|
10
|
+
}
|
|
11
|
+
const apiUrl = getTileflowAnalyticsApiUrl(analytics);
|
|
12
|
+
if (!apiUrl || !isTileflowTileUrl(url, apiUrl)) {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
const nextUrl = new URL(url);
|
|
16
|
+
nextUrl.searchParams.set("session", sessionId);
|
|
17
|
+
if (analytics.mapId) {
|
|
18
|
+
nextUrl.searchParams.set("map", analytics.mapId);
|
|
19
|
+
}
|
|
20
|
+
if (analytics.styleId) {
|
|
21
|
+
nextUrl.searchParams.set("styleId", analytics.styleId);
|
|
22
|
+
}
|
|
23
|
+
return nextUrl.toString();
|
|
24
|
+
}
|
|
25
|
+
function startTileflowSession(analytics, input) {
|
|
26
|
+
if (!analytics || analytics.enabled === false || !input.mapId || typeof navigator === "undefined") {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const endpoint = analytics.endpoint ?? `${getTileflowAnalyticsApiUrl(analytics)}/v1/sessions/start`;
|
|
30
|
+
const payload = JSON.stringify({
|
|
31
|
+
mapId: input.mapId,
|
|
32
|
+
metadata: analytics.metadata,
|
|
33
|
+
referrer: typeof document === "undefined" ? void 0 : document.referrer || "",
|
|
34
|
+
sdkVersion: analytics.sdkVersion,
|
|
35
|
+
sessionId: input.sessionId,
|
|
36
|
+
source: analytics.source ?? input.source,
|
|
37
|
+
styleId: input.styleId,
|
|
38
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
39
|
+
});
|
|
40
|
+
const blob = new Blob([payload], { type: "application/json" });
|
|
41
|
+
if (navigator.sendBeacon?.(endpoint, blob)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
void fetch(endpoint, {
|
|
45
|
+
body: payload,
|
|
46
|
+
credentials: "omit",
|
|
47
|
+
headers: {
|
|
48
|
+
"Content-Type": "application/json"
|
|
49
|
+
},
|
|
50
|
+
keepalive: true,
|
|
51
|
+
method: "POST"
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function getTileflowAnalyticsApiUrl(analytics) {
|
|
55
|
+
if (analytics.apiUrl) {
|
|
56
|
+
return normalizeTileflowUrl(analytics.apiUrl);
|
|
57
|
+
}
|
|
58
|
+
if (analytics.endpoint) {
|
|
59
|
+
try {
|
|
60
|
+
const url = new URL(analytics.endpoint, getTileflowLocationOrigin());
|
|
61
|
+
return url.origin;
|
|
62
|
+
} catch {
|
|
63
|
+
return "https://api.tileflow.dev";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return "https://api.tileflow.dev";
|
|
67
|
+
}
|
|
68
|
+
function isTileflowTileUrl(url, apiUrl) {
|
|
69
|
+
try {
|
|
70
|
+
const parsedUrl = new URL(url);
|
|
71
|
+
const parsedApiUrl = new URL(apiUrl);
|
|
72
|
+
return parsedUrl.origin === parsedApiUrl.origin && isTilePath(parsedUrl);
|
|
73
|
+
} catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function isTilePath(url) {
|
|
78
|
+
return url.pathname.startsWith("/tiles/") || url.pathname.startsWith("/v1/tiles/");
|
|
79
|
+
}
|
|
80
|
+
function getTileflowLocationOrigin() {
|
|
81
|
+
if (typeof window === "undefined") {
|
|
82
|
+
return "http://localhost";
|
|
83
|
+
}
|
|
84
|
+
return window.location.origin;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/browser.ts
|
|
88
|
+
function createTileflowSessionStarter(options) {
|
|
89
|
+
const starts = /* @__PURE__ */ new Set();
|
|
90
|
+
const send = options.startSession ?? startTileflowSession;
|
|
91
|
+
return {
|
|
92
|
+
clear() {
|
|
93
|
+
starts.clear();
|
|
94
|
+
},
|
|
95
|
+
start(analytics, styleId) {
|
|
96
|
+
const mapId = analytics?.mapId;
|
|
97
|
+
if (!mapId) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const key = `${options.sessionId}:${mapId}:${styleId ?? ""}`;
|
|
101
|
+
if (starts.has(key)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
starts.add(key);
|
|
105
|
+
send(analytics, {
|
|
106
|
+
mapId,
|
|
107
|
+
sessionId: options.sessionId,
|
|
108
|
+
source: options.source,
|
|
109
|
+
styleId
|
|
110
|
+
});
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function attachTileflowMapLifecycle(options) {
|
|
116
|
+
let disposed = false;
|
|
117
|
+
let generation = 0;
|
|
118
|
+
const frames = /* @__PURE__ */ new Set();
|
|
119
|
+
const unsubscribers = [];
|
|
120
|
+
const cancelFrames = () => {
|
|
121
|
+
for (const frame of frames) {
|
|
122
|
+
options.scheduler.cancelFrame(frame);
|
|
123
|
+
}
|
|
124
|
+
frames.clear();
|
|
125
|
+
};
|
|
126
|
+
const invalidate = (state) => {
|
|
127
|
+
generation += 1;
|
|
128
|
+
cancelFrames();
|
|
129
|
+
if (state) {
|
|
130
|
+
options.setState(state);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const scheduleFrame = (callback) => {
|
|
134
|
+
const frame = options.scheduler.requestFrame(() => {
|
|
135
|
+
frames.delete(frame);
|
|
136
|
+
callback();
|
|
137
|
+
});
|
|
138
|
+
frames.add(frame);
|
|
139
|
+
};
|
|
140
|
+
const handleLoad = () => {
|
|
141
|
+
if (disposed) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
options.onLoad?.(options.map);
|
|
145
|
+
const session = options.getSession?.();
|
|
146
|
+
if (session) {
|
|
147
|
+
options.sessionStarter?.start(session.analytics, session.styleId);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const handleLoading = () => {
|
|
151
|
+
if (!disposed) {
|
|
152
|
+
invalidate("loading");
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const handleIdle = () => {
|
|
156
|
+
if (disposed) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
generation += 1;
|
|
160
|
+
cancelFrames();
|
|
161
|
+
const run = generation;
|
|
162
|
+
scheduleFrame(() => {
|
|
163
|
+
if (disposed || generation !== run) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
scheduleFrame(() => {
|
|
167
|
+
if (!disposed && generation === run) {
|
|
168
|
+
options.setState("idle");
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
const handleError = () => {
|
|
174
|
+
if (!disposed) {
|
|
175
|
+
invalidate("error");
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
invalidate("loading");
|
|
179
|
+
try {
|
|
180
|
+
for (const [event, listener] of [
|
|
181
|
+
["load", handleLoad],
|
|
182
|
+
["dataloading", handleLoading],
|
|
183
|
+
["styledataloading", handleLoading],
|
|
184
|
+
["idle", handleIdle],
|
|
185
|
+
["error", handleError]
|
|
186
|
+
]) {
|
|
187
|
+
unsubscribers.push(options.subscribe(options.map, event, listener));
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
disposed = true;
|
|
191
|
+
generation += 1;
|
|
192
|
+
cancelFrames();
|
|
193
|
+
try {
|
|
194
|
+
disposeFunctions(unsubscribers);
|
|
195
|
+
} catch {
|
|
196
|
+
}
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
dispose() {
|
|
201
|
+
if (disposed) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
disposed = true;
|
|
205
|
+
generation += 1;
|
|
206
|
+
cancelFrames();
|
|
207
|
+
disposeFunctions(unsubscribers);
|
|
208
|
+
},
|
|
209
|
+
invalidate(state) {
|
|
210
|
+
if (!disposed) {
|
|
211
|
+
invalidate(state);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function createTileflowTransformRequest(options) {
|
|
217
|
+
if (!options.always && !options.transformRequest) {
|
|
218
|
+
const analytics = options.getAnalytics();
|
|
219
|
+
if (!analytics || analytics.enabled === false) {
|
|
220
|
+
return void 0;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const asyncAnalyticsTiming = options.asyncAnalyticsTiming ?? "request";
|
|
224
|
+
return (url, resourceType) => {
|
|
225
|
+
const request = options.transformRequest?.(url, resourceType);
|
|
226
|
+
const analyticsAtRequest = asyncAnalyticsTiming === "request" ? options.getAnalytics() : void 0;
|
|
227
|
+
if (isPromiseLike(request)) {
|
|
228
|
+
return request.then(
|
|
229
|
+
(resolvedRequest) => applyTileflowAnalyticsRequest(
|
|
230
|
+
url,
|
|
231
|
+
resolvedRequest,
|
|
232
|
+
asyncAnalyticsTiming === "resolution" ? options.getAnalytics() : analyticsAtRequest,
|
|
233
|
+
options.sessionId
|
|
234
|
+
) ?? resolvedRequest
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
return applyTileflowAnalyticsRequest(
|
|
238
|
+
url,
|
|
239
|
+
request,
|
|
240
|
+
asyncAnalyticsTiming === "resolution" ? options.getAnalytics() : analyticsAtRequest,
|
|
241
|
+
options.sessionId
|
|
242
|
+
);
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function createTileflowMarkerController(options) {
|
|
246
|
+
let markers = [];
|
|
247
|
+
const clear = () => {
|
|
248
|
+
const previousMarkers = markers;
|
|
249
|
+
markers = [];
|
|
250
|
+
removeMarkers(previousMarkers, options.remove);
|
|
251
|
+
};
|
|
252
|
+
return {
|
|
253
|
+
clear,
|
|
254
|
+
dispose: clear,
|
|
255
|
+
replace(map, definitions) {
|
|
256
|
+
clear();
|
|
257
|
+
const nextMarkers = [];
|
|
258
|
+
try {
|
|
259
|
+
for (const definition of definitions) {
|
|
260
|
+
const marker = options.create(definition);
|
|
261
|
+
nextMarkers.push(marker);
|
|
262
|
+
options.attach(marker, map, definition);
|
|
263
|
+
}
|
|
264
|
+
} catch (error) {
|
|
265
|
+
try {
|
|
266
|
+
removeMarkers(nextMarkers, options.remove);
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
markers = nextMarkers;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function applyTileflowAnalyticsRequest(url, request, analytics, sessionId) {
|
|
276
|
+
const nextUrl = resolveTileflowAnalyticsRequestUrl(request?.url ?? url, analytics, sessionId);
|
|
277
|
+
if (!nextUrl) {
|
|
278
|
+
return request;
|
|
279
|
+
}
|
|
280
|
+
return request ? { ...request, url: nextUrl } : { url: nextUrl };
|
|
281
|
+
}
|
|
282
|
+
function disposeFunctions(disposers) {
|
|
283
|
+
let firstError;
|
|
284
|
+
let failed = false;
|
|
285
|
+
for (const dispose of disposers.splice(0).reverse()) {
|
|
286
|
+
try {
|
|
287
|
+
dispose();
|
|
288
|
+
} catch (error) {
|
|
289
|
+
if (!failed) firstError = error;
|
|
290
|
+
failed = true;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (failed) {
|
|
294
|
+
throw firstError;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function isPromiseLike(value) {
|
|
298
|
+
return Boolean(value && typeof value.then === "function");
|
|
299
|
+
}
|
|
300
|
+
function removeMarkers(markers, remove) {
|
|
301
|
+
let firstError;
|
|
302
|
+
let failed = false;
|
|
303
|
+
for (const marker of markers) {
|
|
304
|
+
try {
|
|
305
|
+
remove(marker);
|
|
306
|
+
} catch (error) {
|
|
307
|
+
if (!failed) firstError = error;
|
|
308
|
+
failed = true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (failed) {
|
|
312
|
+
throw firstError;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
export {
|
|
316
|
+
attachTileflowMapLifecycle,
|
|
317
|
+
createTileflowMarkerController,
|
|
318
|
+
createTileflowSessionStarter,
|
|
319
|
+
createTileflowTransformRequest
|
|
320
|
+
};
|