@arcote.tech/arc-cli 0.5.6 → 0.5.7

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
@@ -26345,19 +26345,20 @@ ${colors3.yellow}Type declaration errors:${colors3.reset}`);
26345
26345
  }
26346
26346
 
26347
26347
  // src/platform/shared.ts
26348
- import { copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync6, readdirSync as readdirSync4, writeFileSync as writeFileSync6 } from "fs";
26349
- import { dirname as dirname6, join as join7 } from "path";
26348
+ import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync8, readdirSync as readdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
26349
+ import { dirname as dirname6, join as join9 } from "path";
26350
26350
 
26351
26351
  // src/builder/module-builder.ts
26352
26352
  import { execSync } from "child_process";
26353
26353
  import {
26354
- existsSync as existsSync5,
26355
- mkdirSync as mkdirSync5,
26356
- readFileSync as readFileSync5,
26357
- readdirSync as readdirSync3,
26358
- writeFileSync as writeFileSync5
26354
+ existsSync as existsSync7,
26355
+ mkdirSync as mkdirSync6,
26356
+ readFileSync as readFileSync7,
26357
+ readdirSync as readdirSync4,
26358
+ rmSync,
26359
+ writeFileSync as writeFileSync6
26359
26360
  } from "fs";
26360
- import { dirname as dirname5, join as join6, relative as relative2 } from "path";
26361
+ import { dirname as dirname5, join as join8, relative as relative3 } from "path";
26361
26362
 
26362
26363
  // src/i18n/index.ts
26363
26364
  import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
@@ -26630,43 +26631,154 @@ async function finalizeTranslations(rootDir, outDir, collector) {
26630
26631
  }
26631
26632
  }
26632
26633
 
26634
+ // src/builder/build-cache.ts
26635
+ import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
26636
+ import { join as join6 } from "path";
26637
+ var CACHE_VERSION = 1;
26638
+ var CACHE_FILE = ".build-cache.json";
26639
+ function emptyCache() {
26640
+ return { version: CACHE_VERSION, units: {} };
26641
+ }
26642
+ function loadBuildCache(arcDir) {
26643
+ const path4 = join6(arcDir, CACHE_FILE);
26644
+ if (!existsSync5(path4))
26645
+ return emptyCache();
26646
+ try {
26647
+ const raw = JSON.parse(readFileSync5(path4, "utf-8"));
26648
+ if (raw?.version !== CACHE_VERSION || typeof raw.units !== "object") {
26649
+ return emptyCache();
26650
+ }
26651
+ return raw;
26652
+ } catch {
26653
+ return emptyCache();
26654
+ }
26655
+ }
26656
+ function saveBuildCache(arcDir, cache) {
26657
+ mkdirSync5(arcDir, { recursive: true });
26658
+ writeFileSync5(join6(arcDir, CACHE_FILE), JSON.stringify(cache, null, 2));
26659
+ }
26660
+ function isCacheHit(cache, unitId, inputHash, requiredOutputs = []) {
26661
+ const entry = cache.units[unitId];
26662
+ if (!entry || entry.inputHash !== inputHash)
26663
+ return false;
26664
+ for (const out of requiredOutputs) {
26665
+ if (!existsSync5(out))
26666
+ return false;
26667
+ }
26668
+ return true;
26669
+ }
26670
+ function updateCache(cache, unitId, inputHash, output = {}) {
26671
+ cache.units[unitId] = {
26672
+ inputHash,
26673
+ ...output.outputHash !== undefined && { outputHash: output.outputHash },
26674
+ ...output.outputHashes !== undefined && { outputHashes: output.outputHashes }
26675
+ };
26676
+ }
26677
+
26678
+ // src/builder/hash.ts
26679
+ import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync } from "fs";
26680
+ import { join as join7, relative as relative2, sep as sep2 } from "path";
26681
+ function sha256Hex(bytes) {
26682
+ const hasher = new Bun.CryptoHasher("sha256");
26683
+ hasher.update(bytes);
26684
+ return hasher.digest("hex");
26685
+ }
26686
+ function sha256OfFiles(paths) {
26687
+ const hasher = new Bun.CryptoHasher("sha256");
26688
+ const sorted = [...paths].sort();
26689
+ for (const p of sorted) {
26690
+ if (!existsSync6(p))
26691
+ continue;
26692
+ hasher.update(readFileSync6(p));
26693
+ hasher.update("\x00");
26694
+ }
26695
+ return hasher.digest("hex");
26696
+ }
26697
+ function sha256OfDir(dir, filter2) {
26698
+ if (!existsSync6(dir))
26699
+ return sha256Hex("");
26700
+ const hasher = new Bun.CryptoHasher("sha256");
26701
+ const entries = [];
26702
+ function walk(absDir) {
26703
+ for (const entry of readdirSync3(absDir, { withFileTypes: true })) {
26704
+ const abs = join7(absDir, entry.name);
26705
+ const rel = relative2(dir, abs).split(sep2).join("/");
26706
+ if (filter2 && !filter2(rel))
26707
+ continue;
26708
+ if (entry.isDirectory())
26709
+ walk(abs);
26710
+ else if (entry.isFile())
26711
+ entries.push({ rel, abs });
26712
+ }
26713
+ }
26714
+ walk(dir);
26715
+ entries.sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
26716
+ for (const { rel, abs } of entries) {
26717
+ hasher.update(rel);
26718
+ hasher.update("\x00");
26719
+ hasher.update(readFileSync6(abs));
26720
+ hasher.update("\x00");
26721
+ }
26722
+ return hasher.digest("hex");
26723
+ }
26724
+ function sha256OfJson(value) {
26725
+ return sha256Hex(stableStringify(value));
26726
+ }
26727
+ function stableStringify(value) {
26728
+ if (value === null || typeof value !== "object")
26729
+ return JSON.stringify(value);
26730
+ if (Array.isArray(value)) {
26731
+ return "[" + value.map(stableStringify).join(",") + "]";
26732
+ }
26733
+ const obj = value;
26734
+ const keys = Object.keys(obj).sort();
26735
+ return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") + "}";
26736
+ }
26737
+ function readInstalledVersion(rootDir, pkgName) {
26738
+ const pkgJson = join7(rootDir, "node_modules", pkgName, "package.json");
26739
+ if (!existsSync6(pkgJson))
26740
+ return null;
26741
+ try {
26742
+ return JSON.parse(readFileSync6(pkgJson, "utf-8")).version ?? null;
26743
+ } catch {
26744
+ return null;
26745
+ }
26746
+ }
26747
+ function mtimeOf(path4) {
26748
+ if (!existsSync6(path4))
26749
+ return 0;
26750
+ try {
26751
+ return statSync(path4).mtimeMs;
26752
+ } catch {
26753
+ return 0;
26754
+ }
26755
+ }
26756
+
26757
+ // src/builder/parallel.ts
26758
+ import { cpus } from "os";
26759
+ var DEFAULT_CONCURRENCY = Math.max(1, cpus().length - 1);
26760
+ async function pAll(tasks, concurrency = DEFAULT_CONCURRENCY) {
26761
+ const results = new Array(tasks.length);
26762
+ let nextIndex = 0;
26763
+ const limit = Math.max(1, Math.min(concurrency, tasks.length));
26764
+ async function worker() {
26765
+ while (true) {
26766
+ const i = nextIndex++;
26767
+ if (i >= tasks.length)
26768
+ return;
26769
+ results[i] = await tasks[i]();
26770
+ }
26771
+ }
26772
+ const workers = Array.from({ length: limit }, () => worker());
26773
+ await Promise.all(workers);
26774
+ return results;
26775
+ }
26776
+
26633
26777
  // src/builder/module-builder.ts
26634
26778
  var CONTEXT_CLIENTS = [
26635
26779
  { name: "server", target: "bun", defines: { ONLY_SERVER: "true", ONLY_BROWSER: "false", ONLY_CLIENT: "false" } },
26636
26780
  { name: "browser", target: "browser", defines: { ONLY_SERVER: "false", ONLY_BROWSER: "true", ONLY_CLIENT: "true" } }
26637
26781
  ];
26638
- async function buildContextPackage(pkg) {
26639
- const entrypoint = pkg.entrypoint;
26640
- const outDir = join6(pkg.path, "dist");
26641
- const peerDeps = Object.keys(pkg.packageJson.peerDependencies || {});
26642
- const deps = Object.keys(pkg.packageJson.dependencies || {});
26643
- const externals = [...peerDeps, ...deps];
26644
- const allDeclErrors = [];
26645
- for (const client of CONTEXT_CLIENTS) {
26646
- const result = await Bun.build({
26647
- entrypoints: [entrypoint],
26648
- outdir: join6(outDir, client.name, "main"),
26649
- target: client.target,
26650
- format: "esm",
26651
- naming: "index.[ext]",
26652
- external: externals,
26653
- define: client.defines
26654
- });
26655
- if (!result.success) {
26656
- console.error(`Context ${client.name} build failed:`);
26657
- for (const log2 of result.logs)
26658
- console.error(log2);
26659
- throw new Error(`${client.name} build failed for ${pkg.name}`);
26660
- }
26661
- const globalsContent = Object.entries(client.defines).map(([k, v]) => `declare const ${k}: ${v};`).join(`
26662
- `);
26663
- const declResult = await buildTypeDeclarations([entrypoint], join6(outDir, client.name), dirname5(entrypoint), globalsContent);
26664
- if (!declResult.success && declResult.errors.length > 0) {
26665
- allDeclErrors.push(...declResult.errors.map((e) => `[${pkg.name}/${client.name}] ${e}`));
26666
- }
26667
- }
26668
- return { declarationErrors: allDeclErrors };
26669
- }
26670
26782
  var SHELL_EXTERNALS = [
26671
26783
  "react",
26672
26784
  "react-dom",
@@ -26680,57 +26792,41 @@ var SHELL_EXTERNALS = [
26680
26792
  "@arcote.tech/arc-workspace",
26681
26793
  "@arcote.tech/platform"
26682
26794
  ];
26683
- function sha256Hex(bytes) {
26684
- const hasher = new Bun.CryptoHasher("sha256");
26685
- hasher.update(bytes);
26686
- return hasher.digest("hex");
26687
- }
26688
- function sha256OfFiles(paths) {
26689
- const hasher = new Bun.CryptoHasher("sha256");
26690
- const sorted = [...paths].sort();
26691
- for (const p of sorted) {
26692
- if (!existsSync5(p))
26693
- continue;
26694
- hasher.update(readFileSync5(p));
26695
- hasher.update("\x00");
26696
- }
26697
- return hasher.digest("hex");
26698
- }
26699
26795
  function discoverPackages(rootDir) {
26700
- const rootPkg = JSON.parse(readFileSync5(join6(rootDir, "package.json"), "utf-8"));
26796
+ const rootPkg = JSON.parse(readFileSync7(join8(rootDir, "package.json"), "utf-8"));
26701
26797
  const workspaceGlobs = rootPkg.workspaces ?? [];
26702
26798
  const results = [];
26703
26799
  for (const glob2 of workspaceGlobs) {
26704
26800
  const base2 = glob2.replace("/*", "");
26705
- const baseDir = join6(rootDir, base2);
26706
- if (!existsSync5(baseDir))
26801
+ const baseDir = join8(rootDir, base2);
26802
+ if (!existsSync7(baseDir))
26707
26803
  continue;
26708
26804
  let entries;
26709
26805
  try {
26710
- entries = readdirSync3(baseDir);
26806
+ entries = readdirSync4(baseDir);
26711
26807
  } catch {
26712
26808
  continue;
26713
26809
  }
26714
26810
  for (const entry of entries) {
26715
- const pkgPath = join6(baseDir, entry, "package.json");
26716
- if (!existsSync5(pkgPath))
26811
+ const pkgPath = join8(baseDir, entry, "package.json");
26812
+ if (!existsSync7(pkgPath))
26717
26813
  continue;
26718
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
26814
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
26719
26815
  if (pkg.name?.startsWith("@arcote.tech/"))
26720
26816
  continue;
26721
- const pkgDir = join6(baseDir, entry);
26817
+ const pkgDir = join8(baseDir, entry);
26722
26818
  const candidates = [
26723
- join6(pkgDir, "src", "index.ts"),
26724
- join6(pkgDir, "src", "index.tsx"),
26725
- join6(pkgDir, "index.ts"),
26726
- join6(pkgDir, "index.tsx")
26819
+ join8(pkgDir, "src", "index.ts"),
26820
+ join8(pkgDir, "src", "index.tsx"),
26821
+ join8(pkgDir, "index.ts"),
26822
+ join8(pkgDir, "index.tsx")
26727
26823
  ];
26728
- const entrypoint = candidates.find((c) => existsSync5(c)) ?? null;
26824
+ const entrypoint = candidates.find((c) => existsSync7(c)) ?? null;
26729
26825
  if (!entrypoint)
26730
26826
  continue;
26731
26827
  results.push({
26732
26828
  name: pkg.name,
26733
- path: join6(baseDir, entry),
26829
+ path: join8(baseDir, entry),
26734
26830
  entrypoint,
26735
26831
  packageJson: pkg
26736
26832
  });
@@ -26749,95 +26845,198 @@ function isContextPackage(pkg) {
26749
26845
  }
26750
26846
  return false;
26751
26847
  }
26752
- async function buildPackages(rootDir, outDir, packages) {
26753
- mkdirSync5(outDir, { recursive: true });
26754
- const contexts = packages.filter((p) => isContextPackage(p.packageJson));
26755
- const allDeclErrors = [];
26756
- for (const ctx of contexts) {
26757
- console.log(` Building context: ${ctx.name}`);
26758
- const { declarationErrors } = await buildContextPackage(ctx);
26759
- allDeclErrors.push(...declarationErrors);
26760
- }
26761
- if (allDeclErrors.length > 0) {
26762
- console.warn(`
26763
- \x1B[33mType declaration errors:\x1B[0m`);
26764
- for (const err of allDeclErrors) {
26765
- console.warn(` ${err}`);
26766
- }
26767
- console.warn("");
26768
- }
26769
- const tmpDir = join6(outDir, "_entries");
26770
- mkdirSync5(tmpDir, { recursive: true });
26771
- const entrypoints = [];
26772
- const fileToName = new Map;
26773
- for (const pkg of packages) {
26774
- const safeName = pkg.path.split("/").pop();
26775
- const moduleName = pkg.name.includes("/") ? pkg.name.split("/").pop() : pkg.name;
26776
- fileToName.set(safeName, moduleName);
26777
- const wrapperFile = join6(tmpDir, `${safeName}.ts`);
26778
- writeFileSync5(wrapperFile, `export * from "${pkg.name}";
26779
- `);
26780
- entrypoints.push(wrapperFile);
26848
+ var sourceFilter = (rel) => {
26849
+ if (rel.startsWith("dist/") || rel.startsWith("dist"))
26850
+ return false;
26851
+ if (rel.includes("/node_modules/") || rel.startsWith("node_modules"))
26852
+ return false;
26853
+ if (rel.startsWith(".arc/") || rel.startsWith(".arc"))
26854
+ return false;
26855
+ return true;
26856
+ };
26857
+ function pkgSourceHash(pkg) {
26858
+ return sha256OfDir(join8(pkg.path, "src"), sourceFilter);
26859
+ }
26860
+ function depVersionsHash(rootDir, pkg) {
26861
+ const peerDeps = Object.keys(pkg.packageJson.peerDependencies ?? {});
26862
+ const deps = Object.keys(pkg.packageJson.dependencies ?? {});
26863
+ const versions = {};
26864
+ for (const dep of [...peerDeps, ...deps].sort()) {
26865
+ versions[dep] = readInstalledVersion(rootDir, dep);
26866
+ }
26867
+ return sha256OfJson(versions);
26868
+ }
26869
+ async function buildContextClient(pkg, rootDir, client, cache, noCache) {
26870
+ const unitId = `context-pkg:${pkg.name}:${client.name}`;
26871
+ const outDir = join8(pkg.path, "dist", client.name);
26872
+ const inputHash = sha256OfJson({
26873
+ src: pkgSourceHash(pkg),
26874
+ pkg: pkg.packageJson,
26875
+ deps: depVersionsHash(rootDir, pkg),
26876
+ client: client.name,
26877
+ target: client.target,
26878
+ defines: client.defines
26879
+ });
26880
+ if (!noCache && isCacheHit(cache, unitId, inputHash, [join8(outDir, "main", "index.js")])) {
26881
+ console.log(` \u2713 cached: ${pkg.name} (${client.name})`);
26882
+ return { pkgName: pkg.name, client: client.name, declarationErrors: [], cached: true };
26781
26883
  }
26782
- console.log(` Bundling ${entrypoints.length} package(s)...`);
26783
- const i18nCollector = new Map;
26784
- const arcExternalPlugin = {
26785
- name: "arc-external",
26786
- setup(build2) {
26787
- build2.onResolve({ filter: /^@arcote\.tech\// }, (args) => {
26788
- return { path: args.path, external: true };
26789
- });
26790
- }
26791
- };
26884
+ console.log(` building: ${pkg.name} (${client.name})`);
26885
+ const peerDeps = Object.keys(pkg.packageJson.peerDependencies ?? {});
26886
+ const deps = Object.keys(pkg.packageJson.dependencies ?? {});
26887
+ const externals = [...peerDeps, ...deps];
26792
26888
  const result = await Bun.build({
26793
- entrypoints,
26794
- outdir: outDir,
26795
- splitting: true,
26889
+ entrypoints: [pkg.entrypoint],
26890
+ outdir: join8(outDir, "main"),
26891
+ target: client.target,
26796
26892
  format: "esm",
26797
- target: "browser",
26798
- external: SHELL_EXTERNALS,
26799
- plugins: [arcExternalPlugin, i18nExtractPlugin(i18nCollector, rootDir)],
26800
- naming: "[name].[ext]",
26801
- define: {
26802
- ONLY_SERVER: "false",
26803
- ONLY_BROWSER: "true",
26804
- ONLY_CLIENT: "true"
26805
- }
26893
+ naming: "index.[ext]",
26894
+ external: externals,
26895
+ define: client.defines
26806
26896
  });
26807
26897
  if (!result.success) {
26808
- console.error("Build failed:");
26898
+ console.error(`Context ${client.name} build failed:`);
26809
26899
  for (const log2 of result.logs)
26810
26900
  console.error(log2);
26811
- throw new Error("Module build failed");
26812
- }
26813
- await finalizeTranslations(rootDir, join6(outDir, ".."), i18nCollector);
26814
- const { rmSync } = await import("fs");
26815
- rmSync(tmpDir, { recursive: true, force: true });
26816
- const moduleEntries = result.outputs.filter((o) => o.kind === "entry-point").map((o) => {
26817
- const file = o.path.split("/").pop();
26818
- const safeName = file.replace(/\.js$/, "");
26819
- const bytes = readFileSync5(o.path);
26820
- return {
26821
- file,
26822
- name: fileToName.get(safeName) ?? safeName,
26823
- hash: sha256Hex(bytes)
26824
- };
26901
+ throw new Error(`${client.name} build failed for ${pkg.name}`);
26902
+ }
26903
+ const globalsContent = Object.entries(client.defines).map(([k, v]) => `declare const ${k}: ${v};`).join(`
26904
+ `);
26905
+ const declResult = await buildTypeDeclarations([pkg.entrypoint], outDir, dirname5(pkg.entrypoint), globalsContent);
26906
+ const declarationErrors = !declResult.success && declResult.errors.length > 0 ? declResult.errors.map((e) => `[${pkg.name}/${client.name}] ${e}`) : [];
26907
+ const outputHash = sha256OfDir(outDir);
26908
+ updateCache(cache, unitId, inputHash, { outputHash });
26909
+ return { pkgName: pkg.name, client: client.name, declarationErrors, cached: false };
26910
+ }
26911
+ async function buildContextPackages(rootDir, packages, cache, noCache) {
26912
+ const contexts = packages.filter((p) => isContextPackage(p.packageJson));
26913
+ if (contexts.length === 0)
26914
+ return { declarationErrors: [] };
26915
+ const tasks = contexts.flatMap((pkg) => CONTEXT_CLIENTS.map((client) => () => buildContextClient(pkg, rootDir, client, cache, noCache)));
26916
+ const results = await pAll(tasks);
26917
+ const declarationErrors = results.flatMap((r) => r.declarationErrors);
26918
+ if (declarationErrors.length > 0) {
26919
+ console.warn(`
26920
+ \x1B[33mType declaration errors:\x1B[0m`);
26921
+ for (const err of declarationErrors)
26922
+ console.warn(` ${err}`);
26923
+ console.warn("");
26924
+ }
26925
+ return { declarationErrors };
26926
+ }
26927
+ async function buildModulesBundle(rootDir, outDir, packages, cache, noCache) {
26928
+ mkdirSync6(outDir, { recursive: true });
26929
+ const unitId = "modules-bundle";
26930
+ const pkgHashes = packages.map((p) => ({
26931
+ name: p.name,
26932
+ safeName: p.path.split("/").pop(),
26933
+ srcHash: pkgSourceHash(p)
26934
+ }));
26935
+ const inputHash = sha256OfJson({
26936
+ pkgHashes,
26937
+ externals: SHELL_EXTERNALS,
26938
+ define: { ONLY_SERVER: "false", ONLY_BROWSER: "true", ONLY_CLIENT: "true" }
26825
26939
  });
26826
- const manifest = {
26827
- modules: moduleEntries,
26828
- shellHash: "",
26829
- stylesHash: "",
26830
- buildTime: new Date().toISOString()
26831
- };
26832
- writeFileSync5(join6(outDir, "manifest.json"), JSON.stringify(manifest, null, 2));
26833
- return manifest;
26940
+ if (!noCache && isCacheHit(cache, unitId, inputHash)) {
26941
+ const existing = cache.units[unitId]?.outputHashes ?? {};
26942
+ const modules = [];
26943
+ for (const { safeName, name } of pkgHashes) {
26944
+ const file = `${safeName}.js`;
26945
+ const filePath = join8(outDir, file);
26946
+ if (!existsSync7(filePath)) {
26947
+ console.log(` rebuilding modules-bundle: output ${file} missing`);
26948
+ return await actuallyBuild();
26949
+ }
26950
+ modules.push({ file, name, hash: existing[safeName] ?? sha256Hex(readFileSync7(filePath)) });
26951
+ }
26952
+ console.log(` \u2713 cached: modules-bundle (${modules.length} module(s))`);
26953
+ return { modules, cached: true };
26954
+ }
26955
+ return await actuallyBuild();
26956
+ async function actuallyBuild() {
26957
+ console.log(` building: modules-bundle (${packages.length} package(s))`);
26958
+ const tmpDir = join8(outDir, "_entries");
26959
+ mkdirSync6(tmpDir, { recursive: true });
26960
+ const entrypoints = [];
26961
+ const fileToName = new Map;
26962
+ for (const pkg of packages) {
26963
+ const safeName = pkg.path.split("/").pop();
26964
+ const moduleName = pkg.name.includes("/") ? pkg.name.split("/").pop() : pkg.name;
26965
+ fileToName.set(safeName, moduleName);
26966
+ const wrapperFile = join8(tmpDir, `${safeName}.ts`);
26967
+ writeFileSync6(wrapperFile, `export * from "${pkg.name}";
26968
+ `);
26969
+ entrypoints.push(wrapperFile);
26970
+ }
26971
+ const i18nCollector = new Map;
26972
+ const arcExternalPlugin = {
26973
+ name: "arc-external",
26974
+ setup(build2) {
26975
+ build2.onResolve({ filter: /^@arcote\.tech\// }, (args) => {
26976
+ return { path: args.path, external: true };
26977
+ });
26978
+ }
26979
+ };
26980
+ const result = await Bun.build({
26981
+ entrypoints,
26982
+ outdir: outDir,
26983
+ splitting: true,
26984
+ format: "esm",
26985
+ target: "browser",
26986
+ external: SHELL_EXTERNALS,
26987
+ plugins: [arcExternalPlugin, i18nExtractPlugin(i18nCollector, rootDir)],
26988
+ naming: "[name].[ext]",
26989
+ define: {
26990
+ ONLY_SERVER: "false",
26991
+ ONLY_BROWSER: "true",
26992
+ ONLY_CLIENT: "true"
26993
+ }
26994
+ });
26995
+ if (!result.success) {
26996
+ console.error("Modules bundle build failed:");
26997
+ for (const log2 of result.logs)
26998
+ console.error(log2);
26999
+ throw new Error("Module build failed");
27000
+ }
27001
+ await finalizeTranslations(rootDir, join8(outDir, ".."), i18nCollector);
27002
+ rmSync(tmpDir, { recursive: true, force: true });
27003
+ const outputHashes = {};
27004
+ const modules = result.outputs.filter((o) => o.kind === "entry-point").map((o) => {
27005
+ const file = o.path.split("/").pop();
27006
+ const safeName = file.replace(/\.js$/, "");
27007
+ const bytes = readFileSync7(o.path);
27008
+ const hash = sha256Hex(bytes);
27009
+ outputHashes[safeName] = hash;
27010
+ return {
27011
+ file,
27012
+ name: fileToName.get(safeName) ?? safeName,
27013
+ hash
27014
+ };
27015
+ });
27016
+ updateCache(cache, unitId, inputHash, { outputHashes });
27017
+ return { modules, cached: false };
27018
+ }
27019
+ }
27020
+ async function buildTranslations(rootDir, arcDir, cache, noCache) {
27021
+ const localesDir = join8(rootDir, "locales");
27022
+ if (!existsSync7(localesDir))
27023
+ return;
27024
+ const unitId = "translations";
27025
+ const poFiles = readdirSync4(localesDir).filter((f) => f.endsWith(".po")).map((f) => join8(localesDir, f));
27026
+ if (poFiles.length === 0)
27027
+ return;
27028
+ const inputHash = sha256OfFiles(poFiles);
27029
+ if (!noCache && isCacheHit(cache, unitId, inputHash, [join8(arcDir, "locales")])) {
27030
+ console.log(` \u2713 cached: translations`);
27031
+ return;
27032
+ }
27033
+ console.log(` building: translations (${poFiles.length} catalog(s))`);
27034
+ compileAllCatalogs(localesDir, join8(arcDir, "locales"));
27035
+ const jsonFiles = readdirSync4(join8(arcDir, "locales")).filter((f) => f.endsWith(".json")).map((f) => join8(arcDir, "locales", f));
27036
+ const outputHash = sha256OfFiles(jsonFiles);
27037
+ updateCache(cache, unitId, inputHash, { outputHash });
26834
27038
  }
26835
- async function buildStyles(rootDir, outDir) {
26836
- mkdirSync5(outDir, { recursive: true });
26837
- const inputCss = join6(outDir, "_input.css");
26838
- const outputCss = join6(outDir, "styles.css");
26839
- const rootRel = relative2(outDir, rootDir).replace(/\\/g, "/");
26840
- writeFileSync5(inputCss, `@import "tailwindcss";
27039
+ var TAILWIND_INPUT_TEMPLATE = (rootRel) => `@import "tailwindcss";
26841
27040
  @import "tw-animate-css";
26842
27041
 
26843
27042
  @source "${rootRel}/packages/*/*.{ts,tsx}";
@@ -26895,12 +27094,61 @@ async function buildStyles(rootDir, outDir) {
26895
27094
  min-height: 100vh;
26896
27095
  }
26897
27096
  }
26898
- `);
26899
- console.log(" Building CSS...");
27097
+ `;
27098
+ async function buildStyles(rootDir, arcDir, packages, themePath, cache, noCache) {
27099
+ mkdirSync6(arcDir, { recursive: true });
27100
+ const inputCss = join8(arcDir, "_input.css");
27101
+ const outputCss = join8(arcDir, "styles.css");
27102
+ const themeOutput = join8(arcDir, "theme.css");
27103
+ const rootRel = relative3(arcDir, rootDir).replace(/\\/g, "/");
27104
+ const inputCssContent = TAILWIND_INPUT_TEMPLATE(rootRel);
27105
+ const tsxFilter = (rel) => {
27106
+ if (!sourceFilter(rel))
27107
+ return false;
27108
+ if (/\.[^/]+$/.test(rel) && !/\.(ts|tsx)$/.test(rel))
27109
+ return false;
27110
+ return true;
27111
+ };
27112
+ const wsHashes = {};
27113
+ for (const p of packages) {
27114
+ wsHashes[p.name] = sha256OfDir(join8(p.path, "src"), tsxFilter);
27115
+ }
27116
+ const platformSrc = join8(rootDir, "node_modules", "@arcote.tech", "platform", "src");
27117
+ const arcDsSrc = join8(rootDir, "node_modules", "@arcote.tech", "arc-ds", "src");
27118
+ const frameworkHashes = {
27119
+ platform: sha256OfDir(platformSrc, tsxFilter),
27120
+ arcDs: sha256OfDir(arcDsSrc, tsxFilter)
27121
+ };
27122
+ const themeContent = themePath && existsSync7(join8(rootDir, themePath)) ? readFileSync7(join8(rootDir, themePath)) : null;
27123
+ const themeHash = themeContent ? sha256Hex(themeContent) : null;
27124
+ const unitId = "styles";
27125
+ const inputHash = sha256OfJson({
27126
+ workspaces: wsHashes,
27127
+ framework: frameworkHashes,
27128
+ inputCss: inputCssContent,
27129
+ themeHash
27130
+ });
27131
+ const requiredOutputs = [outputCss];
27132
+ if (themePath)
27133
+ requiredOutputs.push(themeOutput);
27134
+ if (!noCache && isCacheHit(cache, unitId, inputHash, requiredOutputs)) {
27135
+ console.log(` \u2713 cached: styles`);
27136
+ return;
27137
+ }
27138
+ console.log(` building: styles`);
27139
+ writeFileSync6(inputCss, inputCssContent);
26900
27140
  execSync(`bunx @tailwindcss/cli -i ${inputCss} -o ${outputCss} --minify`, {
26901
27141
  cwd: rootDir,
26902
27142
  stdio: "inherit"
26903
27143
  });
27144
+ if (themePath && themeContent) {
27145
+ writeFileSync6(themeOutput, themeContent);
27146
+ }
27147
+ const outFiles = [outputCss];
27148
+ if (themePath && existsSync7(themeOutput))
27149
+ outFiles.push(themeOutput);
27150
+ const outputHash = sha256OfFiles(outFiles);
27151
+ updateCache(cache, unitId, inputHash, { outputHash });
26904
27152
  }
26905
27153
 
26906
27154
  // src/platform/shared.ts
@@ -26920,9 +27168,9 @@ function resolveWorkspace() {
26920
27168
  process.exit(1);
26921
27169
  }
26922
27170
  const rootDir = dirname6(packageJsonPath);
26923
- const rootPkg = JSON.parse(readFileSync6(packageJsonPath, "utf-8"));
27171
+ const rootPkg = JSON.parse(readFileSync8(packageJsonPath, "utf-8"));
26924
27172
  const appName = rootPkg.name ?? "Arc App";
26925
- const arcDir = join7(rootDir, ".arc", "platform");
27173
+ const arcDir = join9(rootDir, ".arc", "platform");
26926
27174
  log2("Scanning workspaces...");
26927
27175
  const packages = discoverPackages(rootDir);
26928
27176
  ok(`Found ${packages.length} package(s): ${packages.map((p) => p.name).join(", ")}`);
@@ -26932,10 +27180,10 @@ function resolveWorkspace() {
26932
27180
  }
26933
27181
  let manifest;
26934
27182
  for (const name of ["manifest.json", "manifest.webmanifest"]) {
26935
- const manifestPath = join7(rootDir, name);
26936
- if (existsSync6(manifestPath)) {
27183
+ const manifestPath = join9(rootDir, name);
27184
+ if (existsSync8(manifestPath)) {
26937
27185
  try {
26938
- const data = JSON.parse(readFileSync6(manifestPath, "utf-8"));
27186
+ const data = JSON.parse(readFileSync8(manifestPath, "utf-8"));
26939
27187
  const icons = data.icons;
26940
27188
  manifest = {
26941
27189
  path: manifestPath,
@@ -26954,61 +27202,135 @@ function resolveWorkspace() {
26954
27202
  rootPkg,
26955
27203
  appName,
26956
27204
  arcDir,
26957
- modulesDir: join7(arcDir, "modules"),
26958
- shellDir: join7(arcDir, "shell"),
26959
- publicDir: join7(rootDir, "public"),
27205
+ modulesDir: join9(arcDir, "modules"),
27206
+ shellDir: join9(arcDir, "shell"),
27207
+ assetsDir: join9(arcDir, "assets"),
27208
+ publicDir: join9(rootDir, "public"),
26960
27209
  packages,
26961
27210
  manifest
26962
27211
  };
26963
27212
  }
26964
- async function buildAll(ws) {
26965
- log2("Building packages...");
26966
- const manifest = await buildPackages(ws.rootDir, ws.modulesDir, ws.packages);
26967
- ok(`Built ${manifest.modules.length} module(s)`);
26968
- log2("Building styles...");
26969
- await buildStyles(ws.rootDir, ws.arcDir);
26970
- ok("Styles built");
27213
+ async function buildAll(ws, opts = {}) {
27214
+ const cache = loadBuildCache(ws.arcDir);
27215
+ const noCache = opts.noCache ?? false;
26971
27216
  const themePath = ws.rootPkg.arc?.theme;
26972
- if (themePath) {
26973
- const src = join7(ws.rootDir, themePath);
26974
- if (existsSync6(src)) {
26975
- copyFileSync(src, join7(ws.arcDir, "theme.css"));
26976
- ok("Theme copied");
26977
- }
26978
- }
26979
- log2("Building shell...");
26980
- await buildShell(ws.shellDir, ws.packages);
26981
- ok("Shell built");
26982
- const finalManifest = finalizeManifest(ws, manifest);
26983
- writeFileSync6(join7(ws.modulesDir, "manifest.json"), JSON.stringify(finalManifest, null, 2));
27217
+ log2(`Building (concurrency parallel${noCache ? ", no-cache" : ""})...`);
27218
+ const [, modulesResult] = await Promise.all([
27219
+ buildContextPackages(ws.rootDir, ws.packages, cache, noCache),
27220
+ buildModulesBundle(ws.rootDir, ws.modulesDir, ws.packages, cache, noCache),
27221
+ buildShell(ws, cache, noCache),
27222
+ buildStyles(ws.rootDir, ws.arcDir, ws.packages, themePath, cache, noCache),
27223
+ copyBrowserAssets(ws, cache, noCache),
27224
+ buildTranslations(ws.rootDir, ws.arcDir, cache, noCache)
27225
+ ]);
27226
+ saveBuildCache(ws.arcDir, cache);
27227
+ const finalManifest = assembleManifest(ws, modulesResult.modules, cache);
27228
+ writeFileSync7(join9(ws.modulesDir, "manifest.json"), JSON.stringify(finalManifest, null, 2));
26984
27229
  return finalManifest;
26985
27230
  }
26986
- function finalizeManifest(ws, manifest) {
26987
- const shellFiles = listFilesRec(ws.shellDir);
26988
- const stylesFiles = [
26989
- join7(ws.arcDir, "styles.css"),
26990
- join7(ws.arcDir, "theme.css")
26991
- ].filter((p) => existsSync6(p));
27231
+ function assembleManifest(ws, modules, cache) {
27232
+ const shellEntries = {};
27233
+ for (const [unitId, entry] of Object.entries(cache.units)) {
27234
+ if (unitId.startsWith("shell:") && entry.outputHash) {
27235
+ shellEntries[unitId] = entry.outputHash;
27236
+ }
27237
+ }
27238
+ const shellHash = sha256OfJson(shellEntries);
27239
+ const stylesHash = cache.units["styles"]?.outputHash ?? "";
26992
27240
  return {
26993
- modules: manifest.modules,
26994
- shellHash: sha256OfFiles(shellFiles),
26995
- stylesHash: sha256OfFiles(stylesFiles),
26996
- buildTime: manifest.buildTime
27241
+ modules,
27242
+ shellHash,
27243
+ stylesHash,
27244
+ buildTime: new Date().toISOString()
26997
27245
  };
26998
27246
  }
26999
- function listFilesRec(dir) {
27000
- if (!existsSync6(dir))
27247
+ function resolveAssetSource(from, pkgDir, rootDir) {
27248
+ if (from.startsWith("./") || from.startsWith("../")) {
27249
+ const resolved = join9(pkgDir, from);
27250
+ return existsSync8(resolved) ? resolved : null;
27251
+ }
27252
+ const candidates = [
27253
+ join9(rootDir, "node_modules", from),
27254
+ join9(pkgDir, "node_modules", from)
27255
+ ];
27256
+ for (const c of candidates) {
27257
+ if (existsSync8(c))
27258
+ return c;
27259
+ }
27260
+ const bunCacheDir = join9(rootDir, "node_modules", ".bun");
27261
+ if (existsSync8(bunCacheDir)) {
27262
+ for (const entry of readdirSync5(bunCacheDir, { withFileTypes: true })) {
27263
+ if (!entry.isDirectory())
27264
+ continue;
27265
+ const candidate = join9(bunCacheDir, entry.name, "node_modules", from);
27266
+ if (existsSync8(candidate))
27267
+ return candidate;
27268
+ }
27269
+ }
27270
+ return null;
27271
+ }
27272
+ function readBrowserAssets(pkgDir) {
27273
+ const pkgJsonPath = join9(pkgDir, "package.json");
27274
+ if (!existsSync8(pkgJsonPath))
27275
+ return [];
27276
+ try {
27277
+ const pkg = JSON.parse(readFileSync8(pkgJsonPath, "utf-8"));
27278
+ const assets = pkg.arc?.browserAssets;
27279
+ if (!Array.isArray(assets))
27280
+ return [];
27281
+ return assets.filter((a) => typeof a?.from === "string" && typeof a?.to === "string");
27282
+ } catch {
27283
+ return [];
27284
+ }
27285
+ }
27286
+ function discoverBrowserAssets(ws) {
27287
+ const arcDir = join9(ws.rootDir, "node_modules", "@arcote.tech");
27288
+ if (!existsSync8(arcDir))
27001
27289
  return [];
27002
27290
  const out = [];
27003
- for (const entry of readdirSync4(dir, { withFileTypes: true })) {
27004
- const p = join7(dir, entry.name);
27005
- if (entry.isDirectory())
27006
- out.push(...listFilesRec(p));
27007
- else if (entry.isFile())
27008
- out.push(p);
27291
+ for (const entry of readdirSync5(arcDir, { withFileTypes: true })) {
27292
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
27293
+ continue;
27294
+ const pkgDir = join9(arcDir, entry.name);
27295
+ const assets = readBrowserAssets(pkgDir);
27296
+ for (const asset of assets) {
27297
+ const src = resolveAssetSource(asset.from, pkgDir, ws.rootDir);
27298
+ if (!src) {
27299
+ err(`browserAsset not found: ${asset.from} (from @arcote.tech/${entry.name})`);
27300
+ continue;
27301
+ }
27302
+ out.push({ arcPkg: entry.name, from: asset.from, to: asset.to, src });
27303
+ }
27009
27304
  }
27010
27305
  return out;
27011
27306
  }
27307
+ async function copyBrowserAssets(ws, cache, noCache) {
27308
+ mkdirSync7(ws.assetsDir, { recursive: true });
27309
+ const assets = discoverBrowserAssets(ws);
27310
+ if (assets.length === 0)
27311
+ return;
27312
+ const unitId = "browser-assets";
27313
+ const inputHash = sha256OfJson(assets.map((a) => ({
27314
+ arcPkg: a.arcPkg,
27315
+ from: a.from,
27316
+ to: a.to,
27317
+ mtime: mtimeOf(a.src)
27318
+ })));
27319
+ const requiredOutputs = assets.map((a) => join9(ws.assetsDir, a.to));
27320
+ if (!noCache && isCacheHit(cache, unitId, inputHash, requiredOutputs)) {
27321
+ console.log(` \u2713 cached: browser-assets (${assets.length})`);
27322
+ return;
27323
+ }
27324
+ console.log(` building: browser-assets (${assets.length})`);
27325
+ const outputHashes = {};
27326
+ for (const asset of assets) {
27327
+ const dest = join9(ws.assetsDir, asset.to);
27328
+ mkdirSync7(dirname6(dest), { recursive: true });
27329
+ copyFileSync(asset.src, dest);
27330
+ outputHashes[asset.to] = sha256Hex(readFileSync8(dest));
27331
+ }
27332
+ updateCache(cache, unitId, inputHash, { outputHashes });
27333
+ }
27012
27334
  function collectArcPeerDeps(packages) {
27013
27335
  const seen = new Set;
27014
27336
  for (const pkg of ["@arcote.tech/arc", "@arcote.tech/arc-ds", "@arcote.tech/arc-react", "@arcote.tech/platform"]) {
@@ -27026,14 +27348,10 @@ function collectArcPeerDeps(packages) {
27026
27348
  return [short, pkg];
27027
27349
  });
27028
27350
  }
27029
- async function buildShell(outDir, packages) {
27030
- mkdirSync6(outDir, { recursive: true });
27031
- const tmpDir = join7(outDir, "_tmp");
27032
- mkdirSync6(tmpDir, { recursive: true });
27033
- const reactEntries = [
27034
- [
27035
- "react",
27036
- `import React from "react";
27351
+ var REACT_ENTRIES = [
27352
+ [
27353
+ "react",
27354
+ `import React from "react";
27037
27355
  export default React;
27038
27356
  export const {
27039
27357
  Children, Component, Fragment, Profiler, PureComponent, StrictMode, Suspense,
@@ -27043,84 +27361,133 @@ export const {
27043
27361
  useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore,
27044
27362
  useTransition, version, useActionState, useOptimistic,
27045
27363
  } = React;`
27046
- ],
27047
- ["jsx-runtime", `export { jsx, jsxs, Fragment } from "react/jsx-runtime";`],
27048
- [
27049
- "jsx-dev-runtime",
27050
- `export { jsxDEV, Fragment } from "react/jsx-dev-runtime";`
27051
- ],
27052
- [
27053
- "react-dom",
27054
- `import ReactDOM from "react-dom";
27364
+ ],
27365
+ ["jsx-runtime", `export { jsx, jsxs, Fragment } from "react/jsx-runtime";`],
27366
+ [
27367
+ "jsx-dev-runtime",
27368
+ `export { jsxDEV, Fragment } from "react/jsx-dev-runtime";`
27369
+ ],
27370
+ [
27371
+ "react-dom",
27372
+ `import ReactDOM from "react-dom";
27055
27373
  export default ReactDOM;
27056
27374
  export const { createPortal, flushSync } = ReactDOM;`
27057
- ],
27058
- [
27059
- "react-dom-client",
27060
- `export { createRoot, hydrateRoot } from "react-dom/client";`
27061
- ]
27062
- ];
27375
+ ],
27376
+ [
27377
+ "react-dom-client",
27378
+ `export { createRoot, hydrateRoot } from "react-dom/client";`
27379
+ ]
27380
+ ];
27381
+ var REACT_OUTPUT_FILES = REACT_ENTRIES.map(([n]) => `${n}.js`);
27382
+ var SHELL_BASE_EXTERNAL = [
27383
+ "react",
27384
+ "react-dom",
27385
+ "react/jsx-runtime",
27386
+ "react/jsx-dev-runtime",
27387
+ "react-dom/client"
27388
+ ];
27389
+ var sourceFilter2 = (rel) => {
27390
+ if (rel.startsWith("dist/") || rel.startsWith("dist"))
27391
+ return false;
27392
+ if (rel.includes("/node_modules/") || rel.startsWith("node_modules"))
27393
+ return false;
27394
+ if (rel.startsWith(".arc/") || rel.startsWith(".arc"))
27395
+ return false;
27396
+ return true;
27397
+ };
27398
+ function arcPkgSrcHash(rootDir, pkg) {
27399
+ const srcDir = join9(rootDir, "node_modules", pkg, "src");
27400
+ if (existsSync8(srcDir))
27401
+ return sha256OfDir(srcDir, sourceFilter2);
27402
+ return sha256OfDir(join9(rootDir, "node_modules", pkg), sourceFilter2);
27403
+ }
27404
+ async function buildShellReact(shellDir, tmpDir, rootDir, cache, noCache) {
27405
+ const unitId = "shell:react";
27406
+ const inputHash = sha256OfJson({
27407
+ react: readInstalledVersion(rootDir, "react"),
27408
+ "react-dom": readInstalledVersion(rootDir, "react-dom"),
27409
+ entries: REACT_ENTRIES.map(([k, v]) => [k, v])
27410
+ });
27411
+ const requiredOutputs = REACT_OUTPUT_FILES.map((f) => join9(shellDir, f));
27412
+ if (!noCache && isCacheHit(cache, unitId, inputHash, requiredOutputs)) {
27413
+ console.log(` \u2713 cached: shell:react`);
27414
+ return;
27415
+ }
27416
+ console.log(` building: shell:react`);
27063
27417
  const reactEps = [];
27064
- for (const [name, code] of reactEntries) {
27065
- const f = join7(tmpDir, `${name}.ts`);
27066
- Bun.write(f, code);
27418
+ for (const [name, code] of REACT_ENTRIES) {
27419
+ const f = join9(tmpDir, `${name}.ts`);
27420
+ await Bun.write(f, code);
27067
27421
  reactEps.push(f);
27068
27422
  }
27069
- const r1 = await Bun.build({
27423
+ const r = await Bun.build({
27070
27424
  entrypoints: reactEps,
27071
- outdir: outDir,
27425
+ outdir: shellDir,
27072
27426
  splitting: true,
27073
27427
  format: "esm",
27074
27428
  target: "browser",
27075
27429
  naming: "[name].[ext]"
27076
27430
  });
27077
- if (!r1.success) {
27078
- for (const l of r1.logs)
27431
+ if (!r.success) {
27432
+ for (const l of r.logs)
27079
27433
  console.error(l);
27080
27434
  throw new Error("Shell React build failed");
27081
27435
  }
27082
- const arcEntries = packages ? collectArcPeerDeps(packages) : [
27083
- ["arc", "@arcote.tech/arc"],
27084
- ["arc-ds", "@arcote.tech/arc-ds"],
27085
- ["arc-react", "@arcote.tech/arc-react"],
27086
- ["platform", "@arcote.tech/platform"]
27087
- ];
27088
- const baseExternal = [
27089
- "react",
27090
- "react-dom",
27091
- "react/jsx-runtime",
27092
- "react/jsx-dev-runtime",
27093
- "react-dom/client"
27094
- ];
27095
- const allArcPkgs = arcEntries.map(([, pkg]) => pkg);
27096
- for (const [name, pkg] of arcEntries) {
27097
- const f = join7(tmpDir, `${name}.ts`);
27098
- Bun.write(f, `export * from "${pkg}";
27436
+ const outputHash = sha256OfFiles(requiredOutputs);
27437
+ updateCache(cache, unitId, inputHash, { outputHash });
27438
+ }
27439
+ async function buildShellArcEntry(shortName, pkg, allArcPkgs, shellDir, tmpDir, rootDir, cache, noCache) {
27440
+ const unitId = `shell:arc:${shortName}`;
27441
+ const otherExternals = allArcPkgs.filter((p) => p !== pkg);
27442
+ const inputHash = sha256OfJson({
27443
+ pkg,
27444
+ version: readInstalledVersion(rootDir, pkg),
27445
+ src: arcPkgSrcHash(rootDir, pkg),
27446
+ base: SHELL_BASE_EXTERNAL,
27447
+ others: [...otherExternals].sort()
27448
+ });
27449
+ const outputFile = join9(shellDir, `${shortName}.js`);
27450
+ if (!noCache && isCacheHit(cache, unitId, inputHash, [outputFile])) {
27451
+ console.log(` \u2713 cached: ${unitId}`);
27452
+ return;
27453
+ }
27454
+ console.log(` building: ${unitId}`);
27455
+ const f = join9(tmpDir, `${shortName}.ts`);
27456
+ await Bun.write(f, `export * from "${pkg}";
27099
27457
  `);
27100
- const r2 = await Bun.build({
27101
- entrypoints: [f],
27102
- outdir: outDir,
27103
- format: "esm",
27104
- target: "browser",
27105
- naming: "[name].[ext]",
27106
- external: [
27107
- ...baseExternal,
27108
- ...allArcPkgs.filter((p) => p !== pkg)
27109
- ],
27110
- define: {
27111
- ONLY_SERVER: "false",
27112
- ONLY_BROWSER: "true",
27113
- ONLY_CLIENT: "true"
27114
- }
27115
- });
27116
- if (!r2.success) {
27117
- for (const l of r2.logs)
27118
- console.error(l);
27119
- throw new Error(`Shell build failed for ${pkg}`);
27458
+ const r = await Bun.build({
27459
+ entrypoints: [f],
27460
+ outdir: shellDir,
27461
+ format: "esm",
27462
+ target: "browser",
27463
+ naming: "[name].[ext]",
27464
+ external: [...SHELL_BASE_EXTERNAL, ...otherExternals],
27465
+ define: {
27466
+ ONLY_SERVER: "false",
27467
+ ONLY_BROWSER: "true",
27468
+ ONLY_CLIENT: "true"
27120
27469
  }
27470
+ });
27471
+ if (!r.success) {
27472
+ for (const l of r.logs)
27473
+ console.error(l);
27474
+ throw new Error(`Shell build failed for ${pkg}`);
27121
27475
  }
27122
- const { rmSync } = await import("fs");
27123
- rmSync(tmpDir, { recursive: true, force: true });
27476
+ const outputHash = sha256OfFiles([outputFile]);
27477
+ updateCache(cache, unitId, inputHash, { outputHash });
27478
+ }
27479
+ async function buildShell(ws, cache, noCache) {
27480
+ mkdirSync7(ws.shellDir, { recursive: true });
27481
+ const tmpDir = join9(ws.shellDir, "_tmp");
27482
+ mkdirSync7(tmpDir, { recursive: true });
27483
+ const arcEntries = collectArcPeerDeps(ws.packages);
27484
+ const allArcPkgs = arcEntries.map(([, pkg]) => pkg);
27485
+ const tasks = [
27486
+ () => buildShellReact(ws.shellDir, tmpDir, ws.rootDir, cache, noCache),
27487
+ ...arcEntries.map(([short, pkg]) => () => buildShellArcEntry(short, pkg, allArcPkgs, ws.shellDir, tmpDir, ws.rootDir, cache, noCache))
27488
+ ];
27489
+ await pAll(tasks);
27490
+ rmSync2(tmpDir, { recursive: true, force: true });
27124
27491
  }
27125
27492
  async function loadServerContext(packages) {
27126
27493
  const ctxPackages = packages.filter((p) => isContextPackage(p.packageJson));
@@ -27129,13 +27496,13 @@ async function loadServerContext(packages) {
27129
27496
  globalThis.ONLY_SERVER = true;
27130
27497
  globalThis.ONLY_BROWSER = false;
27131
27498
  globalThis.ONLY_CLIENT = false;
27132
- const platformDir = join7(process.cwd(), "node_modules", "@arcote.tech", "platform");
27133
- const platformPkg = JSON.parse(readFileSync6(join7(platformDir, "package.json"), "utf-8"));
27134
- const platformEntry = join7(platformDir, platformPkg.main ?? "src/index.ts");
27499
+ const platformDir = join9(process.cwd(), "node_modules", "@arcote.tech", "platform");
27500
+ const platformPkg = JSON.parse(readFileSync8(join9(platformDir, "package.json"), "utf-8"));
27501
+ const platformEntry = join9(platformDir, platformPkg.main ?? "src/index.ts");
27135
27502
  await import(platformEntry);
27136
27503
  for (const ctx of ctxPackages) {
27137
- const serverDist = join7(ctx.path, "dist", "server", "main", "index.js");
27138
- if (!existsSync6(serverDist)) {
27504
+ const serverDist = join9(ctx.path, "dist", "server", "main", "index.js");
27505
+ if (!existsSync8(serverDist)) {
27139
27506
  err(`Context server dist not found: ${serverDist}`);
27140
27507
  continue;
27141
27508
  }
@@ -27159,26 +27526,26 @@ async function loadServerContext(packages) {
27159
27526
  }
27160
27527
 
27161
27528
  // src/commands/platform-build.ts
27162
- async function platformBuild() {
27529
+ async function platformBuild(opts = {}) {
27163
27530
  const ws = resolveWorkspace();
27164
- const manifest = await buildAll(ws);
27531
+ const manifest = await buildAll(ws, { noCache: opts.noCache });
27165
27532
  ok(`Platform built \u2014 ${manifest.modules.length} module(s)`);
27166
27533
  }
27167
27534
 
27168
27535
  // src/commands/platform-deploy.ts
27169
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
27170
- import { join as join13 } from "path";
27536
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
27537
+ import { join as join15 } from "path";
27171
27538
 
27172
27539
  // src/deploy/bootstrap.ts
27173
- import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync10 } from "fs";
27540
+ import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync11 } from "fs";
27174
27541
  import { tmpdir as tmpdir3 } from "os";
27175
- import { join as join11 } from "path";
27542
+ import { join as join13 } from "path";
27176
27543
 
27177
27544
  // src/deploy/ansible.ts
27178
27545
  var {spawn: spawn2 } = globalThis.Bun;
27179
- import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync7 } from "fs";
27546
+ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
27180
27547
  import { tmpdir } from "os";
27181
- import { join as join8 } from "path";
27548
+ import { join as join10 } from "path";
27182
27549
 
27183
27550
  // src/deploy/assets.ts
27184
27551
  var TERRAFORM_MAIN_TF = `terraform {
@@ -27435,18 +27802,18 @@ var ASSETS = {
27435
27802
  }
27436
27803
  };
27437
27804
  async function materializeAssets(targetDir, files) {
27438
- const { mkdirSync: mkdirSync7, writeFileSync: writeFileSync7 } = await import("fs");
27439
- const { join: join8 } = await import("path");
27440
- mkdirSync7(targetDir, { recursive: true });
27805
+ const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync8 } = await import("fs");
27806
+ const { join: join10 } = await import("path");
27807
+ mkdirSync8(targetDir, { recursive: true });
27441
27808
  for (const [name, content] of Object.entries(files)) {
27442
- writeFileSync7(join8(targetDir, name), content);
27809
+ writeFileSync8(join10(targetDir, name), content);
27443
27810
  }
27444
27811
  }
27445
27812
 
27446
27813
  // src/deploy/ansible.ts
27447
27814
  async function runAnsible(inputs) {
27448
- const workDir = join8(tmpdir(), "arc-deploy", `ansible-${Date.now()}`);
27449
- mkdirSync7(workDir, { recursive: true });
27815
+ const workDir = join10(tmpdir(), "arc-deploy", `ansible-${Date.now()}`);
27816
+ mkdirSync8(workDir, { recursive: true });
27450
27817
  await materializeAssets(workDir, ASSETS.ansible);
27451
27818
  const user = inputs.asRoot ? "root" : inputs.target.user;
27452
27819
  const port = inputs.ansible?.sshPort ?? inputs.target.port;
@@ -27460,7 +27827,7 @@ async function runAnsible(inputs) {
27460
27827
  ""
27461
27828
  ].join(`
27462
27829
  `);
27463
- writeFileSync7(join8(workDir, "inventory.ini"), inventory);
27830
+ writeFileSync8(join10(workDir, "inventory.ini"), inventory);
27464
27831
  const extraVars = [
27465
27832
  `username=${inputs.target.user}`,
27466
27833
  `ssh_port=${port}`
@@ -27585,15 +27952,15 @@ function generateCompose({ cfg }) {
27585
27952
 
27586
27953
  // src/deploy/terraform.ts
27587
27954
  var {spawn: spawn3 } = globalThis.Bun;
27588
- import { existsSync as existsSync7, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
27955
+ import { existsSync as existsSync9, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
27589
27956
  import { tmpdir as tmpdir2 } from "os";
27590
- import { join as join9 } from "path";
27957
+ import { join as join11 } from "path";
27591
27958
  async function runTerraform(inputs) {
27592
- const workDir = join9(tmpdir2(), "arc-deploy", `tf-${Date.now()}`);
27593
- mkdirSync8(workDir, { recursive: true });
27959
+ const workDir = join11(tmpdir2(), "arc-deploy", `tf-${Date.now()}`);
27960
+ mkdirSync9(workDir, { recursive: true });
27594
27961
  await materializeAssets(workDir, ASSETS.terraform);
27595
27962
  const sshPubKey = inputs.tf.sshPublicKey ?? expandHome("~/.ssh/id_ed25519.pub");
27596
- if (!existsSync7(expandHome(sshPubKey))) {
27963
+ if (!existsSync9(expandHome(sshPubKey))) {
27597
27964
  throw new Error(`SSH public key not found at ${sshPubKey}. Set provision.terraform.sshPublicKey in deploy.arc.json.`);
27598
27965
  }
27599
27966
  const tfvars = [
@@ -27606,7 +27973,7 @@ async function runTerraform(inputs) {
27606
27973
  ].join(`
27607
27974
  `) + `
27608
27975
  `;
27609
- writeFileSync8(join9(workDir, "terraform.tfvars"), tfvars);
27976
+ writeFileSync9(join11(workDir, "terraform.tfvars"), tfvars);
27610
27977
  await runTf(workDir, ["init", "-input=false", "-no-color"]);
27611
27978
  await runTf(workDir, [
27612
27979
  "apply",
@@ -27658,21 +28025,21 @@ function expandHome(p) {
27658
28025
  }
27659
28026
 
27660
28027
  // src/deploy/config.ts
27661
- import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync9 } from "fs";
27662
- import { join as join10 } from "path";
28028
+ import { existsSync as existsSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync10 } from "fs";
28029
+ import { join as join12 } from "path";
27663
28030
  var DEPLOY_CONFIG_FILE = "deploy.arc.json";
27664
28031
  function deployConfigPath(rootDir) {
27665
- return join10(rootDir, DEPLOY_CONFIG_FILE);
28032
+ return join12(rootDir, DEPLOY_CONFIG_FILE);
27666
28033
  }
27667
28034
  function deployConfigExists(rootDir) {
27668
- return existsSync8(deployConfigPath(rootDir));
28035
+ return existsSync10(deployConfigPath(rootDir));
27669
28036
  }
27670
28037
  function loadDeployConfig(rootDir) {
27671
28038
  const path4 = deployConfigPath(rootDir);
27672
- if (!existsSync8(path4)) {
28039
+ if (!existsSync10(path4)) {
27673
28040
  throw new Error(`Missing ${DEPLOY_CONFIG_FILE} at ${path4}`);
27674
28041
  }
27675
- const raw = readFileSync7(path4, "utf-8");
28042
+ const raw = readFileSync9(path4, "utf-8");
27676
28043
  let parsed;
27677
28044
  try {
27678
28045
  parsed = JSON.parse(raw);
@@ -27683,7 +28050,7 @@ function loadDeployConfig(rootDir) {
27683
28050
  return validateDeployConfig(expanded);
27684
28051
  }
27685
28052
  function saveDeployConfig(rootDir, cfg) {
27686
- writeFileSync9(deployConfigPath(rootDir), JSON.stringify(cfg, null, 2) + `
28053
+ writeFileSync10(deployConfigPath(rootDir), JSON.stringify(cfg, null, 2) + `
27687
28054
  `);
27688
28055
  }
27689
28056
  var VAR_REGEX = /\$\{([A-Z0-9_]+)\}|\$([A-Z0-9_]+)/g;
@@ -28083,22 +28450,22 @@ async function bootstrap(inputs) {
28083
28450
  }
28084
28451
  async function upStack(inputs) {
28085
28452
  const { cfg } = inputs;
28086
- const workDir = join11(tmpdir3(), "arc-deploy", `stack-${Date.now()}`);
28087
- mkdirSync9(workDir, { recursive: true });
28088
- writeFileSync10(join11(workDir, "Caddyfile"), generateCaddyfile(cfg));
28089
- writeFileSync10(join11(workDir, "docker-compose.yml"), generateCompose({ cfg }));
28453
+ const workDir = join13(tmpdir3(), "arc-deploy", `stack-${Date.now()}`);
28454
+ mkdirSync10(workDir, { recursive: true });
28455
+ writeFileSync11(join13(workDir, "Caddyfile"), generateCaddyfile(cfg));
28456
+ writeFileSync11(join13(workDir, "docker-compose.yml"), generateCompose({ cfg }));
28090
28457
  await assertExec(cfg.target, `sudo mkdir -p ${cfg.target.remoteDir} && sudo chown ${cfg.target.user}:${cfg.target.user} ${cfg.target.remoteDir}`);
28091
28458
  for (const name of Object.keys(cfg.envs)) {
28092
28459
  await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/${name}`);
28093
28460
  }
28094
- await scpUpload(cfg.target, join11(workDir, "Caddyfile"), `${cfg.target.remoteDir}/Caddyfile`);
28095
- await scpUpload(cfg.target, join11(workDir, "docker-compose.yml"), `${cfg.target.remoteDir}/docker-compose.yml`);
28461
+ await scpUpload(cfg.target, join13(workDir, "Caddyfile"), `${cfg.target.remoteDir}/Caddyfile`);
28462
+ await scpUpload(cfg.target, join13(workDir, "docker-compose.yml"), `${cfg.target.remoteDir}/docker-compose.yml`);
28096
28463
  await assertExec(cfg.target, `cd ${cfg.target.remoteDir} && docker compose pull --ignore-pull-failures && docker compose up -d`);
28097
28464
  }
28098
28465
 
28099
28466
  // src/deploy/remote-sync.ts
28100
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
28101
- import { join as join12, relative as relative3 } from "path";
28467
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
28468
+ import { join as join14, relative as relative4 } from "path";
28102
28469
  function diffManifests(local, remote) {
28103
28470
  const remoteByName = new Map(remote.modules.map((m) => [m.name, m]));
28104
28471
  const changedModules = local.modules.filter((m) => remoteByName.get(m.name)?.hash !== m.hash);
@@ -28115,11 +28482,11 @@ async function syncEnv(inputs) {
28115
28482
  throw new Error(`Unknown env: ${env2}`);
28116
28483
  const remotePath = `${cfg.target.remoteDir}/${env2}`;
28117
28484
  await rsyncDir(cfg.target, projectDir, remotePath);
28118
- const localManifestPath = join12(ws.modulesDir, "manifest.json");
28119
- if (!existsSync9(localManifestPath)) {
28485
+ const localManifestPath = join14(ws.modulesDir, "manifest.json");
28486
+ if (!existsSync11(localManifestPath)) {
28120
28487
  throw new Error(`Local build missing at ${localManifestPath}. Run arc platform build first.`);
28121
28488
  }
28122
- const localManifest = JSON.parse(readFileSync8(localManifestPath, "utf-8"));
28489
+ const localManifest = JSON.parse(readFileSync10(localManifestPath, "utf-8"));
28123
28490
  const localPort = 15500 + hashEnvToOffset(env2);
28124
28491
  const tunnel = await openTunnel(cfg.target, localPort, "127.0.0.1", 2019);
28125
28492
  try {
@@ -28134,8 +28501,8 @@ async function syncEnv(inputs) {
28134
28501
  const shellFiles = collectFiles(ws.shellDir);
28135
28502
  const form = new FormData;
28136
28503
  for (const absPath of shellFiles) {
28137
- const rel = relative3(ws.shellDir, absPath);
28138
- form.append(rel, new Blob([readFileSync8(absPath)]), rel);
28504
+ const rel = relative4(ws.shellDir, absPath);
28505
+ form.append(rel, new Blob([readFileSync10(absPath)]), rel);
28139
28506
  }
28140
28507
  const res2 = await fetch(`${base2}/api/deploy/shell`, {
28141
28508
  method: "POST",
@@ -28147,9 +28514,9 @@ async function syncEnv(inputs) {
28147
28514
  if (diff.stylesChanged) {
28148
28515
  const form = new FormData;
28149
28516
  for (const name of ["styles.css", "theme.css"]) {
28150
- const p = join12(ws.arcDir, name);
28151
- if (existsSync9(p)) {
28152
- form.append(name, new Blob([readFileSync8(p)]), name);
28517
+ const p = join14(ws.arcDir, name);
28518
+ if (existsSync11(p)) {
28519
+ form.append(name, new Blob([readFileSync10(p)]), name);
28153
28520
  }
28154
28521
  }
28155
28522
  const res2 = await fetch(`${base2}/api/deploy/shell`, {
@@ -28162,8 +28529,8 @@ async function syncEnv(inputs) {
28162
28529
  if (diff.changedModules.length > 0) {
28163
28530
  const form = new FormData;
28164
28531
  for (const mod of diff.changedModules) {
28165
- const p = join12(ws.modulesDir, mod.file);
28166
- form.append(mod.file, new Blob([readFileSync8(p)]), mod.file);
28532
+ const p = join14(ws.modulesDir, mod.file);
28533
+ form.append(mod.file, new Blob([readFileSync10(p)]), mod.file);
28167
28534
  }
28168
28535
  const res2 = await fetch(`${base2}/api/deploy/modules`, {
28169
28536
  method: "POST",
@@ -28190,12 +28557,12 @@ async function syncEnv(inputs) {
28190
28557
  }
28191
28558
  }
28192
28559
  function collectFiles(dir) {
28193
- if (!existsSync9(dir))
28560
+ if (!existsSync11(dir))
28194
28561
  return [];
28195
- const { readdirSync: readdirSync5 } = __require("fs");
28562
+ const { readdirSync: readdirSync6 } = __require("fs");
28196
28563
  const out = [];
28197
- for (const entry of readdirSync5(dir, { withFileTypes: true })) {
28198
- const p = join12(dir, entry.name);
28564
+ for (const entry of readdirSync6(dir, { withFileTypes: true })) {
28565
+ const p = join14(dir, entry.name);
28199
28566
  if (entry.isDirectory())
28200
28567
  out.push(...collectFiles(p));
28201
28568
  else if (entry.isFile())
@@ -28907,13 +29274,13 @@ async function platformDeploy(envArg, options = {}) {
28907
29274
  err(`Unknown env "${envArg}". Known: ${Object.keys(cfg.envs).join(", ")}`);
28908
29275
  process.exit(1);
28909
29276
  })() : Object.keys(cfg.envs);
28910
- const manifestPath = join13(ws.modulesDir, "manifest.json");
28911
- const needBuild = options.rebuild || !existsSync10(manifestPath);
29277
+ const manifestPath = join15(ws.modulesDir, "manifest.json");
29278
+ const needBuild = options.rebuild || !existsSync12(manifestPath);
28912
29279
  if (needBuild && !options.skipBuild) {
28913
29280
  log2("Building platform...");
28914
- await buildAll(ws);
29281
+ await buildAll(ws, { noCache: options.rebuild });
28915
29282
  ok("Build complete");
28916
- } else if (!existsSync10(manifestPath)) {
29283
+ } else if (!existsSync12(manifestPath)) {
28917
29284
  err("No build found and --skip-build was set.");
28918
29285
  process.exit(1);
28919
29286
  }
@@ -28956,24 +29323,24 @@ async function platformDeploy(envArg, options = {}) {
28956
29323
  }
28957
29324
  function readCliVersion() {
28958
29325
  try {
28959
- const pkgPath = join13(import.meta.dir, "..", "..", "package.json");
28960
- const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
29326
+ const pkgPath = join15(import.meta.dir, "..", "..", "package.json");
29327
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
28961
29328
  return pkg.version ?? "unknown";
28962
29329
  } catch {
28963
29330
  return "unknown";
28964
29331
  }
28965
29332
  }
28966
29333
  async function hashDeployConfig(rootDir) {
28967
- const p2 = join13(rootDir, "deploy.arc.json");
28968
- const content = readFileSync9(p2);
29334
+ const p2 = join15(rootDir, "deploy.arc.json");
29335
+ const content = readFileSync11(p2);
28969
29336
  const hasher = new Bun.CryptoHasher("sha256");
28970
29337
  hasher.update(content);
28971
29338
  return hasher.digest("hex").slice(0, 16);
28972
29339
  }
28973
29340
 
28974
29341
  // src/commands/platform-dev.ts
28975
- import { existsSync as existsSync13, watch } from "fs";
28976
- import { join as join16 } from "path";
29342
+ import { existsSync as existsSync15, watch } from "fs";
29343
+ import { join as join18 } from "path";
28977
29344
 
28978
29345
  // ../host/src/create-server.ts
28979
29346
  var import_jsonwebtoken = __toESM(require_jsonwebtoken(), 1);
@@ -30292,7 +30659,10 @@ async function createArcServer(config) {
30292
30659
  "Access-Control-Allow-Origin": origin,
30293
30660
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
30294
30661
  "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Arc-Scope, X-Arc-Tokens",
30295
- "Access-Control-Allow-Credentials": "true"
30662
+ "Access-Control-Allow-Credentials": "true",
30663
+ "Cross-Origin-Opener-Policy": "same-origin",
30664
+ "Cross-Origin-Embedder-Policy": "require-corp",
30665
+ "Cross-Origin-Resource-Policy": "cross-origin"
30296
30666
  };
30297
30667
  }
30298
30668
  const corsHeaders = buildCorsHeaders();
@@ -30420,12 +30790,12 @@ async function createArcServer(config) {
30420
30790
  };
30421
30791
  }
30422
30792
  // src/platform/server.ts
30423
- import { existsSync as existsSync12, mkdirSync as mkdirSync11 } from "fs";
30424
- import { join as join15 } from "path";
30793
+ import { existsSync as existsSync14, mkdirSync as mkdirSync12 } from "fs";
30794
+ import { join as join17 } from "path";
30425
30795
 
30426
30796
  // src/platform/deploy-api.ts
30427
- import { existsSync as existsSync11, mkdirSync as mkdirSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync11 } from "fs";
30428
- import { dirname as dirname7, join as join14, normalize as normalize2, relative as relative4, resolve } from "path";
30797
+ import { existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "fs";
30798
+ import { dirname as dirname7, join as join16, normalize as normalize2, relative as relative5, resolve } from "path";
30429
30799
  function createDeployApiHandler(opts) {
30430
30800
  return async (req, url, ctx) => {
30431
30801
  const p3 = url.pathname;
@@ -30442,7 +30812,7 @@ function createDeployApiHandler(opts) {
30442
30812
  if (!validateManifest(body)) {
30443
30813
  return Response.json({ error: "Invalid manifest body" }, { status: 400, headers: ctx.corsHeaders });
30444
30814
  }
30445
- writeFileSync11(join14(opts.ws.modulesDir, "manifest.json"), JSON.stringify(body, null, 2));
30815
+ writeFileSync12(join16(opts.ws.modulesDir, "manifest.json"), JSON.stringify(body, null, 2));
30446
30816
  opts.setManifest(body);
30447
30817
  opts.notifyReload(body);
30448
30818
  return Response.json({ ok: true, moduleCount: body.modules.length }, { headers: ctx.corsHeaders });
@@ -30468,8 +30838,8 @@ function createDeployApiHandler(opts) {
30468
30838
  const writtenShell = await writeUploadedFileList(shellFiles, opts.ws.shellDir);
30469
30839
  const writtenRoot = [];
30470
30840
  for (const { name, file } of rootFiles) {
30471
- const target = join14(opts.ws.arcDir, name);
30472
- writeFileSync11(target, Buffer.from(await file.arrayBuffer()));
30841
+ const target = join16(opts.ws.arcDir, name);
30842
+ writeFileSync12(target, Buffer.from(await file.arrayBuffer()));
30473
30843
  writtenRoot.push(name);
30474
30844
  }
30475
30845
  return Response.json({ ok: true, written: [...writtenShell, ...writtenRoot] }, { headers: ctx.corsHeaders });
@@ -30494,16 +30864,16 @@ function isFile(v3) {
30494
30864
  async function writeUploadedFileList(files, targetDir) {
30495
30865
  const written = [];
30496
30866
  const safeRoot = resolve(targetDir);
30497
- mkdirSync10(safeRoot, { recursive: true });
30867
+ mkdirSync11(safeRoot, { recursive: true });
30498
30868
  for (const file of files) {
30499
30869
  const rel = normalize2(file.name);
30500
30870
  const full = resolve(safeRoot, rel);
30501
30871
  if (!full.startsWith(safeRoot + "/") && full !== safeRoot) {
30502
30872
  throw new Error(`Path traversal rejected: ${file.name}`);
30503
30873
  }
30504
- mkdirSync10(dirname7(full), { recursive: true });
30505
- writeFileSync11(full, Buffer.from(await file.arrayBuffer()));
30506
- written.push(relative4(safeRoot, full) || rel);
30874
+ mkdirSync11(dirname7(full), { recursive: true });
30875
+ writeFileSync12(full, Buffer.from(await file.arrayBuffer()));
30876
+ written.push(relative5(safeRoot, full) || rel);
30507
30877
  }
30508
30878
  return written;
30509
30879
  }
@@ -30570,14 +30940,15 @@ var MIME = {
30570
30940
  ".ico": "image/x-icon",
30571
30941
  ".woff2": "font/woff2",
30572
30942
  ".woff": "font/woff",
30573
- ".ttf": "font/ttf"
30943
+ ".ttf": "font/ttf",
30944
+ ".wasm": "application/wasm"
30574
30945
  };
30575
30946
  function getMime(path4) {
30576
30947
  const ext2 = path4.substring(path4.lastIndexOf("."));
30577
30948
  return MIME[ext2] ?? "application/octet-stream";
30578
30949
  }
30579
30950
  function serveFile(filePath, headers = {}) {
30580
- if (!existsSync12(filePath))
30951
+ if (!existsSync14(filePath))
30581
30952
  return new Response("Not Found", { status: 404 });
30582
30953
  return new Response(Bun.file(filePath), {
30583
30954
  headers: { "Content-Type": getMime(filePath), ...headers }
@@ -30666,7 +31037,7 @@ function staticFilesHandler(ws, devMode, moduleAccessMap) {
30666
31037
  return (_req, url, ctx) => {
30667
31038
  const path4 = url.pathname;
30668
31039
  if (path4.startsWith("/shell/"))
30669
- return serveFile(join15(ws.shellDir, path4.slice(7)), ctx.corsHeaders);
31040
+ return serveFile(join17(ws.shellDir, path4.slice(7)), ctx.corsHeaders);
30670
31041
  if (path4.startsWith("/modules/")) {
30671
31042
  const fileWithParams = path4.slice(9);
30672
31043
  const filename = fileWithParams.split("?")[0];
@@ -30678,23 +31049,25 @@ function staticFilesHandler(ws, devMode, moduleAccessMap) {
30678
31049
  return new Response("Forbidden", { status: 403, headers: ctx.corsHeaders });
30679
31050
  }
30680
31051
  }
30681
- return serveFile(join15(ws.modulesDir, filename), {
31052
+ return serveFile(join17(ws.modulesDir, filename), {
30682
31053
  ...ctx.corsHeaders,
30683
31054
  "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
30684
31055
  });
30685
31056
  }
30686
31057
  if (path4.startsWith("/locales/"))
30687
- return serveFile(join15(ws.arcDir, path4.slice(1)), ctx.corsHeaders);
31058
+ return serveFile(join17(ws.arcDir, path4.slice(1)), ctx.corsHeaders);
31059
+ if (path4.startsWith("/assets/"))
31060
+ return serveFile(join17(ws.assetsDir, path4.slice(8)), ctx.corsHeaders);
30688
31061
  if (path4 === "/styles.css")
30689
- return serveFile(join15(ws.arcDir, "styles.css"), ctx.corsHeaders);
31062
+ return serveFile(join17(ws.arcDir, "styles.css"), ctx.corsHeaders);
30690
31063
  if (path4 === "/theme.css")
30691
- return serveFile(join15(ws.arcDir, "theme.css"), ctx.corsHeaders);
31064
+ return serveFile(join17(ws.arcDir, "theme.css"), ctx.corsHeaders);
30692
31065
  if ((path4 === "/manifest.json" || path4 === "/manifest.webmanifest") && ws.manifest) {
30693
31066
  return serveFile(ws.manifest.path, ctx.corsHeaders);
30694
31067
  }
30695
31068
  if (path4.lastIndexOf(".") > path4.lastIndexOf("/")) {
30696
- const publicFile = join15(ws.publicDir, path4.slice(1));
30697
- if (existsSync12(publicFile))
31069
+ const publicFile = join17(ws.publicDir, path4.slice(1));
31070
+ if (existsSync14(publicFile))
30698
31071
  return serveFile(publicFile, ctx.corsHeaders);
30699
31072
  }
30700
31073
  return null;
@@ -30791,7 +31164,10 @@ async function startPlatformServer(opts) {
30791
31164
  const cors = {
30792
31165
  "Access-Control-Allow-Origin": "*",
30793
31166
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
30794
- "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Arc-Tokens"
31167
+ "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Arc-Tokens",
31168
+ "Cross-Origin-Opener-Policy": "same-origin",
31169
+ "Cross-Origin-Embedder-Policy": "require-corp",
31170
+ "Cross-Origin-Resource-Policy": "cross-origin"
30795
31171
  };
30796
31172
  const server = Bun.serve({
30797
31173
  port,
@@ -30829,10 +31205,10 @@ async function startPlatformServer(opts) {
30829
31205
  };
30830
31206
  }
30831
31207
  const { createBunSQLiteAdapterFactory: createBunSQLiteAdapterFactory2 } = await Promise.resolve().then(() => (init_dist(), exports_dist2));
30832
- const dbPath = opts.dbPath || join15(ws.arcDir, "data", "arc.db");
31208
+ const dbPath = opts.dbPath || join17(ws.arcDir, "data", "arc.db");
30833
31209
  const dbDir = dbPath.substring(0, dbPath.lastIndexOf("/"));
30834
31210
  if (dbDir)
30835
- mkdirSync11(dbDir, { recursive: true });
31211
+ mkdirSync12(dbDir, { recursive: true });
30836
31212
  const arcServer = await createArcServer({
30837
31213
  context,
30838
31214
  dbAdapterFactory: createBunSQLiteAdapterFactory2(dbPath),
@@ -30857,10 +31233,10 @@ async function startPlatformServer(opts) {
30857
31233
  }
30858
31234
 
30859
31235
  // src/commands/platform-dev.ts
30860
- async function platformDev() {
31236
+ async function platformDev(opts = {}) {
30861
31237
  const ws = resolveWorkspace();
30862
31238
  const port = 5005;
30863
- let manifest = await buildAll(ws);
31239
+ let manifest = await buildAll(ws, { noCache: opts.noCache });
30864
31240
  log2("Loading server context...");
30865
31241
  const { context, moduleAccess } = await loadServerContext(ws.packages);
30866
31242
  if (context) {
@@ -30875,7 +31251,7 @@ async function platformDev() {
30875
31251
  manifest,
30876
31252
  context,
30877
31253
  moduleAccess,
30878
- dbPath: join16(ws.rootDir, ".arc", "data", "dev.db"),
31254
+ dbPath: join18(ws.rootDir, ".arc", "data", "dev.db"),
30879
31255
  devMode: true,
30880
31256
  arcEntries
30881
31257
  });
@@ -30885,53 +31261,44 @@ async function platformDev() {
30885
31261
  log2("Watching for changes...");
30886
31262
  let rebuildTimer = null;
30887
31263
  let isRebuilding = false;
31264
+ const triggerRebuild = () => {
31265
+ if (rebuildTimer)
31266
+ clearTimeout(rebuildTimer);
31267
+ rebuildTimer = setTimeout(async () => {
31268
+ if (isRebuilding)
31269
+ return;
31270
+ isRebuilding = true;
31271
+ log2("Rebuilding...");
31272
+ try {
31273
+ manifest = await buildAll(ws);
31274
+ platform3.setManifest(manifest);
31275
+ platform3.notifyReload(manifest);
31276
+ ok(`Rebuilt \u2014 ${manifest.modules.length} module(s)`);
31277
+ } catch (e2) {
31278
+ console.error(`Rebuild failed: ${e2}`);
31279
+ } finally {
31280
+ isRebuilding = false;
31281
+ }
31282
+ }, 300);
31283
+ };
30888
31284
  for (const pkg of ws.packages) {
30889
- const srcDir = join16(pkg.path, "src");
30890
- if (!existsSync13(srcDir))
31285
+ const srcDir = join18(pkg.path, "src");
31286
+ if (!existsSync15(srcDir))
30891
31287
  continue;
30892
31288
  watch(srcDir, { recursive: true }, (_event, filename) => {
30893
31289
  if (!filename || filename.includes(".arc") || filename.endsWith(".d.ts") || filename.includes("node_modules") || filename.includes("dist"))
30894
31290
  return;
30895
31291
  if (!filename.endsWith(".ts") && !filename.endsWith(".tsx"))
30896
31292
  return;
30897
- if (rebuildTimer)
30898
- clearTimeout(rebuildTimer);
30899
- rebuildTimer = setTimeout(async () => {
30900
- if (isRebuilding)
30901
- return;
30902
- isRebuilding = true;
30903
- log2("Rebuilding...");
30904
- try {
30905
- manifest = await buildPackages(ws.rootDir, ws.modulesDir, ws.packages);
30906
- await buildStyles(ws.rootDir, ws.arcDir);
30907
- platform3.setManifest(manifest);
30908
- platform3.notifyReload(manifest);
30909
- ok(`Rebuilt ${manifest.modules.length} module(s)`);
30910
- } catch (e2) {
30911
- console.error(`Rebuild failed: ${e2}`);
30912
- } finally {
30913
- isRebuilding = false;
30914
- }
30915
- }, 300);
31293
+ triggerRebuild();
30916
31294
  });
30917
31295
  }
30918
- const localesDir = join16(ws.rootDir, "locales");
30919
- if (existsSync13(localesDir)) {
30920
- let poTimer = null;
31296
+ const localesDir = join18(ws.rootDir, "locales");
31297
+ if (existsSync15(localesDir)) {
30921
31298
  watch(localesDir, { recursive: false }, (_event, filename) => {
30922
31299
  if (!filename?.endsWith(".po"))
30923
31300
  return;
30924
- if (poTimer)
30925
- clearTimeout(poTimer);
30926
- poTimer = setTimeout(async () => {
30927
- try {
30928
- compileAllCatalogs(localesDir, join16(ws.arcDir, "locales"));
30929
- ok("Translations recompiled");
30930
- platform3.notifyReload(manifest);
30931
- } catch (e2) {
30932
- console.error(`Translation compile failed: ${e2}`);
30933
- }
30934
- }, 200);
31301
+ triggerRebuild();
30935
31302
  });
30936
31303
  }
30937
31304
  const cleanup = () => {
@@ -30943,17 +31310,17 @@ async function platformDev() {
30943
31310
  }
30944
31311
 
30945
31312
  // src/commands/platform-start.ts
30946
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
30947
- import { join as join17 } from "path";
31313
+ import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
31314
+ import { join as join19 } from "path";
30948
31315
  async function platformStart() {
30949
31316
  const ws = resolveWorkspace();
30950
31317
  const port = parseInt(process.env.PORT || "5005", 10);
30951
- const manifestPath = join17(ws.modulesDir, "manifest.json");
30952
- if (!existsSync14(manifestPath)) {
31318
+ const manifestPath = join19(ws.modulesDir, "manifest.json");
31319
+ if (!existsSync16(manifestPath)) {
30953
31320
  err("No build found. Run `arc platform build` first.");
30954
31321
  process.exit(1);
30955
31322
  }
30956
- const manifest = JSON.parse(readFileSync11(manifestPath, "utf-8"));
31323
+ const manifest = JSON.parse(readFileSync13(manifestPath, "utf-8"));
30957
31324
  log2("Loading server context...");
30958
31325
  const { context, moduleAccess } = await loadServerContext(ws.packages);
30959
31326
  if (context) {
@@ -30971,7 +31338,7 @@ async function platformStart() {
30971
31338
  manifest,
30972
31339
  context,
30973
31340
  moduleAccess,
30974
- dbPath: join17(ws.rootDir, ".arc", "data", "prod.db"),
31341
+ dbPath: join19(ws.rootDir, ".arc", "data", "prod.db"),
30975
31342
  devMode: false,
30976
31343
  deployApi,
30977
31344
  arcEntries
@@ -30993,8 +31360,8 @@ program2.name("arc").description("CLI tool for Arc framework").version("0.0.3");
30993
31360
  program2.command("dev").description("Run development mode for Arc framework").action(dev);
30994
31361
  program2.command("build").description("Build all clients and declarations").action(build);
30995
31362
  var platform3 = program2.command("platform").description("Platform commands \u2014 run full stack (server + UI)");
30996
- platform3.command("dev").description("Start platform in dev mode (Bun server + Vite HMR)").action(platformDev);
30997
- platform3.command("build").description("Build platform for production").action(platformBuild);
31363
+ platform3.command("dev").description("Start platform in dev mode (Bun server + Vite HMR)").option("--no-cache", "Force full rebuild on startup").action((opts) => platformDev({ noCache: opts.cache === false }));
31364
+ platform3.command("build").description("Build platform for production").option("--no-cache", "Force full rebuild").action((opts) => platformBuild({ noCache: opts.cache === false }));
30998
31365
  platform3.command("start").description("Start platform in production mode (requires prior build)").action(platformStart);
30999
31366
  platform3.command("deploy [env]").description("Deploy platform to a remote server (reads deploy.arc.json, surveys if missing)").option("--skip-build", "Skip local build step").option("--rebuild", "Force rebuild before deploy").action((env2, opts) => platformDeploy(env2, opts));
31000
31367
  program2.parse(process.argv);