@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 +9 -21
- package/dist/artifacts.js +4 -4
- package/dist/{chunk-MRFNAWWR.js → chunk-AVJMTXW5.js} +57 -22
- package/dist/{chunk-54VM4SNV.js → chunk-C4VPHZ3F.js} +1 -1
- package/dist/{chunk-UTRO4IBQ.js → chunk-FOMTSCOP.js} +1 -1
- package/dist/{chunk-7C4F6GAX.js → chunk-RTU7TD4M.js} +4 -2
- package/dist/fonts.js +2 -2
- package/dist/icons.js +2 -2
- package/dist/preview.js +4 -2
- package/dist/server.js +4 -4
- package/package.json +4 -5
- package/docs/artifact-lifecycle.md +0 -152
- package/docs/beacon-map-audits.md +0 -145
- package/docs/bounded-pmtiles-resource-inspection.md +0 -21
- package/docs/bounded-vector-feature-inspection.md +0 -99
- package/docs/building-models.md +0 -218
- package/docs/layers-inspection.md +0 -92
- package/docs/local-comparison-workbench-and-compiler-inspection.md +0 -130
- package/docs/map-design-diagnostics.md +0 -76
- package/docs/native-artifacts.md +0 -287
- package/docs/native-icons.md +0 -129
- package/docs/structured-config-inspection-and-diagnostics.md +0 -39
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
|
|
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.
|
|
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-
|
|
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-
|
|
42
|
+
} from "./chunk-FOMTSCOP.js";
|
|
43
43
|
import {
|
|
44
44
|
encodeTileflowLandmarkRegion,
|
|
45
45
|
packageTileflowLandmarkTile,
|
|
46
46
|
prepareTileflowBuildingModel
|
|
47
|
-
} from "./chunk-
|
|
48
|
-
import "./chunk-
|
|
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-
|
|
7
|
+
} from "./chunk-FOMTSCOP.js";
|
|
8
8
|
import {
|
|
9
9
|
getTileflowIconWatchPaths,
|
|
10
10
|
prepareTileflowCatalogIcons
|
|
11
|
-
} from "./chunk-
|
|
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
|
|
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
|
|
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
|
-
|
|
588
|
-
|
|
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
|
|
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:
|
|
1328
|
-
loweredStyleSha256:
|
|
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 ===
|
|
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
|
|
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) =>
|
|
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
|
|
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
|
|
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 =
|
|
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");
|
|
@@ -71,7 +71,9 @@ async function resolveTileflowAssetDirectories(directories, options) {
|
|
|
71
71
|
let packageRoot;
|
|
72
72
|
try {
|
|
73
73
|
validatePackageDirectory(directory);
|
|
74
|
-
const requireFromProject = createRequire(
|
|
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-
|
|
8
|
-
import "./chunk-
|
|
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-
|
|
21
|
-
import "./chunk-
|
|
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-
|
|
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-
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
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.
|
|
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.
|
|
88
|
-
"chokidar": "
|
|
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.
|
|
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.
|