@tileflow/dev 0.1.0-alpha.18 → 0.1.0-alpha.19

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.
Files changed (46) hide show
  1. package/GENERATED_OUTPUT_LICENSE.md +21 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +9 -0
  4. package/README.md +397 -41
  5. package/THIRD_PARTY_NOTICES.md +65 -7
  6. package/TRADEMARKS.md +10 -0
  7. package/dist/artifacts.d.ts +152 -0
  8. package/dist/artifacts.js +62 -0
  9. package/dist/chunk-BALG4DSG.js +844 -0
  10. package/dist/chunk-BKHIZ7H2.js +287 -0
  11. package/dist/chunk-BL3FK4GO.js +170 -0
  12. package/dist/chunk-D4OE4ITL.js +13 -0
  13. package/dist/chunk-GWUMIHL6.js +4208 -0
  14. package/dist/chunk-KDFTE4Q5.js +156 -0
  15. package/dist/chunk-O7PE6FB2.js +1210 -0
  16. package/dist/chunk-OEABXOJH.js +1991 -0
  17. package/dist/chunk-VEB43KDS.js +670 -0
  18. package/dist/chunk-ZOYFZ3MT.js +801 -0
  19. package/dist/chunk-ZRI6PNFL.js +211 -0
  20. package/dist/config.d.ts +33 -0
  21. package/dist/config.js +27 -0
  22. package/dist/fonts.d.ts +47 -0
  23. package/dist/fonts.js +16 -0
  24. package/dist/icons.d.ts +136 -0
  25. package/dist/icons.js +17 -0
  26. package/dist/index.d.ts +12 -320
  27. package/dist/index.js +128 -2566
  28. package/dist/inspect.d.ts +147 -0
  29. package/dist/inspect.js +16 -0
  30. package/dist/preview-assets-6LYL7GP7.js +145 -0
  31. package/dist/preview.d.ts +69 -0
  32. package/dist/preview.js +18 -0
  33. package/dist/server.d.ts +77 -0
  34. package/dist/server.js +47 -0
  35. package/dist/validation.d.ts +57 -0
  36. package/dist/validation.js +22 -0
  37. package/package.json +61 -7
  38. package/assets/streets-poi/cafe.svg +0 -10
  39. package/assets/streets-poi/hospital.svg +0 -10
  40. package/assets/streets-poi/hotel.svg +0 -10
  41. package/assets/streets-poi/museum.svg +0 -10
  42. package/assets/streets-poi/restaurant.svg +0 -10
  43. package/assets/streets-poi/school.svg +0 -10
  44. package/assets/streets-poi/services.svg +0 -10
  45. package/assets/streets-poi/shopping.svg +0 -10
  46. package/assets/streets-poi/train.svg +0 -10
package/README.md CHANGED
@@ -4,18 +4,69 @@ Node utilities used by the Tileflow CLI and build integrations.
4
4
 
5
5
  Most apps should use `@tileflow/cli`, `@tileflow/vite`, or `@tileflow/next` directly. Use this
6
6
  package when you are building a custom dev server, bundler plugin, or deployment adapter that needs
7
- to load and compile a `tileflow.config.ts`.
7
+ to load and compile the single map exported by `tileflow.config.ts`.
8
8
 
9
9
  ```ts
10
- import {createTileflowBuildArtifacts} from '@tileflow/dev';
10
+ import {createTileflowBuildArtifacts} from '@tileflow/dev/artifacts';
11
11
 
12
12
  const artifacts = await createTileflowBuildArtifacts({
13
13
  config: 'tileflow.config.ts',
14
14
  });
15
15
  ```
16
16
 
