@arcote.tech/arc-cli 0.8.3 → 0.8.5

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
@@ -31322,8 +31322,8 @@ ${colors3.yellow}Type declaration errors:${colors3.reset}`);
31322
31322
  }
31323
31323
 
31324
31324
  // src/platform/shared.ts
31325
- import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync10, readdirSync as readdirSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
31326
- import { dirname as dirname8, join as join13 } from "path";
31325
+ import { copyFileSync, existsSync as existsSync12, mkdirSync as mkdirSync10, readdirSync as readdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "fs";
31326
+ import { dirname as dirname8, join as join14 } from "path";
31327
31327
 
31328
31328
  // src/builder/module-builder.ts
31329
31329
  import { execSync } from "child_process";
@@ -32023,6 +32023,26 @@ function readServerExternals(path4) {
32023
32023
  return [];
32024
32024
  }
32025
32025
  }
32026
+ var STATIC_CHUNK_IMPORT = /(?:^|[;}\s])import\s*["']\.\/(chunk-[a-z0-9]+\.js)["']|from\s*["']\.\/(chunk-[a-z0-9]+\.js)["']/g;
32027
+ function staticChunkClosure(entryBytes, chunkBytes) {
32028
+ const seen = new Set;
32029
+ const stack = [entryBytes];
32030
+ while (stack.length) {
32031
+ const src = Buffer.from(stack.pop()).toString("utf8");
32032
+ STATIC_CHUNK_IMPORT.lastIndex = 0;
32033
+ let m;
32034
+ while (m = STATIC_CHUNK_IMPORT.exec(src)) {
32035
+ const chunk = m[1] ?? m[2];
32036
+ if (seen.has(chunk))
32037
+ continue;
32038
+ seen.add(chunk);
32039
+ const b = chunkBytes.get(chunk);
32040
+ if (b)
32041
+ stack.push(b);
32042
+ }
32043
+ }
32044
+ return [...seen];
32045
+ }
32026
32046
  function buildStatsEnabled() {
32027
32047
  return process.env.ARC_BUILD_STATS === "1";
32028
32048
  }
@@ -32322,22 +32342,31 @@ export { startApp } from "@arcote.tech/platform";
32322
32342
  }
32323
32343
  let initialFile = "";
32324
32344
  let initialHash = "";
32345
+ let initialStaticChunks = [];
32325
32346
  const groups = {};
32326
32347
  const sharedChunks = [];
32348
+ const chunkBytes = new Map;
32349
+ for (const out of outFiles) {
32350
+ if (out.kind === "chunk")
32351
+ chunkBytes.set(out.name, out.bytes);
32352
+ }
32327
32353
  for (const out of outFiles) {
32328
32354
  if (out.kind === "entry-point") {
32329
32355
  const hash = sha256Hex(out.bytes).slice(0, 16);
32330
32356
  const stem = out.name.replace(/\.js$/, "");
32331
32357
  const finalName = `${stem}.${hash}.js`;
32332
32358
  writeFileSync7(join9(outDir, finalName), out.bytes);
32359
+ const staticChunks = staticChunkClosure(out.bytes, chunkBytes);
32333
32360
  if (stem === "initial") {
32334
32361
  initialFile = finalName;
32335
32362
  initialHash = hash;
32363
+ initialStaticChunks = staticChunks;
32336
32364
  } else {
32337
32365
  groups[stem] = {
32338
32366
  file: finalName,
32339
32367
  hash,
32340
- modules: groupModuleMap.get(stem) ?? []
32368
+ modules: groupModuleMap.get(stem) ?? [],
32369
+ staticChunks
32341
32370
  };
32342
32371
  }
32343
32372
  } else if (out.kind === "chunk") {
@@ -32365,7 +32394,7 @@ export { startApp } from "@arcote.tech/platform";
32365
32394
  }
32366
32395
  }
32367
32396
  const manifest = {
32368
- initial: { file: initialFile, hash: initialHash },
32397
+ initial: { file: initialFile, hash: initialHash, staticChunks: initialStaticChunks },
32369
32398
  groups,
32370
32399
  sharedChunks,
32371
32400
  cached: false
@@ -32629,9 +32658,86 @@ const { writeFileSync } = await import("node:fs");
32629
32658
  writeFileSync(out, JSON.stringify({ access, exposure }, null, 2) + "\\n");
32630
32659
  `.trim();
32631
32660
 
