@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 +400 -194
- package/package.json +10 -10
- package/src/builder/module-builder.ts +43 -2
- package/src/builder/seed-guard.ts +143 -0
- package/src/deploy/bootstrap.ts +35 -1
- package/src/deploy/remote-state.ts +9 -0
- package/src/platform/server.ts +190 -52
- package/src/platform/shared.ts +8 -2
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
|
|
31326
|
-
import { dirname as dirname8, join as
|
|
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
|
|
32634
|
-
import { basename as basename4, join as
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
32737
|
-
import { basename as basename5, dirname as dirname7, join as
|
|
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(
|
|
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 =
|
|
32761
|
-
if (
|
|
32866
|
+
const rootPkgPath = join13(rootDir, "package.json");
|
|
32867
|
+
if (existsSync11(rootPkgPath)) {
|
|
32762
32868
|
try {
|
|
32763
|
-
const rootPkg = JSON.parse(
|
|
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 =
|
|
32881
|
+
const manifestPath = join13(arcDir, "package.json");
|
|
32776
32882
|
writeFileSync9(manifestPath, JSON.stringify(manifest, null, 2) + `
|
|
32777
32883
|
`);
|
|
32778
|
-
const hash = sha256Hex2(
|
|
32779
|
-
writeFileSync9(
|
|
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(
|
|
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(
|
|
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(
|
|
32952
|
+
const rootPkg = JSON.parse(readFileSync13(packageJsonPath, "utf-8"));
|
|
32847
32953
|
const appName = rootPkg.name ?? "Arc App";
|
|
32848
|
-
const arcDir =
|
|
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 =
|
|
32859
|
-
if (
|
|
32964
|
+
const manifestPath = join14(rootDir, name);
|
|
32965
|
+
if (existsSync12(manifestPath)) {
|
|
32860
32966
|
try {
|
|
32861
|
-
const data = JSON.parse(
|
|
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:
|
|
32881
|
-
assetsDir:
|
|
32882
|
-
publicDir:
|
|
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 =
|
|
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,
|
|
33005
|
+
const accessMap = await extractAccessMap(ws.rootDir, join14(serverDir, serverEntry));
|
|
32899
33006
|
mkdirSync10(ws.arcDir, { recursive: true });
|
|
32900
|
-
writeFileSync10(
|
|
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,
|
|
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,
|
|
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(
|
|
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 =
|
|
32980
|
-
return
|
|
33086
|
+
const resolved = join14(pkgDir, from);
|
|
33087
|
+
return existsSync12(resolved) ? resolved : null;
|
|
32981
33088
|
}
|
|
32982
33089
|
const candidates = [
|
|
32983
|
-
|
|
32984
|
-
|
|
33090
|
+
join14(rootDir, "node_modules", from),
|
|
33091
|
+
join14(pkgDir, "node_modules", from)
|
|
32985
33092
|
];
|
|
32986
33093
|
for (const c of candidates) {
|
|
32987
|
-
if (
|
|
33094
|
+
if (existsSync12(c))
|
|
32988
33095
|
return c;
|
|
32989
33096
|
}
|
|
32990
|
-
const bunCacheDir =
|
|
32991
|
-
if (
|
|
32992
|
-
for (const entry of
|
|
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 =
|
|
32996
|
-
if (
|
|
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 =
|
|
33004
|
-
if (!
|
|
33110
|
+
const pkgJsonPath = join14(pkgDir, "package.json");
|
|
33111
|
+
if (!existsSync12(pkgJsonPath))
|
|
33005
33112
|
return [];
|
|
33006
33113
|
try {
|
|
33007
|
-
const pkg = JSON.parse(
|
|
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 =
|
|
33018
|
-
if (!
|
|
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
|
|
33128
|
+
for (const entry of readdirSync7(arcDir, { withFileTypes: true })) {
|
|
33022
33129
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
33023
33130
|
continue;
|
|
33024
|
-
const pkgDir =
|
|
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) =>
|
|
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 =
|
|
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(
|
|
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 =
|
|
33069
|
-
const platformPkg = JSON.parse(
|
|
33070
|
-
const platformEntry =
|
|
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 =
|
|
33073
|
-
if (!
|
|
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
|
|
33104
|
-
import { dirname as dirname11, join as
|
|
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
|
|
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
|
|
33222
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
33116
33223
|
import { homedir, tmpdir } from "os";
|
|
33117
|
-
import { join as
|
|
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:
|
|
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(
|
|
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("~") ?
|
|
33442
|
-
return
|
|
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 =
|
|
33446
|
-
if (
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
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 (!
|
|
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(
|
|
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
|
|
35111
|
-
import { join as
|
|
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
|
|
35115
|
-
import { join as
|
|
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 =
|
|
35118
|
-
const globals =
|
|
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 =
|
|
35122
|
-
if (
|
|
35123
|
-
perEnv[name] = parseEnvFile(
|
|
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 =
|
|
35140
|
-
if (
|
|
35141
|
-
const existing = parseEnvFile(
|
|
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 =
|
|
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
|
|
35291
|
+
return join18(rootDir, DEPLOY_CONFIG_FILE);
|
|
35185
35292
|
}
|
|
35186
35293
|
function deployConfigExists(rootDir) {
|
|
35187
|
-
return
|
|
35294
|
+
return existsSync16(deployConfigPath(rootDir));
|
|
35188
35295
|
}
|
|
35189
35296
|
function loadDeployConfig(rootDir) {
|
|
35190
35297
|
const path4 = deployConfigPath(rootDir);
|
|
35191
|
-
if (!
|
|
35298
|
+
if (!existsSync16(path4)) {
|
|
35192
35299
|
throw new Error(`Missing ${DEPLOY_CONFIG_FILE} at ${path4}`);
|
|
35193
35300
|
}
|
|
35194
|
-
const raw =
|
|
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 =
|
|
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
|
|
35561
|
+
import { existsSync as existsSync17 } from "fs";
|
|
35455
35562
|
import { homedir as homedir3 } from "os";
|
|
35456
|
-
import { join as
|
|
35563
|
+
import { join as join19 } from "path";
|
|
35457
35564
|
function pickSshKey(target) {
|
|
35458
35565
|
if (target.sshKey) {
|
|
35459
|
-
const expanded = target.sshKey.startsWith("~") ?
|
|
35460
|
-
return
|
|
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 =
|
|
35464
|
-
if (
|
|
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=${
|
|
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
|
|
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 =
|
|
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(
|
|
35757
|
-
writeFileSync14(
|
|
35758
|
-
writeFileSync14(
|
|
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 =
|
|
35879
|
+
const fullPath = join20(workDir, relPath);
|
|
35771
35880
|
mkdirSync13(dirname9(fullPath), { recursive: true });
|
|
35772
35881
|
writeFileSync14(fullPath, contents);
|
|
35773
35882
|
}
|
|
35774
|
-
writeFileSync14(
|
|
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,
|
|
35782
|
-
await scpUpload(cfg.target,
|
|
35783
|
-
await scpUpload(cfg.target,
|
|
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(
|
|
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,
|
|
35900
|
+
await scpUpload(cfg.target, join20(workDir, relPath), `${cfg.target.remoteDir}/${relPath}`);
|
|
35792
35901
|
}
|
|
35793
|
-
await scpUpload(cfg.target,
|
|
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
|
|
36058
|
+
existsSync as existsSync18,
|
|
35949
36059
|
mkdirSync as mkdirSync14,
|
|
35950
|
-
readFileSync as
|
|
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
|
|
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 =
|
|
36006
|
-
if (!
|
|
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 =
|
|
36126
|
+
const dockerfileDir = join21(tmpdir3(), `arc-image-${Date.now()}`);
|
|
36017
36127
|
mkdirSync14(dockerfileDir, { recursive: true });
|
|
36018
|
-
const dockerfilePath =
|
|
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 =
|
|
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 =
|
|
36052
|
-
if (
|
|
36161
|
+
const candidate = join21(cur, "package.json");
|
|
36162
|
+
if (existsSync18(candidate)) {
|
|
36053
36163
|
try {
|
|
36054
|
-
const pkg = JSON.parse(
|
|
36164
|
+
const pkg = JSON.parse(readFileSync16(candidate, "utf-8"));
|
|
36055
36165
|
if (pkg.name === "@arcote.tech/arc-cli") {
|
|
36056
|
-
const distIndex =
|
|
36057
|
-
if (!
|
|
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(
|
|
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 =
|
|
36096
|
-
const hasLocales =
|
|
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 (
|
|
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 =
|
|
37053
|
+
const manifestPath = join22(ws.arcDir, "manifest.json");
|
|
36944
37054
|
if (!options.imageTag) {
|
|
36945
|
-
const needBuild = options.rebuild || !
|
|
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 (!
|
|
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 =
|
|
37021
|
-
if (
|
|
37022
|
-
const pkg = JSON.parse(
|
|
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 =
|
|
37039
|
-
const content =
|
|
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
|
|
37049
|
-
import { join as
|
|
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
|
|
38679
|
-
import { join as
|
|
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
|
|
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=${
|
|
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">${
|
|
38715
|
-
<script>window.__ARC_PEERS__=${
|
|
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="
|
|
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>${
|
|
38723
|
-
<link rel="icon" href="${
|
|
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 (!
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
38943
|
-
if (
|
|
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 (
|
|
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(
|
|
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 ||
|
|
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 ??
|
|
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 =
|
|
39304
|
-
if (!
|
|
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(
|
|
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) =>
|
|
39393
|
-
const packageOf = (absFile) => ws.packages.find((p3) => absFile.startsWith(
|
|
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 =
|
|
39634
|
+
const file = join24(rootDir, ".arc", "app-secret");
|
|
39429
39635
|
try {
|
|
39430
|
-
const saved =
|
|
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(
|
|
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 =
|
|
39653
|
+
const file = join24(rootDir, ".arc", "pairing-token");
|
|
39448
39654
|
try {
|
|
39449
|
-
const saved =
|
|
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(
|
|
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(
|
|
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 =
|
|
39501
|
-
if (!
|
|
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 =
|
|
39512
|
-
if (
|
|
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
|
|
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 =
|
|
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 });
|