17
+ The integration surfaces are deliberately split: use `@tileflow/dev/artifacts` for source
18
+ preparation and production outputs, `@tileflow/dev/server` for HTTP development handling,
19
+ `@tileflow/dev/icons` for icon compilation, `@tileflow/dev/inspect` for safe resolved-config and
20
+ bounded feature inspection, `@tileflow/dev/validation` for structured diagnostics, and
21
+ `@tileflow/dev/preview` only for the local preview contract. The root entry keeps compatibility
22
+ re-exports.
23
+
24
+ Artifact construction is an explicit `source map → resolved map → prepared map → ArtifactPlan`
25
+ pipeline. Map inheritance resolves before its ordered local/package icon and font directories are
26
+ prepared, and before the result reaches the core style compiler. The Node layer may normalize
27
+ the map into an internal build catalog, but that catalog is orchestration state rather than another
28
+ public authoring model. The plan exposes its complete files and observed input graph, so build
29
+ adapters emit and watch the same generation instead of reconstructing either list themselves. Production manifests
30
+ point to content-addressed `generations/<sha256>/...` theme styles and sprites; those immutable files are
31
+ installed before the manifest pointer changes. Stable style paths are
32
+ `styles/<map>/<theme>.json`; every manifest lookup resolves a concrete theme. The disk writer also stages the complete plan,
33
+ records a managed-file inventory, rolls back caught filesystem failures, and removes only stale
34
+ files named by its previous valid inventory. It retains the immediately preceding immutable
35
+ generation during each replacement, so a client that already read the old manifest can finish its
36
+ style and sprite requests; the next replacement retires that older generation. Stable `styles/`
37
+ and `icons/` paths remain available, but generation-consistent consumers
38
+ resolve maps through `manifest.json`.
39
+
40
+ Every plan also emits canonical `build-manifest.json` (schema version 1). For each map it records
41
+ the leaf `mapVersion`, resolved lineage, effective icon/font source identities, Recipe ABI, and
42
+ one entry per concrete theme with `colorScheme`, identity, inferred legacy `dataRequirements`,
43
+ multi-source `sourceRequirements`, and `styleSha256`. The multi-source contract records each
44
+ referenced vector source independently and records raster DEM source ID, encoding, and tile size;
45
+ it therefore distinguishes Bathymetry vector bands from optional relief without inventing vector
46
+ fields. `mapRevisionSha256` identifies the effective cartographic definition after `extends`;
47
+ `assetSetSha256` for that map's generated runtime resources. Data requirements are derived from the
48
+ final Style layers and fields rather than copied from a manually maintained allowlist, so disabled
49
+ or overridden modules do not claim data they no longer use. The Recipe ABI remains a separate
50
+ `recipe: {compiler, compilerVersion}` compatibility axis.
51
+
52
+ The map revision is versioned and domain-separated. It contains the resolved cartographic design,
53
+ the compiler family, private effective module contributions, and effective icon/font source
54
+ identities. Map `id`, `name`, editorial `mapVersion`, default `view`, capture `scenes`, delivery
55
+ policy, package SemVer, compiler ABI, local paths, timestamps, generated sprite/font output, and a
56
+ concrete resolution of a floating World selector are deliberately outside it. Changing a shadowed
57
+ ancestor does not change the revision; changing an effective cartographic override or source asset
58
+ does. Lineage and the leaf `mapVersion` remain beside the hash for traceability, Style JSON owns its
59
+ compiled-output identity, and Hosted delivery policy belongs to the deployment fingerprint. The
60
+ top-level `provenance` block records the exact participating Tileflow package versions and the
61
+ nearest package-manager lockfile's format and content hash, without embedding its local path;
62
+ provenance remains outside every map revision. The same portable block is available to Hosted
63
+ adapters through
64
+ `createTileflowBuildProvenance(cwd)`. The dev server exposes the complete document at
65
+ `/build-manifest.json`.
66
+
17
67
  `createTileflowStyle`, `createTileflowStyles`, and artifact construction all enforce the same
18
68
  recursive JSON-value invariant and MapLibre style-spec semantics without fetching remote resources.
69
+ The family API returns `styles[map][theme]`; it never flattens a default theme into a map alias.
19
70
  Invalid output throws `TileflowStyleValidationError` with at most 32 deterministic
20
71
  `map`/`path`/`message` issues; layer indexes are projected to stable layer IDs.
21
72
 
@@ -23,7 +74,7 @@ For a watched, last-known-good integration, use one artifact session rather than
23
74
  request independently:
24
75
 
25
76
  ```ts
26
- import {createTileflowArtifactSession} from '@tileflow/dev';
77
+ import {createTileflowArtifactSession} from '@tileflow/dev/artifacts';
27
78
 
28
79
  const session = await createTileflowArtifactSession({
29
80
  config: 'tileflow.config.ts',
@@ -40,15 +91,17 @@ await session.close();
40
91
  ```
41
92
 
42
93
  The bounded watcher follows the config, transitive local TypeScript/JavaScript/JSON imports, and
43
- effective icon inputs. Generations are monotonic, overlapping refreshes are latest-wins, invalid
94
+ effective local icon/font directory inputs. Generations are monotonic, overlapping refreshes are latest-wins, invalid
44
95
  edits retain the last good snapshot, and caller-supplied output directories can be ignored to avoid
45
- feedback loops. `tileflow dev`, `capture --watch`, and framework adapters use this shared status
46
- vocabulary.
96
+ feedback loops. `tileflow preview` (`tileflow dev` compatibility alias), `capture --watch`, and
97
+ framework adapters use this shared status
98
+ vocabulary. The built-in preview reloads after its live event stream reconnects following a server
99
+ restart, even when the replacement process begins again at generation 1.
47
100
 
48
- Custom preview servers can select the same map or standalone scene semantics as the CLI:
101
+ Custom preview servers use the exported map or one of its standalone scenes:
49
102
 
50
103
  ```ts
51
- import {createTileflowDevRequestHandler} from '@tileflow/dev';
104
+ import {createTileflowDevRequestHandler} from '@tileflow/dev/server';
52
105
 
53
106
  const fetch = createTileflowDevRequestHandler({
54
107
  session,
@@ -56,16 +109,308 @@ const fetch = createTileflowDevRequestHandler({
56
109
  });
57
110
  ```
58
111
 
