@tileflow/core 0.1.0-alpha.2 → 0.1.0-alpha.20

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
@@ -1,34 +1,326 @@
1
1
  # @tileflow/core
2
2
 
3
- Typed Tileflow config helpers and the MapLibre style compiler.
3
+ Typed Tileflow authoring primitives and the deterministic Tileflow Streets compiler.
4
4
 
5
5
  ```ts
6
- import {defineTileflow, osm, roads} from '@tileflow/core';
6
+ import {defineTileflow, labels, poi, roads, streets, water, zoom} from '@tileflow/core';
7
7
 
8
8
  export default defineTileflow({
9
9
  themes: {
10
- light: {
11
- mode: 'light',
10
+ editorial: {
11
+ extends: 'light',
12
12
  colors: {
13
- water: '#8ED6E8',
14
- park: '#C3F1D5',
15
- road: '#D8E0E7',
16
- text: '#566371',
13
+ background: '#E4DFD4',
14
+ land: '#F5F2EB',
15
+ water: '#A7CED7',
16
+ park: '#C6D8B7',
17
+ building: '#D6CFC3',
18
+ road: '#FCFBF8',
19
+ roadMajor: '#EBCB8F',
20
+ roadCasing: '#BBB1A1',
21
+ boundary: '#999184',
22
+ text: '#252B2D',
23
+ textMuted: '#626866',
24
+ textHalo: '#FBF9F4',
17
25
  },
26
+ typography: {font: 'Noto Sans', places: {weight: 'bold'}},
18
27
  },
19
28
  },
20
29
  maps: {
21
30
  madrid: {
22
- basemap: osm(),
23
- theme: 'light',
24
- modules: [roads({preset: 'modern', density: 'balanced'})],
25
- view: {
26
- center: [-3.7038, 40.4168],
27
- zoom: 12,
31
+ basemap: streets(),
32
+ theme: 'editorial',
33
+ modules: {
34
+ water: water({bodies: {fill: {opacity: 0.95}}}),
35
+ roads: roads({
36
+ detail: 'streets',
37
+ hierarchy: 'strong',
38
+ classes: {
39
+ primary: {
40
+ surface: {
41
+ fill: {
42
+ color: '#E4A85B',
43
+ width: zoom.linear([
44
+ [7, 0.6],
45
+ [16, 8],
46
+ ]),
47
+ },
48
+ },
49
+ },
50
+ },
51
+ }),
52
+ labels: labels({
53
+ language: 'local',
54
+ places: 'all',
55
+ roads: 'streets',
56
+ styles: {
57
+ places: {city: {text: {size: 18, haloWidth: 1.5}}},
58
+ },
59
+ }),
60
+ poi: poi({categories: ['food', 'culture', 'transit'], color: 'category'}),
28
61
  },
62
+ view: {center: [-3.7038, 40.4168], pitch: 35, zoom: 12},
29
63
  },
30
64
  },
31
65
  });
32
66
  ```
33
67
 
