tileflow 0.1.0-alpha.6 → 0.1.0-alpha.8

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/index.js CHANGED
@@ -7,55 +7,59 @@ import { createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
7
7
  import { existsSync, readFileSync, realpathSync } from "fs";
8
8
  import { readFile as readFile7, writeFile as writeFile2 } from "fs/promises";
9
9
  import { hostname } from "os";
10
- import { dirname as dirname10, isAbsolute as isAbsolute4, relative as relative5, resolve as resolve7, sep as sep5 } from "path";
10
+ import { dirname as dirname12, isAbsolute as isAbsolute5, relative as relative5, resolve as resolve8, sep as sep5 } from "path";
11
11
  import { createInterface } from "readline/promises";
12
- import pc3 from "picocolors";
12
+ import pc6 from "picocolors";
13
13
  import { createLocalCoordinates, setupCoordinates } from "@tileflow/coordinates-runtime";
14
14
  import {
15
- auditTileflowMapThemeValues,
16
- getTileflowStyleFontFaces,
17
- parseResolvedTileflowMap as parseResolvedTileflowMap3,
15
+ auditTileflowMapThemeValues as auditTileflowMapThemeValues2,
16
+ getTileflowStyleFontFaces as getTileflowStyleFontFaces2,
17
+ parseResolvedTileflowMap as parseResolvedTileflowMap5,
18
18
  tileflowMapIdSchema as tileflowMapIdSchema2
19
19
  } from "@tileflow/core";
20
20
  import {
21
- createTileflowMapBuildManifest
21
+ createTileflowMapBuildManifest as createTileflowMapBuildManifest2
22
22
  } from "@tileflow/core/build";
23
23
  import {
24
- parseTileflowRuntimeManifest
24
+ parseTileflowRuntimeManifest as parseTileflowRuntimeManifest2
25
25
  } from "@tileflow/core/manifest";
26
+ import { resolveTileflowRenderer as resolveTileflowRenderer2 } from "@tileflow/core/native-profile";
26
27
  import {
27
28
  createTileflowArtifactDiagnostics as createTileflowArtifactDiagnostics3,
28
29
  createTileflowArtifactSession as createTileflowArtifactSession3,
29
- createTileflowBuildProvenance,
30
- createTileflowStyles,
31
- prepareTileflowLocalTilesets,
30
+ createTileflowBuildProvenance as createTileflowBuildProvenance2,
31
+ createTileflowStyles as createTileflowStyles2,
32
+ prepareTileflowLocalTilesets as prepareTileflowLocalTilesets2,
32
33
  writeTileflowBuildArtifacts
33
34
  } from "@tileflow/dev/artifacts";
34
35
  import {
35
- assertValidTileflowConfig as assertValidTileflowConfig2,
36
+ assertValidTileflowConfig as assertValidTileflowConfig3,
36
37
  defaultTileflowApiUrl,
37
38
  defaultTileflowConfigPath,
38
39
  defaultTileflowManifestPath,
39
- getTileflowMapNames as getTileflowMapNames3,
40
- loadTileflowConfigWithInputs as loadTileflowConfigWithInputs3,
41
- TileflowValidationError as TileflowValidationError4
40
+ getTileflowMapNames as getTileflowMapNames5,
41
+ loadTileflowConfigWithInputs as loadTileflowConfigWithInputs4,
42
+ TileflowValidationError as TileflowValidationError5
42
43
  } from "@tileflow/dev/config";
43
44
  import {
44
45
  bindTileflowStyleFontBundle,
45
- prepareTileflowStyleFonts,
46
+ prepareTileflowStyleFonts as prepareTileflowStyleFonts2,
46
47
  TileflowFontCompilationError
47
48
  } from "@tileflow/dev/fonts";
48
- import { compileTileflowIconPackages as compileTileflowIconPackages2, TileflowIconCompilationError as TileflowIconCompilationError3 } from "@tileflow/dev/icons";
49
+ import {
50
+ compileTileflowIconPackages as compileTileflowIconPackages3,
51
+ TileflowIconCompilationError as TileflowIconCompilationError3
52
+ } from "@tileflow/dev/icons";
49
53
  import { resolveTileflowPreview } from "@tileflow/dev/preview";
50
54
  import {
51
55
  createTileflowComparisonRequestHandler,
52
- createTileflowDevRequestHandler
56
+ createTileflowDevRequestHandler as createTileflowDevRequestHandler2
53
57
  } from "@tileflow/dev/server";
54
58
  import {
55
- createTileflowCommandFailureDocument as createTileflowCommandFailureDocument4,
56
- createTileflowCommandSummary as createTileflowCommandSummary2,
57
- createTileflowStructuredDiagnostics as createTileflowStructuredDiagnostics2,
58
- serializeTileflowCommandDocument as serializeTileflowCommandDocument4,
59
+ createTileflowCommandFailureDocument as createTileflowCommandFailureDocument5,
60
+ createTileflowCommandSummary as createTileflowCommandSummary3,
61
+ createTileflowStructuredDiagnostics as createTileflowStructuredDiagnostics3,
62
+ serializeTileflowCommandDocument as serializeTileflowCommandDocument5,
59
63
  TileflowStyleValidationError as TileflowStyleValidationError2
60
64
  } from "@tileflow/dev/validation";
61
65
 
@@ -382,14 +386,14 @@ async function runExplain(options, compileStyle) {
382
386
  });
383
387
  if (!selection) throw new Error("Explain selection was not resolved.");