59
- `map` and `scene` are mutually exclusive. Map preview uses the configured `view`; scene preview
60
- uses its committed camera and CSS viewport. `resolveTileflowPreview()` exposes the validated
61
- selection for custom integrations. Application-target scenes remain the responsibility of the
62
- application's development server. The built-in preview records longitude, latitude, zoom, bearing,
63
- and pitch in the current URL after the camera moves, so browser refreshes and config-triggered
64
- reloads return to the same view. Removing those query parameters restores the configured camera.
112
+ Map preview uses the exported map's `view` and accepts one concrete `theme`; scene preview uses its
113
+ committed concrete theme, camera, and CSS viewport metadata from that same map. A scene does not
114
+ repeat the map ID because its owner is implicit.
115
+ `resolveTileflowPreview()` exposes the validated selection for custom integrations.
116
+ Application-target scenes remain the responsibility of the application's development server. The
117
+ built-in preview records longitude, latitude, zoom, bearing, and pitch in the current URL, so browser
118
+ refreshes and config-triggered reloads return to the same view. Removing those query parameters
119
+ restores the configured camera.
120
+
121
+ ## Local comparison workbench and compiler inspection
122
+
123
+ The CLI exposes the complete authoring surface through `tileflow preview --against-map ...` or
124
+ `--against-config ...`. Custom local servers can compose the same shell around two existing dev
125
+ handlers:
126
+
127
+ ```ts
128
+ import {createTileflowComparisonRequestHandler} from '@tileflow/dev/server';
129
+
130
+ const fetch = createTileflowComparisonRequestHandler({
131
+ left: {
132
+ basePath: '/left',
133
+ handler: leftHandler,
134
+ label: 'candidate / light',
135
+ previewUrl: '/left/',
136
+ sidecarUrl: '/left/__inspection/candidate/light.json',
137
+ },
138
+ right: {
139
+ basePath: '/right',
140
+ handler: rightHandler,
141
+ label: 'reference / light',
142
+ previewUrl: '/right/',
143
+ sidecarUrl: '/right/__inspection/reference/light.json',
144
+ },
145
+ });
146
+ ```
147
+
148
+ Each side handler retains ownership of its styles, fonts, sprites, events, diagnostics, and
149
+ last-known-good artifact generation. The comparison router only owns the root HTML and strict
150
+ dispatch to two disjoint same-origin route prefixes. Its synchronized camera, URL state,
151
+ side-by-side/split/overlay/blink modes, rendered-feature inspector, zoom-curve sampling, sprite
152
+ atlas, and scene/capture-command copying require no hosted service.
153
+
154
+ Set `inspection: true` on `createTileflowBuildArtifacts` or
155
+ `createTileflowArtifactSession` to compute `artifacts.styleInspections[map][theme]`. A dev handler
156
+ serves an available sidecar at `/__inspection/<map>/<theme>.json`; otherwise that route is 404. The
157
+ option is deliberately off by default. Sidecars remain in memory, do not alter Style bytes or
158
+ manifest hashes, and are excluded from `getTileflowArtifactFiles()` and every stable build output.
159
+
160
+ When a resolved map declares `fonts`, preparation reads its ordered directories and uses OpenType
161
+ full names as the canonical IDs referenced by `text-font`. TTF, OTF, and WOFF2 inputs are supported;
162
+ every contributing directory must contain `LICENSE.txt`. Later directories replace earlier faces
163
+ with the same exact ID, case-only collisions fail, and every primary font in the final style must be
164
+ present. `font` is an exact face ID; local `fallbacks` entries are exact face names or explicit CSS
165
+ generic families such as `sans-serif`. Preparation never synthesizes a face name from a family and
166
+ weight. Only selected primary faces and licenses are emitted as
167
+ content-addressed managed artifacts.
168
+
169
+ `fonts` and `glyphs` are mutually exclusive map fields. Omission inherits the parent's current text
170
+ provider; declaring either one replaces an inherited provider of either kind atomically. The font
171
+ pipeline is driven only by directory metadata and final style usage—there are no hard-coded map or
172
+ family names, and a URL provider comes only from the resolved map instead of being invented by the
173
+ pipeline. After inheritance resolves, a map with any text layer must have exactly one provider. A
174
+ `glyphs: {kind: 'url', ...}` provider enumerates the exact comma-joined MapLibre request keys
175
+ produced by the style's `text-font` arrays.
176
+
177
+ The resulting style records strict `tileflow:fontFaces` metadata. Preview, capture, and browser
178
+ framework adapters load those generic definitions before constructing MapLibre. Hosted preparation
179
+ also emits one canonical `tileflow-font-bundle-v1` closure containing only selected faces and their
180
+ license bytes. Its manifest SHA-256 is the sole content identity; Hosted adds an opaque project-owned
181
+ storage ID. Deploy uploads that bundle before the Style and then replaces provisional sources with
182
+ the exact immutable ID URLs confirmed by Hosted. A derived map that declares `glyphs` replaces the
183
+ inherited local provider atomically; the URL-backed map is complete as declared and independent of
184
+ its World selection.
185
+
186
+ That upload and binding contract is implemented, but it is not by itself a production-availability
187
+ promise. The matching Hosted rollout candidate adds DB-backed project ownership, organization quota,
188
+ durable deployment references, grace-based garbage collection, and deletion receipts; availability
189
+ still requires that matching migration, API, and SDK pair to be promoted together.
190
+
191
+ For Streets styles whose vegetation layer declares `tileflow:vegetation-mode = 3d`, the built-in
192
+ preview uses the same portable circle fallback as capture and the published framework adapters by
193
+ default. This makes an ordinary `tileflow preview` session WYSIWYG with production and visual
194
+ baselines. Append `?treeRenderer=simple` or `?treeRenderer=complex` to opt into the experimental,
195
+ preview-only instanced renderer described below; that explicit mode is not capture evidence.
196
+
197
+ The experimental renderer uses source height and crown diameter when present, distinguishes palms,
198
+ columnar cypresses, conifers, and three broadleaf silhouettes from the bound botanical fields, and
199
+ falls back to deterministic broadleaf variety and dimensions when tags are missing. Bark color,
200
+ crown palettes, height scale, and crown scale come from `vegetation.threeDimensional`; the preview
201
+ does not maintain a second hard-coded appearance contract. During camera
202
+ gestures the existing 3D batch remains visible while native circles cover newly arriving tiles; the
203
+ batch refreshes when movement ends without waiting for the whole map to become idle. Source-tile
204
+ events are rate-limited and unchanged instance selections do not upload new GPU buffers. The
205
+ preview caps one view at 3,000 trees and uses zoom-dependent density. Both detailed modes use thin,
206
+ tapered trunks, branches that enter the crown, softly shaded overlapping ellipsoids, and stable
207
+ per-tree scale and rotation. `simple` reduces lobe and segment counts; `complex` keeps the complete
208
+ round, open, avenue, pine, cypress, and palm silhouettes. Each archetype remains fused into one
209
+ reusable geometry and every complete tree remains
210
+ one GPU instance, so the additional detail introduces no extra draw calls. The experimental
211
+ renderer defaults to its WebGL2 backend, which
212
+ draws those instances directly without a Three.js scene traversal; append `&treeBackend=three` for
213
+ the compatibility backend. Use `?treeRenderer=circle`, `?treeRenderer=simple`, or
214
+ `?treeRenderer=complex&treeBenchmark=1` to compare identical scenes; the benchmark reports frame
215
+ p95, query, selection, build and render time, draw calls, and triangle count in the tree status badge.
216
+ Visible candidates come from MapLibre's worker-built tile index instead of a second copy of the
217
+ vector source. Terrain height is sampled on a fixed 5-by-5 viewport grid and interpolated per tree,
218
+ avoiding hundreds of synchronous terrain queries when a new batch first appears.
219
+ The separate `TREES ON/OFF` control sits below the building `3D ON/OFF` control and pauses tree
220
+ refresh work while disabled. Both choices survive reloads and shared links through the
221
+ `buildings3d=on|off` and `trees3d=on|off` URL parameters.
222
+
223
+ Detailed trees retain the compiler's scene order: they render above road and pedestrian surfaces,
224
+ ordinary building geometry, and detailed landmark models. Map labels and other annotations remain
225
+ above the physical scene, while road names and route references stay attached to the carriageway
226
+ below buildings and trees.
227
+
228
+ The same control toggles any compiled layer whose metadata contains
229
+ `tileflow:3d-toggle = building`. It switches the building volumes independently from the camera,
230
+ so enabling 3D never changes the pitch and users remain free to tilt the map with pointer controls.
231
+
232
+ ### Detailed landmark models
233
+
234
+ A hidden style layer can opt the Streets preview into detailed GLB landmarks by setting
235
+ `tileflow:landmark-manifest-url` in its metadata. The bounded version-2 manifest indexes GLB entries
236
+ inside one or more PMTiles archives and is fetched only when the 3D building control is enabled near
237
+ the landmark runtime zoom. Archive URLs may be absolute or relative to the manifest URL.
238
+
239
+ ```json
240
+ {
241
+ "schemaVersion": 2,
242
+ "id": "madrid-landmarks-2026-08-19",
243
+ "minzoom": 16,
244
+ "maximumVisibleModels": 8,
245
+ "maximumCachedModels": 16,
246
+ "archives": [
247
+ {
248
+ "id": "madrid-v1",
249
+ "url": "./madrid-landmarks-v1.pmtiles",
250
+ "bytes": 14832912,
251
+ "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
252
+ }
253
+ ],
254
+ "landmarks": [
255
+ {
256
+ "id": "palacio-cibeles",
257
+ "center": [-3.6921, 40.4193],
258
+ "bounds": [-3.693, 40.4186, -3.6912, 40.42],
259
+ "priority": 100,
260
+ "models": [
261
+ {
262
+ "minzoom": 16,
263
+ "archiveId": "madrid-v1",
264
+ "z": 0,
265
+ "x": 0,
266
+ "y": 0,
267
+ "bytes": 481024,
268
+ "sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
269
+ "axisConvention": "EUN_Y_UP"
270
+ },
271
+ {
272
+ "minzoom": 18,
273
+ "archiveId": "madrid-v1",
274
+ "z": 1,
275
+ "x": 0,
276
+ "y": 0,
277
+ "bytes": 1276416,
278
+ "sha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
279
+ "axisConvention": "EUN_Y_UP"
280
+ }
281
+ ]
282
+ }
283
+ ]
284
+ }
285
+ ```
286
+
287
+ `maximumVisibleModels` is bounded to 1–64. `maximumCachedModels` defaults to twice that value,
288
+ must be at least the visible limit, and is bounded to 128. Higher `priority` values win when more
289
+ landmarks intersect the viewport than can be shown. `models[0]` is the base LOD and must start at the
290
+ manifest `minzoom`; later entries replace it at their distinct `minzoom`. The parser bounds manifest,
291
+ archive, landmark, model and GLB-entry sizes, rejects credential-bearing or non-HTTP archive URLs,
292
+ and verifies each extracted entry's declared byte length and SHA-256 before parsing it.
293
+
294
+ The preview starts the Three.js runtime and manifest request together one zoom before the landmark
295
+ layer becomes visible. A landmark entering directly at a close zoom always displays its base LOD
296
+ first and upgrades in place. Nearby base LODs are warmed in the parsed-model cache. Style layers
297
+ marked with `tileflow:landmark-fallback = true` remain visible until the corresponding active GLB is
298
+ ready, so a cold request never leaves an empty building volume.
299
+
300
+ Embedded Streets previews also accept a same-origin parent command without reloading the document:
301
+
302
+ ```js
303
+ iframe.contentWindow.postMessage(
304
+ {
305
+ type: 'tileflow:set-map-state',
306
+ schemaVersion: 1,
307
+ state: {
308
+ center: [-3.688344, 40.453053],
309
+ zoom: 17.75,
310
+ bearing: -24,
311
+ pitch: 58,
312
+ buildings3d: true,
313
+ trees3d: false,
314
+ visibleLayerGroups: ['labels', 'roads', 'buildings', 'landuse', 'water'],
315
+ },
316
+ },
317
+ location.origin,
318
+ );
319
+ ```
320
+
321
+ The command is applied atomically only for the embedding parent, with exact same-origin and numeric
322
+ range checks. `visibleLayerGroups` is optional and accepts only `labels`, `pois`, `roads`, `transit`,
323
+ `buildings`, `landuse`, and `water`; omitted groups are hidden while base background and attribution
324
+ remain. Camera and toggle state continue to be written to the preview URL after application.
325
+
326
+ GLB base colors, textures, emissive values, metalness, and roughness are authoritative and are not
327
+ rewritten by the preview. Models outside the active selection remain hidden in a least-recently-used
328
+ cache; leaving the cache disposes their geometry, materials, and textures.
329
+
330
+ The vector building represented by a detailed model must carry `hide_3d = true`. The ordinary
331
+ building extrusion and both screen-space shadow passes filter on that field, preventing the coarse
332
+ volume from intersecting the GLB while retaining the flat footprint below it. This flag belongs in
333
+ the vector-tile dataset; the landmark endpoint cannot substitute for it. Version model and GLB URLs
334
+ so they can be served with immutable caching. Cross-origin endpoints must allow credentialed GETs
335
+ because the development renderer requests the manifest and models with credentials.
336
+
337
+ If a map uses `icons: ['./icons/brand']`, build artifacts include the generated MapLibre
338
+ `sprite.json`, `sprite.png`, `sprite@2x.json`, and `sprite@2x.png` beside its style. Ordinary sources
339
+ are normalized into 24 px icon cells. A source named `<id>.pattern.svg` (or another supported raster
340
+ format with the same marker) instead keeps its intrinsic dimensions and is published as `<id>`.
341
+ This provides calibrated, non-square textures for line and fill patterns while the runtime still
342
+ consumes only the generated PNG sprite. Pattern widths must be a power of two from 2 through 512
343
+ pixels, matching MapLibre's seamless line-pattern contract.
344
+
345
+ `icons` is one ordered array. Omitting it inherits the parent's exact array, declaring it replaces
346
+ the array atomically, and `[]` means no icons. Use a spread to preserve imported directories while
347
+ adding or replacing files:
348
+
349
+ ```ts
350
+ import {defineMap} from '@tileflow/core';
351
+ import {streets} from '@tileflow/maps';
352
+
353
+ export default defineMap({
354
+ id: 'brand-map',
355
+ version: 1,
356
+ extends: streets,
357
+ icons: [...streets.icons, './icons/brand'],
358
+ });
359
+ ```
360
+
361
+ Directories apply left to right. `<id>.<ext>` publishes an icon as `<id>`;
362
+ `<id>.pattern.<ext>` publishes the intrinsic-size pattern as `<id>`. The published ID must already
363
+ be canonical lower-kebab-case. A later file replaces an earlier file only for the same exact ID;
364
+ case-only collisions fail. Package maps export `streetsIcons`, `ferrarisIcons`,
365
+ `haradIcons`, `siegfriedIcons`, `soundingsIcons`, `cyberpunkIcons`, `matrixIcons`, and `verdantIcons`
366
+ directory descriptors from `@tileflow/maps`. Cyberpunk, Matrix, and Siegfried also export
367
+ `cyberpunkFonts`, `matrixFonts`, and `siegfriedFonts`. Preparation resolves package descriptors inside their owning
368
+ package, checks real-path containment, and compiles them through the same pipeline as a
369
+ config-relative directory. There are no built-in/source/sprite selectors, mappings, icon-specific
370
+ inheritance, or compatibility aliases.
371
+
372
+ Local directories resolve from the directory containing the selected `tileflow.config.ts`, while
373
+ the workspace `cwd` remains their containment boundary. Canonical local syntax starts with `./` or
374
+ one or more leading `../` segments and then uses only non-empty segments other than `.` or `..`;
375
+ trailing or repeated slashes, backslashes, controls, and paths longer than 512 characters fail.
376
+
377
+ ## Structured config inspection and diagnostics
378
+
379
+ Custom Node tooling can use the same deterministic documents as `tileflow validate --json` and
380
+ `tileflow inspect --json`:
381
+
382
+ ```ts
383
+ import {inspectTileflowConfig} from '@tileflow/dev/inspect';
384
+ import {
385
+ createTileflowCommandFailureDocument,
386
+ serializeTileflowCommandDocument,
387
+ } from '@tileflow/dev/validation';
388
+
389
+ try {
390
+ const inspection = await inspectTileflowConfig({config: 'tileflow.config.ts'});
391
+ process.stdout.write(serializeTileflowCommandDocument(inspection));
392
+ } catch (error) {
393
+ const failure = createTileflowCommandFailureDocument('inspect', error, process.cwd(), {
394
+ code: 'INSPECTION_FAILED',
395
+ phase: 'config-inspection',
396
+ });
397
+ process.stderr.write(serializeTileflowCommandDocument(failure));
398
+ }
399
+ ```
65
400
 