34
- Docs: https://tileflow.dev/docs
68
+ ## Authoring model
69
+
70
+ `streets()` is the only built-in basemap. It identifies a versioned cartographic recipe and does
71
+ not inherit or patch another style. The compiler resolves the complete Streets design and asks
72
+ each domain module to create its own MapLibre layers. A shared graph then determines layer order.
73
+
74
+ ```mermaid
75
+ flowchart LR
76
+ A["Human or agent edits config"] --> B["Streets defaults + theme + keyed modules"]
77
+ B --> C["Domain compilers"]
78
+ C --> D["Ordered Streets layers"]
79
+ D --> E["Validated MapLibre Style JSON"]
80
+ ```
81
+
82
+ Modules are keyed by domain, so object order never controls rendering and a domain can appear only
83
+ once. Omit a module to keep the complete Streets default. Use `enabled: false` to remove a domain
84
+ deliberately. The public domains are `land`, `water`, `roads`, `buildings`, `boundaries`, `labels`,
85
+ `poi`, `aeroways`, and `transit`.
86
+
87
+ Every styling module supports semantic shortcuts and exact semantic targets. Style values accept
88
+ constants, raw MapLibre expressions through `expression(...)`, and zoom functions through
89
+ `zoom.step(...)`, `zoom.linear(...)`, or `zoom.exponential(...)`. Exact controls address stable
90
+ concepts such as `roads.classes.primary.surface.fill`, not renderer layer IDs.
91
+
92
+ ## Shared visual primitives
93
+
94
+ The same small visual language is reused across every geographic domain. This keeps an agent from
95
+ having to learn different spellings for the same MapLibre behavior:
96
+
97
+ - `BackgroundStyle`: color, opacity, pattern, visibility, and zoom range.
98
+ - `FillStyle`: color, opacity, pattern, antialiasing, visibility, and zoom range.
99
+ - `LineStyle`: color, width, opacity, dash, blur, gap, offset, pattern, caps, joins, and zoom range.
100
+ - `LineHatchStyle`: repeated diagonal detail with color, opacity, spacing, size, angle, and zoom
101
+ range for a road structure.
102
+ - `TextStyle`: field, font, weight, fallbacks, size, color, halo, spacing metrics, transform,
103
+ collision policy, rotation, fixed/variable anchoring, line constraints, and zoom range.
104
+ - `IconStyle`: image, size, color, halo, rotation/alignment, collision policy, and zoom range.
105
+ - `CircleStyle`: radius, fill/stroke appearance, blur, opacity, pitch behavior, and zoom range.
106
+ - `ExtrusionStyle`: color, height, base, opacity, pattern, vertical gradient, and zoom range.
107
+
108
+ Compounds express common cartographic structures: `AreaStyle` has `fill` and `outline`,
109
+ `LineStackStyle` has `shadow`, `casing`, and `fill`, and `SymbolStyle` combines placement with
110
+ optional `text`, `icon`, and `marker`. Geographic selection remains owned by semantic modules;
111
+ ordinary visual styles intentionally do not accept raw source filters.
112
+
113
+ A `SymbolStyle` root zoom range governs its text/icon layer and is inherited by its marker layer.
114
+ Because a marker is materialized as a separate circle layer, an explicit marker range may refine
115
+ that inherited default. Text and icon ranges must agree because MapLibre renders them together.
116
+
117
+ The road module distinguishes the path family semantically:
118
+
119
+ ```ts
120
+ roads({
121
+ extras: {paths: true},
122
+ areas: {
123
+ pedestrian: {
124
+ fill: {color: '#F1F3F5'},
125
+ outline: {color: '#D5DCE3', width: 1},
126
+ },
127
+ },
128
+ classes: {
129
+ primary: {
130
+ tunnel: {
131
+ casing: {color: '#8EA3B8', width: 10},
132
+ fill: {color: '#F5F8FA', width: 8},
133
+ hatch: {color: '#8EA3B8', opacity: 0.25, spacing: 10},
134
+ },
135
+ },
136
+ pedestrian: {surface: {fill: {color: '#F5F6F7', width: 6}}},
137
+ footway: {surface: {fill: {color: '#D9DEE3', width: 1.2}}},
138
+ cycleway: {surface: {fill: {color: '#9ED8C5', width: 1.5}}},
139
+ steps: {surface: {fill: {color: '#C7CED6', dash: [1, 1], width: 1.4}}},
140
+ pathway: {surface: {fill: {color: '#E8ECEF', width: 1.2}}},
141
+ },
142
+ modifiers: {
143
+ expressway: {widthScale: 1.06},
144
+ ramp: {widthScale: 0.7},
145
+ unpaved: {surface: {fill: {color: '#E6DFD3', dash: [2, 1]}}},
146
+ construction: {surface: {fill: {dash: [2, 1], opacity: 0.7}}},
147
+ indoor: {surface: {fill: {dash: [1, 1], opacity: 0.4}}},
148
+ },
149
+ restrictions: {
150
+ access: {surface: {fill: {dash: [1.5, 1], opacity: 0.55}}},
151
+ toll: {surface: {casing: {color: '#C5B7D8'}}},
152
+ },
153
+ serviceTypes: {
154
+ driveway: {widthScale: 0.75},
155
+ parkingAisle: {widthScale: 0.6},
156
+ },
157
+ });
158
+ ```
159
+
160
+ These targets are non-overlapping translations of the OpenMapTiles road class and subclass. The
161
+ same names are available under `labels().roadClasses` and `labels().styles.roads`. Surface, tunnel,
162
+ and bridge phases can be controlled independently for each target; each structure has
163
+ `shadow`/`casing`/`fill` and an optional `hatch`. Hatch marks inherit the resolved fill width when
164
+ `size` is omitted and never participate in label collision. No raw source filter or generated layer
165
+ ID is needed.
166
+
167
+ `classes.pedestrian` styles line-like pedestrian ways. Polygon pedestrian plazas are a distinct
168
+ geometry and use `areas.pedestrian`, including optional fill opacity, outline color, or sprite
169
+ pattern. This prevents plazas from degrading into a thin polygon outline.
170
+
171
+ Road treatments refine a class without creating another class or exposing source fields.
172
+ `construction`, `expressway`, `indoor`, `official`, `ramp`, and `unpaved` live under `modifiers`;
173
+ general, bicycle, foot, horse, and toll restrictions live under `restrictions`; `alley`,
174
+ `crossover`, `driveway`, `parkingAisle`, and `yard` live under `serviceTypes`; and exact
175
+ mountain-bike difficulty values live under `mountainBike`. Treatments reuse
176
+ surface/tunnel/bridge and casing/fill/shadow paint controls, plus a relative `widthScale`. The
177
+ compiler emits feature-driven paint expressions inside the existing stable class layers rather
178
+ than multiplying every possible combination.
179
+
180
+ Road names, shields, and motorway junctions remain label concerns:
181
+
182
+ ```ts
183
+ labels({
184
+ roads: 'all',
185
+ shields: 'all',
186
+ junctions: true,
187
+ styles: {
188
+ shields: {
189
+ default: {text: {color: '#405264', haloColor: '#fff', haloWidth: 2, weight: 'bold'}},
190
+ networks: {
191
+ 'network-value': {text: {color: '#B43A35'}},
192
+ },
193
+ },
194
+ junctions: {text: {color: '#405264', haloColor: '#fff', haloWidth: 2}},
195
+ },
196
+ });
197
+ ```
198
+
199
+ POI `density`, `labels`, and `icons` are independent policies. Density bounds eligible feature
200
+ ranks, label detail controls text ranks, icon detail controls icon ranks, and
201
+ `placement.coupleIconAndLabel` deliberately keeps only features eligible for both. Without
202
+ coupling, icon and label layers collide normally but can be styled and zoomed independently.
203
+ The `balanced` policy keeps a practical cross-category candidate set from an overscaled
204
+ OpenMapTiles source tile; MapLibre collision placement still decides which candidates fit the
205
+ current viewport. Use a category style's inclusive `maxRank` when different feature families need
206
+ different candidate ceilings; the explicit category value replaces the density/label/icon preset
207
+ ceiling for that category without exposing a raw layer ID or filter.
208
+
209
+ Theme tokens provide shared color and typography defaults. A module-level exact style wins over a
210
+ theme token for that target. Precedence is deterministic:
211
+
212
+ 1. Streets recipe defaults.
213
+ 2. Selected theme and inherited named theme.
214
+ 3. Explicit keyed module fields.
215
+ 4. Ordered raw overrides.
216
+
217
+ Raw `addLayer`, `patchLayer`, `moveLayer`, and `removeLayer` operations are the final MapLibre escape
218
+ hatch. They are ordered, target generated `streets-*` IDs explicitly, and fail when a target or
219
+ anchor does not exist. They should be used for one-off MapLibre behavior, not ordinary design.
220
+
221
+ ## Data is separate from design
222
+
223
+ Omitting `data` selects Tileflow World at the revision pinned by this SDK version:
224
+
225
+ ```ts
226
+ {
227
+ basemap: streets();
228
+ }
229
+ ```
230
+
231
+ Pin the official data deliberately, or use another OpenMapTiles-compatible vector source:
232
+
233
+ ```ts
234
+ import {openMapTiles, tileflowWorld, vectorTiles} from '@tileflow/core';
235
+
236
+ const official = tileflowWorld({revision: '2026-06-07'});
237
+ const external = vectorTiles({
238
+ url: 'https://tiles.example.com/tiles.json',
239
+ attribution: '© Example © OpenStreetMap contributors',
240
+ schema: openMapTiles(),
241
+ });
242
+ ```
243
+
244
+ The basemap controls how data is drawn; `data` controls where compatible features come from. The
245
+ compiler performs no network request. `openMapTiles({layers, fields})` can bind renamed
246
+ source-layers or properties while preserving the versioned semantic contract. External browser
247
+ credentials do not belong in public Style JSON; supply them through the framework's
248
+ `transformRequest` integration.
249
+
250
+ Tileflow's OpenMapTiles contract also recognizes the optional `globallandcover` extension. The
251
+ default Streets style maps its `barren`, `crop`, `grass`, `shrub`, `snow`, `trees`, and `urban`
252
+ classes beneath the OSM land layers at zooms 0–7, fading to transparent at zoom 8. Sources with a
253
+ different layer name can use `openMapTiles({layers: {globalLandcover: 'my_landcover'}})`; archives
254
+ without the extension remain compatible and simply render no features for that style layer.
255
+
256
+ The pinned legacy Tileflow World revision keeps its historical attribution inline in the generated
257
+ style. Every other explicitly pinned official revision delegates attribution to its versioned
258
+ TileJSON, so MapLibre displays the exact text bound to that archive's release receipt. The SDK does
259
+ not guess a legal string or fall back to the legacy attribution for a new product; publishing must
260
+ fail before selection when that immutable TileJSON has no receipt-backed attribution.
261
+
262
+ ## Capture scenes
263
+
264
+ Commit named scenes beside maps. Scenes affect visual evidence, not style or manifest identity.
265
+
266
+ ```ts
267
+ export default defineTileflow({
268
+ maps: {madrid: {basemap: streets()}},
269
+ scenes: {
270
+ 'madrid-desktop': {
271
+ map: 'madrid',
272
+ camera: {type: 'center', center: [-3.7038, 40.4168], zoom: 12},
273
+ viewport: {width: 1280, height: 800, dpr: 1},
274
+ },
275
+ },
276
+ });
277
+ ```
278
+
279
+ Use `tileflow dev --scene madrid-desktop` for live review and `tileflow capture madrid-desktop`
280
+ for exact pixels and a schema-version-2 receipt containing the Streets, data, style, renderer, and
281
+ image identities.
282
+
283
+ ## Public API and browser subpath
284
+
285
+ Config, data, modules, compilation, validation, runtime resolution, and capture-scene APIs come
286
+ from the package root. Legacy basemap factories, `renderer`, top-level `tiles`/`tileset`, module
287
+ arrays, and raw `layers` are not part of this API.
288
+
289
+ Framework adapters import the browser-only lifecycle kernel explicitly:
290
+
291
+ ```ts
292
+ import {
293
+ attachTileflowMapLifecycle,
294
+ createTileflowMarkerController,
295
+ createTileflowSessionStarter,
296
+ createTileflowTransformRequest,
297
+ } from '@tileflow/core/browser';
298
+ ```
299
+
300
+ The browser entry is not re-exported from the root. It is SSR-safe to import, has no MapLibre
301
+ runtime dependency, and reads no browser global during module evaluation. See the
302
+ [framework browser runtime contract](../../docs/contracts/framework-browser-runtime.md) and the
303
+ [cartographic authoring contract](../../docs/contracts/cartographic-authoring.md).
304
+
305
+ ## Hosted session authorization
306
+
307
+ The framework adapters use `createTileflowSessionController` to obtain one short-lived session
308
+ grant before eligible hosted style, tile, font, or sprite requests. Concurrent resources share the
309
+ same preflight, grants are attached only to reviewed HTTP(S) origins, and an expired unused session
310
+ rotates once when the server requests it. A controller also rotates after six hours or 10,000
311
+ eligible requests. The preflight has a 10-second client timeout by default; direct controller users
312
+ can set `grantTimeoutMs` from 1 to 120,000 milliseconds.
313
+
314
+ `analytics.enabled: false` disables only the optional analytics beacon. It does not disable this
315
+ server-owned commercial authorization. User `transformRequest` callbacks still run first; Tileflow
316
+ then decorates only the resulting eligible URL and preserves the other request options.
317
+
318
+ Other subpaths under `src/`, `themes/`, `templates/`, and `modules/` are internal implementation
319
+ details and can change during alpha releases.
320
+
321
+ Streets build, preview, capture, and deploy flows supply a built-in POI sprite with Google Places
322
+ glyphs inside Tileflow circular markers. The markers use Google's category colors, a white rim, and
323
+ a compact shadow. A project-local icon source or explicit hosted sprite replaces that catalog;
324
+ build and deploy replace local sources with generated sprite URLs without exposing source paths.
325
+ Calling the pure core style compiler without asset preparation remains text-only unless the map
326
+ provides `icons` or `sprite` explicitly.
@@ -0,0 +1,77 @@
1
+ import { T as TileflowAnalytics, a as TileflowSessionController } from './runtime-DroKDIuB.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
+ getSessionId?: () => string;
39
+ sessionId: string;
40
+ source: string;
41
+ startSession?: TileflowSessionSender;
42
+ }): TileflowSessionStarter;
43
+ declare function attachTileflowMapLifecycle<TMap, TFrame>(options: {
44
+ getSession?: () => {
45
+ analytics: TileflowAnalytics | undefined;
46
+ styleId?: string;
47
+ };
48
+ map: TMap;
49
+ onLoad?: (map: TMap) => void;
50
+ scheduler: TileflowFrameScheduler<TFrame>;
51
+ sessionStarter?: TileflowSessionStarter;
52
+ setState: (state: TileflowMapReadinessState) => void;
53
+ subscribe: TileflowMapLifecycleSubscriber<TMap>;
54
+ }): TileflowMapLifecycleAttachment;
55
+ declare function createTileflowTransformRequest<TRequest extends TileflowTransformRequestParameters = TileflowTransformRequestParameters, TResourceType = unknown>(options: {
56
+ always: true;
57
+ asyncAnalyticsTiming?: TileflowAsyncAnalyticsTiming;
58
+ getAnalytics: () => TileflowAnalytics | undefined;
59
+ sessionController?: TileflowSessionController;
60
+ sessionId: string;
61
+ transformRequest?: TileflowUserTransformRequest<TRequest, TResourceType>;
62
+ }): TileflowTransformRequest<TRequest, TResourceType>;
63
+ declare function createTileflowTransformRequest<TRequest extends TileflowTransformRequestParameters = TileflowTransformRequestParameters, TResourceType = unknown>(options: {
64
+ always?: boolean;
65
+ asyncAnalyticsTiming?: TileflowAsyncAnalyticsTiming;
66
+ getAnalytics: () => TileflowAnalytics | undefined;
67
+ sessionController?: TileflowSessionController;
68
+ sessionId: string;
69
+ transformRequest?: TileflowUserTransformRequest<TRequest, TResourceType>;
70
+ }): TileflowTransformRequest<TRequest, TResourceType> | undefined;
71
+ declare function createTileflowMarkerController<TMap, TDefinition, TMarker>(options: {
72
+ attach: (marker: TMarker, map: TMap, definition: TDefinition) => void;
73
+ create: (definition: TDefinition) => TMarker;
74
+ remove: (marker: TMarker) => void;
75
+ }): TileflowMarkerController<TMap, TDefinition, TMarker>;
76
+
77
+ 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 };