lecodes-cli 2.4.9 → 2.4.10

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
@@ -64,7 +64,7 @@ var package_default;
64
64
  var init_package = __esm(() => {
65
65
  package_default = {
66
66
  name: "lecodes-cli",
67
- version: "2.4.9",
67
+ version: "2.4.10",
68
68
  dependencies: {
69
69
  "@letary/chisel": "^0.15.0",
70
70
  jimp: "^1.6.1",
@@ -89,9 +89,9 @@ var init_package = __esm(() => {
89
89
  },
90
90
  peerDependencies: {
91
91
  "lecodes-design": "^0.11.0",
92
- "lecodes-headless": "^2.4.9",
92
+ "lecodes-headless": "^2.4.10",
93
93
  "lecodes-3d-editor": "^0.9.0",
94
- "lecodes-assets": "^0.2.1",
94
+ "lecodes-assets": "^2.4.10",
95
95
  "heic-decode": "^2.1.0",
96
96
  "marcidb-embedded": "^0.15.0",
97
97
  sharp: "^0.35.2",
@@ -159,7 +159,7 @@ var init_package = __esm(() => {
159
159
  });
160
160
 
161
161
  // ../../sdk/src/version.ts
162
- var SDK_VERSION = "2.4.9";
162
+ var SDK_VERSION = "2.4.10";
163
163
 
164
164
  // src/cli/args.ts
165
165
  var isNegativeNumber = (s) => /^-(\d|\.\d)/.test(s), isValue = (s) => !s.startsWith("-") || isNegativeNumber(s), parseArgv = (argv, booleans = new Set) => {
@@ -651,6 +651,94 @@ var init_peers = __esm(() => {
651
651
  ];
652
652
  });
653
653
 
654
+ // src/hosts/releases.ts
655
+ import { createHash } from "node:crypto";
656
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, mkdtempSync, readFileSync as readFileSync3, renameSync, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
657
+ import { join as join4 } from "node:path";
658
+ import { spawnSync } from "node:child_process";
659
+ var MANIFEST_URL = "https://storage.yandexcloud.net/le-codes/releases/versions.json", ARTIFACT_STAMP = ".lecodes-artifact.json", cachedArtifactSha = (dir) => {
660
+ try {
661
+ return JSON.parse(readFileSync3(join4(dir, ARTIFACT_STAMP), "utf8")).sha256 ?? null;
662
+ } catch {
663
+ return null;
664
+ }
665
+ }, macPlatformKey = () => process.arch === "arm64" ? "macos-arm64" : "macos-x64", semverNewer = (a, b) => {
666
+ const pa = a.split(/[.-]/).map(Number), pb = b.split(/[.-]/).map(Number);
667
+ for (let i = 0;i < 3; i++)
668
+ if ((pb[i] ?? 0) !== (pa[i] ?? 0))
669
+ return (pb[i] ?? 0) - (pa[i] ?? 0);
670
+ return 0;
671
+ }, newestForPlatform = (versions, key) => {
672
+ const carrying = versions.filter((v) => v.files[key]).sort((a, b) => semverNewer(a.version, b.version));
673
+ const newest = carrying[0];
674
+ return newest ? { version: newest.version, file: newest.files[key] } : null;
675
+ }, fetchReleaseProduct = async (product, label) => {
676
+ let manifest;
677
+ try {
678
+ const res = await fetch(MANIFEST_URL);
679
+ if (!res.ok)
680
+ throw new Error(`HTTP ${res.status}`);
681
+ manifest = await res.json();
682
+ } catch (e) {
683
+ throw new CliError(`Can't fetch the release manifest (${MANIFEST_URL}): ${e instanceof Error ? e.message : String(e)}`);
684
+ }
685
+ const entry = manifest.products?.[product];
686
+ if (!entry)
687
+ throw new CliError(`The release manifest has no ${label} product.`);
688
+ return entry;
689
+ }, downloadReleaseArchive = async (file, opts) => {
690
+ let bytes;
691
+ try {
692
+ const res = await fetch(file.url);
693
+ if (!res.ok)
694
+ throw new Error(`HTTP ${res.status}`);
695
+ bytes = Buffer.from(await res.arrayBuffer());
696
+ } catch (e) {
697
+ throw new CliError(`Download failed (${file.url}): ${e instanceof Error ? e.message : String(e)}`);
698
+ }
699
+ const sha = createHash("sha256").update(bytes).digest("hex");
700
+ if (sha !== file.sha256)
701
+ throw new CliError(`Downloaded archive is corrupt (sha256 mismatch) — try again.`);
702
+ mkdirSync2(opts.cacheRoot, { recursive: true });
703
+ const stage = mkdtempSync(join4(opts.cacheRoot, "dl-"));
704
+ try {
705
+ const archiveName = file.url.split("/").pop() ?? "archive";
706
+ const archive = join4(stage, archiveName);
707
+ writeFileSync2(archive, bytes);
708
+ if (opts.extract) {
709
+ opts.extract(archive, stage);
710
+ } else {
711
+ const winTar = join4(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe");
712
+ const tarExe = process.platform === "win32" && existsSync4(winTar) ? winTar : "tar";
713
+ const tar = spawnSync(tarExe, ["-xf", archiveName], { cwd: stage, stdio: ["ignore", "ignore", "pipe"] });
714
+ if (tar.status !== 0)
715
+ throw new CliError(`Couldn't extract the archive: ${tar.stderr?.toString() ?? "tar failed"}`);
716
+ }
717
+ rmSync(archive);
718
+ const bad = opts.validate(stage);
719
+ if (bad)
720
+ throw new CliError(bad);
721
+ writeFileSync2(join4(stage, ARTIFACT_STAMP), JSON.stringify({ sha256: file.sha256, url: file.url }, null, 2));
722
+ try {
723
+ renameSync(stage, opts.dest);
724
+ } catch {
725
+ let destBad;
726
+ try {
727
+ destBad = opts.validate(opts.dest);
728
+ } catch {
729
+ destBad = "missing";
730
+ }
731
+ if (destBad)
732
+ throw new CliError(`Couldn't move the download into ${opts.dest}.`);
733
+ }
734
+ } finally {
735
+ rmSync(stage, { recursive: true, force: true });
736
+ }
737
+ };
738
+ var init_releases = __esm(() => {
739
+ init_cli();
740
+ });
741
+
654
742
  // src/hosts/archives.ts
655
743
  var ARCHIVED_PEERS, BASE = "https://storage.yandexcloud.net/le-codes/releases/packages", packagesBase = () => (process.env.LECODES_PACKAGES_URL || BASE).replace(/\/$/, ""), archiveUrl = (name, version) => `${packagesBase()}/${name}/${name}-${version}.tgz`, numbers = (version) => version.split(/[.-]/).slice(0, 3).map(Number), byVersion = (a, b) => {
656
744
  const pa = numbers(a), pb = numbers(b);
@@ -699,17 +787,17 @@ var init_archives = __esm(() => {
699
787
  });
700
788
 
701
789
  // src/hosts/packageManager.ts
702
- import { spawnSync } from "node:child_process";
790
+ import { spawnSync as spawnSync2 } from "node:child_process";
703
791
  import { homedir as homedir2 } from "node:os";
704
- import { join as join4, resolve as resolve2, sep } from "node:path";
792
+ import { join as join5, resolve as resolve2, sep } from "node:path";
705
793
  import { fileURLToPath as fileURLToPath2 } from "node:url";
706
794
  var onPath = (cmd) => {
707
795
  try {
708
- return spawnSync(cmd, ["--version"], { stdio: "ignore", shell: process.platform === "win32" }).status === 0;
796
+ return spawnSync2(cmd, ["--version"], { stdio: "ignore", shell: process.platform === "win32" }).status === 0;
709
797
  } catch {
710
798
  return false;
711
799
  }
712
- }, bunGlobalDir = () => join4(process.env.BUN_INSTALL ? resolve2(process.env.BUN_INSTALL) : join4(homedir2(), ".bun"), "install", "global"), ownManager = (file = fileURLToPath2(import.meta.url)) => {
800
+ }, bunGlobalDir = () => join5(process.env.BUN_INSTALL ? resolve2(process.env.BUN_INSTALL) : join5(homedir2(), ".bun"), "install", "global"), ownManager = (file = fileURLToPath2(import.meta.url)) => {
713
801
  const norm = (p) => process.platform === "win32" ? p.toLowerCase() : p;
714
802
  return norm(resolve2(file)).startsWith(norm(bunGlobalDir()) + sep) ? "bun" : "npm";
715
803
  }, pickManager = () => {
@@ -720,12 +808,16 @@ var onPath = (cmd) => {
720
808
  var init_packageManager = () => {};
721
809
 
722
810
  // src/hosts/peerInstall.ts
723
- import { spawnSync as spawnSync2 } from "node:child_process";
724
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
725
- import { join as join5 } from "node:path";
811
+ import { spawnSync as spawnSync3 } from "node:child_process";
812
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
813
+ import { join as join6 } from "node:path";
726
814
  import { pathToFileURL } from "node:url";
727
815
  async function loadPeer(name, subpath, opts) {
728
816
  const spec = subpath ? `${name}/${subpath}` : name;
817
+ const byHand = peerVersionGlobal(name, spec);
818
+ if (olderThanCli(name, byHand)) {
819
+ throw new CliError(`${name} ${byHand} (installed by hand) is older than this CLI (${package_default.version}) needs — update it: ${commandLine(ownManager(), installGlobalArgv(ownManager(), [await peerSpec(name)]))}`);
820
+ }
729
821
  try {
730
822
  return await import(spec);
731
823
  } catch (e) {
@@ -733,18 +825,22 @@ async function loadPeer(name, subpath, opts) {
733
825
  throw e;
734
826
  }
735
827
  const dir = peerDirInPrefix(name);
736
- if (!existsSync4(join5(dir, "package.json"))) {
828
+ const installed = peerVersionPrefix(name);
829
+ const stale = olderThanCli(name, installed);
830
+ if (installed === null || stale) {
737
831
  if (opts.install === false)
738
832
  return null;
739
- if (peerVersionGlobal(name, spec) !== null)
833
+ if (installed === null && peerVersionGlobal(name, spec) !== null)
740
834
  throw new CliError(`${name} is installed but "${spec}" can't be loaded from it — reinstall it: ${commandLine(ownManager(), installGlobalArgv(ownManager(), [await peerSpec(name)]))}`);
835
+ if (stale)
836
+ note(`${name} ${installed} in ${peersDir()} is older than this CLI (${package_default.version}) — installing the one it needs`);
741
837
  await installPeers(companions(name), opts.for);
742
838
  }
743
839
  return await import(pathToFileURL(resolveEntry(dir, subpath)).href);
744
840
  }
745
841
  var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson = (path) => {
746
842
  try {
747
- return JSON.parse(readFileSync3(path, "utf8"));
843
+ return JSON.parse(readFileSync4(path, "utf8"));
748
844
  } catch {
749
845
  return null;
750
846
  }
@@ -753,7 +849,7 @@ var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson = (path) => {
753
849
  if (archive)
754
850
  return archive;
755
851
  if (name === "@napi-rs/canvas") {
756
- const renderer = readJson(join5(peerDirInPrefix("lecodes-headless"), "package.json"));
852
+ const renderer = readJson(join6(peerDirInPrefix("lecodes-headless"), "package.json"));
757
853
  const range = renderer?.peerDependencies?.["@napi-rs/canvas"] ?? renderer?.optionalDependencies?.["@napi-rs/canvas"] ?? CANVAS_RANGE_FALLBACK;
758
854
  return `@napi-rs/canvas@${range}`;
759
855
  }
@@ -761,10 +857,10 @@ var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson = (path) => {
761
857
  return range ? `${name}@${range}` : `${name}@latest`;
762
858
  }, companions = (name) => name === "lecodes-headless" ? ["lecodes-headless", "@napi-rs/canvas"] : name === "sharp" ? ["sharp", "heic-decode"] : [name], manualCommand = (dir, specs) => commandLine(ownManager(), installIntoArgv(ownManager(), dir, specs)), installPeers = async (names, why, opts = {}) => {
763
859
  const dir = peersDir();
764
- mkdirSync2(dir, { recursive: true });
765
- const manifest = join5(dir, "package.json");
766
- if (!existsSync4(manifest)) {
767
- writeFileSync2(manifest, JSON.stringify({
860
+ mkdirSync3(dir, { recursive: true });
861
+ const manifest = join6(dir, "package.json");
862
+ if (!existsSync5(manifest)) {
863
+ writeFileSync3(manifest, JSON.stringify({
768
864
  name: "lecodes-peers",
769
865
  private: true,
770
866
  description: "Optional lecodes-cli peers, installed on first use. Safe to delete — the CLI re-installs what a command needs."
@@ -792,16 +888,16 @@ var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson = (path) => {
792
888
  const spec = await peerSpec(name);
793
889
  const argv = installIntoArgv(tool, dir, [spec]);
794
890
  note(`$ ${tool} ${argv.join(" ")}`);
795
- const r = spawnSync2(tool, argv, { stdio: "inherit", shell: process.platform === "win32", timeout: 10 * 60000 });
891
+ const r = spawnSync3(tool, argv, { stdio: "inherit", shell: process.platform === "win32", timeout: 10 * 60000 });
796
892
  if (r.error)
797
893
  throw new CliError(`${tool} could not run: ${r.error.message}`);
798
894
  if (r.status !== 0)
799
895
  throw new CliError(`${tool} exited with ${r.status ?? "a signal"} while installing ${spec}. By hand: ${manualCommand(dir, [spec])}`);
800
- if (!existsSync4(join5(peerDirInPrefix(name), "package.json")))
896
+ if (!existsSync5(join6(peerDirInPrefix(name), "package.json")))
801
897
  throw new CliError(`${name} did not land in ${dir} — the installer finished but the package is missing`);
802
898
  }
803
899
  }, resolveEntry = (pkgDir, subpath) => {
804
- const manifest = readJson(join5(pkgDir, "package.json"));
900
+ const manifest = readJson(join6(pkgDir, "package.json"));
805
901
  if (!manifest)
806
902
  throw new CliError(`${pkgDir} holds no package.json`);
807
903
  const key = subpath ? `./${subpath}` : ".";
@@ -829,8 +925,8 @@ var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson = (path) => {
829
925
  rel = typeof manifest.main === "string" ? manifest.main : "index.js";
830
926
  if (!rel)
831
927
  throw new CliError(`${manifest.name} has no export "${key}"`);
832
- return join5(pkgDir, rel);
833
- }, isNotFound = (e, spec) => {
928
+ return join6(pkgDir, rel);
929
+ }, SET_ON_NPM, olderThanCli = (name, version) => SET_ON_NPM.has(name) && version !== null && semverNewer(version, package_default.version) > 0, isNotFound = (e, spec) => {
834
930
  const code = e?.code;
835
931
  const msg = e instanceof Error ? e.message : String(e);
836
932
  return (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" || /Cannot find (module|package)/i.test(msg)) && msg.includes(spec.split("/")[0]);
@@ -838,9 +934,11 @@ var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson = (path) => {
838
934
  var init_peerInstall = __esm(() => {
839
935
  init_package();
840
936
  init_peers();
937
+ init_releases();
841
938
  init_archives();
842
939
  init_packageManager();
843
940
  init_cli();
941
+ SET_ON_NPM = new Set(["lecodes-assets"]);
844
942
  });
845
943
 
846
944
  // ../../sdk/src/compile/resourceSlots.ts
@@ -2041,18 +2139,18 @@ var init_chisel = __esm(() => {
2041
2139
  });
2042
2140
 
2043
2141
  // ../../sdk/src/compile/bundler.ts
2044
- import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync4, statSync } from "node:fs";
2045
- import { spawnSync as spawnSync3 } from "node:child_process";
2142
+ import { existsSync as existsSync6, readdirSync, readFileSync as readFileSync5, statSync } from "node:fs";
2143
+ import { spawnSync as spawnSync4 } from "node:child_process";
2046
2144
  import { createRequire as createRequire3 } from "node:module";
2047
- import { dirname as dirname4, join as join6, relative } from "node:path";
2145
+ import { dirname as dirname4, join as join7, relative } from "node:path";
2048
2146
  import { fileURLToPath as fileURLToPath3 } from "node:url";
2049
2147
  var resolveInject = () => {
2050
2148
  const override = process.env.LECODES_SDK_INJECT;
2051
- if (override && existsSync5(override))
2149
+ if (override && existsSync6(override))
2052
2150
  return override;
2053
2151
  const here = dirname4(fileURLToPath3(import.meta.url));
2054
- const src = join6(here, "..", "inject.ts");
2055
- if (existsSync5(src))
2152
+ const src = join7(here, "..", "inject.ts");
2153
+ if (existsSync6(src))
2056
2154
  return src;
2057
2155
  throw new Error("sdk: could not locate the inject source (LECODES_SDK_INJECT / src/inject.ts)");
2058
2156
  }, normalize2 = (path) => {
@@ -2068,7 +2166,7 @@ var resolveInject = () => {
2068
2166
  return "/" + parts.join("/");
2069
2167
  }, CHISEL_DEFINE, CHISEL_KEEP, CLIENT_INJECT = "/__sdk/inject.ts", SERVER_INJECT = "/__sdk/server/inject.ts", EDITOR_INJECT = "/__sdk/inject.editor.ts", readInjectSources = () => {
2070
2168
  const client = resolveInject();
2071
- return { client: readFileSync4(client, "utf8"), server: readFileSync4(join6(dirname4(client), "server", "inject.ts"), "utf8") };
2169
+ return { client: readFileSync5(client, "utf8"), server: readFileSync5(join7(dirname4(client), "server", "inject.ts"), "utf8") };
2072
2170
  }, resolveSdkSrc = () => {
2073
2171
  const inject = resolveInject();
2074
2172
  if (!inject.endsWith("inject.ts")) {
@@ -2082,11 +2180,11 @@ var resolveInject = () => {
2082
2180
  const out = {};
2083
2181
  const walk = (dir) => {
2084
2182
  for (const name of readdirSync(dir)) {
2085
- const p = join6(dir, name);
2183
+ const p = join7(dir, name);
2086
2184
  if (statSync(p).isDirectory())
2087
2185
  walk(p);
2088
2186
  else if (isSdkSource(p))
2089
- out["/__sdk/" + relative(sdkSrc, p).split("\\").join("/")] = readFileSync4(p, "utf8");
2187
+ out["/__sdk/" + relative(sdkSrc, p).split("\\").join("/")] = readFileSync5(p, "utf8");
2090
2188
  }
2091
2189
  };
2092
2190
  walk(sdkSrc);
@@ -2095,13 +2193,13 @@ var resolveInject = () => {
2095
2193
  }, resolveChiselBin = () => {
2096
2194
  const override = process.env.LECODES_CHISEL_BIN;
2097
2195
  if (override) {
2098
- if (existsSync5(override))
2196
+ if (existsSync6(override))
2099
2197
  return override;
2100
2198
  throw new Error(`LECODES_CHISEL_BIN points at a missing file: ${override}`);
2101
2199
  }
2102
2200
  try {
2103
2201
  const bin = createRequire3(import.meta.url)("@letary/chisel").binaryPath();
2104
- if (bin && existsSync5(bin))
2202
+ if (bin && existsSync6(bin))
2105
2203
  return bin;
2106
2204
  } catch {}
2107
2205
  throw new Error("chisel binary not found. Install @letary/chisel, or set LECODES_CHISEL_BIN.");
@@ -2113,7 +2211,7 @@ var resolveInject = () => {
2113
2211
  return null;
2114
2212
  }
2115
2213
  const input = JSON.stringify({ scan: true, files });
2116
- const r = spawnSync3(bin, [], { input, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
2214
+ const r = spawnSync4(bin, [], { input, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
2117
2215
  if (r.status !== 0 || r.error)
2118
2216
  return null;
2119
2217
  try {
@@ -2243,7 +2341,7 @@ var resolveInject = () => {
2243
2341
  vocab: chiselVocab
2244
2342
  });
2245
2343
  const bin = resolveChiselBin();
2246
- const r = spawnSync3(bin, [], { input, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
2344
+ const r = spawnSync4(bin, [], { input, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
2247
2345
  if (r.status === 2 || r.error) {
2248
2346
  throw new Error(`chisel failed: ${r.error?.message ?? r.stderr ?? "unknown error"}`);
2249
2347
  }
@@ -172350,15 +172448,15 @@ var init_compile = __esm(() => {
172350
172448
  });
172351
172449
 
172352
172450
  // src/compile/projectCompile.ts
172353
- import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
172451
+ import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
172354
172452
  import { createRequire as createRequire4 } from "node:module";
172355
- import { dirname as dirname6, join as join7 } from "node:path";
172453
+ import { dirname as dirname6, join as join8 } from "node:path";
172356
172454
  var resolveSdkInject = () => {
172357
172455
  try {
172358
172456
  return createRequire4(import.meta.url).resolve("lecodes-sdk/inject");
172359
172457
  } catch {}
172360
- const vendored = join7(distRoot, "runtime", "sdk", "inject.ts");
172361
- return existsSync6(vendored) ? vendored : null;
172458
+ const vendored = join8(distRoot, "runtime", "sdk", "inject.ts");
172459
+ return existsSync7(vendored) ? vendored : null;
172362
172460
  }, sdkInject, sdkSourceDir, resolveChiselBin2 = () => {
172363
172461
  if (process.env.LECODES_CHISEL_BIN)
172364
172462
  return process.env.LECODES_CHISEL_BIN;
@@ -172367,8 +172465,8 @@ var resolveSdkInject = () => {
172367
172465
  if (fromPkg)
172368
172466
  return fromPkg;
172369
172467
  } catch {}
172370
- const local = join7(dirname6(process.execPath), process.platform === "win32" ? "chisel.exe" : "chisel");
172371
- return existsSync6(local) ? local : null;
172468
+ const local = join8(dirname6(process.execPath), process.platform === "win32" ? "chisel.exe" : "chisel");
172469
+ return existsSync7(local) ? local : null;
172372
172470
  }, chiselBin, ambiguousEntryWarning = (entries) => {
172373
172471
  const { entry, roots } = detectEntry(entries, scanModulesChisel);
172374
172472
  if (!entry || roots.length < 2 || roots.includes(ENTRY_CONVENTION))
@@ -172381,7 +172479,7 @@ var resolveSdkInject = () => {
172381
172479
  if (e?.text !== undefined)
172382
172480
  return e.text;
172383
172481
  try {
172384
- return e?.absPath ? readFileSync5(e.absPath, "utf8") : null;
172482
+ return e?.absPath ? readFileSync6(e.absPath, "utf8") : null;
172385
172483
  } catch {
172386
172484
  return null;
172387
172485
  }
@@ -172417,15 +172515,15 @@ ${inlineSourceMapComment(map)}` : code, bakes };
172417
172515
  };
172418
172516
  const dir = peerDirInPrefix("typescript");
172419
172517
  const peer = () => {
172420
- if (!existsSync6(join7(dir, "package.json")))
172518
+ if (!existsSync7(join8(dir, "package.json")))
172421
172519
  return null;
172422
172520
  try {
172423
- return api(createRequire4(import.meta.url)(join7(dir, "lib", "typescript.js")));
172521
+ return api(createRequire4(import.meta.url)(join8(dir, "lib", "typescript.js")));
172424
172522
  } catch {
172425
172523
  return null;
172426
172524
  }
172427
172525
  };
172428
- const found = from(join7(root, "package.json")) ?? from(import.meta.url) ?? peer();
172526
+ const found = from(join8(root, "package.json")) ?? from(import.meta.url) ?? peer();
172429
172527
  if (found)
172430
172528
  return found;
172431
172529
  await installPeers(["typescript"], "The project's server code (*.server.ts)");
@@ -172610,8 +172708,8 @@ var init_assetName = __esm(() => {
172610
172708
  });
172611
172709
 
172612
172710
  // src/project/paths.ts
172613
- import { createHash } from "node:crypto";
172614
- var sha256 = (data) => createHash("sha256").update(data).digest("hex"), toKey = (path) => {
172711
+ import { createHash as createHash2 } from "node:crypto";
172712
+ var sha256 = (data) => createHash2("sha256").update(data).digest("hex"), toKey = (path) => {
172615
172713
  const parts = path.replace(/\\/g, "/").split("/").map((p) => p.trim()).filter((p) => p && p !== ".");
172616
172714
  if (parts.some((p) => p === ".."))
172617
172715
  throw new CliError(`Unsafe path: ${path}`);
@@ -172626,8 +172724,8 @@ var init_paths = __esm(() => {
172626
172724
  });
172627
172725
 
172628
172726
  // src/project/ignore.ts
172629
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "node:fs";
172630
- import { join as join10 } from "node:path";
172727
+ import { existsSync as existsSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "node:fs";
172728
+ import { join as join11 } from "node:path";
172631
172729
  var BUILTIN_PATTERNS, DEFAULT_IGNORE_FILE = `# Files matched here are NOT pushed to LeCodes.
172632
172730
  # Only untracked files are affected — assets already in the project stay tracked
172633
172731
  # even if they match a pattern. Built-in defaults (node_modules, _*, *.log, .artifacts)
@@ -172708,18 +172806,18 @@ design/comments/
172708
172806
  return (path) => match(path.startsWith("/") ? path : "/" + path, false);
172709
172807
  }, loadIgnore = (root) => {
172710
172808
  const lines = [...BUILTIN_PATTERNS];
172711
- const file = join10(root, ".lecodesignore");
172712
- if (existsSync9(file)) {
172809
+ const file = join11(root, ".lecodesignore");
172810
+ if (existsSync10(file)) {
172713
172811
  try {
172714
- lines.push(...readFileSync8(file, "utf8").split(/\r?\n/));
172812
+ lines.push(...readFileSync9(file, "utf8").split(/\r?\n/));
172715
172813
  } catch {}
172716
172814
  }
172717
172815
  const rules = lines.map(compile).filter((r) => r !== null);
172718
172816
  return buildMatcher(rules);
172719
172817
  }, writeDefaultIgnore = (root) => {
172720
- const file = join10(root, ".lecodesignore");
172721
- if (!existsSync9(file))
172722
- writeFileSync3(file, DEFAULT_IGNORE_FILE);
172818
+ const file = join11(root, ".lecodesignore");
172819
+ if (!existsSync10(file))
172820
+ writeFileSync4(file, DEFAULT_IGNORE_FILE);
172723
172821
  }, DEFAULT_GITIGNORE = `# LeCodes per-checkout state: generated IDE types + this machine's sync cursor
172724
172822
  # (.lecodes/manifest.json). Regenerate with \`lecodes update\` / re-attach with \`lecodes link\`.
172725
172823
  .lecodes/
@@ -172734,19 +172832,19 @@ node_modules/
172734
172832
 
172735
172833
  dist/
172736
172834
  `, ensureGitignore = (root) => {
172737
- const file = join10(root, ".gitignore");
172738
- if (!existsSync9(file)) {
172739
- writeFileSync3(file, DEFAULT_GITIGNORE);
172835
+ const file = join11(root, ".gitignore");
172836
+ if (!existsSync10(file)) {
172837
+ writeFileSync4(file, DEFAULT_GITIGNORE);
172740
172838
  return "created";
172741
172839
  }
172742
- const text = readFileSync8(file, "utf8");
172840
+ const text = readFileSync9(file, "utf8");
172743
172841
  const covered = text.split(/\r?\n/).some((l) => /^\/?\.lecodes\/?\s*$/.test(l.trim()));
172744
172842
  if (covered)
172745
172843
  return null;
172746
172844
  const sep = text.length === 0 || text.endsWith(`
172747
172845
  `) ? "" : `
172748
172846
  `;
172749
- writeFileSync3(file, `${text}${sep}
172847
+ writeFileSync4(file, `${text}${sep}
172750
172848
  # LeCodes per-checkout state (generated types + sync cursor) — not source
172751
172849
  .lecodes/
172752
172850
  `);
@@ -172757,8 +172855,8 @@ var init_ignore = __esm(() => {
172757
172855
  });
172758
172856
 
172759
172857
  // src/project/localFiles.ts
172760
- import { existsSync as existsSync10, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "node:fs";
172761
- import { basename, dirname as dirname7, join as join11, relative as relative3 } from "node:path";
172858
+ import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync10 } from "node:fs";
172859
+ import { basename, dirname as dirname7, join as join12, relative as relative3 } from "node:path";
172762
172860
  var SKIP_DIRS, SKIP_FILES, TEXT_EXTS, ext = (name) => {
172763
172861
  const i = name.lastIndexOf(".");
172764
172862
  return i < 0 ? "" : name.slice(i + 1).toLowerCase();
@@ -172776,14 +172874,14 @@ var SKIP_DIRS, SKIP_FILES, TEXT_EXTS, ext = (name) => {
172776
172874
  continue;
172777
172875
  let folder;
172778
172876
  try {
172779
- folder = JSON.parse(readFileSync9(file.absPath, "utf8").replace(/^\uFEFF/, "")).web?.static;
172877
+ folder = JSON.parse(readFileSync10(file.absPath, "utf8").replace(/^\uFEFF/, "")).web?.static;
172780
172878
  } catch {
172781
172879
  continue;
172782
172880
  }
172783
172881
  if (typeof folder !== "string" || folder.trim() === "")
172784
172882
  continue;
172785
172883
  try {
172786
- folders.push(toKey(relative3(root, join11(dirname7(file.absPath), folder))) + "/");
172884
+ folders.push(toKey(relative3(root, join12(dirname7(file.absPath), folder))) + "/");
172787
172885
  } catch {}
172788
172886
  }
172789
172887
  return folders;
@@ -172804,7 +172902,7 @@ var SKIP_DIRS, SKIP_FILES, TEXT_EXTS, ext = (name) => {
172804
172902
  };
172805
172903
  const walk = (dir, parentIgnored) => {
172806
172904
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
172807
- const abs = join11(dir, entry.name);
172905
+ const abs = join12(dir, entry.name);
172808
172906
  const key = toKey(relative3(root, abs));
172809
172907
  if (entry.isDirectory()) {
172810
172908
  if (SKIP_DIRS.has(entry.name))
@@ -172824,7 +172922,7 @@ var SKIP_DIRS, SKIP_FILES, TEXT_EXTS, ext = (name) => {
172824
172922
  ignored.push(key);
172825
172923
  continue;
172826
172924
  }
172827
- found.push({ path: key, absPath: abs, sha: sha256(readFileSync9(abs)) });
172925
+ found.push({ path: key, absPath: abs, sha: sha256(readFileSync10(abs)) });
172828
172926
  }
172829
172927
  }
172830
172928
  };
@@ -172836,8 +172934,8 @@ var SKIP_DIRS, SKIP_FILES, TEXT_EXTS, ext = (name) => {
172836
172934
  }));
172837
172935
  return { files, dirs, ignored };
172838
172936
  }, resolveLocalPath = (root, key) => {
172839
- const direct = join11(root, key.slice(1));
172840
- if (existsSync10(direct))
172937
+ const direct = join12(root, key.slice(1));
172938
+ if (existsSync11(direct))
172841
172939
  return direct;
172842
172940
  let dir = root;
172843
172941
  try {
@@ -172845,7 +172943,7 @@ var SKIP_DIRS, SKIP_FILES, TEXT_EXTS, ext = (name) => {
172845
172943
  const match = readdirSync3(dir).find((entry) => entry === name || sanitizeAssetName2(entry) === name);
172846
172944
  if (!match)
172847
172945
  return null;
172848
- dir = join11(dir, match);
172946
+ dir = join12(dir, match);
172849
172947
  }
172850
172948
  } catch {
172851
172949
  return null;
@@ -172904,9 +173002,9 @@ var init_localFiles = __esm(() => {
172904
173002
  });
172905
173003
 
172906
173004
  // src/compile/fonts.ts
172907
- import { createHash as createHash2 } from "node:crypto";
172908
- import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync12, writeFileSync as writeFileSync5 } from "node:fs";
172909
- import { dirname as dirname9, join as join13 } from "node:path";
173005
+ import { createHash as createHash3 } from "node:crypto";
173006
+ import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "node:fs";
173007
+ import { dirname as dirname9, join as join14 } from "node:path";
172910
173008
  var applyProjectFonts = async (entries, opts) => {
172911
173009
  const registry = (opts.registry ?? fontRegistryBase()).replace(/\/+$/, "");
172912
173010
  const byPath = () => new Map(entries.map((e) => [e.path.replace(/^\//, ""), e]));
@@ -172915,9 +173013,9 @@ var applyProjectFonts = async (entries, opts) => {
172915
173013
  registryBase: registry,
172916
173014
  readBytes: (entryPath) => {
172917
173015
  const entry = byPath().get(entryPath);
172918
- const abs = entry?.absPath ?? join13(opts.root, entryPath);
173016
+ const abs = entry?.absPath ?? join14(opts.root, entryPath);
172919
173017
  try {
172920
- return new Uint8Array(readFileSync12(abs));
173018
+ return new Uint8Array(readFileSync13(abs));
172921
173019
  } catch {
172922
173020
  return null;
172923
173021
  }
@@ -172925,9 +173023,9 @@ var applyProjectFonts = async (entries, opts) => {
172925
173023
  vendorFace: opts.mode === "vendor" ? async (face) => {
172926
173024
  const vendorDir = opts.vendorDir ?? opts.root;
172927
173025
  const rel = `assets/fonts/${face.fileName}`;
172928
- const abs = join13(vendorDir, rel);
173026
+ const abs = join14(vendorDir, rel);
172929
173027
  const entryPath = `${opts.vendorPrefix ?? ""}/${rel}`;
172930
- if (!existsSync11(abs))
173028
+ if (!existsSync12(abs))
172931
173029
  await downloadFace(registry, vendorDir, face, rel, abs);
172932
173030
  if (!byPath().has(entryPath.replace(/^\//, ""))) {
172933
173031
  entries.push(opts.makeResourceEntry?.(entryPath, abs) ?? { path: entryPath, type: "resource", absPath: abs });
@@ -172943,19 +173041,19 @@ var applyProjectFonts = async (entries, opts) => {
172943
173041
  const fileSrc = byPath().get(path.replace(/^\//, ""))?.fileSrc;
172944
173042
  return fileSrc ? (opts.publicUrl ?? "") + fileSrc : null;
172945
173043
  }) || undefined;
172946
- }, isHttp = (s) => /^https?:/i.test(s), sha12 = (b) => createHash2("sha256").update(b).digest("hex").slice(0, 12), registryHome = (registry) => registry.replace(/\/v\d+$/, ""), readRegistry = async (registry, rel) => {
173044
+ }, isHttp = (s) => /^https?:/i.test(s), sha12 = (b) => createHash3("sha256").update(b).digest("hex").slice(0, 12), registryHome = (registry) => registry.replace(/\/v\d+$/, ""), readRegistry = async (registry, rel) => {
172947
173045
  if (isHttp(registry)) {
172948
173046
  const res = await fetch(`${registry}/${rel}`);
172949
173047
  return res.ok ? new Uint8Array(await res.arrayBuffer()) : null;
172950
173048
  }
172951
173049
  try {
172952
- return new Uint8Array(readFileSync12(join13(registry, rel)));
173050
+ return new Uint8Array(readFileSync13(join14(registry, rel)));
172953
173051
  } catch {
172954
173052
  return null;
172955
173053
  }
172956
173054
  }, readLock = (path) => {
172957
173055
  try {
172958
- const lock = JSON.parse(readFileSync12(path, "utf8"));
173056
+ const lock = JSON.parse(readFileSync13(path, "utf8"));
172959
173057
  if (lock && typeof lock === "object" && lock.faces && lock.families)
172960
173058
  return lock;
172961
173059
  } catch {}
@@ -172987,10 +173085,10 @@ var applyProjectFonts = async (entries, opts) => {
172987
173085
  `);
172988
173086
  }, writeIfChanged = (path, text) => {
172989
173087
  try {
172990
- if (readFileSync12(path, "utf8") === text)
173088
+ if (readFileSync13(path, "utf8") === text)
172991
173089
  return;
172992
173090
  } catch {}
172993
- writeFileSync5(path, text);
173091
+ writeFileSync6(path, text);
172994
173092
  }, downloadFace = async (registry, vendorDir, face, rel, abs) => {
172995
173093
  const licRaw = await readRegistry(registry, "licenses.json");
172996
173094
  if (!licRaw)
@@ -173002,15 +173100,15 @@ var applyProjectFonts = async (entries, opts) => {
173002
173100
  const bytes = await readRegistry(registry, `${face.id}/${face.fileName}`);
173003
173101
  if (!bytes)
173004
173102
  throw new Error(`font("${face.id}"): face "${face.fileName}" not found in the registry (${registryHome(registry)}).`);
173005
- mkdirSync4(dirname9(abs), { recursive: true });
173006
- writeFileSync5(abs, bytes);
173007
- const lockPath = join13(vendorDir, "fonts.lock.json");
173103
+ mkdirSync5(dirname9(abs), { recursive: true });
173104
+ writeFileSync6(abs, bytes);
173105
+ const lockPath = join14(vendorDir, "fonts.lock.json");
173008
173106
  const lock = readLock(lockPath) ?? { registry, faces: {}, families: {} };
173009
173107
  lock.registry = registry;
173010
173108
  lock.faces[`${face.id}@${face.weight}${face.italic ? "i" : ""}`] = { id: face.id, family: face.family, weight: face.weight, italic: face.italic, hash: sha12(bytes), spdx: lic.spdx, path: rel };
173011
173109
  lock.families[face.id] = lic;
173012
173110
  writeIfChanged(lockPath, serializeLock(lock));
173013
- writeIfChanged(join13(vendorDir, "FONT-NOTICES.md"), renderNotices(lock));
173111
+ writeIfChanged(join14(vendorDir, "FONT-NOTICES.md"), renderNotices(lock));
173014
173112
  };
173015
173113
  var init_fonts = __esm(() => {
173016
173114
  init_compile();
@@ -227346,27 +227444,27 @@ import { homedir as homedir10 } from "node:os";
227346
227444
  import { basename as basename10, dirname as dirname17, join as join27, resolve as resolve14 } from "node:path";
227347
227445
 
227348
227446
  // src/plugin/manifest.ts
227349
- import { existsSync as existsSync8, readFileSync as readFileSync7, statSync as statSync2 } from "node:fs";
227350
- import { isAbsolute as isAbsolute2, join as join9 } from "node:path";
227447
+ import { existsSync as existsSync9, readFileSync as readFileSync8, statSync as statSync2 } from "node:fs";
227448
+ import { isAbsolute as isAbsolute2, join as join10 } from "node:path";
227351
227449
 
227352
227450
  // src/plugin/firstParty.ts
227353
227451
  init_projectCompile();
227354
- import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync2 } from "node:fs";
227355
- import { join as join8 } from "node:path";
227452
+ import { existsSync as existsSync8, readFileSync as readFileSync7, readdirSync as readdirSync2 } from "node:fs";
227453
+ import { join as join9 } from "node:path";
227356
227454
  var ID = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
227357
227455
  var firstPartyContractFile = (id) => {
227358
227456
  if (!sdkSourceDir || !ID.test(id))
227359
227457
  return null;
227360
- const file = join8(sdkSourceDir, "plugins", "gen", id, "contract.d.ts");
227361
- return existsSync7(file) ? file : null;
227458
+ const file = join9(sdkSourceDir, "plugins", "gen", id, "contract.d.ts");
227459
+ return existsSync8(file) ? file : null;
227362
227460
  };
227363
227461
  var firstPartyIds = () => {
227364
227462
  if (!sdkSourceDir)
227365
227463
  return [];
227366
- const gen = join8(sdkSourceDir, "plugins", "gen");
227367
- return existsSync7(gen) ? readdirSync2(gen).filter((id) => existsSync7(join8(gen, id, "contract.d.ts"))).sort() : [];
227464
+ const gen = join9(sdkSourceDir, "plugins", "gen");
227465
+ return existsSync8(gen) ? readdirSync2(gen).filter((id) => existsSync8(join9(gen, id, "contract.d.ts"))).sort() : [];
227368
227466
  };
227369
- var firstPartyContractText = (file) => readFileSync6(file, "utf8").replace(/from "(?:\.\.\/)+plugin"/g, `from "lecodes-sdk/plugin"`);
227467
+ var firstPartyContractText = (file) => readFileSync7(file, "utf8").replace(/from "(?:\.\.\/)+plugin"/g, `from "lecodes-sdk/plugin"`);
227370
227468
 
227371
227469
  // src/plugin/manifest.ts
227372
227470
  var MANIFEST_FILE = "lecodes-plugin.json";
@@ -227399,15 +227497,15 @@ var BRANCH = {
227399
227497
  web: new Set(["register", "dependencies", "resources", "static"])
227400
227498
  };
227401
227499
  var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
227402
- var isFolder = (path) => existsSync8(path) && statSync2(path).isDirectory();
227500
+ var isFolder = (path) => existsSync9(path) && statSync2(path).isDirectory();
227403
227501
  var pascal = (id) => id.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
227404
227502
  var XCFRAMEWORK = ".xcframework";
227405
227503
  var binaryName = (path) => path.split(/[\\/]/).pop().slice(0, -XCFRAMEWORK.length);
227406
227504
  var readManifest3 = (dir, id) => {
227407
- const file = join9(dir, MANIFEST_FILE);
227505
+ const file = join10(dir, MANIFEST_FILE);
227408
227506
  let raw;
227409
227507
  try {
227410
- raw = JSON.parse(readFileSync7(file, "utf8").replace(/^/, ""));
227508
+ raw = JSON.parse(readFileSync8(file, "utf8").replace(/^/, ""));
227411
227509
  } catch (e) {
227412
227510
  throw new ManifestError(file, [`not JSON: ${e instanceof Error ? e.message : String(e)}`]);
227413
227511
  }
@@ -227446,14 +227544,14 @@ var readManifest3 = (dir, id) => {
227446
227544
  problems.push(`implements: the id of the first-party plugin whose contract the folder implements ("ar")`);
227447
227545
  else if (!firstPartyContractFile(impl))
227448
227546
  problems.push(`implements: "${impl}" is no first-party plugin of this SDK (${firstPartyIds().join(", ") || "it carries none"})`);
227449
- if (existsSync8(join9(dir, CONTRACT_FILE)))
227547
+ if (existsSync9(join10(dir, CONTRACT_FILE)))
227450
227548
  problems.push(`implements: the folder implements another plugin's contract — it has no ${CONTRACT_FILE} of its own`);
227451
- if (isFolder(join9(dir, "sdk")))
227549
+ if (isFolder(join10(dir, "sdk")))
227452
227550
  problems.push("implements: the folder implements another plugin — its app side is the SDK's, it has no sdk/ of its own");
227453
227551
  }
227454
227552
  for (const platform of PLATFORMS) {
227455
227553
  const branch = raw[platform];
227456
- const folder = isFolder(join9(dir, platform));
227554
+ const folder = isFolder(join10(dir, platform));
227457
227555
  if (branch === undefined) {
227458
227556
  problems.push(`${platform}: missing — an object, or null where the plugin has no ${platform} half`);
227459
227557
  continue;
@@ -227484,7 +227582,7 @@ var readManifest3 = (dir, id) => {
227484
227582
  if (platform === "web" && register !== undefined) {
227485
227583
  if (!register.startsWith("web/"))
227486
227584
  problems.push(`web.register: "${register}" — a module of the web/ folder`);
227487
- else if (!existsSync8(join9(dir, register)))
227585
+ else if (!existsSync9(join10(dir, register)))
227488
227586
  problems.push(`web.register: ${register} is not there`);
227489
227587
  }
227490
227588
  }
@@ -227524,7 +227622,7 @@ var readManifest3 = (dir, id) => {
227524
227622
  problems.push(`ios.binaries[${i}]: ${path} is in ios/, the half's sources — a binary stands beside them ("binaries/ios/X.xcframework")`);
227525
227623
  else if (!/^[A-Za-z_]\w*$/.test(binaryName(path)))
227526
227624
  problems.push(`ios.binaries[${i}]: "${path}" — the folder's name is its binary target's: a Swift name before ${XCFRAMEWORK}`);
227527
- else if (!isFolder(join9(dir, path)))
227625
+ else if (!isFolder(join10(dir, path)))
227528
227626
  problems.push(`ios.binaries[${i}]: ${path} is not a folder of the plugin`);
227529
227627
  else if (named.has(binaryName(path)))
227530
227628
  problems.push(`ios.binaries[${i}]: ${path} is named "${binaryName(path)}" like ${named.get(binaryName(path))} — a name is one binary target`);
@@ -227565,7 +227663,7 @@ var readManifest3 = (dir, id) => {
227565
227663
  const segments = typeof folder === "string" ? folder.split(/[\\/]/) : [];
227566
227664
  if (typeof folder !== "string" || folder.trim() === "" || isAbsolute2(folder) || segments.some((s) => s === ".." || s === "." || s === "")) {
227567
227665
  problems.push(`web.static: a folder of the plugin, by its path in it ("xr-runtime")`);
227568
- } else if (!isFolder(join9(dir, folder)))
227666
+ } else if (!isFolder(join10(dir, folder)))
227569
227667
  problems.push(`web.static: ${folder} is not a folder of the plugin`);
227570
227668
  else if (folder === "index.js")
227571
227669
  problems.push(`web.static: "index.js" is the built module's name`);
@@ -227575,7 +227673,7 @@ var readManifest3 = (dir, id) => {
227575
227673
  throw new ManifestError(file, problems);
227576
227674
  return raw;
227577
227675
  };
227578
- var readManifestIfAny = (dir, id) => existsSync8(join9(dir, MANIFEST_FILE)) ? readManifest3(dir, id) : null;
227676
+ var readManifestIfAny = (dir, id) => existsSync9(join10(dir, MANIFEST_FILE)) ? readManifest3(dir, id) : null;
227579
227677
 
227580
227678
  // src/commands/compile.ts
227581
227679
  init_api();
@@ -227586,15 +227684,15 @@ import { basename as basename6, resolve as resolve6 } from "node:path";
227586
227684
  // src/compile/collect.ts
227587
227685
  init_localFiles();
227588
227686
  init_ignore();
227589
- import { readFileSync as readFileSync10 } from "node:fs";
227687
+ import { readFileSync as readFileSync11 } from "node:fs";
227590
227688
 
227591
227689
  // src/project/materialize.ts
227592
227690
  init_api();
227593
227691
  init_paths();
227594
- import { dirname as dirname8, join as join12 } from "node:path";
227595
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "node:fs";
227692
+ import { dirname as dirname8, join as join13 } from "node:path";
227693
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "node:fs";
227596
227694
  var kindOf = (type) => type === "file" ? "resource" : type;
227597
- var absOf = (root, key) => join12(root, ...key.replace(/^\//, "").split("/"));
227695
+ var absOf = (root, key) => join13(root, ...key.replace(/^\//, "").split("/"));
227598
227696
  var buildPaths = (assets) => {
227599
227697
  const pathById = new Map;
227600
227698
  for (const asset of assets) {
@@ -227612,20 +227710,20 @@ var materializeAssets = async (root, project, apiUrl, token, onProgress) => {
227612
227710
  const key = pathById.get(asset.id);
227613
227711
  const abs = absOf(root, key);
227614
227712
  if (asset.type === "folder") {
227615
- mkdirSync3(abs, { recursive: true });
227713
+ mkdirSync4(abs, { recursive: true });
227616
227714
  continue;
227617
227715
  }
227618
- mkdirSync3(dirname8(abs), { recursive: true });
227716
+ mkdirSync4(dirname8(abs), { recursive: true });
227619
227717
  let sha;
227620
227718
  if (asset.type === "file") {
227621
227719
  if (!asset.file?.src)
227622
227720
  continue;
227623
227721
  const bytes = await downloadBlob(apiUrl, asset.file.src);
227624
- writeFileSync4(abs, bytes);
227722
+ writeFileSync5(abs, bytes);
227625
227723
  sha = sha256(bytes);
227626
227724
  } else {
227627
227725
  const text = asset.text ?? "";
227628
- writeFileSync4(abs, text);
227726
+ writeFileSync5(abs, text);
227629
227727
  sha = sha256(text);
227630
227728
  }
227631
227729
  files[key] = { id: asset.id, type: kindOf(asset.type), sha };
@@ -227659,7 +227757,7 @@ var collectEntries = (root, manifest, project, opts = {}) => {
227659
227757
  warnings.push(`No compiled shader for ${file.path} — push it to compile the shader.`);
227660
227758
  entries.push({ path: file.path, type, shaders, absPath: file.absPath });
227661
227759
  } else {
227662
- entries.push({ path: file.path, type, text: readFileSync10(file.absPath, "utf8") });
227760
+ entries.push({ path: file.path, type, text: readFileSync11(file.absPath, "utf8") });
227663
227761
  }
227664
227762
  }
227665
227763
  return { entries, warnings };
@@ -227668,11 +227766,11 @@ var collectEntries = (root, manifest, project, opts = {}) => {
227668
227766
  // src/compile/collectLocal.ts
227669
227767
  init_localFiles();
227670
227768
  init_ignore();
227671
- import { readFileSync as readFileSync11 } from "node:fs";
227769
+ import { readFileSync as readFileSync12 } from "node:fs";
227672
227770
  import { basename as basename2 } from "node:path";
227673
227771
  var isHalfManifest = (abs) => {
227674
227772
  try {
227675
- return typeof JSON.parse(readFileSync11(abs, "utf8").replace(/^/, "")).implements === "string";
227773
+ return typeof JSON.parse(readFileSync12(abs, "utf8").replace(/^/, "")).implements === "string";
227676
227774
  } catch {
227677
227775
  return false;
227678
227776
  }
@@ -227694,7 +227792,7 @@ var collectLocalEntries = (root, manifest, assetBase, opts = {}) => {
227694
227792
  }
227695
227793
  entries.push({ path: file.path, type, shaders: [], absPath: file.absPath });
227696
227794
  } else {
227697
- entries.push({ path: file.path, type, text: readFileSync11(file.absPath, "utf8") });
227795
+ entries.push({ path: file.path, type, text: readFileSync12(file.absPath, "utf8") });
227698
227796
  }
227699
227797
  }
227700
227798
  return { entries, warnings };
@@ -227713,105 +227811,11 @@ import { tmpdir } from "node:os";
227713
227811
  import { basename as basename3, join as join16 } from "node:path";
227714
227812
 
227715
227813
  // src/hosts/matcTool.ts
227814
+ init_releases();
227815
+ init_cli();
227716
227816
  import { existsSync as existsSync13, readdirSync as readdirSync4 } from "node:fs";
227717
227817
  import { homedir as homedir3 } from "node:os";
227718
227818
  import { join as join15, resolve as resolve4 } from "node:path";
227719
-
227720
- // src/hosts/releases.ts
227721
- init_cli();
227722
- import { createHash as createHash3 } from "node:crypto";
227723
- import { existsSync as existsSync12, mkdirSync as mkdirSync5, mkdtempSync, readFileSync as readFileSync13, renameSync, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
227724
- import { join as join14 } from "node:path";
227725
- import { spawnSync as spawnSync4 } from "node:child_process";
227726
- var MANIFEST_URL = "https://storage.yandexcloud.net/le-codes/releases/versions.json";
227727
- var ARTIFACT_STAMP = ".lecodes-artifact.json";
227728
- var cachedArtifactSha = (dir) => {
227729
- try {
227730
- return JSON.parse(readFileSync13(join14(dir, ARTIFACT_STAMP), "utf8")).sha256 ?? null;
227731
- } catch {
227732
- return null;
227733
- }
227734
- };
227735
- var macPlatformKey = () => process.arch === "arm64" ? "macos-arm64" : "macos-x64";
227736
- var semverNewer = (a, b) => {
227737
- const pa = a.split(/[.-]/).map(Number), pb = b.split(/[.-]/).map(Number);
227738
- for (let i = 0;i < 3; i++)
227739
- if ((pb[i] ?? 0) !== (pa[i] ?? 0))
227740
- return (pb[i] ?? 0) - (pa[i] ?? 0);
227741
- return 0;
227742
- };
227743
- var newestForPlatform = (versions, key) => {
227744
- const carrying = versions.filter((v) => v.files[key]).sort((a, b) => semverNewer(a.version, b.version));
227745
- const newest = carrying[0];
227746
- return newest ? { version: newest.version, file: newest.files[key] } : null;
227747
- };
227748
- var fetchReleaseProduct = async (product, label) => {
227749
- let manifest;
227750
- try {
227751
- const res = await fetch(MANIFEST_URL);
227752
- if (!res.ok)
227753
- throw new Error(`HTTP ${res.status}`);
227754
- manifest = await res.json();
227755
- } catch (e) {
227756
- throw new CliError(`Can't fetch the release manifest (${MANIFEST_URL}): ${e instanceof Error ? e.message : String(e)}`);
227757
- }
227758
- const entry = manifest.products?.[product];
227759
- if (!entry)
227760
- throw new CliError(`The release manifest has no ${label} product.`);
227761
- return entry;
227762
- };
227763
- var downloadReleaseArchive = async (file, opts) => {
227764
- let bytes;
227765
- try {
227766
- const res = await fetch(file.url);
227767
- if (!res.ok)
227768
- throw new Error(`HTTP ${res.status}`);
227769
- bytes = Buffer.from(await res.arrayBuffer());
227770
- } catch (e) {
227771
- throw new CliError(`Download failed (${file.url}): ${e instanceof Error ? e.message : String(e)}`);
227772
- }
227773
- const sha = createHash3("sha256").update(bytes).digest("hex");
227774
- if (sha !== file.sha256)
227775
- throw new CliError(`Downloaded archive is corrupt (sha256 mismatch) — try again.`);
227776
- mkdirSync5(opts.cacheRoot, { recursive: true });
227777
- const stage = mkdtempSync(join14(opts.cacheRoot, "dl-"));
227778
- try {
227779
- const archiveName = file.url.split("/").pop() ?? "archive";
227780
- const archive = join14(stage, archiveName);
227781
- writeFileSync6(archive, bytes);
227782
- if (opts.extract) {
227783
- opts.extract(archive, stage);
227784
- } else {
227785
- const winTar = join14(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe");
227786
- const tarExe = process.platform === "win32" && existsSync12(winTar) ? winTar : "tar";
227787
- const tar = spawnSync4(tarExe, ["-xf", archiveName], { cwd: stage, stdio: ["ignore", "ignore", "pipe"] });
227788
- if (tar.status !== 0)
227789
- throw new CliError(`Couldn't extract the archive: ${tar.stderr?.toString() ?? "tar failed"}`);
227790
- }
227791
- rmSync(archive);
227792
- const bad = opts.validate(stage);
227793
- if (bad)
227794
- throw new CliError(bad);
227795
- writeFileSync6(join14(stage, ARTIFACT_STAMP), JSON.stringify({ sha256: file.sha256, url: file.url }, null, 2));
227796
- try {
227797
- renameSync(stage, opts.dest);
227798
- } catch {
227799
- let destBad;
227800
- try {
227801
- destBad = opts.validate(opts.dest);
227802
- } catch {
227803
- destBad = "missing";
227804
- }
227805
- if (destBad)
227806
- throw new CliError(`Couldn't move the download into ${opts.dest}.`);
227807
- }
227808
- } finally {
227809
- rmSync(stage, { recursive: true, force: true });
227810
- }
227811
- };
227812
-
227813
- // src/hosts/matcTool.ts
227814
- init_cli();
227815
227819
  var matcName = () => process.platform === "win32" ? "matc.exe" : "matc";
227816
227820
  var platformKey = () => {
227817
227821
  if (process.platform === "win32")
@@ -232591,11 +232595,12 @@ import { cpSync as cpSync3, existsSync as existsSync24, mkdirSync as mkdirSync15
232591
232595
  import { basename as basename9, dirname as dirname16, join as join26, resolve as resolve13 } from "node:path";
232592
232596
 
232593
232597
  // src/hosts/desktopRenderer.ts
232598
+ init_releases();
232599
+ init_cli();
232594
232600
  import { execFileSync as execFileSync3, spawn as spawn2, spawnSync as spawnSync6 } from "node:child_process";
232595
232601
  import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync2, readdirSync as readdirSync7, readFileSync as readFileSync23, rmSync as rmSync5, statSync as statSync6, writeFileSync as writeFileSync16, linkSync } from "node:fs";
232596
232602
  import { homedir as homedir9, tmpdir as tmpdir2 } from "node:os";
232597
232603
  import { delimiter, join as join24, resolve as resolve11 } from "node:path";
232598
- init_cli();
232599
232604
 
232600
232605
  // ../../renderers/web-shared/src/sourcemapError.ts
232601
232606
  var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -234694,10 +234699,11 @@ var app_default = defineCommand({
234694
234699
  init_cli();
234695
234700
 
234696
234701
  // src/hosts/cmgenTool.ts
234702
+ init_releases();
234703
+ init_cli();
234697
234704
  import { existsSync as existsSync26, readdirSync as readdirSync11 } from "node:fs";
234698
234705
  import { homedir as homedir11 } from "node:os";
234699
234706
  import { join as join28, resolve as resolve15 } from "node:path";
234700
- init_cli();
234701
234707
  var exeName2 = () => process.platform === "win32" ? "cmgen.exe" : "cmgen";
234702
234708
  var platformKey3 = () => {
234703
234709
  if (process.platform === "win32")
@@ -237697,6 +237703,9 @@ var unwrapReferenced = async (js, resources, root, texel) => {
237697
237703
  }
237698
237704
  note(`Unwrapping ${raw.length} of the ${glbs.length} GLB(s) the app references — no lightmap uvs; written in place (--no-unwrap skips)…`);
237699
237705
  const assets = await loadPeer("lecodes-assets", "cli", { for: "lecodes lightmap bake (lightmap uvs)" });
237706
+ if (typeof assets.unwrapGlbFiles !== "function") {
237707
+ throw new CliError("The lecodes-assets this CLI loaded has no unwrapGlbFiles (it predates the bake's unwrap) — `lecodes update` moves it forward; --no-unwrap bakes the GLBs as they are.");
237708
+ }
237700
237709
  const t0 = performance.now();
237701
237710
  const rel = (f) => relative7(root, f).replace(/\\/g, "/");
237702
237711
  const reports = await assets.unwrapGlbFiles(raw, {
@@ -244756,6 +244765,7 @@ var serve_default = defineCommand({
244756
244765
  // src/commands/shaders/index.ts
244757
244766
  import { readFileSync as readFileSync46 } from "node:fs";
244758
244767
  import { relative as relative13 } from "node:path";
244768
+ init_releases();
244759
244769
  init_cli();
244760
244770
  init_shared();
244761
244771