66
- If a map uses `icons: './icons/brand'`, build artifacts include the generated
67
- MapLibre `sprite.json`, `sprite.png`, `sprite@2x.json`, and `sprite@2x.png`
68
- assets alongside the styles.
401
+ The inspection resolves one config load into sorted maps, root-to-leaf lineage, declared paths, and
402
+ leaf-level merge provenance. Its `themeContract` exposes the default/system mapping, shared token
403
+ schema, concrete resolved token values, typography and lighting, differences from the default, and
404
+ stable `THEME_IMPLICIT_FIXED` diagnostics across modules, terrain, and compiler-owned effects. Each
405
+ diagnostic carries a semantic scope and, for compiler effects, its owner, target, and effect kind,
406
+ plus machine-readable severity and remediation. Colors, fonts, and images are followed through
407
+ expression outputs; numeric diagnostics intentionally cover only direct visual scalars, excluding
408
+ expression operands, zoom stops, tuples, placement, priority, and other structural numbers.
409
+ Agents can therefore edit semantic roles without
410
+ reverse-engineering compiler layers or parsing prose. It never returns `inputFiles`, absolute filesystem paths, credentials,
411
+ URL queries, data URLs, or recognized secret formats. Structured summaries and diagnostics use the
412
+ same required schema-version-1 fields and bounded safe suggestions so consumers do not need to
413
+ parse human prose.
69
414
 
