@tileflow/core 0.1.0-alpha.31 → 0.1.0-alpha.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +41 -2
  2. package/THIRD_PARTY_NOTICES.md +25 -2
  3. package/dist/browser.d.ts +1008 -16
  4. package/dist/browser.js +6661 -1392
  5. package/dist/build.d.ts +64 -56
  6. package/dist/build.js +702 -253
  7. package/dist/capture-shot-ChPQvPyi.d.ts +291 -0
  8. package/dist/capture.d.ts +1 -1
  9. package/dist/capture.js +230 -0
  10. package/dist/config-reference.json +1710 -1370
  11. package/dist/{icon-composition-BiOTwrvD.d.ts → icon-composition-CSnN2-Xt.d.ts} +169 -33
  12. package/dist/index.d.ts +57 -13
  13. package/dist/index.js +1684 -800
  14. package/dist/{manifest-types-CTT2eLbp.d.ts → manifest-types-E7m23eXL.d.ts} +18 -1
  15. package/dist/manifest.d.ts +2 -2
  16. package/dist/native-profile.d.ts +25 -11
  17. package/dist/native-profile.js +21 -1
  18. package/dist/native.js +1893 -1884
  19. package/dist/runtime.d.ts +1 -1
  20. package/docs/application-tile-reading.md +80 -0
  21. package/docs/authoring-model.md +16 -6
  22. package/docs/buildings-3d.md +168 -0
  23. package/docs/camera-moves-and-tile-cache.md +112 -0
  24. package/docs/capture-scenes.md +5 -1
  25. package/docs/capture-shots.md +173 -0
  26. package/docs/compiled-style-performance.md +4 -1
  27. package/docs/data-is-separate-from-design.md +103 -9
  28. package/docs/depth-markers.md +65 -0
  29. package/docs/dynamic-style-images.md +100 -0
  30. package/docs/native-artifact-profile.md +5 -3
  31. package/docs/public-api-and-browser-subpath.md +3 -3
  32. package/docs/route-drawing.md +191 -0
  33. package/docs/shared-visual-primitives.md +20 -0
  34. package/docs/street-lights.md +80 -0
  35. package/docs/style-states.md +73 -0
  36. package/docs/sun-and-light.md +125 -0
  37. package/docs/theme-transitions-and-blends.md +41 -2
  38. package/docs/themes-and-module-styles.md +7 -0
  39. package/package.json +4 -1
  40. package/dist/capture-scene-D6soWztE.d.ts +0 -161
