@tileflow/core 0.1.0-alpha.43 → 0.1.0-alpha.45

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/dist/build.js CHANGED
@@ -1198,6 +1198,9 @@ function cloneJson6(value) {
1198
1198
  return JSON.parse(JSON.stringify(value));
1199
1199
  }
1200
1200
 
1201
+ // src/building-layer-ids.ts
1202
+ var tileflowNativeBuildingsLayerId = "tileflow-buildings-native-extrusion";
1203
+
1201
1204
  // src/modules/buildings/compiler.ts
1202
1205
  function compileBuildings(request, context) {
1203
1206
  const { colors } = context;
@@ -1277,7 +1280,7 @@ function compileBuildings(request, context) {
1277
1280
  kind: "layer",
1278
1281
  layer: applyExtrusionStyle(
1279
1282
  {
1280
- id: "tileflow-buildings-3d",
1283
+ id: tileflowNativeBuildingsLayerId,
1281
1284
  type: "fill-extrusion",
1282
1285
  source,
1283
1286
  "source-layer": sourceLayer,
@@ -4834,7 +4837,7 @@ function appendNauticalLayer(contributions, options) {
4834
4837
  if (point.visible === false) return;
4835
4838
  const isLabel = options.target.startsWith("nautical.labels.");
4836
4839
  if (!isLabel && point.marker && point.marker.visible !== false) {
4837
- const marker = applyCircleStyle(
4840
+ const marker2 = applyCircleStyle(
4838
4841
  base(
4839
4842
  `${options.id}-marker`,
4840
4843
  "circle",
@@ -4852,7 +4855,7 @@ function appendNauticalLayer(contributions, options) {
4852
4855
  nauticalContribution(
4853
4856
  `${options.target}.marker`,
4854
4857
  options.localOrder,
4855
- options.flash ? flashing(marker, ["circle-opacity", "circle-stroke-opacity"]) : marker,
4858
+ options.flash ? flashing(marker2, ["circle-opacity", "circle-stroke-opacity"]) : marker2,
4856
4859
  "symbols"
4857
4860
  )
4858
4861
  );
@@ -5298,12 +5301,12 @@ function mergePoiCategoryLayers(entries, options) {
5298
5301
  };
5299
5302
  for (const part of ["layout", "paint"]) {
5300
5303
  const records = entries.map(({ layer }) => asRecord2(layer[part]));
5301
- const properties2 = [...new Set(records.flatMap((record5) => Object.keys(record5)))].sort();
5304
+ const properties2 = [...new Set(records.flatMap((record7) => Object.keys(record7)))].sort();
5302
5305
  const merged = {};
5303
5306
  for (const property of properties2) {
5304
5307
  const fallback = Object.hasOwn(varying, property) ? varying[property] : tileflowPoiLayerPropertyDefaults[property];
5305
5308
  const values = records.map(
5306
- (record5) => Object.hasOwn(record5, property) ? record5[property] : fallback
5309
+ (record7) => Object.hasOwn(record7, property) ? record7[property] : fallback
5307
5310
  );
5308
5311
  if (values.every((value) => sameJson(value, values[0]))) {
5309
5312
  if (values[0] !== void 0) merged[property] = values[0];
@@ -6334,8 +6337,8 @@ function lastPathKey(path) {
6334
6337
  }
6335
6338
  function visitAuditableArray(value, path, category, state, forceExpression = false, intentRequired = false) {
6336
6339
  const operator = typeof value[0] === "string" ? value[0] : void 0;
6337
- const isExpression2 = forceExpression || isMapLibreExpressionOperator(operator);
6338
- if (!isExpression2) {
6340
+ const isExpression3 = forceExpression || isMapLibreExpressionOperator(operator);
6341
+ if (!isExpression3) {
6339
6342
  for (const [index, entry] of value.entries()) {
6340
6343
  visitAuditableValue(entry, `${path}[${index}]`, category, state, intentRequired);
6341
6344
  }
@@ -6832,13 +6835,13 @@ function assertThemeIdentity(theme) {
6832
6835
  }
6833
6836
  }
6834
6837
  function assertThemeDocument(theme) {
6835
- const record5 = assertPlainThemeRecord(theme, "theme");
6836
- assertExactThemeKeys(record5, "theme", themeDocumentKeys);
6837
- assertRequiredThemeKeys(record5, "theme", themeDocumentKeys);
6838
+ const record7 = assertPlainThemeRecord(theme, "theme");
6839
+ assertExactThemeKeys(record7, "theme", themeDocumentKeys);
6840
+ assertRequiredThemeKeys(record7, "theme", themeDocumentKeys);
6838
6841
  assertThemeIdentity(theme);
6839
- assertThemeTokens(record5.tokens, "theme.tokens", true);
6840
- assertThemeTypography(record5.typography, "theme.typography");
6841
- assertThemeLighting(record5.lighting, "theme.lighting");
6842
+ assertThemeTokens(record7.tokens, "theme.tokens", true);
6843
+ assertThemeTypography(record7.typography, "theme.typography");
6844
+ assertThemeLighting(record7.lighting, "theme.lighting");
6842
6845
  cloneJson14(theme);
6843
6846
  }
6844
6847
  function assertThemeTokens(value, path, complete) {
@@ -7296,6 +7299,132 @@ function compareCodeUnits(left, right) {
7296
7299
  return left < right ? -1 : left > right ? 1 : 0;
7297
7300
  }
7298
7301
 
7302
+ // src/color-program.ts
7303
+ var tileflowColorMetadataKey = "tileflow:colors";
7304
+ var marker = "__tileflow_color_program";
7305
+ function colorProgramMarker(value, program) {
7306
+ return [
7307
+ "let",
7308
+ marker,
7309
+ ["literal", program],
7310
+ "__tileflow_color_value",
7311
+ value,
7312
+ ["var", "__tileflow_color_value"]
7313
+ ];
7314
+ }
7315
+ function isColorProgramMarker(value) {
7316
+ return Array.isArray(value) && value.length === 6 && value[0] === "let" && value[1] === marker && Array.isArray(value[2]) && value[2][0] === "literal";
7317
+ }
7318
+ function colorProgramValue(value) {
7319
+ return isColorProgramMarker(value) ? value[4] : value;
7320
+ }
7321
+ function colorProgramOf(value) {
7322
+ return isColorProgramMarker(value) ? value[2][1] : value;
7323
+ }
7324
+ function preserveThemeColorPrograms(authored, resolved, theme, insideExpression = false) {
7325
+ if (isTileflowThemeTokenReference(authored) && authored.category === "color" || isTileflowThemeColorOperation(authored)) {
7326
+ const value = colorProgramMarker(resolved, resolveProgramNumbers(authored, theme));
7327
+ return insideExpression ? value : { kind: "expression", value };
7328
+ }
7329
+ if (Array.isArray(authored) && Array.isArray(resolved))
7330
+ return resolved.map(
7331
+ (value, index) => preserveThemeColorPrograms(authored[index], value, theme, insideExpression)
7332
+ );
7333
+ if (record(authored) && record(resolved)) {
7334
+ if (authored.kind === "theme-fixed") return resolved;
7335
+ return Object.fromEntries(
7336
+ Object.entries(resolved).map(([key, value]) => [
7337
+ key,
7338
+ preserveThemeColorPrograms(
7339
+ authored[key],
7340
+ value,
7341
+ theme,
7342
+ insideExpression || authored.kind === "expression" || authored.kind === "zoom" && key === "stops"
7343
+ )
7344
+ ])
7345
+ );
7346
+ }
7347
+ return resolved;
7348
+ }
7349
+ function resolveProgramNumbers(value, theme) {
7350
+ if (isTileflowThemeTokenReference(value) && value.category === "number")
7351
+ return resolveThemeValues({ opacity: value }, theme).opacity;
7352
+ if (Array.isArray(value)) return value.map((node2) => resolveProgramNumbers(node2, theme));
7353
+ if (record(value))
7354
+ return Object.fromEntries(
7355
+ Object.entries(value).map(([key, node2]) => [key, resolveProgramNumbers(node2, theme)])
7356
+ );
7357
+ return value;
7358
+ }
7359
+ function extractTileflowColorPrograms(style, tokens) {
7360
+ const programs = [];
7361
+ const ids = /* @__PURE__ */ new Map();
7362
+ const bindings = [];
7363
+ const visit2 = (node2, path) => {
7364
+ if (record(node2) && node2.kind === "expression" && isColorProgramMarker(node2.value))
7365
+ return visit2(node2.value, path);
7366
+ if (isColorProgramMarker(node2)) {
7367
+ const program = colorProgramOf(node2);
7368
+ const key = JSON.stringify(program);
7369
+ let id2 = ids.get(key);
7370
+ if (id2 === void 0) {
7371
+ id2 = programs.length;
7372
+ ids.set(key, id2);
7373
+ programs.push(program);
7374
+ }
7375
+ bindings.push([
7376
+ path[0] === "layers" && path[2] === "paint" ? [path[1], ...path.slice(3)] : path,
7377
+ id2
7378
+ ]);
7379
+ return colorProgramValue(node2);
7380
+ }
7381
+ if (Array.isArray(node2)) return node2.map((v, i) => visit2(v, [...path, i]));
7382
+ if (record(node2))
7383
+ return Object.fromEntries(
7384
+ Object.entries(node2).filter(([key]) => key !== tileflowColorMetadataKey).map(([key, v]) => [key, visit2(v, [...path, key])])
7385
+ );
7386
+ return node2;
7387
+ };
7388
+ const result = visit2(style, []);
7389
+ if (bindings.length)
7390
+ result.metadata = {
7391
+ ...result.metadata,
7392
+ // Keep the declared catalog shape across presets. Per-preset pruning makes a shared
7393
+ // object structurally different and repeats the entire catalog in the transport.
7394
+ [tileflowColorMetadataKey]: { version: 1, programs, bindings, tokens }
7395
+ };
7396
+ return result;
7397
+ }
7398
+ function record(value) {
7399
+ return !!value && typeof value === "object" && !Array.isArray(value);
7400
+ }
7401
+ function mapColorProgramsThrough(input, reader) {
7402
+ return overlayColorPrograms(input, reader(stripColorPrograms(input)));
7403
+ }
7404
+ function stripColorPrograms(value) {
7405
+ if (isColorProgramMarker(value)) return colorProgramValue(value);
7406
+ if (record(value) && value.kind === "expression" && isColorProgramMarker(value.value))
7407
+ return colorProgramValue(value.value);
7408
+ if (Array.isArray(value)) return value.map(stripColorPrograms);
7409
+ if (record(value))
7410
+ return Object.fromEntries(
7411
+ Object.entries(value).map(([key, node2]) => [key, stripColorPrograms(node2)])
7412
+ );
7413
+ return value;
7414
+ }
7415
+ function overlayColorPrograms(input, output) {
7416
+ const candidate = record(input) && input.kind === "expression" ? input.value : input;
7417
+ if (isColorProgramMarker(candidate) && typeof output === "string")
7418
+ return colorProgramMarker(output, colorProgramOf(candidate));
7419
+ if (Array.isArray(input) && Array.isArray(output))
7420
+ return output.map((value, index) => overlayColorPrograms(input[index], value));
7421
+ if (record(input) && record(output))
7422
+ return Object.fromEntries(
7423
+ Object.entries(output).map(([key, node2]) => [key, overlayColorPrograms(input[key], node2)])
7424
+ );
7425
+ return output;
7426
+ }
7427
+
7299
7428
  // src/themes/index.ts
7300
7429
  var defaultColors = {
7301
7430
  background: "#F6F7F3",
@@ -7502,8 +7631,18 @@ var baseThemeColorRoles = {
7502
7631
  };
7503
7632
  var tileflowRequiredThemeColorRoles = Object.freeze(Object.values(baseThemeColorRoles));
7504
7633
  var baseThemeColorRoleSet = new Set(tileflowRequiredThemeColorRoles);
7505
- function resolveThemeColors(theme, onDecision) {
7506
- const tokens = resolveTileflowTheme(theme).tokens.color;
7634
+ function resolveThemeColors(theme, onDecision, preservePrograms = false) {
7635
+ if (onDecision && preservePrograms) {
7636
+ const report = onDecision;
7637
+ onDecision = (decision) => report({ ...decision, value: colorProgramValue(decision.value) });
7638
+ }
7639
+ const concrete = resolveTileflowTheme(theme).tokens.color;
7640
+ const tokens = Object.fromEntries(
7641
+ Object.entries(concrete).map(([name, value]) => [
7642
+ name,
7643
+ preservePrograms ? colorProgramMarker(value, { kind: "theme-token", category: "color", token: name }) : value
7644
+ ])
7645
+ );
7507
7646
  const requireColor = (name) => {
7508
7647
  const value = tokens[name];
7509
7648
  if (value !== void 0) {
@@ -7741,10 +7880,22 @@ function relativeLuminance(color2) {
7741
7880
  return 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b);
7742
7881
  }
7743
7882
  function alpha(color2, opacity) {
7883
+ if (isColorProgramMarker(color2))
7884
+ return colorProgramMarker(alpha(colorProgramValue(color2), opacity), {
7885
+ kind: "theme-color",
7886
+ operation: "alpha",
7887
+ color: colorProgramOf(color2),
7888
+ opacity
7889
+ });
7744
7890
  const { a, b, g, r } = hexToRgba(color2);
7745
7891
  return `rgba(${r}, ${g}, ${b}, ${roundAlpha(a * opacity)})`;
7746
7892
  }
7747
7893
  function mix(hexA, hexB, amount) {
7894
+ if (isColorProgramMarker(hexA) || isColorProgramMarker(hexB))
7895
+ return colorProgramMarker(
7896
+ mix(colorProgramValue(hexA), colorProgramValue(hexB), amount),
7897
+ ["srgb", colorProgramOf(hexA), colorProgramOf(hexB), amount]
7898
+ );
7748
7899
  const a = hexToRgb(hexA);
7749
7900
  const b = hexToRgb(hexB);
7750
7901
  const r = Math.round(a.r + (b.r - a.r) * amount);
@@ -7757,6 +7908,7 @@ function hexToRgb(hex) {
7757
7908
  return { b, g, r };
7758
7909
  }
7759
7910
  function hexToRgba(hex) {
7911
+ hex = colorProgramValue(hex);
7760
7912
  const rgbMatch = hex.match(
7761
7913
  /^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)(?:\s*,\s*(\d+(?:\.\d+)?))?\s*\)$/
7762
7914
  );
@@ -7778,6 +7930,11 @@ function hexToRgba(hex) {
7778
7930
  };
7779
7931
  }
7780
7932
  function cssColor(hex) {
7933
+ if (isColorProgramMarker(hex))
7934
+ return colorProgramMarker(
7935
+ cssColor(colorProgramValue(hex)),
7936
+ colorProgramOf(hex)
7937
+ );
7781
7938
  const value = hex.replace("#", "");
7782
7939
  if (value.length !== 4 && value.length !== 8) {
7783
7940
  return hex;
@@ -7955,10 +8112,10 @@ function bindRepeated(curve) {
7955
8112
  for (let index = 0; index < 2; index += 1) {
7956
8113
  const json = /* @__PURE__ */ new Map();
7957
8114
  const found = /* @__PURE__ */ new Map();
7958
- const visit = (value) => {
8115
+ const visit2 = (value) => {
7959
8116
  if (!Array.isArray(value) || value[0] === "literal")
7960
8117
  return { text: JSON.stringify(value), free: true };
7961
- const parts = value.map(visit);
8118
+ const parts = value.map(visit2);
7962
8119
  const text = `[${parts.map((part) => part.text).join(",")}]`;
7963
8120
  json.set(value, text);
7964
8121
  const free = value[0] !== "zoom" && value[0] !== "var" && parts.every((part) => part.free);
@@ -7966,7 +8123,7 @@ function bindRepeated(curve) {
7966
8123
  found.set(text, (found.get(text) ?? 0) + 1);
7967
8124
  return { text, free };
7968
8125
  };
7969
- visit(tree);
8126
+ visit2(tree);
7970
8127
  let best;
7971
8128
  let saving = 256;
7972
8129
  for (const [text, count] of found) {
@@ -8822,8 +8979,8 @@ function withinClasses(value, drawn) {
8822
8979
  if ((operator === "==" || operator === "!=") && operands.length === 2) {
8823
8980
  const label = isClass(operands[0]) ? operands[1] : isClass(operands[1]) ? operands[0] : null;
8824
8981
  if (typeof label !== "string") return void 0;
8825
- const equal2 = among([label]);
8826
- return equal2 === void 0 ? void 0 : operator === "==" ? equal2 : !equal2;
8982
+ const equal3 = among([label]);
8983
+ return equal3 === void 0 ? void 0 : operator === "==" ? equal3 : !equal3;
8827
8984
  }
8828
8985
  if (operator === "match" && operands.length === 4 && isClass(operands[0]) && typeof operands[2] === "boolean" && typeof operands[3] === "boolean") {
8829
8986
  const labels2 = Array.isArray(operands[1]) ? operands[1] : [operands[1]];
@@ -9470,7 +9627,10 @@ function tileflowVegetationDescription(request, context) {
9470
9627
  return void 0;
9471
9628
  const tree = context.colors.vegetation.tree;
9472
9629
  const style = request?.threeDimensional ?? {};
9473
- const colours = (value, fallback) => Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "string") ? value : [...fallback];
9630
+ const colours = (value, fallback) => {
9631
+ const values = Array.isArray(value) ? value.map((entry) => toMapLibreStyleValue(entry)) : [];
9632
+ return values.length && values.every((entry) => typeof entry === "string" || isColorProgramMarker(entry)) ? values : [...fallback];
9633
+ };
9474
9634
  const number = (value) => {
9475
9635
  const resolved = value === void 0 ? void 0 : toMapLibreStyleValue(value);
9476
9636
  return typeof resolved === "number" && resolved > 0 ? resolved : 1;
@@ -9497,7 +9657,10 @@ function tileflowVegetationDescription(request, context) {
9497
9657
  hedge: field2("hedge", tileflowWorldTreeFields.hedge)
9498
9658
  },
9499
9659
  colors: {
9500
- bark: typeof style.barkColor === "string" ? style.barkColor : tree.bark,
9660
+ bark: (() => {
9661
+ const value = toMapLibreStyleValue(style.barkColor ?? tree.bark);
9662
+ return typeof value === "string" || isColorProgramMarker(value) ? value : tree.bark;
9663
+ })(),
9501
9664
  broadleaf: colours(style.broadleafColors, tree.broadleaf),
9502
9665
  conifer: colours(style.coniferColors, tree.conifer),
9503
9666
  palm: colours(style.palmColors, tree.palm),
@@ -9539,7 +9702,7 @@ function tileflowVegetationDescription(request, context) {
9539
9702
  })(),
9540
9703
  ...(() => {
9541
9704
  const edgeColor = style.edgeColor === void 0 ? void 0 : toMapLibreStyleValue(style.edgeColor);
9542
- return typeof edgeColor === "string" ? { edgeColor } : {};
9705
+ return typeof edgeColor === "string" || isColorProgramMarker(edgeColor) ? { edgeColor } : {};
9543
9706
  })(),
9544
9707
  unknownForms: style.unknownForms ?? tileflowDefaultUnknownTreeForms,
9545
9708
  regions: style.regions ?? [],
@@ -10382,8 +10545,8 @@ function assertProvidedServices(domain, services) {
10382
10545
  }
10383
10546
  }
10384
10547
  function checkedModuleRecord(entries) {
10385
- const record5 = Object.fromEntries(entries);
10386
- const actual = Object.keys(record5).sort(compareCodeUnits2);
10548
+ const record7 = Object.fromEntries(entries);
10549
+ const actual = Object.keys(record7).sort(compareCodeUnits2);
10387
10550
  if (actual.length !== tileflowSemanticDomainRegistry.length || actual.some(
10388
10551
  (name, index) => name !== [...tileflowSemanticDomainRegistry.map(({ name: name2 }) => name2)].sort(compareCodeUnits2)[index]
10389
10552
  )) {
@@ -10391,7 +10554,7 @@ function checkedModuleRecord(entries) {
10391
10554
  "Tileflow semantic domain registry did not produce one value for every module."
10392
10555
  );
10393
10556
  }
10394
- return record5;
10557
+ return record7;
10395
10558
  }
10396
10559
  function assertRegistryIsValid() {
10397
10560
  const names = /* @__PURE__ */ new Set();
@@ -10502,12 +10665,12 @@ import { z as z3 } from "zod";
10502
10665
  // src/native-zod-object.ts
10503
10666
  import { z as z2 } from "zod";
10504
10667
  function strictNativeObject(shape) {
10505
- const object2 = new z2.core.$ZodObject({
10668
+ const object3 = new z2.core.$ZodObject({
10506
10669
  type: "object",
10507
10670
  shape,
10508
10671
  catchall: z2.never()
10509
10672
  });
10510
- return z2.lazy(() => object2);
10673
+ return z2.lazy(() => object3);
10511
10674
  }
10512
10675
 
10513
10676
  // src/manifest-schema-classic.ts
@@ -10994,7 +11157,7 @@ var defaultBuildingFloodLight = Object.freeze({
10994
11157
  });
10995
11158
  var defaultModelEmissionGradient = [0, 1, 1, 1, 0];
10996
11159
  function readBuildingFloodLight(value) {
10997
- if (!record(value)) return void 0;
11160
+ if (!record2(value)) return void 0;
10998
11161
  const defaults4 = defaultBuildingFloodLight;
10999
11162
  const radius = value.groundRadius;
11000
11163
  return {
@@ -11008,11 +11171,11 @@ function readBuildingFloodLight(value) {
11008
11171
  };
11009
11172
  }
11010
11173
  function readModelRoleLighting(value) {
11011
- if (!record(value)) return void 0;
11174
+ if (!record2(value)) return void 0;
11012
11175
  const result = {};
11013
11176
  for (const role of Object.keys(tileflowModelLightingRoles)) {
11014
11177
  const entry = value[role];
11015
- if (!record(entry)) continue;
11178
+ if (!record2(entry)) continue;
11016
11179
  const gradient = entry.emissiveHeight;
11017
11180
  result[role] = {
11018
11181
  emissiveStrength: bounded(entry.emissiveStrength, 0, 5, 0),
@@ -11035,7 +11198,7 @@ function readModelRoleLighting(value) {
11035
11198
  return Object.keys(result).length ? result : void 0;
11036
11199
  }
11037
11200
  function readRoleColorVariation(value) {
11038
- if (!record(value)) return {};
11201
+ if (!record2(value)) return {};
11039
11202
  for (const [key, max] of [
11040
11203
  ["hue", 360],
11041
11204
  ["saturation", 100],
@@ -11051,16 +11214,16 @@ function readRoleColorVariation(value) {
11051
11214
  function bounded(value, min, max, fallback) {
11052
11215
  return typeof value === "number" && Number.isFinite(value) && value >= min && value <= max ? value : fallback;
11053
11216
  }
11054
- function record(value) {
11217
+ function record2(value) {
11055
11218
  return typeof value === "object" && value !== null && !Array.isArray(value);
11056
11219
  }
11057
11220
 
11058
11221
  // src/buildings-3d-surfaces.ts
11059
11222
  function readTileflowBuildingSurfaces(value) {
11060
- if (!record2(value)) return void 0;
11223
+ if (!record3(value)) return void 0;
11061
11224
  const number = (value2, min, max, fallback) => typeof value2 === "number" && Number.isFinite(value2) && value2 >= min && value2 <= max ? value2 : fallback;
11062
11225
  const result = {};
11063
- if (record2(value.walls) && value.walls.kind === "brick") {
11226
+ if (record3(value.walls) && value.walls.kind === "brick") {
11064
11227
  const size = Array.isArray(value.walls.size) ? value.walls.size : [];
11065
11228
  const width = number(size[0], 0.5, 100, 6);
11066
11229
  const height = number(size[1], 0.5, 100, 3);
@@ -11074,7 +11237,7 @@ function readTileflowBuildingSurfaces(value) {
11074
11237
  contrast: number(value.walls.contrast, 0, 1, 0.2)
11075
11238
  };
11076
11239
  }
11077
- if (record2(value.roof) && value.roof.kind === "tiles") {
11240
+ if (record3(value.roof) && value.roof.kind === "tiles") {
11078
11241
  const size = number(value.roof.size, 0.5, 100, 8);
11079
11242
  result.roof = {
11080
11243
  kind: "tiles",
@@ -11083,14 +11246,14 @@ function readTileflowBuildingSurfaces(value) {
11083
11246
  contrast: number(value.roof.contrast, 0, 1, 0.1)
11084
11247
  };
11085
11248
  }
11086
- if (record2(value.cornice))
11249
+ if (record3(value.cornice))
11087
11250
  result.cornice = {
11088
11251
  height: number(value.cornice.height, 0.05, 5, 0.6),
11089
11252
  contrast: number(value.cornice.contrast, 0, 1, 0.2)
11090
11253
  };
11091
11254
  return Object.keys(result).length ? result : void 0;
11092
11255
  }
11093
- function record2(value) {
11256
+ function record3(value) {
11094
11257
  return value !== null && typeof value === "object" && !Array.isArray(value);
11095
11258
  }
11096
11259
 
@@ -11117,8 +11280,9 @@ function tileflowBuildingsMetadata(design) {
11117
11280
  return { version: 1, ...design };
11118
11281
  }
11119
11282
  function readTileflowBuildingsEntrance(value) {
11283
+ if (value === false) return { entrance: false };
11120
11284
  if (!isRecord5(value)) return {};
11121
- const band = (entry) => Array.isArray(entry) && entry.length === 2 && entry.every((zoom3) => typeof zoom3 === "number" && zoom3 >= 0 && zoom3 <= 24) && entry[0] < entry[1] ? [entry[0], entry[1]] : void 0;
11285
+ const band = (entry) => Array.isArray(entry) && entry.length === 2 && entry.every((zoom3) => typeof zoom3 === "number" && zoom3 >= 0 && zoom3 <= 24) && entry[0] <= entry[1] ? [entry[0], entry[1]] : void 0;
11122
11286
  const buildings2 = band(value.buildings);
11123
11287
  const models = band(value.models);
11124
11288
  return buildings2 || models ? { entrance: { ...buildings2 ? { buildings: buildings2 } : {}, ...models ? { models } : {} } } : {};
@@ -11127,6 +11291,9 @@ function isRecord5(value) {
11127
11291
  return typeof value === "object" && value !== null && !Array.isArray(value);
11128
11292
  }
11129
11293
 
11294
+ // src/camera-limits.ts
11295
+ var tileflowMaxPitch = 85;
11296
+
11130
11297
  // src/camera.ts
11131
11298
  var tileflowCameraMetadataKey = "tileflow:camera";
11132
11299
 
@@ -11269,11 +11436,11 @@ function collectFieldAccesses(value, fields2, dynamicAccesses, path) {
11269
11436
  return;
11270
11437
  }
11271
11438
  if (!value || typeof value !== "object") return;
11272
- const record5 = value;
11273
- if (typeof record5.property === "string" && Array.isArray(record5.stops)) {
11274
- fields2.set(record5.property, fields2.get(record5.property));
11439
+ const record7 = value;
11440
+ if (typeof record7.property === "string" && Array.isArray(record7.stops)) {
11441
+ fields2.set(record7.property, fields2.get(record7.property));
11275
11442
  }
11276
- for (const [key, child] of Object.entries(record5)) {
11443
+ for (const [key, child] of Object.entries(record7)) {
11277
11444
  collectFieldAccesses(child, fields2, dynamicAccesses, `${path}.${key}`);
11278
11445
  }
11279
11446
  }
@@ -11408,9 +11575,6 @@ function parsePathGroundRequest(value) {
11408
11575
  import { Color as Color3 } from "@maplibre/maplibre-gl-style-spec";
11409
11576
  import { z as z5 } from "zod";
11410
11577
 
11411
- // src/camera-limits.ts
11412
- var tileflowMaxPitch = 85;
11413
-
11414
11578
  // src/cartography/data-expression.ts
11415
11579
  var tileflowDataExpressionLimits = Object.freeze({
11416
11580
  maxBranches: 16,
@@ -11676,7 +11840,7 @@ function visitExpression(value, path, variables, issues) {
11676
11840
  const branchLimit = tileflowDataExpressionLimits.maxBranches;
11677
11841
  const operandLimit = tileflowDataExpressionLimits.maxOperands;
11678
11842
  const stopLimit = tileflowDataExpressionLimits.maxStops;
11679
- const visit = (entry, index, scope = variables) => visitExpressionNode(entry, [...path, index], scope, issues);
11843
+ const visit2 = (entry, index, scope = variables) => visitExpressionNode(entry, [...path, index], scope, issues);
11680
11844
  const exact = (length) => {
11681
11845
  if (value.length === length) return true;
11682
11846
  addExpressionIssue(
@@ -11738,18 +11902,18 @@ function visitExpression(value, path, variables, issues) {
11738
11902
  if (!exact(4)) return;
11739
11903
  if (!isNonEmptyString(value[1])) {
11740
11904
  addExpressionIssue(issues, [...path, 1], "Variable names must be non-empty strings.");
11741
- visit(value[2], 2);
11742
- visit(value[3], 3);
11905
+ visit2(value[2], 2);
11906
+ visit2(value[3], 3);
11743
11907
  return;
11744
11908
  }
11745
- visit(value[2], 2);
11746
- visit(value[3], 3, /* @__PURE__ */ new Set([...variables, value[1]]));
11909
+ visit2(value[2], 2);
11910
+ visit2(value[3], 3, /* @__PURE__ */ new Set([...variables, value[1]]));
11747
11911
  return;
11748
11912
  }
11749
11913
  case "abs":
11750
11914
  case "!":
11751
11915
  case "to-string": {
11752
- if (exact(2)) visit(value[1], 1);
11916
+ if (exact(2)) visit2(value[1], 1);
11753
11917
  return;
11754
11918
  }
11755
11919
  case "boolean":
@@ -11758,7 +11922,7 @@ function visitExpression(value, path, variables, issues) {
11758
11922
  addExpressionIssue(issues, path, `Expression ${operator} expects one or two arguments.`);
11759
11923
  return;
11760
11924
  }
11761
- for (let index = 1; index < value.length; index += 1) visit(value[index], index);
11925
+ for (let index = 1; index < value.length; index += 1) visit2(value[index], index);
11762
11926
  return;
11763
11927
  }
11764
11928
  case "-":
@@ -11771,8 +11935,8 @@ function visitExpression(value, path, variables, issues) {
11771
11935
  case ">":
11772
11936
  case ">=": {
11773
11937
  if (!exact(3)) return;
11774
- visit(value[1], 1);
11775
- visit(value[2], 2);
11938
+ visit2(value[1], 1);
11939
+ visit2(value[2], 2);
11776
11940
  return;
11777
11941
  }
11778
11942
  case "+":
@@ -11790,7 +11954,7 @@ function visitExpression(value, path, variables, issues) {
11790
11954
  );
11791
11955
  return;
11792
11956
  }
11793
- for (let index = 1; index < value.length; index += 1) visit(value[index], index);
11957
+ for (let index = 1; index < value.length; index += 1) visit2(value[index], index);
11794
11958
  return;
11795
11959
  }
11796
11960
  case "all":
@@ -11804,7 +11968,7 @@ function visitExpression(value, path, variables, issues) {
11804
11968
  );
11805
11969
  return;
11806
11970
  }
11807
- for (let index = 1; index < value.length; index += 1) visit(value[index], index);
11971
+ for (let index = 1; index < value.length; index += 1) visit2(value[index], index);
11808
11972
  return;
11809
11973
  }
11810
11974
  case "case": {
@@ -11824,7 +11988,7 @@ function visitExpression(value, path, variables, issues) {
11824
11988
  );
11825
11989
  return;
11826
11990
  }
11827
- for (let index = 1; index < value.length; index += 1) visit(value[index], index);
11991
+ for (let index = 1; index < value.length; index += 1) visit2(value[index], index);
11828
11992
  return;
11829
11993
  }
11830
11994
  case "match": {
@@ -11844,10 +12008,10 @@ function visitExpression(value, path, variables, issues) {
11844
12008
  );
11845
12009
  return;
11846
12010
  }
11847
- visit(value[1], 1);
12011
+ visit2(value[1], 1);
11848
12012
  validateSerializedMatchLabels(value, path, issues);
11849
- for (let index = 3; index < value.length; index += 2) visit(value[index], index);
11850
- visit(value.at(-1), value.length - 1);
12013
+ for (let index = 3; index < value.length; index += 2) visit2(value[index], index);
12014
+ visit2(value.at(-1), value.length - 1);
11851
12015
  return;
11852
12016
  }
11853
12017
  case "step": {
@@ -11863,10 +12027,10 @@ function visitExpression(value, path, variables, issues) {
11863
12027
  addExpressionIssue(issues, path, `Expression step exceeds ${stopLimit} stops.`);
11864
12028
  return;
11865
12029
  }
11866
- visit(value[1], 1);
11867
- visit(value[2], 2);
12030
+ visit2(value[1], 1);
12031
+ visit2(value[2], 2);
11868
12032
  validateSerializedStops(value, 3, path, issues);
11869
- for (let index = 4; index < value.length; index += 2) visit(value[index], index);
12033
+ for (let index = 4; index < value.length; index += 2) visit2(value[index], index);
11870
12034
  return;
11871
12035
  }
11872
12036
  case "interpolate": {
@@ -11883,9 +12047,9 @@ function visitExpression(value, path, variables, issues) {
11883
12047
  return;
11884
12048
  }
11885
12049
  validateInterpolationArray(value[1], [...path, 1], issues);
11886
- visit(value[2], 2);
12050
+ visit2(value[2], 2);
11887
12051
  validateSerializedStops(value, 3, path, issues);
11888
- for (let index = 4; index < value.length; index += 2) visit(value[index], index);
12052
+ for (let index = 4; index < value.length; index += 2) visit2(value[index], index);
11889
12053
  return;
11890
12054
  }
11891
12055
  default:
@@ -12020,6 +12184,260 @@ var expr = Object.freeze({
12020
12184
  zoom: zoom2
12021
12185
  });
12022
12186
 
12187
+ // src/cartography/expression-compaction.ts
12188
+ function compactTileflowExpression(value, scope = /* @__PURE__ */ new Map()) {
12189
+ if (isColorProgramMarker(value)) return value;
12190
+ if (!isExpression(value) || value[0] === "literal" || value[0] === "var") return value;
12191
+ if (value[0] === "let") return compactLet(value, scope);
12192
+ const node2 = value.map((entry, index) => {
12193
+ if (value[0] === "match" && index >= 2 && index < value.length - 1 && index % 2 === 0)
12194
+ return entry;
12195
+ if (index === 0 || value[0] === "interpolate" && index === 1) return entry;
12196
+ return compactTileflowExpression(entry, scope);
12197
+ });
12198
+ if (node2[0] === "all" || node2[0] === "any") {
12199
+ const operands = node2.slice(1).flatMap(
12200
+ (entry) => Array.isArray(entry) && entry[0] === node2[0] ? entry.slice(1) : [entry]
12201
+ );
12202
+ return [node2[0], ...operands];
12203
+ }
12204
+ if (node2[0] === "case") {
12205
+ while (node2.length >= 4 && equal(node2.at(-2), node2.at(-1)) && totalBoolean(node2.at(-3), scope))
12206
+ node2.splice(node2.length - 3, 2);
12207
+ return node2.length === 2 ? node2[1] : compactClassDispatch(node2, scope);
12208
+ }
12209
+ if (node2[0] === "match" && totalValue(node2[1], scope)) {
12210
+ for (let index = node2.length - 3; index >= 2; index -= 2)
12211
+ if (equal(node2[index + 1], node2.at(-1))) node2.splice(index, 2);
12212
+ return node2.length === 3 ? node2[2] : node2;
12213
+ }
12214
+ if (node2[0] === "*" && node2.slice(1).every((entry) => numeric(entry, scope))) {
12215
+ const operands = node2.slice(1).filter((entry) => entry !== 1);
12216
+ return operands.length === 0 ? 1 : operands.length === 1 ? operands[0] : ["*", ...operands];
12217
+ }
12218
+ if (node2[0] === "interpolate" && numeric(node2[2], scope)) {
12219
+ const outputs2 = node2.filter((_, index) => index >= 4 && index % 2 === 0);
12220
+ if (outputs2.length > 0 && outputs2.every((entry) => equal(entry, outputs2[0]))) return outputs2[0];
12221
+ if (typeof node2[2] === "number" && outputs2.every((output) => typeof output === "number" && Number.isFinite(output))) {
12222
+ for (let index = 3; index < node2.length - 1; index += 2)
12223
+ if (node2[index] === node2[2]) return node2[index + 1];
12224
+ }
12225
+ }
12226
+ return node2;
12227
+ }
12228
+ function compactClassDispatch(node2, scope) {
12229
+ if (node2.length < 6) return node2;
12230
+ const branches = [];
12231
+ const labels2 = /* @__PURE__ */ new Set();
12232
+ let labelType;
12233
+ for (let index = 1; index < node2.length - 1; index += 2) {
12234
+ const condition = node2[index];
12235
+ const parts = Array.isArray(condition) && condition[0] === "all" ? condition.slice(1) : [condition];
12236
+ const at = parts.findIndex(
12237
+ (part) => Array.isArray(part) && part[0] === "match" && part.length === 5 && part[3] === true && part[4] === false
12238
+ );
12239
+ if (at < 0 || !parts.every((part) => totalBoolean(part, scope))) return node2;
12240
+ const match4 = parts[at];
12241
+ const ownLabels = Array.isArray(match4[2]) ? match4[2] : [match4[2]];
12242
+ if (ownLabels.length === 0) return node2;
12243
+ for (const label of ownLabels) {
12244
+ labelType ??= typeof label;
12245
+ if (!["number", "string"].includes(labelType) || typeof label !== labelType) return node2;
12246
+ const key = JSON.stringify(label);
12247
+ if (labels2.has(key)) return node2;
12248
+ labels2.add(key);
12249
+ }
12250
+ branches.push({
12251
+ input: match4[1],
12252
+ labels: ownLabels,
12253
+ guards: parts.filter((_, i) => i !== at),
12254
+ output: node2[index + 1]
12255
+ });
12256
+ }
12257
+ const first = branches[0];
12258
+ if (!branches.every(
12259
+ (branch) => equal(branch.input, first.input) && equal(branch.guards, first.guards)
12260
+ ))
12261
+ return node2;
12262
+ const fallback = node2.at(-1);
12263
+ const match3 = [
12264
+ "match",
12265
+ first.input,
12266
+ ...branches.flatMap((branch) => [branch.labels, branch.output]),
12267
+ fallback
12268
+ ];
12269
+ const result = first.guards.length ? [
12270
+ "case",
12271
+ first.guards.length === 1 ? first.guards[0] : ["all", ...first.guards],
12272
+ match3,
12273
+ fallback
12274
+ ] : match3;
12275
+ return JSON.stringify(result).length < JSON.stringify(node2).length ? result : node2;
12276
+ }
12277
+ function compactLet(node2, scope) {
12278
+ const inner = new Map(scope);
12279
+ const bindings = [];
12280
+ for (let index = 1; index < node2.length - 1; index += 2) {
12281
+ const name = node2[index];
12282
+ if (typeof name !== "string") return node2;
12283
+ const value = compactTileflowExpression(node2[index + 1], scope);
12284
+ bindings.push(name, value);
12285
+ inner.set(name, { value, scope });
12286
+ }
12287
+ const result = compactTileflowExpression(node2.at(-1), inner);
12288
+ if (Array.isArray(result) && result[0] === "var" && bindings.includes(result[1])) {
12289
+ for (let index = bindings.length - 2; index >= 0; index -= 2)
12290
+ if (bindings[index] === result[1]) return bindings[index + 1];
12291
+ }
12292
+ const retained = [];
12293
+ for (let index = 0; index < bindings.length; index += 2)
12294
+ if (usesVariable(result, bindings[index]))
12295
+ retained.push(bindings[index], bindings[index + 1]);
12296
+ return retained.length ? ["let", ...retained, result] : result;
12297
+ }
12298
+ function usesVariable(value, name) {
12299
+ if (!Array.isArray(value) || value[0] === "literal") return false;
12300
+ if (value[0] === "var") return value[1] === name;
12301
+ return value.some((entry) => usesVariable(entry, name));
12302
+ }
12303
+ function numeric(value, scope) {
12304
+ if (typeof value === "number") return Number.isFinite(value);
12305
+ if (!Array.isArray(value)) return false;
12306
+ if (value[0] === "zoom") return value.length === 1;
12307
+ if (value[0] === "var" && typeof value[1] === "string") {
12308
+ const binding = scope.get(value[1]);
12309
+ return binding !== void 0 && numeric(binding.value, binding.scope);
12310
+ }
12311
+ if (value[0] === "*" || value[0] === "+" || value[0] === "-" || value[0] === "max" || value[0] === "min")
12312
+ return value.length > 1 && value.slice(1).every((entry) => numeric(entry, scope));
12313
+ if (value[0] === "case")
12314
+ return value.slice(1, -1).every(
12315
+ (entry, index) => index % 2 === 0 ? totalBoolean(entry, scope) : numeric(entry, scope)
12316
+ ) && numeric(value.at(-1), scope);
12317
+ if (value[0] === "interpolate")
12318
+ return numeric(value[2], scope) && value.filter((_, index) => index >= 4 && index % 2 === 0).every((entry) => numeric(entry, scope));
12319
+ return false;
12320
+ }
12321
+ function totalBoolean(value, scope) {
12322
+ if (typeof value === "boolean") return true;
12323
+ if (!Array.isArray(value)) return false;
12324
+ if (value[0] === "has") return value.length === 2 && typeof value[1] === "string";
12325
+ if (value[0] === "!" && value.length === 2) return totalBoolean(value[1], scope);
12326
+ if (value[0] === "all" || value[0] === "any")
12327
+ return value.slice(1).every((entry) => totalBoolean(entry, scope));
12328
+ if ((value[0] === "==" || value[0] === "!=") && value.length === 3)
12329
+ return totalValue(value[1], scope) && totalValue(value[2], scope);
12330
+ if (value[0] === "match")
12331
+ return totalValue(value[1], scope) && value.filter((_, index) => index >= 3 && index % 2 === 1).every((entry) => totalBoolean(entry, scope)) && totalBoolean(value.at(-1), scope);
12332
+ return false;
12333
+ }
12334
+ function totalValue(value, scope) {
12335
+ if (value === null || ["string", "boolean", "number"].includes(typeof value)) return true;
12336
+ if (!Array.isArray(value)) return false;
12337
+ if (value[0] === "literal") return true;
12338
+ if (value[0] === "get" || value[0] === "has")
12339
+ return value.length === 2 && typeof value[1] === "string";
12340
+ if (value[0] === "geometry-type" || value[0] === "id") return value.length === 1;
12341
+ if (value[0] === "to-string") return value.length === 2 && totalValue(value[1], scope);
12342
+ return numeric(value, scope) || totalBoolean(value, scope);
12343
+ }
12344
+ function isExpression(value) {
12345
+ return Array.isArray(value) && isMapLibreExpressionOperator(value[0]);
12346
+ }
12347
+ function equal(left, right) {
12348
+ return JSON.stringify(left) === JSON.stringify(right);
12349
+ }
12350
+ function shareExpressions(value) {
12351
+ if (!isExpression(value)) return value;
12352
+ let result = value;
12353
+ const bindings = [];
12354
+ const serialized = /* @__PURE__ */ new WeakMap();
12355
+ const textOf = (node2) => {
12356
+ let text = serialized.get(node2);
12357
+ if (text === void 0) {
12358
+ text = JSON.stringify(node2);
12359
+ serialized.set(node2, text);
12360
+ }
12361
+ return text;
12362
+ };
12363
+ const originalText = textOf(value);
12364
+ for (let index = 0; index < 16; index += 1) {
12365
+ let name = `__tf${index}`;
12366
+ while (originalText.includes(JSON.stringify(name))) name += "_";
12367
+ const candidates = /* @__PURE__ */ new Map();
12368
+ visit(result, (node2) => {
12369
+ if (textOf(node2).length < 48 || !selfContained(node2) || !(totalBoolean(node2, /* @__PURE__ */ new Map()) || numeric(node2, /* @__PURE__ */ new Map()) || stringOutput(node2)))
12370
+ return node2;
12371
+ const key = textOf(node2);
12372
+ const entry = candidates.get(key);
12373
+ if (entry) entry.count += 1;
12374
+ else candidates.set(key, { node: node2, count: 1 });
12375
+ return node2;
12376
+ });
12377
+ const reference = ["var", name];
12378
+ const referenceLength = JSON.stringify(reference).length;
12379
+ const best = [...candidates.entries()].map(([key, entry]) => ({
12380
+ key,
12381
+ ...entry,
12382
+ saving: (entry.count - 1) * key.length - entry.count * referenceLength - name.length - 8
12383
+ })).filter(({ count, saving }) => count > 1 && saving > 0).sort((a, b) => b.saving - a.saving || (a.key < b.key ? -1 : a.key > b.key ? 1 : 0))[0];
12384
+ if (!best) break;
12385
+ bindings.push(name, best.node);
12386
+ result = visit(result, (node2) => textOf(node2) === best.key ? reference : node2);
12387
+ }
12388
+ if (!bindings.length) return value;
12389
+ const shared = ["let", ...bindings, result];
12390
+ return JSON.stringify(shared).length < originalText.length ? shared : value;
12391
+ }
12392
+ function stringOutput(value) {
12393
+ if (typeof value === "string") return true;
12394
+ if (!Array.isArray(value)) return false;
12395
+ if (value[0] === "case")
12396
+ return value.slice(1, -1).every(
12397
+ (entry, index) => index % 2 === 0 ? totalBoolean(entry, /* @__PURE__ */ new Map()) : stringOutput(entry)
12398
+ ) && stringOutput(value.at(-1));
12399
+ if (value[0] === "match")
12400
+ return totalValue(value[1], /* @__PURE__ */ new Map()) && value.filter((_, index) => index >= 3 && index % 2 === 1).every(stringOutput) && stringOutput(value.at(-1));
12401
+ return false;
12402
+ }
12403
+ function selfContained(value) {
12404
+ if (!isExpression(value) || value[0] === "literal") return true;
12405
+ if (value[0] === "var" || value[0] === "let" || value[0] === "zoom") return false;
12406
+ return value.slice(1).every(selfContained);
12407
+ }
12408
+ function visit(value, transform) {
12409
+ if (!isExpression(value) || value[0] === "literal") return value;
12410
+ const replaced = transform(value);
12411
+ if (replaced !== value) return replaced;
12412
+ let changed = false;
12413
+ const children = value.map((entry, index) => {
12414
+ if (value[0] === "match" && index >= 2 && index < value.length - 1 && index % 2 === 0)
12415
+ return entry;
12416
+ const child = isExpression(entry) ? visit(entry, transform) : entry;
12417
+ changed ||= child !== entry;
12418
+ return child;
12419
+ });
12420
+ return changed ? children : value;
12421
+ }
12422
+ function compactTileflowLayer(layer) {
12423
+ const result = { ...layer };
12424
+ for (const group of ["paint", "layout"]) {
12425
+ const properties2 = layer[group];
12426
+ if (properties2 && typeof properties2 === "object" && !Array.isArray(properties2))
12427
+ Object.assign(result, {
12428
+ [group]: Object.fromEntries(
12429
+ Object.entries(properties2).map(([key, value]) => [
12430
+ key,
12431
+ // Asset discovery enumerates these outputs before resource preparation. Keep
12432
+ // their branches visible instead of hiding an image/font choice in a binding.
12433
+ /(?:image|pattern|font)$/u.test(key) ? compactTileflowExpression(value) : shareExpressions(compactTileflowExpression(value))
12434
+ ])
12435
+ )
12436
+ });
12437
+ }
12438
+ return result;
12439
+ }
12440
+
12023
12441
  // src/cartography/semantic-bindings.ts
12024
12442
  var completeSemanticDataContract = tileflowWorldV1Schema();
12025
12443
  var tileflowSemanticFieldNames = Object.freeze(
@@ -12222,7 +12640,7 @@ function materializeTileflowLayerFamilyIR(template, semantics) {
12222
12640
  }
12223
12641
  function lowerTileflowDomainIR(families, data, resolvePhysicalId = physicalLayerIdForSemanticKey) {
12224
12642
  const layers2 = families.map(
12225
- (family) => bindSemanticReferences(emitLayer(family, resolvePhysicalId), data)
12643
+ (family) => compactTileflowLayer(bindSemanticReferences(emitLayer(family, resolvePhysicalId), data))
12226
12644
  );
12227
12645
  assertNoSemanticReferences(layers2);
12228
12646
  const ids = /* @__PURE__ */ new Set();
@@ -12272,6 +12690,7 @@ function emitLayer(family, resolvePhysicalId) {
12272
12690
  function physicalLayerIdForSemanticKey(semanticKey) {
12273
12691
  const exact = exactPhysicalLayerIds[semanticKey];
12274
12692
  if (exact) return exact;
12693
+ if (semanticKey === "buildings.render.extrusion") return tileflowNativeBuildingsLayerId;
12275
12694
  let match3 = /^([A-Za-z0-9_-]+)\.render\.([A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*)$/u.exec(
12276
12695
  semanticKey
12277
12696
  );
@@ -12370,7 +12789,7 @@ function physicalLayerIdForSemanticKey(semanticKey) {
12370
12789
  var exactPhysicalLayerIds = Object.freeze({
12371
12790
  "addresses.labels": "tileflow-addresses-labels",
12372
12791
  "aeroways.runwayRef": "tileflow-aeroway-runway-ref",
12373
- "buildings.extrusion": "tileflow-buildings-3d",
12792
+ "buildings.extrusion": tileflowNativeBuildingsLayerId,
12374
12793
  "labels.aerodrome": "tileflow-label-aerodrome",
12375
12794
  "labels.junctions": "tileflow-label-road-junction",
12376
12795
  "land.background": "tileflow-background",
@@ -14545,7 +14964,7 @@ function boundedThemeNumberSchema(min, max) {
14545
14964
  }).strict()
14546
14965
  ]);
14547
14966
  }
14548
- var entranceBandSchema = z5.tuple([z5.number().finite().min(0).max(24), z5.number().finite().min(0).max(24)]).refine(([from, to]) => from < to, "A band starts at a lower zoom than it ends.");
14967
+ var entranceBandSchema = z5.tuple([z5.number().finite().min(0).max(24), z5.number().finite().min(0).max(24)]).refine(([from, to]) => from <= to, "A band cannot end before it starts.");
14549
14968
  var modelRoleLightingSchema = z5.object({
14550
14969
  emissiveStrength: boundedThemeNumberSchema(0, 5).optional(),
14551
14970
  emissiveVariation: boundedThemeNumberSchema(0, 1).optional(),
@@ -14605,7 +15024,7 @@ var buildingsModuleSchema = z5.object({
14605
15024
  lamp: modelRoleLightingSchema.optional(),
14606
15025
  logo: modelRoleLightingSchema.optional()
14607
15026
  }).strict().optional(),
14608
- entrance: z5.object({ buildings: entranceBandSchema.optional(), models: entranceBandSchema.optional() }).strict().optional(),
15027
+ entrance: z5.object({ buildings: entranceBandSchema.optional(), models: entranceBandSchema.optional() }).strict().or(z5.literal(false)).optional(),
14609
15028
  flatFootprints: z5.boolean().optional(),
14610
15029
  occlusion: z5.boolean().optional(),
14611
15030
  groundOcclusion: boundedThemeNumberSchema(0, 1).optional(),
@@ -15644,7 +16063,13 @@ var systemThemesSchema = z5.object({ dark: tileflowThemeNameSchema, light: tilef
15644
16063
  var resolvedTileflowMapSchema = z5.object({
15645
16064
  atmosphere: atmosphereSchema.optional(),
15646
16065
  boundaryAreas: boundaryAreasSchema.optional(),
15647
- camera: z5.object({ adaptivePerspective: z5.boolean().optional() }).strict().optional(),
16066
+ camera: z5.object({
16067
+ adaptivePerspective: z5.boolean().optional(),
16068
+ pitchLimit: z5.object({
16069
+ maxZoom: z5.number().finite().min(0).max(24),
16070
+ maxPitch: z5.number().finite().min(0).max(tileflowMaxPitch)
16071
+ }).strict().optional()
16072
+ }).strict().optional(),
15648
16073
  data: dataSchema.optional(),
15649
16074
  defaultTheme: tileflowThemeNameSchema,
15650
16075
  fonts: fontDirectoriesSchema.optional(),
@@ -15819,8 +16244,8 @@ function isSafeGlyphUrl(value) {
15819
16244
  function validateZoomRanges(value, context, path = []) {
15820
16245
  if (!value || typeof value !== "object") return;
15821
16246
  if (!Array.isArray(value)) {
15822
- const record5 = value;
15823
- if (typeof record5.minZoom === "number" && typeof record5.maxZoom === "number" && record5.minZoom > record5.maxZoom) {
16247
+ const record7 = value;
16248
+ if (typeof record7.minZoom === "number" && typeof record7.maxZoom === "number" && record7.minZoom > record7.maxZoom) {
15824
16249
  context.addIssue({
15825
16250
  code: "custom",
15826
16251
  path: [...path, "maxZoom"],
@@ -16472,7 +16897,7 @@ function applyTileflowIconCapabilities(style, icons) {
16472
16897
  "line-pattern",
16473
16898
  "background-pattern"
16474
16899
  ]) {
16475
- const pattern = record3(layer.paint)[property];
16900
+ const pattern = record4(layer.paint)[property];
16476
16901
  if (pattern !== void 0 && [...outputs(pattern, ids)].some((id2) => appearances[id2]?.representation === "sdf")) {
16477
16902
  throw Object.assign(new Error(`Layer "${layer.id}" cannot use an SDF icon as a pattern.`), {
16478
16903
  code: "TF_ICON_PATTERN_SDF",
@@ -16481,7 +16906,7 @@ function applyTileflowIconCapabilities(style, icons) {
16481
16906
  });
16482
16907
  }
16483
16908
  }
16484
- const image = record3(layer.layout)["icon-image"];
16909
+ const image = record4(layer.layout)["icon-image"];
16485
16910
  if (image === void 0) continue;
16486
16911
  if (typeof image === "string" && /\{[^{}]+\}/u.test(image) || typeof image !== "string" && !Array.isArray(image)) {
16487
16912
  throw Object.assign(new Error(`Layer "${layer.id}" has an unproven legacy icon selection.`), {
@@ -16503,7 +16928,7 @@ function applyTileflowIconCapabilities(style, icons) {
16503
16928
  }
16504
16929
  );
16505
16930
  }
16506
- const paint = { ...record3(layer.paint) };
16931
+ const paint = { ...record4(layer.paint) };
16507
16932
  for (const [property, limit] of [
16508
16933
  ["icon-halo-width", 2],
16509
16934
  ["icon-halo-blur", 1]
@@ -16533,7 +16958,7 @@ function applyTileflowIconCapabilities(style, icons) {
16533
16958
  layer.paint = paint;
16534
16959
  }
16535
16960
  }
16536
- function record3(value) {
16961
+ function record4(value) {
16537
16962
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
16538
16963
  }
16539
16964
  function outputs(value, ids) {
@@ -17390,6 +17815,15 @@ function combineConditionalValues(entries, fallback, minimumRelevantZoom) {
17390
17815
  if (allEqual(entries.map(({ value }) => value))) {
17391
17816
  return { ok: true, value: entries[0]?.value };
17392
17817
  }
17818
+ const atMinimumZoom = (value) => {
17819
+ if (!isZoomStep(value)) return value;
17820
+ const step2 = value;
17821
+ const lastStop = step2[step2.length - 2];
17822
+ return typeof lastStop === "number" && lastStop <= minimumRelevantZoom ? evaluateZoomStep(value, minimumRelevantZoom) : value;
17823
+ };
17824
+ entries = entries.map(({ condition, value }) => ({ condition, value: atMinimumZoom(value) }));
17825
+ fallback = atMinimumZoom(fallback);
17826
+ if (allEqual(entries.map(({ value }) => value))) return { ok: true, value: entries[0].value };
17393
17827
  const letValues = entries.map(
17394
17828
  ({ value }) => Array.isArray(value) && value.length === 4 && value[0] === "let" ? value : void 0
17395
17829
  );
@@ -17555,7 +17989,8 @@ function classMatchLabelsAreDisjoint(labels2) {
17555
17989
  return true;
17556
17990
  }
17557
17991
  function splitClassMatch(condition) {
17558
- const parts = condition[0] === "all" ? condition.slice(1) : [condition];
17992
+ const conjuncts = (value) => Array.isArray(value) && value[0] === "all" ? value.slice(1).flatMap(conjuncts) : [value];
17993
+ const parts = conjuncts(condition);
17559
17994
  const matchIndex = parts.findIndex(
17560
17995
  (part) => Array.isArray(part) && part.length === 5 && part[0] === "match" && Array.isArray(part[1]) && part[1][0] === "get" && part[1][1] !== void 0 && part.at(-2) === true && part.at(-1) === false
17561
17996
  );
@@ -17694,8 +18129,7 @@ function stableSerialize(value) {
17694
18129
 
17695
18130
  // src/cartography/scene-3d-metadata.ts
17696
18131
  var colourSources = [
17697
- ["tileflow-buildings-render-extrusion", "fill-extrusion-color"],
17698
- ["tileflow-buildings-3d", "fill-extrusion-color"],
18132
+ [tileflowNativeBuildingsLayerId, "fill-extrusion-color"],
17699
18133
  ["tileflow-buildings-fill", "fill-color"]
17700
18134
  ];
17701
18135
  function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, tileflowLandmarks) {
@@ -17718,7 +18152,7 @@ function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, t
17718
18152
  return typeof resolved === "number" ? resolved : fallback;
17719
18153
  };
17720
18154
  const edgeColor = style.edgeColor === void 0 ? void 0 : toMapLibreStyleValue(style.edgeColor);
17721
- const surfaces = isRecord10(style.surfaces) ? readTileflowBuildingSurfaces(
18155
+ const surfaces = isRecord10(style.surfaces) ? mapColorProgramsThrough(
17722
18156
  Object.fromEntries(
17723
18157
  Object.entries(style.surfaces).map(([key, entry]) => [
17724
18158
  key,
@@ -17729,7 +18163,8 @@ function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, t
17729
18163
  ])
17730
18164
  ) : entry
17731
18165
  ])
17732
- )
18166
+ ),
18167
+ readTileflowBuildingSurfaces
17733
18168
  ) : void 0;
17734
18169
  return {
17735
18170
  source: context.data.sourceId,
@@ -17743,18 +18178,21 @@ function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, t
17743
18178
  verticalGradient,
17744
18179
  edges: number(style.edges, 0),
17745
18180
  edgeRadius: number(style.edgeRadius, defaults4.edgeRadius),
17746
- ...typeof edgeColor === "string" ? { edgeColor } : {},
18181
+ ...typeof edgeColor === "string" || isColorProgramMarker(edgeColor) ? { edgeColor } : {},
17747
18182
  nightLight: number(style.nightLight, 0),
17748
18183
  ...isRecord10(style.floodLight) ? {
17749
- floodLight: readBuildingFloodLight({
17750
- ...style.floodLight,
17751
- intensity: number(style.floodLight.intensity, 0),
17752
- brightnessScale: number(style.floodLight.brightnessScale, 1),
17753
- color: style.floodLight.color === void 0 ? void 0 : toMapLibreStyleValue(style.floodLight.color)
17754
- })
18184
+ floodLight: mapColorProgramsThrough(
18185
+ {
18186
+ ...style.floodLight,
18187
+ intensity: number(style.floodLight.intensity, 0),
18188
+ brightnessScale: number(style.floodLight.brightnessScale, 1),
18189
+ color: style.floodLight.color === void 0 ? void 0 : toMapLibreStyleValue(style.floodLight.color)
18190
+ },
18191
+ readBuildingFloodLight
18192
+ )
17755
18193
  } : {},
17756
18194
  ...isRecord10(style.modelLighting) ? {
17757
- modelLighting: readModelRoleLighting(
18195
+ modelLighting: mapColorProgramsThrough(
17758
18196
  Object.fromEntries(
17759
18197
  Object.entries(style.modelLighting).filter(([, entry]) => isRecord10(entry)).map(([role, entry]) => {
17760
18198
  const own = entry;
@@ -17785,7 +18223,8 @@ function tileflowBuildings3DDesignOf(module, context, layers2, buildingModels, t
17785
18223
  }
17786
18224
  ];
17787
18225
  })
17788
- )
18226
+ ),
18227
+ readModelRoleLighting
17789
18228
  )
17790
18229
  } : {},
17791
18230
  windows: style.windows === true,
@@ -17839,7 +18278,8 @@ function modelColours(value) {
17839
18278
  const colours = {};
17840
18279
  for (const [part, colour] of Object.entries(value)) {
17841
18280
  const resolved = colour === void 0 ? void 0 : toMapLibreStyleValue(colour);
17842
- if (typeof resolved === "string") colours[part] = resolved;
18281
+ if (typeof resolved === "string" || isColorProgramMarker(resolved))
18282
+ colours[part] = resolved;
17843
18283
  }
17844
18284
  return Object.keys(colours).length > 0 ? { modelColors: colours } : {};
17845
18285
  }
@@ -17860,7 +18300,7 @@ function tileflowStreetLightsDesignOf(module, context) {
17860
18300
  ...layers2.sidewalk ? { sidewalk: layers2.sidewalk } : {}
17861
18301
  },
17862
18302
  estimate: estimate === "streets" || estimate === "none" || estimate === "lit" ? estimate : "lit",
17863
- color: typeof color2 === "string" ? color2 : tileflowDefaultStreetLightsDesign.color,
18303
+ color: typeof color2 === "string" || isColorProgramMarker(color2) ? color2 : tileflowDefaultStreetLightsDesign.color,
17864
18304
  intensity: typeof intensity === "number" ? intensity : 1
17865
18305
  };
17866
18306
  }
@@ -17908,7 +18348,7 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
17908
18348
  "theme",
17909
18349
  () => resolveThemeValues(themeableConfig, selected.theme, `map.${input.id}`)
17910
18350
  );
17911
- const config = runCompilationPhase(
18351
+ let config = runCompilationPhase(
17912
18352
  "config-validation",
17913
18353
  () => parseResolvedTileflowMap({
17914
18354
  ...resolvedThemeConfig,
@@ -17916,6 +18356,11 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
17916
18356
  themes: input.themes
17917
18357
  })
17918
18358
  );
18359
+ if (options.runtimeColors)
18360
+ config = {
18361
+ ...config,
18362
+ modules: preserveThemeColorPrograms(input.modules, config.modules, selected.theme)
18363
+ };
17919
18364
  const apiBaseUrl = normalizeBaseUrl(options.apiBaseUrl);
17920
18365
  const data = runCompilationPhase("data", () => resolveTileflowData(config.data, { apiBaseUrl }));
17921
18366
  const marine = runCompilationPhase("data", () => resolveMarine(config.marine, apiBaseUrl));
@@ -18008,7 +18453,8 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
18008
18453
  "theme",
18009
18454
  () => resolveThemeColors(
18010
18455
  selected.theme,
18011
- onReport ? (decision) => colorDecisions.push(decision) : void 0
18456
+ onReport ? (decision) => colorDecisions.push(decision) : void 0,
18457
+ options.runtimeColors
18012
18458
  )
18013
18459
  );
18014
18460
  const images = runCompilationPhase("theme", () => resolveThemeImages(selected.theme));
@@ -18310,7 +18756,7 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
18310
18756
  marine?.nautical?.sourceId,
18311
18757
  tileflowNauticalV1Schema.layers.lights
18312
18758
  );
18313
- const style = {
18759
+ let style = {
18314
18760
  ...sky ? { sky: compileTileflowStreetSky(sky, Boolean(atmosphere)) } : atmosphere ? { sky: compileTileflowSky(atmosphere) } : {},
18315
18761
  version: 8,
18316
18762
  name: config.name ?? "Streets",
@@ -18366,16 +18812,73 @@ function compileSemanticStyleInternal(input, options, inspect, onReport) {
18366
18812
  [tileflowOverlayPlacementManifestMetadataKey]: overlayPlacementManifest,
18367
18813
  "tileflow:modules": Object.entries(modules).filter(([, module]) => !isRecord11(module) || module.enabled !== false).map(([name]) => name).sort(),
18368
18814
  ...config.view ? { "tileflow:view": config.view } : {},
18369
- ...config.camera?.adaptivePerspective !== void 0 ? {
18815
+ ...config.camera?.adaptivePerspective !== void 0 || config.camera?.pitchLimit !== void 0 ? {
18370
18816
  [tileflowCameraMetadataKey]: {
18371
18817
  version: 1,
18372
- adaptivePerspective: config.camera.adaptivePerspective
18818
+ ...config.camera
18373
18819
  }
18374
18820
  } : {},
18375
18821
  ...notices.length > 0 ? { [tileflowNoticesMetadataKey]: notices } : {},
18376
18822
  ...nauticalLights ? { [tileflowNauticalLightsMetadataKey]: nauticalLights } : {}
18377
18823
  }
18378
18824
  };
18825
+ if (options.runtimeColors) {
18826
+ const theme = selected.theme;
18827
+ if (style.light)
18828
+ style.light = preserveThemeColorPrograms(
18829
+ { color: theme.lighting.sun?.color ?? theme.lighting.color },
18830
+ style.light,
18831
+ theme,
18832
+ true
18833
+ );
18834
+ const authoredSky = typeof input.sky === "object" ? input.sky : void 0;
18835
+ if (style.sky && authoredSky)
18836
+ style.sky = preserveThemeColorPrograms(
18837
+ {
18838
+ "sky-color": authoredSky.color,
18839
+ "horizon-color": authoredSky.horizonColor,
18840
+ "fog-color": authoredSky.horizonColor
18841
+ },
18842
+ style.sky,
18843
+ theme,
18844
+ true
18845
+ );
18846
+ if (style.metadata) {
18847
+ style.metadata["tileflow:sun"] = preserveThemeColorPrograms(
18848
+ { color: theme.lighting.sun?.color, ambient: { color: theme.lighting.ambient?.color } },
18849
+ style.metadata["tileflow:sun"],
18850
+ theme,
18851
+ true
18852
+ );
18853
+ if (authoredSky)
18854
+ style.metadata["tileflow:sky"] = preserveThemeColorPrograms(
18855
+ {
18856
+ glowColor: authoredSky.glowColor,
18857
+ timesOfDay: Object.fromEntries(
18858
+ ["day", "night", "dawn", "dusk"].map((moment) => [moment, authoredSky])
18859
+ )
18860
+ },
18861
+ style.metadata["tileflow:sky"],
18862
+ theme,
18863
+ true
18864
+ );
18865
+ const route = Object.fromEntries(
18866
+ ["line", "casing", "core", "glow", "alternative"].filter((key) => Object.hasOwn(theme.tokens.color, `route.${key}`)).map((key) => [key, { kind: "theme-token", category: "color", token: `route.${key}` }])
18867
+ );
18868
+ style.metadata["tileflow:route"] = preserveThemeColorPrograms(
18869
+ route,
18870
+ style.metadata["tileflow:route"],
18871
+ theme,
18872
+ true
18873
+ );
18874
+ for (const key of Object.keys(style.metadata))
18875
+ if (style.metadata[key] === void 0) delete style.metadata[key];
18876
+ }
18877
+ style = extractTileflowColorPrograms(style, {
18878
+ color: theme.tokens.color,
18879
+ number: theme.tokens.number
18880
+ });
18881
+ }
18379
18882
  const report = {
18380
18883
  ...partialReport,
18381
18884
  ...onReport ? {
@@ -18657,8 +19160,8 @@ function runCompilationPhase(phase, operation) {
18657
19160
  }
18658
19161
  }
18659
19162
  function withCompilationPhase(error, phase) {
18660
- const record5 = isRecord11(error) ? error : {};
18661
- if (typeof record5.phase === "string") return error;
19163
+ const record7 = isRecord11(error) ? error : {};
19164
+ if (typeof record7.phase === "string") return error;
18662
19165
  if (error instanceof Error && Object.isExtensible(error)) {
18663
19166
  Object.defineProperty(error, "phase", { configurable: true, enumerable: true, value: phase });
18664
19167
  return error;
@@ -18674,7 +19177,7 @@ function withCompilationPhase(error, phase) {
18674
19177
  "severity",
18675
19178
  "target"
18676
19179
  ]) {
18677
- if (record5[key] !== void 0) Object.assign(wrapped, { [key]: record5[key] });
19180
+ if (record7[key] !== void 0) Object.assign(wrapped, { [key]: record7[key] });
18678
19181
  }
18679
19182
  Object.assign(wrapped, { phase });
18680
19183
  return wrapped;
@@ -18781,6 +19284,7 @@ function createTileflowRuntimeManifestSchema(primitives, operations, identities
18781
19284
  apiUrl: operations.optional(apiOriginSchema),
18782
19285
  defaultTheme: tileflowThemeNameSchema2,
18783
19286
  environment: operations.optional(operations.maxLength(safeTextSchema, 128)),
19287
+ familyUrl: operations.optional(publicResourceUrlSchema),
18784
19288
  mapId: operations.optional(operations.maxLength(safeTextSchema, 128)),
18785
19289
  systemThemes: operations.optional(
18786
19290
  operations.object({
@@ -18862,11 +19366,20 @@ function createTileflowRuntimeManifestSchema(primitives, operations, identities
18862
19366
  maps: operations.record(tileflowPortableIdSchema2, manifestMapSchema),
18863
19367
  version: operations.union(
18864
19368
  operations.literal(tileflowRuntimeManifestVersion),
18865
- operations.literal(2)
19369
+ operations.union(operations.literal(2), operations.literal(3))
18866
19370
  )
18867
19371
  }),
18868
19372
  (manifest, context) => {
18869
19373
  const mapCount = Object.keys(manifest.maps).length;
19374
+ if (manifest.version !== 3)
19375
+ for (const [name, map] of Object.entries(manifest.maps)) {
19376
+ if (map.familyUrl !== void 0)
19377
+ context.addIssue({
19378
+ code: "custom",
19379
+ message: "familyUrl requires manifest version 3",
19380
+ path: ["maps", name, "familyUrl"]
19381
+ });
19382
+ }
18870
19383
  if (manifest.version === 1) {
18871
19384
  for (const [name, map] of Object.entries(manifest.maps)) {
18872
19385
  if (map.timeOfDay !== void 0)
@@ -19706,6 +20219,473 @@ function isPlainRecord5(value) {
19706
20219
  return prototype === Object.prototype || prototype === null;
19707
20220
  }
19708
20221
 
20222
+ // src/style-family.ts
20223
+ var tileflowStyleFamilyLimits = Object.freeze({
20224
+ themes: 64,
20225
+ documentBytes: 16 * 1024 * 1024,
20226
+ expandedStyleBytes: 8 * 1024 * 1024,
20227
+ expandedFamilyBytes: 32 * 1024 * 1024,
20228
+ layers: 4096,
20229
+ nodes: 5e5,
20230
+ depth: 96
20231
+ });
20232
+ function createTileflowStyleFamily(styles) {
20233
+ const themes = Object.keys(styles).sort();
20234
+ if (!themes.length || themes.length > tileflowStyleFamilyLimits.themes || themes.some((name) => !isTileflowThemeName(name)))
20235
+ fail("Invalid theme names.");
20236
+ const parameters = [];
20237
+ const columns = /* @__PURE__ */ new Map();
20238
+ const add2 = (values) => {
20239
+ const key = serializeCanonicalJson(values);
20240
+ let index = columns.get(key);
20241
+ if (index === void 0) {
20242
+ index = parameters.length;
20243
+ columns.set(key, index);
20244
+ parameters.push(values);
20245
+ }
20246
+ return { $variant: index };
20247
+ };
20248
+ const factor = (values) => {
20249
+ const first = values[0];
20250
+ if (values.some((value) => isReference(value) || isConstantReference(value)))
20251
+ return add2(values);
20252
+ if (Array.isArray(first) && values.every((v) => Array.isArray(v) && v.length === first.length))
20253
+ return first.map((_, index) => factor(values.map((v) => v[index])));
20254
+ if (record5(first) && values.every((v) => record5(v) && same(Object.keys(v).sort(), Object.keys(first).sort())))
20255
+ return Object.fromEntries(
20256
+ Object.keys(first).sort().map((key) => [key, factor(values.map((v) => v[key]))])
20257
+ );
20258
+ if (values.every((v) => same(v, first))) return first;
20259
+ return add2(values);
20260
+ };
20261
+ const documents = themes.map((name) => JSON.parse(serializeCanonicalJson(styles[name])));
20262
+ for (const document of documents) assertStyle(document);
20263
+ const template = factor(documents);
20264
+ const packed = shareConstants(template);
20265
+ const family = {
20266
+ kind: "tileflow-style-family",
20267
+ version: 1,
20268
+ themes,
20269
+ template: packed.template,
20270
+ constants: packed.constants,
20271
+ parameters,
20272
+ updates: planUpdates(template)
20273
+ };
20274
+ return parseTileflowStyleFamily(family);
20275
+ }
20276
+ function parseTileflowStyleFamily(input) {
20277
+ assertJsonBudget(input);
20278
+ if (!record5(input) || !same(Object.keys(input).sort(), [
20279
+ "constants",
20280
+ "kind",
20281
+ "parameters",
20282
+ "template",
20283
+ "themes",
20284
+ "updates",
20285
+ "version"
20286
+ ]) || input.kind !== "tileflow-style-family" || input.version !== 1 || !Array.isArray(input.themes) || !input.themes.length || input.themes.length > tileflowStyleFamilyLimits.themes || input.themes.some((name) => typeof name !== "string" || !isTileflowThemeName(name)) || new Set(input.themes).size !== input.themes.length || !Array.isArray(input.parameters) || !Array.isArray(input.constants))
20287
+ fail("Invalid family document.");
20288
+ const family = input;
20289
+ if (bytes(input) > tileflowStyleFamilyLimits.documentBytes) fail("Family document is too large.");
20290
+ for (const values of family.parameters)
20291
+ if (!Array.isArray(values) || values.length !== family.themes.length)
20292
+ fail("Invalid parameter column.");
20293
+ walk(family.template, (value) => {
20294
+ if (isReference(value) && (!Number.isSafeInteger(value.$variant) || value.$variant < 0 || value.$variant >= family.parameters.length))
20295
+ fail("Invalid parameter reference.");
20296
+ if (isConstantReference(value) && (!Number.isSafeInteger(value.$constant) || value.$constant < 0 || value.$constant >= family.constants.length))
20297
+ fail("Invalid constant reference.");
20298
+ });
20299
+ let total = 0;
20300
+ const sizes = family.parameters.map((column) => column.map(bytes));
20301
+ const constants = family.constants.map(bytes);
20302
+ for (let index = 0; index < family.themes.length; index += 1) {
20303
+ const size = expandedBytes(family.template, sizes, constants, index);
20304
+ total += size;
20305
+ if (size > tileflowStyleFamilyLimits.expandedStyleBytes || total > tileflowStyleFamilyLimits.expandedFamilyBytes)
20306
+ fail("Expanded family is too large.");
20307
+ const style = materialize(family.template, family, index);
20308
+ assertJsonBudget(style);
20309
+ assertStyle(style);
20310
+ }
20311
+ if (!same(family.updates, planUpdates(expandTileflowFamilyTemplate(family))))
20312
+ fail("Invalid update plan.");
20313
+ return family;
20314
+ }
20315
+ function materializeTileflowStyleFamily(family, theme) {
20316
+ const index = family.themes.indexOf(theme);
20317
+ if (index < 0) fail(`Unknown family theme: ${theme}.`);
20318
+ return materialize(family.template, family, index);
20319
+ }
20320
+ function expandTileflowStyleFamily(input) {
20321
+ const family = parseTileflowStyleFamily(input);
20322
+ return Object.fromEntries(
20323
+ family.themes.map((name) => [name, materializeTileflowStyleFamily(family, name)])
20324
+ );
20325
+ }
20326
+ function materialize(node2, family, index) {
20327
+ if (isReference(node2)) return structuredClone(family.parameters[node2.$variant][index]);
20328
+ if (isConstantReference(node2)) return structuredClone(family.constants[node2.$constant]);
20329
+ if (Array.isArray(node2)) return node2.map((value) => materialize(value, family, index));
20330
+ if (record5(node2))
20331
+ return Object.fromEntries(
20332
+ Object.entries(node2).map(([key, value]) => [key, materialize(value, family, index)])
20333
+ );
20334
+ return node2;
20335
+ }
20336
+ function planUpdates(template) {
20337
+ let appearanceOnly = true;
20338
+ const paint = /* @__PURE__ */ new Map();
20339
+ const roots = /* @__PURE__ */ new Set();
20340
+ walk(template, (node2, path) => {
20341
+ if (!isReference(node2)) return;
20342
+ if (path[0] === "name") return;
20343
+ if (path[0] === "metadata") {
20344
+ const key = path[1];
20345
+ if ([
20346
+ "tileflow:colors",
20347
+ "tileflow:family-image-theme",
20348
+ "tileflow:theme",
20349
+ "tileflow:colorScheme",
20350
+ "tileflow:sun",
20351
+ "tileflow:route",
20352
+ "tileflow:sky",
20353
+ "tileflow:sourceRequirements",
20354
+ "tileflow:dataRequirements"
20355
+ ].includes(String(key)))
20356
+ return;
20357
+ if (key === "tileflow:buildings" && ["color", "modelColors", "modelLighting", "floodLight"].includes(String(path[2])))
20358
+ return;
20359
+ if (key === "tileflow:vegetation" && path[2] === "colors") return;
20360
+ appearanceOnly = false;
20361
+ return;
20362
+ }
20363
+ if (path[0] === "layers" && path[2] === "layout" && path[3] === "icon-image") return;
20364
+ if (path[0] === "sky" || path[0] === "light") {
20365
+ roots.add(path[0]);
20366
+ return;
20367
+ }
20368
+ if (path[0] === "layers" && typeof path[1] === "number" && path[2] === "paint" && typeof path[3] === "string") {
20369
+ paint.set(`${path[1]}/${path[3]}`, [path[1], path[3]]);
20370
+ } else appearanceOnly = false;
20371
+ });
20372
+ if (record5(template) && record5(template.metadata) && template.metadata["tileflow:colors"] && Array.isArray(template.layers))
20373
+ template.layers.forEach((layer, index) => {
20374
+ if (record5(layer) && record5(layer.paint)) {
20375
+ for (const property of Object.keys(layer.paint))
20376
+ if (property.endsWith("color")) paint.set(`${index}/${property}`, [index, property]);
20377
+ }
20378
+ });
20379
+ return { appearanceOnly, paint: [...paint.values()], roots: [...roots].sort() };
20380
+ }
20381
+ function expandedBytes(node2, sizes, constants, index) {
20382
+ if (isReference(node2)) return sizes[node2.$variant][index];
20383
+ if (isConstantReference(node2)) return constants[node2.$constant];
20384
+ if (Array.isArray(node2))
20385
+ return 2 + Math.max(0, node2.length - 1) + node2.reduce((n, v) => n + expandedBytes(v, sizes, constants, index), 0);
20386
+ if (record5(node2))
20387
+ return 2 + Math.max(0, Object.keys(node2).length - 1) + Object.entries(node2).reduce(
20388
+ (n, [key, v]) => n + bytes(key) + 1 + expandedBytes(v, sizes, constants, index),
20389
+ 0
20390
+ );
20391
+ return bytes(node2);
20392
+ }
20393
+ function assertStyle(style) {
20394
+ if (!record5(style) || style.version !== 8 || !record5(style.sources) || !Array.isArray(style.layers) || style.layers.length > tileflowStyleFamilyLimits.layers)
20395
+ fail("Invalid expanded MapLibre style.");
20396
+ const ids = /* @__PURE__ */ new Set();
20397
+ for (const layer of style.layers) {
20398
+ if (!record5(layer) || typeof layer.id !== "string" || ids.has(layer.id) || typeof layer.type !== "string")
20399
+ fail("Invalid or duplicate layer.");
20400
+ ids.add(layer.id);
20401
+ }
20402
+ }
20403
+ function assertJsonBudget(input) {
20404
+ let nodes = 0;
20405
+ const stack = /* @__PURE__ */ new Set();
20406
+ const visit2 = (value, depth) => {
20407
+ if (++nodes > tileflowStyleFamilyLimits.nodes || depth > tileflowStyleFamilyLimits.depth)
20408
+ fail("Family complexity limit exceeded.");
20409
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value))
20410
+ return;
20411
+ if (typeof value !== "object" || !value || stack.has(value)) fail("Expected acyclic JSON.");
20412
+ stack.add(value);
20413
+ if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)
20414
+ fail("Expected JSON objects.");
20415
+ for (const [key, item] of Object.entries(value)) {
20416
+ if (["__proto__", "constructor", "prototype"].includes(key)) fail("Unsafe object key.");
20417
+ visit2(item, depth + 1);
20418
+ }
20419
+ stack.delete(value);
20420
+ };
20421
+ visit2(input, 0);
20422
+ }
20423
+ function walk(node2, visit2, path = []) {
20424
+ visit2(node2, path);
20425
+ if (isReference(node2) || isConstantReference(node2)) return;
20426
+ if (Array.isArray(node2)) node2.forEach((value, i) => walk(value, visit2, [...path, i]));
20427
+ else if (record5(node2))
20428
+ Object.entries(node2).forEach(([key, value]) => walk(value, visit2, [...path, key]));
20429
+ }
20430
+ function isReference(node2) {
20431
+ return record5(node2) && Object.keys(node2).length === 1 && Object.hasOwn(node2, "$variant");
20432
+ }
20433
+ function isConstantReference(node2) {
20434
+ return record5(node2) && Object.keys(node2).length === 1 && Object.hasOwn(node2, "$constant");
20435
+ }
20436
+ function expandTileflowFamilyTemplate(family) {
20437
+ const visit2 = (node2) => {
20438
+ if (isConstantReference(node2)) return structuredClone(family.constants[node2.$constant]);
20439
+ if (isReference(node2)) return node2;
20440
+ if (Array.isArray(node2)) return node2.map(visit2);
20441
+ if (record5(node2))
20442
+ return Object.fromEntries(
20443
+ Object.entries(node2).map(([key, value]) => [key, visit2(value)])
20444
+ );
20445
+ return node2;
20446
+ };
20447
+ return visit2(family.template);
20448
+ }
20449
+ function withTileflowFamilyTemplate(family, template) {
20450
+ assertJsonBudget(template);
20451
+ const packed = shareConstants(template);
20452
+ return parseTileflowStyleFamily({
20453
+ ...family,
20454
+ template: packed.template,
20455
+ constants: packed.constants
20456
+ });
20457
+ }
20458
+ function shareConstants(template) {
20459
+ const counts = /* @__PURE__ */ new Map();
20460
+ const keys = /* @__PURE__ */ new WeakMap();
20461
+ const inspect = (node2) => {
20462
+ if (isReference(node2) || isConstantReference(node2)) return false;
20463
+ if (!node2 || typeof node2 !== "object") return true;
20464
+ const children = Array.isArray(node2) ? node2 : Object.values(node2);
20465
+ const constant = children.map(inspect).every(Boolean);
20466
+ if (constant) {
20467
+ const key = JSON.stringify(node2);
20468
+ if (key.length >= 96) {
20469
+ keys.set(node2, key);
20470
+ counts.set(key, (counts.get(key) ?? 0) + 1);
20471
+ }
20472
+ }
20473
+ return constant;
20474
+ };
20475
+ inspect(template);
20476
+ const constants = [];
20477
+ const ids = /* @__PURE__ */ new Map();
20478
+ const pack = (node2) => {
20479
+ if (!node2 || typeof node2 !== "object" || isReference(node2)) return node2;
20480
+ const key = keys.get(node2);
20481
+ if (key && counts.get(key) > 1) {
20482
+ let index = ids.get(key);
20483
+ if (index === void 0) {
20484
+ index = constants.length;
20485
+ ids.set(key, index);
20486
+ constants.push(node2);
20487
+ }
20488
+ return { $constant: index };
20489
+ }
20490
+ if (Array.isArray(node2)) return node2.map(pack);
20491
+ return Object.fromEntries(
20492
+ Object.entries(node2).map(([key2, value]) => [key2, pack(value)])
20493
+ );
20494
+ };
20495
+ return { template: pack(template), constants };
20496
+ }
20497
+ function record5(node2) {
20498
+ return !!node2 && typeof node2 === "object" && !Array.isArray(node2);
20499
+ }
20500
+ function same(a, b) {
20501
+ return serializeCanonicalJson(a) === serializeCanonicalJson(b);
20502
+ }
20503
+ function serializeCanonicalJson(value) {
20504
+ const sort = (node2) => Array.isArray(node2) ? node2.map(sort) : record5(node2) ? Object.fromEntries(
20505
+ Object.keys(node2).sort().map((key) => [key, sort(node2[key])])
20506
+ ) : node2;
20507
+ return JSON.stringify(sort(value));
20508
+ }
20509
+ function bytes(value) {
20510
+ return new TextEncoder().encode(JSON.stringify(value)).length;
20511
+ }
20512
+ function fail(message) {
20513
+ throw new TypeError(`Tileflow style family: ${message}`);
20514
+ }
20515
+
20516
+ // src/family-deployment.ts
20517
+ function compactTileflowDeploymentArtifact(input) {
20518
+ const artifact = object(input);
20519
+ if (artifact.kind !== "tileflow-map-deployment") throw new TypeError("Invalid deployment kind.");
20520
+ if (artifact.schemaVersion === 1) {
20521
+ const { styles, ...rest } = artifact;
20522
+ return {
20523
+ ...rest,
20524
+ ...compactRequirements(rest.buildManifest),
20525
+ schemaVersion: 3,
20526
+ family: createTileflowStyleFamily(object(styles))
20527
+ };
20528
+ }
20529
+ if (artifact.schemaVersion === 2) {
20530
+ const renderers = object(artifact.renderers);
20531
+ const { styles, ...web } = object(renderers.web);
20532
+ return {
20533
+ ...artifact,
20534
+ schemaVersion: 4,
20535
+ renderers: {
20536
+ ...renderers,
20537
+ web: {
20538
+ ...web,
20539
+ ...compactRequirements(web.buildManifest),
20540
+ family: createTileflowStyleFamily(object(styles))
20541
+ }
20542
+ }
20543
+ };
20544
+ }
20545
+ throw new TypeError("Expected deployment schema version 1 or 2.");
20546
+ }
20547
+ function expandTileflowDeploymentArtifact(input) {
20548
+ const artifact = object(input);
20549
+ if (artifact.schemaVersion === 3) {
20550
+ if (Object.hasOwn(artifact, "styles"))
20551
+ throw new TypeError("A compact deployment cannot include flat styles.");
20552
+ const { family, sharedRequirements, ...rest } = artifact;
20553
+ return {
20554
+ ...rest,
20555
+ buildManifest: expandRequirements(rest.buildManifest, sharedRequirements),
20556
+ schemaVersion: 1,
20557
+ styles: expandTileflowStyleFamily(family)
20558
+ };
20559
+ }
20560
+ if (artifact.schemaVersion === 4) {
20561
+ const renderers = object(artifact.renderers);
20562
+ const web = object(renderers.web);
20563
+ if (Object.hasOwn(web, "styles"))
20564
+ throw new TypeError("A compact deployment cannot include flat web styles.");
20565
+ const { family, sharedRequirements, ...rest } = web;
20566
+ return {
20567
+ ...artifact,
20568
+ schemaVersion: 2,
20569
+ renderers: {
20570
+ ...renderers,
20571
+ web: {
20572
+ ...rest,
20573
+ buildManifest: expandRequirements(rest.buildManifest, sharedRequirements),
20574
+ styles: expandTileflowStyleFamily(family)
20575
+ }
20576
+ }
20577
+ };
20578
+ }
20579
+ return input;
20580
+ }
20581
+ var requirementKeys = ["dataRequirements", "sourceRequirements"];
20582
+ function compactRequirements(input) {
20583
+ const manifest = object(input);
20584
+ const sharedRequirements = {};
20585
+ const maps = Object.fromEntries(
20586
+ Object.entries(object(manifest.maps)).map(([name, raw]) => {
20587
+ const map = object(raw);
20588
+ const themes = Object.entries(object(map.themes));
20589
+ const common = {};
20590
+ for (const key of requirementKeys) {
20591
+ const frequencies = /* @__PURE__ */ new Map();
20592
+ for (const [, rawTheme] of themes) {
20593
+ const value = object(rawTheme)[key];
20594
+ if (value === void 0) continue;
20595
+ const serialized = JSON.stringify(value);
20596
+ const entry = frequencies.get(serialized) ?? { value, count: 0 };
20597
+ entry.count += 1;
20598
+ frequencies.set(serialized, entry);
20599
+ }
20600
+ const frequent = [...frequencies.values()].sort((a, b) => b.count - a.count)[0];
20601
+ if (frequent && frequent.count > 1 && themes.every(([, theme]) => Object.hasOwn(object(theme), key)))
20602
+ common[key] = frequent.value;
20603
+ }
20604
+ if (!Object.keys(common).length) return [name, map];
20605
+ sharedRequirements[name] = common;
20606
+ return [
20607
+ name,
20608
+ {
20609
+ ...map,
20610
+ themes: Object.fromEntries(
20611
+ themes.map(([theme, value]) => [
20612
+ theme,
20613
+ Object.fromEntries(
20614
+ Object.entries(object(value)).filter(
20615
+ ([key, field2]) => !Object.hasOwn(common, key) || JSON.stringify(field2) !== JSON.stringify(common[key])
20616
+ )
20617
+ )
20618
+ ])
20619
+ )
20620
+ }
20621
+ ];
20622
+ })
20623
+ );
20624
+ return {
20625
+ buildManifest: { ...manifest, maps },
20626
+ ...Object.keys(sharedRequirements).length ? { sharedRequirements } : {}
20627
+ };
20628
+ }
20629
+ function expandRequirements(input, shared) {
20630
+ if (shared === void 0) return input;
20631
+ const manifest = object(input);
20632
+ const maps = { ...object(manifest.maps) };
20633
+ let expandedBytes2 = 0;
20634
+ for (const [name, raw] of Object.entries(object(shared))) {
20635
+ if (!Object.hasOwn(maps, name))
20636
+ throw new TypeError("Shared requirements refer to an unknown map.");
20637
+ const map = object(maps[name]);
20638
+ const common = object(raw);
20639
+ if (!Object.keys(common).length || Object.keys(common).some(
20640
+ (key) => !requirementKeys.includes(key)
20641
+ ))
20642
+ throw new TypeError("Invalid shared requirements.");
20643
+ const themes = Object.entries(object(map.themes));
20644
+ if (!themes.length || themes.length > 64)
20645
+ throw new TypeError("Invalid requirement theme count.");
20646
+ expandedBytes2 += new TextEncoder().encode(JSON.stringify(common)).length * themes.length;
20647
+ if (expandedBytes2 > 8 * 1024 * 1024)
20648
+ throw new TypeError("Expanded requirements are too large.");
20649
+ maps[name] = {
20650
+ ...map,
20651
+ themes: Object.fromEntries(
20652
+ themes.map(([theme, rawTheme]) => {
20653
+ const value = object(rawTheme);
20654
+ return [theme, { ...common, ...value }];
20655
+ })
20656
+ )
20657
+ };
20658
+ }
20659
+ return { ...manifest, maps };
20660
+ }
20661
+ function tileflowDeploymentWebBytes(input) {
20662
+ const { iconComposition: _receipt, ...body } = object(input);
20663
+ const source = object(body.artifact);
20664
+ const artifact = source.schemaVersion === 2 || source.schemaVersion === 4 ? {
20665
+ kind: source.kind,
20666
+ schemaVersion: source.schemaVersion === 4 ? 3 : 1,
20667
+ mapId: source.mapId,
20668
+ teamSources: source.teamSources,
20669
+ ...object(object(source.renderers).web)
20670
+ } : { ...source };
20671
+ const buildManifest = object(artifact.buildManifest);
20672
+ const maps = Object.fromEntries(
20673
+ Object.entries(object(buildManifest.maps)).map(([name, raw]) => {
20674
+ const map = object(raw);
20675
+ const { iconComposition: _embedded, ...sourceAssets } = object(map.sourceAssets);
20676
+ return [name, { ...map, sourceAssets }];
20677
+ })
20678
+ );
20679
+ return new TextEncoder().encode(
20680
+ JSON.stringify({ ...body, artifact: { ...artifact, buildManifest: { ...buildManifest, maps } } })
20681
+ ).length;
20682
+ }
20683
+ function object(value) {
20684
+ if (!value || typeof value !== "object" || Array.isArray(value))
20685
+ throw new TypeError("Expected a deployment object.");
20686
+ return value;
20687
+ }
20688
+
19709
20689
  // src/icon-composition.ts
19710
20690
  import { z as z11 } from "zod";
19711
20691
 
@@ -19838,13 +20818,13 @@ function compareCodeUnits8(left, right) {
19838
20818
  }
19839
20819
  return 0;
19840
20820
  }
19841
- function serializeCanonicalJson(value) {
20821
+ function serializeCanonicalJson2(value) {
19842
20822
  return JSON.stringify(sortJsonValue(value));
19843
20823
  }
19844
20824
  async function sha256Hex(input) {
19845
- const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
19846
- const digestInput = new Uint8Array(bytes.byteLength);
19847
- digestInput.set(bytes);
20825
+ const bytes2 = typeof input === "string" ? new TextEncoder().encode(input) : input;
20826
+ const digestInput = new Uint8Array(bytes2.byteLength);
20827
+ digestInput.set(bytes2);
19848
20828
  const digest = await crypto.subtle.digest("SHA-256", digestInput.buffer);
19849
20829
  return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
19850
20830
  }
@@ -19999,7 +20979,7 @@ var tileflowIconCompositionSchema = z11.object({
19999
20979
  message: "Winners must exactly cover the sorted last-wins export closure"
20000
20980
  });
20001
20981
  }
20002
- if (new TextEncoder().encode(serializeCanonicalJson(receipt)).byteLength > tileflowIconCompositionMaximumBytes) {
20982
+ if (new TextEncoder().encode(serializeCanonicalJson2(receipt)).byteLength > tileflowIconCompositionMaximumBytes) {
20003
20983
  context.addIssue({ code: "custom", message: "Composition receipt exceeds its byte limit" });
20004
20984
  }
20005
20985
  });
@@ -20091,7 +21071,7 @@ async function normalizeCompiledThemes(map, styles) {
20091
21071
  colorScheme: theme.colorScheme,
20092
21072
  dataRequirements: inferTileflowDataRequirements(style),
20093
21073
  sourceRequirements: inferTileflowSourceRequirements(style),
20094
- styleSha256: await sha256Hex(serializeCanonicalJson(style)),
21074
+ styleSha256: await sha256Hex(serializeCanonicalJson2(style)),
20095
21075
  themeId: theme.id,
20096
21076
  themeVersion: theme.version
20097
21077
  }
@@ -20147,7 +21127,7 @@ async function hashTileflowMapRevision(input, sourceAssets) {
20147
21127
  sourceAssets: normalizedSources
20148
21128
  };
20149
21129
  const domain = normalizedSources.iconComposition ? "tileflow-map-revision-v2\0" : tileflowMapRevisionDomain;
20150
- return sha256Hex(`${domain}${serializeCanonicalJson(revisionDocument)}`);
21130
+ return sha256Hex(`${domain}${serializeCanonicalJson2(revisionDocument)}`);
20151
21131
  }
20152
21132
  function normalizeRevisionCartography(input) {
20153
21133
  if (!input.sources) return input;
@@ -20194,7 +21174,7 @@ async function hashTileflowAssetSetIdentities(input) {
20194
21174
  sha256: asset.sha256
20195
21175
  };
20196
21176
  });
20197
- return sha256Hex(`${tileflowAssetSetDomain}${serializeCanonicalJson({ files, schemaVersion: 1 })}`);
21177
+ return sha256Hex(`${tileflowAssetSetDomain}${serializeCanonicalJson2({ files, schemaVersion: 1 })}`);
20198
21178
  }
20199
21179
  function normalizeLineage(input, map) {
20200
21180
  if (input.length === 0 || input[0]?.id !== map.id || input[0].mapVersion !== map.version) {
@@ -20280,7 +21260,7 @@ function assertIconCompositionMatchesMap(map, sources) {
20280
21260
  const identity = sources.icons[ordinal];
20281
21261
  if (identity.id !== winner.id) throw new Error("Effective icon identity mismatch");
20282
21262
  if (receipt.contributors[winner.contributor].kind === "icon-set") {
20283
- if (identity.kind !== "rendered-icon" || identity.width !== winner.width || identity.height !== winner.height || identity.pixelSha256.oneX !== winner.pixelSha256.oneX || identity.pixelSha256.twoX !== winner.pixelSha256.twoX || serializeCanonicalJson(identity.appearance ?? null) !== serializeCanonicalJson(winner.appearance ?? null) || serializeCanonicalJson(identity.layout ?? null) !== serializeCanonicalJson(winner.layout ?? null))
21263
+ if (identity.kind !== "rendered-icon" || identity.width !== winner.width || identity.height !== winner.height || identity.pixelSha256.oneX !== winner.pixelSha256.oneX || identity.pixelSha256.twoX !== winner.pixelSha256.twoX || serializeCanonicalJson2(identity.appearance ?? null) !== serializeCanonicalJson2(winner.appearance ?? null) || serializeCanonicalJson2(identity.layout ?? null) !== serializeCanonicalJson2(winner.layout ?? null))
20284
21264
  throw new Error("Shared icon identity must match verified pixels, appearance and layout");
20285
21265
  } else if (identity.kind === "rendered-icon")
20286
21266
  throw new Error("Local originals must retain their existing source identity");
@@ -20413,7 +21393,7 @@ function isBoundedNativeJson(input, stage = "input") {
20413
21393
  let stringBytes = 0;
20414
21394
  const ancestors = /* @__PURE__ */ new Set();
20415
21395
  const encoder = new TextEncoder();
20416
- const visit = (value, depth) => {
21396
+ const visit2 = (value, depth) => {
20417
21397
  if (++nodes > limits.maximumNodes || depth > limits.maximumDepth) {
20418
21398
  return false;
20419
21399
  }
@@ -20436,7 +21416,7 @@ function isBoundedNativeJson(input, stage = "input") {
20436
21416
  for (const key of keys) {
20437
21417
  if (Array.isArray(value) && key === "length") continue;
20438
21418
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
20439
- if (!descriptor || !descriptor.enumerable || !("value" in descriptor) || !visit(key, depth + 1) || !visit(descriptor.value, depth + 1)) {
21419
+ if (!descriptor || !descriptor.enumerable || !("value" in descriptor) || !visit2(key, depth + 1) || !visit2(descriptor.value, depth + 1)) {
20440
21420
  return false;
20441
21421
  }
20442
21422
  }
@@ -20444,7 +21424,7 @@ function isBoundedNativeJson(input, stage = "input") {
20444
21424
  return true;
20445
21425
  };
20446
21426
  try {
20447
- return visit(input, 0) && encoder.encode(JSON.stringify(input)).byteLength <= limits.maximumStyleBytes;
21427
+ return visit2(input, 0) && encoder.encode(JSON.stringify(input)).byteLength <= limits.maximumStyleBytes;
20448
21428
  } catch {
20449
21429
  return false;
20450
21430
  }
@@ -20526,9 +21506,9 @@ var tileflowNativeBuildRecordSchema = strictNativeObject({
20526
21506
  engines: strictNativeObject({ android: z13.literal("13.2.0"), ios: z13.literal("6.26.0") }),
20527
21507
  buildManifestSha256: hashSchema,
20528
21508
  transformations: z13.array(tileflowNativeStyleTransformationSchema).max(4096)
20529
- }).superRefine((record5, context) => {
21509
+ }).superRefine((record7, context) => {
20530
21510
  let previous = "";
20531
- for (const [index, item] of record5.transformations.entries()) {
21511
+ for (const [index, item] of record7.transformations.entries()) {
20532
21512
  const name = `${item.map}/${item.theme}`;
20533
21513
  if (name <= previous)
20534
21514
  context.addIssue({
@@ -20541,7 +21521,7 @@ var tileflowNativeBuildRecordSchema = strictNativeObject({
20541
21521
  });
20542
21522
 
20543
21523
  // src/native-profile.ts
20544
- import { isExpression, validateStyleMin as validateStyleMin2 } from "@maplibre/maplibre-gl-style-spec";
21524
+ import { isExpression as isExpression2, validateStyleMin as validateStyleMin2 } from "@maplibre/maplibre-gl-style-spec";
20545
21525
 
20546
21526
  // ../../node_modules/.pnpm/@maplibre+maplibre-gl-style-spec@24.8.5/node_modules/@maplibre/maplibre-gl-style-spec/dist/latest.json
20547
21527
  var latest_default = {
@@ -29231,13 +30211,13 @@ function validateNativeStyle(input, options, stage) {
29231
30211
  issues.push(createTileflowNativeDiagnostic(code, path));
29232
30212
  }
29233
30213
  };
29234
- if (!record4(options) || !plainOptions(options)) {
30214
+ if (!record6(options) || !plainOptions(options)) {
29235
30215
  return [createTileflowNativeDiagnostic("NATIVE_RENDERER_UNSUPPORTED", "/profile")];
29236
30216
  }
29237
30217
  if (options.profile !== void 0 && options.profile !== "native-v1" || !expressionDefinitions) {
29238
30218
  return [createTileflowNativeDiagnostic("NATIVE_RENDERER_UNSUPPORTED", "/profile")];
29239
30219
  }
29240
- if (!isBoundedNativeJson(input, stage) || !record4(input)) {
30220
+ if (!isBoundedNativeJson(input, stage) || !record6(input)) {
29241
30221
  return [createTileflowNativeDiagnostic("NATIVE_UNSUPPORTED_STYLE")];
29242
30222
  }
29243
30223
  const style = input;
@@ -29251,11 +30231,11 @@ function validateNativeStyle(input, options, stage) {
29251
30231
  }
29252
30232
  if (style.projection !== void 0) {
29253
30233
  const projection = style.projection;
29254
- if (!record4(projection) || projection.type !== "mercator" || Object.keys(projection).length !== 1) {
30234
+ if (!record6(projection) || projection.type !== "mercator" || Object.keys(projection).length !== 1) {
29255
30235
  add2("NATIVE_UNSUPPORTED_STYLE", "/projection");
29256
30236
  }
29257
30237
  }
29258
- if (record4(style.metadata)) {
30238
+ if (record6(style.metadata)) {
29259
30239
  for (const key of Object.keys(style.metadata).sort()) {
29260
30240
  if (/^tileflow:(?:contour|pmtiles)/iu.test(key)) {
29261
30241
  add2("NATIVE_UNSUPPORTED_SOURCE", nativePointer("/metadata", key));
@@ -29298,14 +30278,14 @@ function validateNativeStyle(input, options, stage) {
29298
30278
  add2("NATIVE_FONT_UNAVAILABLE", "/glyphs");
29299
30279
  } else checkUrl(style.glyphs, "/glyphs", "glyphs");
29300
30280
  }
29301
- const sources = record4(style.sources) ? style.sources : {};
30281
+ const sources = record6(style.sources) ? style.sources : {};
29302
30282
  if (Object.keys(sources).length > tileflowNativeProfileLimits.maximumSources) {
29303
30283
  add2("NATIVE_UNSUPPORTED_SOURCE", "/sources");
29304
30284
  }
29305
30285
  for (const id2 of Object.keys(sources).sort().slice(0, tileflowNativeProfileLimits.maximumSources)) {
29306
30286
  const source = sources[id2];
29307
30287
  const path = nativePointer("/sources", id2);
29308
- if (!record4(source) || typeof source.type !== "string" || !sourceKinds.has(source.type)) {
30288
+ if (!record6(source) || typeof source.type !== "string" || !sourceKinds.has(source.type)) {
29309
30289
  add2("NATIVE_UNSUPPORTED_SOURCE", path);
29310
30290
  continue;
29311
30291
  }
@@ -29317,7 +30297,7 @@ function validateNativeStyle(input, options, stage) {
29317
30297
  "NATIVE_UNSUPPORTED_SOURCE"
29318
30298
  );
29319
30299
  if (source.clusterProperties !== void 0) {
29320
- if (record4(source.clusterProperties)) {
30300
+ if (record6(source.clusterProperties)) {
29321
30301
  for (const key of Object.keys(source.clusterProperties).sort()) {
29322
30302
  const values = source.clusterProperties[key];
29323
30303
  if (Array.isArray(values))
@@ -29343,7 +30323,7 @@ function validateNativeStyle(input, options, stage) {
29343
30323
  if (faces !== void 0) {
29344
30324
  const support = spec.$root?.["font-faces"]?.["sdk-support"]?.["basic functionality"];
29345
30325
  if (!support || !supportsBoth(support)) add2("NATIVE_FONT_UNAVAILABLE", "/font-faces");
29346
- if (!record4(faces) || Object.keys(faces).length > tileflowNativeProfileLimits.maximumFontFaces) {
30326
+ if (!record6(faces) || Object.keys(faces).length > tileflowNativeProfileLimits.maximumFontFaces) {
29347
30327
  add2("NATIVE_FONT_UNAVAILABLE", "/font-faces");
29348
30328
  } else {
29349
30329
  for (const name of Object.keys(faces).sort()) {
@@ -29360,14 +30340,14 @@ function validateNativeStyle(input, options, stage) {
29360
30340
  add2("NATIVE_UNSUPPORTED_STYLE", "/layers");
29361
30341
  layers2.slice(0, tileflowNativeProfileLimits.maximumLayers).forEach((layer, i) => {
29362
30342
  const path = `/layers/${i}`;
29363
- if (!record4(layer) || typeof layer.type !== "string" || !layerKinds.has(layer.type) || layer.ref !== void 0) {
30343
+ if (!record6(layer) || typeof layer.type !== "string" || !layerKinds.has(layer.type) || layer.ref !== void 0) {
29364
30344
  add2("NATIVE_UNSUPPORTED_STYLE", path);
29365
30345
  return;
29366
30346
  }
29367
30347
  properties(layer, spec.layer, path, add2);
29368
30348
  for (const group of ["layout", "paint"]) {
29369
30349
  const values = layer[group];
29370
- if (!record4(values)) continue;
30350
+ if (!record6(values)) continue;
29371
30351
  properties(
29372
30352
  values,
29373
30353
  spec[`${group}_${layer.type}`],
@@ -29381,7 +30361,7 @@ function validateNativeStyle(input, options, stage) {
29381
30361
  }
29382
30362
  }
29383
30363
  expressions(layer.filter, `${path}/filter`, add2);
29384
- if (!options.deferFontClosure && record4(layer.layout) && layer.layout["text-field"] !== void 0) {
30364
+ if (!options.deferFontClosure && record6(layer.layout) && layer.layout["text-field"] !== void 0) {
29385
30365
  const fonts = layer.layout["text-font"];
29386
30366
  if (style.glyphs === void 0) {
29387
30367
  if (!fontStackAvailable(fonts, faces))
@@ -29391,7 +30371,7 @@ function validateNativeStyle(input, options, stage) {
29391
30371
  }
29392
30372
  });
29393
30373
  for (const key of ["light", "transition"]) {
29394
- if (record4(style[key])) {
30374
+ if (record6(style[key])) {
29395
30375
  properties(style[key], spec[key], `/${key}`, add2);
29396
30376
  for (const [property, value] of Object.entries(style[key]))
29397
30377
  expressions(value, nativePointer(`/${key}`, property), add2);
@@ -29424,13 +30404,13 @@ function properties(values, definitions, path, add2, code = "NATIVE_UNSUPPORTED_
29424
30404
  const basic = definition["sdk-support"]?.["basic functionality"];
29425
30405
  if (basic && !supportsBoth(basic)) add2(code, nativePointer(path, key));
29426
30406
  const value = values[key];
29427
- if (checkFeatureSupport && record4(value) && Object.hasOwn(value, "stops"))
30407
+ if (checkFeatureSupport && record6(value) && Object.hasOwn(value, "stops"))
29428
30408
  add2(code, nativePointer(path, key));
29429
30409
  if (typeof value === "string") {
29430
30410
  const enumSupport = definition.values?.[value]?.["sdk-support"]?.["basic functionality"];
29431
30411
  if (enumSupport && !supportsBoth(enumSupport)) add2(code, nativePointer(path, key));
29432
30412
  }
29433
- if (isExpression(value)) {
30413
+ if (isExpression2(value)) {
29434
30414
  const support = definition["sdk-support"]?.["data-driven styling"];
29435
30415
  if (checkFeatureSupport && usesFeatureInput(value) && (!support || !supportsBoth(support)))
29436
30416
  add2(code, nativePointer(path, key));
@@ -29441,7 +30421,7 @@ function supportsBoth(support) {
29441
30421
  return supportsNativeVersions(support, tileflowNativeProfile);
29442
30422
  }
29443
30423
  function expressions(value, path, add2) {
29444
- if (!Array.isArray(value) || !isExpression(value)) return;
30424
+ if (!Array.isArray(value) || !isExpression2(value)) return;
29445
30425
  const operator = value[0];
29446
30426
  const support = expressionDefinitions?.[operator]?.["sdk-support"]?.["basic functionality"];
29447
30427
  if (!support || !supportsBoth(support)) add2("NATIVE_UNSUPPORTED_STYLE", `${path}/0`);
@@ -29450,7 +30430,7 @@ function expressions(value, path, add2) {
29450
30430
  if (index === 0) return;
29451
30431
  if (operator === "match" && index >= 2 && index < value.length - 1 && index % 2 === 0) return;
29452
30432
  if (operator.startsWith("interpolate") && index === 1) return;
29453
- if (record4(child)) {
30433
+ if (record6(child)) {
29454
30434
  for (const key of Object.keys(child).sort())
29455
30435
  expressions(child[key], nativePointer(`${path}/${index}`, key), add2);
29456
30436
  } else expressions(child, `${path}/${index}`, add2);
@@ -29458,14 +30438,14 @@ function expressions(value, path, add2) {
29458
30438
  }
29459
30439
  function fontStackAvailable(value, faces) {
29460
30440
  const stack = Array.isArray(value) && value.length === 2 && value[0] === "literal" ? value[1] : value;
29461
- return Array.isArray(stack) && stack.length > 0 && record4(faces) && stack.every((name) => typeof name === "string" && Object.hasOwn(faces, name));
30441
+ return Array.isArray(stack) && stack.length > 0 && record6(faces) && stack.every((name) => typeof name === "string" && Object.hasOwn(faces, name));
29462
30442
  }
29463
30443
  function inlineFontStacks(value, path, faces, add2) {
29464
- if (!Array.isArray(value) || !isExpression(value) || value[0] === "literal") return;
30444
+ if (!Array.isArray(value) || !isExpression2(value) || value[0] === "literal") return;
29465
30445
  value.forEach((child, index) => {
29466
30446
  if (index === 0 || value[0] === "match" && index >= 2 && index < value.length - 1 && index % 2 === 0)
29467
30447
  return;
29468
- if (value[0] === "format" && record4(child) && child["text-font"] !== void 0 && !fontStackAvailable(child["text-font"], faces)) {
30448
+ if (value[0] === "format" && record6(child) && child["text-font"] !== void 0 && !fontStackAvailable(child["text-font"], faces)) {
29469
30449
  add2("NATIVE_FONT_UNAVAILABLE", `${path}/${index}/text-font`);
29470
30450
  }
29471
30451
  inlineFontStacks(child, `${path}/${index}`, faces, add2);
@@ -29482,7 +30462,7 @@ function parserIssuePointer(key, style) {
29482
30462
  path = nativePointer(path, match3[1]);
29483
30463
  value = value[Number(match3[1])];
29484
30464
  rest = rest.slice(match3[0].length).replace(/^\./u, "");
29485
- } else if (record4(value)) {
30465
+ } else if (record6(value)) {
29486
30466
  const matches = Object.keys(value).filter(
29487
30467
  (name2) => rest === name2 || rest.startsWith(`${name2}.`) || rest.startsWith(`${name2}[`)
29488
30468
  );
@@ -29496,7 +30476,7 @@ function parserIssuePointer(key, style) {
29496
30476
  return path;
29497
30477
  }
29498
30478
  function usesFeatureInput(value) {
29499
- if (!Array.isArray(value) || !isExpression(value) || value[0] === "literal") return false;
30479
+ if (!Array.isArray(value) || !isExpression2(value) || value[0] === "literal") return false;
29500
30480
  return ["get", "has", "id", "properties", "geometry-type", "feature-state"].includes(
29501
30481
  String(value[0])
29502
30482
  ) || value.slice(1).some(usesFeatureInput);
@@ -29513,7 +30493,7 @@ function plainOptions(value) {
29513
30493
  return false;
29514
30494
  }
29515
30495
  }
29516
- function record4(value) {
30496
+ function record6(value) {
29517
30497
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
29518
30498
  }
29519
30499
  function normalizeNativeDiagnostics(input) {
@@ -29626,7 +30606,7 @@ var tileflowRendererDeploymentArtifactSchema = z15.object({
29626
30606
  native: z15.object({ ...familyShape, buildRecord: tileflowNativeBuildRecordSchema }).strict()
29627
30607
  }).strict()
29628
30608
  }).strict().superRefine((artifact, context) => {
29629
- const fail = () => context.addIssue({
30609
+ const fail2 = () => context.addIssue({
29630
30610
  code: "custom",
29631
30611
  message: "Renderer families must share one complete authored Map identity"
29632
30612
  });
@@ -29634,22 +30614,22 @@ var tileflowRendererDeploymentArtifactSchema = z15.object({
29634
30614
  const names = Object.keys(family.buildManifest.maps);
29635
30615
  const map = family.buildManifest.maps[artifact.mapId];
29636
30616
  if (names.length !== 1 || names[0] !== artifact.mapId || !map) {
29637
- fail();
30617
+ fail2();
29638
30618
  continue;
29639
30619
  }
29640
30620
  const themes = Object.keys(map.themes).sort();
29641
- if (!themes.length || themes.length > tileflowRendererDeploymentLimits.maximumThemes || !equal(themes, Object.keys(family.styles).sort()) || !themes.includes(map.defaultTheme) || map.lineage[0]?.id !== artifact.mapId || map.lineage[0]?.mapVersion !== map.mapVersion || Boolean(map.sourceAssets.iconComposition) !== (map.mapRevisionSchemaVersion === 2))
29642
- fail();
30621
+ if (!themes.length || themes.length > tileflowRendererDeploymentLimits.maximumThemes || !equal2(themes, Object.keys(family.styles).sort()) || !themes.includes(map.defaultTheme) || map.lineage[0]?.id !== artifact.mapId || map.lineage[0]?.mapVersion !== map.mapVersion || Boolean(map.sourceAssets.iconComposition) !== (map.mapRevisionSchemaVersion === 2))
30622
+ fail2();
29643
30623
  if (map.timeOfDay && validateTileflowTimeOfDayVariants(map.timeOfDay, map.themes).length)
29644
- fail();
30624
+ fail2();
29645
30625
  for (const scheme of ["light", "dark"]) {
29646
30626
  if (map.systemThemes && map.themes[map.systemThemes[scheme]]?.colorScheme !== scheme)
29647
- fail();
30627
+ fail2();
29648
30628
  }
29649
30629
  }
29650
30630
  const web = artifact.renderers.web.buildManifest;
29651
30631
  const native = artifact.renderers.native.buildManifest;
29652
- if (!equal(authoredIdentity(web), authoredIdentity(native))) fail();
30632
+ if (!equal2(authoredIdentity(web), authoredIdentity(native))) fail2();
29653
30633
  });
29654
30634
  var TileflowRendererDeploymentError = class extends Error {
29655
30635
  code = "RENDERER_DEPLOYMENT_INVALID";
@@ -29660,17 +30640,17 @@ var TileflowRendererDeploymentError = class extends Error {
29660
30640
  };
29661
30641
  async function parseTileflowRendererDeploymentArtifact(input) {
29662
30642
  try {
29663
- const inputText = serializeCanonicalJson(input);
30643
+ const inputText = serializeCanonicalJson2(input);
29664
30644
  if (new TextEncoder().encode(inputText).byteLength > tileflowRendererDeploymentLimits.maximumBytes || /tf_(?:public|native|live)_/u.test(inputText))
29665
30645
  throw new Error();
29666
30646
  const artifact = tileflowRendererDeploymentArtifactSchema.parse(input);
29667
30647
  const { web, native } = artifact.renderers;
29668
- if (new TextEncoder().encode(serializeCanonicalJson(native)).byteLength > tileflowRendererDeploymentLimits.maximumNativeFamilyBytes)
30648
+ if (new TextEncoder().encode(serializeCanonicalJson2(native)).byteLength > tileflowRendererDeploymentLimits.maximumNativeFamilyBytes)
29669
30649
  throw new Error();
29670
- if (await sha256Hex(serializeCanonicalJson(native.buildManifest)) !== native.buildRecord.buildManifestSha256)
30650
+ if (await sha256Hex(serializeCanonicalJson2(native.buildManifest)) !== native.buildRecord.buildManifestSha256)
29671
30651
  throw new Error();
29672
30652
  const names = Object.keys(web.styles).sort();
29673
- if (!equal(
30653
+ if (!equal2(
29674
30654
  native.buildRecord.transformations.map((value) => `${value.map}/${value.theme}`),
29675
30655
  names.map((name) => `${artifact.mapId}/${name}`)
29676
30656
  ))
@@ -29683,8 +30663,8 @@ async function parseTileflowRendererDeploymentArtifact(input) {
29683
30663
  const transformation = native.buildRecord.transformations.find(
29684
30664
  (value) => value.theme === name
29685
30665
  );
29686
- const webHash = await sha256Hex(serializeCanonicalJson(webStyle));
29687
- const nativeHash = await sha256Hex(serializeCanonicalJson(nativeStyle));
30666
+ const webHash = await sha256Hex(serializeCanonicalJson2(webStyle));
30667
+ const nativeHash = await sha256Hex(serializeCanonicalJson2(nativeStyle));
29688
30668
  if (webHash !== webTheme.styleSha256 || nativeHash !== nativeTheme.styleSha256 || transformation.inputStyleSha256 !== webHash || !Array.isArray(webStyle.layers) || !Array.isArray(nativeStyle.layers) || transformation.inputLayers !== webStyle.layers.length || transformation.outputLayers !== nativeStyle.layers.length)
29689
30669
  throw new Error();
29690
30670
  if (validateTileflowNativePreparedStyle(
@@ -29695,10 +30675,10 @@ async function parseTileflowRendererDeploymentArtifact(input) {
29695
30675
  ).length)
29696
30676
  throw new Error();
29697
30677
  for (const field2 of ["sources", "sprite", "glyphs"]) {
29698
- if (!equal(webStyle[field2], nativeStyle[field2])) throw new Error();
30678
+ if (!equal2(webStyle[field2], nativeStyle[field2])) throw new Error();
29699
30679
  }
29700
30680
  for (const style of [webStyle, nativeStyle]) {
29701
- const metadata = object(style.metadata);
30681
+ const metadata = object2(style.metadata);
29702
30682
  if (!metadata || metadata["tileflow:map"] !== artifact.mapId || metadata["tileflow:theme"] !== name || metadata["tileflow:mapVersion"] !== web.buildManifest.maps[artifact.mapId].mapVersion || metadata["tileflow:colorScheme"] !== webTheme.colorScheme || [
29703
30683
  "tileflow:deploymentId",
29704
30684
  "tileflow:deploymentVersion",
@@ -29707,7 +30687,7 @@ async function parseTileflowRendererDeploymentArtifact(input) {
29707
30687
  ].some((key) => metadata[key] !== void 0))
29708
30688
  throw new Error();
29709
30689
  }
29710
- if (!equal(semanticIdentity(webStyle), semanticIdentity(nativeStyle))) throw new Error();
30690
+ if (!equal2(semanticIdentity(webStyle), semanticIdentity(nativeStyle))) throw new Error();
29711
30691
  }
29712
30692
  return artifact;
29713
30693
  } catch {
@@ -29715,10 +30695,10 @@ async function parseTileflowRendererDeploymentArtifact(input) {
29715
30695
  }
29716
30696
  }
29717
30697
  function nativeValidationStyle(style, bindings) {
29718
- const copy = JSON.parse(serializeCanonicalJson(style));
29719
- const sources = object(copy.sources);
30698
+ const copy = JSON.parse(serializeCanonicalJson2(style));
30699
+ const sources = object2(copy.sources);
29720
30700
  for (const [id2, binding] of Object.entries(bindings)) {
29721
- const source = object(sources?.[id2]);
30701
+ const source = object2(sources?.[id2]);
29722
30702
  if (!source || source.type !== binding.type || source.url !== `tileflow://hosted-sources/${id2}` || source.tiles !== void 0)
29723
30703
  throw new Error();
29724
30704
  source.url = `https://artifacts.invalid/tiles/${id2}/tiles.json`;
@@ -29744,11 +30724,11 @@ function authoredIdentity(manifest) {
29744
30724
  )
29745
30725
  };
29746
30726
  }
29747
- function object(value) {
30727
+ function object2(value) {
29748
30728
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
29749
30729
  }
29750
- function equal(left, right) {
29751
- return left === void 0 || right === void 0 ? left === right : serializeCanonicalJson(left) === serializeCanonicalJson(right);
30730
+ function equal2(left, right) {
30731
+ return left === void 0 || right === void 0 ? left === right : serializeCanonicalJson2(left) === serializeCanonicalJson2(right);
29752
30732
  }
29753
30733
  var artifactName = z15.string().min(1).max(256).refine(
29754
30734
  (value) => [...value].every((char) => {
@@ -29800,11 +30780,11 @@ var semanticSchema = z15.object({
29800
30780
  }).strict()
29801
30781
  }).strict();
29802
30782
  function semanticIdentity(style) {
29803
- const candidate = object(style.metadata)?.["tileflow:interaction-manifest"];
30783
+ const candidate = object2(style.metadata)?.["tileflow:interaction-manifest"];
29804
30784
  if (candidate === void 0) return void 0;
29805
30785
  const manifest = semanticSchema.parse(candidate);
29806
30786
  const layers2 = style.layers.map((value) => {
29807
- const layer = object(value);
30787
+ const layer = object2(value);
29808
30788
  if (!layer) throw new Error();
29809
30789
  return layer;
29810
30790
  });
@@ -29837,7 +30817,10 @@ var tileflowRendererDeploymentResponseSchema = z15.object({
29837
30817
  version: positive,
29838
30818
  worldConversionId: z15.string().min(1).max(128).optional(),
29839
30819
  renderers: z15.object({
29840
- web: z15.object({ themes: z15.record(tileflowThemeNameSchema, webResponseTheme) }).strict(),
30820
+ web: z15.object({
30821
+ themes: z15.record(tileflowThemeNameSchema, webResponseTheme),
30822
+ familyUrl: publicUrlSchema.optional()
30823
+ }).strict(),
29841
30824
  native: z15.object({
29842
30825
  profile: z15.literal("native-v1"),
29843
30826
  manifestUrl: publicUrlSchema,
@@ -29849,7 +30832,7 @@ var tileflowRendererDeploymentResponseSchema = z15.object({
29849
30832
  const names = Object.keys(value.renderers.web.themes).sort();
29850
30833
  const native = value.renderers.native;
29851
30834
  const url = new URL(native.manifestUrl);
29852
- if (!names.length || names.length > 64 || !equal(names, Object.keys(native.themes).sort()) || url.pathname !== `/maps/${value.mapId}/native/manifest.json` || names.some(
30835
+ if (!names.length || names.length > 64 || !equal2(names, Object.keys(native.themes).sort()) || url.pathname !== `/maps/${value.mapId}/native/manifest.json` || value.renderers.web.familyUrl !== void 0 && value.renderers.web.familyUrl !== `${url.origin}/maps/${value.mapId}/family/v${value.version}.json` || names.some(
29853
30836
  (name) => native.themes[name].styleUrl !== `${url.origin}/maps/${value.mapId}/native/v${value.version}/${name}.json`
29854
30837
  ))
29855
30838
  throw new Error();
@@ -29958,6 +30941,7 @@ function createManifest(catalog, options = {}) {
29958
30941
  mapName,
29959
30942
  {
29960
30943
  defaultTheme: resolved.defaultTheme,
30944
+ ...options.families ? { familyUrl: `${styleBaseUrl}/families/${mapName}.json` } : {},
29961
30945
  ...resolved.systemThemes ? { systemThemes: resolved.systemThemes } : {},
29962
30946
  themes,
29963
30947
  ...options.manifestVersion !== 1 && resolved.timeOfDay && Object.keys(resolved.timeOfDay).length ? { timeOfDay: resolved.timeOfDay } : {},
@@ -29968,7 +30952,7 @@ function createManifest(catalog, options = {}) {
29968
30952
  );
29969
30953
  const manifest = {
29970
30954
  maps,
29971
- version: options.manifestVersion ?? (Object.values(maps).some((map) => map.timeOfDay) ? 2 : tileflowRuntimeManifestVersion)
30955
+ version: options.manifestVersion ?? (options.families ? 3 : Object.values(maps).some((map) => map.timeOfDay) ? 2 : tileflowRuntimeManifestVersion)
29972
30956
  };
29973
30957
  return parseTileflowRuntimeManifest(manifest);
29974
30958
  }
@@ -29990,6 +30974,7 @@ export {
29990
30974
  TileflowRendererDeploymentError,
29991
30975
  collectMapLineage,
29992
30976
  collectTileflowMapBuildLineage,
30977
+ compactTileflowDeploymentArtifact,
29993
30978
  createManifest,
29994
30979
  createStyleFromCatalog,
29995
30980
  createStyleFromCatalogWithInspection,
@@ -29997,13 +30982,20 @@ export {
29997
30982
  createStylesFromCatalog,
29998
30983
  createStylesFromCatalogWithInspection,
29999
30984
  createTileflowMapBuildManifest,
30985
+ createTileflowStyleFamily,
30986
+ expandTileflowDeploymentArtifact,
30987
+ expandTileflowFamilyTemplate,
30988
+ expandTileflowStyleFamily,
30000
30989
  hashTileflowAssetSet,
30001
30990
  hashTileflowAssetSetIdentities,
30002
30991
  hashTileflowMapRevision,
30003
30992
  inferTileflowDataRequirements,
30004
30993
  inferTileflowSourceRequirements,
30994
+ materializeTileflowStyleFamily,
30005
30995
  parseTileflowRendererDeploymentArtifact,
30996
+ parseTileflowStyleFamily,
30006
30997
  prepareTileflowNativePathGround,
30998
+ tileflowDeploymentWebBytes,
30007
30999
  tileflowMapBuildManifestFileName,
30008
31000
  tileflowMapBuildManifestSchemaVersion,
30009
31001
  tileflowMapRevisionCanonicalization,
@@ -30012,6 +31004,8 @@ export {
30012
31004
  tileflowRendererDeploymentLimits,
30013
31005
  tileflowRendererDeploymentResponseSchema,
30014
31006
  tileflowSharedIconMapRevisionSchemaVersion,
31007
+ tileflowStyleFamilyLimits,
30015
31008
  tileflowStyleInspectionSchemaVersion,
30016
- validateTileflowDataCompatibility
31009
+ validateTileflowDataCompatibility,
31010
+ withTileflowFamilyTemplate
30017
31011
  };