@tileflow/dev 0.1.0-alpha.37 → 0.1.0-alpha.39

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
@@ -37,8 +37,6 @@ The CLI's local Inspector provides a dockable layer inspector, loaded tiles, ass
37
37
  controls and frame timing. Its element picker selects a layer and evaluates its rules using the
38
38
  feature clicked on the map.
39
39
  Debug's Overdraw overlay uses a heatmap with a visible pass-count legend and explanation.
40
- See [Inspector and compiler inspection](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/local-comparison-workbench-and-compiler-inspection.md)
41
- for interaction details and observation limits.
42
40
 
43
41
  ## Prepare a generation
44
42
 
@@ -66,6 +64,8 @@ compile icons separately can use `prepareTileflowMapTreeModels` from `@tileflow/
66
64
  the map's model directories, base directory, working directory, target and diagnostic path. Pass
67
65
  its `models` result as `preparedAssets.models` when compiling the style; retain its source
68
66
  identities and watch paths when managing revisions. The artifact pipeline does this automatically.
67
+ A GLB whose materials name only `bark`, `leaves`, `fronds` or `stem` is a tree model; any other
68
+ is a building model, whose materials name its parts.
69
69
 
70
70
  To write production output rather than manage it in memory:
71
71
 
@@ -82,6 +82,8 @@ await writeTileflowBuildArtifacts({
82
82
 
83
83
  Serve the complete output. The writer installs immutable generation assets before changing the
84
84
  manifest pointer, tracks its own files, and refuses to replace a hosted manifest by default.
85
+ Native builds retain embedded PNG image sources as hashed files beside the styles, so their
86
+ resource URLs remain portable without permitting data URLs in the native profile.
85
87
  `overwriteHostedManifest: true` is an explicit migration option, not a normal build setting.
86
88
  Production rejects unresolved local PMTiles; publish data explicitly or provide an application-owned
87
89
  production source. Local development snapshots do not publish or redistribute the original dataset.
@@ -122,7 +124,12 @@ export async function createMapHandler() {
122
124
  The caller mounts `fetch` in its server and calls `close()` on shutdown. This factory does not open
123
125
  a listener. Sessions follow config imports and local asset inputs, retain the last good generation
124
126
  after invalid edits, and publish structured state changes through `subscribe()`.
127
+ Session creation waits for the first valid generation or a settled invalid result, including
128
+ when a watched edit replaces the initial build before it finishes.
125
129
  Watching follows aliased working directories, including newly created or removed icon metadata.
130
+ Package assets resolve from the config directory. Linked package assets inside the selected working
131
+ tree are watched too; installed assets in `node_modules` remain immutable.
132
+
126
133
  A single startup consistency check complements ordinary watcher notifications; no recurring scan
127
134
  is added.
128
135
 
@@ -138,7 +145,7 @@ for local PMTiles inspection, and `/validation` for diagnostics. `/preview` is t
138
145
  contract, including experimental preview-only rendering; it does not promise hosted support.
139
146
  The package has no root entry point. `/server` also exposes the reusable Inspector shell. The CLI
140
147
  connects appearance controls and shared browser context through its optional local
141
- workflow adapter; see the [Inspector guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/local-comparison-workbench-and-compiler-inspection.md).
148
+ workflow adapter.
142
149
  The navigation contains **Map**, **Layers**, **Tiles**, **Assets**, **Debug** and **Diagnostics**.
143
150
  Zoom, Pitch and Bearing appear as **Z**, **P** and **B** directly below it, with live Zoom and Pitch
144
151
  sliders and **Copy for AI**. Pitch and Bearing each have an inline reset to 0° within the map's
@@ -163,7 +170,6 @@ feature's layer in **Rules**. Rules expose live colors, numeric sliders, enums,
163
170
  defaults, and checkboxes for temporarily disabling paint, layout and filters. Diagnosis, Data and
164
171
  Origin explain renderer state, sample features and available compiler provenance. **Visible**
165
172
  filters to the current viewport. Context can be reviewed as Markdown or JSON before copying.
166
- See [Layers inspection](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/layers-inspection.md).
167
173
 
168
174
  **Tiles** shows resident map tiles as a coordinate grid and source-grouped list. Source layers,
169
175
  Features and Info expose raw payload sizes, consumer layers, paginated features, GeoJSON and bounds.
@@ -184,8 +190,6 @@ drawing work. Findings open the affected layer and rule, show available evidence
184
190
  for an agent. Unavailable observations and stale results remain explicit. **Diagnosis** in
185
191
  Layers describes the selected layer's zoom, visibility and feature-rule context without turning
186
192
  empty viewport queries into errors. Legacy `tab=beacon` links open Diagnostics.
187
- See [Map design diagnostics](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/map-design-diagnostics.md)
188
- for detectors, sampling and interpretation limits.
189
193
 
190
194
  The preview's globe rim follows the rendered canvas silhouette, including camera and viewport
191
195
  changes, and fades between zooms 3 and 6. Its colors follow the map's atmosphere when declared.
@@ -254,22 +258,8 @@ publish anything. Every normal entry point composes declared shared descriptors
254
258
  catalog publication and lock maintenance stay in the `tileflow` CLI, which is the only writer of
255
259
  `tileflow.icons.lock.json` and the only caller that resolves `latest`.
256
260
 
257
- ## Detailed guides
258
-
259
- These references are also included under `docs/` in the installed package:
260
-
261
- - [Building model preparation](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/building-models.md): colour slots, architectural roles, ambient visibility and compatibility.
262
- - [Native artifact preparation](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/native-artifacts.md): static compatibility, renderer-qualified output, and limitations.
263
- - [Artifact lifecycle](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/artifact-lifecycle.md): generations, immutable snapshots, provenance, and cleanup.
264
- - [Local comparison and inspection](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/local-comparison-workbench-and-compiler-inspection.md): Inspector controls and preview-only models.
265
- - [Config diagnostics](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/structured-config-inspection-and-diagnostics.md): bounded, machine-readable inspection.
266
- - [PMTiles inspection](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/bounded-pmtiles-resource-inspection.md): metadata, sampling limits, and requested properties.
267
- - [Feature and icon inspection](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/bounded-vector-feature-inspection.md): supported sources, icon compilation, and delivered-pixel hashes.
268
-
269
- For an installed release, prefer its declarations and packaged guides over newer source on `main`.
270
-
271
261
  ## Recolorable icons
272
262
 
273
263
  Declare SDF defaults and optional stretch/content layout in `tileflow.icons.json` beside ordinary artwork. Preparation
274
264
  verifies both densities and rejects mixed representations within one symbol layer, including
275
- fallbacks and local overrides. See [Author recolorable and adaptable icons](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/native-icons.md) for the input profile, diagnostics and recovery.
265
+ fallbacks and local overrides.
package/dist/artifacts.js CHANGED
@@ -32,20 +32,20 @@ import {
32
32
  usesTileflowLineBackgroundGlyphs,
33
33
  writeTileflowArtifactPlan,
34
34
  writeTileflowBuildArtifacts
35
- } from "./chunk-UKWAQKLH.js";
35
+ } from "./chunk-AVJMTXW5.js";
36
36
  import {
37
37
  getTileflowAssetBasePath,
38
38
  getTileflowAssetFileName,
39
39
  joinTileflowPublicUrl,
40
40
  normalizeTileflowBasePath,
41
41
  resolveTileflowArtifactPublicUrls
42
- } from "./chunk-UTRO4IBQ.js";
42
+ } from "./chunk-FOMTSCOP.js";
43
43
  import {
44
44
  encodeTileflowLandmarkRegion,
45
45
  packageTileflowLandmarkTile,
46
46
  prepareTileflowBuildingModel
47
- } from "./chunk-5RT4CHSK.js";
48
- import "./chunk-7C4F6GAX.js";
47
+ } from "./chunk-C4VPHZ3F.js";
48
+ import "./chunk-RTU7TD4M.js";
49
49
  import "./chunk-KDFTE4Q5.js";
50
50
  import {
51
51
  defaultTileflowConfigPath
@@ -4,11 +4,11 @@ import {
4
4
  joinTileflowPublicUrl,
5
5
  prepareTileflowStyleFonts,
6
6
  replaceTileflowStyleFontSources
7
- } from "./chunk-UTRO4IBQ.js";
7
+ } from "./chunk-FOMTSCOP.js";
8
8
  import {
9
9
  getTileflowIconWatchPaths,
10
10
  prepareTileflowCatalogIcons
11
- } from "./chunk-5RT4CHSK.js";
11
+ } from "./chunk-C4VPHZ3F.js";
12
12
  import {
13
13
  TileflowStyleValidationError,
14
14
  normalizeTileflowStyleValidationIssues,
@@ -28,7 +28,7 @@ import {
28
28
  } from "./chunk-ONYQNLNA.js";
29
29
 
30
30
  // src/artifacts.ts
31
- import { createHash as createHash4 } from "crypto";
31
+ import { createHash as createHash5 } from "crypto";
32
32
  import { realpathSync as realpathSync2 } from "fs";
33
33
  import {
34
34
  copyFile as copyFile2,
@@ -68,8 +68,32 @@ import {
68
68
  TileflowNativeCompatibilityError as TileflowNativeCompatibilityError4
69
69
  } from "@tileflow/core/native-profile";
70
70
 
71
+ // src/image-source-assets.ts
72
+ import { createHash } from "crypto";
73
+ function prepareEmbeddedImageSources(input) {
74
+ const styles = structuredClone(input);
75
+ const assets = /* @__PURE__ */ new Map();
76
+ for (const [map, themes] of Object.entries(styles)) {
77
+ for (const style of Object.values(themes)) {
78
+ for (const source of Object.values(style.sources)) {
79
+ if (source.type !== "image" || typeof source.url !== "string") continue;
80
+ const match = /^data:image\/png;base64,([A-Za-z0-9+/]+={0,2})$/u.exec(source.url);
81
+ if (!match || match[1].length > 14 * 1024 * 1024) continue;
82
+ const bytes = Buffer.from(match[1], "base64");
83
+ if (bytes.length < 24 || !bytes.subarray(0, 8).equals(Buffer.from("89504e470d0a1a0a", "hex")))
84
+ continue;
85
+ const hash = createHash("sha256").update(bytes).digest("hex");
86
+ const fileName = `images/${map}/${hash}.png`;
87
+ assets.set(fileName, { contentType: "image/png", fileName, source: bytes });
88
+ source.url = `../../${fileName}`;
89
+ }
90
+ }
91
+ }
92
+ return { styles, assets: [...assets.values()] };
93
+ }
94
+
71
95
  // src/line-background-glyphs.ts
72
- import { createHash, randomUUID } from "crypto";
96
+ import { createHash as createHash2, randomUUID } from "crypto";
73
97
  import { lstat, mkdir, readFile, rename, rm, writeFile } from "fs/promises";
74
98
  import { resolve } from "path";
75
99
  import {
@@ -245,7 +269,7 @@ function absoluteHttpUrl(value) {
245
269
  }
246
270
  }
247
271
  function digest(value) {
248
- return createHash("sha256").update(value).digest("hex").slice(0, 32);
272
+ return createHash2("sha256").update(value).digest("hex").slice(0, 32);
249
273
  }
250
274
  function hasCode(error, code) {
251
275
  return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
@@ -567,25 +591,28 @@ function hasErrorCode(error, code) {
567
591
  }
568
592
 
569
593
  // src/native-artifacts.ts
570
- import { createHash as createHash2 } from "crypto";
571
594
  import {
572
595
  convertFilter,
573
596
  validateStyleMin
574
597
  } from "@maplibre/maplibre-gl-style-spec";
598
+ import { createHash as createHash3 } from "crypto";
575
599
  import {
576
600
  getTileflowStyleFontFaces,
577
601
  serializeCanonicalJson,
578
602
  steadyTileflowNauticalLights,
579
- tileflowStyleFontFacesMetadataKey,
580
603
  tileflowIconIdPattern,
581
- tileflowIconPackageLimits
604
+ tileflowIconPackageLimits,
605
+ tileflowStyleFontFacesMetadataKey
582
606
  } from "@tileflow/core";
607
+ import {
608
+ prepareTileflowNativePathGround
609
+ } from "@tileflow/core/build";
583
610
  import {
584
611
  createTileflowNativeDiagnostic,
585
612
  TileflowNativeCompatibilityError,
586
613
  tileflowNativeProfileLimits,
587
- validateTileflowNativeStyle,
588
- validateTileflowNativePreparedStyle
614
+ validateTileflowNativePreparedStyle,
615
+ validateTileflowNativeStyle
589
616
  } from "@tileflow/core/native-profile";
590
617
 
591
618
  // src/native-layer-references.ts
@@ -1266,7 +1293,9 @@ function lowerTileflowNativeCompiledStyles(input, policy = {}) {
1266
1293
  styles[map] = {};
1267
1294
  for (const theme of Object.keys(input[map]).sort()) {
1268
1295
  const web = input[map][theme];
1269
- const original = steadyTileflowNauticalLights(web);
1296
+ const steady = steadyTileflowNauticalLights(web);
1297
+ const original = prepareTileflowNativePathGround(steady);
1298
+ const pathGroundFallback = original !== steady;
1270
1299
  try {
1271
1300
  const options = { ...urlOptions(map, theme, policy), deferFontClosure: true };
1272
1301
  const pending = validateTileflowNativeStyle(original, options);
@@ -1324,12 +1353,13 @@ function lowerTileflowNativeCompiledStyles(input, policy = {}) {
1324
1353
  transformations.push({
1325
1354
  map,
1326
1355
  theme,
1327
- inputStyleSha256: createHash2("sha256").update(serializeCanonicalJson(web)).digest("hex"),
1328
- loweredStyleSha256: createHash2("sha256").update(serializeCanonicalJson(style)).digest("hex"),
1356
+ inputStyleSha256: createHash3("sha256").update(serializeCanonicalJson(web)).digest("hex"),
1357
+ loweredStyleSha256: createHash3("sha256").update(serializeCanonicalJson(style)).digest("hex"),
1329
1358
  inputLayers: lowered.inputLayers,
1330
1359
  outputLayers: lowered.outputLayers,
1331
1360
  projection: lowered.projection,
1332
1361
  ...lowered.sky ? { sky: lowered.sky } : {},
1362
+ ...pathGroundFallback ? { pathGround: "ground-fallback" } : {},
1333
1363
  layers: lowered.layers,
1334
1364
  symbolOverlap: lowered.symbolOverlap
1335
1365
  });
@@ -1441,7 +1471,7 @@ function isNativeFontAsset(asset) {
1441
1471
  if (!/\.(?:ttf|otf)$/u.test(asset.fileName) || !["00010000", "4f54544f"].includes(signature))
1442
1472
  return false;
1443
1473
  const expectedHash = /-([a-f0-9]{64})\.(?:ttf|otf)$/u.exec(asset.fileName)?.[1];
1444
- return expectedHash === createHash2("sha256").update(bytes).digest("hex");
1474
+ return expectedHash === createHash3("sha256").update(bytes).digest("hex");
1445
1475
  }
1446
1476
  function assertTileflowNativeGeneratedStyle(style, map, theme, policy = {}) {
1447
1477
  const issues = validateTileflowNativePreparedStyle(style, urlOptions(map, theme, policy));
@@ -1647,6 +1677,9 @@ var TileflowArtifactSessionImpl = class {
1647
1677
  await watcherReady;
1648
1678
  }
1649
1679
  await this.refresh("initial");
1680
+ while (!this.#current && this.#state.status === "building" && !this.#closed) {
1681
+ await Promise.all([...this.#refreshes]);
1682
+ }
1650
1683
  if (this.#watcher && !this.#closed) {
1651
1684
  this.#startupCheckTimer = setTimeout(() => {
1652
1685
  this.#startupCheckTimer = void 0;
@@ -2050,7 +2083,7 @@ function boundDiagnosticText(value, limit) {
2050
2083
  }
2051
2084
 
2052
2085
  // src/hosted-native-artifacts.ts
2053
- import { createHash as createHash3 } from "crypto";
2086
+ import { createHash as createHash4 } from "crypto";
2054
2087
  import { serializeCanonicalJson as serializeCanonicalJson2 } from "@tileflow/core";
2055
2088
  import {
2056
2089
  inferTileflowDataRequirements,
@@ -2062,7 +2095,7 @@ import {
2062
2095
  TileflowNativeCompatibilityError as TileflowNativeCompatibilityError3,
2063
2096
  createTileflowNativeDiagnostic as createTileflowNativeDiagnostic2
2064
2097
  } from "@tileflow/core/native-profile";
2065
- var digest2 = (value) => createHash3("sha256").update(serializeCanonicalJson2(value)).digest("hex");
2098
+ var digest2 = (value) => createHash4("sha256").update(serializeCanonicalJson2(value)).digest("hex");
2066
2099
  var invalidSource = () => new TileflowNativeCompatibilityError3([
2067
2100
  createTileflowNativeDiagnostic2("NATIVE_UNSUPPORTED_SOURCE", "/sources")
2068
2101
  ]);
@@ -2239,7 +2272,8 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
2239
2272
  apiBaseUrl: options.apiBaseUrl,
2240
2273
  mapAssets: prepared.mapAssets
2241
2274
  });
2242
- const nativeLowering = renderer === "native" ? lowerTileflowNativeCompiledStyles(compiledStyles, nativeUrls) : void 0;
2275
+ const embeddedImages = renderer === "native" ? prepareEmbeddedImageSources(compiledStyles) : { styles: compiledStyles, assets: [] };
2276
+ const nativeLowering = renderer === "native" ? lowerTileflowNativeCompiledStyles(embeddedImages.styles, nativeUrls) : void 0;
2243
2277
  const rendererStyles = nativeLowering?.styles ?? compiledStyles;
2244
2278
  if (renderer === "native")
2245
2279
  assertTileflowNativeCompiledStyles(prepared.project, rendererStyles, nativeUrls);
@@ -2262,7 +2296,7 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
2262
2296
  }
2263
2297
  throw error;
2264
2298
  });
2265
- const assets = [...prepared.assets, ...preparedFonts.assets];
2299
+ const assets = [...prepared.assets, ...preparedFonts.assets, ...embeddedImages.assets];
2266
2300
  const lineBackgroundGlyphs = renderer === "native" || options.target === "production" ? { providers: {}, styles: preparedFonts.styles } : prepareTileflowLineBackgroundGlyphs(preparedFonts.styles, {
2267
2301
  assetBaseUrl: resolveRendererAssetBaseUrl(options)
2268
2302
  });
@@ -2526,7 +2560,7 @@ function retargetLocalFontAssetUrl(source, assets, generation) {
2526
2560
  function getMapBuildAssets(mapName, assets, fontAssets) {
2527
2561
  const selected = /* @__PURE__ */ new Map();
2528
2562
  for (const asset of assets) {
2529
- if (asset.fileName.startsWith(`icons/${mapName}/`) || asset.fileName.startsWith(`models/${mapName}/`))
2563
+ if (asset.fileName.startsWith(`icons/${mapName}/`) || asset.fileName.startsWith(`models/${mapName}/`) || asset.fileName.startsWith(`images/${mapName}/`))
2530
2564
  selected.set(asset.fileName, asset);
2531
2565
  }
2532
2566
  for (const asset of fontAssets) {
@@ -2537,7 +2571,7 @@ function getMapBuildAssets(mapName, assets, fontAssets) {
2537
2571
  );
2538
2572
  }
2539
2573
  function hashBytes(source) {
2540
- return createHash4("sha256").update(typeof source === "string" ? Buffer.from(source, "utf8") : Buffer.from(source)).digest("hex");
2574
+ return createHash5("sha256").update(typeof source === "string" ? Buffer.from(source, "utf8") : Buffer.from(source)).digest("hex");
2541
2575
  }
2542
2576
  async function createTileflowBuildProvenance(cwd) {
2543
2577
  const projectDirectory = resolve4(cwd);
@@ -2981,10 +3015,11 @@ function assertTileflowArtifactFileName(fileName) {
2981
3015
  const logicalName = fileName.replace(/^generations\/[a-f0-9]{64}\//, "");
2982
3016
  const styleMatch = /^styles\/([^/]+)\/([^/]+)\.json$/u.exec(logicalName);
2983
3017
  const iconMatch = /^icons\/([^/]+)\/sprite(?:@2x)?\.(?:json|png)$/u.exec(logicalName);
3018
+ const imageMatch = /^images\/([^/]+)\/[a-f0-9]{64}\.png$/u.exec(logicalName);
2984
3019
  const modelMatch = /^models\/([^/]+)\/[a-z0-9]+(?:-[a-z0-9]+)*-[a-f0-9]{16}\.glb$/u.exec(
2985
3020
  logicalName
2986
3021
  );
2987
- if (fileName === "manifest.json" || fileName === tileflowMapBuildManifestFileName || fileName === tileflowNativeBuildRecordFileName || styleMatch && tileflowMapIdSchema.safeParse(styleMatch[1]).success && tileflowThemeNameSchema.safeParse(styleMatch[2]).success || iconMatch && tileflowMapIdSchema.safeParse(iconMatch[1]).success || modelMatch && tileflowMapIdSchema.safeParse(modelMatch[1]).success || /^tilesets\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}\.pmtiles$/u.test(logicalName) || /^fonts\/[a-z0-9]+(?:-[a-z0-9]+)*-[a-f0-9]{64}\.(?:otf|ttf|woff2)$/.test(logicalName) || /^fonts\/licenses\/license-[a-f0-9]{64}\.txt$/.test(logicalName)) {
3022
+ if (fileName === "manifest.json" || fileName === tileflowMapBuildManifestFileName || fileName === tileflowNativeBuildRecordFileName || styleMatch && tileflowMapIdSchema.safeParse(styleMatch[1]).success && tileflowThemeNameSchema.safeParse(styleMatch[2]).success || iconMatch && tileflowMapIdSchema.safeParse(iconMatch[1]).success || imageMatch && tileflowMapIdSchema.safeParse(imageMatch[1]).success || modelMatch && tileflowMapIdSchema.safeParse(modelMatch[1]).success || /^tilesets\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}\.pmtiles$/u.test(logicalName) || /^fonts\/[a-z0-9]+(?:-[a-z0-9]+)*-[a-f0-9]{64}\.(?:otf|ttf|woff2)$/.test(logicalName) || /^fonts\/licenses\/license-[a-f0-9]{64}\.txt$/.test(logicalName)) {
2988
3023
  return;
2989
3024
  }
2990
3025
  throw new Error(`Unexpected Tileflow managed artifact file name: ${fileName}`);
@@ -3024,7 +3059,7 @@ async function removeManagedFile(path) {
3024
3059
  }
3025
3060
  }
3026
3061
  function hashArtifactGeneration(files) {
3027
- const hash = createHash4("sha256");
3062
+ const hash = createHash5("sha256");
3028
3063
  for (const file of files) {
3029
3064
  const source = typeof file.source === "string" ? Buffer.from(file.source, "utf8") : Buffer.from(file.source);
3030
3065
  hash.update(`${file.fileName}\0${file.contentType}\0${source.byteLength}\0`, "utf8");