384
388
  if (!output.result.ok) {
385
- const failure = createTileflowCommandFailureDocument(
389
+ const failure2 = createTileflowCommandFailureDocument(
386
390
  "explain",
387
391
  { diagnostics: output.structuredDiagnostics },
388
392
  process.cwd(),
389
393
  { code: "COMPILATION_FAILED", phase: "compilation" }
390
394
  );
391
395
  const document2 = {
392
- ...failure,
396
+ ...failure2,
393
397
  authoringManifestSchemaVersion: tileflowAuthoringManifestSchemaVersion,
394
398
  selection,
395
399
  compilation: output.compilation
@@ -419,12 +423,12 @@ async function runExplain(options, compileStyle) {
419
423
  if (options.json) process.stdout.write(serializeTileflowCommandDocument(document));
420
424
  else printHumanExplain(document);
421
425
  } catch (error) {
422
- const failure = createTileflowCommandFailureDocument("explain", error, process.cwd(), {
426
+ const failure2 = createTileflowCommandFailureDocument("explain", error, process.cwd(), {
423
427
  code: "EXPLAIN_FAILED",
424
428
  phase: "compilation"
425
429
  });
426
- if (options.json) process.stderr.write(serializeTileflowCommandDocument(failure));
427
- else printHumanCommandFailure(failure);
430
+ if (options.json) process.stderr.write(serializeTileflowCommandDocument(failure2));
431
+ else printHumanCommandFailure(failure2);
428
432
  process.exitCode = 1;
429
433
  }
430
434
  }
@@ -468,12 +472,12 @@ async function runSemanticDiff(options, defaultConfigPath2) {
468
472
  if (options.json) process.stdout.write(serializeTileflowCommandDocument(document));
469
473
  else printHumanSemanticDiff(difference);
470
474
  } catch (error) {
471
- const failure = createTileflowCommandFailureDocument("semantic-diff", error, process.cwd(), {
475
+ const failure2 = createTileflowCommandFailureDocument("semantic-diff", error, process.cwd(), {
472
476
  code: "SEMANTIC_DIFF_FAILED",
473
477
  phase: "semantic-diff"
474
478
  });
475
- if (options.json) process.stderr.write(serializeTileflowCommandDocument(failure));
476
- else printHumanCommandFailure(failure);
479
+ if (options.json) process.stderr.write(serializeTileflowCommandDocument(failure2));
480
+ else printHumanCommandFailure(failure2);
477
481
  process.exitCode = 1;
478
482
  }
479
483
  }
@@ -1212,14 +1216,14 @@ async function runCaptureWatchCommand(positionalScenes, options) {
1212
1216
  } catch (error) {
1213
1217
  if (signal.aborted || stopped) return;
1214
1218
  unresolvedFailure = true;
1215
- const failure = createTileflowCaptureFailureJson(error, cwd);
1219
+ const failure2 = createTileflowCaptureFailureJson(error, cwd);
1216
1220
  emit({
1217
1221
  event: "failed",
1218
1222
  generation,
1219
- code: failure.code,
1220
- phase: failure.phase,
1221
- diagnostics: failure.diagnostics,
1222
- ...failure.resources ? { resources: failure.resources } : {}
1223
+ code: failure2.code,
1224
+ phase: failure2.phase,
1225
+ diagnostics: failure2.diagnostics,
1226
+ ...failure2.resources ? { resources: failure2.resources } : {}
1223
1227
  });
1224
1228
  }
1225
1229
  };
@@ -1545,6 +1549,11 @@ function runGit(cwd, args) {
1545
1549
  });
1546
1550
  }
1547
1551
 
1552
+ // src/cli-version.ts
1553
+ function isRootCliVersionRequest(arguments_) {
1554
+ return arguments_.length === 1 && (arguments_[0] === "--version" || arguments_[0] === "-V");
1555
+ }
1556
+
1548
1557
  // src/config-inspect-command.ts
1549
1558
  import { inspectTileflowConfig } from "@tileflow/dev/inspect";
1550
1559
  import {
@@ -1563,18 +1572,18 @@ function registerConfigInspectCommand(inspect, dependencies) {
1563
1572
  }
1564
1573
  printHumanConfigInspection(document);
1565
1574
  } catch (error) {
1566
- const failure = createTileflowCommandFailureDocument2("inspect", error, process.cwd(), {
1575
+ const failure2 = createTileflowCommandFailureDocument2("inspect", error, process.cwd(), {
1567
1576
  code: "INSPECTION_FAILED",
1568
1577
  phase: "config-inspection"
1569
1578
  });
1570
1579
  if (options.json) {
1571
- process.stderr.write(serializeTileflowCommandDocument2(failure));
1580
+ process.stderr.write(serializeTileflowCommandDocument2(failure2));
1572
1581
  } else {
1573
- console.error(`${failure.message} [${failure.code}; ${failure.phase}]`);
1574
- for (const diagnostic of failure.diagnostics.slice(1)) {
1582
+ console.error(`${failure2.message} [${failure2.code}; ${failure2.phase}]`);
1583
+ for (const diagnostic of failure2.diagnostics.slice(1)) {
1575
1584
  console.error(`- ${diagnostic.path || "(root)"}: ${diagnostic.message}`);
1576
1585
  }
1577
- console.error(`Suggestion: ${failure.suggestion}`);
1586
+ console.error(`Suggestion: ${failure2.suggestion}`);
1578
1587
  }
1579
1588
  process.exitCode = 1;
1580
1589
  }
@@ -1654,19 +1663,19 @@ function registerCoordinatesCommands(program2, dependencies) {
1654
1663
  coordinates,
1655
1664
  "search",
1656
1665
  dependencies,
1657
- (options) => parseRequest("search", options, { query: options.query })
1666
+ (options) => parseCoordinatesRequest("search", { query: options.query })
1658
1667
  );
1659
1668
  registerOperation(
1660
1669
  coordinates,
1661
1670
  "describe",
1662
1671
  dependencies,
1663
- (options) => parseRequest("describe", options, { id: options.id })
1672
+ (options) => parseCoordinatesRequest("describe", { id: options.id })
1664
1673
  );
1665
1674
  registerOperation(
1666
1675
  coordinates,
1667
1676
  "operations",
1668
1677
  dependencies,
1669
- (options) => parseRequest("operations", options, { from: options.from, to: options.to })
1678
+ (options) => parseCoordinatesRequest("operations", { from: options.from, to: options.to })
1670
1679
  );
1671
1680
  registerOperation(
1672
1681
  coordinates,
@@ -1765,9 +1774,6 @@ function hasConvenienceOptions(command2, options) {
1765
1774
  const transform = options;
1766
1775
  return transform.from !== void 0 || transform.to !== void 0 || transform.positions !== void 0;
1767
1776
  }
1768
- function parseRequest(command2, options, request) {
1769
- return parseCoordinatesRequest(command2, request);
1770
- }
1771
1777
  function parseTransformRequest(options) {
1772
1778
  let positions;
1773
1779
  try {
@@ -1812,7 +1818,7 @@ async function readCoordinatesInput(input, signal) {
1812
1818
  async function readStandardInput(signal) {
1813
1819
  const chunks = [];
1814
1820
  let length = 0;
1815
- return new Promise((resolve8, reject) => {
1821
+ return new Promise((resolve9, reject) => {
1816
1822
  const finish = (result) => {
1817
1823
  process.stdin.removeListener("data", onData);
1818
1824
  process.stdin.removeListener("end", onEnd);
@@ -1834,7 +1840,7 @@ async function readStandardInput(signal) {
1834
1840
  }
1835
1841
  chunks.push(bytes);
1836
1842
  };
1837
- const onEnd = () => finish(() => resolve8(decodeInput(Buffer.concat(chunks))));
1843
+ const onEnd = () => finish(() => resolve9(decodeInput(Buffer.concat(chunks))));
1838
1844
  const onError = () => finish(() => reject(new CoordinatesInputError("INVALID_VALUE")));
1839
1845
  if (signal.aborted) {
1840
1846
  onAbort();
@@ -1892,10 +1898,10 @@ function serializeCoordinatesFailure(error, command2) {
1892
1898
  if (!(error instanceof CoordinatesContractError)) return void 0;
1893
1899
  const parsed = coordinatesFailureSchema.safeParse(error.toJSON());
1894
1900
  if (!parsed.success) return void 0;
1895
- const failure = parsed.data;
1901
+ const failure2 = parsed.data;
1896
1902
  return new CoordinatesContractError({
1897
- ...failure,
1898
- command: failure.command ?? command2 ?? null
1903
+ ...failure2,
1904
+ command: failure2.command ?? command2 ?? null
1899
1905
  }).toJSON();
1900
1906
  }
1901
1907
  function serializeSetupFailure(error) {
@@ -2217,6 +2223,7 @@ import { tileflowIconPackageLimits } from "@tileflow/core";
2217
2223
  var maximumHostedResponseBytes = 1024 * 1024;
2218
2224
  var safeTextSchema = z2.string().min(1).max(300).refine((value) => !/[\p{Cc}]/u.test(value));
2219
2225
  var safeIdentifierSchema = z2.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u);
2226
+ var safeStorageKeySchema = z2.string().min(1).max(512).regex(/^[A-Za-z0-9][A-Za-z0-9._:/-]*$/u).refine((value) => value.split("/").every((segment) => segment !== "." && segment !== ".."));
2220
2227
  var publicHttpUrlSchema = z2.url().max(2048).refine((value) => {
2221
2228
  const url = new URL(value);
2222
2229
  return (url.protocol === "http:" || url.protocol === "https:") && !url.username && !url.password && !url.hash;
@@ -2252,7 +2259,7 @@ var hostedFontBundleResponseSchema = z2.object({
2252
2259
  });
2253
2260
  var hostedStatusStyleSchema = z2.object({
2254
2261
  environment: safeTextSchema,
2255
- key: safeIdentifierSchema,
2262
+ key: safeStorageKeySchema,
2256
2263
  mapId: safeIdentifierSchema,
2257
2264
  size: z2.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
2258
2265
  uploaded: z2.iso.datetime({ offset: true })
@@ -2343,7 +2350,7 @@ async function startDeviceAuthorization(appUrl, body, options = {}) {
2343
2350
  async function pollDeviceToken(appUrl, authorization, input, options = {}) {
2344
2351
  const expiresAt = Date.now() + authorization.expiresIn * 1e3;
2345
2352
  const intervalMs = Math.max(authorization.interval, 1) * 1e3;
2346
- const wait = input.sleep ?? ((milliseconds) => new Promise((resolve8) => setTimeout(resolve8, milliseconds)));
2353
+ const wait = input.sleep ?? ((milliseconds) => new Promise((resolve9) => setTimeout(resolve9, milliseconds)));
2347
2354
  while (Date.now() < expiresAt) {
2348
2355
  await wait(intervalMs);
2349
2356
  const response = await requestHostedJson(
@@ -2673,7 +2680,7 @@ async function withHostedResponse(apiUrl, path, init, options, consume) {
2673
2680
  }
2674
2681
  function raceWithAbort(operation, signal) {
2675
2682
  if (signal.aborted) return Promise.reject(signal.reason);
2676
- return new Promise((resolve8, reject) => {
2683
+ return new Promise((resolve9, reject) => {
2677
2684
  const cleanup = () => signal.removeEventListener("abort", onAbort);
2678
2685
  const onAbort = () => {
2679
2686
  cleanup();
@@ -2683,7 +2690,7 @@ function raceWithAbort(operation, signal) {
2683
2690
  operation.then(
2684
2691
  (value) => {
2685
2692
  cleanup();
2686
- resolve8(value);
2693
+ resolve9(value);
2687
2694
  },
2688
2695
  (error) => {
2689
2696
  cleanup();
@@ -2801,6 +2808,33 @@ function safeTransportError(error, fallback) {
2801
2808
  return fallback;
2802
2809
  }
2803
2810
 
2811
+ // src/hosted-native-deploy.ts
2812
+ import { dirname as dirname5 } from "path";
2813
+ import {
2814
+ getTileflowStyleFontFaces,
2815
+ parseResolvedTileflowMap as parseResolvedTileflowMap2,
2816
+ serializeCanonicalJson
2817
+ } from "@tileflow/core";
2818
+ import {
2819
+ createTileflowMapBuildManifest,
2820
+ parseTileflowRendererDeploymentArtifact,
2821
+ tileflowRendererDeploymentResponseSchema
2822
+ } from "@tileflow/core/build";
2823
+ import { parseTileflowRuntimeManifest } from "@tileflow/core/manifest";
2824
+ import {
2825
+ createTileflowBuildProvenance,
2826
+ createTileflowStyles,
2827
+ prepareTileflowHostedNativeDeployment,
2828
+ prepareTileflowLocalTilesets
2829
+ } from "@tileflow/dev/artifacts";
2830
+ import {
2831
+ assertValidTileflowConfig,
2832
+ getTileflowMapNames,
2833
+ loadTileflowConfigWithInputs as loadTileflowConfigWithInputs2
2834
+ } from "@tileflow/dev/config";
2835
+ import { prepareTileflowStyleFonts } from "@tileflow/dev/fonts";
2836
+ import { compileTileflowIconPackages } from "@tileflow/dev/icons";
2837
+
2804
2838
  // src/hosted-preflight.ts
2805
2839
  import {
2806
2840
  compareCodeUnits as compareCodeUnits4,
@@ -2865,44 +2899,312 @@ function cloneStyle(style) {
2865
2899
  return JSON.parse(JSON.stringify(style));
2866
2900
  }
2867
2901
 
2902
+ // src/hosted-native-deploy.ts
2903
+ function failure(message) {
2904
+ return Object.assign(new Error(message), { code: "TF_NATIVE_DEPLOYMENT_INVALID" });
2905
+ }
2906
+ function requireNativeTarget(options) {
2907
+ if (!options.mapId || !/^map_[A-Za-z0-9_-]{16}$/u.test(options.mapId))
2908
+ throw failure("--with-native requires one valid --map-id.");
2909
+ const apiOrigin = normalizeApiOrigin(options.apiUrl);
2910
+ if (!apiOrigin.startsWith("https://"))
2911
+ throw failure("Hosted Native deployment requires an HTTPS API origin.");
2912
+ return { apiOrigin, mapId: options.mapId };
2913
+ }
2914
+ async function prepareNativeHostedDeploy(options) {
2915
+ const { apiOrigin } = requireNativeTarget(options);
2916
+ const loaded = await withTileflowConfigSecretsHidden(
2917
+ () => loadTileflowConfigWithInputs2(options.config)
2918
+ );
2919
+ assertValidTileflowConfig(loaded.project);
2920
+ const names = getTileflowMapNames(loaded.project);
2921
+ const mapName = options.map ?? (names.length === 1 ? names[0] : void 0);
2922
+ if (!mapName || !names.includes(mapName))
2923
+ throw failure("Select one configured map with --map for this Native deployment.");
2924
+ const map = loaded.project.maps[mapName];
2925
+ const project = { ...loaded.project, maps: { [mapName]: map } };
2926
+ const baseDirectory = dirname5(loaded.configFile);
2927
+ const compiledIcons = await compileTileflowIconPackages(project, {
2928
+ baseDirectory,
2929
+ cwd: process.cwd(),
2930
+ target: "hosted",
2931
+ icons: {
2932
+ ...options.cacheDir ? { cacheRoot: options.cacheDir } : {},
2933
+ ...options.offline ? { offline: true } : {}
2934
+ }
2935
+ });
2936
+ const binding = compiledIcons.bindings.find((entry) => entry.mapName === mapName);
2937
+ const iconPackage = binding ? compiledIcons.packages.find((entry) => entry.contentHash === binding.packageHash) : void 0;
2938
+ const sprite = binding ? `${apiOrigin}/sprites/preflight/${binding.packageHash}/sprite` : void 0;
2939
+ const mapAssets = Object.fromEntries(
2940
+ compiledIcons.bindings.map((entry) => [
2941
+ entry.mapName,
2942
+ {
2943
+ icons: {
2944
+ ids: entry.iconIds,
2945
+ sprite: `${apiOrigin}/sprites/preflight/${entry.packageHash}/sprite`
2946
+ }
2947
+ }
2948
+ ])
2949
+ );
2950
+ const styles = createTileflowStyles(project, { apiBaseUrl: apiOrigin, mapAssets });
2951
+ const local = await prepareTileflowLocalTilesets(project, styles, {
2952
+ assetBaseUrl: apiOrigin,
2953
+ baseDirectory,
2954
+ cwd: process.cwd()
2955
+ });
2956
+ try {
2957
+ const fonts = await prepareTileflowStyleFonts(project, local.styles, {
2958
+ assetBaseUrl: `${apiOrigin}/fonts/preflight`,
2959
+ baseDirectory,
2960
+ cwd: process.cwd(),
2961
+ target: "hosted"
2962
+ });
2963
+ if (inspectTileflowHostedCompatibility(project, fonts.styles).length)
2964
+ throw failure("Hosted renderer deployment requires compatible Tileflow World sources.");
2965
+ if (Object.keys(fonts.bundles).length)
2966
+ throw failure(
2967
+ "Hosted deploy does not yet support package-owned web fonts. Use an explicit public glyph provider."
2968
+ );
2969
+ const assets = (iconPackage?.files ?? []).map((file) => ({
2970
+ contentType: file.contentType,
2971
+ fileName: `icons/${mapName}/${file.fileName}`,
2972
+ source: file.source
2973
+ }));
2974
+ const provenance = await createTileflowBuildProvenance(process.cwd());
2975
+ const resolved = parseResolvedTileflowMap2(map);
2976
+ const inputStyles = JSON.parse(
2977
+ serializeCanonicalJson(fonts.styles[mapName])
2978
+ );
2979
+ const createArtifact = async (selectedSprite) => {
2980
+ const selected = JSON.parse(
2981
+ serializeCanonicalJson(inputStyles)
2982
+ );
2983
+ if (binding) {
2984
+ if (!sprite || !selectedSprite) throw failure("Missing confirmed sprite binding.");
2985
+ for (const style of Object.values(selected)) {
2986
+ if (style.sprite !== sprite)
2987
+ throw failure("Compiled sprite identity changed before publication.");
2988
+ style.sprite = selectedSprite;
2989
+ }
2990
+ }
2991
+ const family = prepareTileflowHostedThemeFamily(mapName, map, selected);
2992
+ const buildManifest = await createTileflowMapBuildManifest(
2993
+ {
2994
+ [mapName]: {
2995
+ assets,
2996
+ lineage: project.mapMetadata?.[mapName]?.lineage ?? [
2997
+ { id: map.id, mapVersion: map.version }
2998
+ ],
2999
+ map,
3000
+ sourceAssets: {
3001
+ fonts: fonts.sourceIdentities[mapName] ?? [],
3002
+ icons: compiledIcons.sourceIdentities[mapName] ?? [],
3003
+ ...compiledIcons.compositions[mapName] ? { iconComposition: compiledIcons.compositions[mapName] } : {}
3004
+ },
3005
+ styles: family.styles
3006
+ }
3007
+ },
3008
+ { provenance }
3009
+ );
3010
+ return prepareTileflowHostedNativeDeployment({
3011
+ mapId: mapName,
3012
+ buildManifest,
3013
+ styles: family.styles,
3014
+ teamSources: family.teamSources,
3015
+ assets,
3016
+ ...resolved.view ? { view: resolved.view } : {}
3017
+ });
3018
+ };
3019
+ const artifact = await createArtifact(sprite);
3020
+ return {
3021
+ mapName,
3022
+ artifact,
3023
+ packages: compiledIcons.packages,
3024
+ ...binding ? { iconBinding: binding } : {},
3025
+ ...compiledIcons.compositions[mapName] ? { iconComposition: compiledIcons.compositions[mapName] } : {},
3026
+ async retarget(sprites) {
3027
+ return binding ? createArtifact(sprites.get(binding.packageHash)) : artifact;
3028
+ },
3029
+ dispose: local.dispose
3030
+ };
3031
+ } catch (error) {
3032
+ await local.dispose();
3033
+ throw error;
3034
+ }
3035
+ }
3036
+ async function runHostedNativeDeploy(options, ports) {
3037
+ const { apiOrigin, mapId } = requireNativeTarget(options);
3038
+ const prepared = await (ports.prepare ?? prepareNativeHostedDeploy)({
3039
+ ...options,
3040
+ apiUrl: apiOrigin
3041
+ });
3042
+ try {
3043
+ const preflight = await parseTileflowRendererDeploymentArtifact(prepared.artifact);
3044
+ if (preflight.mapId !== prepared.mapName)
3045
+ throw failure("The prepared map does not match its artifact.");
3046
+ const existing = await ports.loadManifest(options.manifest, {
3047
+ overwriteSelfHosted: options.overwriteSelfHostedManifest === true
3048
+ });
3049
+ const previousOrigin = existing?.apiUrl ?? (existing ? Object.values(existing.maps)[0]?.apiUrl : void 0);
3050
+ if (previousOrigin !== void 0 && normalizeApiOrigin(previousOrigin) !== apiOrigin)
3051
+ throw failure("Existing manifest belongs to a different Tileflow API origin.");
3052
+ const api = await ports.resolveApi(prepared.mapName);
3053
+ if (!api) return null;
3054
+ if (api.apiUrl !== apiOrigin || api.mapId !== mapId || !api.apiKey || api.apiKey.length > 8192 || /[\p{Cc}]/u.test(api.apiKey))
3055
+ throw failure("Authorization did not confirm the requested API and Map.");
3056
+ const sprites = /* @__PURE__ */ new Map();
3057
+ for (const icon of prepared.packages) {
3058
+ if (sprites.has(icon.contentHash)) continue;
3059
+ let uploaded;
3060
+ try {
3061
+ uploaded = await (ports.uploadIcon ?? uploadHostedIconPackage)(api, icon, ports.request);
3062
+ } catch {
3063
+ throw failure("Native deployment icon upload failed. The local manifest was preserved.");
3064
+ }
3065
+ if (!uploaded.ok)
3066
+ throw failure("Native deployment icon upload failed. Retry the same artifact.");
3067
+ const expected = `${apiOrigin}/sprites/${uploaded.value.id}/sprite`;
3068
+ if (uploaded.value.contentHash !== icon.contentHash || uploaded.value.spriteUrl !== expected)
3069
+ throw failure("Icon publication did not confirm the owned resource and origin.");
3070
+ sprites.set(icon.contentHash, expected);
3071
+ }
3072
+ const artifact = await parseTileflowRendererDeploymentArtifact(
3073
+ await prepared.retarget(sprites)
3074
+ );
3075
+ if (artifact.mapId !== preflight.mapId || artifact.renderers.web.buildManifest.maps[artifact.mapId].mapRevisionSha256 !== preflight.renderers.web.buildManifest.maps[preflight.mapId].mapRevisionSha256)
3076
+ throw failure("Authored map identity changed after preflight.");
3077
+ const iconPackage = prepared.iconBinding ? prepared.packages.find((entry) => entry.contentHash === prepared.iconBinding.packageHash) : void 0;
3078
+ if (prepared.iconComposition && prepared.iconComposition.packageHash !== iconPackage?.contentHash)
3079
+ throw failure("Icon composition does not match the confirmed effective package.");
3080
+ let response;
3081
+ try {
3082
+ response = await requestHostedJson(
3083
+ apiOrigin,
3084
+ "/v1/styles",
3085
+ {
3086
+ method: "POST",
3087
+ headers: {
3088
+ Authorization: `Bearer ${api.apiKey}`,
3089
+ "X-Tileflow-Map-Id": mapId,
3090
+ "Content-Type": "application/json"
3091
+ },
3092
+ body: serializeCanonicalJson({
3093
+ artifact,
3094
+ environment: prepared.mapName,
3095
+ managedMapId: mapId,
3096
+ usageMode: "session",
3097
+ source: ports.source,
3098
+ ...iconPackage && prepared.iconBinding ? {
3099
+ iconPackage: {
3100
+ contentHash: iconPackage.contentHash,
3101
+ label: prepared.iconBinding.label
3102
+ }
3103
+ } : {},
3104
+ ...prepared.iconComposition ? { iconComposition: prepared.iconComposition } : {}
3105
+ })
3106
+ },
3107
+ ports.request
3108
+ );
3109
+ } catch {
3110
+ throw failure(
3111
+ "Renderer deployment response was unavailable. Retry the same artifact; the local manifest was preserved."
3112
+ );
3113
+ }
3114
+ if (!response.ok || !response.json)
3115
+ throw failure("Renderer deployment failed. The local manifest was preserved.");
3116
+ const parsed = tileflowRendererDeploymentResponseSchema.safeParse(response.body);
3117
+ if (!parsed.success)
3118
+ throw failure("Invalid renderer deployment response. The local manifest was preserved.");
3119
+ const result = parsed.data;
3120
+ const names = Object.keys(artifact.renderers.web.styles).sort();
3121
+ const expectedManifestUrl = `${apiOrigin}/maps/${mapId}/native/manifest.json`;
3122
+ if (result.mapId !== mapId || result.renderers.native.manifestUrl !== expectedManifestUrl || names.join("\0") !== Object.keys(result.renderers.web.themes).sort().join("\0") || names.join("\0") !== Object.keys(result.renderers.native.themes).sort().join("\0"))
3123
+ throw failure(
3124
+ "Renderer deployment did not confirm the requested Map and complete theme families."
3125
+ );
3126
+ for (const name of names) {
3127
+ if (result.renderers.web.themes[name].styleUrl !== `${apiOrigin}/maps/${mapId}/${name}.json` || result.renderers.native.themes[name].styleUrl !== `${apiOrigin}/maps/${mapId}/native/v${result.version}/${name}.json`)
3128
+ throw failure("Renderer deployment returned an unexpected delivery route.");
3129
+ }
3130
+ const build = artifact.renderers.web.buildManifest.maps[artifact.mapId];
3131
+ const manifest = parseTileflowRuntimeManifest({
3132
+ apiUrl: apiOrigin,
3133
+ version: 1,
3134
+ maps: {
3135
+ ...existing?.maps ?? {},
3136
+ [prepared.mapName]: {
3137
+ defaultTheme: build.defaultTheme,
3138
+ environment: prepared.mapName,
3139
+ mapId,
3140
+ ...build.systemThemes ? { systemThemes: build.systemThemes } : {},
3141
+ ...artifact.view ? { view: artifact.view } : {},
3142
+ usageMode: "session",
3143
+ worldGeneration: "v1",
3144
+ themes: Object.fromEntries(
3145
+ names.map((name) => [
3146
+ name,
3147
+ {
3148
+ colorScheme: build.themes[name].colorScheme,
3149
+ fontFaces: getTileflowStyleFontFaces(
3150
+ artifact.renderers.web.styles[name]
3151
+ ),
3152
+ revision: build.themes[name].styleSha256,
3153
+ ...result.renderers.web.themes[name]
3154
+ }
3155
+ ])
3156
+ )
3157
+ }
3158
+ }
3159
+ });
3160
+ const manifestPath = await ports.writeManifest(options.manifest, manifest);
3161
+ return Object.freeze({
3162
+ manifestPath,
3163
+ mapName: prepared.mapName,
3164
+ mapId,
3165
+ deploymentId: result.deploymentId,
3166
+ version: result.version,
3167
+ changed: result.changed,
3168
+ nativeManifestUrl: expectedManifestUrl
3169
+ });
3170
+ } finally {
3171
+ await prepared.dispose();
3172
+ }
3173
+ }
3174
+
2868
3175
  // src/icon-diff-command.ts
2869
- import { dirname as dirname6, resolve as resolve4 } from "path";
2870
- import { z as z4 } from "zod";
3176
+ import { dirname as dirname7, resolve as resolve4 } from "path";
3177
+ import { z as z3 } from "zod";
2871
3178
  import {
2872
3179
  diffTileflowIconPackageManifests,
2873
3180
  hashTileflowIconPackageManifest,
2874
3181
  tileflowIconPackageContentHashSchema,
2875
3182
  tileflowIconPackageLabelSchema,
2876
- tileflowIconPackageLimits as tileflowIconPackageLimits3,
3183
+ tileflowIconPackageLimits as tileflowIconPackageLimits2,
2877
3184
  tileflowIconPackageManifestSchema,
2878
3185
  tileflowMapIdSchema
2879
3186
  } from "@tileflow/core";
2880
3187
  import {
2881
- assertValidTileflowConfig,
2882
- getTileflowMapNames,
2883
- loadTileflowConfigWithInputs as loadTileflowConfigWithInputs2
3188
+ assertValidTileflowConfig as assertValidTileflowConfig2,
3189
+ getTileflowMapNames as getTileflowMapNames2,
3190
+ loadTileflowConfigWithInputs as loadTileflowConfigWithInputs3
2884
3191
  } from "@tileflow/dev/config";
2885
- import { compileTileflowIconPackages } from "@tileflow/dev/icons";
3192
+ import {
3193
+ compileTileflowIconPackages as compileTileflowIconPackages2
3194
+ } from "@tileflow/dev/icons";
2886
3195
 
2887
3196
  // src/icon-diff-report.ts
2888
3197
  import { randomUUID as randomUUID2 } from "crypto";
2889
3198
  import { mkdir as mkdir3, readFile as readFile3, rename as rename3, unlink as unlink3, writeFile } from "fs/promises";
2890
- import { basename as basename2, dirname as dirname5, join } from "path";
2891
- import { z as z3 } from "zod";
3199
+ import { basename as basename2, dirname as dirname6, join } from "path";
2892
3200
  import {
2893
3201
  compareCodeUnits as compareCodeUnits5,
2894
3202
  sha256Hex,
2895
- tileflowIconIdSchema,
2896
- tileflowIconPackageLimits as tileflowIconPackageLimits2
3203
+ tileflowIconSpriteIndexSchema
2897
3204
  } from "@tileflow/core";
2898
- var spriteEntrySchema = z3.object({
2899
- height: z3.number().int().positive().max(tileflowIconPackageLimits2.maxAtlasDimension),
2900
- pixelRatio: z3.union([z3.literal(1), z3.literal(2)]),
2901
- width: z3.number().int().positive().max(tileflowIconPackageLimits2.maxAtlasDimension),
2902
- x: z3.number().int().nonnegative().max(tileflowIconPackageLimits2.maxAtlasDimension),
2903
- y: z3.number().int().nonnegative().max(tileflowIconPackageLimits2.maxAtlasDimension)
2904
- }).strict();
2905
- var spriteIndexSchema = z3.record(tileflowIconIdSchema, spriteEntrySchema);
3205
+ import {
3206
+ renderTileflowIconAtlasPreview
3207
+ } from "@tileflow/dev/icons";
2906
3208
  async function writeIconDiffReport(input) {
2907
3209
  const [before, after] = await Promise.all([
2908
3210
  input.baseline?.package ? loadRemoteSprite(input.baseline.package) : null,
@@ -2940,18 +3242,20 @@ function loadCompiledSprite(iconPackage) {
2940
3242
  iconPackage.manifest.sprites.twoX
2941
3243
  );
2942
3244
  }
2943
- function reportSpriteFromFiles(files, oneX, twoX) {
3245
+ async function reportSpriteFromFiles(files, oneX, twoX) {
2944
3246
  const oneXJson = requiredFile(files, "sprite.json");
2945
3247
  const oneXPng = requiredFile(files, "sprite.png");
2946
3248
  const twoXJson = requiredFile(files, "sprite@2x.json");
2947
3249
  const twoXPng = requiredFile(files, "sprite@2x.png");
3250
+ const indexOneX = parseIndex(oneXJson, 1, oneX.width, oneX.height);
3251
+ const indexTwoX = parseIndex(twoXJson, 2, twoX.width, twoX.height);
2948
3252
  return {
2949
- indexOneX: parseIndex(oneXJson, 1, oneX.width, oneX.height),
2950
- indexTwoX: parseIndex(twoXJson, 2, twoX.width, twoX.height),
2951
- oneXDataUrl: pngDataUrl(oneXPng),
3253
+ indexOneX,
3254
+ indexTwoX,
3255
+ oneXDataUrl: pngDataUrl(await renderTileflowIconAtlasPreview(oneXPng, indexOneX)),
2952
3256
  oneXHeight: oneX.height,
2953
3257
  oneXWidth: oneX.width,
2954
- twoXDataUrl: pngDataUrl(twoXPng),
3258
+ twoXDataUrl: pngDataUrl(await renderTileflowIconAtlasPreview(twoXPng, indexTwoX)),
2955
3259
  twoXHeight: twoX.height,
2956
3260
  twoXWidth: twoX.width
2957
3261
  };
@@ -2963,7 +3267,7 @@ function parseIndex(source, pixelRatio, atlasWidth, atlasHeight) {
2963
3267
  } catch {
2964
3268
  throw new Error("Sprite report index is not valid UTF-8 JSON");
2965
3269
  }
2966
- const parsed = spriteIndexSchema.safeParse(value);
3270
+ const parsed = tileflowIconSpriteIndexSchema.safeParse(value);
2967
3271
  if (!parsed.success) {
2968
3272
  throw new Error("Sprite report index does not match the MapLibre schema");
2969
3273
  }
@@ -3358,9 +3662,9 @@ async function writeAtomicReport(path, html, force) {
3358
3662
  } catch (error) {
3359
3663
  if (!isMissingFile3(error)) throw error;
3360
3664
  }
3361
- await mkdir3(dirname5(path), { recursive: true });
3665
+ await mkdir3(dirname6(path), { recursive: true });
3362
3666
  const temporaryPath = join(
3363
- dirname5(path),
3667
+ dirname6(path),
3364
3668
  `.${basename2(path)}.${process.pid}.${randomUUID2()}.tmp`
3365
3669
  );
3366
3670
  try {
@@ -3389,59 +3693,59 @@ function isMissingFile3(error) {
3389
3693
 
3390
3694
  // src/icon-diff-command.ts
3391
3695
  var environmentSchema = tileflowMapIdSchema;
3392
- var publicHttpUrlSchema2 = z4.url().max(2048).refine((value) => {
3696
+ var publicHttpUrlSchema2 = z3.url().max(2048).refine((value) => {
3393
3697
  const url = new URL(value);
3394
3698
  return (url.protocol === "http:" || url.protocol === "https:") && !url.username && !url.password;
3395
3699
  });
3396
- var iconPackageBaselineResponseSchema = z4.object({
3397
- baseline: z4.object({
3398
- deployedAt: z4.iso.datetime({ offset: true }),
3399
- deploymentId: z4.string().min(1).max(128),
3400
- package: z4.object({
3700
+ var iconPackageBaselineResponseSchema = z3.object({
3701
+ baseline: z3.object({
3702
+ deployedAt: z3.iso.datetime({ offset: true }),
3703
+ deploymentId: z3.string().min(1).max(128),
3704
+ package: z3.object({
3401
3705
  contentHash: tileflowIconPackageContentHashSchema,
3402
3706
  label: tileflowIconPackageLabelSchema,
3403
3707
  manifest: tileflowIconPackageManifestSchema,
3404
3708
  spriteUrl: publicHttpUrlSchema2,
3405
- totalBytes: z4.number().int().positive().max(tileflowIconPackageLimits3.maxGeneratedPackageBytes)
3709
+ totalBytes: z3.number().int().positive().max(tileflowIconPackageLimits2.maxGeneratedPackageBytes)
3406
3710
  }).strict().nullable(),
3407
- version: z4.number().int().positive()
3711
+ version: z3.number().int().positive()
3408
3712
  }).strict().nullable(),
3409
3713
  environment: environmentSchema,
3410
- schemaVersion: z4.literal(1)
3714
+ schemaVersion: z3.literal(1)
3411
3715
  }).strict();
3412
- var packageSummarySchema = z4.object({
3716
+ var packageSummarySchema = z3.object({
3413
3717
  contentHash: tileflowIconPackageContentHashSchema,
3414
- iconCount: z4.number().int().nonnegative().max(tileflowIconPackageLimits3.maxIconCount),
3718
+ iconCount: z3.number().int().nonnegative().max(tileflowIconPackageLimits2.maxIconCount),
3415
3719
  label: tileflowIconPackageLabelSchema,
3416
- totalBytes: z4.number().int().nonnegative().max(tileflowIconPackageLimits3.maxGeneratedPackageBytes)
3720
+ totalBytes: z3.number().int().nonnegative().max(tileflowIconPackageLimits2.maxGeneratedPackageBytes)
3417
3721
  }).strict();
3418
- var tileflowIconDiffDocumentSchema = z4.object({
3419
- schemaVersion: z4.literal(1),
3722
+ var tileflowIconDiffDocumentSchema = z3.object({
3723
+ schemaVersion: z3.literal(1),
3420
3724
  environment: environmentSchema,
3421
- baseline: z4.object({
3422
- deploymentId: z4.string(),
3725
+ baseline: z3.object({
3726
+ deploymentId: z3.string(),
3423
3727
  package: packageSummarySchema.nullable(),
3424
- version: z4.number().int().positive()
3728
+ version: z3.number().int().positive()
3425
3729
  }).strict().nullable(),
3426
- proposed: z4.object({
3730
+ proposed: z3.object({
3427
3731
  package: packageSummarySchema.nullable()
3428
3732
  }).strict(),
3429
- icons: z4.object({
3430
- added: z4.array(z4.string()),
3431
- removed: z4.array(z4.string()),
3432
- modified: z4.array(z4.string()),
3433
- unchangedCount: z4.number().int().nonnegative()
3733
+ icons: z3.object({
3734
+ added: z3.array(z3.string()),
3735
+ removed: z3.array(z3.string()),
3736
+ modified: z3.array(z3.string()),
3737
+ unchangedCount: z3.number().int().nonnegative()
3434
3738
  }).strict(),
3435
- generatedBytes: z4.object({
3436
- before: z4.number().int().nonnegative(),
3437
- after: z4.number().int().nonnegative(),
3438
- delta: z4.number().int()
3739
+ generatedBytes: z3.object({
3740
+ before: z3.number().int().nonnegative(),
3741
+ after: z3.number().int().nonnegative(),
3742
+ delta: z3.number().int()
3439
3743
  }).strict(),
3440
- artifacts: z4.object({ report: z4.string().nullable() }).strict(),
3441
- hasChanges: z4.boolean()
3744
+ artifacts: z3.object({ report: z3.string().nullable() }).strict(),
3745
+ hasChanges: z3.boolean()
3442
3746
  }).strict();
3443
3747
  function registerIconDiffCommand(icons, dependencies) {
3444
- icons.command("diff").description("Preview a local icon package against an active hosted revision").requiredOption("--against <environment>", "named map environment to compare").option("-c, --config <path>", "config path", dependencies.defaultConfigPath).option("--api-url <url>", "Tileflow API URL", process.env.TILEFLOW_API_URL).option("--api-key <key>", "Tileflow API key", process.env.TILEFLOW_API_KEY).requiredOption("--map-id <id>", "managed Map destination").option("--json", "print deterministic schema-version-1 JSON").option("--report <path>", "write a self-contained HTML visual report").option("--open", "open an explicitly requested report").option("--force", "replace a different explicitly requested report").action(async (options) => {
3748
+ icons.command("diff").description("Preview a local icon package against an active hosted revision").requiredOption("--against <environment>", "named map environment to compare").option("-c, --config <path>", "config path", dependencies.defaultConfigPath).option("--api-url <url>", "Tileflow API URL", process.env.TILEFLOW_API_URL).option("--api-key <key>", "Tileflow API key", process.env.TILEFLOW_API_KEY).requiredOption("--map-id <id>", "managed Map destination").option("--json", "print deterministic schema-version-1 JSON").option("--cache-dir <path>", "verified Icon Set artifact cache root").option("--offline", "fail a locked Icon Set cache miss instead of hydrating it").option("--report <path>", "write a self-contained HTML visual report").option("--open", "open an explicitly requested report").option("--force", "replace a different explicitly requested report").action(async (options) => {
3445
3749
  try {
3446
3750
  await runIconDiff(options, dependencies);
3447
3751
  } catch (error) {
@@ -3462,11 +3766,11 @@ async function runIconDiff(options, dependencies) {
3462
3766
  }
3463
3767
  const environment = environmentSchema.parse(options.against);
3464
3768
  const loaded = await withTileflowConfigSecretsHidden(
3465
- () => loadTileflowConfigWithInputs2(options.config)
3769
+ () => loadTileflowConfigWithInputs3(options.config)
3466
3770
  );
3467
3771
  const project = loaded.project;
3468
- assertValidTileflowConfig(project);
3469
- const mapNames = getTileflowMapNames(project);
3772
+ assertValidTileflowConfig2(project);
3773
+ const mapNames = getTileflowMapNames2(project);
3470
3774
  if (!Object.hasOwn(project.maps, environment)) {
3471
3775
  throw new Error(
3472
3776
  `Unknown map environment "${environment}". Available maps: ${mapNames.join(", ") || "(none)"}`
@@ -3477,9 +3781,14 @@ async function runIconDiff(options, dependencies) {
3477
3781
  throw new Error("Missing Tileflow API key. Run tileflow login or set TILEFLOW_API_KEY.");
3478
3782
  }
3479
3783
  const selectedProject = { ...project, maps: { [environment]: project.maps[environment] } };
3480
- const compiled = await compileTileflowIconPackages(selectedProject, {
3481
- baseDirectory: dirname6(loaded.configFile),
3784
+ const icons = {
3785
+ ...options.cacheDir ? { cacheRoot: options.cacheDir } : {},
3786
+ ...options.offline ? { offline: true } : {}
3787
+ };
3788
+ const compiled = await compileTileflowIconPackages2(selectedProject, {
3789
+ baseDirectory: dirname7(loaded.configFile),
3482
3790
  cwd: process.cwd(),
3791
+ ...Object.keys(icons).length > 0 ? { icons } : {},
3483
3792
  target: "hosted"
3484
3793
  });
3485
3794
  const binding = compiled.bindings.find((candidate) => candidate.mapName === environment);
@@ -3621,10 +3930,10 @@ function formatSignedBytes(bytes) {
3621
3930
  }
3622
3931
 
3623
3932
  // src/icon-list-command.ts
3624
- import { dirname as dirname7 } from "path";
3933
+ import { dirname as dirname8 } from "path";
3625
3934
  import { compareCodeUnits as compareCodeUnits6 } from "@tileflow/core";
3626
3935
  import {
3627
- getTileflowMapNames as getTileflowMapNames2,
3936
+ getTileflowMapNames as getTileflowMapNames3,
3628
3937
  loadValidTileflowConfigWithInputs as loadValidTileflowConfigWithInputs2,
3629
3938
  TileflowValidationError as TileflowValidationError3
3630
3939
  } from "@tileflow/dev/config";
@@ -3633,7 +3942,7 @@ import {
3633
3942
  TileflowIconCompilationError as TileflowIconCompilationError2
3634
3943
  } from "@tileflow/dev/icons";
3635
3944
  function registerIconListCommand(icons, dependencies) {
3636
- icons.command("list").description("List each map icon directory composition as deterministic agent JSON").option("-c, --config <path>", "config path", dependencies.defaultConfigPath).option("--map <id>", "inspect one exact configured map").option("--json", "print deterministic schema-version-2 JSON").action(async (options) => {
3945
+ icons.command("list").description("List each map icon directory composition as deterministic agent JSON").option("-c, --config <path>", "config path", dependencies.defaultConfigPath).option("--map <id>", "inspect one exact configured map").option("--cache-dir <path>", "verified Icon Set artifact cache root").option("--offline", "fail a locked Icon Set cache miss instead of hydrating it").option("--json", "print deterministic schema-version-3 JSON").action(async (options) => {
3637
3946
  if (!options.json) {
3638
3947
  console.error(
3639
3948
  "tileflow icons list currently requires --json. Run tileflow icons list --json."
@@ -3651,7 +3960,7 @@ function registerIconListCommand(icons, dependencies) {
3651
3960
  }
3652
3961
  function createTileflowIconListJson(inspection) {
3653
3962
  return {
3654
- schemaVersion: 2,
3963
+ schemaVersion: 3,
3655
3964
  pathBase: "cwd",
3656
3965
  maps: [...inspection.maps].sort((left, right) => compareCodeUnits6(left.name, right.name)).map((map) => createMapJson(map, inspection.catalogs))
3657
3966
  };
@@ -3665,15 +3974,20 @@ async function runIconList(options) {
3665
3974
  () => loadValidTileflowConfigWithInputs2(options.config)
3666
3975
  );
3667
3976
  const project = loaded.project;
3668
- const mapIds = getTileflowMapNames2(project).sort(compareCodeUnits6);
3977
+ const mapIds = getTileflowMapNames3(project).sort(compareCodeUnits6);
3669
3978
  if (options.map && !Object.hasOwn(project.maps, options.map)) {
3670
3979
  throw new Error(
3671
3980
  `Unknown map "${options.map}". Available maps: ${mapIds.join(", ") || "(none)"}`
3672
3981
  );
3673
3982
  }
3983
+ const icons = {
3984
+ ...options.cacheDir ? { cacheRoot: options.cacheDir } : {},
3985
+ ...options.offline ? { offline: true } : {}
3986
+ };
3674
3987
  const inspection = await inspectTileflowIconCatalogs(project, {
3675
- baseDirectory: dirname7(loaded.configFile),
3988
+ baseDirectory: dirname8(loaded.configFile),
3676
3989
  cwd: process.cwd(),
3990
+ ...Object.keys(icons).length > 0 ? { icons } : {},
3677
3991
  ...options.map ? { mapNames: [options.map] } : {}
3678
3992
  });
3679
3993
  process.stdout.write(serializeTileflowIconListJson(createTileflowIconListJson(inspection)));
@@ -3684,7 +3998,7 @@ function createMapJson(map, catalogs) {
3684
3998
  }
3685
3999
  const mapIcons = map.icons;
3686
4000
  const catalog = catalogs.find(
3687
- (candidate) => candidate.compiledPackage.contentHash === mapIcons.packageHash && sameStrings(candidate.directories, mapIcons.directories)
4001
+ (candidate) => candidate.compiledPackage.contentHash === mapIcons.packageHash && sameContributors(candidate.contributors, mapIcons.contributors)
3688
4002
  );
3689
4003
  if (!catalog) {
3690
4004
  throw new Error(`Missing inspected icon catalog for map ${map.name}`);
@@ -3692,9 +4006,21 @@ function createMapJson(map, catalogs) {
3692
4006
  return {
3693
4007
  id: map.name,
3694
4008
  icons: {
3695
- kind: "directories",
3696
- directories: [...mapIcons.directories],
4009
+ kind: "sources",
4010
+ contributors: mapIcons.contributors.map(createContributorJson),
3697
4011
  finalIds: [...mapIcons.iconIds],
4012
+ ...catalog.icons.some((icon) => icon.appearance) ? {
4013
+ appearances: Object.fromEntries(
4014
+ catalog.icons.flatMap(
4015
+ (icon) => icon.appearance ? [[icon.id, icon.appearance]] : []
4016
+ )
4017
+ )
4018
+ } : {},
4019
+ ...catalog.icons.some((icon) => icon.layout) ? {
4020
+ layouts: Object.fromEntries(
4021
+ catalog.icons.flatMap((icon) => icon.layout ? [[icon.id, icon.layout]] : [])
4022
+ )
4023
+ } : {},
3698
4024
  insideWorkingTree: catalog.insideWorkingTree,
3699
4025
  replacements: catalog.replacements.map((replacement) => ({
3700
4026
  id: replacement.id,
@@ -3702,18 +4028,43 @@ function createMapJson(map, catalogs) {
3702
4028
  winner: replacement.winner
3703
4029
  })),
3704
4030
  packageHash: mapIcons.packageHash,
3705
- sources: [...catalog.icons].sort((left, right) => compareCodeUnits6(left.id, right.id)).map((icon) => ({
3706
- id: icon.id,
3707
- path: icon.source.path,
3708
- format: icon.source.format,
3709
- byteLength: icon.source.byteLength,
3710
- dimensions: icon.source.dimensions ? { width: icon.source.dimensions.width, height: icon.source.dimensions.height } : null
3711
- }))
4031
+ composition: mapIcons.composition,
4032
+ sources: [...catalog.icons].sort((left, right) => compareCodeUnits6(left.id, right.id)).map(
4033
+ (icon) => icon.source.kind === "icon-set" ? {
4034
+ kind: "icon-set",
4035
+ id: icon.id,
4036
+ contributor: icon.source.contributor,
4037
+ reference: icon.source.reference,
4038
+ version: icon.source.version
4039
+ } : {
4040
+ kind: "file",
4041
+ id: icon.id,
4042
+ contributor: icon.source.contributor,
4043
+ path: icon.source.path,
4044
+ format: icon.source.format,
4045
+ byteLength: icon.source.byteLength,
4046
+ dimensions: icon.source.dimensions ? { width: icon.source.dimensions.width, height: icon.source.dimensions.height } : null
4047
+ }
4048
+ )
3712
4049
  }
3713
4050
  };
3714
4051
  }
3715
- function sameStrings(left, right) {
3716
- return left.length === right.length && left.every((value, index) => value === right[index]);
4052
+ function createContributorJson(contributor) {
4053
+ return contributor.kind === "icon-set" ? {
4054
+ kind: "icon-set",
4055
+ label: contributor.label,
4056
+ iconIds: [...contributor.iconIds],
4057
+ reference: contributor.reference,
4058
+ version: contributor.version
4059
+ } : {
4060
+ kind: contributor.kind,
4061
+ label: contributor.label,
4062
+ iconIds: [...contributor.iconIds],
4063
+ insideWorkingTree: contributor.insideWorkingTree
4064
+ };
4065
+ }
4066
+ function sameContributors(left, right) {
4067
+ return left.length === right.length && left.every((contributor, index) => contributor.label === right[index]?.label);
3717
4068
  }
3718
4069
  function printIconListError(error) {
3719
4070
  if (error instanceof TileflowValidationError3) {
@@ -3732,116 +4083,1066 @@ function printIssues(heading, issues) {
3732
4083
  );
3733
4084
  }
3734
4085
 
3735
- // src/language-command.ts
3736
- import { readFile as readFile4 } from "fs/promises";
3737
- import { fileURLToPath } from "url";
4086
+ // src/icon-lock-command.ts
4087
+ import { dirname as dirname9 } from "path";
4088
+ import pc3 from "picocolors";
3738
4089
  import {
3739
- serializeCanonicalJson,
3740
- tileflowAuthoringManifest
4090
+ collectTileflowIconSetReferences,
4091
+ compareCodeUnits as compareCodeUnits7,
4092
+ parseResolvedTileflowMap as parseResolvedTileflowMap3,
4093
+ parseTileflowIconsLockfile,
4094
+ tileflowIconSetReferenceSchema as tileflowIconSetReferenceSchema2,
4095
+ tileflowIconsLockfileName
3741
4096
  } from "@tileflow/core";
3742
4097
  import {
3743
- createTileflowCommandFailureDocument as createTileflowCommandFailureDocument3,
3744
- serializeTileflowCommandDocument as serializeTileflowCommandDocument3
3745
- } from "@tileflow/dev/validation";
3746
- function registerLanguageCommand(program2, dependencies = {}) {
3747
- const language = program2.command("language").description("Inspect the complete machine-readable Tileflow semantic language");
3748
- language.command("manifest").description("Print the semantic compiler authoring manifest").option("--json", "print deterministic JSON").action(
3749
- async (options) => runLanguageCommand(
3750
- "language-manifest",
3751
- options,
3752
- () => printManifest((dependencies.loadManifest ?? (() => tileflowAuthoringManifest))(), options)
3753
- )
3754
- );
3755
- language.command("schema").description("Print the generated authoring and resolved JSON Schema").option("--json", "print deterministic JSON").action(
3756
- async (options) => runLanguageCommand(
3757
- "language-schema",
3758
- options,
3759
- async () => printSchema(await (dependencies.loadSchema ?? loadPackagedSchema)(), options)
3760
- )
3761
- );
3762
- }
3763
- async function runLanguageCommand(command2, options, operation) {
4098
+ getTileflowMapNames as getTileflowMapNames4,
4099
+ loadValidTileflowConfigWithInputs as loadValidTileflowConfigWithInputs3,
4100
+ TileflowValidationError as TileflowValidationError4
4101
+ } from "@tileflow/dev/config";
4102
+ import { readTileflowIconsLockfileText, writeTileflowIconsLockfile } from "@tileflow/dev/icons";
4103
+
4104
+ // src/hosted-team.ts
4105
+ import pc from "picocolors";
4106
+ import { serializeCanonicalJson as serializeCanonicalJson2 } from "@tileflow/core";
4107
+ async function resolveTeamAuthority(options, scopes, dependencies) {
4108
+ let apiOrigin;
3764
4109
  try {
3765
- await operation();
4110
+ apiOrigin = normalizeApiOrigin(options.apiUrl ?? dependencies.defaultApiUrl);
3766
4111
  } catch (error) {
3767
- const failure = createTileflowCommandFailureDocument3(command2, error, process.cwd(), {
3768
- code: command2 === "language-manifest" ? "LANGUAGE_MANIFEST_FAILED" : "LANGUAGE_SCHEMA_FAILED",
3769
- phase: "language-discovery"
3770
- });
3771
- if (options.json) process.stderr.write(serializeTileflowCommandDocument3(failure));
3772
- else {
3773
- console.error(`${failure.message} [${failure.code}; ${failure.phase}]`);
3774
- console.error(`Suggestion: ${failure.suggestion}`);
3775
- }
3776
- process.exitCode = 1;
3777
- }
3778
- }
3779
- function printManifest(manifest, options) {
3780
- if (options.json) {
3781
- process.stdout.write(`${serializeCanonicalJson(manifest)}
3782
- `);
3783
- return;
4112
+ emitFailure(options.json, "invalid_api_url", safeMessage(error));
4113
+ return null;
3784
4114
  }
3785
- console.log(
3786
- `${manifest.language} (${manifest.compiler.name} v${manifest.compiler.version}); ${manifest.domains.length} domains, ${manifest.expressions.builders.length} expression builders.`
3787
- );
3788
- console.log("Run tileflow language manifest --json for the complete machine-readable document.");
3789
- }
3790
- function printSchema(schema, options) {
3791
- if (options.json) {
3792
- process.stdout.write(`${serializeCanonicalJson(schema)}
3793
- `);
3794
- return;
4115
+ if (options.apiKey) {
4116
+ if (!/^tf_live_[0-9a-f]{48}$/u.test(options.apiKey)) {
4117
+ emitFailure(options.json, "invalid_team_data_key", "Team data API key is invalid.");
4118
+ return null;
4119
+ }
4120
+ if (options.team) {
4121
+ emitFailure(
4122
+ options.json,
4123
+ "team_selector_with_api_key",
4124
+ "A Team data key already selects its Team; omit --team."
4125
+ );
4126
+ return null;
4127
+ }
4128
+ return { apiOrigin, credential: options.apiKey, team: null };
3795
4129
  }
3796
- console.log(
3797
- `Tileflow config reference schema ${String(schema.schemaVersion)}: ${String(schema.$id)}.`
3798
- );
3799
- console.log("Run tileflow language schema --json for the complete machine-readable document.");
3800
- }
3801
- async function loadPackagedSchema() {
3802
- const schemaUrl = import.meta.resolve("@tileflow/core/config-reference.json");
3803
- const parsed = JSON.parse(await readFile4(fileURLToPath(schemaUrl), "utf8"));
3804
- if (!isRecord(parsed))
3805
- throw new Error("Packaged Tileflow config reference is not a JSON object.");
3806
- return parsed;
3807
- }
3808
- function isRecord(value) {
3809
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
3810
- }
3811
-
3812
- // src/open-external.ts
3813
- import { spawn as spawnChild } from "child_process";
3814
- import { platform as hostPlatform } from "os";
3815
- function openTileflowExternal(target, dependencies = {}) {
3816
- const invocation = resolveTileflowExternalOpenInvocation(
3817
- target,
3818
- dependencies.platform ?? hostPlatform()
3819
- );
3820
- const spawn2 = dependencies.spawn ?? spawnChild;
3821
- let child;
4130
+ let config;
3822
4131
  try {
3823
- child = spawn2(invocation.command, invocation.args, {
3824
- detached: true,
3825
- shell: false,
3826
- stdio: "ignore",
3827
- windowsHide: true
3828
- });
4132
+ config = await dependencies.loadAuthConfig();
3829
4133
  } catch (error) {
3830
- dependencies.onError?.(error);
3831
- return;
4134
+ emitFailure(options.json, "auth_state_unavailable", safeMessage(error));
4135
+ return null;
3832
4136
  }
3833
- child.once("error", (error) => dependencies.onError?.(error));
3834
- child.unref();
3835
- }
3836
- function resolveTileflowExternalOpenInvocation(target, platform) {
3837
- if (!target || target.includes("\0")) throw new Error("The external target is invalid.");
3838
- if (platform === "darwin") return { command: "open", args: [target] };
3839
- if (platform === "win32") return { command: "explorer.exe", args: [target] };
4137
+ const selected = resolveAccountSession(config, options.apiUrl ?? dependencies.defaultApiUrl);
4138
+ if (selected.kind !== "selected") {
4139
+ emitFailure(
4140
+ options.json,
4141
+ `account_session_${selected.kind}`,
4142
+ selected.kind === "expired" ? "Account session expired. Run tileflow login." : "Run tileflow login."
4143
+ );
4144
+ return null;
4145
+ }
4146
+ const discovered = await listAccountTeams(selected.session);
4147
+ if (!discovered.ok) {
4148
+ emitFailure(options.json, "team_discovery_failed", discovered.error);
4149
+ return null;
4150
+ }
4151
+ const requested = options.team ? /^@([A-Za-z0-9][A-Za-z0-9._-]{0,127})$/u.exec(options.team) : null;
4152
+ if (options.team && !requested) {
4153
+ emitFailure(options.json, "invalid_team", "Team must use @team syntax.");
4154
+ return null;
4155
+ }
4156
+ const team = requested ? discovered.teams.find(({ slug }) => slug === requested[1]) : discovered.teams.length === 1 ? discovered.teams[0] : null;
4157
+ if (!team) {
4158
+ const options_ = discovered.teams.map(({ slug }) => `@${slug}`);
4159
+ emitFailure(
4160
+ options.json,
4161
+ options_.length > 1 ? "team_ambiguous" : "team_not_found",
4162
+ options_.length > 1 ? `Choose one Team with --team: ${options_.join(", ")}.` : "No matching Team is available.",
4163
+ { options: options_ }
4164
+ );
4165
+ return null;
4166
+ }
4167
+ const capability = await requestTeamCapability(selected.session, `@${team.slug}`, scopes);
4168
+ if (!capability.ok) {
4169
+ emitFailure(options.json, "team_capability_failed", capability.error);
4170
+ return null;
4171
+ }
4172
+ return {
4173
+ apiOrigin: selected.session.apiOrigin,
4174
+ credential: capability.capability,
4175
+ team
4176
+ };
4177
+ }
4178
+ function authorizedTeamRequest(authority, path, method, init = {}) {
4179
+ return requestHostedJson(authority.apiOrigin, path, {
4180
+ ...init.body === void 0 ? {} : { body: init.body },
4181
+ headers: { Authorization: `Bearer ${authority.credential}`, ...init.headers },
4182
+ method
4183
+ });
4184
+ }
4185
+ function emitJson(value) {
4186
+ process.stdout.write(`${serializeCanonicalJson2(value)}
4187
+ `);
4188
+ }
4189
+ function emitFailure(json, code, message, context = {}) {
4190
+ if (json) {
4191
+ process.stderr.write(
4192
+ `${serializeCanonicalJson2({ error: { code, ...context, message }, ok: false, schemaVersion: 1 })}
4193
+ `
4194
+ );
4195
+ } else {
4196
+ console.error(`${pc.red("Error:")} ${message}`);
4197
+ }
4198
+ process.exitCode = 1;
4199
+ }
4200
+ function safeMessage(error) {
4201
+ return error instanceof Error ? error.message : "Team command failed.";
4202
+ }
4203
+
4204
+ // src/icon-set-command.ts
4205
+ import { isAbsolute as isAbsolute2 } from "path";
4206
+ import pc2 from "picocolors";
4207
+ import { z as z4 } from "zod";
4208
+ import {
4209
+ tileflowIconPackageLimits as tileflowIconPackageLimits3,
4210
+ tileflowIconSetPackageIdSchema,
4211
+ tileflowIconSetPinSchema,
4212
+ tileflowIconSetReferenceSchema
4213
+ } from "@tileflow/core";
4214
+ import { composeTileflowIconSources } from "@tileflow/dev/icons";
4215
+ var maximumCatalogPages = 100;
4216
+ var pageSize = 100;
4217
+ function registerIconSetCommands(program2, dependencies) {
4218
+ const iconSet = program2.command("icon-set").description("Publish and manage versioned Team Icon Sets");
4219
+ withTeamOptions(
4220
+ iconSet.command("publish").description("Compile one local directory and publish it as an immutable Team revision").argument("<directory>", "repository-local icon directory").requiredOption("--id <id>", "Team-local Icon Set slug").requiredOption("--idempotency-key <key>", "durable retry key for this exact publication").option("--name <name>", "display name").option("--description <text>", "display description")
4221
+ ).action(
4222
+ async (directory, options) => {
4223
+ try {
4224
+ if (!isIconSetSlug(options.id)) {
4225
+ return emitFailure(options.json, "invalid_icon_set_id", "Icon Set ID is invalid.");
4226
+ }
4227
+ if (!isIdempotencyKey(options.idempotencyKey)) {
4228
+ return emitFailure(
4229
+ options.json,
4230
+ "invalid_idempotency_key",
4231
+ "Idempotency key must be 8 to 128 characters from A-Z a-z 0-9 . _ : -"
4232
+ );
4233
+ }
4234
+ const source = normalizeLocalDirectory(directory);
4235
+ if (!source) {
4236
+ return emitFailure(
4237
+ options.json,
4238
+ "invalid_icon_directory",
4239
+ "Publish one repository-relative icon directory."
4240
+ );
4241
+ }
4242
+ let iconPackage;
4243
+ try {
4244
+ const composed = await composeTileflowIconSources([source], {
4245
+ cwd: process.cwd(),
4246
+ target: "hosted"
4247
+ });
4248
+ if (!composed.package) throw new Error("The selected directory has no icons.");
4249
+ iconPackage = composed.package;
4250
+ } catch (error) {
4251
+ return emitFailure(options.json, "icon_compilation_failed", safeMessage(error));
4252
+ }
4253
+ const authority = await resolveTeamAuthority(options, ["icons:write"], dependencies);
4254
+ if (!authority) return;
4255
+ const body = new FormData();
4256
+ for (const file of iconPackage.files) {
4257
+ const bytes = new Uint8Array(file.source.byteLength);
4258
+ bytes.set(file.source);
4259
+ body.append(
4260
+ file.fileName,
4261
+ new Blob([bytes.buffer], { type: file.contentType }),
4262
+ file.fileName
4263
+ );
4264
+ }
4265
+ const response = await authorizedTeamRequest(
4266
+ authority,
4267
+ `/v1/icon-sets/${encodeURIComponent(options.id)}/versions/${encodeURIComponent(iconPackage.contentHash)}`,
4268
+ "PUT",
4269
+ { body, headers: { "Idempotency-Key": options.idempotencyKey } }
4270
+ );
4271
+ if (!response.ok) return emitHttpFailure(options.json, response.status, "publish");
4272
+ const parsed = publishResponseSchema.parse(response.body);
4273
+ if (parsed.revision.pin && parsed.revision.pin.contentHash !== iconPackage.contentHash) {
4274
+ return emitFailure(
4275
+ options.json,
4276
+ "icon_set_publish_unconfirmed",
4277
+ "Publication response did not confirm the submitted generated artifact."
4278
+ );
4279
+ }
4280
+ const metadata = {
4281
+ ...options.description === void 0 ? {} : { description: options.description },
4282
+ ...options.name === void 0 ? {} : { name: options.name }
4283
+ };
4284
+ if (Object.keys(metadata).length > 0) {
4285
+ try {
4286
+ const metadataResponse = await authorizedTeamRequest(
4287
+ authority,
4288
+ `/v1/icon-sets/${encodeURIComponent(options.id)}`,
4289
+ "PATCH",
4290
+ {
4291
+ body: JSON.stringify(metadata),
4292
+ headers: { "Content-Type": "application/json" }
4293
+ }
4294
+ );
4295
+ if (!metadataResponse.ok || !catalogDetailResponseSchema.safeParse(metadataResponse.body).success) {
4296
+ return emitFailure(
4297
+ options.json,
4298
+ "icon_set_metadata_update_failed",
4299
+ "Icon Set publication succeeded, but display metadata could not be updated; rerun the same command."
4300
+ );
4301
+ }
4302
+ } catch {
4303
+ return emitFailure(
4304
+ options.json,
4305
+ "icon_set_metadata_update_failed",
4306
+ "Icon Set publication succeeded, but display metadata could not be updated; rerun the same command."
4307
+ );
4308
+ }
4309
+ }
4310
+ const totalBytes = iconPackage.manifest.files.reduce(
4311
+ (total, file) => total + file.byteLength,
4312
+ 0
4313
+ );
4314
+ const document = {
4315
+ command: "icon-set publish",
4316
+ iconSet: {
4317
+ contentHash: iconPackage.contentHash,
4318
+ iconCount: iconPackage.manifest.iconNames.length,
4319
+ logicalId: options.id,
4320
+ packageId: parsed.revision.pin?.packageId ?? null,
4321
+ reference: parsed.revision.reference,
4322
+ totalBytes,
4323
+ version: parsed.revision.version,
4324
+ versionId: parsed.revision.id
4325
+ },
4326
+ publication: parsed.changed ? "changed" : "unchanged",
4327
+ schemaVersion: 1,
4328
+ team: describeTeam(authority)
4329
+ };
4330
+ if (options.json) return emitJson(document);
4331
+ console.log(
4332
+ `${pc2.green("\u2713")} ${parsed.changed ? "Published" : "Unchanged"} ${pc2.bold(parsed.revision.reference)} revision ${parsed.revision.version}.`
4333
+ );
4334
+ } catch (error) {
4335
+ emitFailure(options.json, "icon_set_publish_failed", boundedMessage(error, "publish"));
4336
+ }
4337
+ }
4338
+ );
4339
+ withTeamOptions(
4340
+ iconSet.command("list").description("List Icon Sets owned by one Team").option("--archived", "list archived Icon Sets instead of active ones")
4341
+ ).action(async (options) => {
4342
+ try {
4343
+ const authority = await resolveTeamAuthority(options, ["icons:read"], dependencies);
4344
+ if (!authority) return;
4345
+ const sets = await readAllPages(
4346
+ authority,
4347
+ "/v1/icon-sets",
4348
+ options.archived ? { archived: "true" } : {},
4349
+ catalogListResponseSchema,
4350
+ (page) => page.sets,
4351
+ (set) => set.id
4352
+ );
4353
+ const document = {
4354
+ command: "icon-set list",
4355
+ schemaVersion: 1,
4356
+ sets,
4357
+ team: describeTeam(authority)
4358
+ };
4359
+ if (options.json) return emitJson(document);
4360
+ if (sets.length === 0) return console.log(pc2.gray("No Team Icon Sets."));
4361
+ for (const set of sets) {
4362
+ console.log(
4363
+ ` ${pc2.bold(set.reference)} ${pc2.gray(set.archivedAt ? "archived" : `v${String(set.latestVersion ?? 0)}`)}`
4364
+ );
4365
+ }
4366
+ } catch (error) {
4367
+ emitFailure(options.json, "icon_set_list_failed", boundedMessage(error, "list"));
4368
+ }
4369
+ });
4370
+ withTeamOptions(
4371
+ iconSet.command("status").description("Show one Team Icon Set").argument("<slug>", "Team-local Icon Set slug")
4372
+ ).action(async (slug, options) => {
4373
+ await readCommand(slug, options, "icon-set status", async (authority) => {
4374
+ const response = await authorizedTeamRequest(
4375
+ authority,
4376
+ `/v1/icon-sets/${encodeURIComponent(slug)}`,
4377
+ "GET"
4378
+ );
4379
+ if (!response.ok) return { status: response.status };
4380
+ const parsed = catalogDetailResponseSchema.parse(response.body);
4381
+ return { value: { set: parsed.set } };
4382
+ });
4383
+ });
4384
+ withTeamOptions(
4385
+ iconSet.command("versions").description("List the retained revisions of one Team Icon Set").argument("<slug>", "Team-local Icon Set slug")
4386
+ ).action(async (slug, options) => {
4387
+ await readCommand(slug, options, "icon-set versions", async (authority) => {
4388
+ const versions = await readAllPages(
4389
+ authority,
4390
+ `/v1/icon-sets/${encodeURIComponent(slug)}/versions`,
4391
+ {},
4392
+ versionsResponseSchema,
4393
+ (page) => page.versions,
4394
+ (revision) => revision.id
4395
+ );
4396
+ return { value: { versions: versions.map(describeRevision) } };
4397
+ });
4398
+ });
4399
+ withTeamOptions(
4400
+ iconSet.command("uses").description("List the known hosted deployments that retain one Team Icon Set").argument("<slug>", "Team-local Icon Set slug")
4401
+ ).action(async (slug, options) => {
4402
+ await readCommand(slug, options, "icon-set uses", async (authority) => {
4403
+ const uses = await readAllPages(
4404
+ authority,
4405
+ `/v1/icon-sets/${encodeURIComponent(slug)}/uses`,
4406
+ {},
4407
+ usesResponseSchema,
4408
+ (page) => page.uses,
4409
+ (use) => `${use.deploymentId}\0${use.versionId}\0${String(use.ordinal)}`
4410
+ );
4411
+ return {
4412
+ value: {
4413
+ // Repository locks that were never deployed are unknowable to the catalog.
4414
+ coverage: "known-hosted-deployments",
4415
+ includesUndeployedRepositoryLocks: false,
4416
+ uses
4417
+ }
4418
+ };
4419
+ });
4420
+ });
4421
+ for (const [name, archived] of [
4422
+ ["archive", true],
4423
+ ["unarchive", false]
4424
+ ]) {
4425
+ withTeamOptions(
4426
+ iconSet.command(name).description(
4427
+ archived ? "Archive one Team Icon Set without removing its retained revisions" : "Return one archived Team Icon Set to the active catalog"
4428
+ ).argument("<slug>", "Team-local Icon Set slug")
4429
+ ).action(async (slug, options) => {
4430
+ try {
4431
+ if (!isIconSetSlug(slug)) {
4432
+ return emitFailure(options.json, "invalid_icon_set_id", "Icon Set ID is invalid.");
4433
+ }
4434
+ const authority = await resolveTeamAuthority(options, ["icons:write"], dependencies);
4435
+ if (!authority) return;
4436
+ const response = await authorizedTeamRequest(
4437
+ authority,
4438
+ `/v1/icon-sets/${encodeURIComponent(slug)}`,
4439
+ "PATCH",
4440
+ {
4441
+ body: JSON.stringify({ archived }),
4442
+ headers: { "Content-Type": "application/json" }
4443
+ }
4444
+ );
4445
+ if (!response.ok) return emitHttpFailure(options.json, response.status, name);
4446
+ const parsed = catalogDetailResponseSchema.parse(response.body);
4447
+ const document = {
4448
+ command: `icon-set ${name}`,
4449
+ schemaVersion: 1,
4450
+ set: parsed.set,
4451
+ team: describeTeam(authority)
4452
+ };
4453
+ if (options.json) return emitJson(document);
4454
+ console.log(
4455
+ `${pc2.green("\u2713")} ${archived ? "Archived" : "Restored"} ${pc2.bold(parsed.set.reference)}.`
4456
+ );
4457
+ } catch (error) {
4458
+ emitFailure(options.json, `icon_set_${name}_failed`, boundedMessage(error, name));
4459
+ }
4460
+ });
4461
+ }
4462
+ withTeamOptions(
4463
+ iconSet.command("purge").description("Permanently purge one exact Team Icon Set revision").argument("<slug>", "Team-local Icon Set slug").requiredOption("--version <version>", "exact positive revision to purge").requiredOption("--idempotency-key <key>", "durable retry key for this exact purge").requiredOption("--confirm <reference>", "repeat the exact @team/set@version being purged").option(
4464
+ "--acknowledge-unknown-locks",
4465
+ "acknowledge that undeployed repository locks are not discoverable"
4466
+ )
4467
+ ).action(
4468
+ async (slug, options) => {
4469
+ try {
4470
+ if (!isIconSetSlug(slug)) {
4471
+ return emitFailure(options.json, "invalid_icon_set_id", "Icon Set ID is invalid.");
4472
+ }
4473
+ if (!/^[1-9][0-9]*$/u.test(options.version) || !Number.isSafeInteger(+options.version)) {
4474
+ return emitFailure(
4475
+ options.json,
4476
+ "invalid_icon_set_version",
4477
+ "Purge requires one exact positive revision."
4478
+ );
4479
+ }
4480
+ if (!isIdempotencyKey(options.idempotencyKey)) {
4481
+ return emitFailure(
4482
+ options.json,
4483
+ "invalid_idempotency_key",
4484
+ "Idempotency key must be 8 to 128 characters from A-Z a-z 0-9 . _ : -"
4485
+ );
4486
+ }
4487
+ const confirmation = parsePurgeConfirmation(options.confirm);
4488
+ if (!confirmation || confirmation.slug !== slug || confirmation.version !== Number(options.version)) {
4489
+ return emitFailure(
4490
+ options.json,
4491
+ "purge_confirmation_mismatch",
4492
+ "Purge confirmation must repeat the exact @team/set@version being purged."
4493
+ );
4494
+ }
4495
+ if (options.acknowledgeUnknownLocks !== true) {
4496
+ return emitFailure(
4497
+ options.json,
4498
+ "purge_unknown_locks_unacknowledged",
4499
+ "Purge requires --acknowledge-unknown-locks; undeployed repository locks are not discoverable."
4500
+ );
4501
+ }
4502
+ const authority = await resolveTeamAuthority(options, ["icons:write"], dependencies);
4503
+ if (!authority) return;
4504
+ if (authority.team && `@${authority.team.slug}` !== confirmation.team) {
4505
+ return emitFailure(
4506
+ options.json,
4507
+ "purge_confirmation_mismatch",
4508
+ "Purge confirmation must name the selected Team."
4509
+ );
4510
+ }
4511
+ const response = await authorizedTeamRequest(
4512
+ authority,
4513
+ `/v1/icon-sets/${encodeURIComponent(slug)}/versions/${encodeURIComponent(options.version)}/purge`,
4514
+ "POST",
4515
+ {
4516
+ body: JSON.stringify({
4517
+ acknowledgeUnknownLocks: true,
4518
+ confirmation: options.confirm
4519
+ }),
4520
+ headers: {
4521
+ "Content-Type": "application/json",
4522
+ "Idempotency-Key": options.idempotencyKey
4523
+ }
4524
+ }
4525
+ );
4526
+ if (!response.ok) return emitHttpFailure(options.json, response.status, "purge");
4527
+ const parsed = purgeResponseSchema.parse(response.body);
4528
+ const document = {
4529
+ command: "icon-set purge",
4530
+ result: parsed.result,
4531
+ schemaVersion: 1,
4532
+ team: describeTeam(authority)
4533
+ };
4534
+ if (options.json) return emitJson(document);
4535
+ console.log(`${pc2.green("\u2713")} Purged ${pc2.bold(options.confirm)}.`);
4536
+ console.log(pc2.yellow(`! ${parsed.result.warning}`));
4537
+ } catch (error) {
4538
+ emitFailure(options.json, "icon_set_purge_failed", boundedMessage(error, "purge"));
4539
+ }
4540
+ }
4541
+ );
4542
+ async function readCommand(slug, options, command2, run) {
4543
+ try {
4544
+ if (!isIconSetSlug(slug)) {
4545
+ return emitFailure(options.json, "invalid_icon_set_id", "Icon Set ID is invalid.");
4546
+ }
4547
+ const authority = await resolveTeamAuthority(options, ["icons:read"], dependencies);
4548
+ if (!authority) return;
4549
+ const result = await run(authority);
4550
+ if ("status" in result) return emitHttpFailure(options.json, result.status, command2);
4551
+ const document = {
4552
+ command: command2,
4553
+ schemaVersion: 1,
4554
+ team: describeTeam(authority),
4555
+ ...result.value
4556
+ };
4557
+ if (options.json) return emitJson(document);
4558
+ process.stdout.write(`${JSON.stringify(document, null, 2)}
4559
+ `);
4560
+ } catch (error) {
4561
+ emitFailure(
4562
+ options.json,
4563
+ `${command2.replaceAll(/[^a-z]+/gu, "_")}_failed`,
4564
+ boundedMessage(error, command2)
4565
+ );
4566
+ }
4567
+ }
4568
+ }
4569
+ function withTeamOptions(command2) {
4570
+ return command2.option("--team <team>", "target Team as @team").option("--api-url <url>", "Tileflow API URL", process.env.TILEFLOW_API_URL).option("--api-key <key>", "Team data API key", process.env.TILEFLOW_API_KEY).option("--json", "print deterministic schema-version-1 JSON");
4571
+ }
4572
+ async function readAllPages(authority, path, query, schema, select, identify) {
4573
+ const items = [];
4574
+ const seenCursors = /* @__PURE__ */ new Set();
4575
+ const seenIds = /* @__PURE__ */ new Set();
4576
+ let cursor = null;
4577
+ for (let page = 0; page < maximumCatalogPages; page += 1) {
4578
+ const parameters = new URLSearchParams({ limit: String(pageSize), ...query });
4579
+ if (cursor) parameters.set("cursor", cursor);
4580
+ const response = await authorizedTeamRequest(
4581
+ authority,
4582
+ `${path}?${parameters.toString()}`,
4583
+ "GET"
4584
+ );
4585
+ if (!response.ok) throw new IconSetTransportError(response.status);
4586
+ const parsed = schema.parse(response.body);
4587
+ for (const item of select(parsed)) {
4588
+ const id = identify(item);
4589
+ if (seenIds.has(id)) throw new Error("Icon Set list returned a duplicate row.");
4590
+ seenIds.add(id);
4591
+ items.push(item);
4592
+ }
4593
+ cursor = parsed.nextCursor ?? null;
4594
+ if (!cursor) return items;
4595
+ if (seenCursors.has(cursor)) throw new Error("Icon Set list repeated a cursor.");
4596
+ seenCursors.add(cursor);
4597
+ }
4598
+ throw new Error("Icon Set list exceeded its safe page limit.");
4599
+ }
4600
+ var IconSetTransportError = class extends Error {
4601
+ constructor(status) {
4602
+ super(`Icon Set request failed (${status}).`);
4603
+ this.status = status;
4604
+ this.name = "IconSetTransportError";
4605
+ }
4606
+ status;
4607
+ };
4608
+ function emitHttpFailure(json, status, command2) {
4609
+ emitFailure(json, `http_${status}`, `Icon Set ${command2} failed (${status}).`);
4610
+ }
4611
+ function boundedMessage(error, command2) {
4612
+ if (error instanceof IconSetTransportError) return error.message;
4613
+ if (error instanceof Error && /limit|cursor|duplicate row/u.test(error.message)) {
4614
+ return error.message;
4615
+ }
4616
+ return `Icon Set ${command2} failed.`;
4617
+ }
4618
+ function describeTeam(authority) {
4619
+ return authority.team ? { id: authority.team.id, slug: authority.team.slug } : null;
4620
+ }
4621
+ function describeRevision(revision) {
4622
+ return {
4623
+ iconCount: revision.iconCount,
4624
+ id: revision.id,
4625
+ packageId: revision.packageId,
4626
+ publishedAt: revision.publishedAt,
4627
+ purgedAt: revision.purgedAt,
4628
+ reference: revision.reference,
4629
+ totalBytes: revision.totalBytes,
4630
+ version: revision.version
4631
+ };
4632
+ }
4633
+ function normalizeLocalDirectory(value) {
4634
+ if (!value || isAbsolute2(value) || value.includes("\\") || /[\p{Cc}]/u.test(value)) return null;
4635
+ const normalized = value.startsWith("./") || value.startsWith("../") ? value : `./${value}`;
4636
+ return normalized.replace(/\/+$/u, "");
4637
+ }
4638
+ function parsePurgeConfirmation(value) {
4639
+ const separator = value.lastIndexOf("@");
4640
+ if (separator <= 0 || value.length > 160) return null;
4641
+ const reference = value.slice(0, separator);
4642
+ const version = value.slice(separator + 1);
4643
+ if (!tileflowIconSetReferenceSchema.safeParse(reference).success) return null;
4644
+ if (!/^[1-9][0-9]*$/u.test(version) || !Number.isSafeInteger(Number(version))) return null;
4645
+ return { slug: reference.split("/")[1], team: reference.split("/")[0], version: Number(version) };
4646
+ }
4647
+ function isIconSetSlug(value) {
4648
+ return value.length <= 64 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(value);
4649
+ }
4650
+ function isIdempotencyKey(value) {
4651
+ return value.length >= 8 && value.length <= 128 && /^[A-Za-z0-9._:-]+$/u.test(value);
4652
+ }
4653
+ var cursorSchema = z4.string().min(1).max(2048).nullable();
4654
+ var isoDateSchema = z4.iso.datetime({ offset: true });
4655
+ var setSchema = z4.object({
4656
+ id: z4.string().regex(/^ics_[A-Za-z0-9_-]{16}$/u),
4657
+ reference: tileflowIconSetReferenceSchema,
4658
+ name: z4.string().max(200),
4659
+ description: z4.string().max(2e3),
4660
+ archivedAt: isoDateSchema.nullable(),
4661
+ latestVersion: z4.number().int().positive().max(Number.MAX_SAFE_INTEGER).nullable(),
4662
+ createdAt: isoDateSchema,
4663
+ updatedAt: isoDateSchema
4664
+ }).strict();
4665
+ var revisionBaseSchema = z4.object({
4666
+ id: z4.string().regex(/^icv_[A-Za-z0-9_-]{16}$/u),
4667
+ reference: tileflowIconSetReferenceSchema,
4668
+ version: z4.number().int().positive().max(Number.MAX_SAFE_INTEGER),
4669
+ publishedAt: isoDateSchema,
4670
+ purgedAt: isoDateSchema.nullable(),
4671
+ iconCount: z4.number().int().min(0).max(tileflowIconPackageLimits3.maxIconCount),
4672
+ totalBytes: z4.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
4673
+ publisher: z4.object({ kind: z4.enum(["credential", "membership"]), id: z4.string().max(200).nullable() }).strict()
4674
+ }).strict();
4675
+ var revisionSchema = revisionBaseSchema.extend({ pin: tileflowIconSetPinSchema.nullable() }).transform(({ id, reference, version, publishedAt, purgedAt, iconCount, totalBytes, pin }) => ({
4676
+ iconCount,
4677
+ id,
4678
+ pin,
4679
+ publishedAt,
4680
+ purgedAt,
4681
+ reference,
4682
+ totalBytes,
4683
+ version
4684
+ }));
4685
+ var revisionSummarySchema = revisionBaseSchema.extend({ packageId: tileflowIconSetPackageIdSchema.nullable() }).transform(
4686
+ ({ id, reference, version, publishedAt, purgedAt, iconCount, totalBytes, packageId }) => ({
4687
+ iconCount,
4688
+ id,
4689
+ packageId,
4690
+ publishedAt,
4691
+ purgedAt,
4692
+ reference,
4693
+ totalBytes,
4694
+ version
4695
+ })
4696
+ );
4697
+ var catalogListResponseSchema = z4.object({
4698
+ schemaVersion: z4.literal(1),
4699
+ sets: z4.array(setSchema).max(pageSize),
4700
+ nextCursor: cursorSchema.optional()
4701
+ }).strict();
4702
+ var catalogDetailResponseSchema = z4.object({ schemaVersion: z4.literal(1), set: setSchema }).strict();
4703
+ var versionsResponseSchema = z4.object({
4704
+ schemaVersion: z4.literal(1),
4705
+ versions: z4.array(revisionSummarySchema).max(pageSize),
4706
+ nextCursor: cursorSchema.optional()
4707
+ }).strict();
4708
+ var revisionResponseSchema = z4.object({
4709
+ schemaVersion: z4.literal(1),
4710
+ archived: z4.boolean(),
4711
+ revision: revisionSchema
4712
+ }).strict();
4713
+ var publishResponseSchema = z4.object({ schemaVersion: z4.literal(1), changed: z4.boolean(), revision: revisionSchema }).strict();
4714
+ var usesResponseSchema = z4.object({
4715
+ schemaVersion: z4.literal(1),
4716
+ uses: z4.array(
4717
+ z4.object({
4718
+ deploymentId: z4.string().min(1).max(160),
4719
+ deploymentVersion: z4.number().int().positive().max(Number.MAX_SAFE_INTEGER),
4720
+ mapId: z4.string().min(1).max(160),
4721
+ mapName: z4.string().max(200).nullable(),
4722
+ mapState: z4.string().max(64),
4723
+ versionId: z4.string().regex(/^icv_[A-Za-z0-9_-]{16}$/u),
4724
+ version: z4.number().int().positive().max(Number.MAX_SAFE_INTEGER),
4725
+ ordinal: z4.number().int().min(0).max(31),
4726
+ relationship: z4.enum(["active", "retained"])
4727
+ }).strict()
4728
+ ).max(pageSize),
4729
+ nextCursor: cursorSchema.optional(),
4730
+ coverage: z4.literal("known-hosted-deployments"),
4731
+ includesUndeployedRepositoryLocks: z4.literal(false)
4732
+ }).strict();
4733
+ var purgeResponseSchema = z4.object({
4734
+ schemaVersion: z4.literal(1),
4735
+ result: z4.object({
4736
+ schemaVersion: z4.literal(1),
4737
+ versionId: z4.string().regex(/^icv_[A-Za-z0-9_-]{16}$/u),
4738
+ version: z4.number().int().positive().max(Number.MAX_SAFE_INTEGER),
4739
+ purged: z4.boolean(),
4740
+ knownRetainedDeployments: z4.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
4741
+ physicalBytesReleased: z4.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
4742
+ unknownRepositoryLocks: z4.boolean(),
4743
+ warning: z4.string().max(400)
4744
+ }).strict()
4745
+ }).strict();
4746
+
4747
+ // src/icon-lock-command.ts
4748
+ function registerIconLockCommands(program2, dependencies) {
4749
+ const icons = program2.commands.find((command2) => command2.name() === "icons") ?? program2.command("icons").description("Inspect managed Tileflow icons");
4750
+ withLockOptions(
4751
+ icons.command("install").description("Lock every declared Icon Set reference that has no exact pin yet").argument("[references...]", "declared @team/set references; omit for every declared set"),
4752
+ dependencies
4753
+ ).action(async (references, options) => {
4754
+ await maintainLock("install", references, options);
4755
+ });
4756
+ withLockOptions(
4757
+ icons.command("update").description("Move selected declared Icon Set references to their current latest revision").argument("[references...]", "declared @team/set references; omit for every declared set"),
4758
+ dependencies
4759
+ ).action(async (references, options) => {
4760
+ await maintainLock("update", references, options);
4761
+ });
4762
+ withLockOptions(
4763
+ icons.command("pin").description("Lock one declared Icon Set reference to one exact revision").argument("<reference>", "declared @team/set reference").requiredOption("--version <version>", "exact positive revision"),
4764
+ dependencies
4765
+ ).action(async (reference, options) => {
4766
+ if (!/^[1-9][0-9]*$/u.test(options.version) || !Number.isSafeInteger(+options.version)) {
4767
+ return emitFailure(
4768
+ options.json,
4769
+ "invalid_icon_set_version",
4770
+ "Pin requires one exact positive revision."
4771
+ );
4772
+ }
4773
+ await maintainLock("pin", [reference], options, Number(options.version));
4774
+ });
4775
+ async function maintainLock(operation, requested, options, version) {
4776
+ let declared;
4777
+ let baseDirectory;
4778
+ try {
4779
+ const loaded = await withTileflowConfigSecretsHidden(
4780
+ () => loadValidTileflowConfigWithInputs3(options.config)
4781
+ );
4782
+ baseDirectory = dirname9(loaded.configFile);
4783
+ declared = collectDeclaredReferences(loaded.project);
4784
+ } catch (error) {
4785
+ return emitFailure(
4786
+ options.json,
4787
+ error instanceof TileflowValidationError4 ? "config_invalid" : "config_load_failed",
4788
+ error instanceof TileflowValidationError4 ? "Tileflow config has errors." : "Tileflow config could not be loaded."
4789
+ );
4790
+ }
4791
+ const selection = selectReferences(operation, requested, declared);
4792
+ if ("error" in selection) {
4793
+ return emitFailure(options.json, selection.error.code, selection.error.message);
4794
+ }
4795
+ let expected;
4796
+ let current;
4797
+ try {
4798
+ expected = await readTileflowIconsLockfileText(baseDirectory);
4799
+ current = expected === null ? {} : await readCurrentPins(expected);
4800
+ } catch {
4801
+ return emitFailure(
4802
+ options.json,
4803
+ "icon_lock_unreadable",
4804
+ `Existing ${tileflowIconsLockfileName} is unreadable; repair or remove it explicitly.`
4805
+ );
4806
+ }
4807
+ const targets = operation === "install" ? selection.selected.filter((reference) => !current[reference]) : selection.selected;
4808
+ if (targets.length === 0 && operation !== "install") {
4809
+ return emitFailure(
4810
+ options.json,
4811
+ "icon_set_not_declared",
4812
+ "Select at least one declared @team/set reference."
4813
+ );
4814
+ }
4815
+ const authority = targets.length === 0 ? null : await resolveTeamAuthority(options, ["icons:read"], dependencies);
4816
+ if (targets.length > 0 && !authority) return;
4817
+ const resolved = {};
4818
+ for (const reference of targets) {
4819
+ const result = await resolvePin(authority, reference, version);
4820
+ if ("error" in result) {
4821
+ return emitFailure(options.json, result.error.code, result.error.message);
4822
+ }
4823
+ resolved[reference] = result.pin;
4824
+ }
4825
+ const sets = {};
4826
+ for (const reference of declared.references) {
4827
+ const pin = resolved[reference] ?? current[reference];
4828
+ if (pin) sets[reference] = pin;
4829
+ }
4830
+ if (expected !== null || Object.keys(sets).length > 0) {
4831
+ try {
4832
+ await writeTileflowIconsLockfile(baseDirectory, { lockfileVersion: 1, sets }, expected);
4833
+ } catch (error) {
4834
+ const code = error !== null && typeof error === "object" && "code" in error ? String(error.code) : "icon_lock_write_failed";
4835
+ return emitFailure(
4836
+ options.json,
4837
+ code,
4838
+ code === "ICON_LOCK_CONFLICT" ? "The icon lock changed while this command ran; rerun it without discarding other pins." : "The icon lock could not be written."
4839
+ );
4840
+ }
4841
+ }
4842
+ const missing = declared.references.filter((reference) => !sets[reference]);
4843
+ const document = {
4844
+ command: `icons ${operation}`,
4845
+ lockfile: tileflowIconsLockfileName,
4846
+ missing,
4847
+ schemaVersion: 1,
4848
+ sets: declared.references.filter((reference) => sets[reference]).map((reference) => ({
4849
+ changed: Boolean(resolved[reference]),
4850
+ contentHash: sets[reference].contentHash,
4851
+ packageId: sets[reference].packageId,
4852
+ reference,
4853
+ version: sets[reference].version,
4854
+ versionId: sets[reference].versionId
4855
+ }))
4856
+ };
4857
+ if (options.json) return emitJson(document);
4858
+ if (document.sets.length === 0) return console.log(pc3.gray("No Icon Set references declared."));
4859
+ for (const entry of document.sets) {
4860
+ console.log(
4861
+ ` ${pc3.bold(entry.reference)} ${pc3.gray(`v${String(entry.version)}`)}${entry.changed ? "" : pc3.gray(" (unchanged)")}`
4862
+ );
4863
+ }
4864
+ for (const reference of missing) {
4865
+ console.log(pc3.yellow(`! ${reference} is declared but not locked.`));
4866
+ }
4867
+ }
4868
+ async function resolvePin(authority, reference, version) {
4869
+ const slug = reference.split("/")[1];
4870
+ const selector = version === void 0 ? "latest" : String(version);
4871
+ const response = await authorizedTeamRequest(
4872
+ authority,
4873
+ `/v1/icon-sets/${encodeURIComponent(slug)}/versions/${encodeURIComponent(selector)}`,
4874
+ "GET"
4875
+ );
4876
+ if (!response.ok) {
4877
+ return {
4878
+ error: {
4879
+ code: `http_${response.status}`,
4880
+ message: `Resolving ${reference}@${selector} failed (${response.status}).`
4881
+ }
4882
+ };
4883
+ }
4884
+ const parsed = revisionResponseSchema.safeParse(response.body);
4885
+ if (!parsed.success) {
4886
+ return {
4887
+ error: {
4888
+ code: "icon_set_response_invalid",
4889
+ message: `Resolving ${reference}@${selector} returned an invalid response.`
4890
+ }
4891
+ };
4892
+ }
4893
+ const revision = parsed.data.revision;
4894
+ if (revision.reference !== reference) {
4895
+ return {
4896
+ error: {
4897
+ code: "icon_set_reference_mismatch",
4898
+ message: `Resolving ${reference} returned a different Icon Set.`
4899
+ }
4900
+ };
4901
+ }
4902
+ if (!revision.pin || revision.purgedAt !== null) {
4903
+ return {
4904
+ error: {
4905
+ code: "icon_set_revision_unavailable",
4906
+ message: `${reference}@${selector} has no retained delivery artifact.`
4907
+ }
4908
+ };
4909
+ }
4910
+ if (version !== void 0 && revision.version !== version) {
4911
+ return {
4912
+ error: {
4913
+ code: "icon_set_revision_mismatch",
4914
+ message: `${reference}@${selector} returned revision ${revision.version}.`
4915
+ }
4916
+ };
4917
+ }
4918
+ if (authority.team && revision.pin.teamId !== authority.team.id) {
4919
+ return {
4920
+ error: {
4921
+ code: "icon_set_team_mismatch",
4922
+ message: `${reference} is owned by a different Team.`
4923
+ }
4924
+ };
4925
+ }
4926
+ return { pin: revision.pin };
4927
+ }
4928
+ }
4929
+ function withLockOptions(command2, dependencies) {
4930
+ return command2.option("-c, --config <path>", "config path", dependencies.defaultConfigPath).option("--team <team>", "target Team as @team").option("--api-url <url>", "Tileflow API URL", process.env.TILEFLOW_API_URL).option("--api-key <key>", "Team data API key", process.env.TILEFLOW_API_KEY).option("--json", "print deterministic schema-version-1 JSON");
4931
+ }
4932
+ function collectDeclaredReferences(project) {
4933
+ const references = [];
4934
+ const seen = /* @__PURE__ */ new Set();
4935
+ for (const mapName of getTileflowMapNames4(project).sort(compareCodeUnits7)) {
4936
+ const sources = parseResolvedTileflowMap3(project.maps[mapName]).icons ?? [];
4937
+ for (const reference of collectTileflowIconSetReferences(sources)) {
4938
+ if (seen.has(reference)) continue;
4939
+ seen.add(reference);
4940
+ references.push(reference);
4941
+ }
4942
+ }
4943
+ return { references: references.sort(compareCodeUnits7) };
4944
+ }
4945
+ function selectReferences(operation, requested, declared) {
4946
+ if (requested.length === 0) {
4947
+ if (operation === "pin") {
4948
+ return { error: { code: "icon_set_not_selected", message: "Pin one @team/set reference." } };
4949
+ }
4950
+ return { selected: [...declared.references] };
4951
+ }
4952
+ const selected = [];
4953
+ for (const reference of requested) {
4954
+ if (!tileflowIconSetReferenceSchema2.safeParse(reference).success) {
4955
+ return {
4956
+ error: {
4957
+ code: "invalid_icon_set_reference",
4958
+ message: `${reference} is not a canonical @team/set reference.`
4959
+ }
4960
+ };
4961
+ }
4962
+ if (!declared.references.includes(reference)) {
4963
+ return {
4964
+ error: {
4965
+ code: "icon_set_not_declared",
4966
+ message: `${reference} is not declared by any map; add iconSet('${reference}') first.`
4967
+ }
4968
+ };
4969
+ }
4970
+ if (!selected.includes(reference)) {
4971
+ selected.push(reference);
4972
+ }
4973
+ }
4974
+ return { selected: selected.sort(compareCodeUnits7) };
4975
+ }
4976
+ async function readCurrentPins(contents) {
4977
+ return (await parseTileflowIconsLockfile(contents)).sets;
4978
+ }
4979
+
4980
+ // src/language-command.ts
4981
+ import { readFile as readFile4 } from "fs/promises";
4982
+ import { fileURLToPath } from "url";
4983
+ import {
4984
+ serializeCanonicalJson as serializeCanonicalJson3,
4985
+ tileflowAuthoringManifest
4986
+ } from "@tileflow/core";
4987
+ import {
4988
+ createTileflowCommandFailureDocument as createTileflowCommandFailureDocument3,
4989
+ serializeTileflowCommandDocument as serializeTileflowCommandDocument3
4990
+ } from "@tileflow/dev/validation";
4991
+ function registerLanguageCommand(program2, dependencies = {}) {
4992
+ const language = program2.command("language").description("Inspect the complete machine-readable Tileflow semantic language");
4993
+ language.command("manifest").description("Print the semantic compiler authoring manifest").option("--json", "print deterministic JSON").action(
4994
+ async (options) => runLanguageCommand(
4995
+ "language-manifest",
4996
+ options,
4997
+ () => printManifest((dependencies.loadManifest ?? (() => tileflowAuthoringManifest))(), options)
4998
+ )
4999
+ );
5000
+ language.command("schema").description("Print the generated authoring and resolved JSON Schema").option("--json", "print deterministic JSON").action(
5001
+ async (options) => runLanguageCommand(
5002
+ "language-schema",
5003
+ options,
5004
+ async () => printSchema(await (dependencies.loadSchema ?? loadPackagedSchema)(), options)
5005
+ )
5006
+ );
5007
+ }
5008
+ async function runLanguageCommand(command2, options, operation) {
5009
+ try {
5010
+ await operation();
5011
+ } catch (error) {
5012
+ const failure2 = createTileflowCommandFailureDocument3(command2, error, process.cwd(), {
5013
+ code: command2 === "language-manifest" ? "LANGUAGE_MANIFEST_FAILED" : "LANGUAGE_SCHEMA_FAILED",
5014
+ phase: "language-discovery"
5015
+ });
5016
+ if (options.json) process.stderr.write(serializeTileflowCommandDocument3(failure2));
5017
+ else {
5018
+ console.error(`${failure2.message} [${failure2.code}; ${failure2.phase}]`);
5019
+ console.error(`Suggestion: ${failure2.suggestion}`);
5020
+ }
5021
+ process.exitCode = 1;
5022
+ }
5023
+ }
5024
+ function printManifest(manifest, options) {
5025
+ if (options.json) {
5026
+ process.stdout.write(`${serializeCanonicalJson3(manifest)}
5027
+ `);
5028
+ return;
5029
+ }
5030
+ console.log(
5031
+ `${manifest.language} (${manifest.compiler.name} v${manifest.compiler.version}); ${manifest.domains.length} domains, ${manifest.expressions.builders.length} expression builders.`
5032
+ );
5033
+ console.log("Run tileflow language manifest --json for the complete machine-readable document.");
5034
+ }
5035
+ function printSchema(schema, options) {
5036
+ if (options.json) {
5037
+ process.stdout.write(`${serializeCanonicalJson3(schema)}
5038
+ `);
5039
+ return;
5040
+ }
5041
+ console.log(
5042
+ `Tileflow config reference schema ${String(schema.schemaVersion)}: ${String(schema.$id)}.`
5043
+ );
5044
+ console.log("Run tileflow language schema --json for the complete machine-readable document.");
5045
+ }
5046
+ async function loadPackagedSchema() {
5047
+ const schemaUrl = import.meta.resolve("@tileflow/core/config-reference.json");
5048
+ const parsed = JSON.parse(await readFile4(fileURLToPath(schemaUrl), "utf8"));
5049
+ if (!isRecord(parsed))
5050
+ throw new Error("Packaged Tileflow config reference is not a JSON object.");
5051
+ return parsed;
5052
+ }
5053
+ function isRecord(value) {
5054
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
5055
+ }
5056
+
5057
+ // src/native-preview-command.ts
5058
+ import { tileflowNativeProfile } from "@tileflow/core/native-profile";
5059
+ import {
5060
+ createTileflowDevRequestHandler
5061
+ } from "@tileflow/dev/server";
5062
+ var tileflowNativePreviewBasePath = "/native";
5063
+ var tileflowNativePreviewProfile = tileflowNativeProfile.id;
5064
+ function getTileflowNativePreviewManifestUrl(origin) {
5065
+ return `${origin}${tileflowNativePreviewBasePath}/manifest.json`;
5066
+ }
5067
+ function resolveTileflowNativePreviewSelection(manifest, options) {
5068
+ const mapNames = Object.keys(manifest.maps).sort();
5069
+ const mapName = options.map ?? (mapNames.length === 1 ? mapNames[0] : void 0);
5070
+ if (!mapName || !Object.hasOwn(manifest.maps, mapName)) {
5071
+ throw new Error(
5072
+ options.map ? "The selected map is not present in the native artifact manifest." : "Select one configured map with --map for native artifact preview."
5073
+ );
5074
+ }
5075
+ const map = manifest.maps[mapName];
5076
+ const themeName = options.theme ?? map.defaultTheme;
5077
+ if (!Object.hasOwn(map.themes, themeName)) {
5078
+ throw new Error("The selected theme is not present in the native artifact manifest.");
5079
+ }
5080
+ return Object.freeze({ mapName, themeName });
5081
+ }
5082
+ var nativePreviewUnavailablePaths = /* @__PURE__ */ new Set([
5083
+ `${tileflowNativePreviewBasePath}/__events`,
5084
+ `${tileflowNativePreviewBasePath}/__status`
5085
+ ]);
5086
+ function createTileflowNativePreviewRequestHandler(options) {
5087
+ const delegate = createTileflowDevRequestHandler({
5088
+ ...options,
5089
+ basePath: tileflowNativePreviewBasePath
5090
+ });
5091
+ const notFound = () => new Response(JSON.stringify({ error: "Not found" }), {
5092
+ headers: { "Content-Type": "application/json; charset=utf-8" },
5093
+ status: 404
5094
+ });
5095
+ const handler = async (request) => {
5096
+ let pathname;
5097
+ try {
5098
+ pathname = new URL(request.url).pathname;
5099
+ } catch {
5100
+ return notFound();
5101
+ }
5102
+ if (pathname === tileflowNativePreviewBasePath || pathname === `${tileflowNativePreviewBasePath}/` || nativePreviewUnavailablePaths.has(pathname) || pathname.startsWith(`${tileflowNativePreviewBasePath}/__runtime/`) || pathname.startsWith(`${tileflowNativePreviewBasePath}/__inspection/`)) {
5103
+ return notFound();
5104
+ }
5105
+ return delegate(request);
5106
+ };
5107
+ return Object.assign(handler, {
5108
+ close: delegate.close,
5109
+ refresh: delegate.refresh
5110
+ });
5111
+ }
5112
+
5113
+ // src/open-external.ts
5114
+ import { spawn as spawnChild } from "child_process";
5115
+ import { platform as hostPlatform } from "os";
5116
+ function openTileflowExternal(target, dependencies = {}) {
5117
+ const invocation = resolveTileflowExternalOpenInvocation(
5118
+ target,
5119
+ dependencies.platform ?? hostPlatform()
5120
+ );
5121
+ const spawn2 = dependencies.spawn ?? spawnChild;
5122
+ let child;
5123
+ try {
5124
+ child = spawn2(invocation.command, invocation.args, {
5125
+ detached: true,
5126
+ shell: false,
5127
+ stdio: "ignore",
5128
+ windowsHide: true
5129
+ });
5130
+ } catch (error) {
5131
+ dependencies.onError?.(error);
5132
+ return;
5133
+ }
5134
+ child.once("error", (error) => dependencies.onError?.(error));
5135
+ child.unref();
5136
+ }
5137
+ function resolveTileflowExternalOpenInvocation(target, platform) {
5138
+ if (!target || target.includes("\0")) throw new Error("The external target is invalid.");
5139
+ if (platform === "darwin") return { command: "open", args: [target] };
5140
+ if (platform === "win32") return { command: "explorer.exe", args: [target] };
3840
5141
  return { command: "xdg-open", args: [target] };
3841
5142
  }
3842
5143
 
3843
5144
  // src/project-commands.ts
3844
- import pc from "picocolors";
5145
+ import pc4 from "picocolors";
3845
5146
  function registerProjectCommands(program2, dependencies) {
3846
5147
  const projects = program2.command("projects", { hidden: true }).description("Manage internal application destinations");
3847
5148
  projects.command("list").description("List application destinations accessible to the authenticated Tileflow account").option("--api-url <url>", "Tileflow API URL", process.env.TILEFLOW_API_URL).option("--include-archived", "include archived application destinations").option("--json", "print deterministic schema-version-1 JSON").action(async (options) => {
@@ -3855,14 +5156,14 @@ function registerProjectCommands(program2, dependencies) {
3855
5156
  command: "projects list",
3856
5157
  projects: result.items
3857
5158
  };
3858
- if (options.json) return emitJson(document);
5159
+ if (options.json) return emitJson2(document);
3859
5160
  if (!result.items.length) {
3860
- console.log(pc.gray("No managed destinations found."));
5161
+ console.log(pc4.gray("No managed destinations found."));
3861
5162
  return;
3862
5163
  }
3863
5164
  for (const target of result.items) {
3864
5165
  console.log(
3865
- ` ${target.project.archivedAt ? pc.yellow("\u25CB") : pc.green("\u25CF")} ${pc.bold(target.project.name)} ${pc.gray(target.reference)}${target.project.archivedAt ? pc.gray(" (archived)") : ""}`
5166
+ ` ${target.project.archivedAt ? pc4.yellow("\u25CB") : pc4.green("\u25CF")} ${pc4.bold(target.project.name)} ${pc4.gray(target.reference)}${target.project.archivedAt ? pc4.gray(" (archived)") : ""}`
3866
5167
  );
3867
5168
  }
3868
5169
  });
@@ -3914,9 +5215,9 @@ function registerProjectCommands(program2, dependencies) {
3914
5215
  command: "projects create",
3915
5216
  ...asRecord2(result.body)
3916
5217
  };
3917
- if (options.json) return emitJson(document);
3918
- console.log(`${pc.green("\u2713")} Application ${pc.bold(options.name.trim())} is ready.`);
3919
- console.log(pc.gray(`Its technical selector is --project @${organization.slug}/${slug}.`));
5218
+ if (options.json) return emitJson2(document);
5219
+ console.log(`${pc4.green("\u2713")} Application ${pc4.bold(options.name.trim())} is ready.`);
5220
+ console.log(pc4.gray(`Its technical selector is --project @${organization.slug}/${slug}.`));
3920
5221
  }
3921
5222
  );
3922
5223
  projects.command("show <target>").description("Show one application destination").option("--api-url <url>", "Tileflow API URL", process.env.TILEFLOW_API_URL).option("--json", "print deterministic schema-version-1 JSON").action(async (target, options) => {
@@ -3933,13 +5234,13 @@ function registerProjectCommands(program2, dependencies) {
3933
5234
  );
3934
5235
  if (!result.ok) return emitRemoteFailure(options.json, result);
3935
5236
  const document = { schemaVersion: 1, command: "projects show", ...asRecord2(result.body) };
3936
- if (options.json) return emitJson(document);
5237
+ if (options.json) return emitJson2(document);
3937
5238
  const project = asRecord2(result.body).project;
3938
5239
  if (!isProjectItem(project)) {
3939
5240
  return emitLocalFailure(false, "invalid_response", "Destination response was invalid.");
3940
5241
  }
3941
- console.log(`${pc.bold(project.name)} ${pc.gray(target)}`);
3942
- console.log(project.archivedAt ? pc.yellow("Archived") : pc.green("Active"));
5242
+ console.log(`${pc4.bold(project.name)} ${pc4.gray(target)}`);
5243
+ console.log(project.archivedAt ? pc4.yellow("Archived") : pc4.green("Active"));
3943
5244
  });
3944
5245
  for (const archived of [true, false]) {
3945
5246
  const verb = archived ? "archive" : "unarchive";
@@ -3965,8 +5266,8 @@ function registerProjectCommands(program2, dependencies) {
3965
5266
  command: `projects ${verb}`,
3966
5267
  ...asRecord2(result.body)
3967
5268
  };
3968
- if (options.json) return emitJson(document);
3969
- console.log(`${pc.green("\u2713")} Application ${archived ? "archived" : "unarchived"}.`);
5269
+ if (options.json) return emitJson2(document);
5270
+ console.log(`${pc4.green("\u2713")} Application ${archived ? "archived" : "unarchived"}.`);
3970
5271
  });
3971
5272
  }
3972
5273
  }
@@ -4008,7 +5309,7 @@ async function requireCommandSession(options, dependencies) {
4008
5309
  try {
4009
5310
  config = await dependencies.loadAuthConfig();
4010
5311
  } catch (error) {
4011
- emitLocalFailure(options.json, "auth_state_unavailable", safeMessage(error));
5312
+ emitLocalFailure(options.json, "auth_state_unavailable", safeMessage2(error));
4012
5313
  return null;
4013
5314
  }
4014
5315
  const resolved = resolveAccountSession(config, options.apiUrl ?? dependencies.defaultApiUrl);
@@ -4087,13 +5388,13 @@ function emitRemoteFailure(json, result) {
4087
5388
  function emitLocalFailure(json, code, message, details = {}) {
4088
5389
  if (json) {
4089
5390
  console.error(message);
4090
- emitJson({ schemaVersion: 1, ok: false, error: { code, ...details } });
5391
+ emitJson2({ schemaVersion: 1, ok: false, error: { code, ...details } });
4091
5392
  } else {
4092
- console.error(`${pc.red("\u2715")} ${message}`);
5393
+ console.error(`${pc4.red("\u2715")} ${message}`);
4093
5394
  }
4094
5395
  process.exitCode = 1;
4095
5396
  }
4096
- function emitJson(value) {
5397
+ function emitJson2(value) {
4097
5398
  process.stdout.write(`${JSON.stringify(value)}
4098
5399
  `);
4099
5400
  }
@@ -4119,21 +5420,130 @@ function validName(value) {
4119
5420
  const normalized = value.trim();
4120
5421
  return normalized.length > 0 && normalized.length <= 200 && !/[\p{Cc}]/u.test(normalized);
4121
5422
  }
4122
- function safeMessage(error) {
5423
+ function safeMessage2(error) {
4123
5424
  return error instanceof Error && error.message.length <= 300 ? error.message : "Tileflow auth state is unavailable.";
4124
5425
  }
4125
5426
 
5427
+ // src/renderer-artifact-command.ts
5428
+ import { resolve as resolve5 } from "path";
5429
+ import { auditTileflowMapThemeValues } from "@tileflow/core";
5430
+ import {
5431
+ createTileflowNativeDiagnostic,
5432
+ resolveTileflowRenderer,
5433
+ TileflowNativeCompatibilityError
5434
+ } from "@tileflow/core/native-profile";
5435
+ import {
5436
+ createTileflowBuildArtifacts,
5437
+ disposeTileflowBuildArtifacts,
5438
+ writeTileflowArtifactPlan
5439
+ } from "@tileflow/dev/artifacts";
5440
+ import {
5441
+ createTileflowCommandFailureDocument as createTileflowCommandFailureDocument4,
5442
+ createTileflowCommandSummary as createTileflowCommandSummary2,
5443
+ createTileflowStructuredDiagnostics as createTileflowStructuredDiagnostics2,
5444
+ serializeTileflowCommandDocument as serializeTileflowCommandDocument4
5445
+ } from "@tileflow/dev/validation";
5446
+ async function runRendererArtifactCommand(command2, options) {
5447
+ let artifacts;
5448
+ try {
5449
+ const renderer = resolveTileflowRenderer(options.renderer);
5450
+ if (options.target !== "local" && options.target !== "hosted") {
5451
+ throw Object.assign(new Error("Expected a local or hosted target."), {
5452
+ code: "INVALID_TARGET",
5453
+ phase: "command-validation",
5454
+ path: "target"
5455
+ });
5456
+ }
5457
+ if (renderer === "native" && options.target === "hosted") {
5458
+ throw new TileflowNativeCompatibilityError([
5459
+ createTileflowNativeDiagnostic("NATIVE_RENDERER_UNSUPPORTED", "/target")
5460
+ ]);
5461
+ }
5462
+ if (command2 === "build" && options.target === "hosted") {
5463
+ throw Object.assign(
5464
+ new Error("Build prepares local artifacts; Hosted publication uses deploy."),
5465
+ {
5466
+ code: "HOSTED_INCOMPATIBLE",
5467
+ phase: "command-validation",
5468
+ path: "target",
5469
+ suggestion: "Use validate --target hosted for web preflight, or build --target local."
5470
+ }
5471
+ );
5472
+ }
5473
+ artifacts = await withTileflowConfigSecretsHidden(
5474
+ () => createTileflowBuildArtifacts({
5475
+ apiBaseUrl: options.apiBaseUrl,
5476
+ config: options.config,
5477
+ ...options.icons ? { icons: options.icons } : {},
5478
+ renderer,
5479
+ styleBaseUrl: ".",
5480
+ target: "production"
5481
+ })
5482
+ );
5483
+ const outDir = options.out ?? "dist/tileflow";
5484
+ if (command2 === "build") await writeTileflowArtifactPlan(artifacts, { outDir });
5485
+ const summary = createTileflowCommandSummary2({
5486
+ code: command2 === "validate" ? "VALIDATION_OK" : "BUILD_OK",
5487
+ command: command2,
5488
+ message: command2 === "validate" ? "Tileflow artifact validation passed." : "Built Tileflow artifacts.",
5489
+ ok: true,
5490
+ phase: command2 === "validate" ? "validation" : "build",
5491
+ severity: "info",
5492
+ suggestion: renderer === "native" ? "Use the prepared native manifest with a separately qualified native runtime." : "Serve the prepared artifact directory."
5493
+ });
5494
+ const warnings = Object.values(artifacts.project.maps).flatMap(
5495
+ (map) => auditTileflowMapThemeValues(map).filter(({ severity }) => severity === "warning")
5496
+ );
5497
+ const diagnostics = warnings.length ? createTileflowStructuredDiagnostics2({ diagnostics: warnings }, process.cwd(), {
5498
+ code: "VALIDATION_WARNING",
5499
+ phase: "theme-audit"
5500
+ }) : [];
5501
+ const document = {
5502
+ ...summary,
5503
+ target: options.target,
5504
+ renderer,
5505
+ ...artifacts.nativeBuild ? { profile: artifacts.nativeBuild.profile, validation: "static-artifacts" } : {},
5506
+ maps: Object.keys(artifacts.project.maps).sort(),
5507
+ diagnostics
5508
+ };
5509
+ if (options.json) process.stdout.write(serializeTileflowCommandDocument4(document));
5510
+ else {
5511
+ console.log(summary.message);
5512
+ for (const issue of diagnostics) console.warn(`${issue.path}: ${issue.message}`);
5513
+ if (artifacts.nativeBuild)
5514
+ console.log("Profile: native-v1 (static artifacts; no device qualification).");
5515
+ if (command2 === "build")
5516
+ console.log(`Output: ${resolve5(outDir, ...renderer === "native" ? ["native"] : [])}`);
5517
+ }
5518
+ } catch (error) {
5519
+ const document = createTileflowCommandFailureDocument4(command2, error, process.cwd(), {
5520
+ code: command2 === "validate" ? "VALIDATION_FAILED" : "BUILD_FAILED",
5521
+ phase: "artifact-preparation"
5522
+ });
5523
+ if (options.json) process.stderr.write(serializeTileflowCommandDocument4(document));
5524
+ else {
5525
+ for (const issue of document.diagnostics) {
5526
+ console.error(`${issue.code} ${issue.path || "(root)"}: ${issue.message}`);
5527
+ console.error(`Suggestion: ${issue.suggestion}`);
5528
+ }
5529
+ }
5530
+ process.exitCode = 1;
5531
+ } finally {
5532
+ if (artifacts) await disposeTileflowBuildArtifacts(artifacts);
5533
+ }
5534
+ }
5535
+
4126
5536
  // src/tileset-command.ts
4127
- import pc2 from "picocolors";
4128
- import { serializeCanonicalJson as serializeCanonicalJson3 } from "@tileflow/core";
4129
- import { inspectTileflowPmtiles } from "@tileflow/dev/tilesets";
5537
+ import pc5 from "picocolors";
4130
5538
  import { z as z6 } from "zod";
5539
+ import { serializeCanonicalJson as serializeCanonicalJson5 } from "@tileflow/core";
5540
+ import { inspectTileflowPmtiles } from "@tileflow/dev/tilesets";
4131
5541
 
4132
5542
  // src/tileset-upload.ts
4133
5543
  import { createHash } from "crypto";
4134
5544
  import { open as open4, stat } from "fs/promises";
4135
5545
  import { z as z5 } from "zod";
4136
- import { serializeCanonicalJson as serializeCanonicalJson2 } from "@tileflow/core";
5546
+ import { serializeCanonicalJson as serializeCanonicalJson4 } from "@tileflow/core";
4137
5547
  var teamTilesetPartSize = 16777216;
4138
5548
  var maximumTeamTilesetBytes = 4e9;
4139
5549
  var authorizationBatchSize = 16;
@@ -4273,7 +5683,7 @@ async function prepareTeamTilesetUpload(file) {
4273
5683
  };
4274
5684
  return Object.freeze({
4275
5685
  byteCount: identity.size,
4276
- contentHash: createHash("sha256").update(serializeCanonicalJson2(contentIdentity)).digest("hex"),
5686
+ contentHash: createHash("sha256").update(serializeCanonicalJson4(contentIdentity)).digest("hex"),
4277
5687
  contentHashAlgorithm: "sha256-fixed-parts-v1",
4278
5688
  identity,
4279
5689
  partSize: teamTilesetPartSize,
@@ -4289,7 +5699,7 @@ function createTeamTilesetUploadIdempotencyKey(tilesetId, upload) {
4289
5699
  );
4290
5700
  }
4291
5701
  return createHash("sha256").update(
4292
- serializeCanonicalJson2({
5702
+ serializeCanonicalJson4({
4293
5703
  byteCount: upload.byteCount,
4294
5704
  contentHash: upload.contentHash,
4295
5705
  contentHashAlgorithm: upload.contentHashAlgorithm,
@@ -4693,7 +6103,7 @@ function asRecord3(value) {
4693
6103
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
4694
6104
  }
4695
6105
  function defaultSleep(milliseconds) {
4696
- return new Promise((resolve8) => setTimeout(resolve8, milliseconds));
6106
+ return new Promise((resolve9) => setTimeout(resolve9, milliseconds));
4697
6107
  }
4698
6108
  var identifierSchema = z5.string().min(20).max(80).regex(/^[A-Za-z0-9_-]+$/u);
4699
6109
  var uploadPartStatusSchema = z5.object({
@@ -4780,11 +6190,11 @@ function registerTilesetCommands(program2, dependencies) {
4780
6190
  sample: options.sample
4781
6191
  });
4782
6192
  if (options.json) {
4783
- process.stdout.write(`${serializeCanonicalJson3(inspection)}
6193
+ process.stdout.write(`${serializeCanonicalJson5(inspection)}
4784
6194
  `);
4785
6195
  return;
4786
6196
  }
4787
- console.log(`${pc2.green("\u2713")} PMTiles archive is valid.`);
6197
+ console.log(`${pc5.green("\u2713")} PMTiles archive is valid.`);
4788
6198
  console.log(` Type: ${inspection.contract.tileType}`);
4789
6199
  console.log(` Zooms: ${inspection.contract.minzoom}-${inspection.contract.maxzoom}`);
4790
6200
  console.log(` Source layers: ${inspection.contract.sourceLayers.length}`);
@@ -4794,7 +6204,7 @@ function registerTilesetCommands(program2, dependencies) {
4794
6204
  );
4795
6205
  }
4796
6206
  for (const warning of inspection.warnings) {
4797
- console.log(`${pc2.yellow("!")} ${warning.message}`);
6207
+ console.log(`${pc5.yellow("!")} ${warning.message}`);
4798
6208
  }
4799
6209
  } catch (error) {
4800
6210
  const record = asRecord4(error);
@@ -4803,11 +6213,11 @@ function registerTilesetCommands(program2, dependencies) {
4803
6213
  const message = error instanceof Error ? error.message : "Tileset inspection failed.";
4804
6214
  if (options.json) {
4805
6215
  process.stderr.write(
4806
- `${serializeCanonicalJson3({ error: { code, message, path }, ok: false, schemaVersion: 1 })}
6216
+ `${serializeCanonicalJson5({ error: { code, message, path }, ok: false, schemaVersion: 1 })}
4807
6217
  `
4808
6218
  );
4809
6219
  } else {
4810
- console.error(`${pc2.red("Error:")} ${message}`);
6220
+ console.error(`${pc5.red("Error:")} ${message}`);
4811
6221
  }
4812
6222
  process.exitCode = 1;
4813
6223
  }
@@ -4889,9 +6299,9 @@ function registerTilesetCommands(program2, dependencies) {
4889
6299
  version: { id: publication.versionId, number: publication.version }
4890
6300
  }
4891
6301
  };
4892
- if (options.json) return emitJson2(document);
6302
+ if (options.json) return emitJson(document);
4893
6303
  console.log(
4894
- `${pc2.green("\u2713")} ${publication.changed ? "Published" : "Unchanged"} ${pc2.bold(options.id)} in ${authority.team ? `@${authority.team.slug}` : "the Team bound to the data key"}.`
6304
+ `${pc5.green("\u2713")} ${publication.changed ? "Published" : "Unchanged"} ${pc5.bold(options.id)} in ${authority.team ? `@${authority.team.slug}` : "the Team bound to the data key"}.`
4895
6305
  );
4896
6306
  } catch (error) {
4897
6307
  if (error instanceof TeamTilesetUploadError) {
@@ -4910,10 +6320,10 @@ function registerTilesetCommands(program2, dependencies) {
4910
6320
  if (!authority) return;
4911
6321
  const items = await fetchAllTeamTilesets(authority);
4912
6322
  const document = { command: "tileset list", schemaVersion: 1, tilesets: items };
4913
- if (options.json) return emitJson2(document);
4914
- if (items.length === 0) return console.log(pc2.gray("No Team tilesets."));
6323
+ if (options.json) return emitJson(document);
6324
+ if (items.length === 0) return console.log(pc5.gray("No Team tilesets."));
4915
6325
  for (const item of items) {
4916
- console.log(` ${pc2.bold(item.logicalName)} ${pc2.gray(item.status)}`);
6326
+ console.log(` ${pc5.bold(item.logicalName)} ${pc5.gray(item.status)}`);
4917
6327
  }
4918
6328
  } catch {
4919
6329
  emitFailure(options.json, "tileset_list_failed", "Tileset list failed.");
@@ -4939,9 +6349,9 @@ function registerTilesetCommands(program2, dependencies) {
4939
6349
  }
4940
6350
  const parsed = tilesetDetailResponseSchema.parse(response.body);
4941
6351
  const document = { command: "tileset status", schemaVersion: 1, tileset: parsed.tileset };
4942
- if (options.json) return emitJson2(document);
6352
+ if (options.json) return emitJson(document);
4943
6353
  const item = parsed.tileset;
4944
- console.log(`${pc2.bold(item.name)} ${pc2.gray(item.state)}`);
6354
+ console.log(`${pc5.bold(item.name)} ${pc5.gray(item.state)}`);
4945
6355
  console.log(` ID: ${item.id}`);
4946
6356
  console.log(` Current version: ${String(item.currentVersion ?? "unpublished")}`);
4947
6357
  } catch {
@@ -4982,97 +6392,20 @@ function registerTilesetCommands(program2, dependencies) {
4982
6392
  nextCursor: inUse.data.nextCursor
4983
6393
  }
4984
6394
  );
4985
- }
4986
- return emitFailure(
4987
- options.json,
4988
- `http_${response.status}`,
4989
- `Tileset purge failed (${response.status}).`
4990
- );
4991
- }
4992
- const parsed = tilesetPurgeResponseSchema.parse(response.body);
4993
- const document = { command: "tileset purge", schemaVersion: 1, ...parsed };
4994
- if (options.json) return emitJson2(document);
4995
- console.log(`${pc2.green("\u2713")} Purge ${parsed.reclamation}.`);
4996
- } catch {
4997
- emitFailure(options.json, "tileset_purge_failed", "Tileset purge failed.");
4998
- }
4999
- });
5000
- }
5001
- async function resolveTeamAuthority(options, scopes, dependencies) {
5002
- let apiOrigin;
5003
- try {
5004
- apiOrigin = normalizeApiOrigin(options.apiUrl ?? dependencies.defaultApiUrl);
5005
- } catch (error) {
5006
- emitFailure(options.json, "invalid_api_url", safeMessage2(error));
5007
- return null;
5008
- }
5009
- if (options.apiKey) {
5010
- if (!/^tf_live_[0-9a-f]{48}$/u.test(options.apiKey)) {
5011
- emitFailure(options.json, "invalid_team_data_key", "Team data API key is invalid.");
5012
- return null;
5013
- }
5014
- if (options.team) {
5015
- emitFailure(
5016
- options.json,
5017
- "team_selector_with_api_key",
5018
- "A Team data key already selects its Team; omit --team."
5019
- );
5020
- return null;
6395
+ }
6396
+ return emitFailure(
6397
+ options.json,
6398
+ `http_${response.status}`,
6399
+ `Tileset purge failed (${response.status}).`
6400
+ );
6401
+ }
6402
+ const parsed = tilesetPurgeResponseSchema.parse(response.body);
6403
+ const document = { command: "tileset purge", schemaVersion: 1, ...parsed };
6404
+ if (options.json) return emitJson(document);
6405
+ console.log(`${pc5.green("\u2713")} Purge ${parsed.reclamation}.`);
6406
+ } catch {
6407
+ emitFailure(options.json, "tileset_purge_failed", "Tileset purge failed.");
5021
6408
  }
5022
- return { apiOrigin, credential: options.apiKey, team: null };
5023
- }
5024
- let config;
5025
- try {
5026
- config = await dependencies.loadAuthConfig();
5027
- } catch (error) {
5028
- emitFailure(options.json, "auth_state_unavailable", safeMessage2(error));
5029
- return null;
5030
- }
5031
- const selected = resolveAccountSession(config, options.apiUrl ?? dependencies.defaultApiUrl);
5032
- if (selected.kind !== "selected") {
5033
- emitFailure(
5034
- options.json,
5035
- `account_session_${selected.kind}`,
5036
- selected.kind === "expired" ? "Account session expired. Run tileflow login." : "Run tileflow login."
5037
- );
5038
- return null;
5039
- }
5040
- const discovered = await listAccountTeams(selected.session);
5041
- if (!discovered.ok) {
5042
- emitFailure(options.json, "team_discovery_failed", discovered.error);
5043
- return null;
5044
- }
5045
- const requested = options.team ? /^@([A-Za-z0-9][A-Za-z0-9._-]{0,127})$/u.exec(options.team) : null;
5046
- if (options.team && !requested) {
5047
- emitFailure(options.json, "invalid_team", "Team must use @team syntax.");
5048
- return null;
5049
- }
5050
- const team = requested ? discovered.teams.find(({ slug }) => slug === requested[1]) : discovered.teams.length === 1 ? discovered.teams[0] : null;
5051
- if (!team) {
5052
- const options_ = discovered.teams.map(({ slug }) => `@${slug}`);
5053
- emitFailure(
5054
- options.json,
5055
- options_.length > 1 ? "team_ambiguous" : "team_not_found",
5056
- options_.length > 1 ? `Choose one Team with --team: ${options_.join(", ")}.` : "No matching Team is available.",
5057
- { options: options_ }
5058
- );
5059
- return null;
5060
- }
5061
- const capability = await requestTeamCapability(selected.session, `@${team.slug}`, scopes);
5062
- if (!capability.ok) {
5063
- emitFailure(options.json, "team_capability_failed", capability.error);
5064
- return null;
5065
- }
5066
- return {
5067
- apiOrigin: selected.session.apiOrigin,
5068
- credential: capability.capability,
5069
- team
5070
- };
5071
- }
5072
- function authorizedTeamRequest(authority, path, method) {
5073
- return requestHostedJson(authority.apiOrigin, path, {
5074
- headers: { Authorization: `Bearer ${authority.credential}` },
5075
- method
5076
6409
  });
5077
6410
  }
5078
6411
  async function fetchAllTeamTilesets(authority) {
@@ -5111,21 +6444,6 @@ async function fetchAllTeamTilesets(authority) {
5111
6444
  }
5112
6445
  throw new Error("Tileset list exceeded its safe page limit.");
5113
6446
  }
5114
- function emitJson2(value) {
5115
- process.stdout.write(`${serializeCanonicalJson3(value)}
5116
- `);
5117
- }
5118
- function emitFailure(json, code, message, context = {}) {
5119
- if (json) {
5120
- process.stderr.write(
5121
- `${serializeCanonicalJson3({ error: { code, ...context, message }, ok: false, schemaVersion: 1 })}
5122
- `
5123
- );
5124
- } else {
5125
- console.error(`${pc2.red("Error:")} ${message}`);
5126
- }
5127
- process.exitCode = 1;
5128
- }
5129
6447
  function isTilesetId(value) {
5130
6448
  return /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(value) && !["terrain", "world"].includes(value);
5131
6449
  }
@@ -5143,15 +6461,12 @@ function inspectionOptionError(message) {
5143
6461
  path: "includeValues"
5144
6462
  });
5145
6463
  }
5146
- function safeMessage2(error) {
5147
- return error instanceof Error ? error.message : "Tileset command failed.";
5148
- }
5149
6464
  function asRecord4(value) {
5150
6465
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
5151
6466
  }
5152
6467
  var safeIdentifierSchema2 = z6.string().min(1).max(160).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/u);
5153
6468
  var physicalTilesetIdSchema = z6.string().min(20).max(80).regex(/^tls_[A-Za-z0-9_-]+$/u);
5154
- var cursorSchema = z6.string().min(1).max(2048).nullable();
6469
+ var cursorSchema2 = z6.string().min(1).max(2048).nullable();
5155
6470
  var tilesetInventoryItemSchema = z6.object({
5156
6471
  currentVersion: z6.number().int().positive().max(Number.MAX_SAFE_INTEGER).nullable(),
5157
6472
  hasCurrent: z6.boolean(),
@@ -5165,7 +6480,7 @@ var tilesetInventoryItemSchema = z6.object({
5165
6480
  usedByMapCount: z6.number().int().min(0).max(Number.MAX_SAFE_INTEGER)
5166
6481
  }).strict();
5167
6482
  var tilesetListResponseSchema = z6.object({
5168
- nextCursor: cursorSchema.optional(),
6483
+ nextCursor: cursorSchema2.optional(),
5169
6484
  schemaVersion: z6.literal(1),
5170
6485
  tilesets: z6.array(tilesetInventoryItemSchema).max(100)
5171
6486
  }).strict();
@@ -5183,7 +6498,7 @@ var tilesetDetailResponseSchema = z6.object({
5183
6498
  attribution: z6.string().max(4096).nullable(),
5184
6499
  currentVersion: z6.number().int().positive().max(Number.MAX_SAFE_INTEGER).nullable(),
5185
6500
  dependencies: z6.array(tilesetDependencySchema).max(25),
5186
- dependencyNextCursor: cursorSchema,
6501
+ dependencyNextCursor: cursorSchema2,
5187
6502
  id: physicalTilesetIdSchema,
5188
6503
  name: z6.string().min(1).max(200),
5189
6504
  slug: z6.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/u),
@@ -5199,7 +6514,7 @@ var tilesetInUseResponseSchema = z6.object({
5199
6514
  code: z6.literal("TF_TILESET_IN_USE"),
5200
6515
  dependencies: z6.array(purgeDependencySchema).max(32),
5201
6516
  error: z6.literal("Tileset is used by retained Map deployments"),
5202
- nextCursor: cursorSchema
6517
+ nextCursor: cursorSchema2
5203
6518
  }).strict();
5204
6519
  var tilesetPurgeResponseSchema = z6.object({
5205
6520
  deliveryFence: z6.enum(["complete", "pending"]),
@@ -5211,7 +6526,7 @@ var tilesetPurgeResponseSchema = z6.object({
5211
6526
 
5212
6527
  // src/visual-command.ts
5213
6528
  import { lstat as lstat4, readFile as readFile6 } from "fs/promises";
5214
- import { dirname as dirname9, isAbsolute as isAbsolute3, parse as parse2, relative as relative4, resolve as resolve6, sep as sep4 } from "path";
6529
+ import { dirname as dirname11, isAbsolute as isAbsolute4, parse as parse2, relative as relative4, resolve as resolve7, sep as sep4 } from "path";
5215
6530
  import {
5216
6531
  analyzeTileflowCaptureReference,
5217
6532
  compareTileflowCaptureToBaseline,
@@ -5225,12 +6540,12 @@ import {
5225
6540
  tileflowVisualArtifactLimits,
5226
6541
  validateTileflowVisualReferencePng
5227
6542
  } from "@tileflow/capture";
5228
- import { compareCodeUnits as compareCodeUnits8 } from "@tileflow/core";
6543
+ import { compareCodeUnits as compareCodeUnits9 } from "@tileflow/core";
5229
6544
 
5230
6545
  // src/visual-compare-command.ts
5231
6546
  import { createHash as createHash3 } from "crypto";
5232
6547
  import { lstat as lstat3, readFile as readFile5, realpath } from "fs/promises";
5233
- import { basename as basename3, dirname as dirname8, extname, isAbsolute as isAbsolute2, parse, relative as relative3, resolve as resolve5, sep as sep3 } from "path";
6548
+ import { basename as basename3, dirname as dirname10, extname, isAbsolute as isAbsolute3, parse, relative as relative3, resolve as resolve6, sep as sep3 } from "path";
5234
6549
  import {
5235
6550
  compareTileflowCapturesForReview,
5236
6551
  createTileflowCaptureSession as createTileflowCaptureSession2,
@@ -5239,16 +6554,16 @@ import {
5239
6554
  tileflowSyntheticAssetOrigin as tileflowSyntheticAssetOrigin2
5240
6555
  } from "@tileflow/capture";
5241
6556
  import {
5242
- compareCodeUnits as compareCodeUnits7,
6557
+ compareCodeUnits as compareCodeUnits8,
5243
6558
  normalizeTileflowCaptureScene as normalizeTileflowCaptureScene2,
5244
- parseResolvedTileflowMap as parseResolvedTileflowMap2,
6559
+ parseResolvedTileflowMap as parseResolvedTileflowMap4,
5245
6560
  resolveThemeSelection,
5246
6561
  tileflowCaptureSceneLimits
5247
6562
  } from "@tileflow/core";
5248
6563
  import {
5249
6564
  createTileflowArtifactDiagnostics as createTileflowArtifactDiagnostics2,
5250
6565
  createTileflowArtifactSession as createTileflowArtifactSession2,
5251
- createTileflowBuildArtifacts
6566
+ createTileflowBuildArtifacts as createTileflowBuildArtifacts2
5252
6567
  } from "@tileflow/dev/artifacts";
5253
6568
 
5254
6569
  // src/visual-compare-report.ts
@@ -5543,15 +6858,15 @@ async function parseVisualComparePlan(options) {
5543
6858
  }
5544
6859
  async function createArtifactPair(plan) {
5545
6860
  const cwd = process.cwd();
5546
- const leftPath = portablePathIdentity(resolve5(cwd, plan.config));
5547
- const rightPath = portablePathIdentity(resolve5(cwd, plan.againstConfig));
5548
- const left = await createTileflowBuildArtifacts({
6861
+ const leftPath = portablePathIdentity(resolve6(cwd, plan.config));
6862
+ const rightPath = portablePathIdentity(resolve6(cwd, plan.againstConfig));
6863
+ const left = await createTileflowBuildArtifacts2({
5549
6864
  assetBaseUrl: tileflowSyntheticAssetOrigin2,
5550
6865
  config: plan.config,
5551
6866
  cwd
5552
6867
  });
5553
6868
  if (leftPath === rightPath) return [left, left];
5554
- const right = await createTileflowBuildArtifacts({
6869
+ const right = await createTileflowBuildArtifacts2({
5555
6870
  assetBaseUrl: tileflowSyntheticAssetOrigin2,
5556
6871
  config: plan.againstConfig,
5557
6872
  cwd
@@ -5671,7 +6986,7 @@ async function prepareVisualCompareOutput(plan, leftArtifacts, rightArtifacts, c
5671
6986
  documentPath: relativePath(cwd, plan.output.documentPath),
5672
6987
  reportPath: relativePath(cwd, plan.output.reportPath)
5673
6988
  },
5674
- warnings: [...warnings].sort(compareCodeUnits7)
6989
+ warnings: [...warnings].sort(compareCodeUnits8)
5675
6990
  };
5676
6991
  const report = renderTileflowVisualCompareReport({
5677
6992
  generatedBy: "Tileflow CLI",
@@ -5696,7 +7011,7 @@ async function prepareVisualCompareOutput(plan, leftArtifacts, rightArtifacts, c
5696
7011
  files,
5697
7012
  incompatible,
5698
7013
  nextAssets: inventory.assets.map((asset) => ({
5699
- path: resolve5(plan.output.assetDirectory, asset.path),
7014
+ path: resolve6(plan.output.assetDirectory, asset.path),
5700
7015
  sha256: asset.sha256
5701
7016
  })),
5702
7017
  removePaths: [...new Set(removePaths)].filter((path) => !filePaths.has(path))
@@ -5715,7 +7030,7 @@ async function writePreparedVisualCompareOutput(output, prepared) {
5715
7030
  async function runVisualCompareWatch(plan, openReport) {
5716
7031
  const cwd = process.cwd();
5717
7032
  const controller = installSignalAbortController();
5718
- const sameConfig = portablePathIdentity(resolve5(cwd, plan.config)) === portablePathIdentity(resolve5(cwd, plan.againstConfig));
7033
+ const sameConfig = portablePathIdentity(resolve6(cwd, plan.config)) === portablePathIdentity(resolve6(cwd, plan.againstConfig));
5719
7034
  let leftSession;
5720
7035
  let rightSession;
5721
7036
  let captureSession;
@@ -5959,7 +7274,7 @@ async function runVisualCompareWatch(plan, openReport) {
5959
7274
  }
5960
7275
  }
5961
7276
  function resolveVisualCompareSide(artifacts, config, requestedMap, requestedTheme, role) {
5962
- const availableMaps = Object.keys(artifacts.project.maps).sort(compareCodeUnits7);
7277
+ const availableMaps = Object.keys(artifacts.project.maps).sort(compareCodeUnits8);
5963
7278
  const map = requestedMap ?? (availableMaps.length === 1 ? availableMaps[0] : void 0);
5964
7279
  if (!map) {
5965
7280
  throw new Error(
@@ -5972,7 +7287,7 @@ function resolveVisualCompareSide(artifacts, config, requestedMap, requestedThem
5972
7287
  `Unknown ${role} map "${map}". Available maps: ${availableMaps.join(", ") || "(none)"}.`
5973
7288
  );
5974
7289
  }
5975
- const resolvedMap = parseResolvedTileflowMap2(candidate);
7290
+ const resolvedMap = parseResolvedTileflowMap4(candidate);
5976
7291
  const theme = resolveThemeSelection(resolvedMap, requestedTheme).name;
5977
7292
  if (!artifacts.styles[map]?.[theme]) {
5978
7293
  throw new Error(`The ${role} map theme "${map}/${theme}" was not compiled.`);
@@ -6024,11 +7339,11 @@ function requireCapture(captures, scene, role) {
6024
7339
  return capture;
6025
7340
  }
6026
7341
  async function resolveVisualCompareOutput(cwd, value, managed, force, watch, zooms) {
6027
- const reportPath = resolve5(cwd, value);
7342
+ const reportPath = resolve6(cwd, value);
6028
7343
  if (extname(reportPath).toLowerCase() !== ".html") {
6029
7344
  throw new Error("--report must name an .html file.");
6030
7345
  }
6031
- const reportDirectory = dirname8(reportPath);
7346
+ const reportDirectory = dirname10(reportPath);
6032
7347
  if (reportDirectory === cwd || reportDirectory === parse(reportPath).root) {
6033
7348
  throw new Error("Visual compare output must use a dedicated subdirectory.");
6034
7349
  }
@@ -6038,7 +7353,7 @@ async function resolveVisualCompareOutput(cwd, value, managed, force, watch, zoo
6038
7353
  const stem = reportPath.slice(0, -".html".length);
6039
7354
  const documentPath = `${stem}.json`;
6040
7355
  const assetDirectory = `${stem}.assets`;
6041
- const inventoryPath = resolve5(assetDirectory, tileflowVisualCompareInventoryFileName);
7356
+ const inventoryPath = resolve6(assetDirectory, tileflowVisualCompareInventoryFileName);
6042
7357
  const boundary = isInside(cwd, reportDirectory) ? cwd : reportDirectory;
6043
7358
  await assertNoSymlinkComponents(reportDirectory, boundary);
6044
7359
  await assertNoSymlinkComponents(assetDirectory, boundary);
@@ -6078,16 +7393,16 @@ async function resolveVisualCompareOutput(cwd, value, managed, force, watch, zoo
6078
7393
  function visualCompareRowPaths(output, index, zoom, includeDiff) {
6079
7394
  const prefix = `${String(index + 1).padStart(2, "0")}-z${zoomSlug(zoom)}`;
6080
7395
  return {
6081
- leftPath: resolve5(output.assetDirectory, `${prefix}.left.png`),
6082
- rightPath: resolve5(output.assetDirectory, `${prefix}.right.png`),
6083
- leftReceiptPath: resolve5(output.assetDirectory, `${prefix}.left.receipt.json`),
6084
- rightReceiptPath: resolve5(output.assetDirectory, `${prefix}.right.receipt.json`),
6085
- diffPath: includeDiff ? resolve5(output.assetDirectory, `${prefix}.diff.png`) : void 0
7396
+ leftPath: resolve6(output.assetDirectory, `${prefix}.left.png`),
7397
+ rightPath: resolve6(output.assetDirectory, `${prefix}.right.png`),
7398
+ leftReceiptPath: resolve6(output.assetDirectory, `${prefix}.left.receipt.json`),
7399
+ rightReceiptPath: resolve6(output.assetDirectory, `${prefix}.right.receipt.json`),
7400
+ diffPath: includeDiff ? resolve6(output.assetDirectory, `${prefix}.diff.png`) : void 0
6086
7401
  };
6087
7402
  }
6088
7403
  function sideJson(side, cwd) {
6089
7404
  return {
6090
- config: relativePath(cwd, resolve5(cwd, side.config)),
7405
+ config: relativePath(cwd, resolve6(cwd, side.config)),
6091
7406
  label: side.label,
6092
7407
  map: side.map,
6093
7408
  theme: side.theme
@@ -6095,13 +7410,13 @@ function sideJson(side, cwd) {
6095
7410
  }
6096
7411
  function watchSideLabel(cwd, config, map, theme) {
6097
7412
  const selection = map ? `${map} / ${theme ?? "default theme"}` : `singular map / ${theme ?? "default theme"}`;
6098
- return `${relativePath(cwd, resolve5(cwd, config))} (${selection})`;
7413
+ return `${relativePath(cwd, resolve6(cwd, config))} (${selection})`;
6099
7414
  }
6100
7415
  async function assertVisualCompareOutputDoesNotOverlapInputs(plan, leftArtifacts, rightArtifacts) {
6101
7416
  const cwd = process.cwd();
6102
7417
  const inputs = mergeVisualCompareInputPaths([
6103
- { kind: "file", path: resolve5(cwd, plan.config) },
6104
- { kind: "file", path: resolve5(cwd, plan.againstConfig) },
7418
+ { kind: "file", path: resolve6(cwd, plan.config) },
7419
+ { kind: "file", path: resolve6(cwd, plan.againstConfig) },
6105
7420
  ...visualCompareArtifactInputPaths(leftArtifacts),
6106
7421
  ...visualCompareArtifactInputPaths(rightArtifacts)
6107
7422
  ]);
@@ -6168,7 +7483,7 @@ function createVisualCompareInventory(assetDirectory, assets) {
6168
7483
  throw new Error("Visual compare produced an invalid managed asset path.");
6169
7484
  }
6170
7485
  return { path: relativeAssetPath, sha256: sha256Source(source) };
6171
- }).sort((left, right) => compareCodeUnits7(left.path, right.path))
7486
+ }).sort((left, right) => compareCodeUnits8(left.path, right.path))
6172
7487
  };
6173
7488
  }
6174
7489
  function serializeVisualCompareInventory(inventory) {
@@ -6176,7 +7491,7 @@ function serializeVisualCompareInventory(inventory) {
6176
7491
  `;
6177
7492
  }
6178
7493
  async function readVisualCompareInventory(assetDirectory, inventoryPath, boundary) {
6179
- await assertNoSymlinkComponents(dirname8(inventoryPath), boundary);
7494
+ await assertNoSymlinkComponents(dirname10(inventoryPath), boundary);
6180
7495
  let info;
6181
7496
  try {
6182
7497
  info = await lstat3(inventoryPath);
@@ -6199,8 +7514,8 @@ async function readVisualCompareInventory(assetDirectory, inventoryPath, boundar
6199
7514
  const inventory = validateVisualCompareInventory(parsed);
6200
7515
  const assets = [];
6201
7516
  for (const asset of inventory.assets) {
6202
- const path = resolve5(assetDirectory, asset.path);
6203
- await assertNoSymlinkComponents(dirname8(path), boundary);
7517
+ const path = resolve6(assetDirectory, asset.path);
7518
+ await assertNoSymlinkComponents(dirname10(path), boundary);
6204
7519
  let assetInfo;
6205
7520
  try {
6206
7521
  assetInfo = await lstat3(path);
@@ -6243,7 +7558,7 @@ function validateVisualCompareInventory(input) {
6243
7558
  return { path: asset.path, sha256: asset.sha256 };
6244
7559
  });
6245
7560
  const paths = assets.map((asset) => asset.path);
6246
- const sortedPaths = [...paths].sort(compareCodeUnits7);
7561
+ const sortedPaths = [...paths].sort(compareCodeUnits8);
6247
7562
  if (new Set(paths).size !== paths.length || paths.some((path, index) => path !== sortedPaths[index])) {
6248
7563
  throw new Error("Visual compare inventory asset paths must be unique and sorted.");
6249
7564
  }
@@ -6263,25 +7578,25 @@ function isVisualCompareOwnedAssetName(value) {
6263
7578
  return Number.isInteger(row) && row >= 1 && row <= tileflowVisualCompareLimits.maximumRows;
6264
7579
  }
6265
7580
  function hasExactKeys(record, keys) {
6266
- const actual = Object.keys(record).sort(compareCodeUnits7);
6267
- const expected = [...keys].sort(compareCodeUnits7);
7581
+ const actual = Object.keys(record).sort(compareCodeUnits8);
7582
+ const expected = [...keys].sort(compareCodeUnits8);
6268
7583
  return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
6269
7584
  }
6270
7585
  function sha256Source(source) {
6271
7586
  return createHash3("sha256").update(typeof source === "string" ? Buffer.from(source, "utf8") : Buffer.from(source)).digest("hex");
6272
7587
  }
6273
7588
  async function canonicalPathIdentity(path) {
6274
- const absolute = resolve5(path);
7589
+ const absolute = resolve6(path);
6275
7590
  let current = absolute;
6276
7591
  const missingSegments = [];
6277
7592
  while (true) {
6278
7593
  try {
6279
7594
  const existing = await realpath(current);
6280
- const canonical = resolve5(existing, ...missingSegments.reverse()).normalize("NFC");
7595
+ const canonical = resolve6(existing, ...missingSegments.reverse()).normalize("NFC");
6281
7596
  return process.platform === "win32" ? canonical.toLowerCase() : canonical;
6282
7597
  } catch (error) {
6283
7598
  if (!isMissingPathComponent(error)) throw error;
6284
- const parent = dirname8(current);
7599
+ const parent = dirname10(current);
6285
7600
  if (parent === current) {
6286
7601
  const canonical = absolute.normalize("NFC");
6287
7602
  return process.platform === "win32" ? canonical.toLowerCase() : canonical;
@@ -6353,12 +7668,12 @@ function formatCoordinate(value) {
6353
7668
  return String(Math.round(value * 1e6) / 1e6);
6354
7669
  }
6355
7670
  function portablePathIdentity(path) {
6356
- const normalized = resolve5(path).normalize("NFC");
7671
+ const normalized = resolve6(path).normalize("NFC");
6357
7672
  return process.platform === "win32" ? normalized.toLowerCase() : normalized;
6358
7673
  }
6359
7674
  function isInside(parent, child) {
6360
7675
  const path = relative3(parent, child);
6361
- return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !isAbsolute2(path);
7676
+ return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !isAbsolute3(path);
6362
7677
  }
6363
7678
  function pathsOverlap(left, right) {
6364
7679
  return isInside(left, right) || isInside(right, left);
@@ -6438,7 +7753,7 @@ function createTileflowVisualDiffJson(comparisons, cwd) {
6438
7753
  return {
6439
7754
  schemaVersion: 1,
6440
7755
  command: "visual.diff",
6441
- comparisons: [...comparisons].sort((left, right) => compareCodeUnits8(left.comparison.scene, right.comparison.scene)).map(({ comparison, baselinePath, actualPath, diffPath, reportPath }) => ({
7756
+ comparisons: [...comparisons].sort((left, right) => compareCodeUnits9(left.comparison.scene, right.comparison.scene)).map(({ comparison, baselinePath, actualPath, diffPath, reportPath }) => ({
6442
7757
  ...createTileflowVisualComparisonDocument(comparison),
6443
7758
  baselinePath: relativePath(cwd, baselinePath),
6444
7759
  actualPath: relativePath(cwd, actualPath),
@@ -6466,14 +7781,14 @@ async function runVisualAnalyze(scene, options) {
6466
7781
  validateVisualSelection([scene]);
6467
7782
  const region = options.region === void 0 ? void 0 : parseTileflowVisualRegion(options.region);
6468
7783
  const cwd = process.cwd();
6469
- const referencePath = resolve6(cwd, options.reference);
7784
+ const referencePath = resolve7(cwd, options.reference);
6470
7785
  const referencePng = await readVisualReference(referencePath);
6471
7786
  const referenceDimensions = validateTileflowVisualReferencePng(referencePng);
6472
7787
  if (region) assertTileflowVisualRegionFits(region, referenceDimensions, "reference PNG");
6473
7788
  const outputDirectory = await resolveVisualDirectory(cwd, options.outputDir, "output");
6474
- const actualPath = resolve6(outputDirectory.path, `${scene}.actual.png`);
6475
- const managedDiffPath = resolve6(outputDirectory.path, `${scene}.diff.png`);
6476
- const reportPath = resolve6(outputDirectory.path, `${scene}.analysis.json`);
7789
+ const actualPath = resolve7(outputDirectory.path, `${scene}.actual.png`);
7790
+ const managedDiffPath = resolve7(outputDirectory.path, `${scene}.diff.png`);
7791
+ const reportPath = resolve7(outputDirectory.path, `${scene}.analysis.json`);
6477
7792
  assertReferenceIsReadOnly(referencePath, [actualPath, managedDiffPath, reportPath]);
6478
7793
  const controller = installSignalAbortController();
6479
7794
  const session = createTileflowCaptureSession3({
@@ -6562,15 +7877,15 @@ async function runVisualDiff(scenes, options) {
6562
7877
  capture,
6563
7878
  files.kind === "complete" ? files.baseline : void 0
6564
7879
  );
6565
- const actualPath = resolve6(outputDirectory.path, `${capture.scene}.actual.png`);
6566
- const diffPath = comparison.diffPng ? resolve6(outputDirectory.path, `${capture.scene}.diff.png`) : void 0;
7880
+ const actualPath = resolve7(outputDirectory.path, `${capture.scene}.actual.png`);
7881
+ const diffPath = comparison.diffPng ? resolve7(outputDirectory.path, `${capture.scene}.diff.png`) : void 0;
6567
7882
  prepared.push({
6568
7883
  capture,
6569
7884
  comparison,
6570
7885
  baselinePath: files.pngPath,
6571
7886
  actualPath,
6572
7887
  diffPath,
6573
- reportPath: resolve6(outputDirectory.path, `${capture.scene}.visual.json`)
7888
+ reportPath: resolve7(outputDirectory.path, `${capture.scene}.visual.json`)
6574
7889
  });
6575
7890
  }
6576
7891
  const document = createTileflowVisualDiffJson(prepared, cwd);
@@ -6590,7 +7905,7 @@ async function runVisualDiff(scenes, options) {
6590
7905
  }
6591
7906
  ]),
6592
7907
  removePaths: prepared.flatMap(
6593
- (item) => item.diffPath ? [] : [resolve6(outputDirectory.path, `${item.capture.scene}.diff.png`)]
7908
+ (item) => item.diffPath ? [] : [resolve7(outputDirectory.path, `${item.capture.scene}.diff.png`)]
6594
7909
  )
6595
7910
  });
6596
7911
  if (options.json) {
@@ -6653,7 +7968,7 @@ async function runVisualUpdate(scenes, options) {
6653
7968
  renderer: capture.renderer,
6654
7969
  networkDependent: capture.networkDependent
6655
7970
  },
6656
- warnings: [...capture.warnings].sort(compareCodeUnits8)
7971
+ warnings: [...capture.warnings].sort(compareCodeUnits9)
6657
7972
  });
6658
7973
  outputFiles.push(
6659
7974
  { path: files.pngPath, source: capture.png },
@@ -6670,7 +7985,7 @@ async function runVisualUpdate(scenes, options) {
6670
7985
  const document = {
6671
7986
  schemaVersion: 1,
6672
7987
  command: "visual.update",
6673
- updates: updates.sort((left, right) => compareCodeUnits8(left.scene, right.scene))
7988
+ updates: updates.sort((left, right) => compareCodeUnits9(left.scene, right.scene))
6674
7989
  };
6675
7990
  if (options.json) {
6676
7991
  process.stdout.write(serializeTileflowVisualCommandJson(document));
@@ -6711,7 +8026,7 @@ async function resolvePreviousBaseline(capture, files) {
6711
8026
  }
6712
8027
  }
6713
8028
  async function readBaselineFiles(directory, scene) {
6714
- const pngPath = resolve6(directory.path, `${scene}.png`);
8029
+ const pngPath = resolve7(directory.path, `${scene}.png`);
6715
8030
  const receiptPath = captureReceiptPath(pngPath);
6716
8031
  const [png, receipt] = await Promise.all([
6717
8032
  readOptionalArtifact(pngPath, directory.boundary, tileflowVisualArtifactLimits.maximumPngBytes),
@@ -6726,7 +8041,7 @@ async function readBaselineFiles(directory, scene) {
6726
8041
  return { kind: "complete", pngPath, receiptPath, baseline: { png, receipt } };
6727
8042
  }
6728
8043
  async function readOptionalArtifact(path, boundary, maximumBytes) {
6729
- await assertNoSymlinkComponents(dirname9(path), boundary);
8044
+ await assertNoSymlinkComponents(dirname11(path), boundary);
6730
8045
  let info;
6731
8046
  try {
6732
8047
  info = await lstat4(path);
@@ -6771,12 +8086,12 @@ async function readVisualReference(path) {
6771
8086
  return new Uint8Array(await readFile6(path));
6772
8087
  }
6773
8088
  async function resolveVisualDirectory(cwd, value, kind) {
6774
- const path = resolve6(cwd, value);
8089
+ const path = resolve7(cwd, value);
6775
8090
  if (path === cwd || path === parse2(path).root) {
6776
8091
  throw new Error(`Visual ${kind} directory must be a dedicated subdirectory.`);
6777
8092
  }
6778
8093
  if (kind === "baseline") {
6779
- for (const managed of [resolve6(cwd, ".tileflow/captures"), resolve6(cwd, ".tileflow/diffs")]) {
8094
+ for (const managed of [resolve7(cwd, ".tileflow/captures"), resolve7(cwd, ".tileflow/diffs")]) {
6780
8095
  if (pathsOverlap2(path, managed)) {
6781
8096
  throw new Error("Visual baselines must not overlap Tileflow-managed capture/diff outputs.");
6782
8097
  }
@@ -6812,11 +8127,11 @@ function assertReferenceIsReadOnly(referencePath, outputPaths) {
6812
8127
  }
6813
8128
  }
6814
8129
  function portablePathIdentity2(path) {
6815
- return resolve6(path).normalize("NFC").toLowerCase();
8130
+ return resolve7(path).normalize("NFC").toLowerCase();
6816
8131
  }
6817
8132
  function isInside2(parent, child) {
6818
8133
  const path = relative4(parent, child);
6819
- return path === "" || path !== ".." && !path.startsWith(`..${sep4}`) && !isAbsolute3(path);
8134
+ return path === "" || path !== ".." && !path.startsWith(`..${sep4}`) && !isAbsolute4(path);
6820
8135
  }
6821
8136
  function isMissingFile5(error) {
6822
8137
  return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
@@ -6831,12 +8146,14 @@ var defaultApiUrl = defaultTileflowApiUrl;
6831
8146
  var defaultAppUrl = "https://tileflow.dev";
6832
8147
  var defaultConfigPath = defaultTileflowConfigPath;
6833
8148
  var defaultManifestPath = defaultTileflowManifestPath;
6834
- program.name("tileflow").description("Beautiful maps from config.").version(packageJson.version).showHelpAfterError(pc3.gray("\nRun tileflow <command> --help for usage.")).showSuggestionAfterError();
8149
+ program.name("tileflow").description("Beautiful maps from config.").showHelpAfterError(pc6.gray("\nRun tileflow <command> --help for usage.")).showSuggestionAfterError();
8150
+ program.addHelpText("after", "\n -V, --version output the version number\n");
6835
8151
  registerLanguageCommand(program);
6836
8152
  registerTilesetCommands(program, { defaultApiUrl, loadAuthConfig });
8153
+ registerIconSetCommands(program, { defaultApiUrl, loadAuthConfig });
6837
8154
  program.command("init").description("Create a starter tileflow.config.ts").option("-f, --force", "overwrite an existing config").action(async (options) => {
6838
- const configPath = resolve7(process.cwd(), defaultConfigPath);
6839
- const ignorePath = resolve7(process.cwd(), ".gitignore");
8155
+ const configPath = resolve8(process.cwd(), defaultConfigPath);
8156
+ const ignorePath = resolve8(process.cwd(), ".gitignore");
6840
8157
  if (existsSync(configPath) && !options.force) {
6841
8158
  logWarning(`${pathLabel(defaultConfigPath)} already exists.`);
6842
8159
  printNextSteps([`Run ${command("tileflow init --force")} to replace it.`]);
@@ -6852,9 +8169,9 @@ program.command("init").description("Create a starter tileflow.config.ts").optio
6852
8169
  }
6853
8170
  logSuccess(`Created ${pathLabel(defaultConfigPath)}.`);
6854
8171
  printKeyValue("Path", pathLabel(configPath));
6855
- printKeyValue("Map", pc3.bold("madrid"));
8172
+ printKeyValue("Map", pc6.bold("madrid"));
6856
8173
  printNextSteps([
6857
- `Install ${pc3.cyan("@tileflow/core")} and ${pc3.cyan("@tileflow/maps")} in this project if needed.`,
8174
+ `Install ${pc6.cyan("@tileflow/core")} and ${pc6.cyan("@tileflow/maps")} in this project if needed.`,
6858
8175
  `Preview locally with ${command("tileflow preview")}.`,
6859
8176
  `Validate the config with ${command("tileflow validate")}.`
6860
8177
  ]);
@@ -6943,177 +8260,244 @@ program.command("whoami").description("Show the authenticated Tileflow account")
6943
8260
  logSuccess("Tileflow CLI is authenticated.");
6944
8261
  printAccountDetails(selected.session);
6945
8262
  });
6946
- program.command("validate").description("Validate a Tileflow config").option("-c, --config <path>", "config path", defaultConfigPath).option("--target <target>", "validation target: local or hosted", "local").option(
8263
+ program.command("validate").description("Validate a Tileflow config").option("-c, --config <path>", "config path", defaultConfigPath).option("--target <target>", "validation target: local or hosted", "local").option("--renderer <renderer>", "artifact renderer: web or native", "web").option(
6947
8264
  "--api-base-url <url>",
6948
8265
  "Tileflow API base URL used to resolve official map assets",
6949
8266
  process.env.TILEFLOW_API_URL ?? defaultApiUrl
6950
- ).option("--json", "print deterministic schema-version-1 JSON").action(async (options) => {
6951
- if (options.target !== "local" && options.target !== "hosted") {
6952
- if (options.json) {
6953
- const failure = createTileflowCommandFailureDocument4(
6954
- "validate",
6955
- {
6956
- code: "INVALID_TARGET",
6957
- issues: [
6958
- {
6959
- code: "INVALID_TARGET",
6960
- message: `Invalid validation target: ${options.target}`,
6961
- path: "target",
6962
- phase: "command-validation"
6963
- }
6964
- ],
6965
- phase: "command-validation"
6966
- },
6967
- process.cwd(),
6968
- { code: "INVALID_TARGET", phase: "command-validation" }
6969
- );
6970
- process.stderr.write(serializeTileflowCommandDocument4(failure));
6971
- process.exitCode = 1;
8267
+ ).option("--cache-dir <path>", "verified Icon Set artifact cache root").option("--offline", "fail a locked Icon Set cache miss instead of hydrating it").option("--json", "print deterministic schema-version-1 JSON").action(
8268
+ async (options) => {
8269
+ if (options.renderer !== "web") {
8270
+ await runRendererArtifactCommand("validate", {
8271
+ ...options,
8272
+ icons: resolveIconOptions(options)
8273
+ });
6972
8274
  return;
6973
8275
  }
6974
- logError(`Invalid validation target: ${options.target}`);
6975
- printNextSteps([
6976
- `Use ${command("tileflow validate --target local")} or ${command("tileflow validate --target hosted")}.`
6977
- ]);
6978
- process.exitCode = 1;
6979
- return;
6980
- }
6981
- let failureDefaults = { code: "CONFIG_LOAD_FAILED", phase: "config-load" };
6982
- try {
6983
- if (!options.json) logInfo(`Validating ${pathLabel(options.config)}.`);
6984
- const loaded = await withTileflowConfigSecretsHidden(
6985
- () => loadTileflowConfigWithInputs3(options.config)
6986
- );
6987
- const project = loaded.project;
6988
- const baseDirectory = dirname10(loaded.configFile);
6989
- failureDefaults = { code: "CONFIG_INVALID", phase: "config-validation" };
6990
- assertValidTileflowConfig2(project);
6991
- const mapNames = getTileflowMapNames3(project).sort();
6992
- failureDefaults = { code: "ICON_COMPILATION_FAILED", phase: "icon-compilation" };
6993
- const compiledIcons = await compileTileflowIconPackages2(project, {
6994
- baseDirectory,
6995
- cwd: process.cwd(),
6996
- target: options.target
6997
- });
6998
- const mapAssets = createCompiledMapAssets(
6999
- compiledIcons,
7000
- (binding) => options.target === "hosted" ? `${normalizeUrl(options.apiBaseUrl)}/sprites/preflight/${binding.packageHash}/sprite` : `/tileflow/icons/${binding.mapName}/sprite`
7001
- );
7002
- failureDefaults = { code: "STYLE_INVALID", phase: "style-validation" };
7003
- const compiledStyles = createTileflowStyles(project, {
7004
- apiBaseUrl: options.apiBaseUrl,
7005
- mapAssets
7006
- });
7007
- failureDefaults = { code: "TF_LOCAL_TILESET_INVALID", phase: "local-tileset-resolution" };
7008
- const localTilesets = await prepareTileflowLocalTilesets(project, compiledStyles, {
7009
- assetBaseUrl: "/tileflow",
7010
- baseDirectory,
7011
- cwd: process.cwd()
7012
- });
7013
- failureDefaults = { code: "FONT_COMPILATION_FAILED", phase: "font-compilation" };
7014
- const { styles } = await prepareTileflowStyleFonts(project, localTilesets.styles, {
7015
- assetBaseUrl: "/tileflow",
7016
- baseDirectory,
7017
- cwd: process.cwd(),
7018
- target: options.target
7019
- });
7020
- failureDefaults = { code: "HOSTED_INCOMPATIBLE", phase: "hosted-validation" };
7021
- const hostedIssues = options.target === "hosted" ? inspectTileflowHostedCompatibility(project, styles) : [];
7022
- if (hostedIssues.length > 0) {
7023
- if (!options.json) {
7024
- printHostedCompatibilityIssues(hostedIssues);
8276
+ if (options.target !== "local" && options.target !== "hosted") {
8277
+ if (options.json) {
8278
+ const failure2 = createTileflowCommandFailureDocument5(
8279
+ "validate",
8280
+ {
8281
+ code: "INVALID_TARGET",
8282
+ issues: [
8283
+ {
8284
+ code: "INVALID_TARGET",
8285
+ message: `Invalid validation target: ${options.target}`,
8286
+ path: "target",
8287
+ phase: "command-validation"
8288
+ }
8289
+ ],
8290
+ phase: "command-validation"
8291
+ },
8292
+ process.cwd(),
8293
+ { code: "INVALID_TARGET", phase: "command-validation" }
8294
+ );
8295
+ process.stderr.write(serializeTileflowCommandDocument5(failure2));
8296
+ process.exitCode = 1;
7025
8297
  return;
7026
8298
  }
7027
- throw Object.assign(new Error("Tileflow config is not Hosted-compatible."), {
7028
- code: "HOSTED_INCOMPATIBLE",
7029
- issues: hostedIssues.map((issue) => ({
8299
+ logError(`Invalid validation target: ${options.target}`);
8300
+ printNextSteps([
8301
+ `Use ${command("tileflow validate --target local")} or ${command("tileflow validate --target hosted")}.`
8302
+ ]);
8303
+ process.exitCode = 1;
8304
+ return;
8305
+ }
8306
+ let failureDefaults = { code: "CONFIG_LOAD_FAILED", phase: "config-load" };
8307
+ try {
8308
+ if (!options.json) logInfo(`Validating ${pathLabel(options.config)}.`);
8309
+ const loaded = await withTileflowConfigSecretsHidden(
8310
+ () => loadTileflowConfigWithInputs4(options.config)
8311
+ );
8312
+ const project = loaded.project;
8313
+ const baseDirectory = dirname12(loaded.configFile);
8314
+ failureDefaults = { code: "CONFIG_INVALID", phase: "config-validation" };
8315
+ assertValidTileflowConfig3(project);
8316
+ const mapNames = getTileflowMapNames5(project).sort();
8317
+ failureDefaults = { code: "ICON_COMPILATION_FAILED", phase: "icon-compilation" };
8318
+ const compiledIcons = await compileTileflowIconPackages3(project, {
8319
+ baseDirectory,
8320
+ cwd: process.cwd(),
8321
+ icons: resolveIconOptions(options),
8322
+ target: options.target
8323
+ });
8324
+ const mapAssets = createCompiledMapAssets(
8325
+ compiledIcons,
8326
+ (binding) => options.target === "hosted" ? `${normalizeUrl(options.apiBaseUrl)}/sprites/preflight/${binding.packageHash}/sprite` : `/tileflow/icons/${binding.mapName}/sprite`
8327
+ );
8328
+ failureDefaults = { code: "STYLE_INVALID", phase: "style-validation" };
8329
+ const compiledStyles = createTileflowStyles2(project, {
8330
+ apiBaseUrl: options.apiBaseUrl,
8331
+ mapAssets
8332
+ });
8333
+ failureDefaults = { code: "TF_LOCAL_TILESET_INVALID", phase: "local-tileset-resolution" };
8334
+ const localTilesets = await prepareTileflowLocalTilesets2(project, compiledStyles, {
8335
+ assetBaseUrl: "/tileflow",
8336
+ baseDirectory,
8337
+ cwd: process.cwd()
8338
+ });
8339
+ failureDefaults = { code: "FONT_COMPILATION_FAILED", phase: "font-compilation" };
8340
+ const { styles } = await prepareTileflowStyleFonts2(project, localTilesets.styles, {
8341
+ assetBaseUrl: "/tileflow",
8342
+ baseDirectory,
8343
+ cwd: process.cwd(),
8344
+ target: options.target
8345
+ });
8346
+ failureDefaults = { code: "HOSTED_INCOMPATIBLE", phase: "hosted-validation" };
8347
+ const hostedIssues = options.target === "hosted" ? inspectTileflowHostedCompatibility(project, styles) : [];
8348
+ if (hostedIssues.length > 0) {
8349
+ if (!options.json) {
8350
+ printHostedCompatibilityIssues(hostedIssues);
8351
+ return;
8352
+ }
8353
+ throw Object.assign(new Error("Tileflow config is not Hosted-compatible."), {
7030
8354
  code: "HOSTED_INCOMPATIBLE",
7031
- message: issue.message,
7032
- path: issue.path,
8355
+ issues: hostedIssues.map((issue) => ({
8356
+ code: "HOSTED_INCOMPATIBLE",
8357
+ message: issue.message,
8358
+ path: issue.path,
8359
+ phase: "hosted-validation"
8360
+ })),
7033
8361
  phase: "hosted-validation"
7034
- })),
7035
- phase: "hosted-validation"
7036
- });
7037
- }
7038
- const themeWarnings = mapNames.flatMap(
7039
- (mapName) => auditTileflowMapThemeValues(project.maps[mapName]).filter(
7040
- ({ severity }) => severity === "warning"
7041
- )
7042
- );
7043
- const diagnostics = themeWarnings.length === 0 ? [] : createTileflowStructuredDiagnostics2({ diagnostics: themeWarnings }, process.cwd(), {
7044
- code: "VALIDATION_WARNING",
7045
- phase: "theme-audit"
7046
- });
7047
- const checks = [
7048
- "Config schema",
7049
- "Icon asset closure",
7050
- "Text provider closure",
7051
- "Named map styles",
7052
- "MapLibre style semantics",
7053
- ...options.target === "hosted" ? ["Hosted compatibility"] : []
7054
- ];
7055
- if (options.json) {
7056
- const summary = createTileflowCommandSummary2({
7057
- code: "VALIDATION_OK",
7058
- command: "validate",
7059
- message: `Tileflow config is valid (${plural(mapNames.length, "map")}).`,
7060
- ok: true,
7061
- path: "",
7062
- phase: "validation",
7063
- severity: "info",
7064
- suggestion: "No changes are required."
8362
+ });
8363
+ }
8364
+ const themeWarnings = mapNames.flatMap(
8365
+ (mapName) => auditTileflowMapThemeValues2(project.maps[mapName]).filter(
8366
+ ({ severity }) => severity === "warning"
8367
+ )
8368
+ );
8369
+ const diagnostics = themeWarnings.length === 0 ? [] : createTileflowStructuredDiagnostics3({ diagnostics: themeWarnings }, process.cwd(), {
8370
+ code: "VALIDATION_WARNING",
8371
+ phase: "theme-audit"
7065
8372
  });
7066
- process.stdout.write(
7067
- serializeTileflowCommandDocument4({
7068
- ...summary,
7069
- target: options.target,
7070
- maps: mapNames,
7071
- checks,
7072
- diagnostics
7073
- })
8373
+ const sharedIconMaps = Object.keys(compiledIcons.compositions).sort();
8374
+ const checks = [
8375
+ "Config schema",
8376
+ "Icon asset closure",
8377
+ ...sharedIconMaps.length > 0 ? ["Locked Icon Set composition"] : [],
8378
+ "Text provider closure",
8379
+ "Named map styles",
8380
+ "MapLibre style semantics",
8381
+ ...options.target === "hosted" ? ["Hosted compatibility"] : []
8382
+ ];
8383
+ if (options.json) {
8384
+ const summary = createTileflowCommandSummary3({
8385
+ code: "VALIDATION_OK",
8386
+ command: "validate",
8387
+ message: `Tileflow config is valid (${plural(mapNames.length, "map")}).`,
8388
+ ok: true,
8389
+ path: "",
8390
+ phase: "validation",
8391
+ severity: "info",
8392
+ suggestion: "No changes are required."
8393
+ });
8394
+ process.stdout.write(
8395
+ serializeTileflowCommandDocument5({
8396
+ ...summary,
8397
+ target: options.target,
8398
+ maps: mapNames,
8399
+ checks,
8400
+ diagnostics
8401
+ })
8402
+ );
8403
+ return;
8404
+ }
8405
+ logSuccess(`Config is valid (${plural(mapNames.length, "map")}).`);
8406
+ printChecks(checks);
8407
+ for (const diagnostic of diagnostics) {
8408
+ logWarning(`${diagnostic.path || "(root)"}: ${diagnostic.message}`);
8409
+ }
8410
+ } catch (error) {
8411
+ if (!options.json) throw error;
8412
+ const failure2 = createTileflowCommandFailureDocument5(
8413
+ "validate",
8414
+ error,
8415
+ process.cwd(),
8416
+ failureDefaults
7074
8417
  );
7075
- return;
7076
- }
7077
- logSuccess(`Config is valid (${plural(mapNames.length, "map")}).`);
7078
- printChecks(checks);
7079
- for (const diagnostic of diagnostics) {
7080
- logWarning(`${diagnostic.path || "(root)"}: ${diagnostic.message}`);
8418
+ process.stderr.write(serializeTileflowCommandDocument5(failure2));
8419
+ process.exitCode = 1;
7081
8420
  }
7082
- } catch (error) {
7083
- if (!options.json) throw error;
7084
- const failure = createTileflowCommandFailureDocument4(
7085
- "validate",
7086
- error,
7087
- process.cwd(),
7088
- failureDefaults
7089
- );
7090
- process.stderr.write(serializeTileflowCommandDocument4(failure));
7091
- process.exitCode = 1;
7092
8421
  }
7093
- });
7094
- program.command("build").description("Generate static Tileflow styles").option("-c, --config <path>", "config path", defaultConfigPath).option("-o, --out <path>", "output directory", "dist/tileflow").option(
8422
+ );
8423
+ program.command("build").description("Generate static Tileflow styles").option("-c, --config <path>", "config path", defaultConfigPath).option("-o, --out <path>", "output directory", "dist/tileflow").option("--renderer <renderer>", "artifact renderer: web or native", "web").option("--target <target>", "artifact target: local or hosted (build supports local)", "local").option("--json", "print deterministic schema-version-1 JSON").option(
7095
8424
  "--api-base-url <url>",
7096
8425
  "Tileflow API base URL used to resolve official map assets",
7097
8426
  process.env.TILEFLOW_API_URL ?? defaultApiUrl
7098
- ).action(async (options) => {
7099
- logInfo(`Building ${pathLabel(options.config)}.`);
7100
- await withTileflowConfigSecretsHidden(
7101
- () => writeTileflowBuildArtifacts({
7102
- config: options.config,
7103
- outDir: options.out,
7104
- styleBaseUrl: ".",
7105
- apiBaseUrl: options.apiBaseUrl
7106
- })
7107
- );
7108
- logSuccess("Built Tileflow artifacts.");
7109
- printKeyValue("Output", pathLabel(resolve7(process.cwd(), options.out)));
7110
- });
7111
- program.command("preview").alias("dev").description("Run a local map preview").option("-c, --config <path>", "config path", defaultConfigPath).option("--against-config <path>", "second config for the comparison workbench").option("--against-map <name>", "map on the right; defaults to --map").option("--against-scene <name>", "committed standalone scene on the right").option("--against-theme <name>", "concrete theme on the right; defaults to --theme").option("--host <host>", "bind host: an explicit IP address or localhost", defaultTileflowDevHost).option("--map <name>", "preview one configured map").option("--theme <name>", "preview one concrete theme; defaults to the map default").option("-p, --port <port>", "preview port", "3333").option("--scene <name>", "preview one committed standalone map scene").option(
8427
+ ).option("--cache-dir <path>", "verified Icon Set artifact cache root").option("--offline", "fail a locked Icon Set cache miss instead of hydrating it").action(
8428
+ async (options) => {
8429
+ const icons = resolveIconOptions(options);
8430
+ if (options.renderer !== "web" || options.target !== "local" || options.json) {
8431
+ await runRendererArtifactCommand("build", { ...options, icons });
8432
+ return;
8433
+ }
8434
+ logInfo(`Building ${pathLabel(options.config)}.`);
8435
+ await withTileflowConfigSecretsHidden(
8436
+ () => writeTileflowBuildArtifacts({
8437
+ config: options.config,
8438
+ outDir: options.out,
8439
+ styleBaseUrl: ".",
8440
+ apiBaseUrl: options.apiBaseUrl,
8441
+ ...icons ? { icons } : {}
8442
+ })
8443
+ );
8444
+ logSuccess("Built Tileflow artifacts.");
8445
+ printKeyValue("Output", pathLabel(resolve8(process.cwd(), options.out)));
8446
+ }
8447
+ );
8448
+ program.command("preview").alias("dev").description("Run a local map preview").option("-c, --config <path>", "config path", defaultConfigPath).option("--against-config <path>", "second config for the comparison workbench").option("--against-map <name>", "map on the right; defaults to --map").option("--against-scene <name>", "committed standalone scene on the right").option("--against-theme <name>", "concrete theme on the right; defaults to --theme").option("--host <host>", "bind host: an explicit IP address or localhost", defaultTileflowDevHost).option("--map <name>", "preview one configured map").option("--theme <name>", "preview one concrete theme; defaults to the map default").option("--renderer <renderer>", "artifact renderer: web or native", "web").option("-p, --port <port>", "preview port", "3333").option("--scene <name>", "preview one committed standalone map scene").option(
7112
8449
  "--api-base-url <url>",
7113
8450
  "Tileflow API base URL used to resolve official map assets",
7114
8451
  process.env.TILEFLOW_API_URL ?? defaultApiUrl
7115
- ).option("--json", "emit schema-version-1 NDJSON lifecycle events").action(
8452
+ ).option("--cache-dir <path>", "verified Icon Set artifact cache root").option("--offline", "fail a locked Icon Set cache miss instead of hydrating it").option("--json", "emit schema-version-1 NDJSON lifecycle events").action(
7116
8453
  async (options) => {
8454
+ let renderer;
8455
+ try {
8456
+ renderer = resolveTileflowRenderer2(options.renderer);
8457
+ } catch (error) {
8458
+ const diagnostics = createTileflowArtifactDiagnostics3(error, process.cwd());
8459
+ if (options.json) {
8460
+ const first = diagnostics[0];
8461
+ process.stdout.write(
8462
+ `${JSON.stringify({
8463
+ schemaVersion: 1,
8464
+ command: "dev",
8465
+ event: "error",
8466
+ code: first?.code ?? "NATIVE_RENDERER_UNSUPPORTED",
8467
+ phase: first?.phase ?? "command-validation",
8468
+ diagnostics
8469
+ })}
8470
+ `
8471
+ );
8472
+ } else {
8473
+ logError(diagnostics[0]?.message ?? "Unsupported artifact renderer.");
8474
+ }
8475
+ process.exitCode = 1;
8476
+ return;
8477
+ }
8478
+ const comparisonRequested = options.againstConfig !== void 0 || options.againstMap !== void 0 || options.againstScene !== void 0 || options.againstTheme !== void 0;
8479
+ if (renderer === "native" && (options.scene !== void 0 || comparisonRequested)) {
8480
+ if (options.json) {
8481
+ process.stdout.write(
8482
+ `${JSON.stringify({
8483
+ schemaVersion: 1,
8484
+ command: "dev",
8485
+ event: "error",
8486
+ code: "NATIVE_RENDERER_UNSUPPORTED",
8487
+ phase: "command-validation",
8488
+ renderer: "native",
8489
+ profile: tileflowNativePreviewProfile
8490
+ })}
8491
+ `
8492
+ );
8493
+ } else {
8494
+ logError(
8495
+ "Native preview serves assets only; scenes and comparison workbench options are unavailable."
8496
+ );
8497
+ }
8498
+ process.exitCode = 1;
8499
+ return;
8500
+ }
7117
8501
  const port = parsePort(options.port);
7118
8502
  if (port === null) {
7119
8503
  logError(`Invalid port: ${options.port}`);
@@ -7158,28 +8542,45 @@ program.command("preview").alias("dev").description("Run a local map preview").o
7158
8542
  process.exitCode = 1;
7159
8543
  return;
7160
8544
  }
7161
- const comparisonRequested = options.againstConfig !== void 0 || options.againstMap !== void 0 || options.againstScene !== void 0 || options.againstTheme !== void 0;
7162
8545
  if (comparisonRequested) {
7163
8546
  await runTileflowComparisonPreview(options, { host, port });
7164
8547
  return;
7165
8548
  }
7166
8549
  await withTileflowConfigSecretsHidden(async () => {
7167
8550
  const origin = tileflowDevOrigin(host, port);
8551
+ const manifestUrl = renderer === "native" ? getTileflowNativePreviewManifestUrl(origin) : void 0;
8552
+ const nativeLifecycleFields = manifestUrl ? {
8553
+ renderer: "native",
8554
+ profile: tileflowNativePreviewProfile,
8555
+ manifest: manifestUrl,
8556
+ assetsOnly: true
8557
+ } : {};
8558
+ const icons = resolveIconOptions(options);
7168
8559
  const session = await createTileflowArtifactSession3({
7169
8560
  assetBaseUrl: origin,
7170
8561
  apiBaseUrl: options.apiBaseUrl,
7171
8562
  config: options.config,
8563
+ ...icons ? { icons } : {},
8564
+ ...renderer === "native" ? { renderer: "native" } : {},
7172
8565
  styleBaseUrl: origin,
7173
8566
  watch: true
7174
8567
  });
7175
8568
  const initialArtifacts = session.getLastGoodArtifacts();
8569
+ let nativeSelection;
7176
8570
  try {
7177
8571
  if (initialArtifacts) {
7178
- resolveTileflowPreview(initialArtifacts.project, {
7179
- map: options.map,
7180
- scene: options.scene,
7181
- theme: options.theme
7182
- });
8572
+ if (renderer === "native") {
8573
+ nativeSelection = resolveTileflowNativePreviewSelection(initialArtifacts.manifest, {
8574
+ map: options.map,
8575
+ theme: options.theme
8576
+ });
8577
+ } else {
8578
+ resolveTileflowPreview(initialArtifacts.project, {
8579
+ map: options.map,
8580
+ scene: options.scene,
8581
+ theme: options.theme
8582
+ });
8583
+ }
7183
8584
  }
7184
8585
  } catch (error) {
7185
8586
  await session.close();
@@ -7187,7 +8588,12 @@ program.command("preview").alias("dev").description("Run a local map preview").o
7187
8588
  process.exitCode = 1;
7188
8589
  return;
7189
8590
  }
7190
- const fetch2 = createTileflowDevRequestHandler({
8591
+ const fetch2 = renderer === "native" ? createTileflowNativePreviewRequestHandler({
8592
+ config: options.config,
8593
+ apiBaseUrl: options.apiBaseUrl,
8594
+ onError: printTileflowPreviewError,
8595
+ session
8596
+ }) : createTileflowDevRequestHandler2({
7191
8597
  config: options.config,
7192
8598
  apiBaseUrl: options.apiBaseUrl,
7193
8599
  map: options.map,
@@ -7212,7 +8618,8 @@ program.command("preview").alias("dev").description("Run a local map preview").o
7212
8618
  ..."lastGoodGeneration" in state && state.lastGoodGeneration !== void 0 ? { lastGoodGeneration: state.lastGoodGeneration } : {},
7213
8619
  ...firstDiagnostic?.code ? { code: firstDiagnostic.code } : {},
7214
8620
  ...firstDiagnostic?.phase ? { phase: firstDiagnostic.phase } : {},
7215
- ...state.status === "invalid" ? { diagnostics: state.diagnostics } : {}
8621
+ ...state.status === "invalid" ? { diagnostics: state.diagnostics } : {},
8622
+ ...nativeLifecycleFields
7216
8623
  })}
7217
8624
  `
7218
8625
  );
@@ -7221,14 +8628,16 @@ program.command("preview").alias("dev").description("Run a local map preview").o
7221
8628
  if (state.status === "invalid") {
7222
8629
  console.error(
7223
8630
  [
7224
- `Tileflow generation ${state.generation} is invalid; preserving the last valid preview.`,
8631
+ renderer === "native" ? `Tileflow generation ${state.generation} is invalid; preserving the last valid native artifact family.` : `Tileflow generation ${state.generation} is invalid; preserving the last valid preview.`,
7225
8632
  ...state.diagnostics.map(
7226
8633
  (diagnostic) => `- ${diagnostic.path || "(root)"}: ${diagnostic.message}`
7227
8634
  )
7228
8635
  ].join("\n")
7229
8636
  );
7230
8637
  } else if (recovered) {
7231
- logSuccess(`Tileflow preview recovered at generation ${state.generation}.`);
8638
+ logSuccess(
8639
+ renderer === "native" ? `Tileflow native artifact preview recovered at generation ${state.generation}.` : `Tileflow preview recovered at generation ${state.generation}.`
8640
+ );
7232
8641
  }
7233
8642
  };
7234
8643
  emitState(session.getState());
@@ -7241,13 +8650,26 @@ program.command("preview").alias("dev").description("Run a local map preview").o
7241
8650
  server = createdServer;
7242
8651
  });
7243
8652
  if (!options.json) {
7244
- logSuccess("Tileflow preview is running and watching for changes.");
7245
- printKeyValue("Local", link(origin));
7246
- printKeyValue("Config", pathLabel(options.config));
7247
- if (options.map) printKeyValue("Map", options.map);
7248
- if (options.theme) printKeyValue("Theme", options.theme);
7249
- if (options.scene) printKeyValue("Scene", options.scene);
7250
- logMuted("Press Ctrl+C to stop.");
8653
+ if (renderer === "native") {
8654
+ logSuccess("Tileflow native artifact preview is running and watching for changes.");
8655
+ printKeyValue("Manifest", link(manifestUrl));
8656
+ printKeyValue("Profile", tileflowNativePreviewProfile);
8657
+ printKeyValue("Endpoint", "assets-only");
8658
+ printKeyValue("Config", pathLabel(options.config));
8659
+ if (nativeSelection) {
8660
+ printKeyValue("Map", nativeSelection.mapName);
8661
+ printKeyValue("Theme", nativeSelection.themeName);
8662
+ }
8663
+ logMuted("Metro remains the JavaScript development server. Press Ctrl+C to stop.");
8664
+ } else {
8665
+ logSuccess("Tileflow preview is running and watching for changes.");
8666
+ printKeyValue("Local", link(origin));
8667
+ printKeyValue("Config", pathLabel(options.config));
8668
+ if (options.map) printKeyValue("Map", options.map);
8669
+ if (options.theme) printKeyValue("Theme", options.theme);
8670
+ if (options.scene) printKeyValue("Scene", options.scene);
8671
+ logMuted("Press Ctrl+C to stop.");
8672
+ }
7251
8673
  }
7252
8674
  await waitForTerminationSignal(server);
7253
8675
  } finally {
@@ -7257,7 +8679,13 @@ program.command("preview").alias("dev").description("Run a local map preview").o
7257
8679
  await session.close();
7258
8680
  if (options.json) {
7259
8681
  process.stdout.write(
7260
- `${JSON.stringify({ schemaVersion: 1, command: "dev", event: "stopped", generation })}
8682
+ `${JSON.stringify({
8683
+ schemaVersion: 1,
8684
+ command: "dev",
8685
+ event: "stopped",
8686
+ generation,
8687
+ ...nativeLifecycleFields
8688
+ })}
7261
8689
  `
7262
8690
  );
7263
8691
  }
@@ -7265,10 +8693,10 @@ program.command("preview").alias("dev").description("Run a local map preview").o
7265
8693
  });
7266
8694
  }
7267
8695
  );
7268
- program.command("deploy").description("Deploy maps to Tileflow and write the frontend manifest").option("-c, --config <path>", "config path", defaultConfigPath).option("--manifest <path>", "manifest path written for frontend bundlers", defaultManifestPath).option("--api-url <url>", "Tileflow API URL", process.env.TILEFLOW_API_URL).option("--api-key <key>", "Tileflow API key", process.env.TILEFLOW_API_KEY).option("--map-id <id>", "managed Map destination").option("--map <name>", "configured map to connect when the repository contains multiple maps").option(
8696
+ program.command("deploy").description("Deploy maps to Tileflow and write the frontend manifest").option("-c, --config <path>", "config path", defaultConfigPath).option("--manifest <path>", "manifest path written for frontend bundlers", defaultManifestPath).option("--api-url <url>", "Tileflow API URL", process.env.TILEFLOW_API_URL).option("--api-key <key>", "Tileflow API key", process.env.TILEFLOW_API_KEY).option("--map-id <id>", "managed Map destination").option("--with-native", "publish web and native-v1 together for the selected managed Map").option("--map <name>", "configured map to connect when the repository contains multiple maps").option(
7269
8697
  "--overwrite-self-hosted-manifest",
7270
8698
  "explicitly replace an existing self-hosted manifest at --manifest"
7271
- ).action(
8699
+ ).option("--cache-dir <path>", "verified Icon Set artifact cache root").option("--offline", "fail a locked Icon Set cache miss instead of hydrating it").action(
7272
8700
  async (options) => {
7273
8701
  const source = resolveDeploySource(process.env);
7274
8702
  const resolveApi = (selectedMap) => requireApiOptions(options, {
@@ -7286,19 +8714,39 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7286
8714
  options.apiUrl ?? defaultApiUrl,
7287
8715
  ...options.overwriteSelfHostedManifest ? ["--overwrite-self-hosted-manifest"] : [],
7288
8716
  ...options.mapId ? ["--map-id", options.mapId] : [],
7289
- ...options.map && selectedMap ? ["--map", selectedMap] : []
8717
+ ...options.map && selectedMap ? ["--map", selectedMap] : [],
8718
+ ...options.withNative ? ["--with-native"] : []
7290
8719
  ])
7291
8720
  });
7292
8721
  const apiUrl = normalizeApiOrigin(options.apiUrl ?? defaultApiUrl);
7293
8722
  delete process.env.TILEFLOW_API_KEY;
8723
+ if (options.withNative) {
8724
+ const result = await runHostedNativeDeploy(
8725
+ { ...options, apiUrl },
8726
+ {
8727
+ source,
8728
+ resolveApi,
8729
+ loadManifest: loadExistingDeployManifest,
8730
+ writeManifest: writeDeployManifest
8731
+ }
8732
+ );
8733
+ if (result) {
8734
+ logSuccess(
8735
+ `${result.changed ? "Published" : "Unchanged"} ${pc6.bold(result.mapName)} web + native-v1 (v${result.version}).`
8736
+ );
8737
+ printKeyValue("Manifest", pathLabel(result.manifestPath));
8738
+ printKeyValue("Native", link(result.nativeManifestUrl));
8739
+ }
8740
+ return;
8741
+ }
7294
8742
  logInfo(`Deploying ${pathLabel(options.config)}.`);
7295
8743
  const loaded = await withTileflowConfigSecretsHidden(
7296
- () => loadTileflowConfigWithInputs3(options.config)
8744
+ () => loadTileflowConfigWithInputs4(options.config)
7297
8745
  );
7298
8746
  const project = loaded.project;
7299
- const baseDirectory = dirname10(loaded.configFile);
7300
- assertValidTileflowConfig2(project);
7301
- const configuredMapNames = getTileflowMapNames3(project);
8747
+ const baseDirectory = dirname12(loaded.configFile);
8748
+ assertValidTileflowConfig3(project);
8749
+ const configuredMapNames = getTileflowMapNames5(project);
7302
8750
  const mapNames = selectManagedDeployMaps(configuredMapNames, options);
7303
8751
  if (!mapNames) return;
7304
8752
  if (!validateDeployTarget(options)) return;
@@ -7309,9 +8757,10 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7309
8757
  mapNames.map((mapName) => [mapName, project.maps[mapName]])
7310
8758
  )
7311
8759
  };
7312
- const compiledIcons = await compileTileflowIconPackages2(deploymentProject, {
8760
+ const compiledIcons = await compileTileflowIconPackages3(deploymentProject, {
7313
8761
  baseDirectory,
7314
8762
  cwd: process.cwd(),
8763
+ icons: resolveIconOptions(options),
7315
8764
  target: "hosted"
7316
8765
  });
7317
8766
  const bindingsByMap = new Map(
@@ -7324,16 +8773,16 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7324
8773
  compiledIcons,
7325
8774
  (binding) => `${apiUrl}/sprites/preflight/${binding.packageHash}/sprite`
7326
8775
  );
7327
- const compiledPreflightStyles = createTileflowStyles(deploymentProject, {
8776
+ const compiledPreflightStyles = createTileflowStyles2(deploymentProject, {
7328
8777
  apiBaseUrl: apiUrl,
7329
8778
  mapAssets: preflightMapAssets
7330
8779
  });
7331
- const preflightLocalTilesets = await prepareTileflowLocalTilesets(
8780
+ const preflightLocalTilesets = await prepareTileflowLocalTilesets2(
7332
8781
  deploymentProject,
7333
8782
  compiledPreflightStyles,
7334
8783
  { assetBaseUrl: apiUrl, baseDirectory, cwd: process.cwd() }
7335
8784
  );
7336
- const preflightFonts = await prepareTileflowStyleFonts(
8785
+ const preflightFonts = await prepareTileflowStyleFonts2(
7337
8786
  deploymentProject,
7338
8787
  preflightLocalTilesets.styles,
7339
8788
  {
@@ -7392,7 +8841,7 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7392
8841
  );
7393
8842
  const action = uploaded.value.changed === false ? "Reused" : "Uploaded";
7394
8843
  logSuccess(
7395
- `${action} icon package ${pc3.bold(binding?.label ?? "Icons")} (${plural(iconPackage.manifest.iconNames.length, "icon")}, ${formatBytes3(totalBytes)}, ${iconPackage.contentHash.slice(0, 12)}).`
8844
+ `${action} icon package ${pc6.bold(binding?.label ?? "Icons")} (${plural(iconPackage.manifest.iconNames.length, "icon")}, ${formatBytes3(totalBytes)}, ${iconPackage.contentHash.slice(0, 12)}).`
7396
8845
  );
7397
8846
  hostedSpriteByPackageHash.set(iconPackage.contentHash, uploaded.value.spriteUrl);
7398
8847
  }
@@ -7401,16 +8850,16 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7401
8850
  if (!sprite) throw new Error(`Missing hosted sprite URL for map ${binding.mapName}`);
7402
8851
  return sprite;
7403
8852
  });
7404
- const compiledHostedStyles = createTileflowStyles(deploymentProject, {
8853
+ const compiledHostedStyles = createTileflowStyles2(deploymentProject, {
7405
8854
  apiBaseUrl: api.apiUrl,
7406
8855
  mapAssets: hostedMapAssets
7407
8856
  });
7408
- const hostedLocalTilesets = await prepareTileflowLocalTilesets(
8857
+ const hostedLocalTilesets = await prepareTileflowLocalTilesets2(
7409
8858
  deploymentProject,
7410
8859
  compiledHostedStyles,
7411
8860
  { assetBaseUrl: api.apiUrl, baseDirectory, cwd: process.cwd() }
7412
8861
  );
7413
- const hostedFonts = await prepareTileflowStyleFonts(
8862
+ const hostedFonts = await prepareTileflowStyleFonts2(
7414
8863
  deploymentProject,
7415
8864
  hostedLocalTilesets.styles,
7416
8865
  {
@@ -7475,7 +8924,7 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7475
8924
  const styles = Object.fromEntries(
7476
8925
  Object.entries(hostedThemeFamilies).map(([mapName, family]) => [mapName, family.styles])
7477
8926
  );
7478
- const buildManifest = await createTileflowMapBuildManifest(
8927
+ const buildManifest = await createTileflowMapBuildManifest2(
7479
8928
  Object.fromEntries(
7480
8929
  mapNames.map((mapName) => {
7481
8930
  const map = deploymentProject.maps[mapName];
@@ -7498,14 +8947,15 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7498
8947
  map,
7499
8948
  sourceAssets: {
7500
8949
  fonts: hostedFonts.sourceIdentities[mapName] ?? [],
7501
- icons: compiledIcons.sourceIdentities[mapName] ?? []
8950
+ icons: compiledIcons.sourceIdentities[mapName] ?? [],
8951
+ ...compiledIcons.compositions[mapName] ? { iconComposition: compiledIcons.compositions[mapName] } : {}
7502
8952
  },
7503
8953
  styles: styles[mapName]
7504
8954
  }
7505
8955
  ];
7506
8956
  })
7507
8957
  ),
7508
- { provenance: await createTileflowBuildProvenance(process.cwd()) }
8958
+ { provenance: await createTileflowBuildProvenance2(process.cwd()) }
7509
8959
  );
7510
8960
  const deployments = mapNames.map((mapName) => {
7511
8961
  const iconBinding = bindingsByMap.get(mapName);
@@ -7513,6 +8963,7 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7513
8963
  const fontBundle = hostedFonts.bundles[mapName];
7514
8964
  return {
7515
8965
  iconBinding,
8966
+ iconComposition: compiledIcons.compositions[mapName],
7516
8967
  iconPackage,
7517
8968
  fontBundle,
7518
8969
  mapName,
@@ -7531,14 +8982,20 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7531
8982
  buildManifest: buildManifest2,
7532
8983
  fontBundle,
7533
8984
  iconBinding,
8985
+ iconComposition,
7534
8986
  iconPackage,
7535
8987
  mapName,
7536
8988
  teamSources,
7537
8989
  styles: themeStyles
7538
8990
  } = deployment;
8991
+ if (iconComposition && iconComposition.packageHash !== iconPackage?.contentHash) {
8992
+ logError(`Icon composition does not match the effective package for ${mapName}.`);
8993
+ process.exitCode = 1;
8994
+ return;
8995
+ }
7539
8996
  const themeNames = Object.keys(themeStyles).sort();
7540
8997
  logInfo(
7541
- `Deploying compiled map ${pc3.bold(mapName)} (${plural(themeNames.length, "theme")}).`
8998
+ `Deploying compiled map ${pc6.bold(mapName)} (${plural(themeNames.length, "theme")}).`
7542
8999
  );
7543
9000
  const response = await publishHostedStyle(
7544
9001
  api,
@@ -7560,6 +9017,7 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7560
9017
  label: iconBinding.label
7561
9018
  }
7562
9019
  } : {},
9020
+ ...iconComposition ? { iconComposition } : {},
7563
9021
  ...fontBundle ? { fontBundle: { contentHash: fontBundle.contentHash } } : {},
7564
9022
  source
7565
9023
  },
@@ -7588,7 +9046,7 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7588
9046
  process.exitCode = 1;
7589
9047
  return;
7590
9048
  }
7591
- const resolvedMap = parseResolvedTileflowMap3(deploymentProject.maps[mapName]);
9049
+ const resolvedMap = parseResolvedTileflowMap5(deploymentProject.maps[mapName]);
7592
9050
  const mapBuild = buildManifest2.maps[mapName];
7593
9051
  deployedMaps[mapName] = {
7594
9052
  defaultTheme: resolvedMap.defaultTheme,
@@ -7598,7 +9056,7 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7598
9056
  themes: Object.fromEntries(
7599
9057
  themeNames.map((themeName) => {
7600
9058
  const deployedTheme = body.themes[themeName];
7601
- const fontFaces = getTileflowStyleFontFaces(themeStyles[themeName]);
9059
+ const fontFaces = getTileflowStyleFontFaces2(themeStyles[themeName]);
7602
9060
  return [
7603
9061
  themeName,
7604
9062
  {
@@ -7617,9 +9075,9 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
7617
9075
  };
7618
9076
  const versionLabel = Number.isInteger(body.version) ? ` (v${body.version})` : "";
7619
9077
  if (body.changed === false) {
7620
- logSuccess(`Unchanged ${pc3.bold(mapName)}${versionLabel}.`);
9078
+ logSuccess(`Unchanged ${pc6.bold(mapName)}${versionLabel}.`);
7621
9079
  } else {
7622
- logSuccess(`Published ${pc3.bold(mapName)}${versionLabel}.`);
9080
+ logSuccess(`Published ${pc6.bold(mapName)}${versionLabel}.`);
7623
9081
  }
7624
9082
  }
7625
9083
  const manifestPath = await writeDeployManifest(options.manifest, {
@@ -7693,6 +9151,7 @@ program.command("status").description("Show deployed compiled styles").option("-
7693
9151
  printMapStatus(status, api.apiUrl);
7694
9152
  });
7695
9153
  var iconsCommand = program.command("icons").description("Inspect managed Tileflow icons");
9154
+ registerIconLockCommands(program, { defaultApiUrl, defaultConfigPath, loadAuthConfig });
7696
9155
  registerIconListCommand(iconsCommand, { defaultConfigPath });
7697
9156
  registerIconDiffCommand(iconsCommand, {
7698
9157
  defaultApiUrl,
@@ -7734,10 +9193,15 @@ registerProjectCommands(program, {
7734
9193
  defaultApiUrl,
7735
9194
  loadAuthConfig
7736
9195
  });
7737
- program.parseAsync().catch((error) => {
7738
- printCliError(error);
7739
- process.exitCode = 1;
7740
- });
9196
+ if (isRootCliVersionRequest(process.argv.slice(2))) {
9197
+ process.stdout.write(`${packageJson.version}
9198
+ `);
9199
+ } else {
9200
+ program.parseAsync().catch((error) => {
9201
+ printCliError(error);
9202
+ process.exitCode = 1;
9203
+ });
9204
+ }
7741
9205
  async function runTileflowComparisonPreview(options, address) {
7742
9206
  await withTileflowConfigSecretsHidden(async () => {
7743
9207
  const origin = tileflowDevOrigin(address.host, address.port);
@@ -7782,10 +9246,12 @@ async function runTileflowComparisonPreview(options, address) {
7782
9246
  try {
7783
9247
  const leftCaptureConfig = comparisonCaptureConfigArgument(options.config);
7784
9248
  const rightCaptureConfig = comparisonCaptureConfigArgument(rightConfig);
9249
+ const comparisonIcons = resolveIconOptions(options);
7785
9250
  leftSession = await createTileflowArtifactSession3({
7786
9251
  apiBaseUrl: options.apiBaseUrl,
7787
9252
  assetBaseUrl: `${origin}${leftBasePath}`,
7788
9253
  config: options.config,
9254
+ ...comparisonIcons ? { icons: comparisonIcons } : {},
7789
9255
  inspection: true,
7790
9256
  styleBaseUrl: `${origin}${leftBasePath}`,
7791
9257
  watch: true
@@ -7794,6 +9260,7 @@ async function runTileflowComparisonPreview(options, address) {
7794
9260
  apiBaseUrl: options.apiBaseUrl,
7795
9261
  assetBaseUrl: `${origin}${rightBasePath}`,
7796
9262
  config: rightConfig,
9263
+ ...comparisonIcons ? { icons: comparisonIcons } : {},
7797
9264
  inspection: true,
7798
9265
  styleBaseUrl: `${origin}${rightBasePath}`,
7799
9266
  watch: true
@@ -7838,7 +9305,7 @@ async function runTileflowComparisonPreview(options, address) {
7838
9305
  scene: rightScene,
7839
9306
  theme: rightTheme
7840
9307
  });
7841
- const leftHandler = createTileflowDevRequestHandler({
9308
+ const leftHandler = createTileflowDevRequestHandler2({
7842
9309
  apiBaseUrl: options.apiBaseUrl,
7843
9310
  basePath: leftBasePath,
7844
9311
  config: options.config,
@@ -7848,7 +9315,7 @@ async function runTileflowComparisonPreview(options, address) {
7848
9315
  session: leftSession,
7849
9316
  theme: options.theme
7850
9317
  });
7851
- const rightHandler = createTileflowDevRequestHandler({
9318
+ const rightHandler = createTileflowDevRequestHandler2({
7852
9319
  apiBaseUrl: options.apiBaseUrl,
7853
9320
  basePath: rightBasePath,
7854
9321
  config: rightConfig,
@@ -7919,13 +9386,13 @@ async function runTileflowComparisonPreview(options, address) {
7919
9386
  }
7920
9387
  function comparisonCaptureConfigArgument(configPath) {
7921
9388
  const cwd = realpathSync(process.cwd());
7922
- const requestedConfigPath = resolve7(process.cwd(), configPath);
7923
- const defaultRequestedPath = resolve7(process.cwd(), defaultConfigPath);
9389
+ const requestedConfigPath = resolve8(process.cwd(), configPath);
9390
+ const defaultRequestedPath = resolve8(process.cwd(), defaultConfigPath);
7924
9391
  const absoluteConfigPath = existsSync(requestedConfigPath) ? realpathSync(requestedConfigPath) : requestedConfigPath;
7925
9392
  const absoluteDefaultPath = existsSync(defaultRequestedPath) ? realpathSync(defaultRequestedPath) : defaultRequestedPath;
7926
9393
  if (absoluteConfigPath === absoluteDefaultPath) return void 0;
7927
9394
  const relativeConfigPath = relative5(cwd, absoluteConfigPath);
7928
- if (relativeConfigPath === "" || relativeConfigPath === ".." || relativeConfigPath.startsWith(`..${sep5}`) || isAbsolute4(relativeConfigPath)) {
9395
+ if (relativeConfigPath === "" || relativeConfigPath === ".." || relativeConfigPath.startsWith(`..${sep5}`) || isAbsolute5(relativeConfigPath)) {
7929
9396
  throw new Error(
7930
9397
  "Comparison configs must be inside the current working directory so Copy command can remain reproducible."
7931
9398
  );
@@ -7964,7 +9431,7 @@ async function loginWithDeviceFlow(options) {
7964
9431
  codeChallenge,
7965
9432
  deviceName: hostname()
7966
9433
  });
7967
- printKeyValue("Code", pc3.bold(authorization.userCode));
9434
+ printKeyValue("Code", pc6.bold(authorization.userCode));
7968
9435
  printKeyValue("URL", link(authorization.verificationUriComplete));
7969
9436
  if (options.noBrowser) {
7970
9437
  logMuted("Open the URL above to continue.");
@@ -7974,7 +9441,7 @@ async function loginWithDeviceFlow(options) {
7974
9441
  output: process.stdout
7975
9442
  });
7976
9443
  try {
7977
- await readline.question(`${pc3.cyan("?")} Press Enter to open Tileflow Dashboard...`);
9444
+ await readline.question(`${pc6.cyan("?")} Press Enter to open Tileflow Dashboard...`);
7978
9445
  openBrowser(authorization.verificationUriComplete);
7979
9446
  } finally {
7980
9447
  readline.close();
@@ -8025,9 +9492,9 @@ function openBrowser(url, errorsToStderr = false) {
8025
9492
  });
8026
9493
  }
8027
9494
  async function writeDeployManifest(manifestPath, manifest) {
8028
- const outputPath = resolve7(process.cwd(), manifestPath);
9495
+ const outputPath = resolve8(process.cwd(), manifestPath);
8029
9496
  await writeAtomicFile({
8030
- boundaryPath: dirname10(outputPath),
9497
+ boundaryPath: dirname12(outputPath),
8031
9498
  force: true,
8032
9499
  label: "Deploy manifest",
8033
9500
  managed: true,
@@ -8037,11 +9504,24 @@ async function writeDeployManifest(manifestPath, manifest) {
8037
9504
  });
8038
9505
  return manifestPath;
8039
9506
  }
9507
+ function resolveIconOptions(options) {
9508
+ const icons = {
9509
+ ...options.cacheDir ? { cacheRoot: options.cacheDir } : {},
9510
+ ...options.offline ? { offline: true } : {}
9511
+ };
9512
+ return Object.keys(icons).length > 0 ? icons : void 0;
9513
+ }
8040
9514
  function createCompiledMapAssets(compiled, resolveSprite) {
8041
9515
  return Object.fromEntries(
8042
9516
  compiled.bindings.map((binding) => [
8043
9517
  binding.mapName,
8044
- { icons: { ids: binding.iconIds, sprite: resolveSprite(binding) } }
9518
+ {
9519
+ icons: {
9520
+ ids: binding.iconIds,
9521
+ sprite: resolveSprite(binding),
9522
+ ...binding.appearances ? { appearances: binding.appearances } : {}
9523
+ }
9524
+ }
8045
9525
  ])
8046
9526
  );
8047
9527
  }
@@ -8098,7 +9578,7 @@ function validateDeployManifestMapNames(mapNames) {
8098
9578
  async function loadExistingDeployManifest(manifestPath, options) {
8099
9579
  let source;
8100
9580
  try {
8101
- source = await readFile7(resolve7(process.cwd(), manifestPath), "utf8");
9581
+ source = await readFile7(resolve8(process.cwd(), manifestPath), "utf8");
8102
9582
  } catch (error) {
8103
9583
  if (error.code === "ENOENT") return null;
8104
9584
  throw new Error("Existing Tileflow manifest could not be read.");
@@ -8112,7 +9592,7 @@ async function loadExistingDeployManifest(manifestPath, options) {
8112
9592
  }
8113
9593
  let manifest;
8114
9594
  try {
8115
- manifest = parseTileflowRuntimeManifest(value);
9595
+ manifest = parseTileflowRuntimeManifest2(value);
8116
9596
  } catch {
8117
9597
  throw new Error(
8118
9598
  "Existing Tileflow manifest does not match the strict theme manifest contract."
@@ -8190,7 +9670,7 @@ async function requireApiOptions(options, behavior = {}) {
8190
9670
  if (!behavior.silent) {
8191
9671
  logError("CI requires an explicit Tileflow credential with access to the target Map.");
8192
9672
  printNextSteps([
8193
- `Set ${pc3.cyan("TILEFLOW_API_KEY")} from the CI secret store.`,
9673
+ `Set ${pc6.cyan("TILEFLOW_API_KEY")} from the CI secret store.`,
8194
9674
  "The saved personal account session is never used in CI."
8195
9675
  ]);
8196
9676
  }
@@ -8270,7 +9750,7 @@ function printTileflowPreviewError(error) {
8270
9750
  printValidationErrors(error.issues);
8271
9751
  return;
8272
9752
  }
8273
- if (error instanceof TileflowValidationError4) {
9753
+ if (error instanceof TileflowValidationError5) {
8274
9754
  printValidationErrors(error.messages);
8275
9755
  return;
8276
9756
  }
@@ -8282,7 +9762,7 @@ function printCliError(error) {
8282
9762
  printValidationErrors(error.issues);
8283
9763
  return;
8284
9764
  }
8285
- if (error instanceof TileflowValidationError4) {
9765
+ if (error instanceof TileflowValidationError5) {
8286
9766
  printValidationErrors(error.messages);
8287
9767
  return;
8288
9768
  }
@@ -8296,17 +9776,17 @@ function printCliError(error) {
8296
9776
  }
8297
9777
  const message = error instanceof Error ? error.message : "Command failed.";
8298
9778
  if (message.includes("Cannot find module '@tileflow/core'")) {
8299
- logError(`Cannot load ${pc3.cyan("@tileflow/core")} from this project.`);
9779
+ logError(`Cannot load ${pc6.cyan("@tileflow/core")} from this project.`);
8300
9780
  printNextSteps([
8301
- `Install ${pc3.cyan("@tileflow/core")} in this project.`,
9781
+ `Install ${pc6.cyan("@tileflow/core")} in this project.`,
8302
9782
  `Run the command again from the project root.`
8303
9783
  ]);
8304
9784
  return;
8305
9785
  }
8306
9786
  if (message.includes("Cannot find module '@tileflow/maps'")) {
8307
- logError(`Cannot load ${pc3.cyan("@tileflow/maps")} from this project.`);
9787
+ logError(`Cannot load ${pc6.cyan("@tileflow/maps")} from this project.`);
8308
9788
  printNextSteps([
8309
- `Install ${pc3.cyan("@tileflow/maps")} in this project.`,
9789
+ `Install ${pc6.cyan("@tileflow/maps")} in this project.`,
8310
9790
  `Run the command again from the project root.`
8311
9791
  ]);
8312
9792
  return;
@@ -8317,51 +9797,51 @@ function printCliError(error) {
8317
9797
  }
8318
9798
  }
8319
9799
  function logSuccess(message) {
8320
- console.log(`${pc3.green("\u2713")} ${message}`);
9800
+ console.log(`${pc6.green("\u2713")} ${message}`);
8321
9801
  }
8322
9802
  function logError(message) {
8323
- console.log(`${pc3.red("\u2715")} ${message}`);
9803
+ console.log(`${pc6.red("\u2715")} ${message}`);
8324
9804
  }
8325
9805
  function logWarning(message) {
8326
- console.log(`${pc3.yellow("!")} ${message}`);
9806
+ console.log(`${pc6.yellow("!")} ${message}`);
8327
9807
  }
8328
9808
  function logInfo(message) {
8329
- console.log(`${pc3.cyan("i")} ${message}`);
9809
+ console.log(`${pc6.cyan("i")} ${message}`);
8330
9810
  }
8331
9811
  function logMuted(message) {
8332
- console.log(pc3.gray(message));
9812
+ console.log(pc6.gray(message));
8333
9813
  }
8334
9814
  function printTitle(title, subtitle) {
8335
- console.log(pc3.bold(title));
9815
+ console.log(pc6.bold(title));
8336
9816
  if (subtitle) {
8337
- console.log(pc3.gray(subtitle));
9817
+ console.log(pc6.gray(subtitle));
8338
9818
  }
8339
9819
  }
8340
9820
  function printKeyValue(label, value) {
8341
- console.log(`${pc3.gray(`${label}:`.padEnd(13))} ${value}`);
9821
+ console.log(`${pc6.gray(`${label}:`.padEnd(13))} ${value}`);
8342
9822
  }
8343
9823
  function printChecks(items) {
8344
9824
  for (const item of items) {
8345
- console.log(` ${pc3.green("\u2713")} ${item}`);
9825
+ console.log(` ${pc6.green("\u2713")} ${item}`);
8346
9826
  }
8347
9827
  }
8348
9828
  function printNextSteps(steps) {
8349
9829
  if (steps.length === 0) return;
8350
9830
  console.log(`
8351
- ${pc3.bold("Next steps")}`);
9831
+ ${pc6.bold("Next steps")}`);
8352
9832
  for (const step of steps) {
8353
- console.log(` ${pc3.cyan("\u2022")} ${step}`);
9833
+ console.log(` ${pc6.cyan("\u2022")} ${step}`);
8354
9834
  }
8355
9835
  }
8356
9836
  function printValidationErrors(messages) {
8357
9837
  logError("Tileflow config has errors.");
8358
9838
  for (const message of messages) {
8359
- const path = message.path ? pc3.cyan(message.path) : pc3.cyan("(root)");
8360
- console.log(` ${pc3.red("\u2715")} ${path} ${pc3.gray("-")} ${message.message}`);
9839
+ const path = message.path ? pc6.cyan(message.path) : pc6.cyan("(root)");
9840
+ console.log(` ${pc6.red("\u2715")} ${path} ${pc6.gray("-")} ${message.message}`);
8361
9841
  }
8362
9842
  }
8363
9843
  function printAccountDetails(session) {
8364
- printKeyValue("Account", pc3.bold(session.account.name));
9844
+ printKeyValue("Account", pc6.bold(session.account.name));
8365
9845
  printKeyValue("Email", session.account.email);
8366
9846
  printKeyValue("API", link(session.apiOrigin));
8367
9847
  printKeyValue("Expires", formatDate(session.expiresAt));
@@ -8390,11 +9870,11 @@ function printDeployedMaps(maps) {
8390
9870
  const entries = Object.entries(maps);
8391
9871
  if (entries.length === 0) return;
8392
9872
  console.log(`
8393
- ${pc3.bold("Maps")}`);
9873
+ ${pc6.bold("Maps")}`);
8394
9874
  for (const [name, map] of entries) {
8395
9875
  for (const [themeName, theme] of Object.entries(map.themes)) {
8396
9876
  const label = `${name}/${themeName}`;
8397
- console.log(` ${pc3.green("\u2713")} ${label.padEnd(24)} ${link(theme.styleUrl)}`);
9877
+ console.log(` ${pc6.green("\u2713")} ${label.padEnd(24)} ${link(theme.styleUrl)}`);
8398
9878
  }
8399
9879
  }
8400
9880
  }
@@ -8412,7 +9892,7 @@ function plural(count, singular, pluralForm = `${singular}s`) {
8412
9892
  return `${count} ${count === 1 ? singular : pluralForm}`;
8413
9893
  }
8414
9894
  function command(value) {
8415
- return pc3.cyan(value);
9895
+ return pc6.cyan(value);
8416
9896
  }
8417
9897
  function cliInvocation(parts) {
8418
9898
  return parts.map(quoteCliArgument).join(" ");
@@ -8421,10 +9901,10 @@ function quoteCliArgument(value) {
8421
9901
  return /^[A-Za-z0-9_./:@%+=,-]+$/u.test(value) ? value : `'${value.replaceAll("'", `'"'"'`)}'`;
8422
9902
  }
8423
9903
  function link(value) {
8424
- return pc3.cyan(value);
9904
+ return pc6.cyan(value);
8425
9905
  }
8426
9906
  function pathLabel(value) {
8427
- return pc3.cyan(value);
9907
+ return pc6.cyan(value);
8428
9908
  }
8429
9909
  function starterConfig() {
8430
9910
  return `import { defineMap, defineTheme, labels, poi } from "@tileflow/core";
@@ -8470,15 +9950,15 @@ function printHostedCompatibilityIssues(issues) {
8470
9950
  }
8471
9951
  function printMapStatus(status, apiUrl) {
8472
9952
  printTitle("Tileflow status");
8473
- printKeyValue("Map", pc3.bold(status.mapId));
9953
+ printKeyValue("Map", pc6.bold(status.mapId));
8474
9954
  console.log(`
8475
- ${pc3.bold("Styles")}`);
9955
+ ${pc6.bold("Styles")}`);
8476
9956
  if (status.styles.length === 0) {
8477
9957
  logMuted(" No styles deployed.");
8478
9958
  }
8479
9959
  for (const style of status.styles) {
8480
9960
  console.log(
8481
- ` ${pc3.green("\u2713")} ${style.environment.padEnd(16)} ${link(`${apiUrl}/maps/${style.mapId}/style.json`).padEnd(36)} ${formatBytes3(style.size).padStart(10)} ${pc3.gray(formatDate(style.uploaded))}`
9961
+ ` ${pc6.green("\u2713")} ${style.environment.padEnd(16)} ${link(`${apiUrl}/maps/${style.mapId}/style.json`).padEnd(36)} ${formatBytes3(style.size).padStart(10)} ${pc6.gray(formatDate(style.uploaded))}`
8482
9962
  );
8483
9963
  }
8484
9964
  }