@tileflow/dev 0.1.0-alpha.38 → 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
 
@@ -84,6 +82,8 @@ await writeTileflowBuildArtifacts({
84
82
 
85
83
  Serve the complete output. The writer installs immutable generation assets before changing the
86
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.
87
87
  `overwriteHostedManifest: true` is an explicit migration option, not a normal build setting.
88
88
  Production rejects unresolved local PMTiles; publish data explicitly or provide an application-owned
89
89
  production source. Local development snapshots do not publish or redistribute the original dataset.
@@ -124,7 +124,12 @@ export async function createMapHandler() {
124
124
  The caller mounts `fetch` in its server and calls `close()` on shutdown. This factory does not open
125
125
  a listener. Sessions follow config imports and local asset inputs, retain the last good generation
126
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.
127
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
+
128
133
  A single startup consistency check complements ordinary watcher notifications; no recurring scan
129
134
  is added.
130
135
 
@@ -140,7 +145,7 @@ for local PMTiles inspection, and `/validation` for diagnostics. `/preview` is t
140
145
  contract, including experimental preview-only rendering; it does not promise hosted support.
141
146
  The package has no root entry point. `/server` also exposes the reusable Inspector shell. The CLI
142
147
  connects appearance controls and shared browser context through its optional local
143
- 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.
144
149
  The navigation contains **Map**, **Layers**, **Tiles**, **Assets**, **Debug** and **Diagnostics**.
145
150
  Zoom, Pitch and Bearing appear as **Z**, **P** and **B** directly below it, with live Zoom and Pitch
146
151
  sliders and **Copy for AI**. Pitch and Bearing each have an inline reset to 0° within the map's
@@ -165,7 +170,6 @@ feature's layer in **Rules**. Rules expose live colors, numeric sliders, enums,
165
170
  defaults, and checkboxes for temporarily disabling paint, layout and filters. Diagnosis, Data and
166
171
  Origin explain renderer state, sample features and available compiler provenance. **Visible**
167
172
  filters to the current viewport. Context can be reviewed as Markdown or JSON before copying.
168
- See [Layers inspection](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/layers-inspection.md).
169
173
 
170
174
  **Tiles** shows resident map tiles as a coordinate grid and source-grouped list. Source layers,
171
175
  Features and Info expose raw payload sizes, consumer layers, paginated features, GeoJSON and bounds.
@@ -186,8 +190,6 @@ drawing work. Findings open the affected layer and rule, show available evidence
186
190
  for an agent. Unavailable observations and stale results remain explicit. **Diagnosis** in
187
191
  Layers describes the selected layer's zoom, visibility and feature-rule context without turning
188
192
  empty viewport queries into errors. Legacy `tab=beacon` links open Diagnostics.
189
- See [Map design diagnostics](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/map-design-diagnostics.md)
190
- for detectors, sampling and interpretation limits.
191
193
 
192
194
  The preview's globe rim follows the rendered canvas silhouette, including camera and viewport
193
195
  changes, and fades between zooms 3 and 6. Its colors follow the map's atmosphere when declared.
@@ -256,22 +258,8 @@ publish anything. Every normal entry point composes declared shared descriptors
256
258
  catalog publication and lock maintenance stay in the `tileflow` CLI, which is the only writer of
257
259
  `tileflow.icons.lock.json` and the only caller that resolves `latest`.
258
260
 
259
- ## Detailed guides
260
-
261
- These references are also included under `docs/` in the installed package:
262
-
263
- - [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.
264
- - [Native artifact preparation](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/native-artifacts.md): static compatibility, renderer-qualified output, and limitations.
265
- - [Artifact lifecycle](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/artifact-lifecycle.md): generations, immutable snapshots, provenance, and cleanup.
266
- - [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.
267
- - [Config diagnostics](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/structured-config-inspection-and-diagnostics.md): bounded, machine-readable inspection.
268
- - [PMTiles inspection](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/bounded-pmtiles-resource-inspection.md): metadata, sampling limits, and requested properties.
269
- - [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.
270
-
271
- For an installed release, prefer its declarations and packaged guides over newer source on `main`.
272
-
273
261
  ## Recolorable icons
274
262
 
275
263
  Declare SDF defaults and optional stretch/content layout in `tileflow.icons.json` beside ordinary artwork. Preparation
276
264
  verifies both densities and rejects mixed representations within one symbol layer, including
277
- 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-MRFNAWWR.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-54VM4SNV.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-54VM4SNV.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");
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  TileflowAssetDirectoryError,
3
3
  resolveTileflowAssetDirectories
4
- } from "./chunk-7C4F6GAX.js";
4
+ } from "./chunk-RTU7TD4M.js";
5
5
 
6
6
  // src/icons.ts
7
7
  import { readdir as readdir3, readFile as readFile2, realpath as realpath3, stat } from "fs/promises";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  TileflowAssetDirectoryError,
3
3
  resolveTileflowAssetDirectories
4
- } from "./chunk-7C4F6GAX.js";
4
+ } from "./chunk-RTU7TD4M.js";
5
5
  import {
6
6
  isPathWithin
7
7
  } from "./chunk-D4OE4ITL.js";
@@ -71,7 +71,9 @@ async function resolveTileflowAssetDirectories(directories, options) {
71
71
  let packageRoot;
72
72
  try {
73
73
  validatePackageDirectory(directory);
74
- const requireFromProject = createRequire(resolve(realCwd, "__tileflow_config__.cjs"));
74
+ const requireFromProject = createRequire(
75
+ resolve(realBaseDirectory, "__tileflow_config__.cjs")
76
+ );
75
77
  let packageJson;
76
78
  try {
77
79
  packageJson = requireFromProject.resolve(`${directory.package}/package.json`);
@@ -120,7 +122,7 @@ async function resolveTileflowAssetDirectories(directories, options) {
120
122
  containmentRoot,
121
123
  packageOwned,
122
124
  realPath: realCandidate,
123
- watch: !packageOwned
125
+ watch: !packageOwned || isPathInside(realCwd, realCandidate) && !realCandidate.split(/[\\/]/u).includes("node_modules")
124
126
  });
125
127
  } catch (error) {
126
128
  issues.push({
package/dist/fonts.js CHANGED
@@ -4,8 +4,8 @@ import {
4
4
  getTileflowFontWatchPaths,
5
5
  prepareTileflowStyleFonts,
6
6
  replaceTileflowStyleFontSources
7
- } from "./chunk-UTRO4IBQ.js";
8
- import "./chunk-7C4F6GAX.js";
7
+ } from "./chunk-FOMTSCOP.js";
8
+ import "./chunk-RTU7TD4M.js";
9
9
  import "./chunk-D4OE4ITL.js";
10
10
  export {
11
11
  TileflowFontCompilationError,
package/dist/icons.js CHANGED
@@ -17,8 +17,8 @@ import {
17
17
  storeTileflowIconSetArtifact,
18
18
  verifyTileflowIconArtifact,
19
19
  writeTileflowIconsLockfile
20
- } from "./chunk-54VM4SNV.js";
21
- import "./chunk-7C4F6GAX.js";
20
+ } from "./chunk-C4VPHZ3F.js";
21
+ import "./chunk-RTU7TD4M.js";
22
22
  export {
23
23
  TileflowIconCompilationError,
24
24
  compileTileflowIconPackages,
package/dist/preview.js CHANGED
@@ -272,8 +272,8 @@ function renderTileflowPreviewHtml(preview, basePath, initialStatus, isSemanticP
272
272
  import * as maplibregl from "${basePath}/__runtime/maplibre-gl.mjs";
273
273
  import {createPreviewObserver} from "${basePath}/__runtime/inspector-observer.js";
274
274
  import {loadTileflowStyleFonts} from "${basePath}/__runtime/tileflow-browser.js";
275
- import {attachTileflowLabelPlacement} from "${basePath}/__runtime/tileflow-browser.js";
276
- import {registerTileflowContourProtocol} from "${basePath}/__runtime/tileflow-browser.js";
275
+ import {attachTileflowHorizonQueries, attachTileflowLabelPlacement} from "${basePath}/__runtime/tileflow-browser.js";
276
+ import {registerTileflowContourProtocol, registerTileflowPathGroundProtocol} from "${basePath}/__runtime/tileflow-browser.js";
277
277
  import {registerTileflowPmtilesProtocol} from "${basePath}/__runtime/tileflow-browser.js";
278
278
  import {attachTileflowNauticalLights, attachTileflowNotices} from "${basePath}/__runtime/tileflow-browser.js";
279
279
  import {attachTileflowAtmosphere, tileflowMaxPitch} from "${basePath}/__runtime/tileflow-browser.js";
@@ -1674,6 +1674,7 @@ function renderTileflowPreviewHtml(preview, basePath, initialStatus, isSemanticP
1674
1674
  maplibregl.setWorkerCount?.(mapWorkerCount);
1675
1675
  await loadTileflowStyleFonts(styleUrl, {fontFaces: previewFontFaces});
1676
1676
  registerTileflowContourProtocol({addProtocol: maplibregl.addProtocol});
1677
+ registerTileflowPathGroundProtocol({addProtocol: maplibregl.addProtocol});
1677
1678
  registerTileflowPmtilesProtocol({addProtocol: maplibregl.addProtocol});
1678
1679
  const map = new maplibregl.Map({
1679
1680
  canvasContextAttributes: tileflowCanvasContextAttributes({antialias: mapAntialias}),
@@ -1689,6 +1690,7 @@ function renderTileflowPreviewHtml(preview, basePath, initialStatus, isSemanticP
1689
1690
  attachTileflowNauticalLights(map);
1690
1691
  attachTileflowAtmosphere(map);
1691
1692
  attachTileflowLabelPlacement(map);
1693
+ attachTileflowHorizonQueries(map);
1692
1694
  inspectorObserver?.attach(map);
1693
1695
  installMapBenchmark(map);
1694
1696
  if (window.parent === window) map.addControl(new maplibregl.NavigationControl(), "top-right");
package/dist/server.js CHANGED
@@ -5,16 +5,16 @@ import {
5
5
  parseTileflowLineBackgroundGlyphPath,
6
6
  tileflowArtifactSessionSchemaVersion,
7
7
  tileflowLineBackgroundGlyphRoute
8
- } from "./chunk-MRFNAWWR.js";
8
+ } from "./chunk-AVJMTXW5.js";
9
9
  import {
10
10
  getTileflowAssetBasePath,
11
11
  getTileflowAssetFileName,
12
12
  joinTileflowPublicUrl,
13
13
  normalizeTileflowBasePath,
14
14
  resolveTileflowArtifactPublicUrls
15
- } from "./chunk-UTRO4IBQ.js";
16
- import "./chunk-54VM4SNV.js";
17
- import "./chunk-7C4F6GAX.js";
15
+ } from "./chunk-FOMTSCOP.js";
16
+ import "./chunk-C4VPHZ3F.js";
17
+ import "./chunk-RTU7TD4M.js";
18
18
  import {
19
19
  TileflowStyleValidationError
20
20
  } from "./chunk-KDFTE4Q5.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/dev",
3
- "version": "0.1.0-alpha.38",
3
+ "version": "0.1.0-alpha.39",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Node development utilities for Tileflow configs and builds.",
6
6
  "type": "module",
@@ -27,7 +27,6 @@
27
27
  "files": [
28
28
  "dist",
29
29
  "ui-fonts",
30
- "docs",
31
30
  "THIRD_PARTY_NOTICES.md",
32
31
  "LICENSE"
33
32
  ],
@@ -84,8 +83,8 @@
84
83
  "dependencies": {
85
84
  "@mapbox/vector-tile": "3.0.0",
86
85
  "@maplibre/maplibre-gl-style-spec": "26.4.4",
87
- "@tileflow/core": ">=0.1.0-alpha.41 <0.1.0-beta.0",
88
- "chokidar": "4.0.3",
86
+ "@tileflow/core": ">=0.1.0-alpha.43 <0.1.0-beta.0",
87
+ "chokidar": "6.0.0",
89
88
  "fontkit": "2.0.4",
90
89
  "jiti": "^2.7.0",
91
90
  "maplibre-gl": "6.13.0",
@@ -96,7 +95,7 @@
96
95
  "three": "0.185.1"
97
96
  },
98
97
  "devDependencies": {
99
- "@tileflow/maps": "0.1.0-alpha.15"
98
+ "@tileflow/maps": "0.1.0-alpha.17"
100
99
  },
101
100
  "optionalDependencies": {
102
101
  "sharp": "0.35.5"
@@ -1,152 +0,0 @@
1
- # Artifact lifecycle
2
-
3
- Start with the [@tileflow/dev guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/README.md) for installation and a complete first example.
4
-
5
- Node utilities used by the Tileflow CLI and build integrations.
6
-
7
- Most apps should use `tileflow`, `@tileflow/vite`, or `@tileflow/next` directly. Use this
8
- package when you are building a custom dev server, bundler plugin, or deployment adapter that needs
9
- to load and compile the single map exported by `tileflow.config.ts`.
10
-
11
- ```ts
12
- import {createTileflowBuildArtifacts} from '@tileflow/dev/artifacts';
13
-
14
- const artifacts = await createTileflowBuildArtifacts({
15
- config: 'tileflow.config.ts',
16
- });
17
-
18
- try {
19
- // Serve, capture, or write this exact generation.
20
- } finally {
21
- await artifacts.dispose?.();
22
- }
23
- ```
24
-
25
- The integration surfaces are deliberately split: use `@tileflow/dev/artifacts` for source
26
- preparation and production outputs, `@tileflow/dev/server` for HTTP development handling,
27
- `@tileflow/dev/icons` for icon compilation, `@tileflow/dev/inspect` for safe resolved-config and
28
- bounded feature inspection, `@tileflow/dev/validation` for structured diagnostics, and
29
- `@tileflow/dev/preview` only for the local preview contract. The package has no root entry point.
30
-
31
- Artifact construction is an explicit `source map → resolved map → prepared map → ArtifactPlan`
32
- pipeline. Map inheritance resolves before its ordered local/package icon and font directories are
33
- prepared, and before the result reaches the core style compiler. The Node layer may normalize
34
- the map into an internal build catalog, but that catalog is orchestration state rather than another
35
- public authoring model. The plan exposes its complete files and observed input graph, so build
36
- adapters emit and watch the same generation instead of reconstructing either list themselves. Production manifests
37
- point to content-addressed `generations/<sha256>/...` theme styles and sprites; those immutable files are
38
- installed before the manifest pointer changes. Stable style paths are
39
- `styles/<map>/<theme>.json`; every manifest lookup resolves a concrete theme. The disk writer also stages the complete plan,
40
- records a managed-file inventory, rolls back caught filesystem failures, and removes only stale
41
- files named by its previous valid inventory. Stable `styles/` and `icons/` paths remain available,
42
- but generation-consistent consumers resolve maps through `manifest.json`.
43
-
44
- A local `hostedTileset()` path remains user-owned input. Dev performs bounded header, root-directory,
45
- and metadata checks, then creates an opaque immutable snapshot under
46
- `.tileflow/cache/pmtiles-snapshots/v1/`, using copy-on-write cloning when available and a safe copy
47
- otherwise. It does not hash the complete archive or traverse every leaf directory. Dev and Capture
48
- read only that snapshot. The current generation and already-started acquisitions retain references;
49
- a replaced generation accepts no new acquisitions and is collected after its last acquisition.
50
- The Style-facing path remains `tilesets/<logical-id>.pmtiles` across generations; physical snapshot
51
- identity stays out of Style JSON. Standalone Capture retains one generation for the complete render.
52
- Application Capture delegates PMTiles reads to the application's development server, where each
53
- range request retains its generation until that response is prepared; it does not lease one
54
- generation for the complete capture. Strong generation ETags make a cached PMTiles read restart if
55
- current changes between its ranges. Invalid edits keep the prior valid generation.
56
-
57
- Production writers reject unresolved local PMTiles instead of copying, content-addressing,
58
- deduplicating, retaining, or publishing them. Production data therefore requires an explicit
59
- `tileset publish` operation or an application-owned PMTiles location. Managed publication owns its
60
- separate exhaustive validation and version lifecycle. Startup removes snapshot generations owned
61
- by dead processes, and symlinked snapshot-store boundaries are rejected.
62
-
63
- Every plan also emits canonical `build-manifest.json` (schema version 1). For each map it records
64
- the leaf `mapVersion`, resolved lineage, effective icon/font source identities, semantic compiler ABI, and
65
- one entry per concrete theme with `colorScheme`, identity, inferred legacy `dataRequirements`,
66
- multi-source `sourceRequirements`, and `styleSha256`. The multi-source contract records each
67
- referenced vector source independently and records raster DEM source ID, encoding, and tile size;
68
- it therefore distinguishes Bathymetry vector bands from optional relief without inventing vector
69
- fields. `mapRevisionSha256` identifies the effective cartographic definition after `extends`;
70
- `assetSetSha256` for that map's generated runtime resources. Data requirements are derived from the
71
- final Style layers and fields rather than copied from a manually maintained allowlist, so disabled
72
- or overridden modules do not claim data they no longer use. Maps use the single implicit semantic
73
- compiler; its ABI remains a separate `semanticCompiler: {name, version}` build-manifest axis.
74
-
75
- The map revision is versioned and domain-separated. It contains the resolved cartographic design,
76
- semantic language version, effective public module/render-stack design, and effective icon/font
77
- identities. Map `id`, `name`, editorial `mapVersion`, default `view`, capture `scenes`, package
78
- SemVer, compiler ABI, local paths, timestamps, generated sprite/font output, and a
79
- concrete resolution of a floating World selector are deliberately outside it. Changing a shadowed
80
- ancestor does not change the revision; changing an effective cartographic override or source asset
81
- does. Lineage and the leaf `mapVersion` remain beside the hash for traceability, and Style JSON owns
82
- its compiled-output identity. Hosted browser policy is managed outside repository artifacts. The
83
- top-level `provenance` block records the exact participating Tileflow package versions and the
84
- nearest package-manager lockfile's format and content hash, without embedding its local path;
85
- provenance remains outside every map revision. The same portable block is available to Hosted
86
- adapters through
87
- `createTileflowBuildProvenance(cwd)`. The dev server exposes the complete document at
88
- `/build-manifest.json`.
89
-
90
- `createTileflowStyle`, `createTileflowStyles`, and artifact construction all enforce the same
91
- recursive JSON-value invariant and MapLibre style-spec semantics without fetching remote resources.
92
- The family API returns `styles[map][theme]`; it never flattens a default theme into a map alias.
93
- Invalid output throws `TileflowStyleValidationError` with at most 32 deterministic
94
- `map`/`path`/`message` issues; layer indexes are projected to stable layer IDs.
95
-
96
- For a watched, last-known-good integration, use one artifact session rather than recompiling each
97
- request independently:
98
-
99
- ```ts
100
- import {createTileflowArtifactSession} from '@tileflow/dev/artifacts';
101
-
102
- const session = await createTileflowArtifactSession({
103
- config: 'tileflow.config.ts',
104
- watch: true,
105
- });
106
-
107
- const unsubscribe = session.subscribe((state) => {
108
- // building -> ready, or invalid while getLastGoodArtifacts() remains available
109
- console.log(state.status, state.generation);
110
- });
111
-
112
- unsubscribe();
113
- await session.close();
114
- ```
115
-
116
- `getState()` and `getLastGoodArtifacts()` are borrowed synchronous views. Before starting async
117
- work, call `session.acquireArtifacts(generation?)` and release the returned acquisition in `finally`.
118
- A replacement stops new acquisitions of the old generation and deletes its snapshot after the last
119
- release. `close()` waits for outstanding acquisitions.
120
-
121
- The bounded watcher follows the config, transitive local TypeScript/JavaScript/JSON imports, and
122
- effective local icon/font directory inputs. Generations are monotonic, overlapping refreshes are latest-wins, invalid
123
- edits retain the last good snapshot, and caller-supplied output directories can be ignored to avoid
124
- feedback loops. `tileflow preview`, `capture --watch`, and
125
- framework adapters use this shared status
126
- vocabulary. The built-in preview reloads after its live event stream reconnects following a server
127
- restart, even when the replacement process begins again at generation 1.
128
-
129
- Custom preview servers use the exported map or one of its standalone scenes:
130
-
131
- ```ts
132
- import {createTileflowDevRequestHandler} from '@tileflow/dev/server';
133
-
134
- const fetch = createTileflowDevRequestHandler({
135
- config: 'tileflow.config.ts',
136
- scene: 'madrid-mobile',
137
- });
138
-
139
- // On server shutdown:
140
- await fetch.close();
141
- ```
142
-
143
- The handler does not close a caller-owned session.
144
-
145
- Map preview uses the exported map's `view` and accepts one concrete `theme`; scene preview uses its
146
- committed concrete theme, camera, and CSS viewport metadata from that same map. A scene does not
147
- repeat the map ID because its owner is implicit.
148
- `resolveTileflowPreview()` exposes the validated selection for custom integrations.
149
- Application-target scenes remain the responsibility of the application's development server. The
150
- built-in preview records longitude, latitude, zoom, bearing, and pitch in the current URL, so browser
151
- refreshes and config-triggered reloads return to the same view. Removing those query parameters
152
- restores the configured camera.