70
415
  ## Bounded vector-feature inspection
71
416
 
@@ -73,13 +418,14 @@ Custom Node tooling can inspect only the features visible near a configured came
73
418
  a browser:
74
419
 
75
420
  ```ts
76
- import {inspectTileflowFeatures} from '@tileflow/dev';
421
+ import {inspectTileflowFeatures} from '@tileflow/dev/inspect';
77
422
 
78
- const inspection = await inspectTileflowFeatures(project, 'madrid', {
423
+ const internalCatalog = artifacts.project;
424
+ const inspection = await inspectTileflowFeatures(internalCatalog, 'madrid', {
79
425
  center: [-3.6927512, 40.4086555],
80
426
  zoom: 16,
81
427
  sourceLayers: ['poi'],
82
- properties: ['name', 'class', 'subclass', 'rank'],
428
+ properties: ['name', 'category', 'type', 'icon', 'filter_rank', 'size_rank'],
83
429
  width: 512,
84
430
  height: 512,
85
431
  limit: 100,
@@ -96,25 +442,33 @@ paths. PMTiles and authenticated provider adapters are not currently supported b
96
442
  Deployment adapters can compile the same sources without writing them to disk:
97
443
 
98
444
  ```ts
99
- import {compileTileflowIconPackages} from '@tileflow/dev';
445
+ import {compileTileflowIconPackages} from '@tileflow/dev/icons';
100
446
 