32661
+ // src/builder/seed-guard.ts
32662
+ import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10 } from "fs";
32663
+ import { join as join11, relative as relative4, sep as sep3 } from "path";
32664
+ var CALL = /\.(?:seedWith|withSeeds)\s*\(/g;
32665
+ var GATED = /^\.(?:seedWith|withSeeds)\s*\(\s*ONLY_SERVER\b/;
32666
+ function walkTsFiles(dir, out) {
32667
+ for (const entry of readdirSync5(dir, { withFileTypes: true })) {
32668
+ if (entry.name === "node_modules" || entry.name === "dist")
32669
+ continue;
32670
+ const abs = join11(dir, entry.name);
32671
+ if (entry.isDirectory()) {
32672
+ if (entry.name === "__tests__")
32673
+ continue;
32674
+ walkTsFiles(abs, out);
32675
+ } else if (entry.isFile() && /\.(ts|tsx)$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
32676
+ out.push(abs);
32677
+ }
32678
+ }
32679
+ }
32680
+ function findUngatedSeeds(pkgs, rootDir) {
32681
+ const leaks = [];
32682
+ const scanned = new Set;
32683
+ for (const pkg of pkgs) {
32684
+ const srcDir = join11(pkg.path, "src");
32685
+ if (scanned.has(srcDir) || !existsSync10(srcDir))
32686
+ continue;
32687
+ scanned.add(srcDir);
32688
+ const files = [];
32689
+ walkTsFiles(srcDir, files);
32690
+ for (const abs of files) {
32691
+ const content = readFileSync10(abs, "utf-8");
32692
+ CALL.lastIndex = 0;
32693
+ let m;
32694
+ while ((m = CALL.exec(content)) !== null) {
32695
+ const rest = content.slice(m.index);
32696
+ if (GATED.test(rest))
32697
+ continue;
32698
+ const lineStart = content.lastIndexOf(`
32699
+ `, m.index) + 1;
32700
+ if (content.slice(lineStart, m.index).includes("//"))
32701
+ continue;
32702
+ const before = content.slice(0, m.index);
32703
+ if (before.lastIndexOf("/*") > before.lastIndexOf("*/"))
32704
+ continue;
32705
+ const nlAfter = content.indexOf(`
32706
+ `, m.index);
32707
+ const lineEnd = nlAfter === -1 ? content.length : nlAfter;
32708
+ const line = content.slice(0, m.index).split(`
32709
+ `).length;
32710
+ const relpath = relative4(rootDir, abs).split(sep3).join("/");
32711
+ leaks.push({
32712
+ relpath,
32713
+ line,
32714
+ snippet: content.slice(lineStart, lineEnd).trim().slice(0, 120)
32715
+ });
32716
+ }
32717
+ }
32718
+ }
32719
+ return leaks;
32720
+ }
32721
+ function assertSeedsServerGated(pkgs, rootDir) {
32722
+ const leaks = findUngatedSeeds(pkgs, rootDir);
32723
+ if (leaks.length === 0)
32724
+ return;
32725
+ const lines = leaks.map((l) => ` \u2022 ${l.relpath}:${l.line}
32726
+ ${l.snippet}`).join(`
32727
+ `);
32728
+ throw new Error(`Seed leak do bundla klienta: ${leaks.length} wywo\u0142a\u0144 .seedWith(...)/.withSeeds(...) ` + `nie jest owini\u0119tych w ONLY_SERVER.
32729
+
32730
+ ${lines}
32731
+
32732
+ ` + `Dane seed czyta wy\u0142\u0105cznie host (runSeeds) \u2014 po stronie klienta s\u0105 zb\u0119dne i ` + `wyciekaj\u0105 do PUBLICZNEGO bundla przegl\u0105darki. Zgejtuj argument w miejscu:
32733
+ ` + ` .seedWith(ONLY_SERVER ? [ /* wiersze */ ] : [])
32734
+ ` + ` .seedWith(ONLY_SERVER ? (await import("./seeds.server")).rows : [])
32735
+ ` + `Statyczny import z osobnego pliku NIE pomaga (build wymusza sideEffects:true).`);
32736
+ }
32737
+
32632
32738
  // src/builder/chunk-planner.ts
32633
- import { readFileSync as readFileSync10, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
32634
- import { basename as basename4, join as join11 } from "path";
32739
+ import { readFileSync as readFileSync11, readdirSync as readdirSync6, statSync as statSync2 } from "fs";
32740
+ import { basename as basename4, join as join12 } from "path";
32635
32741
  var PUBLIC_CHUNK = "public";
32636
32742
  function planChunks(packages, accessMap) {
32637
32743
  const assignments = new Map;
@@ -32667,14 +32773,14 @@ var MODULE_CALL = /\bmodule\(\s*["'`]([a-zA-Z0-9_-]+)["'`]/g;
32667
32773
  function collectModuleNames(dir, acc) {
32668
32774
  let entries;
32669
32775
  try {
32670
- entries = readdirSync5(dir);
32776
+ entries = readdirSync6(dir);
32671
32777
  } catch {
32672
32778
  return;
32673
32779
  }
32674
32780
  for (const e of entries) {
32675
32781
  if (e === "node_modules" || e === "dist")
32676
32782
  continue;
32677
- const full = join11(dir, e);
32783
+ const full = join12(dir, e);
32678
32784
  let isDir = false;
32679
32785
  try {
32680
32786
  isDir = statSync2(full).isDirectory();
@@ -32684,7 +32790,7 @@ function collectModuleNames(dir, acc) {
32684
32790
  if (isDir) {
32685
32791
  collectModuleNames(full, acc);
32686
32792
  } else if (e.endsWith(".ts") || e.endsWith(".tsx")) {
32687
- const src = readFileSync10(full, "utf-8");
32793
+ const src = readFileSync11(full, "utf-8");
32688
32794
  MODULE_CALL.lastIndex = 0;
32689
32795
  let m;
32690
32796
  while (m = MODULE_CALL.exec(src))
@@ -32694,14 +32800,14 @@ function collectModuleNames(dir, acc) {
32694
32800
  }
32695
32801
  function packageDeclaresModule(pkg) {
32696
32802
  const names = new Set;
32697
- collectModuleNames(join11(pkg.path, "src"), names);
32803
+ collectModuleNames(join12(pkg.path, "src"), names);
32698
32804
  return names.size > 0;
32699
32805
  }
32700
32806
  function assertOneModulePerPackage(packages) {
32701
32807
  const offenders = [];
32702
32808
  for (const pkg of packages) {
32703
32809
  const names = new Set;
32704
- collectModuleNames(join11(pkg.path, "src"), names);
32810
+ collectModuleNames(join12(pkg.path, "src"), names);
32705
32811
  if (names.size > 1) {
32706
32812
  offenders.push({ pkg: pkg.name, modules: [...names].sort() });
32707
32813
  }
@@ -32733,8 +32839,8 @@ function resolveChunk(moduleName, access) {
32733
32839
 
32734
32840
  // src/builder/dependency-collector.ts
32735
32841
  import { createHash } from "crypto";
32736
- import { existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
32737
- import { basename as basename5, dirname as dirname7, join as join12 } from "path";
32842
+ import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "fs";
32843
+ import { basename as basename5, dirname as dirname7, join as join13 } from "path";
32738
32844
  import { fileURLToPath as fileURLToPath6 } from "url";
32739
32845
  function collectFrameworkDeps(arcDir, rootDir, packages, sharedDeps = [], serverExternals = []) {
32740
32846
  mkdirSync9(arcDir, { recursive: true });
@@ -32746,7 +32852,7 @@ function collectFrameworkDeps(arcDir, rootDir, packages, sharedDeps = [], server
32746
32852
  try {
32747
32853
  const cliDir = dirname7(fileURLToPath6(import.meta.url));
32748
32854
  const arcOtelPkgPath = Bun.resolveSync("@arcote.tech/arc-otel/package.json", cliDir);
32749
- const arcOtelPkg = JSON.parse(readFileSync11(arcOtelPkgPath, "utf-8"));
32855
+ const arcOtelPkg = JSON.parse(readFileSync12(arcOtelPkgPath, "utf-8"));
32750
32856
  for (const [name, spec] of Object.entries(arcOtelPkg.dependencies ?? {})) {
32751
32857
  if (name.startsWith("@opentelemetry/")) {
32752
32858
  versions[name] = spec;
@@ -32757,10 +32863,10 @@ function collectFrameworkDeps(arcDir, rootDir, packages, sharedDeps = [], server
32757
32863
  console.warn(`[arc-otel] could not resolve @arcote.tech/arc-otel \u2014 image will run without telemetry deps: ${e.message}`);
32758
32864
  }
32759
32865
  let rootArc;
32760
- const rootPkgPath = join12(rootDir, "package.json");
32761
- if (existsSync10(rootPkgPath)) {
32866
+ const rootPkgPath = join13(rootDir, "package.json");
32867
+ if (existsSync11(rootPkgPath)) {
32762
32868
  try {
32763
- const rootPkg = JSON.parse(readFileSync11(rootPkgPath, "utf-8"));
32869
+ const rootPkg = JSON.parse(readFileSync12(rootPkgPath, "utf-8"));
32764
32870
  if (rootPkg.arc && typeof rootPkg.arc === "object")
32765
32871
  rootArc = rootPkg.arc;
32766
32872
  } catch {}
@@ -32772,11 +32878,11 @@ function collectFrameworkDeps(arcDir, rootDir, packages, sharedDeps = [], server
32772
32878
  dependencies: versions,
32773
32879
  ...rootArc ? { arc: rootArc } : {}
32774
32880
  };
32775
- const manifestPath = join12(arcDir, "package.json");
32881
+ const manifestPath = join13(arcDir, "package.json");
32776
32882
  writeFileSync9(manifestPath, JSON.stringify(manifest, null, 2) + `
32777
32883
  `);
32778
- const hash = sha256Hex2(readFileSync11(manifestPath));
32779
- writeFileSync9(join12(arcDir, ".deps-hash"), hash + `
32884
+ const hash = sha256Hex2(readFileSync12(manifestPath));
32885
+ writeFileSync9(join13(arcDir, ".deps-hash"), hash + `
32780
32886
  `);
32781
32887
  return { hash, manifestPath };
32782
32888
  }
@@ -32784,7 +32890,7 @@ function sha256Hex2(bytes) {
32784
32890
  return createHash("sha256").update(bytes).digest("hex");
32785
32891
  }
32786
32892
  function resolveFrameworkVersions(rootDir, packages) {
32787
- const rootPkg = JSON.parse(readFileSync11(join12(rootDir, "package.json"), "utf-8"));
32893
+ const rootPkg = JSON.parse(readFileSync12(join13(rootDir, "package.json"), "utf-8"));
32788
32894
  const rootDeps = rootPkg.dependencies ?? {};
32789
32895
  const rootDevDeps = rootPkg.devDependencies ?? {};
32790
32896
  const required = new Set(FRAMEWORK_PEERS);
@@ -32815,7 +32921,7 @@ function findWorkspaceSpec(packages, name) {
32815
32921
  return;
32816
32922
  }
32817
32923
  function mergeServerExternals(versions, rootDir, packages, externals) {
32818
- const rootPkg = JSON.parse(readFileSync11(join12(rootDir, "package.json"), "utf-8"));
32924
+ const rootPkg = JSON.parse(readFileSync12(join13(rootDir, "package.json"), "utf-8"));
32819
32925
  const rootDeps = rootPkg.dependencies ?? {};
32820
32926
  const rootDevDeps = rootPkg.devDependencies ?? {};
32821
32927
  for (const { name, version } of externals) {
@@ -32843,9 +32949,9 @@ function resolveWorkspace() {
32843
32949
  process.exit(1);
32844
32950
  }
32845
32951
  const rootDir = dirname8(packageJsonPath);
32846
- const rootPkg = JSON.parse(readFileSync12(packageJsonPath, "utf-8"));
32952
+ const rootPkg = JSON.parse(readFileSync13(packageJsonPath, "utf-8"));
32847
32953
  const appName = rootPkg.name ?? "Arc App";
32848
- const arcDir = join13(rootDir, ".arc", "platform");
32954
+ const arcDir = join14(rootDir, ".arc", "platform");
32849
32955
  log2("Scanning workspaces...");
32850
32956
  const packages = discoverPackages(rootDir);
32851
32957
  if (packages.length > 0) {
@@ -32855,10 +32961,10 @@ function resolveWorkspace() {
32855
32961
  }
32856
32962
  let manifest;
32857
32963
  for (const name of ["manifest.json", "manifest.webmanifest"]) {
32858
- const manifestPath = join13(rootDir, name);
32859
- if (existsSync11(manifestPath)) {
32964
+ const manifestPath = join14(rootDir, name);
32965
+ if (existsSync12(manifestPath)) {
32860
32966
  try {
32861
- const data = JSON.parse(readFileSync12(manifestPath, "utf-8"));
32967
+ const data = JSON.parse(readFileSync13(manifestPath, "utf-8"));
32862
32968
  const icons = data.icons;
32863
32969
  manifest = {
32864
32970
  path: manifestPath,
@@ -32877,9 +32983,9 @@ function resolveWorkspace() {
32877
32983
  rootPkg,
32878
32984
  appName,
32879
32985
  arcDir,
32880
- browserDir: join13(arcDir, "browser"),
32881
- assetsDir: join13(arcDir, "assets"),
32882
- publicDir: join13(rootDir, "public"),
32986
+ browserDir: join14(arcDir, "browser"),
32987
+ assetsDir: join14(arcDir, "assets"),
32988
+ publicDir: join14(rootDir, "public"),
32883
32989
  packages,
32884
32990
  manifest
32885
32991
  };
@@ -32892,12 +32998,13 @@ async function buildAll(ws, opts = {}) {
32892
32998
  const federation = getFederationConfig(ws);
32893
32999
  log2(`Building (concurrency parallel${noCache ? ", no-cache" : ""})...`);
32894
33000
  assertOneModulePerPackage(ws.packages);
33001
+ assertSeedsServerGated(ws.packages, ws.rootDir);
32895
33002
  await buildContextPackages(ws.rootDir, ws.packages, cache, noCache);
32896
- const serverDir = join13(ws.arcDir, "server");
33003
+ const serverDir = join14(ws.arcDir, "server");
32897
33004
  const { entryFile: serverEntry, externals: serverExternals } = await buildServerApp(ws.rootDir, serverDir, ws.packages, cache, noCache);
32898
- const accessMap = await extractAccessMap(ws.rootDir, join13(serverDir, serverEntry));
33005
+ const accessMap = await extractAccessMap(ws.rootDir, join14(serverDir, serverEntry));
32899
33006
  mkdirSync10(ws.arcDir, { recursive: true });
32900
- writeFileSync10(join13(ws.arcDir, "access.json"), JSON.stringify(accessMap.access, null, 2) + `
33007
+ writeFileSync10(join14(ws.arcDir, "access.json"), JSON.stringify(accessMap.access, null, 2) + `
32901
33008
  `);
32902
33009
  const isHost = Boolean(federation?.host);
32903
33010
  const moduleNameOf2 = (name) => name.split("/").pop() ?? name;
@@ -32917,7 +33024,7 @@ async function buildAll(ws, opts = {}) {
32917
33024
  const i18nCollector = new Map;
32918
33025
  const browserSequence = (async () => {
32919
33026
  const bundled = await buildBrowserApp(ws.rootDir, ws.browserDir, plan, cache, noCache, i18nCollector, { federated: false, exposeRuntime: isHost, minify });
32920
- const fed = fedPlan ? await buildBrowserApp(ws.rootDir, join13(ws.arcDir, "browser-fed"), fedPlan, cache, noCache, i18nCollector, { federated: true, unitId: "browser-app-fed", minify }) : undefined;
33027
+ const fed = fedPlan ? await buildBrowserApp(ws.rootDir, join14(ws.arcDir, "browser-fed"), fedPlan, cache, noCache, i18nCollector, { federated: true, unitId: "browser-app-fed", minify }) : undefined;
32921
33028
  return { bundled, fed };
32922
33029
  })();
32923
33030
  const [{ bundled: browserResult, fed: fedResult }, , , , shellResult] = await Promise.all([
@@ -32925,7 +33032,7 @@ async function buildAll(ws, opts = {}) {
32925
33032
  buildStyles(ws.rootDir, ws.arcDir, ws.packages, themePath, cache, noCache),
32926
33033
  copyBrowserAssets(ws, cache, noCache),
32927
33034
  buildTranslations(ws.rootDir, ws.arcDir, cache, noCache),
32928
- needsPeerShell ? buildPeerShell(ws.rootDir, join13(ws.arcDir, "shell"), cache, noCache) : Promise.resolve(undefined)
33035
+ needsPeerShell ? buildPeerShell(ws.rootDir, join14(ws.arcDir, "shell"), cache, noCache) : Promise.resolve(undefined)
32929
33036
  ]);
32930
33037
  const { finalizeTranslations: finalizeTranslations3 } = await Promise.resolve().then(() => (init_i18n(), exports_i18n));
32931
33038
  await finalizeTranslations3(ws.rootDir, ws.arcDir, i18nCollector);
@@ -32950,7 +33057,7 @@ async function buildAll(ws, opts = {}) {
32950
33057
  }
32951
33058
  } : undefined;
32952
33059
  const finalManifest = assembleManifest(ws, browserResult, cache, shellResult, federationBuild);
32953
- writeFileSync10(join13(ws.arcDir, "manifest.json"), JSON.stringify(finalManifest, null, 2));
33060
+ writeFileSync10(join14(ws.arcDir, "manifest.json"), JSON.stringify(finalManifest, null, 2));
32954
33061
  return finalManifest;
32955
33062
  }
32956
33063
  function assembleManifest(ws, browser, cache, shell, federation) {
@@ -32976,35 +33083,35 @@ function getFederationConfig(ws) {
32976
33083
  }
32977
33084
  function resolveAssetSource(from, pkgDir, rootDir) {
32978
33085
  if (from.startsWith("./") || from.startsWith("../")) {
32979
- const resolved = join13(pkgDir, from);
32980
- return existsSync11(resolved) ? resolved : null;
33086
+ const resolved = join14(pkgDir, from);
33087
+ return existsSync12(resolved) ? resolved : null;
32981
33088
  }
32982
33089
  const candidates = [
32983
- join13(rootDir, "node_modules", from),
32984
- join13(pkgDir, "node_modules", from)
33090
+ join14(rootDir, "node_modules", from),
33091
+ join14(pkgDir, "node_modules", from)
32985
33092
  ];
32986
33093
  for (const c of candidates) {
32987
- if (existsSync11(c))
33094
+ if (existsSync12(c))
32988
33095
  return c;
32989
33096
  }
32990
- const bunCacheDir = join13(rootDir, "node_modules", ".bun");
32991
- if (existsSync11(bunCacheDir)) {
32992
- for (const entry of readdirSync6(bunCacheDir, { withFileTypes: true })) {
33097
+ const bunCacheDir = join14(rootDir, "node_modules", ".bun");
33098
+ if (existsSync12(bunCacheDir)) {
33099
+ for (const entry of readdirSync7(bunCacheDir, { withFileTypes: true })) {
32993
33100
  if (!entry.isDirectory())
32994
33101
  continue;
32995
- const candidate = join13(bunCacheDir, entry.name, "node_modules", from);
32996
- if (existsSync11(candidate))
33102
+ const candidate = join14(bunCacheDir, entry.name, "node_modules", from);
33103
+ if (existsSync12(candidate))
32997
33104
  return candidate;
32998
33105
  }
32999
33106
  }
33000
33107
  return null;
33001
33108
  }
33002
33109
  function readBrowserAssets(pkgDir) {
33003
- const pkgJsonPath = join13(pkgDir, "package.json");
33004
- if (!existsSync11(pkgJsonPath))
33110
+ const pkgJsonPath = join14(pkgDir, "package.json");
33111
+ if (!existsSync12(pkgJsonPath))
33005
33112
  return [];
33006
33113
  try {
33007
- const pkg = JSON.parse(readFileSync12(pkgJsonPath, "utf-8"));
33114
+ const pkg = JSON.parse(readFileSync13(pkgJsonPath, "utf-8"));
33008
33115
  const assets = pkg.arc?.browserAssets;
33009
33116
  if (!Array.isArray(assets))
33010
33117
  return [];
@@ -33014,14 +33121,14 @@ function readBrowserAssets(pkgDir) {
33014
33121
  }
33015
33122
  }
33016
33123
  function discoverBrowserAssets(ws) {
33017
- const arcDir = join13(ws.rootDir, "node_modules", "@arcote.tech");
33018
- if (!existsSync11(arcDir))
33124
+ const arcDir = join14(ws.rootDir, "node_modules", "@arcote.tech");
33125
+ if (!existsSync12(arcDir))
33019
33126
  return [];
33020
33127
  const out = [];
33021
- for (const entry of readdirSync6(arcDir, { withFileTypes: true })) {
33128
+ for (const entry of readdirSync7(arcDir, { withFileTypes: true })) {
33022
33129
  if (!entry.isDirectory() && !entry.isSymbolicLink())
33023
33130
  continue;
33024
- const pkgDir = join13(arcDir, entry.name);
33131
+ const pkgDir = join14(arcDir, entry.name);
33025
33132
  const assets = readBrowserAssets(pkgDir);
33026
33133
  for (const asset of assets) {
33027
33134
  const src = resolveAssetSource(asset.from, pkgDir, ws.rootDir);
@@ -33046,7 +33153,7 @@ async function copyBrowserAssets(ws, cache, noCache) {
33046
33153
  to: a.to,
33047
33154
  mtime: mtimeOf(a.src)
33048
33155
  })));
33049
- const requiredOutputs = assets.map((a) => join13(ws.assetsDir, a.to));
33156
+ const requiredOutputs = assets.map((a) => join14(ws.assetsDir, a.to));
33050
33157
  if (!noCache && isCacheHit(cache, unitId, inputHash, requiredOutputs)) {
33051
33158
  console.log(` \u2713 cached: browser-assets (${assets.length})`);
33052
33159
  return;
@@ -33054,10 +33161,10 @@ async function copyBrowserAssets(ws, cache, noCache) {
33054
33161
  console.log(` building: browser-assets (${assets.length})`);
33055
33162
  const outputHashes = {};
33056
33163
  for (const asset of assets) {
33057
- const dest = join13(ws.assetsDir, asset.to);
33164
+ const dest = join14(ws.assetsDir, asset.to);
33058
33165
  mkdirSync10(dirname8(dest), { recursive: true });
33059
33166
  copyFileSync(asset.src, dest);
33060
- outputHashes[asset.to] = sha256Hex(readFileSync12(dest));
33167
+ outputHashes[asset.to] = sha256Hex(readFileSync13(dest));
33061
33168
  }
33062
33169
  updateCache(cache, unitId, inputHash, { outputHashes });
33063
33170
  }
@@ -33065,12 +33172,12 @@ async function loadServerContext(ws) {
33065
33172
  globalThis.ONLY_SERVER = true;
33066
33173
  globalThis.ONLY_BROWSER = false;
33067
33174
  globalThis.ONLY_CLIENT = false;
33068
- const platformDir = join13(process.cwd(), "node_modules", "@arcote.tech", "platform");
33069
- const platformPkg = JSON.parse(readFileSync12(join13(platformDir, "package.json"), "utf-8"));
33070
- const platformEntry = join13(platformDir, platformPkg.main ?? "src/index.ts");
33175
+ const platformDir = join14(process.cwd(), "node_modules", "@arcote.tech", "platform");
33176
+ const platformPkg = JSON.parse(readFileSync13(join14(platformDir, "package.json"), "utf-8"));
33177
+ const platformEntry = join14(platformDir, platformPkg.main ?? "src/index.ts");
33071
33178
  await import(platformEntry);
33072
- const serverEntry = join13(ws.arcDir, "server", SERVER_ENTRY_FILE);
33073
- if (!existsSync11(serverEntry)) {
33179
+ const serverEntry = join14(ws.arcDir, "server", SERVER_ENTRY_FILE);
33180
+ if (!existsSync12(serverEntry)) {
33074
33181
  return { context: null, moduleAccess: new Map };
33075
33182
  }
33076
33183
  let serverError;
@@ -33100,21 +33207,21 @@ async function platformBuild(opts = {}) {
33100
33207
 
33101
33208
  // src/commands/platform-deploy.ts
33102
33209
  import { randomBytes } from "crypto";
33103
- import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
33104
- import { dirname as dirname11, join as join21 } from "path";
33210
+ import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
33211
+ import { dirname as dirname11, join as join22 } from "path";
33105
33212
  import { fileURLToPath as fileURLToPath8 } from "url";
33106
33213
 
33107
33214
  // src/deploy/bootstrap.ts
33108
33215
  var {spawn: spawn4 } = globalThis.Bun;
33109
33216
  import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync14 } from "fs";
33110
33217
  import { tmpdir as tmpdir2 } from "os";
33111
- import { dirname as dirname9, join as join19 } from "path";
33218
+ import { dirname as dirname9, join as join20 } from "path";
33112
33219
 
33113
33220
  // src/deploy/ansible.ts
33114
33221
  import { spawn as nodeSpawn } from "child_process";
33115
- import { existsSync as existsSync12, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
33222
+ import { existsSync as existsSync13, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
33116
33223
  import { homedir, tmpdir } from "os";
33117
- import { join as join14 } from "path";
33224
+ import { join as join15 } from "path";
33118
33225
 
33119
33226
  // src/deploy/assets.ts
33120
33227
  var TERRAFORM_MAIN_TF = `terraform {
@@ -33428,28 +33535,28 @@ var ASSETS = {
33428
33535
  };
33429
33536
  async function materializeAssets(targetDir, files) {
33430
33537
  const { mkdirSync: mkdirSync11, writeFileSync: writeFileSync11 } = await import("fs");
33431
- const { join: join14 } = await import("path");
33538
+ const { join: join15 } = await import("path");
33432
33539
  mkdirSync11(targetDir, { recursive: true });
33433
33540
  for (const [name, content] of Object.entries(files)) {
33434
- writeFileSync11(join14(targetDir, name), content);
33541
+ writeFileSync11(join15(targetDir, name), content);
33435
33542
  }
33436
33543
  }
33437
33544
 
33438
33545
  // src/deploy/ansible.ts
33439
33546
  function pickSshKeyForAnsible(configured) {
33440
33547
  if (configured) {
33441
- const expanded = configured.startsWith("~") ? join14(homedir(), configured.slice(1)) : configured;
33442
- return existsSync12(expanded) ? expanded : null;
33548
+ const expanded = configured.startsWith("~") ? join15(homedir(), configured.slice(1)) : configured;
33549
+ return existsSync13(expanded) ? expanded : null;
33443
33550
  }
33444
33551
  for (const name of ["id_ed25519", "id_ecdsa", "id_rsa"]) {
33445
- const path4 = join14(homedir(), ".ssh", name);
33446
- if (existsSync12(path4))
33552
+ const path4 = join15(homedir(), ".ssh", name);
33553
+ if (existsSync13(path4))
33447
33554
  return path4;
33448
33555
  }
33449
33556
  return null;
33450
33557
  }
33451
33558
  async function runAnsible(inputs) {
33452
- const workDir = join14(tmpdir(), "arc-deploy", `ansible-${Date.now()}`);
33559
+ const workDir = join15(tmpdir(), "arc-deploy", `ansible-${Date.now()}`);
33453
33560
  mkdirSync11(workDir, { recursive: true });
33454
33561
  await materializeAssets(workDir, ASSETS.ansible);
33455
33562
  const user = inputs.asRoot ? "root" : inputs.target.user;
@@ -33466,7 +33573,7 @@ async function runAnsible(inputs) {
33466
33573
  ""
33467
33574
  ].join(`
33468
33575
  `);
33469
- writeFileSync11(join14(workDir, "inventory.ini"), inventory);
33576
+ writeFileSync11(join15(workDir, "inventory.ini"), inventory);
33470
33577
  const extraVarsJson = JSON.stringify({
33471
33578
  username: inputs.target.user,
33472
33579
  ssh_port: port,
@@ -35026,16 +35133,16 @@ function panelTimeseries(title, gridPos, query, legend, unit, datasource = PROME
35026
35133
  // src/deploy/terraform.ts
35027
35134
  import { spawn as nodeSpawn2 } from "child_process";
35028
35135
  import { createHash as createHash2 } from "crypto";
35029
- import { existsSync as existsSync13, mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "fs";
35136
+ import { existsSync as existsSync14, mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "fs";
35030
35137
  import { homedir as homedir2 } from "os";
35031
- import { join as join15 } from "path";
35138
+ import { join as join16 } from "path";
35032
35139
  async function runTerraform(inputs) {
35033
35140
  const wsHash = createHash2("sha256").update(inputs.workspaceDir).digest("hex").slice(0, 16);
35034
- const workDir = join15(homedir2(), ".arc-deploy", wsHash, "tf");
35141
+ const workDir = join16(homedir2(), ".arc-deploy", wsHash, "tf");
35035
35142
  mkdirSync12(workDir, { recursive: true });
35036
35143
  await materializeAssets(workDir, ASSETS.terraform);
35037
35144
  const sshPubKey = inputs.tf.sshPublicKey ?? expandHome("~/.ssh/id_ed25519.pub");
35038
- if (!existsSync13(expandHome(sshPubKey))) {
35145
+ if (!existsSync14(expandHome(sshPubKey))) {
35039
35146
  throw new Error(`SSH public key not found at ${sshPubKey}. Set provision.terraform.sshPublicKey in deploy.arc.json.`);
35040
35147
  }
35041
35148
  const tfvars = [
@@ -35049,7 +35156,7 @@ async function runTerraform(inputs) {
35049
35156
  ].join(`
35050
35157
  `) + `
35051
35158
  `;
35052
- writeFileSync12(join15(workDir, "terraform.tfvars"), tfvars);
35159
+ writeFileSync12(join16(workDir, "terraform.tfvars"), tfvars);
35053
35160
  await runTf(workDir, ["init", "-input=false", "-no-color"]);
35054
35161
  await runTf(workDir, [
35055
35162
  "apply",
@@ -35107,20 +35214,20 @@ function expandHome(p) {
35107
35214
  }
35108
35215
 
35109
35216
  // src/deploy/config.ts
35110
- import { existsSync as existsSync15, readFileSync as readFileSync14, writeFileSync as writeFileSync13 } from "fs";
35111
- import { join as join17 } from "path";
35217
+ import { existsSync as existsSync16, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
35218
+ import { join as join18 } from "path";
35112
35219
 
35113
35220
  // src/deploy/env-file.ts
35114
- import { appendFileSync, existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
35115
- import { join as join16 } from "path";
35221
+ import { appendFileSync, existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
35222
+ import { join as join17 } from "path";
35116
35223
  function loadDeployEnvFiles(rootDir, envNames) {
35117
- const globalsPath = join16(rootDir, "deploy.arc.env");
35118
- const globals = existsSync14(globalsPath) ? parseEnvFile(readFileSync13(globalsPath, "utf-8"), globalsPath) : {};
35224
+ const globalsPath = join17(rootDir, "deploy.arc.env");
35225
+ const globals = existsSync15(globalsPath) ? parseEnvFile(readFileSync14(globalsPath, "utf-8"), globalsPath) : {};
35119
35226
  const perEnv = {};
35120
35227
  for (const name of envNames) {
35121
- const envPath = join16(rootDir, `deploy.arc.${name}.env`);
35122
- if (existsSync14(envPath)) {
35123
- perEnv[name] = parseEnvFile(readFileSync13(envPath, "utf-8"), envPath);
35228
+ const envPath = join17(rootDir, `deploy.arc.${name}.env`);
35229
+ if (existsSync15(envPath)) {
35230
+ perEnv[name] = parseEnvFile(readFileSync14(envPath, "utf-8"), envPath);
35124
35231
  }
35125
35232
  }
35126
35233
  return { globals, perEnv };
@@ -35136,16 +35243,16 @@ function ensurePersistedSecret(rootDir, scope, key, generate) {
35136
35243
  if (process.env[key])
35137
35244
  return process.env[key];
35138
35245
  const fileName = scope === "globals" ? "deploy.arc.env" : `deploy.arc.${scope}.env`;
35139
- const path4 = join16(rootDir, fileName);
35140
- if (existsSync14(path4)) {
35141
- const existing = parseEnvFile(readFileSync13(path4, "utf-8"), path4);
35246
+ const path4 = join17(rootDir, fileName);
35247
+ if (existsSync15(path4)) {
35248
+ const existing = parseEnvFile(readFileSync14(path4, "utf-8"), path4);
35142
35249
  if (existing[key]) {
35143
35250
  process.env[key] = existing[key];
35144
35251
  return existing[key];
35145
35252
  }
35146
35253
  }
35147
35254
  const value = generate();
35148
- const prefix = existsSync14(path4) && !readFileSync13(path4, "utf-8").endsWith(`
35255
+ const prefix = existsSync15(path4) && !readFileSync14(path4, "utf-8").endsWith(`
35149
35256
  `) ? `
35150
35257
  ` : "";
35151
35258
  appendFileSync(path4, `${prefix}${key}=${value}
@@ -35181,17 +35288,17 @@ function parseEnvFile(content, pathForErrors) {
35181
35288
  // src/deploy/config.ts
35182
35289
  var DEPLOY_CONFIG_FILE = "deploy.arc.json";
35183
35290
  function deployConfigPath(rootDir) {
35184
- return join17(rootDir, DEPLOY_CONFIG_FILE);
35291
+ return join18(rootDir, DEPLOY_CONFIG_FILE);
35185
35292
  }
35186
35293
  function deployConfigExists(rootDir) {
35187
- return existsSync15(deployConfigPath(rootDir));
35294
+ return existsSync16(deployConfigPath(rootDir));
35188
35295
  }
35189
35296
  function loadDeployConfig(rootDir) {
35190
35297
  const path4 = deployConfigPath(rootDir);
35191
- if (!existsSync15(path4)) {
35298
+ if (!existsSync16(path4)) {
35192
35299
  throw new Error(`Missing ${DEPLOY_CONFIG_FILE} at ${path4}`);
35193
35300
  }
35194
- const raw = readFileSync14(path4, "utf-8");
35301
+ const raw = readFileSync15(path4, "utf-8");
35195
35302
  let parsed;
35196
35303
  try {
35197
35304
  parsed = JSON.parse(raw);
@@ -35214,7 +35321,7 @@ function loadDeployConfig(rootDir) {
35214
35321
  }
35215
35322
  function saveDeployConfig(rootDir, cfg) {
35216
35323
  const path4 = deployConfigPath(rootDir);
35217
- const raw = existsSync15(path4) ? JSON.parse(readFileSync14(path4, "utf-8")) : {};
35324
+ const raw = existsSync16(path4) ? JSON.parse(readFileSync15(path4, "utf-8")) : {};
35218
35325
  raw.target = { ...raw.target, ...cfg.target };
35219
35326
  writeFileSync13(path4, JSON.stringify(raw, null, 2) + `
35220
35327
  `);
@@ -35451,17 +35558,17 @@ function cfgErr(path4, expected) {
35451
35558
 
35452
35559
  // src/deploy/ssh.ts
35453
35560
  var {spawn: spawn3 } = globalThis.Bun;
35454
- import { existsSync as existsSync16 } from "fs";
35561
+ import { existsSync as existsSync17 } from "fs";
35455
35562
  import { homedir as homedir3 } from "os";
35456
- import { join as join18 } from "path";
35563
+ import { join as join19 } from "path";
35457
35564
  function pickSshKey(target) {
35458
35565
  if (target.sshKey) {
35459
- const expanded = target.sshKey.startsWith("~") ? join18(homedir3(), target.sshKey.slice(1)) : target.sshKey;
35460
- return existsSync16(expanded) ? expanded : null;
35566
+ const expanded = target.sshKey.startsWith("~") ? join19(homedir3(), target.sshKey.slice(1)) : target.sshKey;
35567
+ return existsSync17(expanded) ? expanded : null;
35461
35568
  }
35462
35569
  for (const name of ["id_ed25519", "id_ecdsa", "id_rsa"]) {
35463
- const path4 = join18(homedir3(), ".ssh", name);
35464
- if (existsSync16(path4))
35570
+ const path4 = join19(homedir3(), ".ssh", name);
35571
+ if (existsSync17(path4))
35465
35572
  return path4;
35466
35573
  }
35467
35574
  return null;
@@ -35476,7 +35583,7 @@ function sshMuxArgs() {
35476
35583
  "-o",
35477
35584
  "ControlMaster=auto",
35478
35585
  "-o",
35479
- `ControlPath=${join18(homedir3(), ".ssh", "cm-arc-%C")}`,
35586
+ `ControlPath=${join19(homedir3(), ".ssh", "cm-arc-%C")}`,
35480
35587
  "-o",
35481
35588
  "ControlPersist=120"
35482
35589
  ];
@@ -35708,7 +35815,8 @@ async function bootstrap(inputs) {
35708
35815
  });
35709
35816
  ok("Host bootstrapped");
35710
35817
  }
35711
- const needUpStack = state.kind !== "ready" || state.marker === null || state.marker.configHash !== inputs.configHash || state.marker.cliVersion !== inputs.cliVersion || !await isRegistryRunning(cfg);
35818
+ const artifactsHash = new Bun.CryptoHasher("sha256").update(generateCaddyfile(cfg)).update(generateCompose({ cfg })).digest("hex");
35819
+ const needUpStack = state.kind !== "ready" || state.marker === null || state.marker.configHash !== inputs.configHash || state.marker.cliVersion !== inputs.cliVersion || state.marker.artifactsHash !== artifactsHash || inputs.forceAnsible === true || !await isRegistryRunning(cfg);
35712
35820
  if (needUpStack) {
35713
35821
  await upStack(inputs);
35714
35822
  ok("Docker stack up");
@@ -35722,6 +35830,7 @@ async function bootstrap(inputs) {
35722
35830
  await writeStateMarker(cfg.target, {
35723
35831
  cliVersion: inputs.cliVersion,
35724
35832
  configHash: inputs.configHash,
35833
+ artifactsHash,
35725
35834
  updatedAt: new Date().toISOString()
35726
35835
  });
35727
35836
  }
@@ -35745,7 +35854,7 @@ async function isRegistryRunning(cfg) {
35745
35854
  }
35746
35855
  async function upStack(inputs) {
35747
35856
  const { cfg } = inputs;
35748
- const workDir = join19(tmpdir2(), "arc-deploy", `stack-${Date.now()}`);
35857
+ const workDir = join20(tmpdir2(), "arc-deploy", `stack-${Date.now()}`);
35749
35858
  mkdirSync13(workDir, { recursive: true });
35750
35859
  await assertRegistryDnsResolves(cfg);
35751
35860
  const password = process.env[cfg.registry.passwordEnv];
@@ -35753,9 +35862,9 @@ async function upStack(inputs) {
35753
35862
  throw new Error(`Registry password env var ${cfg.registry.passwordEnv} is not set. ` + `Set it (e.g. \`export ${cfg.registry.passwordEnv}=...\`) before bootstrap.`);
35754
35863
  }
35755
35864
  const htpasswdLine = await generateHtpasswd(cfg.registry.username, password);
35756
- writeFileSync14(join19(workDir, "htpasswd"), htpasswdLine);
35757
- writeFileSync14(join19(workDir, "Caddyfile"), generateCaddyfile(cfg));
35758
- writeFileSync14(join19(workDir, "docker-compose.yml"), generateCompose({ cfg }));
35865
+ writeFileSync14(join20(workDir, "htpasswd"), htpasswdLine);
35866
+ writeFileSync14(join20(workDir, "Caddyfile"), generateCaddyfile(cfg));
35867
+ writeFileSync14(join20(workDir, "docker-compose.yml"), generateCompose({ cfg }));
35759
35868
  let observabilityFiles = null;
35760
35869
  let observabilityHtpasswd = null;
35761
35870
  if (cfg.observability?.enabled) {
@@ -35767,30 +35876,30 @@ async function upStack(inputs) {
35767
35876
  }
35768
35877
  observabilityHtpasswd = await generateCaddyBasicAuthLine("admin", adminPassword);
35769
35878
  for (const [relPath, contents] of Object.entries(observabilityFiles)) {
35770
- const fullPath = join19(workDir, relPath);
35879
+ const fullPath = join20(workDir, relPath);
35771
35880
  mkdirSync13(dirname9(fullPath), { recursive: true });
35772
35881
  writeFileSync14(fullPath, contents);
35773
35882
  }
35774
- writeFileSync14(join19(workDir, "observability-htpasswd"), observabilityHtpasswd);
35883
+ writeFileSync14(join20(workDir, "observability-htpasswd"), observabilityHtpasswd);
35775
35884
  }
35776
35885
  await assertExec(cfg.target, `sudo mkdir -p ${cfg.target.remoteDir} && sudo chown ${cfg.target.user}:${cfg.target.user} ${cfg.target.remoteDir}`);
35777
35886
  for (const name of Object.keys(cfg.envs)) {
35778
35887
  await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/${name}`);
35779
35888
  }
35780
35889
  await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/registry-auth`);
35781
- await scpUpload(cfg.target, join19(workDir, "Caddyfile"), `${cfg.target.remoteDir}/Caddyfile`);
35782
- await scpUpload(cfg.target, join19(workDir, "docker-compose.yml"), `${cfg.target.remoteDir}/docker-compose.yml`);
35783
- await scpUpload(cfg.target, join19(workDir, "htpasswd"), `${cfg.target.remoteDir}/registry-auth/htpasswd`);
35890
+ await scpUpload(cfg.target, join20(workDir, "Caddyfile"), `${cfg.target.remoteDir}/Caddyfile`);
35891
+ await scpUpload(cfg.target, join20(workDir, "docker-compose.yml"), `${cfg.target.remoteDir}/docker-compose.yml`);
35892
+ await scpUpload(cfg.target, join20(workDir, "htpasswd"), `${cfg.target.remoteDir}/registry-auth/htpasswd`);
35784
35893
  if (observabilityFiles && observabilityHtpasswd) {
35785
35894
  await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/observability/grafana-dashboards ${cfg.target.remoteDir}/observability/grafana-alerting`);
35786
35895
  for (const relPath of Object.keys(observabilityFiles)) {
35787
- const localDir = dirname9(join19(workDir, relPath));
35896
+ const localDir = dirname9(join20(workDir, relPath));
35788
35897
  mkdirSync13(localDir, { recursive: true });
35789
35898
  }
35790
35899
  for (const relPath of Object.keys(observabilityFiles)) {
35791
- await scpUpload(cfg.target, join19(workDir, relPath), `${cfg.target.remoteDir}/${relPath}`);
35900
+ await scpUpload(cfg.target, join20(workDir, relPath), `${cfg.target.remoteDir}/${relPath}`);
35792
35901
  }
35793
- await scpUpload(cfg.target, join19(workDir, "observability-htpasswd"), `${cfg.target.remoteDir}/observability-htpasswd`);
35902
+ await scpUpload(cfg.target, join20(workDir, "observability-htpasswd"), `${cfg.target.remoteDir}/observability-htpasswd`);
35794
35903
  }
35795
35904
  await assertExec(cfg.target, `touch ${cfg.target.remoteDir}/.env`);
35796
35905
  if (cfg.observability?.enabled) {
@@ -35812,6 +35921,7 @@ async function upStack(inputs) {
35812
35921
  await writeEnvLine(cfg.target, `${cfg.target.remoteDir}/.env`, key, value);
35813
35922
  }
35814
35923
  await assertExec(cfg.target, `cd ${cfg.target.remoteDir} && docker compose pull --ignore-pull-failures caddy registry && docker compose up -d caddy registry`);
35924
+ await assertExec(cfg.target, `cd ${cfg.target.remoteDir} && docker compose restart caddy`);
35815
35925
  await sshDockerLogin(cfg);
35816
35926
  const knownEnvs = await listConfiguredEnvs(cfg);
35817
35927
  if (knownEnvs.length > 0) {
@@ -35945,14 +36055,14 @@ var {spawn: spawn5 } = globalThis.Bun;
35945
36055
  import { createHash as createHash3 } from "crypto";
35946
36056
  import {
35947
36057
  copyFileSync as copyFileSync2,
35948
- existsSync as existsSync17,
36058
+ existsSync as existsSync18,
35949
36059
  mkdirSync as mkdirSync14,
35950
- readFileSync as readFileSync15,
36060
+ readFileSync as readFileSync16,
35951
36061
  realpathSync as realpathSync2,
35952
36062
  writeFileSync as writeFileSync15
35953
36063
  } from "fs";
35954
36064
  import { tmpdir as tmpdir3 } from "os";
35955
- import { dirname as dirname10, join as join20 } from "path";
36065
+ import { dirname as dirname10, join as join21 } from "path";
35956
36066
  import { fileURLToPath as fileURLToPath7 } from "url";
35957
36067
 
35958
36068
  // src/deploy/image-template.ts
@@ -36002,8 +36112,8 @@ function generateDockerfile(inputs) {
36002
36112
  // src/deploy/image.ts
36003
36113
  async function buildImage(ws, opts) {
36004
36114
  await ensureDocker();
36005
- const manifestPath = join20(ws.arcDir, "manifest.json");
36006
- if (!existsSync17(manifestPath)) {
36115
+ const manifestPath = join21(ws.arcDir, "manifest.json");
36116
+ if (!existsSync18(manifestPath)) {
36007
36117
  throw new Error(`No build manifest at ${manifestPath}. Run \`arc platform build\` first or omit --skip-build.`);
36008
36118
  }
36009
36119
  embedCliBundle(ws);
@@ -36013,9 +36123,9 @@ async function buildImage(ws, opts) {
36013
36123
  const dockerfileInputs = collectDockerfileInputs(ws);
36014
36124
  const dockerfile = generateDockerfile(dockerfileInputs);
36015
36125
  const buildContextDir = ws.rootDir;
36016
- const dockerfileDir = join20(tmpdir3(), `arc-image-${Date.now()}`);
36126
+ const dockerfileDir = join21(tmpdir3(), `arc-image-${Date.now()}`);
36017
36127
  mkdirSync14(dockerfileDir, { recursive: true });
36018
- const dockerfilePath = join20(dockerfileDir, "Dockerfile");
36128
+ const dockerfilePath = join21(dockerfileDir, "Dockerfile");
36019
36129
  writeFileSync15(dockerfilePath, dockerfile);
36020
36130
  const buildArgs = [
36021
36131
  "build",
@@ -36041,20 +36151,20 @@ async function buildImage(ws, opts) {
36041
36151
  }
36042
36152
  function embedCliBundle(ws) {
36043
36153
  const source = locateCliBundle();
36044
- const target = join20(ws.arcDir, "host.js");
36154
+ const target = join21(ws.arcDir, "host.js");
36045
36155
  copyFileSync2(source, target);
36046
36156
  }
36047
36157
  function locateCliBundle() {
36048
36158
  const here = fileURLToPath7(import.meta.url);
36049
36159
  let cur = dirname10(here);
36050
36160
  while (cur !== "/" && cur !== "") {
36051
- const candidate = join20(cur, "package.json");
36052
- if (existsSync17(candidate)) {
36161
+ const candidate = join21(cur, "package.json");
36162
+ if (existsSync18(candidate)) {
36053
36163
  try {
36054
- const pkg = JSON.parse(readFileSync15(candidate, "utf-8"));
36164
+ const pkg = JSON.parse(readFileSync16(candidate, "utf-8"));
36055
36165
  if (pkg.name === "@arcote.tech/arc-cli") {
36056
- const distIndex = join20(realpathSync2(cur), "dist", "index.js");
36057
- if (!existsSync17(distIndex)) {
36166
+ const distIndex = join21(realpathSync2(cur), "dist", "index.js");
36167
+ if (!existsSync18(distIndex)) {
36058
36168
  throw new Error(`arc-cli bundle missing at ${distIndex}. Run \`bun run build\` in packages/cli/.`);
36059
36169
  }
36060
36170
  return distIndex;
@@ -36086,17 +36196,17 @@ async function ensureDocker() {
36086
36196
  }
36087
36197
  }
36088
36198
  function computeContentHash(manifestPath) {
36089
- const raw = JSON.parse(readFileSync15(manifestPath, "utf-8"));
36199
+ const raw = JSON.parse(readFileSync16(manifestPath, "utf-8"));
36090
36200
  delete raw.buildTime;
36091
36201
  const canonical = JSON.stringify(raw);
36092
36202
  return createHash3("sha256").update(canonical).digest("hex").slice(0, 12);
36093
36203
  }
36094
36204
  function collectDockerfileInputs(ws) {
36095
- const hasPublicDir = existsSync17(join20(ws.rootDir, "public"));
36096
- const hasLocales = existsSync17(join20(ws.rootDir, "locales"));
36205
+ const hasPublicDir = existsSync18(join21(ws.rootDir, "public"));
36206
+ const hasLocales = existsSync18(join21(ws.rootDir, "locales"));
36097
36207
  let manifestPath;
36098
36208
  for (const name of ["manifest.webmanifest", "manifest.json"]) {
36099
- if (existsSync17(join20(ws.rootDir, name))) {
36209
+ if (existsSync18(join21(ws.rootDir, name))) {
36100
36210
  manifestPath = name;
36101
36211
  break;
36102
36212
  }
@@ -36940,14 +37050,14 @@ async function platformDeploy(envArg, options = {}) {
36940
37050
  err(`Unknown env "${envArg}". Known: ${Object.keys(cfg.envs).join(", ")}`);
36941
37051
  process.exit(1);
36942
37052
  })() : Object.keys(cfg.envs);
36943
- const manifestPath = join21(ws.arcDir, "manifest.json");
37053
+ const manifestPath = join22(ws.arcDir, "manifest.json");
36944
37054
  if (!options.imageTag) {
36945
- const needBuild = options.rebuild || !existsSync18(manifestPath);
37055
+ const needBuild = options.rebuild || !existsSync19(manifestPath);
36946
37056
  if (needBuild && !options.skipBuild) {
36947
37057
  log2("Building platform...");
36948
37058
  await buildAll(ws, { noCache: options.rebuild });
36949
37059
  ok("Build complete");
36950
- } else if (!existsSync18(manifestPath)) {
37060
+ } else if (!existsSync19(manifestPath)) {
36951
37061
  err("No build found and --skip-build was set.");
36952
37062
  process.exit(1);
36953
37063
  }
@@ -37017,9 +37127,9 @@ function readCliVersion2() {
37017
37127
  let cur = dirname11(fileURLToPath8(import.meta.url));
37018
37128
  const root = dirname11(cur).startsWith("/") ? "/" : ".";
37019
37129
  while (cur !== root && cur !== "") {
37020
- const candidate = join21(cur, "package.json");
37021
- if (existsSync18(candidate)) {
37022
- const pkg = JSON.parse(readFileSync16(candidate, "utf-8"));
37130
+ const candidate = join22(cur, "package.json");
37131
+ if (existsSync19(candidate)) {
37132
+ const pkg = JSON.parse(readFileSync17(candidate, "utf-8"));
37023
37133
  if (pkg.name === "@arcote.tech/arc-cli") {
37024
37134
  return pkg.version ?? "unknown";
37025
37135
  }
@@ -37035,8 +37145,8 @@ function readCliVersion2() {
37035
37145
  }
37036
37146
  }
37037
37147
  async function hashDeployConfig(rootDir) {
37038
- const p2 = join21(rootDir, "deploy.arc.json");
37039
- const content = readFileSync16(p2);
37148
+ const p2 = join22(rootDir, "deploy.arc.json");
37149
+ const content = readFileSync17(p2);
37040
37150
  const hasher = new Bun.CryptoHasher("sha256");
37041
37151
  hasher.update(content);
37042
37152
  hasher.update(readCliVersion2());
@@ -37045,8 +37155,8 @@ async function hashDeployConfig(rootDir) {
37045
37155
 
37046
37156
  // src/platform/startup.ts
37047
37157
  import { randomBytes as randomBytes2 } from "crypto";
37048
- import { existsSync as existsSync20, mkdirSync as mkdirSync16, readFileSync as readFileSync17, watch, writeFileSync as writeFileSync16 } from "fs";
37049
- import { join as join23 } from "path";
37158
+ import { existsSync as existsSync21, mkdirSync as mkdirSync16, readFileSync as readFileSync19, watch, writeFileSync as writeFileSync16 } from "fs";
37159
+ import { join as join24 } from "path";
37050
37160
 
37051
37161
  // ../host/src/connection-manager.ts
37052
37162
  class ConnectionManager {
@@ -38675,8 +38785,8 @@ async function createArcServer(config) {
38675
38785
  // src/platform/server.ts
38676
38786
  init_i18n();
38677
38787
  import { timingSafeEqual } from "crypto";
38678
- import { existsSync as existsSync19, mkdirSync as mkdirSync15 } from "fs";
38679
- import { join as join22 } from "path";
38788
+ import { existsSync as existsSync20, mkdirSync as mkdirSync15, readFileSync as readFileSync18 } from "fs";
38789
+ import { join as join23 } from "path";
38680
38790
  async function initContextHandler(context2, dbPath) {
38681
38791
  const factory = await resolveDbAdapterFactory(dbPath);
38682
38792
  const dbAdapter = factory(context2);
@@ -38696,7 +38806,61 @@ async function resolveDbAdapterFactory(dbPath) {
38696
38806
  mkdirSync15(dbDir, { recursive: true });
38697
38807
  return createBunSQLiteAdapterFactory2(dbPath);
38698
38808
  }
38699
- function generateShellHtml(appName, manifest, initial, stylesHash, shell) {
38809
+ function inlineJson(value) {
38810
+ return JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/[\u2028\u2029]/g, (c2) => `\\u${c2.charCodeAt(0).toString(16)}`);
38811
+ }
38812
+ function tokenPayloadsFromCookies(cookieHeader) {
38813
+ if (!cookieHeader)
38814
+ return [];
38815
+ const payloads = [];
38816
+ for (const part of cookieHeader.split(";")) {
38817
+ const eq = part.indexOf("=");
38818
+ if (eq < 0)
38819
+ continue;
38820
+ const name = part.slice(0, eq).trim();
38821
+ if (name !== "arc_token" && !name.startsWith("arc_token_"))
38822
+ continue;
38823
+ const payload = decodeTokenPayload(decodeURIComponent(part.slice(eq + 1).trim()));
38824
+ if (payload)
38825
+ payloads.push(payload);
38826
+ }
38827
+ return payloads;
38828
+ }
38829
+ function pickLocale(cookieHeader, acceptLanguage, locales, sourceLocale) {
38830
+ const fromCookie = cookieHeader?.match(/(?:^|;)\s*arc-locale=([^;]+)/)?.[1];
38831
+ if (fromCookie && locales.includes(decodeURIComponent(fromCookie))) {
38832
+ return decodeURIComponent(fromCookie);
38833
+ }
38834
+ for (const tag of (acceptLanguage ?? "").split(",")) {
38835
+ const lang = tag.split(";")[0].trim();
38836
+ if (!lang)
38837
+ continue;
38838
+ const exact = locales.find((l2) => l2.toLowerCase() === lang.toLowerCase());
38839
+ if (exact)
38840
+ return exact;
38841
+ const prefix = locales.find((l2) => l2.toLowerCase().split("-")[0] === lang.toLowerCase().split("-")[0]);
38842
+ if (prefix)
38843
+ return prefix;
38844
+ }
38845
+ return sourceLocale;
38846
+ }
38847
+ async function generateShellHtml(opts) {
38848
+ const {
38849
+ appName,
38850
+ appManifest,
38851
+ buildManifest,
38852
+ moduleAccessMap,
38853
+ arcDir,
38854
+ rootDir,
38855
+ cookieHeader,
38856
+ acceptLanguage
38857
+ } = opts;
38858
+ if (!buildManifest?.initial) {
38859
+ throw new Error("generateShellHtml: initial bundle missing from manifest");
38860
+ }
38861
+ const initial = buildManifest.initial;
38862
+ const initialUrl = `/browser/${initial.file}`;
38863
+ const shell = buildManifest.shell;
38700
38864
  const otelConfig = process.env.ARC_OTEL_ENABLED === "true" ? {
38701
38865
  enabled: true,
38702
38866
  endpoint: "/otel",
@@ -38705,26 +38869,58 @@ function generateShellHtml(appName, manifest, initial, stylesHash, shell) {
38705
38869
  sampleRate: Number(process.env.ARC_OTEL_BROWSER_SAMPLE_RATE ?? "0.1")
38706
38870
  } : null;
38707
38871
  const otelTag = otelConfig ? `
38708
- <script>window.__ARC_OTEL_CONFIG=${JSON.stringify(otelConfig)};</script>` : "";
38709
- const initialUrl = initial ? `/browser/${initial.file}` : null;
38710
- if (!initialUrl) {
38711
- throw new Error("generateShellHtml: initial bundle missing from manifest");
38712
- }
38872
+ <script>window.__ARC_OTEL_CONFIG=${inlineJson(otelConfig)};</script>` : "";
38713
38873
  const importMapTag = shell ? `
38714
- <script type="importmap">${JSON.stringify({ imports: shell.imports })}</script>
38715
- <script>window.__ARC_PEERS__=${JSON.stringify(shell.peers)};</script>` : "";
38716
- const stylesQs = stylesHash ? `?v=${stylesHash.slice(0, 16)}` : "";
38874
+ <script type="importmap">${inlineJson({ imports: shell.imports })}</script>
38875
+ <script>window.__ARC_PEERS__=${inlineJson(shell.peers)};</script>` : "";
38876
+ const stylesQs = buildManifest.stylesHash ? `?v=${buildManifest.stylesHash.slice(0, 16)}` : "";
38877
+ const tokenPayloads = tokenPayloadsFromCookies(cookieHeader);
38878
+ const filtered = await filterManifestForTokens(buildManifest, moduleAccessMap, tokenPayloads);
38879
+ const preloadUrls = new Set;
38880
+ preloadUrls.add(initialUrl);
38881
+ for (const c2 of initial.staticChunks ?? [])
38882
+ preloadUrls.add(`/browser/${c2}`);
38883
+ for (const group of Object.values(filtered.groups)) {
38884
+ if (group.url)
38885
+ preloadUrls.add(group.url);
38886
+ for (const c2 of group.staticChunks ?? [])
38887
+ preloadUrls.add(`/browser/${c2}`);
38888
+ }
38889
+ const preloadTags = [...preloadUrls].map((u2) => `
38890
+ <link rel="modulepreload" href="${u2}" />`).join("");
38891
+ const manifestTag = `
38892
+ <script>window.__ARC_MANIFEST__=${inlineJson({
38893
+ initial: filtered.initial,
38894
+ groups: filtered.groups,
38895
+ sharedChunks: filtered.sharedChunks,
38896
+ stylesHash: filtered.stylesHash,
38897
+ buildTime: filtered.buildTime
38898
+ })};</script>`;
38899
+ let lang = "en";
38900
+ const tconfig = readTranslationsConfig(rootDir);
38901
+ let messages2 = {};
38902
+ if (tconfig) {
38903
+ lang = pickLocale(cookieHeader, acceptLanguage, tconfig.locales, tconfig.sourceLocale);
38904
+ try {
38905
+ messages2 = JSON.parse(readFileSync18(join23(arcDir, "locales", `${lang}.json`), "utf-8"));
38906
+ } catch {}
38907
+ }
38908
+ const i18nTag = `
38909
+ <script>window.__ARC_I18N__=${inlineJson({
38910
+ locale: lang,
38911
+ config: tconfig,
38912
+ messages: messages2
38913
+ })};</script>`;
38717
38914
  return `<!doctype html>
38718
- <html lang="en">
38915
+ <html lang="${lang}">
38719
38916
  <head>
38720
38917
  <meta charset="UTF-8" />
38721
38918
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
38722
- <title>${manifest?.title ?? appName}</title>${manifest?.favicon ? `
38723
- <link rel="icon" href="${manifest.favicon}">` : ""}${manifest ? `
38919
+ <title>${appManifest?.title ?? appName}</title>${appManifest?.favicon ? `
38920
+ <link rel="icon" href="${appManifest.favicon}">` : ""}${appManifest ? `
38724
38921
  <link rel="manifest" href="/manifest.json${stylesQs}">` : ""}
38725
38922
  <link rel="stylesheet" href="/styles.css${stylesQs}" />
38726
- <link rel="stylesheet" href="/theme.css${stylesQs}" />${importMapTag}
38727
- <link rel="modulepreload" href="${initialUrl}" />${otelTag}
38923
+ <link rel="stylesheet" href="/theme.css${stylesQs}" />${importMapTag}${preloadTags}${manifestTag}${i18nTag}${otelTag}
38728
38924
  </head>
38729
38925
  <body>
38730
38926
  <div id="root"></div>
@@ -38755,7 +38951,7 @@ function getMime(path4) {
38755
38951
  return MIME[ext2] ?? "application/octet-stream";
38756
38952
  }
38757
38953
  function serveFile(filePath, headers = {}) {
38758
- if (!existsSync19(filePath))
38954
+ if (!existsSync20(filePath))
38759
38955
  return new Response("Not Found", { status: 404 });
38760
38956
  return new Response(Bun.file(filePath), {
38761
38957
  headers: { "Content-Type": getMime(filePath), ...headers }
@@ -38877,7 +39073,7 @@ function staticFilesHandler(ws, devMode, getManifest) {
38877
39073
  return new Response("Forbidden", { status: 403, headers: ctx.corsHeaders });
38878
39074
  }
38879
39075
  }
38880
- return serveFile(join22(ws.browserDir, file), {
39076
+ return serveFile(join23(ws.browserDir, file), {
38881
39077
  ...ctx.corsHeaders,
38882
39078
  "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
38883
39079
  });
@@ -38887,7 +39083,7 @@ function staticFilesHandler(ws, devMode, getManifest) {
38887
39083
  if (!BROWSER_FILE_RE.test(file)) {
38888
39084
  return new Response("Not Found", { status: 404, headers: ctx.corsHeaders });
38889
39085
  }
38890
- return serveFile(join22(ws.arcDir, "shell", file), {
39086
+ return serveFile(join23(ws.arcDir, "shell", file), {
38891
39087
  ...ctx.corsHeaders,
38892
39088
  "Cross-Origin-Resource-Policy": "cross-origin",
38893
39089
  "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
@@ -38906,29 +39102,29 @@ function staticFilesHandler(ws, devMode, getManifest) {
38906
39102
  return new Response("Forbidden", { status: 403, headers: ctx.corsHeaders });
38907
39103
  }
38908
39104
  }
38909
- return serveFile(join22(ws.arcDir, "browser-fed", file), {
39105
+ return serveFile(join23(ws.arcDir, "browser-fed", file), {
38910
39106
  ...ctx.corsHeaders,
38911
39107
  "Cross-Origin-Resource-Policy": "cross-origin",
38912
39108
  "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
38913
39109
  });
38914
39110
  }
38915
39111
  if (path4.startsWith("/locales/"))
38916
- return serveFile(join22(ws.arcDir, path4.slice(1)), {
39112
+ return serveFile(join23(ws.arcDir, path4.slice(1)), {
38917
39113
  ...ctx.corsHeaders,
38918
39114
  "Cache-Control": devMode ? "no-cache" : "max-age=300,stale-while-revalidate=3600"
38919
39115
  });
38920
39116
  if (path4.startsWith("/assets/"))
38921
- return serveFile(join22(ws.assetsDir, path4.slice(8)), {
39117
+ return serveFile(join23(ws.assetsDir, path4.slice(8)), {
38922
39118
  ...ctx.corsHeaders,
38923
39119
  "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
38924
39120
  });
38925
39121
  if (path4 === "/styles.css")
38926
- return serveFile(join22(ws.arcDir, "styles.css"), {
39122
+ return serveFile(join23(ws.arcDir, "styles.css"), {
38927
39123
  ...ctx.corsHeaders,
38928
39124
  "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
38929
39125
  });
38930
39126
  if (path4 === "/theme.css")
38931
- return serveFile(join22(ws.arcDir, "theme.css"), {
39127
+ return serveFile(join23(ws.arcDir, "theme.css"), {
38932
39128
  ...ctx.corsHeaders,
38933
39129
  "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
38934
39130
  });
@@ -38939,8 +39135,8 @@ function staticFilesHandler(ws, devMode, getManifest) {
38939
39135
  });
38940
39136
  }
38941
39137
  if (path4.lastIndexOf(".") > path4.lastIndexOf("/")) {
38942
- const publicFile = join22(ws.publicDir, path4.slice(1));
38943
- if (existsSync19(publicFile))
39138
+ const publicFile = join23(ws.publicDir, path4.slice(1));
39139
+ if (existsSync20(publicFile))
38944
39140
  return serveFile(publicFile, ctx.corsHeaders);
38945
39141
  }
38946
39142
  return null;
@@ -39140,12 +39336,13 @@ function headlessFallbackHandler(ws, getManifest) {
39140
39336
  };
39141
39337
  }
39142
39338
  function spaFallbackHandler(getShellHtml) {
39143
- return (_req, _url, ctx) => {
39144
- return new Response(getShellHtml(), {
39339
+ return async (req, _url, ctx) => {
39340
+ return new Response(await getShellHtml(req), {
39145
39341
  headers: {
39146
39342
  ...ctx.corsHeaders,
39147
39343
  "Content-Type": "text/html",
39148
- "Cache-Control": "no-cache, must-revalidate"
39344
+ "Cache-Control": "no-cache, must-revalidate, private",
39345
+ Vary: "Cookie, Accept-Language"
39149
39346
  }
39150
39347
  });
39151
39348
  };
@@ -39186,7 +39383,16 @@ async function startPlatformServer(opts) {
39186
39383
  mapUrl = u2;
39187
39384
  };
39188
39385
  const getMapUrl = () => mapUrl;
39189
- const getShellHtml = () => generateShellHtml(ws.appName, ws.manifest, manifest?.initial, manifest?.stylesHash, manifest?.shell);
39386
+ const getShellHtml = (req) => generateShellHtml({
39387
+ appName: ws.appName,
39388
+ appManifest: ws.manifest,
39389
+ buildManifest: manifest,
39390
+ moduleAccessMap,
39391
+ arcDir: ws.arcDir,
39392
+ rootDir: ws.rootDir,
39393
+ cookieHeader: req.headers.get("Cookie"),
39394
+ acceptLanguage: req.headers.get("Accept-Language")
39395
+ });
39190
39396
  const sseClients = new Set;
39191
39397
  const fallbackHandler = opts.headless ? headlessFallbackHandler(ws, getManifest) : spaFallbackHandler(getShellHtml);
39192
39398
  const notifyReload = (m4) => {
@@ -39250,7 +39456,7 @@ async function startPlatformServer(opts) {
39250
39456
  stop: () => server.stop()
39251
39457
  };
39252
39458
  }
39253
- const dbPath = opts.dbPath || join22(ws.arcDir, "data", "arc.db");
39459
+ const dbPath = opts.dbPath || join23(ws.arcDir, "data", "arc.db");
39254
39460
  const baseDbFactory = await resolveDbAdapterFactory(dbPath);
39255
39461
  let dbAdapterFactory = baseDbFactory;
39256
39462
  if (telemetry) {
@@ -39295,17 +39501,17 @@ async function startPlatformServer(opts) {
39295
39501
  async function startPlatform(opts) {
39296
39502
  const { ws, devMode } = opts;
39297
39503
  const port = opts.port ?? parseInt(process.env.PORT || "5005", 10);
39298
- const dbPath = opts.dbPath ?? join23(ws.rootDir, ".arc", "data", devMode ? "dev.db" : "prod.db");
39504
+ const dbPath = opts.dbPath ?? join24(ws.rootDir, ".arc", "data", devMode ? "dev.db" : "prod.db");
39299
39505
  let manifest;
39300
39506
  if (devMode) {
39301
39507
  manifest = await buildAll(ws, { minify: false });
39302
39508
  } else {
39303
- const manifestPath = join23(ws.arcDir, "manifest.json");
39304
- if (!existsSync20(manifestPath)) {
39509
+ const manifestPath = join24(ws.arcDir, "manifest.json");
39510
+ if (!existsSync21(manifestPath)) {
39305
39511
  err("No build found. Run `arc platform build` first.");
39306
39512
  process.exit(1);
39307
39513
  }
39308
- manifest = JSON.parse(readFileSync17(manifestPath, "utf-8"));
39514
+ manifest = JSON.parse(readFileSync19(manifestPath, "utf-8"));
39309
39515
  }
39310
39516
  assertProdFederationSecrets(devMode, !!manifest.federation);
39311
39517
  const needsPairing = !!manifest.federation || devMode && (opts.map || opts.headless);
@@ -39389,8 +39595,8 @@ async function startMapTool(ws, port, authToken, platformServer, platform3) {
39389
39595
  try {
39390
39596
  const { startMapServer, MAP_SCHEMA_VERSION } = await import("@arcote.tech/arc-map");
39391
39597
  const { getContext } = await import("@arcote.tech/platform");
39392
- const globs = ws.packages.map((p3) => join23(p3.path, "src", "**", "*.{ts,tsx}"));
39393
- const packageOf = (absFile) => ws.packages.find((p3) => absFile.startsWith(join23(p3.path, "src")))?.name;
39598
+ const globs = ws.packages.map((p3) => join24(p3.path, "src", "**", "*.{ts,tsx}"));
39599
+ const packageOf = (absFile) => ws.packages.find((p3) => absFile.startsWith(join24(p3.path, "src")))?.name;
39394
39600
  const meta = readAppMeta(ws.rootDir, MAP_SCHEMA_VERSION);
39395
39601
  const map = await startMapServer({
39396
39602
  getContext: () => getContext(),
@@ -39425,15 +39631,15 @@ function resolveAppSecret(rootDir) {
39425
39631
  const fromEnv = process.env.ARC_APP_SECRET;
39426
39632
  if (fromEnv)
39427
39633
  return fromEnv;
39428
- const file = join23(rootDir, ".arc", "app-secret");
39634
+ const file = join24(rootDir, ".arc", "app-secret");
39429
39635
  try {
39430
- const saved = readFileSync17(file, "utf-8").trim();
39636
+ const saved = readFileSync19(file, "utf-8").trim();
39431
39637
  if (saved)
39432
39638
  return saved;
39433
39639
  } catch {}
39434
39640
  const secret = randomBytes2(32).toString("base64url");
39435
39641
  try {
39436
- mkdirSync16(join23(rootDir, ".arc"), { recursive: true });
39642
+ mkdirSync16(join24(rootDir, ".arc"), { recursive: true });
39437
39643
  writeFileSync16(file, secret, { mode: 384 });
39438
39644
  } catch (e2) {
39439
39645
  err(`App secret not persisted (${e2.message}) \u2014 hosts will need re-install after restart`);
@@ -39444,15 +39650,15 @@ function resolvePairingToken(rootDir) {
39444
39650
  const fromEnv = process.env.ARC_PAIRING_TOKEN || process.env.ARC_MAP_TOKEN;
39445
39651
  if (fromEnv)
39446
39652
  return fromEnv;
39447
- const file = join23(rootDir, ".arc", "pairing-token");
39653
+ const file = join24(rootDir, ".arc", "pairing-token");
39448
39654
  try {
39449
- const saved = readFileSync17(file, "utf-8").trim();
39655
+ const saved = readFileSync19(file, "utf-8").trim();
39450
39656
  if (saved)
39451
39657
  return saved;
39452
39658
  } catch {}
39453
39659
  const token = randomBytes2(16).toString("base64url");
39454
39660
  try {
39455
- mkdirSync16(join23(rootDir, ".arc"), { recursive: true });
39661
+ mkdirSync16(join24(rootDir, ".arc"), { recursive: true });
39456
39662
  writeFileSync16(file, token, { mode: 384 });
39457
39663
  } catch (e2) {
39458
39664
  err(`Pairing token not persisted (${e2.message}) \u2014 it will change on restart`);
@@ -39461,7 +39667,7 @@ function resolvePairingToken(rootDir) {
39461
39667
  }
39462
39668
  function readAppMeta(rootDir, schemaVersion) {
39463
39669
  try {
39464
- const pkg = JSON.parse(readFileSync17(join23(rootDir, "package.json"), "utf-8"));
39670
+ const pkg = JSON.parse(readFileSync19(join24(rootDir, "package.json"), "utf-8"));
39465
39671
  return {
39466
39672
  schemaVersion,
39467
39673
  name: pkg.name ?? rootDir.split("/").pop() ?? "arc-app",
@@ -39497,8 +39703,8 @@ function attachDevWatcher(ws, platform3, onReload) {
39497
39703
  }, 300);
39498
39704
  };
39499
39705
  for (const pkg of ws.packages) {
39500
- const srcDir = join23(pkg.path, "src");
39501
- if (!existsSync20(srcDir))
39706
+ const srcDir = join24(pkg.path, "src");
39707
+ if (!existsSync21(srcDir))
39502
39708
  continue;
39503
39709
  watch(srcDir, { recursive: true }, (_event, filename) => {
39504
39710
  if (!filename || filename.includes(".arc") || filename.endsWith(".d.ts") || filename.includes("node_modules") || filename.includes("dist"))
@@ -39508,8 +39714,8 @@ function attachDevWatcher(ws, platform3, onReload) {
39508
39714
  triggerRebuild();
39509
39715
  });
39510
39716
  }
39511
- const localesDir = join23(ws.rootDir, "locales");
39512
- if (existsSync20(localesDir)) {
39717
+ const localesDir = join24(ws.rootDir, "locales");
39718
+ if (existsSync21(localesDir)) {
39513
39719
  watch(localesDir, { recursive: false }, (_event, filename) => {
39514
39720
  if (!filename?.endsWith(".po"))
39515
39721
  return;
@@ -39531,7 +39737,7 @@ async function platformDev(opts = {}) {
39531
39737
 
39532
39738
  // src/commands/platform-pairing-token.ts
39533
39739
  import { randomBytes as randomBytes3 } from "crypto";
39534
- import { join as join24 } from "path";
39740
+ import { join as join25 } from "path";
39535
39741
  async function platformPairingToken() {
39536
39742
  const ws = resolveWorkspace();
39537
39743
  const { context: context2 } = await loadServerContext(ws);
@@ -39540,7 +39746,7 @@ async function platformPairingToken() {
39540
39746
  process.exit(1);
39541
39747
  }
39542
39748
  const isProd = false;
39543
- const dbPath = join24(ws.rootDir, ".arc", "data", isProd ? "prod.db" : "dev.db");
39749
+ const dbPath = join25(ws.rootDir, ".arc", "data", isProd ? "prod.db" : "dev.db");
39544
39750
  const handler = await initContextHandler(context2, dbPath);
39545
39751
  const code = randomBytes3(24).toString("base64url");
39546
39752
  const res = await handler.executeCommand("pairingCode.issue", { code }, null, { internal: true });