@tileflow/dev 0.1.0-alpha.16 → 0.1.0-alpha.17

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
@@ -45,6 +45,22 @@ edits retain the last good snapshot, and caller-supplied output directories can
45
45
  feedback loops. `tileflow dev`, `capture --watch`, and framework adapters use this shared status
46
46
  vocabulary.
47
47
 
48
+ Custom preview servers can select the same map or standalone scene semantics as the CLI:
49
+
50
+ ```ts
51
+ import {createTileflowDevRequestHandler} from '@tileflow/dev';
52
+
53
+ const fetch = createTileflowDevRequestHandler({
54
+ session,
55
+ scene: 'madrid-mobile',
56
+ });
57
+ ```
58
+
59
+ `map` and `scene` are mutually exclusive. Map preview uses the configured `view`; scene preview
60
+ uses its committed camera and CSS viewport. `resolveTileflowPreview()` exposes the validated
61
+ selection for custom integrations. Application-target scenes remain the responsibility of the
62
+ application's development server.
63
+
48
64
  If a map uses `icons: './icons/brand'`, build artifacts include the generated
49
65
  MapLibre `sprite.json`, `sprite.png`, `sprite@2x.json`, and `sprite@2x.png`
50
66
  assets alongside the styles.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { IncomingMessage, ServerResponse } from 'node:http';
2
- import { TileflowIconPackageFileName, TileflowIconPackageManifest, TileflowProjectConfig, MapLibreStyle, TileflowManifest, ValidationMessage, TileflowStyleOptions } from '@tileflow/core';
2
+ import { TileflowIconPackageFileName, TileflowIconPackageManifest, TileflowProjectConfig, MapLibreStyle, NormalizedTileflowCaptureScene, TileflowManifest, ValidationMessage, TileflowStyleOptions } from '@tileflow/core';
3
3
 
4
4
  declare const tileflowArtifactSessionSchemaVersion: 1;