101
- const result = await compileTileflowIconPackages(project, {
447
+ const internalCatalog = artifacts.project;
448
+ const result = await compileTileflowIconPackages(internalCatalog, {
102
449
  cwd: process.cwd(),
103
450
  target: 'hosted',
104
451
  });
105
452
  ```
106
453
 
107
- When a Streets map does not select a local or external icon set, the pipeline compiles the built-in
108
- `tileflow-streets` POI catalog and merges any mapping-only overrides on top of its semantic mapping.
109
- `modules.poi.icons: false`, a disabled POI module, or the `none` POI preset suppresses that implicit
110
- package. An explicit local source or hosted sprite always replaces it.
454
+ Streets declares `[streetsIcons]` and keeps light/dark image-token targets in that one closure;
455
+ Cyberpunk and Matrix independently declare `[cyberpunkIcons]` and `[matrixIcons]`. Ferraris, Härad,
456
+ Siegfried, Soundings, and Verdant declare only `[ferrarisIcons]`, `[haradIcons]`, `[siegfriedIcons]`,
457
+ `[soundingsIcons]`, and `[verdantIcons]`, respectively. No official root composes another map's
458
+ assets even though all use the semantic Streets compiler ABI. Härad's directory contains nine original Tileflow
459
+ patterns inspired by Lantmäteriet's CC0 Häradsekonomiska kartan series from 1859–1934. Soundings owns ten
460
+ original nautical symbols and patterns. An application map may inherit a root's exact array,
461
+ replace it, clear it with `[]`, or
462
+ compose it explicitly with a spread.
463
+ `modules.poi.icons: false` or a disabled POI module suppresses POI icon layers without moving asset
464
+ ownership into dev. POI density is the numeric producer threshold 1–5; it is not an asset-provider
465
+ preset.
111
466
 
112
467
  The hosted target enforces repository containment, portable icon IDs, safe SVG
113
- references, deterministic ordering, and the public package limits. The package-owned Streets
114
- catalog is trusted input and is the only source exempt from project-root containment. It returns
115
- deduplicated generated files plus a canonical manifest/content hash. Upload only
116
- those generated files: source icons and absolute paths remain local. External
117
- sprite URLs are preserved as references and are not downloaded.
468
+ references, deterministic ordering, and the public package limits. Package-owned directories are
469
+ trusted only inside their declared owning package and are exempt from config working-tree
470
+ containment. It returns deduplicated generated files plus a canonical manifest/content hash. Upload
471
+ only those generated files: source icons and absolute paths remain local.
118
472
 
119
473
  The version-1 manifest includes `renderedIcons` in the exact sorted `iconNames` order. Each entry
120
474
  contains SHA-256 digests for the normalized 1x and 2x RGBA cell. The digest framing includes its
@@ -132,22 +486,24 @@ Custom Node tooling can inspect the same local pipeline without walking or decod
132
486
  second time:
133
487
 
134
488
  ```ts
135
- import {inspectTileflowIconCatalogs} from '@tileflow/dev';
489
+ import {inspectTileflowIconCatalogs} from '@tileflow/dev/icons';
136
490
 
137
- const inspection = await inspectTileflowIconCatalogs(project, {
491
+ const internalCatalog = artifacts.project;
492
+ const inspection = await inspectTileflowIconCatalogs(internalCatalog, {
138
493
  cwd: process.cwd(),
139
494
  mapNames: ['madrid'],
140
495
  });
141
496
  ```
142
497
 
143
- The result keeps one catalog per resolved source directory, map-specific resolved mappings,
144
- cwd-relative source metadata, exact atlas rectangles, both manifest pixel digests, and the shared
145
- compiled package. It classifies remote and absent catalogs without fetching them. This is a
146
- local-authoring integration surface: use the hosted compilation target separately for deployment
147
- compatibility, and project only the metadata needed by an external protocol rather than serializing
148
- generated byte arrays accidentally.
498
+ The result keeps one catalog per distinct ordered directory composition, the exact winning source
499
+ for every final ID, explicit same-ID replacements, config-relative source metadata, exact atlas rectangles,
500
+ both manifest pixel digests, and the shared compiled package. `icons: []` is represented as an
501
+ absent catalog. This is a local-authoring integration surface: use the hosted compilation target
502
+ separately for deployment compatibility, and project only the metadata needed by an external
503
+ protocol rather than serializing generated byte arrays accidentally.
149
504
 
150
- This package is public, but it is an integration layer. Map styling primitives live in
151
- `@tileflow/core`; React rendering lives in `@tileflow/react`.
505
+ This package is public, but it is an integration layer. Styling primitives live in
506
+ `@tileflow/core`, official maps and their source assets live in `@tileflow/maps`, and React
507
+ rendering lives in `@tileflow/react`.
152
508
 
153
509
  Docs: https://tileflow.dev/docs
@@ -1,11 +1,69 @@
1
1
  # Third-party notices
2
2
 
3
- ## Google Places icons
3
+ This file records third-party components used by Node preparation and preview. Official map artwork
4
+ and fonts are owned and published by `@tileflow/maps`; `@tileflow/dev` does not carry an `assets/`
5
+ directory. Runtime dependencies are installed as separate npm packages, and their code and native
6
+ binaries are not copied into the dev package.
4
7
 
5
- The glyph artwork in `assets/streets-poi` is adapted from Google Places icons published in the
6
- [Google Places API documentation](https://developers.google.com/maps/documentation/places/web-service/icons).
7
- Copyright Google LLC. The documentation content is licensed under the
8
- [Creative Commons Attribution 4.0 License](https://creativecommons.org/licenses/by/4.0/).
8
+ ## Package-owned map fonts
9
9
 
10
- Tileflow modifies the source artwork by resizing and recoloring the glyphs, then composing them
11
- inside circular category markers with a white rim and shadow.
10
+ The generic font preparation pipeline reads map-owned TTF, OTF, and WOFF2 directories; it does not
11
+ embed fonts in `@tileflow/dev`. Each source directory must carry its own `LICENSE.txt`, which is
12
+ copied into the deterministic build artifacts whenever that directory contributes a selected face.
13
+
14
+ The Cyberpunk map in `@tileflow/maps` currently owns unmodified Oxanium Medium and SemiBold TTFs.
15
+ Oxanium is copyright 2019 The Oxanium Project Authors and is distributed under the SIL Open Font
16
+ License 1.1. The source and binaries are pinned to upstream commit
17
+ `a8f39e0c71186190027a093e9001459410192d1e`; the complete license accompanies the files at
18
+ `@tileflow/maps/assets/cyberpunk/fonts/LICENSE.txt`.
19
+
20
+ - Source: <https://github.com/sevmeyer/oxanium/tree/a8f39e0c71186190027a093e9001459410192d1e>
21
+ - Google Fonts specimen: <https://fonts.google.com/specimen/Oxanium>
22
+ - License: <https://github.com/sevmeyer/oxanium/blob/a8f39e0c71186190027a093e9001459410192d1e/OFL.txt>
23
+
24
+ ## fontkit 2.0.4
25
+
26
+ Font metadata for TTF, OTF, and WOFF2 inputs is read with the separately installed `fontkit`
27
+ package. fontkit is copyright Devon Govett and contributors and is distributed under the MIT
28
+ License.
29
+
30
+ - Source: <https://github.com/foliojs/fontkit/tree/v2.0.4>
31
+ - License: <https://github.com/foliojs/fontkit/blob/v2.0.4/LICENSE>
32
+
33
+ ## PMTiles JavaScript 4.5.0
34
+
35
+ The local Streets preview loads the separately installed `pmtiles` browser decoder to retrieve
36
+ individual landmark GLB entries with HTTP byte ranges. PMTiles is copyright Protomaps and
37
+ contributors and is distributed under the BSD 3-Clause License. Its `fflate` dependency is
38
+ copyright 2020 Arjun Barrett and is distributed under the MIT License.
39
+
40
+ - PMTiles source and license: <https://github.com/protomaps/PMTiles/tree/v4.5.0/js>
41
+ - fflate source and license: <https://github.com/101arrowz/fflate/tree/v0.8.2>
42
+
43
+ ## sharp 0.35.3
44
+
45
+ `@tileflow/dev` declares `sharp` as an optional dependency and loads it dynamically only when local
46
+ SVG or raster icon sources must be inspected and packed. The Tileflow build externalizes `sharp`
47
+ rather than bundling it. `sharp` is copyright Lovell Fuller and contributors and is distributed under
48
+ the Apache License 2.0.
49
+
50
+ - Source: <https://github.com/lovell/sharp/tree/v0.35.3>
51
+ - License: <https://github.com/lovell/sharp/blob/v0.35.3/LICENSE>
52
+
53
+ ## sharp libvips distributions 1.3.2
54
+
55
+ `sharp` selects a separate optional `@img/sharp-libvips-*` package for the current platform. The
56
+ installed platform-package manifest declares `LGPL-3.0-or-later`; the packaging scripts themselves
57
+ are distributed under Apache-2.0. The packages contain prebuilt libvips and separately licensed
58
+ runtime dependencies. For `sharp` 0.35.3 the selected libvips ABI is 8.18.3.
59
+
60
+ - Packaging source: <https://github.com/lovell/sharp-libvips/tree/v1.3.2>
61
+ - Packaging-script license: <https://github.com/lovell/sharp-libvips/blob/v1.3.2/LICENSE>
62
+ - libvips 8.18.3 source: <https://github.com/libvips/libvips/tree/v8.18.3>
63
+ - libvips 8.18.3 license (LGPL-2.1-or-later):
64
+ <https://github.com/libvips/libvips/blob/v8.18.3/LICENSE>
65
+
66
+ Because `sharp` remains a separately installed optional package, users can install, update, remove,
67
+ or replace it independently of `@tileflow/dev`. Tileflow does not modify libvips and does not place a
68
+ libvips shared library or a `sharp` native addon inside any Tileflow package tarball. Release smoke
69
+ tests enforce that package boundary.
package/TRADEMARKS.md ADDED
@@ -0,0 +1,10 @@
1
+ # Tileflow trademarks
2
+
3
+ The Apache License, Version 2.0, and the Tileflow generated-output grant do not grant permission to
4
+ use the Tileflow name, `tileflow.dev`, logos, service marks, product names, or other Tileflow brand
5
+ features except for reasonable and customary use that truthfully describes the origin of the SDK or
6
+ compatibility with Tileflow, and for reproducing required notices.
7
+
8
+ You may not use Tileflow brand features in a way that suggests sponsorship, endorsement, or an
9
+ official Tileflow product when none exists. Third-party names and marks remain the property of their
10
+ respective owners.