package/dist/build.js CHANGED
@@ -72,6 +72,7 @@ var canonicalLayers = {
72
72
  boundary: "boundary",
73
73
  building: "building",
74
74
  businessCorridor: "business_corridor",
75
+ canopy: "canopy",
75
76
  globalLandcover: "globallandcover",
76
77
  houseNumber: "housenumber",
77
78
  landcover: "landcover",
@@ -93,8 +94,10 @@ var canonicalFields = {
93
94
  adminLevel: "admin_level",
94
95
  bicycle: "bicycle",
95
96
  brunnel: "brunnel",
96
- buildingKind: "building_kind",
97
97
  buildingTone: "building_tone",
98
+ canopyClearanceAtZoom15: "clearance_px_z15",
99
+ canopyCoverPercent: "cover_pct",
100
+ canopyRank: "rank",
98
101
  capital: "capital",
99
102
  circularClearanceExtraAtZoom15: "clearance_extra_px_z15",
100
103
  circularInnerRadiusMeters: "inner_radius_m",
@@ -105,9 +108,9 @@ var canonicalFields = {
105
108
  class: "class",
106
109
  crossing: "crossing",
107
110
  circumference: "circumference",
108
- classificationConfidence: "classification_confidence",
109
111
  confidence: "confidence",
110
112
  diameterCrown: "diameter_crown",
113
+ direction: "direction",
111
114
  disputed: "disputed",
112
115
  elevation: "ele",
113
116
  elevationFeet: "ele_ft",
@@ -115,7 +118,6 @@ var canonicalFields = {
115
118
  foot: "foot",
116
119
  height: "height",
117
120
  hide3d: "hide_3d",
118
- hasBusiness: "has_business",
119
121
  hasParts: "has_parts",
120
122
  horse: "horse",
121
123
  houseNumber: "housenumber",
@@ -150,7 +152,6 @@ var canonicalFields = {
150
152
  ref: "ref",
151
153
  refLength: "ref_length",
152
154
  renderHeight: "render_height",
153
- renderMinZoom: "render_min_zoom",
154
155
  renderMinHeight: "render_min_height",
155
156
  service: "service",
156
157
  species: "species",
@@ -173,6 +174,7 @@ function openMapTiles(options = {}) {
173
174
  delete fields.bathymetryMinDepth;
174
175
  delete fields.bathymetrySortKey;
175
176
  }
177
+ if (options.capabilities?.canopy === false) delete layers.canopy;
176
178
  if (options.capabilities?.globalLandcover === false) delete layers.globalLandcover;
177
179
  if (options.capabilities?.tree === false) delete layers.tree;
178
180
  return {
@@ -190,14 +192,6 @@ function tileflowWorldV1Schema(options = {}) {
190
192
  ...options,
191
193
  capabilities: { ...options.capabilities, bathymetry: true, globalLandcover: true },
192
194
  fields: {
193
- circularClearanceExtraAtZoom15: "clearance_extra_px_z15",
194
- circularInnerRadiusMeters: "inner_radius_m",
195
- circularKind: "circle_kind",
196
- circularOuterRadiusMeters: "outer_radius_m",
197
- circularRadiusAtZoom15: "radius_px_z15",
198
- circularRadiusMeters: "radius_m",
199
- direction: "direction",
200
- hasParts: "has_parts",
201
195
  shieldKind: "shield_kind",
202
196
  shieldLineLengthMeters: "shield_line_length_m",
203
197
  shieldNetwork: "shield_network",
@@ -291,6 +285,7 @@ function dataIdentity(kind, schema, version, url) {
291
285
  capabilities: {
292
286
  businessCorridor: Boolean(schema.layers.businessCorridor),
293
287
  bathymetry: Boolean(schema.layers.bathymetry),
288
+ canopy: Boolean(schema.layers.canopy),
294
289
  globalLandcover: Boolean(schema.layers.globalLandcover),
295
290
  tree: Boolean(schema.layers.tree)
296
291
  }
@@ -447,7 +442,7 @@ function validateOpenMapTilesSchema(schema) {
447
442
  `Tileflow requires OpenMapTiles contract version ${openMapTilesContractVersion}.`
448
443
  );
449
444
  }
450
- const parkLayer = schema.semantics?.parkLayer ?? "mixed";
445
+ const parkLayer = schema.semantics?.parkLayer;
451
446
  if (parkLayer !== "mixed" && parkLayer !== "protected-only") {
452
447
  throw new Error("Tileflow OpenMapTiles semantics.parkLayer is invalid.");
453
448
  }
@@ -1340,7 +1335,7 @@ function applyTextStyle(layer, style) {
1340
1335
  layout["text-variable-anchor"] = [...style.variableAnchors];
1341
1336
  }
1342
1337
  if (style.justify !== void 0) layout["text-justify"] = style.justify;
1343
- if (style.allowOverlap !== void 0) layout["text-allow-overlap"] = style.allowOverlap;
1338
+ setOverlap(layout, "text", style);
1344
1339
  if (style.ignorePlacement !== void 0) layout["text-ignore-placement"] = style.ignorePlacement;
1345
1340
  if (style.keepUpright !== void 0) layout["text-keep-upright"] = style.keepUpright;
1346
1341
  if (style.optional !== void 0) layout["text-optional"] = style.optional;
@@ -1410,7 +1405,7 @@ function applyIconStyle(layer, style) {
1410
1405
  if (style.rotationAlignment !== void 0) {
1411
1406
  layout["icon-rotation-alignment"] = style.rotationAlignment;
1412
1407
  }
1413
- if (style.allowOverlap !== void 0) layout["icon-allow-overlap"] = style.allowOverlap;
1408
+ setOverlap(layout, "icon", style);
1414
1409
  if (style.ignorePlacement !== void 0) layout["icon-ignore-placement"] = style.ignorePlacement;
1415
1410
  if (style.keepUpright !== void 0) layout["icon-keep-upright"] = style.keepUpright;
1416
1411
  if (style.optional !== void 0) layout["icon-optional"] = style.optional;
@@ -1528,6 +1523,18 @@ function withoutRange(value) {
1528
1523
  const { maxZoom: _maxZoom, minZoom: _minZoom, visible: _visible, ...rest } = value;
1529
1524
  return rest;
1530
1525
  }
1526
+ function setOverlap(layout, part, style) {
1527
+ if (style.overlap !== void 0) layout[`${part}-overlap`] = style.overlap;
1528
+ else if (style.allowOverlap !== void 0) layout[`${part}-allow-overlap`] = style.allowOverlap;
1529
+ if (layout[`${part}-overlap`] !== void 0) delete layout[`${part}-allow-overlap`];
1530
+ }
1531
+ function withoutGovernedAllowOverlap(layout) {
1532
+ const result = { ...layout };
1533
+ for (const part of ["icon", "text"]) {
1534
+ if (result[`${part}-overlap`] !== void 0) delete result[`${part}-allow-overlap`];
1535
+ }
1536
+ return result;
1537
+ }
1531
1538
  function setPaint(paint, property, value) {
1532
1539
  if (value !== void 0) paint[property] = toMapLibreStyleValue(value);
1533
1540
  }
@@ -2061,15 +2068,6 @@ function buildingColorExpression(context) {
2061
2068
  colors.destination,
2062
2069
  ["==", ["coalesce", ["get", fields.buildingTone], ""], "commercial"],
2063
2070
  colors.commercial,
2064
- // Defensive compatibility for immutable V8.9 archives. New candidates emit building_tone.
2065
- [
2066
- "any",
2067
- ["==", ["get", fields.hasBusiness], true],
2068
- ["==", ["get", fields.hasBusiness], 1],
2069
- ["==", ["get", fields.hasBusiness], "1"],
2070
- ["==", ["coalesce", ["get", fields.buildingKind], "generic"], "commercial"]
2071
- ],
2072
- colors.commercial,
2073
2071
  colors.generic
2074
2072
  ];
2075
2073
  }
@@ -4144,7 +4142,7 @@ function createPoiLayer(options) {
4144
4142
  ["==", ["get", options.categoryField], options.category],
4145
4143
  ["has", options.filterRankField],
4146
4144
  [">=", filterRank, 0],
4147
- ["<=", filterRank, options.density],
4145
+ ["<=", filterRank, poiRankThreshold(options.density)],
4148
4146
  ["has", options.sizeRankField],
4149
4147
  [">=", sizeRank, 0],
4150
4148
  ["<=", sizeRank, 16]
@@ -4152,6 +4150,9 @@ function createPoiLayer(options) {
4152
4150
  layout: { "symbol-sort-key": poiSortKey(options.filterRankField, options.sizeRankField) }
4153
4151
  };
4154
4152
  }
4153
+ function poiRankThreshold(density) {
4154
+ return ["+", ["step", ["zoom"], 0, 16, 1, 17, 2], density];
4155
+ }
4155
4156
  function numericPoiField(field2, fallback) {
4156
4157
  return ["to-number", ["get", field2], fallback];
4157
4158
  }
@@ -4210,6 +4211,38 @@ function isTileflowThemeName(value) {
4210
4211
  return isTileflowPortableId(value) && value !== "system";
4211
4212
  }
4212
4213
 
4214
+ // src/sun.ts
4215
+ var tileflowSunMetadataKey = "tileflow:sun";
4216
+ var tileflowDefaultSunPreset = Object.freeze({
4217
+ azimuth: 210,
4218
+ altitude: 60,
4219
+ color: "#ffffff",
4220
+ intensity: 0.5,
4221
+ shadowIntensity: 1,
4222
+ ambient: Object.freeze({ color: "#ffffff", intensity: 0.5 })
4223
+ });
4224
+ var degrees = Math.PI / 180;
4225
+ function normalizeTileflowAzimuth(azimuth) {
4226
+ const turned = azimuth % 360;
4227
+ return turned < 0 ? turned + 360 : turned === 0 ? 0 : turned;
4228
+ }
4229
+ function tileflowSunMetadata(preset) {
4230
+ return Object.freeze({ version: 1, ...preset });
4231
+ }
4232
+ function tileflowSunLight(direction, base) {
4233
+ const radial = base?.position?.[0] ?? 1.15;
4234
+ return {
4235
+ ...base ?? {},
4236
+ anchor: "map",
4237
+ position: [
4238
+ radial,
4239
+ Number(normalizeTileflowAzimuth(direction.azimuth).toFixed(3)),
4240
+ // MapLibre lights the extrusions from above the horizon only.
4241
+ Number(Math.min(90, Math.max(0, 90 - direction.altitude)).toFixed(3))
4242
+ ]
4243
+ };
4244
+ }
4245
+
4213
4246
  // src/themes/visual-semantics.ts
4214
4247
  var visualNumberProperties = /* @__PURE__ */ new Set([
4215
4248
  "angle",
@@ -4368,7 +4401,9 @@ var themeTypographyKeys = [
4368
4401
  "water"
4369
4402
  ];
4370
4403
  var themeTypographyDomains = ["places", "poi", "roads", "water"];
4371
- var themeLightingKeys = ["anchor", "color", "intensity", "position"];
4404
+ var themeLightingKeys = ["ambient", "anchor", "color", "intensity", "position", "sun"];
4405
+ var themeSunKeys = ["altitude", "azimuth", "color", "intensity", "shadowIntensity"];
4406
+ var themeAmbientKeys = ["color", "intensity"];
4372
4407
  function resolveTileflowTheme(theme) {
4373
4408
  assertThemeDocument(theme);
4374
4409
  const resolver = new ThemeValueResolver(theme);
@@ -4379,11 +4414,12 @@ function resolveTileflowTheme(theme) {
4379
4414
  number: resolver.resolveCategory("number")
4380
4415
  };
4381
4416
  const typography = resolver.resolveTypography(theme.typography);
4382
- const lighting = resolver.resolveLighting(theme.lighting);
4417
+ const { light: lighting, sun } = resolver.resolveLighting(theme.lighting);
4383
4418
  return {
4384
4419
  colorScheme: theme.colorScheme,
4385
4420
  id: theme.id,
4386
4421
  lighting,
4422
+ ...sun ? { sun } : {},
4387
4423
  tokens,
4388
4424
  typography,
4389
4425
  version: theme.version
@@ -4901,6 +4937,65 @@ var ThemeValueResolver = class {
4901
4937
  };
4902
4938
  }
4903
4939
  resolveLighting(lighting) {
4940
+ const { ambient, sun, ...flat } = lighting;
4941
+ const light = this.#resolveFlatLight(flat);
4942
+ if (sun === void 0 && ambient === void 0) return { light };
4943
+ const resolvedSun = this.resolveTree(sun ?? {}, "theme.lighting.sun");
4944
+ const resolvedAmbient = this.resolveTree(ambient ?? {}, "theme.lighting.ambient");
4945
+ const defaults4 = tileflowDefaultSunPreset;
4946
+ const number = (value, path, fallback, range2) => {
4947
+ if (value === void 0) return fallback;
4948
+ assertFiniteNumber(value, path);
4949
+ if (range2 && (value < range2[0] || value > range2[1])) {
4950
+ throw new Error(`Tileflow ${path} must be between ${range2[0]} and ${range2[1]}.`);
4951
+ }
4952
+ return value;
4953
+ };
4954
+ const colour = (value, path, fallback) => {
4955
+ if (value === void 0) return fallback;
4956
+ assertColor(value, path);
4957
+ return value;
4958
+ };
4959
+ const preset = {
4960
+ azimuth: normalizeTileflowAzimuth(
4961
+ number(resolvedSun.azimuth, "theme.lighting.sun.azimuth", defaults4.azimuth)
4962
+ ),
4963
+ altitude: number(
4964
+ resolvedSun.altitude,
4965
+ "theme.lighting.sun.altitude",
4966
+ defaults4.altitude,
4967
+ [-90, 90]
4968
+ ),
4969
+ color: colour(resolvedSun.color, "theme.lighting.sun.color", defaults4.color),
4970
+ intensity: number(
4971
+ resolvedSun.intensity,
4972
+ "theme.lighting.sun.intensity",
4973
+ defaults4.intensity,
4974
+ [0, 1]
4975
+ ),
4976
+ shadowIntensity: number(
4977
+ resolvedSun.shadowIntensity,
4978
+ "theme.lighting.sun.shadowIntensity",
4979
+ defaults4.shadowIntensity,
4980
+ [0, 1]
4981
+ ),
4982
+ ambient: {
4983
+ color: colour(
4984
+ resolvedAmbient.color,
4985
+ "theme.lighting.ambient.color",
4986
+ defaults4.ambient.color
4987
+ ),
4988
+ intensity: number(
4989
+ resolvedAmbient.intensity,
4990
+ "theme.lighting.ambient.intensity",
4991
+ defaults4.ambient.intensity,
4992
+ [0, 1]
4993
+ )
4994
+ }
4995
+ };
4996
+ return { light: sun === void 0 ? light : tileflowSunLight(preset, light), sun: preset };
4997
+ }
4998
+ #resolveFlatLight(lighting) {
4904
4999
  const result = this.resolveTree(lighting, "theme.lighting");
4905
5000
  if (result.anchor !== void 0 && result.anchor !== "map" && result.anchor !== "viewport") {
4906
5001
  throw themeTypeError("theme.lighting.anchor", "map or viewport", result.anchor);
@@ -5077,6 +5172,24 @@ function assertThemeTypographyStyleFields(style, path) {
5077
5172
  function assertThemeLighting(value, path) {
5078
5173
  const lighting = assertPlainThemeRecord(value, path);
5079
5174
  assertExactThemeKeys(lighting, path, themeLightingKeys);
5175
+ if (Object.hasOwn(lighting, "sun")) {
5176
+ const sun = assertPlainThemeRecord(lighting.sun, `${path}.sun`);
5177
+ assertExactThemeKeys(sun, `${path}.sun`, themeSunKeys);
5178
+ for (const key of ["altitude", "azimuth", "intensity", "shadowIntensity"]) {
5179
+ if (Object.hasOwn(sun, key))
5180
+ assertThemeCategoryValue(sun[key], "number", `${path}.sun.${key}`);
5181
+ }
5182
+ if (Object.hasOwn(sun, "color"))
5183
+ assertThemeCategoryValue(sun.color, "color", `${path}.sun.color`);
5184
+ }
5185
+ if (Object.hasOwn(lighting, "ambient")) {
5186
+ const ambient = assertPlainThemeRecord(lighting.ambient, `${path}.ambient`);
5187
+ assertExactThemeKeys(ambient, `${path}.ambient`, themeAmbientKeys);
5188
+ if (Object.hasOwn(ambient, "intensity"))
5189
+ assertThemeCategoryValue(ambient.intensity, "number", `${path}.ambient.intensity`);
5190
+ if (Object.hasOwn(ambient, "color"))
5191
+ assertThemeCategoryValue(ambient.color, "color", `${path}.ambient.color`);
5192
+ }
5080
5193
  if (Object.hasOwn(lighting, "anchor") && lighting.anchor !== "map" && lighting.anchor !== "viewport") {
5081
5194
  throw themeTypeError(`${path}.anchor`, "map or viewport", lighting.anchor);
5082
5195
  }
@@ -5243,6 +5356,7 @@ function inferThemeCategoryFromPath(path) {
5243
5356
  const tokenCategory = path.match(/^theme\.tokens\.(color|font|image|number)(?:\.|$)/u)?.[1];
5244
5357
  if (isThemeTokenCategory(tokenCategory)) return tokenCategory;
5245
5358
  if (/^theme\.lighting\.position(?:\[\d+\])?$/u.test(path)) return "number";
5359
+ if (/^theme\.lighting\.sun\.(?:altitude|azimuth|shadowIntensity)$/u.test(path)) return "number";
5246
5360
  const segments = path.replace(/\[\d+\]/g, "").split(".");
5247
5361
  return [...segments].reverse().map((segment) => classifyTileflowVisualProperty(segment)).find((category) => category !== void 0);
5248
5362
  }
@@ -6216,7 +6330,7 @@ function compileRoads(request, context) {
6216
6330
  });
6217
6331
  }
6218
6332
  }
6219
- compileSidewalks(request?.sidewalks, context, contributions);
6333
+ compileSidewalks(request?.sidewalks, areaStyles.pedestrian?.fill?.color, context, contributions);
6220
6334
  compileRoundabouts(request?.roundabouts, context, contributions);
6221
6335
  compileCrossings(request?.crossings, context, contributions);
6222
6336
  if (semantics.oneWayMarkers && visible.size > 0) {
@@ -6285,15 +6399,15 @@ function roadPhysicalCohort(roadClass) {
6285
6399
  if (roadClass === "minor" || roadClass === "service" || roadClass === "track") return "local";
6286
6400
  return "path";
6287
6401
  }
6288
- function compileSidewalks(request, context, contributions) {
6402
+ function compileSidewalks(request, pedestrianAreaColor, context, contributions) {
6289
6403
  const { fields, layers } = context.data.schema;
6290
6404
  if (!layers.sidewalk) return;
6291
6405
  const config = mergeTileflowDesign(
6292
6406
  {
6293
6407
  surface: {
6294
- color: context.colors.roads.casing,
6408
+ color: pedestrianAreaColor ?? context.colors.road,
6295
6409
  minZoom: 17,
6296
- opacity: 0.96
6410
+ opacity: 1
6297
6411
  }
6298
6412
  },
6299
6413
  request
@@ -6344,9 +6458,7 @@ function compileSidewalks(request, context, contributions) {
6344
6458
  }
6345
6459
  function compileRoundabouts(request, context, contributions) {
6346
6460
  const { fields, layers } = context.data.schema;
6347
- if (!layers.circularFeature || !fields.circularKind || !fields.circularRadiusAtZoom15 || !fields.circularRadiusMeters || !fields.circularOuterRadiusMeters || !fields.circularInnerRadiusMeters) {
6348
- return;
6349
- }
6461
+ if (!layers.circularFeature) return;
6350
6462
  const circularFields = {
6351
6463
  circularInnerRadiusMeters: fields.circularInnerRadiusMeters,
6352
6464
  circularOuterRadiusMeters: fields.circularOuterRadiusMeters,
@@ -6420,7 +6532,7 @@ function compileRoundabouts(request, context, contributions) {
6420
6532
  }
6421
6533
  function compileCrossings(request, context, contributions) {
6422
6534
  const { fields, layers } = context.data.schema;
6423
- if (!request || !layers.streetFurniture || !fields.direction) return;
6535
+ if (!request || !layers.streetFurniture) return;
6424
6536
  const config = mergeTileflowDesign(
6425
6537
  {
6426
6538
  allowOverlap: true,
@@ -6486,7 +6598,7 @@ function circularRoadInnerRadius(fields, casing) {
6486
6598
  ["/", values.innerRadiusMeters, values.radiusMeters]
6487
6599
  ];
6488
6600
  const baseWidth = circularRoadBaseWidth(fields.class);
6489
- const fallbackWidth = level === 15 ? baseWidth : ["*", baseWidth, circularRoadLegacyScale(level)];
6601
+ const fallbackWidth = level === 15 ? baseWidth : ["*", baseWidth, circularRoadFallbackScale(level)];
6490
6602
  const fallbackInnerRadius = ["-", centerlineRadius, ["/", fallbackWidth, 2]];
6491
6603
  const innerRadius = ["case", values.hasPhysicalRadii, physicalInnerRadius, fallbackInnerRadius];
6492
6604
  stops.push(level, [
@@ -6508,7 +6620,7 @@ function circularRoadStrokeWidth(fields, casing) {
6508
6620
  centerlineRadius,
6509
6621
  ["/", ["-", values.outerRadiusMeters, values.innerRadiusMeters], values.radiusMeters]
6510
6622
  ];
6511
- const fallbackWidth = level === 15 ? baseWidth : ["*", baseWidth, circularRoadLegacyScale(level)];
6623
+ const fallbackWidth = level === 15 ? baseWidth : ["*", baseWidth, circularRoadFallbackScale(level)];
6512
6624
  stops.push(level, [
6513
6625
  "case",
6514
6626
  values.hasPhysicalRadii,
@@ -6557,7 +6669,7 @@ function circularRoadValues(fields) {
6557
6669
  radiusMeters
6558
6670
  };
6559
6671
  }
6560
- function circularRoadLegacyScale(level) {
6672
+ function circularRoadFallbackScale(level) {
6561
6673
  const interpolationBase = 1.35;
6562
6674
  const progress = (interpolationBase ** (level - 15) - 1) / (interpolationBase ** (22 - 15) - 1);
6563
6675
  return 1 + progress * 1.2;
@@ -6947,10 +7059,10 @@ function roadWidth(roadClass, scale) {
6947
7059
  [16, 3.4]
6948
7060
  ]
6949
7061
  };
6950
- return zoom.linear(stops[roadClass].map(([z15, value]) => [z15, value * scale]));
7062
+ return zoom.linear(stops[roadClass].map(([z16, value]) => [z16, value * scale]));
6951
7063
  }
6952
7064
  function widen(value, addition) {
6953
- return zoom.linear(value.stops.map(([z15, width]) => [z15, width + addition]));
7065
+ return zoom.linear(value.stops.map(([z16, width]) => [z16, width + addition]));
6954
7066
  }
6955
7067
  function structureFilter(field2, structure) {
6956
7068
  if (structure === "surface") {
@@ -7162,7 +7274,7 @@ function cloneJson16(value) {
7162
7274
 
7163
7275
  // src/modules/vegetation/compiler.ts
7164
7276
  function compileVegetation(request, context) {
7165
- const minZoom = request?.flat?.minZoom ?? request?.minZoom ?? 16;
7277
+ const minZoom = request?.flat?.minZoom ?? 16;
7166
7278
  const mode = request?.mode ?? "3d";
7167
7279
  const config = mergeTileflowDesign(
7168
7280
  {
@@ -7182,7 +7294,6 @@ function compileVegetation(request, context) {
7182
7294
  strokeOpacity: 0.55,
7183
7295
  strokeWidth: 0.8
7184
7296
  },
7185
- minZoom,
7186
7297
  mode,
7187
7298
  threeDimensional: {
7188
7299
  barkColor: context.colors.vegetation.tree.bark,
@@ -8464,24 +8575,39 @@ function validateStops(stops, api) {
8464
8575
  }
8465
8576
  }
8466
8577
  function validateOperandCount(count, api) {
8467
- if (count > tileflowDataExpressionLimits.maxOperands) {
8468
- throw new Error(
8469
- `${api} supports at most ${tileflowDataExpressionLimits.maxOperands} operands.`
8578
+ const limit = tileflowDataExpressionLimits.maxOperands;
8579
+ if (count > limit) {
8580
+ throw limitError(
8581
+ `${api} received ${count} operands; it supports at most ${limit}. Nest ${api} calls of at most ${limit} operands each.`
8470
8582
  );
8471
8583
  }
8472
8584
  }
8473
8585
  function validateBranchCount(count, api) {
8474
- if (count > tileflowDataExpressionLimits.maxBranches) {
8475
- throw new Error(
8476
- `${api} supports at most ${tileflowDataExpressionLimits.maxBranches} branches.`
8586
+ const limit = tileflowDataExpressionLimits.maxBranches;
8587
+ if (count > limit) {
8588
+ throw limitError(
8589
+ `${api} received ${count} branches; it supports at most ${limit}. Split the lookup into nested ${api} groups of at most ${limit} branches, or reduce the mapping.`
8477
8590
  );
8478
8591
  }
8479
8592
  }
8480
8593
  function validateStopCount(count, api) {
8481
- if (count > tileflowDataExpressionLimits.maxStops) {
8482
- throw new Error(`${api} supports at most ${tileflowDataExpressionLimits.maxStops} stops.`);
8594
+ const limit = tileflowDataExpressionLimits.maxStops;
8595
+ if (count > limit) {
8596
+ throw limitError(
8597
+ `${api} received ${count} stops; it supports at most ${limit}. Remove stops the curve can interpolate.`
8598
+ );
8483
8599
  }
8484
8600
  }
8601
+ function limitError(message) {
8602
+ const site = authoringCallSite(new Error().stack);
8603
+ return new Error(site ? `${message} Called at ${site}.` : message);
8604
+ }
8605
+ function authoringCallSite(stack) {
8606
+ const locations = (stack ?? "").split("\n").slice(1).map((line) => /\(?([^()\s]+?):(\d+):(\d+)\)?$/.exec(line.trim())).filter((match2) => match2 !== null).map(([, file, line, column]) => ({ file: file.replace(/^file:\/\//, ""), line, column }));
8607
+ const own = locations[0]?.file;
8608
+ const site = locations.find(({ file }) => file !== own && !file.startsWith("node:"));
8609
+ return site ? `${site.file}:${site.line}:${site.column}` : void 0;
8610
+ }
8485
8611
  function node(value) {
8486
8612
  if (isTileflowExpression(value)) return cloneJson19(value.value);
8487
8613
  if (isDataFieldReference(value)) return ["get", cloneJson19(value)];
@@ -8675,7 +8801,11 @@ function visitExpression(value, path, variables, issues) {
8675
8801
  return;
8676
8802
  }
8677
8803
  if ((value.length - 2) / 2 > branchLimit) {
8678
- addExpressionIssue(issues, path, `Expression case exceeds ${branchLimit} branches.`);
8804
+ addExpressionIssue(
8805
+ issues,
8806
+ path,
8807
+ `Expression case has ${(value.length - 2) / 2} branches and exceeds ${branchLimit}.`
8808
+ );
8679
8809
  return;
8680
8810
  }
8681
8811
  for (let index = 1; index < value.length; index += 1) visit(value[index], index);
@@ -8691,7 +8821,11 @@ function visitExpression(value, path, variables, issues) {
8691
8821
  return;
8692
8822
  }
8693
8823
  if ((value.length - 3) / 2 > branchLimit) {
8694
- addExpressionIssue(issues, path, `Expression match exceeds ${branchLimit} branches.`);
8824
+ addExpressionIssue(
8825
+ issues,
8826
+ path,
8827
+ `Expression match has ${(value.length - 3) / 2} branches and exceeds ${branchLimit}.`
8828
+ );
8695
8829
  return;
8696
8830
  }
8697
8831
  visit(value[1], 1);
@@ -9943,10 +10077,10 @@ function applyRefinement(previous, refinement) {
9943
10077
  style: {
9944
10078
  ...previous.style,
9945
10079
  ...patch.style?.placement === void 0 ? {} : {
9946
- placement: {
10080
+ placement: withoutGovernedAllowOverlap({
9947
10081
  ...previous.style.placement ?? {},
9948
10082
  ...patch.style.placement
9949
- }
10083
+ })
9950
10084
  },
9951
10085
  ...patch.style?.appearance === void 0 ? {} : {
9952
10086
  appearance: {
@@ -10618,7 +10752,7 @@ function validateExpressionThemeValues(value, expectedCategory, context, path) {
10618
10752
  if (!parsed.success || parsed.data.category !== expectedCategory) {
10619
10753
  context.addIssue({
10620
10754
  code: "custom",
10621
- message: expectedCategory === void 0 ? "Structural expressions cannot contain theme-token references" : `Expected a ${expectedCategory} theme-token reference`,
10755
+ message: expectedCategory === void 0 ? `Structural expressions cannot contain theme-token references. ${structuralThemeValueHint}` : parsed.success ? `Expected a ${expectedCategory} theme-token reference, received ${parsed.data.category} token "${parsed.data.token}". ${expressionOperandHint(expectedCategory)}` : `Expected a ${expectedCategory} theme-token reference`,
10622
10756
  path
10623
10757
  });
10624
10758
  }
@@ -10627,9 +10761,11 @@ function validateExpressionThemeValues(value, expectedCategory, context, path) {
10627
10761
  if (candidate.kind === "theme-fixed") {
10628
10762
  const parsed = expectedCategory === "number" ? z5.union([fixedNumberSchema, fixedNumberArraySchema]).safeParse(candidate) : expectedCategory === "color" || expectedCategory === "image" ? fixedStringSchema.safeParse(candidate) : void 0;
10629
10763
  if (!parsed?.success) {
10764
+ const received = fixedValueKind(candidate.value);
10765
+ const literal2 = JSON.stringify(candidate.value);
10630
10766
  context.addIssue({
10631
10767
  code: "custom",
10632
- message: expectedCategory === void 0 ? "Structural expressions cannot contain theme-fixed values" : `Expected a fixed ${expectedCategory} value`,
10768
+ message: expectedCategory === void 0 ? `Structural expressions cannot contain theme-fixed values. ${structuralThemeValueHint}` : received !== void 0 && received !== expectedCategoryKind(expectedCategory) ? `Expected a fixed ${expectedCategory} value, received a fixed ${received}. ${expressionOperandHint(expectedCategory)} Write ${literal2} instead of fixed(${literal2}).` : `Expected a fixed ${expectedCategory} value`,
10633
10769
  path
10634
10770
  });
10635
10771
  }
@@ -10649,6 +10785,20 @@ function validateExpressionThemeValues(value, expectedCategory, context, path) {
10649
10785
  validateExpressionThemeValues(entry, expectedCategory, context, [...path, key]);
10650
10786
  }
10651
10787
  }
10788
+ var structuralThemeValueHint = "fixed() and theme tokens apply only to visual properties such as colours, sizes, and images; write structural values directly.";
10789
+ function expressionOperandHint(category) {
10790
+ return `In ${category === "image" ? "an" : "a"} ${category} expression, fixed() and theme tokens belong in its ${category} results; conditions and other operands take plain values.`;
10791
+ }
10792
+ function fixedValueKind(value) {
10793
+ if (typeof value === "number") return "number";
10794
+ if (typeof value === "string") return "string";
10795
+ if (typeof value === "boolean") return "boolean";
10796
+ if (Array.isArray(value)) return "array";
10797
+ return void 0;
10798
+ }
10799
+ function expectedCategoryKind(category) {
10800
+ return category === "number" ? "number" : "string";
10801
+ }
10652
10802
  var numberExpressionSchema = expressionSchemaFor("number");
10653
10803
  var colorExpressionSchema = expressionSchemaFor("color");
10654
10804
  var imageExpressionSchema = expressionSchemaFor("image");
@@ -10761,10 +10911,21 @@ var themeTypographySchema = themeTypographyStyleSchema.extend({
10761
10911
  water: themeTypographyStyleSchema.optional()
10762
10912
  });
10763
10913
  var themeLightingSchema = z5.object({
10914
+ ambient: z5.object({
10915
+ color: themeColorValueSchema.optional(),
10916
+ intensity: themeNumberValueSchema.optional()
10917
+ }).strict().optional(),
10764
10918
  anchor: z5.enum(["map", "viewport"]).optional(),
10765
10919
  color: themeColorValueSchema.optional(),
10766
10920
  intensity: themeNumberValueSchema.optional(),
10767
- position: z5.tuple([themeNumberValueSchema, themeNumberValueSchema, themeNumberValueSchema]).optional()
10921
+ position: z5.tuple([themeNumberValueSchema, themeNumberValueSchema, themeNumberValueSchema]).optional(),
10922
+ sun: z5.object({
10923
+ altitude: themeNumberValueSchema.optional(),
10924
+ azimuth: themeNumberValueSchema.optional(),
10925
+ color: themeColorValueSchema.optional(),
10926
+ intensity: themeNumberValueSchema.optional(),
10927
+ shadowIntensity: themeNumberValueSchema.optional()
10928
+ }).strict().optional()
10768
10929
  }).strict();
10769
10930
  var tileflowThemeSchema = z5.object({
10770
10931
  colorScheme: z5.enum(["light", "dark"]),
@@ -10852,6 +11013,7 @@ var textStyleSchema = z5.object({
10852
11013
  offset: numberOffsetValueSchema.optional(),
10853
11014
  optional: z5.boolean().optional(),
10854
11015
  opacity: numberValueSchema.optional(),
11016
+ overlap: z5.enum(["always", "cooperative", "never"]).optional(),
10855
11017
  padding: numberValueSchema.optional(),
10856
11018
  pitchAlignment: z5.enum(["auto", "map", "viewport"]).optional(),
10857
11019
  radialOffset: numberValueSchema.optional(),
@@ -10897,6 +11059,7 @@ var iconStyleSchema = z5.object({
10897
11059
  opacity: numberValueSchema.optional(),
10898
11060
  keepUpright: z5.boolean().optional(),
10899
11061
  optional: z5.boolean().optional(),
11062
+ overlap: z5.enum(["always", "cooperative", "never"]).optional(),
10900
11063
  padding: numberValueSchema.optional(),
10901
11064
  pitchAlignment: z5.enum(["auto", "map", "viewport"]).optional(),
10902
11065
  rotate: numberValueSchema.optional(),
@@ -11469,7 +11632,6 @@ var modulesSchema = z5.object({
11469
11632
  type: z5.literal("vegetation"),
11470
11633
  enabled: z5.boolean().optional(),
11471
11634
  flat: circleStyleSchema.optional(),
11472
- minZoom: zoomNumberSchema.optional(),
11473
11635
  mode: z5.enum(["3d", "flat"]).optional(),
11474
11636
  renderStack: renderStackSchema.optional(),
11475
11637
  threeDimensional: z5.object({
@@ -11505,6 +11667,7 @@ var layerBindingsSchema = z5.object({
11505
11667
  building: z5.string().trim().min(1),
11506
11668
  bathymetry: z5.string().trim().min(1).optional(),
11507
11669
  businessCorridor: z5.string().trim().min(1).optional(),
11670
+ canopy: z5.string().trim().min(1).optional(),
11508
11671
  circularFeature: z5.string().trim().min(1).optional(),
11509
11672
  globalLandcover: z5.string().trim().min(1).optional(),
11510
11673
  houseNumber: z5.string().trim().min(1),
@@ -11532,34 +11695,34 @@ var fieldBindingsSchema = z5.object({
11532
11695
  bathymetryMinDepth: z5.string().trim().min(1).optional(),
11533
11696
  bathymetrySortKey: z5.string().trim().min(1).optional(),
11534
11697
  brunnel: z5.string().trim().min(1),
11535
- buildingKind: z5.string().trim().min(1),
11536
11698
  buildingTone: z5.string().trim().min(1),
11699
+ canopyClearanceAtZoom15: z5.string().trim().min(1),
11700
+ canopyCoverPercent: z5.string().trim().min(1),
11701
+ canopyRank: z5.string().trim().min(1),
11537
11702
  capital: z5.string().trim().min(1),
11538
- circularClearanceExtraAtZoom15: z5.string().trim().min(1).optional(),
11539
- circularKind: z5.string().trim().min(1).optional(),
11540
- circularRadiusAtZoom15: z5.string().trim().min(1).optional(),
11541
- circularRadiusMeters: z5.string().trim().min(1).optional(),
11542
- circularInnerRadiusMeters: z5.string().trim().min(1).optional(),
11543
- circularOuterRadiusMeters: z5.string().trim().min(1).optional(),
11703
+ circularClearanceExtraAtZoom15: z5.string().trim().min(1),
11704
+ circularKind: z5.string().trim().min(1),
11705
+ circularRadiusAtZoom15: z5.string().trim().min(1),
11706
+ circularRadiusMeters: z5.string().trim().min(1),
11707
+ circularInnerRadiusMeters: z5.string().trim().min(1),
11708
+ circularOuterRadiusMeters: z5.string().trim().min(1),
11544
11709
  class: z5.string().trim().min(1),
11545
11710
  crossing: z5.string().trim().min(1),
11546
- classificationConfidence: z5.string().trim().min(1),
11547
11711
  confidence: z5.string().trim().min(1),
11548
11712
  circumference: z5.string().trim().min(1),
11549
11713
  diameterCrown: z5.string().trim().min(1),
11550
11714
  disputed: z5.string().trim().min(1),
11551
- direction: z5.string().trim().min(1).optional(),
11715
+ direction: z5.string().trim().min(1),
11552
11716
  elevation: z5.string().trim().min(1),
11553
11717
  elevationFeet: z5.string().trim().min(1),
11554
11718
  expressway: z5.string().trim().min(1),
11555
11719
  foot: z5.string().trim().min(1),
11556
11720
  height: z5.string().trim().min(1),
11557
- hasBusiness: z5.string().trim().min(1),
11558
- hasParts: z5.string().trim().min(1).optional(),
11721
+ hasParts: z5.string().trim().min(1),
11559
11722
  hide3d: z5.string().trim().min(1),
11560
11723
  horse: z5.string().trim().min(1),
11561
11724
  houseNumber: z5.string().trim().min(1),
11562
- importanceTier: z5.string().trim().min(1).optional(),
11725
+ importanceTier: z5.string().trim().min(1),
11563
11726
  iata: z5.string().trim().min(1),
11564
11727
  icao: z5.string().trim().min(1),
11565
11728
  indoor: z5.string().trim().min(1),
@@ -11590,7 +11753,6 @@ var fieldBindingsSchema = z5.object({
11590
11753
  ref: z5.string().trim().min(1),
11591
11754
  refLength: z5.string().trim().min(1),
11592
11755
  renderHeight: z5.string().trim().min(1),
11593
- renderMinZoom: z5.string().trim().min(1),
11594
11756
  renderMinHeight: z5.string().trim().min(1),
11595
11757
  shieldKind: z5.string().trim().min(1).optional(),
11596
11758
  shieldLineLengthMeters: z5.string().trim().min(1).optional(),
@@ -11616,6 +11778,7 @@ var openMapTilesSchema = z5.object({
11616
11778
  capabilities: schema.layers === void 0 ? void 0 : {
11617
11779
  businessCorridor: schema.layers.businessCorridor !== void 0,
11618
11780
  bathymetry: schema.layers.bathymetry !== void 0,
11781
+ canopy: schema.layers.canopy !== void 0,
11619
11782
  globalLandcover: schema.layers.globalLandcover !== void 0,
11620
11783
  tree: schema.layers.tree !== void 0
11621
11784
  },
@@ -12117,9 +12280,43 @@ function parseOrThrow(schema, input, name) {
12117
12280
  if (result.success) return result.data;
12118
12281
  throw new TileflowResolvedMapValidationError(
12119
12282
  name,
12120
- validationMessagesFromIssues(result.error.issues)
12283
+ validationMessagesFromIssues(expandUnionIssues(result.error.issues, [], input))
12121
12284
  );
12122
12285
  }
12286
+ function expandUnionIssues(issues, base, input) {
12287
+ return issues.flatMap((issue) => {
12288
+ const path = [...base, ...issue.path];
12289
+ if (issue.code !== "invalid_union" || issue.errors.length === 0) return [{ ...issue, path }];
12290
+ const candidates = issue.errors.filter((branch) => !branch.some(isUnionBranchMismatch));
12291
+ if (candidates.length === 0) {
12292
+ return [{ ...issue, path, message: unmatchedUnionMessage(valueAtPath(input, path)) }];
12293
+ }
12294
+ const fewest = Math.min(...candidates.map((branch) => branch.length));
12295
+ const best = candidates.filter((branch) => branch.length === fewest);
12296
+ return best.length === 1 ? expandUnionIssues(best[0], path, input) : [{ ...issue, path }];
12297
+ });
12298
+ }
12299
+ function isUnionBranchMismatch(issue) {
12300
+ if (issue.path.length === 0)
12301
+ return issue.code === "invalid_type" || issue.code === "invalid_value";
12302
+ return issue.path.length === 1 && (issue.code === "invalid_value" || issue.code === "invalid_union" && issue.errors.length === 0);
12303
+ }
12304
+ function unmatchedUnionMessage(value) {
12305
+ const kind = value !== null && typeof value === "object" ? value.kind : void 0;
12306
+ if (kind === "theme-fixed" || kind === "theme-token") {
12307
+ return `This property is structural. ${structuralThemeValueHint}`;
12308
+ }
12309
+ const received = value === null ? "null" : Array.isArray(value) ? "an array" : typeof value;
12310
+ return `Invalid input: no accepted form takes ${received === "object" ? "this object" : received}`;
12311
+ }
12312
+ function valueAtPath(input, path) {
12313
+ let value = input;
12314
+ for (const key of path) {
12315
+ if (value === null || typeof value !== "object" || !Object.hasOwn(value, key)) return void 0;
12316
+ value = value[key];
12317
+ }
12318
+ return value;
12319
+ }
12123
12320
  function validationMessagesFromIssues(issues) {
12124
12321
  return issues.flatMap((issue) => {
12125
12322
  const basePath = issue.path.join(".");
@@ -12183,6 +12380,71 @@ function findUnsafeObjectStructure(input) {
12183
12380
  return void 0;
12184
12381
  }
12185
12382
 
12383
+ // src/route-inks.ts
12384
+ var tileflowRouteMetadataKey = "tileflow:route";
12385
+ var tileflowRouteTokens = Object.freeze({
12386
+ color: Object.freeze({
12387
+ line: "route.line",
12388
+ casing: "route.casing",
12389
+ core: "route.core",
12390
+ glow: "route.glow",
12391
+ alternative: "route.alternative"
12392
+ }),
12393
+ number: Object.freeze({
12394
+ glowOpacity: "route.glow.opacity",
12395
+ coreOpacity: "route.core.opacity"
12396
+ })
12397
+ });
12398
+ var tileflowDefaultRouteInks = Object.freeze({
12399
+ light: Object.freeze({
12400
+ line: "#2563eb",
12401
+ casing: "#1e3a8a",
12402
+ core: "#dbeafe",
12403
+ glow: "#2563eb",
12404
+ alternative: "#94a3b8",
12405
+ glowOpacity: 0.16,
12406
+ coreOpacity: 0
12407
+ }),
12408
+ dark: Object.freeze({
12409
+ line: "#60a5fa",
12410
+ casing: "#0b1220",
12411
+ core: "#eff6ff",
12412
+ glow: "#60a5fa",
12413
+ alternative: "#64748b",
12414
+ glowOpacity: 0.4,
12415
+ coreOpacity: 0.7
12416
+ })
12417
+ });
12418
+ var colourKeys = Object.keys(
12419
+ tileflowRouteTokens.color
12420
+ );
12421
+ var numberKeys = Object.keys(
12422
+ tileflowRouteTokens.number
12423
+ );
12424
+ function resolveTileflowRouteInks(theme) {
12425
+ const defaults4 = tileflowDefaultRouteInks[theme.colorScheme];
12426
+ const inks = { ...defaults4 };
12427
+ for (const key of colourKeys) {
12428
+ const value = theme.tokens.color[tileflowRouteTokens.color[key]];
12429
+ if (value !== void 0) inks[key] = value;
12430
+ }
12431
+ for (const key of numberKeys) {
12432
+ const token = tileflowRouteTokens.number[key];
12433
+ const value = theme.tokens.number[token];
12434
+ if (value === void 0) continue;
12435
+ if (!(value >= 0 && value <= 1)) {
12436
+ throw new Error(
12437
+ `Tileflow theme "${theme.id}" sets number token "${token}" to ${value}; route opacities run from 0 to 1.`
12438
+ );
12439
+ }
12440
+ inks[key] = value;
12441
+ }
12442
+ return Object.freeze(inks);
12443
+ }
12444
+ function tileflowRouteMetadata(inks) {
12445
+ return Object.freeze({ version: 1, ...inks });
12446
+ }
12447
+
12186
12448
  // src/terrain/index.ts
12187
12449
  function resolveTerrain(terrain, apiBaseUrl) {
12188
12450
  if (!terrain || terrain === "none") return void 0;
@@ -12851,6 +13113,47 @@ function isSafeArtifactName(value) {
12851
13113
  });
12852
13114
  }
12853
13115
 
13116
+ // src/cartography/layout-zoom-diagnostics.ts
13117
+ function diagnoseFractionalLayoutZoomSteps(layers) {
13118
+ const diagnostics = [];
13119
+ for (const layer of layers) {
13120
+ const layout = layer.layout;
13121
+ if (layout === null || typeof layout !== "object") continue;
13122
+ for (const [property, value] of Object.entries(layout)) {
13123
+ const stops = fractionalZoomSteps(value);
13124
+ if (stops.length === 0) continue;
13125
+ const [origin] = readTileflowCompilerProvenance(layer);
13126
+ const listed = stops.map((stop) => String(stop)).join(", ");
13127
+ diagnostics.push({
13128
+ code: "TF_LAYOUT_FRACTIONAL_ZOOM_STEP",
13129
+ ...origin ? { domain: origin.owner } : {},
13130
+ message: `${property} steps at zoom ${listed}; layout properties are evaluated at each tile's integer zoom, so the step takes effect at zoom ${stops.map(Math.ceil).join(", ")}.`,
13131
+ path: `layers.${String(layer.id)}.layout.${property}`,
13132
+ phase: "lowering",
13133
+ severity: "warning",
13134
+ suggestion: "Step at whole zooms, or move the change to a paint property or a zoom range.",
13135
+ ...origin ? { target: origin.target } : {}
13136
+ });
13137
+ }
13138
+ }
13139
+ return diagnostics;
13140
+ }
13141
+ function fractionalZoomSteps(value) {
13142
+ if (!Array.isArray(value)) return [];
13143
+ const stops = [];
13144
+ if (value[0] === "step" && isZoom(value[1])) {
13145
+ for (let index = 3; index < value.length; index += 2) {
13146
+ const stop = value[index];
13147
+ if (typeof stop === "number" && !Number.isInteger(stop)) stops.push(stop);
13148
+ }
13149
+ }
13150
+ for (const entry of value) stops.push(...fractionalZoomSteps(entry));
13151
+ return stops;
13152
+ }
13153
+ function isZoom(value) {
13154
+ return Array.isArray(value) && value.length === 1 && value[0] === "zoom";
13155
+ }
13156
+
12854
13157
  // src/cartography/overlay-placement-manifest.ts
12855
13158
  var tileflowOverlayPlacementManifestMetadataKey = "tileflow:overlay-placement-manifest";
12856
13159
  var tileflowOverlayPlacementManifestSchemaVersion = 1;
@@ -14055,6 +14358,8 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
14055
14358
  ...atmosphere ? { [tileflowAtmosphereMetadataKey]: { version: 1, ...atmosphere } } : {},
14056
14359
  "tileflow:theme": selected.name,
14057
14360
  "tileflow:colorScheme": resolvedTheme.colorScheme,
14361
+ [tileflowRouteMetadataKey]: tileflowRouteMetadata(resolveTileflowRouteInks(resolvedTheme)),
14362
+ ...resolvedTheme.sun ? { [tileflowSunMetadataKey]: tileflowSunMetadata(resolvedTheme.sun) } : {},
14058
14363
  "tileflow:data": data.identity,
14059
14364
  "tileflow:sources": sourceIdentities,
14060
14365
  ...Object.keys(loweredOverlays.identities).length > 0 ? { "tileflow:overlays": loweredOverlays.identities } : {},
@@ -14081,7 +14386,16 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
14081
14386
  runCompilationPhase("assets", () => assertTextAssets(style, config));
14082
14387
  runCompilationPhase("assets", () => assertGlyphFontStacks(style, config));
14083
14388
  runCompilationPhase("validation", () => assertMapLibreStyle(style, config.id));
14084
- return { diagnostics, report, style, ...inspection ? { inspection } : {} };
14389
+ const layoutDiagnostics = runCompilationPhase(
14390
+ "validation",
14391
+ () => diagnoseFractionalLayoutZoomSteps(plannedLayers)
14392
+ );
14393
+ return {
14394
+ diagnostics: [...diagnostics, ...layoutDiagnostics],
14395
+ report,
14396
+ style,
14397
+ ...inspection ? { inspection } : {}
14398
+ };
14085
14399
  }
14086
14400
  function finalizeTileflowLayers(input) {
14087
14401
  const ids = /* @__PURE__ */ new Set();
@@ -14697,6 +15011,15 @@ var tileflowCaptureSceneSchema = z8.object({
14697
15011
  viewport: tileflowCaptureViewportSchema,
14698
15012
  target: tileflowCaptureTargetSchema.optional()
14699
15013
  }).strict();
15014
+ function tileflowCaptureSceneFitIssue(scene) {
15015
+ if (scene.camera.type !== "bounds" || scene.camera.padding === void 0) return void 0;
15016
+ const room = Math.min(scene.viewport.width, scene.viewport.height);
15017
+ if (scene.camera.padding * 2 < room) return void 0;
15018
+ return {
15019
+ message: `Expected bounds padding less than half the viewport's shorter side (${room / 2} pixels)`,
15020
+ path: ["camera", "padding"]
15021
+ };
15022
+ }
14700
15023
  function isPortableApplicationPath(value) {
14701
15024
  if (value !== value.trim() || !value.startsWith("/") || value.startsWith("//") || value.includes("#") || value.includes("\\") || hasControlCharacter(value)) {
14702
15025
  return false;
@@ -14717,6 +15040,97 @@ function hasControlCharacter(value) {
14717
15040
  return false;
14718
15041
  }
14719
15042
 
15043
+ // src/capture-shot.ts
15044
+ import { z as z9 } from "zod";
15045
+ var tileflowCaptureShotLimits = Object.freeze({
15046
+ blendThemes: Object.freeze({ maximum: 8, minimum: 2 }),
15047
+ defaultFps: 30,
15048
+ durationMs: Object.freeze({ maximum: 6e4 }),
15049
+ fps: Object.freeze([24, 25, 30, 50, 60]),
15050
+ keyframes: Object.freeze({ maximum: 64, minimum: 2 }),
15051
+ maximumFrames: 3600
15052
+ });
15053
+ var tileflowCaptureShotBlendSchema = z9.object({
15054
+ themes: z9.array(tileflowThemeNameSchema).min(tileflowCaptureShotLimits.blendThemes.minimum).max(tileflowCaptureShotLimits.blendThemes.maximum),
15055
+ position: z9.number().finite().min(0)
15056
+ }).strict().superRefine((blend, context) => {
15057
+ if (new Set(blend.themes).size !== blend.themes.length) {
15058
+ context.addIssue({ code: "custom", message: "Expected distinct themes", path: ["themes"] });
15059
+ }
15060
+ if (blend.position > blend.themes.length - 1) {
15061
+ context.addIssue({
15062
+ code: "custom",
15063
+ message: `Expected a position from 0 to ${blend.themes.length - 1}`,
15064
+ path: ["position"]
15065
+ });
15066
+ }
15067
+ });
15068
+ var tileflowCaptureShotKeyframeSchema = z9.object({
15069
+ at: z9.number().int().min(0).max(tileflowCaptureShotLimits.durationMs.maximum),
15070
+ camera: tileflowCaptureCenterCameraSchema,
15071
+ blend: tileflowCaptureShotBlendSchema.optional()
15072
+ }).strict();
15073
+ var tileflowCaptureShotFields = {
15074
+ theme: tileflowThemeNameSchema,
15075
+ viewport: tileflowCaptureViewportSchema,
15076
+ fps: z9.literal(tileflowCaptureShotLimits.fps).optional(),
15077
+ keyframes: z9.array(tileflowCaptureShotKeyframeSchema).min(tileflowCaptureShotLimits.keyframes.minimum).max(tileflowCaptureShotLimits.keyframes.maximum),
15078
+ easing: z9.enum(["smooth", "linear"]).optional()
15079
+ };
15080
+ var tileflowCaptureShotSchema = z9.object({ map: tileflowPortableIdSchema, ...tileflowCaptureShotFields }).strict().superRefine(checkShotTiming);
15081
+ var tileflowMapShotSchema = z9.object(tileflowCaptureShotFields).strict().superRefine(checkShotTiming);
15082
+ function tileflowCaptureShotFrameCount(shot) {
15083
+ const fps = shot.fps ?? tileflowCaptureShotLimits.defaultFps;
15084
+ const duration = shot.keyframes.at(-1)?.at ?? 0;
15085
+ return Math.floor(duration * fps / 1e3 + 1e-9) + 1;
15086
+ }
15087
+ function checkShotTiming(shot, context) {
15088
+ const { keyframes } = shot;
15089
+ if (keyframes[0] && keyframes[0].at !== 0) {
15090
+ context.addIssue({
15091
+ code: "custom",
15092
+ message: "Expected the first keyframe at 0 milliseconds",
15093
+ path: ["keyframes", 0, "at"]
15094
+ });
15095
+ }
15096
+ for (let index = 1; index < keyframes.length; index += 1) {
15097
+ if (keyframes[index].at <= keyframes[index - 1].at) {
15098
+ context.addIssue({
15099
+ code: "custom",
15100
+ message: "Expected a time after the previous keyframe",
15101
+ path: ["keyframes", index, "at"]
15102
+ });
15103
+ }
15104
+ }
15105
+ const blended = keyframes.filter((keyframe) => keyframe.blend !== void 0);
15106
+ if (blended.length > 0) {
15107
+ const themes = JSON.stringify(blended[0].blend.themes);
15108
+ keyframes.forEach((keyframe, index) => {
15109
+ if (!keyframe.blend) {
15110
+ context.addIssue({
15111
+ code: "custom",
15112
+ message: "Expected a blend on every keyframe when one keyframe blends themes",
15113
+ path: ["keyframes", index, "blend"]
15114
+ });
15115
+ } else if (JSON.stringify(keyframe.blend.themes) !== themes) {
15116
+ context.addIssue({
15117
+ code: "custom",
15118
+ message: "Expected the same themes, in the same order, on every keyframe",
15119
+ path: ["keyframes", index, "blend", "themes"]
15120
+ });
15121
+ }
15122
+ });
15123
+ }
15124
+ const frames = tileflowCaptureShotFrameCount(shot);
15125
+ if (frames > tileflowCaptureShotLimits.maximumFrames) {
15126
+ context.addIssue({
15127
+ code: "custom",
15128
+ message: `Expected at most ${tileflowCaptureShotLimits.maximumFrames} frames; this shot renders ${frames}`,
15129
+ path: ["keyframes", keyframes.length - 1, "at"]
15130
+ });
15131
+ }
15132
+ }
15133
+
14720
15134
  // src/maps/operations.ts
14721
15135
  var resetValue = deepFreeze({ $tileflow: "reset" });
14722
15136
  var disableValue = deepFreeze({ op: "disable" });
@@ -14769,6 +15183,7 @@ var tileflowMapMergeStrategies = {
14769
15183
  overlays: "keyed-resources",
14770
15184
  projection: "atomic",
14771
15185
  scenes: "leaf",
15186
+ shots: "leaf",
14772
15187
  sources: "keyed-resources",
14773
15188
  systemThemes: "atomic",
14774
15189
  terrain: "atomic",
@@ -15145,6 +15560,7 @@ function parseTileflowMap(map) {
15145
15560
  for (const node2 of lineage.reverse()) {
15146
15561
  const resolved = node2 === map ? resolvedLeaf : resolveMap(node2);
15147
15562
  validateMapScenes(node2, resolved.id);
15563
+ validateMapShots(node2, resolved.id);
15148
15564
  const parsed = parseResolvedTileflowMap(resolved);
15149
15565
  if (node2 === map) parsedLeaf = parsed;
15150
15566
  }
@@ -15152,40 +15568,53 @@ function parseTileflowMap(map) {
15152
15568
  return parsedLeaf;
15153
15569
  }
15154
15570
  function validateMapScenes(map, mapId) {
15155
- if (map.scenes === void 0) return;
15156
- if (!isPlainRecord5(map.scenes)) {
15157
- throw new Error(`Invalid Tileflow map "${map.id}". scenes: Expected a plain object.`);
15571
+ validateMapCaptureEntries(map, mapId, "scenes", (input) => {
15572
+ const result = tileflowCaptureSceneSchema.safeParse(input);
15573
+ return result.success ? tileflowCaptureSceneFitIssue(result.data) : result.error.issues[0];
15574
+ });
15575
+ }
15576
+ function validateMapShots(map, mapId) {
15577
+ validateMapCaptureEntries(map, mapId, "shots", (input) => {
15578
+ const result = tileflowCaptureShotSchema.safeParse(input);
15579
+ return result.success ? void 0 : result.error.issues[0];
15580
+ });
15581
+ }
15582
+ function validateMapCaptureEntries(map, mapId, key, findIssue) {
15583
+ const entries = map[key];
15584
+ if (entries === void 0) return;
15585
+ if (!isPlainRecord5(entries)) {
15586
+ throw new Error(`Invalid Tileflow map "${map.id}". ${key}: Expected a plain object.`);
15158
15587
  }
15588
+ const label = key === "scenes" ? "scene" : "shot";
15159
15589
  const namesByCaseFold = /* @__PURE__ */ new Map();
15160
- for (const [sceneName, input] of Object.entries(map.scenes)) {
15161
- const nameResult = tileflowCaptureSceneNameSchema.safeParse(sceneName);
15590
+ for (const [name, input] of Object.entries(entries)) {
15591
+ const nameResult = tileflowCaptureSceneNameSchema.safeParse(name);
15162
15592
  if (!nameResult.success) {
15163
15593
  throw new Error(
15164
- `Invalid Tileflow map "${map.id}". scenes.${sceneName}: ${nameResult.error.issues[0]?.message ?? "Invalid scene name"}`
15594
+ `Invalid Tileflow map "${map.id}". ${key}.${name}: ${nameResult.error.issues[0]?.message ?? `Invalid ${label} name`}`
15165
15595
  );
15166
15596
  }
15167
- const normalizedSceneName = nameResult.data;
15168
- const folded = normalizedSceneName.toLowerCase();
15597
+ const normalizedName = nameResult.data;
15598
+ const folded = normalizedName.toLowerCase();
15169
15599
  const existing = namesByCaseFold.get(folded);
15170
15600
  if (existing) {
15171
15601
  throw new Error(
15172
- `Invalid Tileflow map "${map.id}". scenes.${sceneName}: Expected an id that does not normalize to "${existing}".`
15602
+ `Invalid Tileflow map "${map.id}". ${key}.${name}: Expected an id that does not normalize to "${existing}".`
15173
15603
  );
15174
15604
  }
15175
- namesByCaseFold.set(folded, normalizedSceneName);
15605
+ namesByCaseFold.set(folded, normalizedName);
15176
15606
  if (!isPlainRecord5(input)) {
15177
- throw new Error(`Invalid Tileflow map "${map.id}". scenes.${sceneName}: Expected an object.`);
15607
+ throw new Error(`Invalid Tileflow map "${map.id}". ${key}.${name}: Expected an object.`);
15178
15608
  }
15179
15609
  if (Object.hasOwn(input, "map")) {
15180
15610
  throw new Error(
15181
- `Invalid Tileflow map "${map.id}". scenes.${sceneName}.map: Unrecognized key; a singular map scene inherits its map id.`
15611
+ `Invalid Tileflow map "${map.id}". ${key}.${name}.map: Unrecognized key; a singular map ${label} inherits its map id.`
15182
15612
  );
15183
15613
  }
15184
- const result = tileflowCaptureSceneSchema.safeParse({ ...input, map: mapId });
15185
- if (!result.success) {
15186
- const issue = result.error.issues[0];
15614
+ const issue = findIssue({ ...input, map: mapId });
15615
+ if (issue) {
15187
15616
  throw new Error(
15188
- `Invalid Tileflow map "${map.id}". scenes.${sceneName}${issue?.path.length ? `.${issue.path.join(".")}` : ""}: ${issue?.message ?? "Invalid scene"}`
15617
+ `Invalid Tileflow map "${map.id}". ${key}.${name}${issue.path.length ? `.${issue.path.join(".")}` : ""}: ${issue.message}`
15189
15618
  );
15190
15619
  }
15191
15620
  }
@@ -15197,10 +15626,10 @@ function isPlainRecord5(value) {
15197
15626
  }
15198
15627
 
15199
15628
  // src/icon-composition.ts
15200
- import { z as z10 } from "zod";
15629
+ import { z as z11 } from "zod";
15201
15630
 
15202
15631
  // src/icon-package.ts
15203
- import { z as z9 } from "zod";
15632
+ import { z as z10 } from "zod";
15204
15633
  var tileflowIconIdPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
15205
15634
  var tileflowIconPackageLimits = {
15206
15635
  decodeConcurrency: 4,
@@ -15219,19 +15648,19 @@ var tileflowHostedAlphaCompatibility = {
15219
15648
  },
15220
15649
  maxMapsPerDeploy: 20
15221
15650
  };
15222
- var sha256Schema = z9.string().regex(/^[a-f0-9]{64}$/);
15651
+ var sha256Schema = z10.string().regex(/^[a-f0-9]{64}$/);
15223
15652
  var tileflowIconPackageContentHashSchema = sha256Schema;
15224
- var tileflowIconIdSchema = z9.string().max(64).regex(tileflowIconIdPattern);
15225
- var tileflowRenderedIconManifestEntrySchema = z9.object({
15653
+ var tileflowIconIdSchema = z10.string().max(64).regex(tileflowIconIdPattern);
15654
+ var tileflowRenderedIconManifestEntrySchema = z10.object({
15226
15655
  name: tileflowIconIdSchema,
15227
15656
  appearance: tileflowIconAppearanceSchema.optional(),
15228
15657
  layout: tileflowIconLayoutSchema.optional(),
15229
- pixelSha256: z9.object({
15658
+ pixelSha256: z10.object({
15230
15659
  oneX: sha256Schema,
15231
15660
  twoX: sha256Schema
15232
15661
  }).strict()
15233
15662
  }).strict();
15234
- var tileflowIconPackageLabelSchema = z9.string().superRefine((value, context) => {
15663
+ var tileflowIconPackageLabelSchema = z10.string().superRefine((value, context) => {
15235
15664
  if (value !== value.trim() || value.length === 0) {
15236
15665
  context.addIssue({
15237
15666
  code: "custom",
@@ -15251,32 +15680,32 @@ var tileflowIconPackageLabelSchema = z9.string().superRefine((value, context) =>
15251
15680
  });
15252
15681
  }
15253
15682
  });
15254
- var spriteDimensionsSchema = z9.object({
15255
- height: z9.number().int().positive().max(tileflowIconPackageLimits.maxAtlasDimension),
15256
- pixelRatio: z9.union([z9.literal(1), z9.literal(2)]),
15257
- width: z9.number().int().positive().max(tileflowIconPackageLimits.maxAtlasDimension)
15683
+ var spriteDimensionsSchema = z10.object({
15684
+ height: z10.number().int().positive().max(tileflowIconPackageLimits.maxAtlasDimension),
15685
+ pixelRatio: z10.union([z10.literal(1), z10.literal(2)]),
15686
+ width: z10.number().int().positive().max(tileflowIconPackageLimits.maxAtlasDimension)
15258
15687
  }).strict();
15259
15688
  function iconPackageFileSchema(name, contentType) {
15260
- return z9.object({
15261
- byteLength: z9.number().int().nonnegative().max(tileflowIconPackageLimits.maxGeneratedFileBytes),
15262
- contentType: z9.literal(contentType),
15263
- name: z9.literal(name),
15689
+ return z10.object({
15690
+ byteLength: z10.number().int().nonnegative().max(tileflowIconPackageLimits.maxGeneratedFileBytes),
15691
+ contentType: z10.literal(contentType),
15692
+ name: z10.literal(name),
15264
15693
  sha256: sha256Schema
15265
15694
  }).strict();
15266
15695
  }
15267
- var tileflowIconPackageManifestSchema = z9.object({
15268
- files: z9.tuple([
15696
+ var tileflowIconPackageManifestSchema = z10.object({
15697
+ files: z10.tuple([
15269
15698
  iconPackageFileSchema("sprite.json", "application/json"),
15270
15699
  iconPackageFileSchema("sprite.png", "image/png"),
15271
15700
  iconPackageFileSchema("sprite@2x.json", "application/json"),
15272
15701
  iconPackageFileSchema("sprite@2x.png", "image/png")
15273
15702
  ]),
15274
- format: z9.enum(["tileflow-icon-package-v1", "tileflow-icon-package-v2"]),
15275
- iconNames: z9.array(tileflowIconIdSchema).min(1).max(tileflowIconPackageLimits.maxIconCount),
15276
- renderedIcons: z9.array(tileflowRenderedIconManifestEntrySchema).min(1).max(tileflowIconPackageLimits.maxIconCount),
15277
- sprites: z9.object({
15278
- oneX: spriteDimensionsSchema.extend({ pixelRatio: z9.literal(1) }),
15279
- twoX: spriteDimensionsSchema.extend({ pixelRatio: z9.literal(2) })
15703
+ format: z10.enum(["tileflow-icon-package-v1", "tileflow-icon-package-v2"]),
15704
+ iconNames: z10.array(tileflowIconIdSchema).min(1).max(tileflowIconPackageLimits.maxIconCount),
15705
+ renderedIcons: z10.array(tileflowRenderedIconManifestEntrySchema).min(1).max(tileflowIconPackageLimits.maxIconCount),
15706
+ sprites: z10.object({
15707
+ oneX: spriteDimensionsSchema.extend({ pixelRatio: z10.literal(1) }),
15708
+ twoX: spriteDimensionsSchema.extend({ pixelRatio: z10.literal(2) })
15280
15709
  }).strict()
15281
15710
  }).strict().superRefine((manifest, context) => {
15282
15711
  if (manifest.renderedIcons.some(
@@ -15375,57 +15804,57 @@ function containsControlCharacter(value) {
15375
15804
 
15376
15805
  // src/icon-composition.ts
15377
15806
  var tileflowIconCompositionMaximumBytes = 1024 * 1024;
15378
- var idsSchema = z10.array(tileflowIconIdSchema).max(tileflowIconPackageLimits.maxIconCount).refine(
15807
+ var idsSchema = z11.array(tileflowIconIdSchema).max(tileflowIconPackageLimits.maxIconCount).refine(
15379
15808
  (ids) => new Set(ids).size === ids.length && ids.every((id2, index) => index === 0 || compareCodeUnits8(ids[index - 1], id2) < 0),
15380
15809
  "Icon exports must be unique and sorted by code unit"
15381
15810
  );
15382
- var localContributorSchema = z10.object({ kind: z10.literal("local"), iconIds: idsSchema }).strict();
15383
- var packageContributorSchema = z10.object({
15384
- kind: z10.literal("package"),
15385
- package: z10.string().max(214).regex(/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u),
15811
+ var localContributorSchema = z11.object({ kind: z11.literal("local"), iconIds: idsSchema }).strict();
15812
+ var packageContributorSchema = z11.object({
15813
+ kind: z11.literal("package"),
15814
+ package: z11.string().max(214).regex(/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u),
15386
15815
  iconIds: idsSchema
15387
15816
  }).strict();
15388
- var setContributorSchema = z10.object({
15389
- kind: z10.literal("icon-set"),
15817
+ var setContributorSchema = z11.object({
15818
+ kind: z11.literal("icon-set"),
15390
15819
  reference: tileflowIconSetReferenceSchema,
15391
15820
  teamId: tileflowIconSetTeamIdSchema,
15392
15821
  setId: tileflowIconSetIdSchema,
15393
15822
  versionId: tileflowIconSetVersionIdSchema,
15394
- version: z10.number().int().positive().max(Number.MAX_SAFE_INTEGER),
15823
+ version: z11.number().int().positive().max(Number.MAX_SAFE_INTEGER),
15395
15824
  packageId: tileflowIconSetPackageIdSchema,
15396
15825
  contentHash: tileflowIconPackageContentHashSchema,
15397
15826
  iconIds: idsSchema.min(1)
15398
15827
  }).strict();
15399
- var tileflowRenderedIconIdentitySchema = z10.object({
15400
- kind: z10.literal("rendered-icon"),
15828
+ var tileflowRenderedIconIdentitySchema = z11.object({
15829
+ kind: z11.literal("rendered-icon"),
15401
15830
  id: tileflowIconIdSchema,
15402
15831
  appearance: tileflowIconAppearanceSchema.optional(),
15403
15832
  layout: tileflowIconLayoutSchema.optional(),
15404
- width: z10.number().int().positive().max(tileflowIconPackageLimits.maxAtlasDimension / 2),
15405
- height: z10.number().int().positive().max(tileflowIconPackageLimits.maxAtlasDimension / 2),
15406
- pixelSha256: z10.object({
15833
+ width: z11.number().int().positive().max(tileflowIconPackageLimits.maxAtlasDimension / 2),
15834
+ height: z11.number().int().positive().max(tileflowIconPackageLimits.maxAtlasDimension / 2),
15835
+ pixelSha256: z11.object({
15407
15836
  oneX: tileflowIconPackageContentHashSchema,
15408
15837
  twoX: tileflowIconPackageContentHashSchema
15409
15838
  }).strict()
15410
15839
  }).strict();
15411
- var tileflowIconCompositionSchema = z10.object({
15840
+ var tileflowIconCompositionSchema = z11.object({
15412
15841
  // An explicit map keeps declaration order independent of TypeScript's literal cache.
15413
- format: z10.enum({
15842
+ format: z11.enum({
15414
15843
  "tileflow-icon-composition-v1": "tileflow-icon-composition-v1",
15415
15844
  "tileflow-icon-composition-v2": "tileflow-icon-composition-v2"
15416
15845
  }),
15417
- compositionVersion: z10.union([z10.literal(1), z10.literal(2)]),
15846
+ compositionVersion: z11.union([z11.literal(1), z11.literal(2)]),
15418
15847
  packageHash: tileflowIconPackageContentHashSchema,
15419
- contributors: z10.array(
15420
- z10.discriminatedUnion("kind", [
15848
+ contributors: z11.array(
15849
+ z11.discriminatedUnion("kind", [
15421
15850
  localContributorSchema,
15422
15851
  packageContributorSchema,
15423
15852
  setContributorSchema
15424
15853
  ])
15425
15854
  ).min(1).max(tileflowIconSourceLimit),
15426
- winners: z10.array(
15855
+ winners: z11.array(
15427
15856
  tileflowRenderedIconIdentitySchema.omit({ kind: true }).extend({
15428
- contributor: z10.number().int().min(0).max(tileflowIconSourceLimit - 1)
15857
+ contributor: z11.number().int().min(0).max(tileflowIconSourceLimit - 1)
15429
15858
  })
15430
15859
  ).min(1).max(tileflowIconPackageLimits.maxIconCount)
15431
15860
  }).strict().superRefine((receipt, context) => {
@@ -15777,23 +16206,23 @@ function compileTileflowMapWithInspection(config, options) {
15777
16206
  }
15778
16207
 
15779
16208
  // src/renderer-deployment.ts
15780
- import { z as z14 } from "zod";
16209
+ import { z as z15 } from "zod";
15781
16210
 
15782
16211
  // src/native-build-record.ts
15783
- import { z as z12 } from "zod";
16212
+ import { z as z13 } from "zod";
15784
16213
 
15785
16214
  // src/native-profile-definition.ts
15786
- import { z as z11 } from "zod";
16215
+ import { z as z12 } from "zod";
15787
16216
  var tileflowNativeProfileSchema = strictNativeObject({
15788
- id: z11.literal("native-v1"),
15789
- renderer: z11.literal("native"),
15790
- schemaVersion: z11.literal(1),
15791
- styleVersion: z11.literal(8),
15792
- validatorStyleSpec: z11.literal("24.8.5"),
15793
- reactNativeStyleSpec: z11.literal("26.2.1"),
15794
- maplibreReactNative: z11.literal("11.3.10"),
15795
- android: z11.literal("13.2.0"),
15796
- ios: z11.literal("6.26.0")
16217
+ id: z12.literal("native-v1"),
16218
+ renderer: z12.literal("native"),
16219
+ schemaVersion: z12.literal(1),
16220
+ styleVersion: z12.literal(8),
16221
+ validatorStyleSpec: z12.literal("24.8.5"),
16222
+ reactNativeStyleSpec: z12.literal("26.2.1"),
16223
+ maplibreReactNative: z12.literal("11.3.10"),
16224
+ android: z12.literal("13.2.0"),
16225
+ ios: z12.literal("6.26.0")
15797
16226
  });
15798
16227
  var tileflowNativeProfile = Object.freeze({
15799
16228
  id: "native-v1",
@@ -15806,8 +16235,8 @@ var tileflowNativeProfile = Object.freeze({
15806
16235
  android: "13.2.0",
15807
16236
  ios: "6.26.0"
15808
16237
  });
15809
- var tileflowNativeProfileIdSchema = z11.literal("native-v1");
15810
- var tileflowRendererSchema = z11.enum(["web", "native"]);
16238
+ var tileflowNativeProfileIdSchema = z12.literal("native-v1");
16239
+ var tileflowRendererSchema = z12.enum(["web", "native"]);
15811
16240
 
15812
16241
  // src/native-profile-helpers.ts
15813
16242
  var tileflowNativeProfileLimits = Object.freeze({
@@ -15888,17 +16317,24 @@ function isBoundedNativeJson(input, stage = "input") {
15888
16317
  }
15889
16318
 
15890
16319
  // src/native-build-record.ts
15891
- var hashSchema = z12.string().regex(/^[a-f0-9]{64}$/u);
15892
- var layerCountSchema = z12.number().int().min(0).max(tileflowNativeProfileLimits.maximumLayers);
16320
+ var hashSchema = z13.string().regex(/^[a-f0-9]{64}$/u);
16321
+ var layerCountSchema = z13.number().int().min(0).max(tileflowNativeProfileLimits.maximumLayers);
15893
16322
  var layerTransformationSchema = strictNativeObject({
15894
16323
  inputLayer: layerCountSchema,
15895
16324
  outputStart: layerCountSchema,
15896
- outputCount: z12.number().int().min(1).max(32),
15897
- properties: z12.array(z12.enum(["line-cap", "line-dasharray"])).min(1).max(2).refine(
16325
+ outputCount: z13.number().int().min(1).max(32),
16326
+ properties: z13.array(z13.enum(["line-cap", "line-dasharray"])).min(1).max(2).refine(
15898
16327
  (properties2) => properties2.length === 1 || properties2[0] === "line-cap" && properties2[1] === "line-dasharray",
15899
16328
  "Expected unique, ordered properties"
15900
16329
  )
15901
16330
  });
16331
+ var symbolOverlapTransformationSchema = strictNativeObject({
16332
+ inputLayer: layerCountSchema,
16333
+ properties: z13.array(z13.enum(["icon-overlap", "text-overlap"])).min(1).max(2).refine(
16334
+ (properties2) => properties2.length === 1 || properties2[0] === "icon-overlap" && properties2[1] === "text-overlap",
16335
+ "Expected unique, ordered properties"
16336
+ )
16337
+ });
15902
16338
  var tileflowNativeStyleTransformationSchema = strictNativeObject({
15903
16339
  map: tileflowPortableIdSchema,
15904
16340
  theme: tileflowThemeNameSchema,
@@ -15906,8 +16342,9 @@ var tileflowNativeStyleTransformationSchema = strictNativeObject({
15906
16342
  loweredStyleSha256: hashSchema,
15907
16343
  inputLayers: layerCountSchema,
15908
16344
  outputLayers: layerCountSchema,
15909
- projection: z12.enum(["none", "mercator-to-implicit", "globe-to-mercator"]),
15910
- layers: z12.array(layerTransformationSchema).max(tileflowNativeProfileLimits.maximumLayers)
16345
+ projection: z13.enum(["none", "mercator-to-implicit", "globe-to-mercator"]),
16346
+ layers: z13.array(layerTransformationSchema).max(tileflowNativeProfileLimits.maximumLayers),
16347
+ symbolOverlap: z13.array(symbolOverlapTransformationSchema).max(tileflowNativeProfileLimits.maximumLayers)
15911
16348
  }).superRefine((style, context) => {
15912
16349
  let previous = -1;
15913
16350
  let added = 0;
@@ -15929,16 +16366,28 @@ var tileflowNativeStyleTransformationSchema = strictNativeObject({
15929
16366
  message: "Layer counts do not match transformation spans"
15930
16367
  });
15931
16368
  }
16369
+ const partitioned = new Set(style.layers.map(({ inputLayer }) => inputLayer));
16370
+ previous = -1;
16371
+ for (const [index, layer] of style.symbolOverlap.entries()) {
16372
+ if (layer.inputLayer <= previous || layer.inputLayer >= style.inputLayers || partitioned.has(layer.inputLayer)) {
16373
+ context.addIssue({
16374
+ code: "custom",
16375
+ path: ["symbolOverlap", index],
16376
+ message: "Invalid symbol overlap layer"
16377
+ });
16378
+ }
16379
+ previous = layer.inputLayer;
16380
+ }
15932
16381
  });
15933
16382
  var tileflowNativeBuildRecordSchema = strictNativeObject({
15934
- schemaVersion: z12.literal(2),
15935
- renderer: z12.literal("native"),
15936
- profile: z12.literal("native-v1"),
15937
- validation: z12.literal("static-artifacts"),
15938
- preparationVersion: z12.literal("native-lowering-v1"),
15939
- engines: strictNativeObject({ android: z12.literal("13.2.0"), ios: z12.literal("6.26.0") }),
16383
+ schemaVersion: z13.literal(2),
16384
+ renderer: z13.literal("native"),
16385
+ profile: z13.literal("native-v1"),
16386
+ validation: z13.literal("static-artifacts"),
16387
+ preparationVersion: z13.literal("native-lowering-v1"),
16388
+ engines: strictNativeObject({ android: z13.literal("13.2.0"), ios: z13.literal("6.26.0") }),
15940
16389
  buildManifestSha256: hashSchema,
15941
- transformations: z12.array(tileflowNativeStyleTransformationSchema).max(4096)
16390
+ transformations: z13.array(tileflowNativeStyleTransformationSchema).max(4096)
15942
16391
  }).superRefine((record3, context) => {
15943
16392
  let previous = "";
15944
16393
  for (const [index, item] of record3.transformations.entries()) {
@@ -24394,7 +24843,7 @@ var latest_default = {
24394
24843
  };
24395
24844
 
24396
24845
  // src/native-profile.ts
24397
- import { z as z13 } from "zod";
24846
+ import { z as z14 } from "zod";
24398
24847
 
24399
24848
  // src/native-url-policy.ts
24400
24849
  var tileflowNativeUrlLimits = Object.freeze({ maximumLength: 2048 });
@@ -24549,7 +24998,7 @@ var { resolveTileflowNativeResourceUrl } = createNativeUrlPolicy(
24549
24998
  );
24550
24999
 
24551
25000
  // src/native-profile.ts
24552
- var tileflowNativeDiagnosticCodeSchema = z13.enum([
25001
+ var tileflowNativeDiagnosticCodeSchema = z14.enum([
24553
25002
  "NATIVE_UNSUPPORTED_STYLE",
24554
25003
  "NATIVE_UNSUPPORTED_SOURCE",
24555
25004
  "NATIVE_FONT_UNAVAILABLE",
@@ -24558,13 +25007,13 @@ var tileflowNativeDiagnosticCodeSchema = z13.enum([
24558
25007
  ]);
24559
25008
  var tileflowNativeDiagnosticSchema = strictNativeObject({
24560
25009
  code: tileflowNativeDiagnosticCodeSchema,
24561
- phase: z13.literal("native-compatibility"),
24562
- severity: z13.literal("error"),
24563
- path: z13.string().max(300).regex(/^(?:\/(?:[^~]|~[01])*)?$/u),
24564
- renderer: z13.literal("native"),
25010
+ phase: z14.literal("native-compatibility"),
25011
+ severity: z14.literal("error"),
25012
+ path: z14.string().max(300).regex(/^(?:\/(?:[^~]|~[01])*)?$/u),
25013
+ renderer: z14.literal("native"),
24565
25014
  profile: tileflowNativeProfileIdSchema,
24566
- message: z13.string().max(300),
24567
- suggestion: z13.string().max(300)
25015
+ message: z14.string().max(300),
25016
+ suggestion: z14.string().max(300)
24568
25017
  });
24569
25018
  var descriptions = {
24570
25019
  NATIVE_UNSUPPORTED_STYLE: [
@@ -24944,24 +25393,24 @@ var tileflowRendererDeploymentLimits = Object.freeze({
24944
25393
  maximumNativeFamilyBytes: tileflowNativeProfileLimits.maximumStyleBytes,
24945
25394
  maximumThemes: 64
24946
25395
  });
24947
- var hashSchema2 = z14.string().regex(/^[a-f0-9]{64}$/u);
24948
- var recordSchema = z14.record(z14.string().min(1).max(256), z14.unknown());
24949
- var positive = z14.number().int().positive().max(Number.MAX_SAFE_INTEGER);
24950
- var sourceAssetsSchema = z14.object({
24951
- fonts: z14.array(
24952
- z14.object({
24953
- family: z14.string().min(1).max(128),
25396
+ var hashSchema2 = z15.string().regex(/^[a-f0-9]{64}$/u);
25397
+ var recordSchema = z15.record(z15.string().min(1).max(256), z15.unknown());
25398
+ var positive = z15.number().int().positive().max(Number.MAX_SAFE_INTEGER);
25399
+ var sourceAssetsSchema = z15.object({
25400
+ fonts: z15.array(
25401
+ z15.object({
25402
+ family: z15.string().min(1).max(128),
24954
25403
  sha256: hashSchema2,
24955
- style: z14.enum(["italic", "normal", "oblique"]),
24956
- weight: z14.enum(["100", "200", "300", "400", "500", "600", "700", "800", "900"])
25404
+ style: z15.enum(["italic", "normal", "oblique"]),
25405
+ weight: z15.enum(["100", "200", "300", "400", "500", "600", "700", "800", "900"])
24957
25406
  }).strict()
24958
25407
  ).max(64),
24959
- icons: z14.array(
24960
- z14.union([
24961
- z14.object({
24962
- format: z14.enum(["jpeg", "png", "svg", "webp"]),
25408
+ icons: z15.array(
25409
+ z15.union([
25410
+ z15.object({
25411
+ format: z15.enum(["jpeg", "png", "svg", "webp"]),
24963
25412
  id: tileflowPortableIdSchema,
24964
- kind: z14.enum(["icon", "pattern"]),
25413
+ kind: z15.enum(["icon", "pattern"]),
24965
25414
  sha256: hashSchema2
24966
25415
  }).strict(),
24967
25416
  tileflowRenderedIconIdentitySchema
@@ -24969,44 +25418,44 @@ var sourceAssetsSchema = z14.object({
24969
25418
  ).max(512),
24970
25419
  iconComposition: tileflowIconCompositionSchema.optional()
24971
25420
  }).strict();
24972
- var themeBuildSchema = z14.object({
24973
- colorScheme: z14.enum(["dark", "light"]),
25421
+ var themeBuildSchema = z15.object({
25422
+ colorScheme: z15.enum(["dark", "light"]),
24974
25423
  dataRequirements: recordSchema,
24975
25424
  sourceRequirements: recordSchema,
24976
25425
  styleSha256: hashSchema2,
24977
25426
  themeId: tileflowPortableIdSchema,
24978
25427
  themeVersion: positive
24979
25428
  }).strict();
24980
- var mapBuildSchema = z14.object({
25429
+ var mapBuildSchema = z15.object({
24981
25430
  assetSetSha256: hashSchema2,
24982
25431
  defaultTheme: tileflowThemeNameSchema,
24983
- lineage: z14.array(z14.object({ id: tileflowPortableIdSchema, mapVersion: positive }).strict()).min(1).max(64),
25432
+ lineage: z15.array(z15.object({ id: tileflowPortableIdSchema, mapVersion: positive }).strict()).min(1).max(64),
24984
25433
  mapRevisionSha256: hashSchema2,
24985
- mapRevisionSchemaVersion: z14.literal(2).optional(),
25434
+ mapRevisionSchemaVersion: z15.literal(2).optional(),
24986
25435
  mapVersion: positive,
24987
- semanticCompiler: z14.object({ name: z14.literal("tileflow-semantic"), version: z14.literal(1) }).strict(),
25436
+ semanticCompiler: z15.object({ name: z15.literal("tileflow-semantic"), version: z15.literal(1) }).strict(),
24988
25437
  sourceAssets: sourceAssetsSchema,
24989
- systemThemes: z14.object({ dark: tileflowThemeNameSchema, light: tileflowThemeNameSchema }).strict().optional(),
24990
- themes: z14.record(tileflowThemeNameSchema, themeBuildSchema)
25438
+ systemThemes: z15.object({ dark: tileflowThemeNameSchema, light: tileflowThemeNameSchema }).strict().optional(),
25439
+ themes: z15.record(tileflowThemeNameSchema, themeBuildSchema)
24991
25440
  }).strict();
24992
- var buildManifestSchema = z14.object({
24993
- schemaVersion: z14.literal(1),
24994
- maps: z14.record(tileflowPortableIdSchema, mapBuildSchema),
24995
- provenance: z14.object({
24996
- schemaVersion: z14.literal(1),
24997
- lockfile: z14.object({ format: z14.enum(["bun", "npm", "pnpm", "yarn"]), sha256: hashSchema2 }).strict().optional(),
24998
- packages: z14.record(z14.string().min(1).max(128), z14.string().min(1).max(128))
25441
+ var buildManifestSchema = z15.object({
25442
+ schemaVersion: z15.literal(1),
25443
+ maps: z15.record(tileflowPortableIdSchema, mapBuildSchema),
25444
+ provenance: z15.object({
25445
+ schemaVersion: z15.literal(1),
25446
+ lockfile: z15.object({ format: z15.enum(["bun", "npm", "pnpm", "yarn"]), sha256: hashSchema2 }).strict().optional(),
25447
+ packages: z15.record(z15.string().min(1).max(128), z15.string().min(1).max(128))
24999
25448
  }).strict().optional()
25000
25449
  }).strict();
25001
25450
  var familyShape = {
25002
25451
  buildManifest: buildManifestSchema,
25003
- styles: z14.record(tileflowThemeNameSchema, recordSchema)
25452
+ styles: z15.record(tileflowThemeNameSchema, recordSchema)
25004
25453
  };
25005
- var teamSourcesSchema = z14.record(
25454
+ var teamSourcesSchema = z15.record(
25006
25455
  tileflowPortableIdSchema,
25007
- z14.object({
25008
- tileset: z14.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/u),
25009
- type: z14.enum(["raster", "vector"])
25456
+ z15.object({
25457
+ tileset: z15.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/u),
25458
+ type: z15.enum(["raster", "vector"])
25010
25459
  }).strict()
25011
25460
  ).superRefine((sources, context) => {
25012
25461
  const entries = Object.entries(sources);
@@ -25018,21 +25467,21 @@ var teamSourcesSchema = z14.record(
25018
25467
  tilesets.add(value.tileset);
25019
25468
  }
25020
25469
  });
25021
- var viewSchema2 = z14.object({
25022
- center: z14.tuple([z14.number().min(-180).max(180), z14.number().min(-90).max(90)]).optional(),
25023
- zoom: z14.number().min(0).max(24).optional(),
25024
- bearing: z14.number().finite().optional(),
25025
- pitch: z14.number().min(0).max(85).optional()
25470
+ var viewSchema2 = z15.object({
25471
+ center: z15.tuple([z15.number().min(-180).max(180), z15.number().min(-90).max(90)]).optional(),
25472
+ zoom: z15.number().min(0).max(24).optional(),
25473
+ bearing: z15.number().finite().optional(),
25474
+ pitch: z15.number().min(0).max(85).optional()
25026
25475
  }).strict();
25027
- var tileflowRendererDeploymentArtifactSchema = z14.object({
25028
- kind: z14.literal("tileflow-map-deployment"),
25029
- schemaVersion: z14.literal(2),
25476
+ var tileflowRendererDeploymentArtifactSchema = z15.object({
25477
+ kind: z15.literal("tileflow-map-deployment"),
25478
+ schemaVersion: z15.literal(2),
25030
25479
  mapId: tileflowPortableIdSchema,
25031
25480
  teamSources: teamSourcesSchema,
25032
25481
  view: viewSchema2.optional(),
25033
- renderers: z14.object({
25034
- web: z14.object(familyShape).strict(),
25035
- native: z14.object({ ...familyShape, buildRecord: tileflowNativeBuildRecordSchema }).strict()
25482
+ renderers: z15.object({
25483
+ web: z15.object(familyShape).strict(),
25484
+ native: z15.object({ ...familyShape, buildRecord: tileflowNativeBuildRecordSchema }).strict()
25036
25485
  }).strict()
25037
25486
  }).strict().superRefine((artifact, context) => {
25038
25487
  const fail = () => context.addIssue({
@@ -25157,30 +25606,30 @@ function object(value) {
25157
25606
  function equal(left, right) {
25158
25607
  return left === void 0 || right === void 0 ? left === right : serializeCanonicalJson(left) === serializeCanonicalJson(right);
25159
25608
  }
25160
- var artifactName = z14.string().min(1).max(256).refine(
25609
+ var artifactName = z15.string().min(1).max(256).refine(
25161
25610
  (value) => [...value].every((char) => {
25162
25611
  const code = char.codePointAt(0);
25163
25612
  return code >= 32 && code !== 127;
25164
25613
  })
25165
25614
  );
25166
- var semanticSchema = z14.object({
25167
- version: z14.literal(2),
25168
- domains: z14.object({
25169
- poi: z14.object({
25170
- deduplication: z14.object({
25171
- identity: z14.tuple([
25172
- z14.literal("source"),
25173
- z14.literal("source-layer"),
25174
- z14.literal("feature-id")
25615
+ var semanticSchema = z15.object({
25616
+ version: z15.literal(2),
25617
+ domains: z15.object({
25618
+ poi: z15.object({
25619
+ deduplication: z15.object({
25620
+ identity: z15.tuple([
25621
+ z15.literal("source"),
25622
+ z15.literal("source-layer"),
25623
+ z15.literal("feature-id")
25175
25624
  ]),
25176
- representationPriority: z14.tuple([
25177
- z14.literal("marker"),
25178
- z14.literal("icon"),
25179
- z14.literal("combined"),
25180
- z14.literal("label")
25625
+ representationPriority: z15.tuple([
25626
+ z15.literal("marker"),
25627
+ z15.literal("icon"),
25628
+ z15.literal("combined"),
25629
+ z15.literal("label")
25181
25630
  ])
25182
25631
  }).strict(),
25183
- fields: z14.object({
25632
+ fields: z15.object({
25184
25633
  category: artifactName,
25185
25634
  filterRank: artifactName,
25186
25635
  icon: artifactName,
@@ -25188,18 +25637,18 @@ var semanticSchema = z14.object({
25188
25637
  sizeRank: artifactName,
25189
25638
  type: artifactName
25190
25639
  }).strict(),
25191
- hitTesting: z14.object({
25192
- frequency: z14.literal("animation-frame"),
25193
- order: z14.literal("rendered-topmost")
25640
+ hitTesting: z15.object({
25641
+ frequency: z15.literal("animation-frame"),
25642
+ order: z15.literal("rendered-topmost")
25194
25643
  }).strict(),
25195
- identity: z14.literal("maplibre-feature-id-if-present"),
25196
- layers: z14.array(
25197
- z14.object({
25198
- anchor: z14.literal("pointer-coordinate"),
25199
- category: z14.enum(tileflowPoiCategories),
25644
+ identity: z15.literal("maplibre-feature-id-if-present"),
25645
+ layers: z15.array(
25646
+ z15.object({
25647
+ anchor: z15.literal("pointer-coordinate"),
25648
+ category: z15.enum(tileflowPoiCategories),
25200
25649
  layerId: artifactName,
25201
- priority: z14.number().int().min(0).max(4095),
25202
- representation: z14.enum(["combined", "icon", "label", "marker"]),
25650
+ priority: z15.number().int().min(0).max(4095),
25651
+ representation: z15.enum(["combined", "icon", "label", "marker"]),
25203
25652
  source: artifactName,
25204
25653
  sourceLayer: artifactName
25205
25654
  }).strict()
@@ -25225,8 +25674,8 @@ function semanticIdentity(style) {
25225
25674
  const poi2 = manifest.domains.poi;
25226
25675
  return poi2 ? { ...poi2, layers: poi2.layers.map(({ priority: _priority, ...identity }) => identity) } : manifest;
25227
25676
  }
25228
- var mapIdSchema = z14.string().regex(/^map_[A-Za-z0-9_-]{16}$/u);
25229
- var publicUrlSchema = z14.string().min(1).max(2048).refine((value) => {
25677
+ var mapIdSchema = z15.string().regex(/^map_[A-Za-z0-9_-]{16}$/u);
25678
+ var publicUrlSchema = z15.string().min(1).max(2048).refine((value) => {
25230
25679
  try {
25231
25680
  const url = new URL(value);
25232
25681
  return url.protocol === "https:" && !url.username && !url.password && !url.search && !url.hash && url.toString() === value && !/tf_(?:public|native|live)_/u.test(value);
@@ -25234,22 +25683,22 @@ var publicUrlSchema = z14.string().min(1).max(2048).refine((value) => {
25234
25683
  return false;
25235
25684
  }
25236
25685
  });
25237
- var webResponseTheme = z14.object({ styleId: z14.string().min(1).max(128).optional(), styleUrl: publicUrlSchema }).strict();
25238
- var nativeResponseTheme = z14.object({ styleUrl: publicUrlSchema, revision: hashSchema2 }).strict();
25239
- var tileflowRendererDeploymentResponseSchema = z14.object({
25240
- kind: z14.literal("tileflow-map-deployment-result"),
25241
- schemaVersion: z14.literal(2),
25242
- changed: z14.boolean(),
25686
+ var webResponseTheme = z15.object({ styleId: z15.string().min(1).max(128).optional(), styleUrl: publicUrlSchema }).strict();
25687
+ var nativeResponseTheme = z15.object({ styleUrl: publicUrlSchema, revision: hashSchema2 }).strict();
25688
+ var tileflowRendererDeploymentResponseSchema = z15.object({
25689
+ kind: z15.literal("tileflow-map-deployment-result"),
25690
+ schemaVersion: z15.literal(2),
25691
+ changed: z15.boolean(),
25243
25692
  mapId: mapIdSchema,
25244
- deploymentId: z14.string().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/u),
25693
+ deploymentId: z15.string().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/u),
25245
25694
  version: positive,
25246
- worldConversionId: z14.string().min(1).max(128).optional(),
25247
- renderers: z14.object({
25248
- web: z14.object({ themes: z14.record(tileflowThemeNameSchema, webResponseTheme) }).strict(),
25249
- native: z14.object({
25250
- profile: z14.literal("native-v1"),
25695
+ worldConversionId: z15.string().min(1).max(128).optional(),
25696
+ renderers: z15.object({
25697
+ web: z15.object({ themes: z15.record(tileflowThemeNameSchema, webResponseTheme) }).strict(),
25698
+ native: z15.object({
25699
+ profile: z15.literal("native-v1"),
25251
25700
  manifestUrl: publicUrlSchema,
25252
- themes: z14.record(tileflowThemeNameSchema, nativeResponseTheme)
25701
+ themes: z15.record(tileflowThemeNameSchema, nativeResponseTheme)
25253
25702
  }).strict()
25254
25703
  }).strict()
25255
25704
  }).strict().superRefine((value, context) => {