5
5
  type TileflowArtifactDiagnostic = {
@@ -244,11 +244,27 @@ type TileflowDevRequestHandlerOptions = {
244
244
  basePath?: string;
245
245
  config?: string;
246
246
  cwd?: string;
247
+ map?: string;
247
248
  onError?: (error: unknown) => void;
249
+ scene?: string;
248
250
  session?: TileflowArtifactSession;
249
251
  styleBaseUrl?: string;
250
252
  tileBaseUrl?: string;
251
253
  };
254
+ type TileflowPreviewSelection = {
255
+ map?: string;
256
+ scene?: string;
257
+ };
258
+ type ResolvedTileflowPreview = {
259
+ camera: NormalizedTileflowCaptureScene['camera'];
260
+ label: string;
261
+ mapName: string;
262
+ viewport?: NormalizedTileflowCaptureScene['viewport'];
263
+ };
264
+ declare class TileflowPreviewSelectionError extends Error {
265
+ readonly code: "PREVIEW_SELECTION_INVALID";
266
+ constructor(message: string);
267
+ }
252
268
  type TileflowBuildArtifactsOptions = {
253
269
  assetBaseUrl?: string;
254
270
  config?: string;
@@ -278,6 +294,7 @@ declare function assertValidTileflowConfig(project: TileflowProjectConfig): void
278
294
  declare function asTileflowProjectConfig(input: unknown): TileflowProjectConfig;
279
295
  declare function getTileflowMapNames(project: TileflowProjectConfig): string[];
280
296
  declare function getFirstTileflowMapName(project: TileflowProjectConfig): string;
297
+ declare function resolveTileflowPreview(project: TileflowProjectConfig, selection?: TileflowPreviewSelection): ResolvedTileflowPreview;
281
298
  declare function getTileflowStyleMapName(path: string): string;
282
299
  declare function createTileflowManifest(project: TileflowProjectConfig, options?: TileflowManifestOptions): TileflowManifest;
283
300
  declare function createTileflowStyle(project: TileflowProjectConfig, mapName: string, options?: TileflowStyleOptions): MapLibreStyle;
@@ -302,4 +319,4 @@ declare function createTileflowArtifactStatus(state: TileflowArtifactSessionStat
302
319
  };
303
320
  declare function normalizeTileflowBasePath(value?: string): string;
304
321
 
305
- export { type CompileTileflowIconPackagesResult, type CompiledTileflowIconPackage, type CompiledTileflowIconPackageFile, type InspectTileflowIconCatalogsOptions, type LoadTileflowConfigOptions, type PreparedTileflowProject, type TileflowArtifactDiagnostic, type TileflowArtifactSession, type TileflowArtifactSessionOptions, type TileflowArtifactSessionState, type TileflowBuildArtifacts, type TileflowBuildArtifactsOptions, type TileflowBuildAsset, type TileflowDevRequestHandlerOptions, type TileflowFeatureInspection, type TileflowFeatureInspectionOptions, type TileflowIconCatalog, type TileflowIconCatalogAtlasRectangle, type TileflowIconCatalogIcon, type TileflowIconCatalogInspection, type TileflowIconCatalogMap, type TileflowIconCatalogMappedFrom, type TileflowIconCatalogMapping, type TileflowIconCatalogRenderedDensity, type TileflowIconCatalogSourceFormat, TileflowIconCompilationError, type TileflowIconCompilationIssue, type TileflowIconCompilationTarget, type TileflowInspectedFeature, type TileflowInspectedProperty, type TileflowManifestOptions, type TileflowMapIconPackageBinding, TileflowStyleValidationError, type TileflowStyleValidationIssue, TileflowValidationError, type WriteTileflowBuildArtifactsOptions, asTileflowProjectConfig, assertValidTileflowConfig, assertValidTileflowStyle, compileTileflowIconPackages, createTileflowArtifactDiagnostics, createTileflowArtifactSession, createTileflowArtifactStatus, createTileflowBuildArtifacts, createTileflowDevRequestHandler, createTileflowManifest, createTileflowNodeRequest, createTileflowStyle, createTileflowStyles, defaultTileflowApiUrl, defaultTileflowConfigPath, defaultTileflowManifestPath, defaultTileflowTileset, getFirstTileflowMapName, getTileflowAssetBasePath, getTileflowAssetFileName, getTileflowMapNames, getTileflowStyleMapName, getTileflowWatchPaths, iconFileExtensions, inspectTileflowFeatures, inspectTileflowIconCatalogs, isTileflowRequestUrl, joinTileflowPublicUrl, loadTileflowConfig, loadValidTileflowConfig, normalizeTileflowBasePath, prepareTileflowProjectIcons, tileflowArtifactSessionSchemaVersion, validateTileflowStyle, writeTileflowBuildArtifacts, writeTileflowNodeResponse };
322
+ export { type CompileTileflowIconPackagesResult, type CompiledTileflowIconPackage, type CompiledTileflowIconPackageFile, type InspectTileflowIconCatalogsOptions, type LoadTileflowConfigOptions, type PreparedTileflowProject, type ResolvedTileflowPreview, type TileflowArtifactDiagnostic, type TileflowArtifactSession, type TileflowArtifactSessionOptions, type TileflowArtifactSessionState, type TileflowBuildArtifacts, type TileflowBuildArtifactsOptions, type TileflowBuildAsset, type TileflowDevRequestHandlerOptions, type TileflowFeatureInspection, type TileflowFeatureInspectionOptions, type TileflowIconCatalog, type TileflowIconCatalogAtlasRectangle, type TileflowIconCatalogIcon, type TileflowIconCatalogInspection, type TileflowIconCatalogMap, type TileflowIconCatalogMappedFrom, type TileflowIconCatalogMapping, type TileflowIconCatalogRenderedDensity, type TileflowIconCatalogSourceFormat, TileflowIconCompilationError, type TileflowIconCompilationIssue, type TileflowIconCompilationTarget, type TileflowInspectedFeature, type TileflowInspectedProperty, type TileflowManifestOptions, type TileflowMapIconPackageBinding, type TileflowPreviewSelection, TileflowPreviewSelectionError, TileflowStyleValidationError, type TileflowStyleValidationIssue, TileflowValidationError, type WriteTileflowBuildArtifactsOptions, asTileflowProjectConfig, assertValidTileflowConfig, assertValidTileflowStyle, compileTileflowIconPackages, createTileflowArtifactDiagnostics, createTileflowArtifactSession, createTileflowArtifactStatus, createTileflowBuildArtifacts, createTileflowDevRequestHandler, createTileflowManifest, createTileflowNodeRequest, createTileflowStyle, createTileflowStyles, defaultTileflowApiUrl, defaultTileflowConfigPath, defaultTileflowManifestPath, defaultTileflowTileset, getFirstTileflowMapName, getTileflowAssetBasePath, getTileflowAssetFileName, getTileflowMapNames, getTileflowStyleMapName, getTileflowWatchPaths, iconFileExtensions, inspectTileflowFeatures, inspectTileflowIconCatalogs, isTileflowRequestUrl, joinTileflowPublicUrl, loadTileflowConfig, loadValidTileflowConfig, normalizeTileflowBasePath, prepareTileflowProjectIcons, resolveTileflowPreview, tileflowArtifactSessionSchemaVersion, validateTileflowStyle, writeTileflowBuildArtifacts, writeTileflowNodeResponse };
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  compareCodeUnits as compareCodeUnits3,
9
9
  createManifest,
10
10
  createStyleFromProject as createStyleFromProject2,
11
+ normalizeTileflowCaptureScene,
11
12
  validateConfig
12
13
  } from "@tileflow/core";
13
14
 
@@ -1840,6 +1841,13 @@ var defaultTileflowApiUrl = "https://api.tileflow.dev";
1840
1841
  var defaultTileflowConfigPath = "tileflow.config.ts";
1841
1842
  var defaultTileflowManifestPath = "public/tileflow/manifest.json";
1842
1843
  var defaultTileflowTileset = "world";
1844
+ var TileflowPreviewSelectionError = class extends Error {
1845
+ code = "PREVIEW_SELECTION_INVALID";
1846
+ constructor(message) {
1847
+ super(message);
1848
+ this.name = "TileflowPreviewSelectionError";
1849
+ }
1850
+ };
1843
1851
  var TileflowValidationError = class extends Error {
1844
1852
  code = "CONFIG_INVALID";
1845
1853
  messages;
@@ -1903,6 +1911,52 @@ function getFirstTileflowMapName(project) {
1903
1911
  }
1904
1912
  return mapName;
1905
1913
  }
1914
+ function resolveTileflowPreview(project, selection = {}) {
1915
+ if (selection.map !== void 0 && selection.scene !== void 0) {
1916
+ throw new TileflowPreviewSelectionError(
1917
+ "Choose either a Tileflow preview map or scene, not both."
1918
+ );
1919
+ }
1920
+ if (selection.scene !== void 0) {
1921
+ const scene = Object.hasOwn(project.scenes ?? {}, selection.scene) ? project.scenes?.[selection.scene] : void 0;
1922
+ if (!scene) {
1923
+ throw new TileflowPreviewSelectionError(`Unknown Tileflow scene: ${selection.scene}`);
1924
+ }
1925
+ const normalized = normalizeTileflowCaptureScene(scene);
1926
+ if (normalized.target.kind === "application") {
1927
+ throw new TileflowPreviewSelectionError(
1928
+ `Tileflow scene "${selection.scene}" targets an application. Preview it through the application's development server.`
1929
+ );
1930
+ }
1931
+ if (!Object.hasOwn(project.maps, normalized.map)) {
1932
+ throw new TileflowPreviewSelectionError(
1933
+ `Tileflow scene "${selection.scene}" references an unknown map: ${normalized.map}`
1934
+ );
1935
+ }
1936
+ return {
1937
+ camera: normalized.camera,
1938
+ label: `${normalized.map} / ${selection.scene} \xB7 ${normalized.viewport.width}\xD7${normalized.viewport.height}`,
1939
+ mapName: normalized.map,
1940
+ viewport: normalized.viewport
1941
+ };
1942
+ }
1943
+ const mapName = selection.map ?? getFirstTileflowMapName(project);
1944
+ const map = Object.hasOwn(project.maps, mapName) ? project.maps[mapName] : void 0;
1945
+ if (!map) {
1946
+ throw new TileflowPreviewSelectionError(`Unknown Tileflow map: ${mapName}`);
1947
+ }
1948
+ return {
1949
+ camera: {
1950
+ type: "center",
1951
+ center: map.view?.center ? [map.view.center[0], map.view.center[1]] : [0, 0],
1952
+ zoom: map.view?.zoom ?? 0,
1953
+ bearing: map.view?.bearing ?? 0,
1954
+ pitch: 0
1955
+ },
1956
+ label: mapName,
1957
+ mapName
1958
+ };
1959
+ }
1906
1960
  function getTileflowStyleMapName(path) {
1907
1961
  const fileName = path.split("/").pop() ?? "";
1908
1962
  return fileName.replace(/\.json$/, "");
@@ -2092,8 +2146,8 @@ function createTileflowDevRequestHandler(options = {}) {
2092
2146
  return jsonResponse(createTileflowArtifactStatus(state));
2093
2147
  }
2094
2148
  if (path === "/" || path === "") {
2095
- const mapName = artifacts ? getFirstTileflowMapName(artifacts.project) : void 0;
2096
- return htmlResponse(previewHtml(mapName, basePath, state));
2149
+ const preview = artifacts ? resolveTileflowPreview(artifacts.project, { map: options.map, scene: options.scene }) : void 0;
2150
+ return htmlResponse(previewHtml(preview, basePath, state));
2097
2151
  }
2098
2152
  if (!artifacts) return unavailableArtifactsResponse(state);
2099
2153
  if (path === "/manifest.json") {
@@ -2199,6 +2253,9 @@ function stripTileflowBasePath(pathname, basePath) {
2199
2253
  return null;
2200
2254
  }
2201
2255
  function tileflowErrorResponse(error) {
2256
+ if (error instanceof TileflowPreviewSelectionError) {
2257
+ return jsonResponse({ error: error.message }, 400);
2258
+ }
2202
2259
  if (error instanceof TileflowStyleValidationError) {
2203
2260
  return jsonResponse(
2204
2261
  {
@@ -2263,9 +2320,23 @@ function resolveAssetBaseUrl(options) {
2263
2320
  function isRelativePublicUrl(value) {
2264
2321
  return !(value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/") || value.startsWith("data:"));
2265
2322
  }
2266
- function previewHtml(mapName, basePath, state) {
2267
- const styleUrl = mapName ? `${basePath}/styles/${mapName}.json` : void 0;
2323
+ function previewHtml(preview, basePath, state) {
2324
+ const styleUrl = preview ? `${basePath}/styles/${preview.mapName}.json` : void 0;
2268
2325
  const initialStatus = createTileflowArtifactStatus(state);
2326
+ const mapOptions = preview ? previewMapOptions(preview) : void 0;
2327
+ const viewportCss = preview?.viewport ? `
2328
+ html, body { min-height: 100%; margin: 0; }
2329
+ body {
2330
+ display: grid;
2331
+ place-items: center;
2332
+ overflow: auto;
2333
+ background: #E8E5DE;
2334
+ }
2335
+ #map {
2336
+ width: ${preview.viewport.width}px;
2337
+ height: ${preview.viewport.height}px;
2338
+ box-shadow: 0 24px 80px rgba(37, 34, 29, 0.18);
2339
+ }` : "html, body, #map { height: 100%; margin: 0; }";
2269
2340
  return `<!doctype html>
2270
2341
  <html lang="en">
2271
2342
  <head>
@@ -2274,7 +2345,7 @@ function previewHtml(mapName, basePath, state) {
2274
2345
  <title>Tileflow Preview</title>
2275
2346
  <link rel="stylesheet" href="${basePath}/__runtime/maplibre-gl.css" />
2276
2347
  <style>
2277
- html, body, #map { height: 100%; margin: 0; }
2348
+ ${viewportCss}
2278
2349
  body { font-family: ui-sans-serif, system-ui, sans-serif; }
2279
2350
  .badge {
2280
2351
  position: fixed;
@@ -2315,20 +2386,21 @@ function previewHtml(mapName, basePath, state) {
2315
2386
  const initialGeneration = initialStatus.generation;
2316
2387
  const badge = document.getElementById("badge");
2317
2388
  const status = document.getElementById("status");
2389
+ const previewLabel = ${JSON.stringify(preview?.label)};
2318
2390
  const styleUrl = ${JSON.stringify(styleUrl)};
2391
+ const previewMapOptions = ${JSON.stringify(mapOptions)};
2319
2392
 
2320
2393
  if (styleUrl) {
2321
2394
  const map = new maplibregl.Map({
2322
2395
  container: "map",
2323
2396
  style: styleUrl,
2324
- center: [-3.7038, 40.4168],
2325
- zoom: 12
2397
+ ...previewMapOptions
2326
2398
  });
2327
2399
  map.addControl(new maplibregl.NavigationControl(), "top-right");
2328
2400
  }
2329
2401
 
2330
2402
  function applyStatus(next) {
2331
- badge.textContent = "Tileflow preview \xB7 " + next.status;
2403
+ badge.textContent = ["Tileflow preview", previewLabel, next.status].filter(Boolean).join(" \xB7 ");
2332
2404
  if (next.status === "invalid") {
2333
2405
  const diagnostics = next.diagnostics || [];
2334
2406
  status.textContent = diagnostics.map((item) =>
@@ -2352,6 +2424,26 @@ function previewHtml(mapName, basePath, state) {
2352
2424
  </body>
2353
2425
  </html>`;
2354
2426
  }
2427
+ function previewMapOptions(preview) {
2428
+ if (preview.camera.type === "center") {
2429
+ return {
2430
+ bearing: preview.camera.bearing,
2431
+ center: preview.camera.center,
2432
+ pitch: preview.camera.pitch,
2433
+ zoom: preview.camera.zoom
2434
+ };
2435
+ }
2436
+ const [west, south, east, north] = preview.camera.bounds;
2437
+ return {
2438
+ bearing: preview.camera.bearing,
2439
+ bounds: [
2440
+ [west, south],
2441
+ [east, north]
2442
+ ],
2443
+ fitBoundsOptions: { padding: preview.camera.padding },
2444
+ pitch: preview.camera.pitch
2445
+ };
2446
+ }
2355
2447
  var localRequire = createRequire(import.meta.url);
2356
2448
  var localMapLibreJavaScript;
2357
2449
  var localMapLibreStylesheet;
@@ -2398,6 +2490,7 @@ function isPathWithin(root, candidate) {
2398
2490
  }
2399
2491
  export {
2400
2492
  TileflowIconCompilationError,
2493
+ TileflowPreviewSelectionError,
2401
2494
  TileflowStyleValidationError,
2402
2495
  TileflowValidationError,
2403
2496
  asTileflowProjectConfig,
@@ -2432,6 +2525,7 @@ export {
2432
2525
  loadValidTileflowConfig,
2433
2526
  normalizeTileflowBasePath,
2434
2527
  prepareTileflowProjectIcons,
2528
+ resolveTileflowPreview,
2435
2529
  tileflowArtifactSessionSchemaVersion,
2436
2530
  validateTileflowStyle,
2437
2531
  writeTileflowBuildArtifacts,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/dev",
3
- "version": "0.1.0-alpha.16",
3
+ "version": "0.1.0-alpha.17",
4
4
  "description": "Node development utilities for Tileflow configs and builds.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -38,12 +38,12 @@
38
38
  "dependencies": {
39
39
  "@mapbox/vector-tile": "2.0.5",
40
40
  "@maplibre/maplibre-gl-style-spec": "24.8.5",
41
+ "@tileflow/core": ">=0.1.0-alpha.17 <0.1.0-beta.0",
41
42
  "chokidar": "4.0.3",
42
43
  "jiti": "^2.7.0",
43
44
  "maplibre-gl": "5.24.0",
44
45
  "pbf": "4.0.2",
45
- "saxes": "6.0.0",
46
- "@tileflow/core": "0.1.0-alpha.16"
46
+ "saxes": "6.0.0"
47
47
  },
48
48
  "optionalDependencies": {
49
49
  "sharp": "0.35.3"