@arcote.tech/arc-cli 0.8.4 → 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 +397 -193
- 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 +13 -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
|
];
|
|
@@ -35747,7 +35854,7 @@ async function isRegistryRunning(cfg) {
|
|
|
35747
35854
|
}
|
|
35748
35855
|
async function upStack(inputs) {
|
|
35749
35856
|
const { cfg } = inputs;
|
|
35750
|
-
const workDir =
|
|
35857
|
+
const workDir = join20(tmpdir2(), "arc-deploy", `stack-${Date.now()}`);
|
|
35751
35858
|
mkdirSync13(workDir, { recursive: true });
|
|
35752
35859
|
await assertRegistryDnsResolves(cfg);
|
|
35753
35860
|
const password = process.env[cfg.registry.passwordEnv];
|
|
@@ -35755,9 +35862,9 @@ async function upStack(inputs) {
|
|
|
35755
35862
|
throw new Error(`Registry password env var ${cfg.registry.passwordEnv} is not set. ` + `Set it (e.g. \`export ${cfg.registry.passwordEnv}=...\`) before bootstrap.`);
|
|
35756
35863
|
}
|
|
35757
35864
|
const htpasswdLine = await generateHtpasswd(cfg.registry.username, password);
|
|
35758
|
-
writeFileSync14(
|
|
35759
|
-
writeFileSync14(
|
|
35760
|
-
writeFileSync14(
|
|
35865
|
+
writeFileSync14(join20(workDir, "htpasswd"), htpasswdLine);
|
|
35866
|
+
writeFileSync14(join20(workDir, "Caddyfile"), generateCaddyfile(cfg));
|
|
35867
|
+
writeFileSync14(join20(workDir, "docker-compose.yml"), generateCompose({ cfg }));
|
|
35761
35868
|
let observabilityFiles = null;
|
|
35762
35869
|
let observabilityHtpasswd = null;
|
|
35763
35870
|
if (cfg.observability?.enabled) {
|
|
@@ -35769,30 +35876,30 @@ async function upStack(inputs) {
|
|
|
35769
35876
|
}
|
|
35770
35877
|
observabilityHtpasswd = await generateCaddyBasicAuthLine("admin", adminPassword);
|
|
35771
35878
|
for (const [relPath, contents] of Object.entries(observabilityFiles)) {
|
|
35772
|
-
const fullPath =
|
|
35879
|
+
const fullPath = join20(workDir, relPath);
|
|
35773
35880
|
mkdirSync13(dirname9(fullPath), { recursive: true });
|
|
35774
35881
|
writeFileSync14(fullPath, contents);
|
|
35775
35882
|
}
|
|
35776
|
-
writeFileSync14(
|
|
35883
|
+
writeFileSync14(join20(workDir, "observability-htpasswd"), observabilityHtpasswd);
|
|
35777
35884
|
}
|
|
35778
35885
|
await assertExec(cfg.target, `sudo mkdir -p ${cfg.target.remoteDir} && sudo chown ${cfg.target.user}:${cfg.target.user} ${cfg.target.remoteDir}`);
|
|
35779
35886
|
for (const name of Object.keys(cfg.envs)) {
|
|
35780
35887
|
await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/${name}`);
|
|
35781
35888
|
}
|
|
35782
35889
|
await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/registry-auth`);
|
|
35783
|
-
await scpUpload(cfg.target,
|
|
35784
|
-
await scpUpload(cfg.target,
|
|
35785
|
-
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`);
|
|
35786
35893
|
if (observabilityFiles && observabilityHtpasswd) {
|
|
35787
35894
|
await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/observability/grafana-dashboards ${cfg.target.remoteDir}/observability/grafana-alerting`);
|
|
35788
35895
|
for (const relPath of Object.keys(observabilityFiles)) {
|
|
35789
|
-
const localDir = dirname9(
|
|
35896
|
+
const localDir = dirname9(join20(workDir, relPath));
|
|
35790
35897
|
mkdirSync13(localDir, { recursive: true });
|
|
35791
35898
|
}
|
|
35792
35899
|
for (const relPath of Object.keys(observabilityFiles)) {
|
|
35793
|
-
await scpUpload(cfg.target,
|
|
35900
|
+
await scpUpload(cfg.target, join20(workDir, relPath), `${cfg.target.remoteDir}/${relPath}`);
|
|
35794
35901
|
}
|
|
35795
|
-
await scpUpload(cfg.target,
|
|
35902
|
+
await scpUpload(cfg.target, join20(workDir, "observability-htpasswd"), `${cfg.target.remoteDir}/observability-htpasswd`);
|
|
35796
35903
|
}
|
|
35797
35904
|
await assertExec(cfg.target, `touch ${cfg.target.remoteDir}/.env`);
|
|
35798
35905
|
if (cfg.observability?.enabled) {
|
|
@@ -35814,6 +35921,7 @@ async function upStack(inputs) {
|
|
|
35814
35921
|
await writeEnvLine(cfg.target, `${cfg.target.remoteDir}/.env`, key, value);
|
|
35815
35922
|
}
|
|
35816
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`);
|
|
35817
35925
|
await sshDockerLogin(cfg);
|
|
35818
35926
|
const knownEnvs = await listConfiguredEnvs(cfg);
|
|
35819
35927
|
if (knownEnvs.length > 0) {
|
|
@@ -35947,14 +36055,14 @@ var {spawn: spawn5 } = globalThis.Bun;
|
|
|
35947
36055
|
import { createHash as createHash3 } from "crypto";
|
|
35948
36056
|
import {
|
|
35949
36057
|
copyFileSync as copyFileSync2,
|
|
35950
|
-
existsSync as
|
|
36058
|
+
existsSync as existsSync18,
|
|
35951
36059
|
mkdirSync as mkdirSync14,
|
|
35952
|
-
readFileSync as
|
|
36060
|
+
readFileSync as readFileSync16,
|
|
35953
36061
|
realpathSync as realpathSync2,
|
|
35954
36062
|
writeFileSync as writeFileSync15
|
|
35955
36063
|
} from "fs";
|
|
35956
36064
|
import { tmpdir as tmpdir3 } from "os";
|
|
35957
|
-
import { dirname as dirname10, join as
|
|
36065
|
+
import { dirname as dirname10, join as join21 } from "path";
|
|
35958
36066
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
35959
36067
|
|
|
35960
36068
|
// src/deploy/image-template.ts
|
|
@@ -36004,8 +36112,8 @@ function generateDockerfile(inputs) {
|
|
|
36004
36112
|
// src/deploy/image.ts
|
|
36005
36113
|
async function buildImage(ws, opts) {
|
|
36006
36114
|
await ensureDocker();
|
|
36007
|
-
const manifestPath =
|
|
36008
|
-
if (!
|
|
36115
|
+
const manifestPath = join21(ws.arcDir, "manifest.json");
|
|
36116
|
+
if (!existsSync18(manifestPath)) {
|
|
36009
36117
|
throw new Error(`No build manifest at ${manifestPath}. Run \`arc platform build\` first or omit --skip-build.`);
|
|
36010
36118
|
}
|
|
36011
36119
|
embedCliBundle(ws);
|
|
@@ -36015,9 +36123,9 @@ async function buildImage(ws, opts) {
|
|
|
36015
36123
|
const dockerfileInputs = collectDockerfileInputs(ws);
|
|
36016
36124
|
const dockerfile = generateDockerfile(dockerfileInputs);
|
|
36017
36125
|
const buildContextDir = ws.rootDir;
|
|
36018
|
-
const dockerfileDir =
|
|
36126
|
+
const dockerfileDir = join21(tmpdir3(), `arc-image-${Date.now()}`);
|
|
36019
36127
|
mkdirSync14(dockerfileDir, { recursive: true });
|
|
36020
|
-
const dockerfilePath =
|
|
36128
|
+
const dockerfilePath = join21(dockerfileDir, "Dockerfile");
|
|
36021
36129
|
writeFileSync15(dockerfilePath, dockerfile);
|
|
36022
36130
|
const buildArgs = [
|
|
36023
36131
|
"build",
|
|
@@ -36043,20 +36151,20 @@ async function buildImage(ws, opts) {
|
|
|
36043
36151
|
}
|
|
36044
36152
|
function embedCliBundle(ws) {
|
|
36045
36153
|
const source = locateCliBundle();
|
|
36046
|
-
const target =
|
|
36154
|
+
const target = join21(ws.arcDir, "host.js");
|
|
36047
36155
|
copyFileSync2(source, target);
|
|
36048
36156
|
}
|
|
36049
36157
|
function locateCliBundle() {
|
|
36050
36158
|
const here = fileURLToPath7(import.meta.url);
|
|
36051
36159
|
let cur = dirname10(here);
|
|
36052
36160
|
while (cur !== "/" && cur !== "") {
|
|
36053
|
-
const candidate =
|
|
36054
|
-
if (
|
|
36161
|
+
const candidate = join21(cur, "package.json");
|
|
36162
|
+
if (existsSync18(candidate)) {
|
|
36055
36163
|
try {
|
|
36056
|
-
const pkg = JSON.parse(
|
|
36164
|
+
const pkg = JSON.parse(readFileSync16(candidate, "utf-8"));
|
|
36057
36165
|
if (pkg.name === "@arcote.tech/arc-cli") {
|
|
36058
|
-
const distIndex =
|
|
36059
|
-
if (!
|
|
36166
|
+
const distIndex = join21(realpathSync2(cur), "dist", "index.js");
|
|
36167
|
+
if (!existsSync18(distIndex)) {
|
|
36060
36168
|
throw new Error(`arc-cli bundle missing at ${distIndex}. Run \`bun run build\` in packages/cli/.`);
|
|
36061
36169
|
}
|
|
36062
36170
|
return distIndex;
|
|
@@ -36088,17 +36196,17 @@ async function ensureDocker() {
|
|
|
36088
36196
|
}
|
|
36089
36197
|
}
|
|
36090
36198
|
function computeContentHash(manifestPath) {
|
|
36091
|
-
const raw = JSON.parse(
|
|
36199
|
+
const raw = JSON.parse(readFileSync16(manifestPath, "utf-8"));
|
|
36092
36200
|
delete raw.buildTime;
|
|
36093
36201
|
const canonical = JSON.stringify(raw);
|
|
36094
36202
|
return createHash3("sha256").update(canonical).digest("hex").slice(0, 12);
|
|
36095
36203
|
}
|
|
36096
36204
|
function collectDockerfileInputs(ws) {
|
|
36097
|
-
const hasPublicDir =
|
|
36098
|
-
const hasLocales =
|
|
36205
|
+
const hasPublicDir = existsSync18(join21(ws.rootDir, "public"));
|
|
36206
|
+
const hasLocales = existsSync18(join21(ws.rootDir, "locales"));
|
|
36099
36207
|
let manifestPath;
|
|
36100
36208
|
for (const name of ["manifest.webmanifest", "manifest.json"]) {
|
|
36101
|
-
if (
|
|
36209
|
+
if (existsSync18(join21(ws.rootDir, name))) {
|
|
36102
36210
|
manifestPath = name;
|
|
36103
36211
|
break;
|
|
36104
36212
|
}
|
|
@@ -36942,14 +37050,14 @@ async function platformDeploy(envArg, options = {}) {
|
|
|
36942
37050
|
err(`Unknown env "${envArg}". Known: ${Object.keys(cfg.envs).join(", ")}`);
|
|
36943
37051
|
process.exit(1);
|
|
36944
37052
|
})() : Object.keys(cfg.envs);
|
|
36945
|
-
const manifestPath =
|
|
37053
|
+
const manifestPath = join22(ws.arcDir, "manifest.json");
|
|
36946
37054
|
if (!options.imageTag) {
|
|
36947
|
-
const needBuild = options.rebuild || !
|
|
37055
|
+
const needBuild = options.rebuild || !existsSync19(manifestPath);
|
|
36948
37056
|
if (needBuild && !options.skipBuild) {
|
|
36949
37057
|
log2("Building platform...");
|
|
36950
37058
|
await buildAll(ws, { noCache: options.rebuild });
|
|
36951
37059
|
ok("Build complete");
|
|
36952
|
-
} else if (!
|
|
37060
|
+
} else if (!existsSync19(manifestPath)) {
|
|
36953
37061
|
err("No build found and --skip-build was set.");
|
|
36954
37062
|
process.exit(1);
|
|
36955
37063
|
}
|
|
@@ -37019,9 +37127,9 @@ function readCliVersion2() {
|
|
|
37019
37127
|
let cur = dirname11(fileURLToPath8(import.meta.url));
|
|
37020
37128
|
const root = dirname11(cur).startsWith("/") ? "/" : ".";
|
|
37021
37129
|
while (cur !== root && cur !== "") {
|
|
37022
|
-
const candidate =
|
|
37023
|
-
if (
|
|
37024
|
-
const pkg = JSON.parse(
|
|
37130
|
+
const candidate = join22(cur, "package.json");
|
|
37131
|
+
if (existsSync19(candidate)) {
|
|
37132
|
+
const pkg = JSON.parse(readFileSync17(candidate, "utf-8"));
|
|
37025
37133
|
if (pkg.name === "@arcote.tech/arc-cli") {
|
|
37026
37134
|
return pkg.version ?? "unknown";
|
|
37027
37135
|
}
|
|
@@ -37037,8 +37145,8 @@ function readCliVersion2() {
|
|
|
37037
37145
|
}
|
|
37038
37146
|
}
|
|
37039
37147
|
async function hashDeployConfig(rootDir) {
|
|
37040
|
-
const p2 =
|
|
37041
|
-
const content =
|
|
37148
|
+
const p2 = join22(rootDir, "deploy.arc.json");
|
|
37149
|
+
const content = readFileSync17(p2);
|
|
37042
37150
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
37043
37151
|
hasher.update(content);
|
|
37044
37152
|
hasher.update(readCliVersion2());
|
|
@@ -37047,8 +37155,8 @@ async function hashDeployConfig(rootDir) {
|
|
|
37047
37155
|
|
|
37048
37156
|
// src/platform/startup.ts
|
|
37049
37157
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
37050
|
-
import { existsSync as
|
|
37051
|
-
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";
|
|
37052
37160
|
|
|
37053
37161
|
// ../host/src/connection-manager.ts
|
|
37054
37162
|
class ConnectionManager {
|
|
@@ -38677,8 +38785,8 @@ async function createArcServer(config) {
|
|
|
38677
38785
|
// src/platform/server.ts
|
|
38678
38786
|
init_i18n();
|
|
38679
38787
|
import { timingSafeEqual } from "crypto";
|
|
38680
|
-
import { existsSync as
|
|
38681
|
-
import { join as
|
|
38788
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync15, readFileSync as readFileSync18 } from "fs";
|
|
38789
|
+
import { join as join23 } from "path";
|
|
38682
38790
|
async function initContextHandler(context2, dbPath) {
|
|
38683
38791
|
const factory = await resolveDbAdapterFactory(dbPath);
|
|
38684
38792
|
const dbAdapter = factory(context2);
|
|
@@ -38698,7 +38806,61 @@ async function resolveDbAdapterFactory(dbPath) {
|
|
|
38698
38806
|
mkdirSync15(dbDir, { recursive: true });
|
|
38699
38807
|
return createBunSQLiteAdapterFactory2(dbPath);
|
|
38700
38808
|
}
|
|
38701
|
-
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;
|
|
38702
38864
|
const otelConfig = process.env.ARC_OTEL_ENABLED === "true" ? {
|
|
38703
38865
|
enabled: true,
|
|
38704
38866
|
endpoint: "/otel",
|
|
@@ -38707,26 +38869,58 @@ function generateShellHtml(appName, manifest, initial, stylesHash, shell) {
|
|
|
38707
38869
|
sampleRate: Number(process.env.ARC_OTEL_BROWSER_SAMPLE_RATE ?? "0.1")
|
|
38708
38870
|
} : null;
|
|
38709
38871
|
const otelTag = otelConfig ? `
|
|
38710
|
-
<script>window.__ARC_OTEL_CONFIG=${
|
|
38711
|
-
const initialUrl = initial ? `/browser/${initial.file}` : null;
|
|
38712
|
-
if (!initialUrl) {
|
|
38713
|
-
throw new Error("generateShellHtml: initial bundle missing from manifest");
|
|
38714
|
-
}
|
|
38872
|
+
<script>window.__ARC_OTEL_CONFIG=${inlineJson(otelConfig)};</script>` : "";
|
|
38715
38873
|
const importMapTag = shell ? `
|
|
38716
|
-
<script type="importmap">${
|
|
38717
|
-
<script>window.__ARC_PEERS__=${
|
|
38718
|
-
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>`;
|
|
38719
38914
|
return `<!doctype html>
|
|
38720
|
-
<html lang="
|
|
38915
|
+
<html lang="${lang}">
|
|
38721
38916
|
<head>
|
|
38722
38917
|
<meta charset="UTF-8" />
|
|
38723
38918
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
|
38724
|
-
<title>${
|
|
38725
|
-
<link rel="icon" href="${
|
|
38919
|
+
<title>${appManifest?.title ?? appName}</title>${appManifest?.favicon ? `
|
|
38920
|
+
<link rel="icon" href="${appManifest.favicon}">` : ""}${appManifest ? `
|
|
38726
38921
|
<link rel="manifest" href="/manifest.json${stylesQs}">` : ""}
|
|
38727
38922
|
<link rel="stylesheet" href="/styles.css${stylesQs}" />
|
|
38728
|
-
<link rel="stylesheet" href="/theme.css${stylesQs}" />${importMapTag}
|
|
38729
|
-
<link rel="modulepreload" href="${initialUrl}" />${otelTag}
|
|
38923
|
+
<link rel="stylesheet" href="/theme.css${stylesQs}" />${importMapTag}${preloadTags}${manifestTag}${i18nTag}${otelTag}
|
|
38730
38924
|
</head>
|
|
38731
38925
|
<body>
|
|
38732
38926
|
<div id="root"></div>
|
|
@@ -38757,7 +38951,7 @@ function getMime(path4) {
|
|
|
38757
38951
|
return MIME[ext2] ?? "application/octet-stream";
|
|
38758
38952
|
}
|
|
38759
38953
|
function serveFile(filePath, headers = {}) {
|
|
38760
|
-
if (!
|
|
38954
|
+
if (!existsSync20(filePath))
|
|
38761
38955
|
return new Response("Not Found", { status: 404 });
|
|
38762
38956
|
return new Response(Bun.file(filePath), {
|
|
38763
38957
|
headers: { "Content-Type": getMime(filePath), ...headers }
|
|
@@ -38879,7 +39073,7 @@ function staticFilesHandler(ws, devMode, getManifest) {
|
|
|
38879
39073
|
return new Response("Forbidden", { status: 403, headers: ctx.corsHeaders });
|
|
38880
39074
|
}
|
|
38881
39075
|
}
|
|
38882
|
-
return serveFile(
|
|
39076
|
+
return serveFile(join23(ws.browserDir, file), {
|
|
38883
39077
|
...ctx.corsHeaders,
|
|
38884
39078
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
|
|
38885
39079
|
});
|
|
@@ -38889,7 +39083,7 @@ function staticFilesHandler(ws, devMode, getManifest) {
|
|
|
38889
39083
|
if (!BROWSER_FILE_RE.test(file)) {
|
|
38890
39084
|
return new Response("Not Found", { status: 404, headers: ctx.corsHeaders });
|
|
38891
39085
|
}
|
|
38892
|
-
return serveFile(
|
|
39086
|
+
return serveFile(join23(ws.arcDir, "shell", file), {
|
|
38893
39087
|
...ctx.corsHeaders,
|
|
38894
39088
|
"Cross-Origin-Resource-Policy": "cross-origin",
|
|
38895
39089
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
|
|
@@ -38908,29 +39102,29 @@ function staticFilesHandler(ws, devMode, getManifest) {
|
|
|
38908
39102
|
return new Response("Forbidden", { status: 403, headers: ctx.corsHeaders });
|
|
38909
39103
|
}
|
|
38910
39104
|
}
|
|
38911
|
-
return serveFile(
|
|
39105
|
+
return serveFile(join23(ws.arcDir, "browser-fed", file), {
|
|
38912
39106
|
...ctx.corsHeaders,
|
|
38913
39107
|
"Cross-Origin-Resource-Policy": "cross-origin",
|
|
38914
39108
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
|
|
38915
39109
|
});
|
|
38916
39110
|
}
|
|
38917
39111
|
if (path4.startsWith("/locales/"))
|
|
38918
|
-
return serveFile(
|
|
39112
|
+
return serveFile(join23(ws.arcDir, path4.slice(1)), {
|
|
38919
39113
|
...ctx.corsHeaders,
|
|
38920
39114
|
"Cache-Control": devMode ? "no-cache" : "max-age=300,stale-while-revalidate=3600"
|
|
38921
39115
|
});
|
|
38922
39116
|
if (path4.startsWith("/assets/"))
|
|
38923
|
-
return serveFile(
|
|
39117
|
+
return serveFile(join23(ws.assetsDir, path4.slice(8)), {
|
|
38924
39118
|
...ctx.corsHeaders,
|
|
38925
39119
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
|
|
38926
39120
|
});
|
|
38927
39121
|
if (path4 === "/styles.css")
|
|
38928
|
-
return serveFile(
|
|
39122
|
+
return serveFile(join23(ws.arcDir, "styles.css"), {
|
|
38929
39123
|
...ctx.corsHeaders,
|
|
38930
39124
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
|
|
38931
39125
|
});
|
|
38932
39126
|
if (path4 === "/theme.css")
|
|
38933
|
-
return serveFile(
|
|
39127
|
+
return serveFile(join23(ws.arcDir, "theme.css"), {
|
|
38934
39128
|
...ctx.corsHeaders,
|
|
38935
39129
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
|
|
38936
39130
|
});
|
|
@@ -38941,8 +39135,8 @@ function staticFilesHandler(ws, devMode, getManifest) {
|
|
|
38941
39135
|
});
|
|
38942
39136
|
}
|
|
38943
39137
|
if (path4.lastIndexOf(".") > path4.lastIndexOf("/")) {
|
|
38944
|
-
const publicFile =
|
|
38945
|
-
if (
|
|
39138
|
+
const publicFile = join23(ws.publicDir, path4.slice(1));
|
|
39139
|
+
if (existsSync20(publicFile))
|
|
38946
39140
|
return serveFile(publicFile, ctx.corsHeaders);
|
|
38947
39141
|
}
|
|
38948
39142
|
return null;
|
|
@@ -39142,12 +39336,13 @@ function headlessFallbackHandler(ws, getManifest) {
|
|
|
39142
39336
|
};
|
|
39143
39337
|
}
|
|
39144
39338
|
function spaFallbackHandler(getShellHtml) {
|
|
39145
|
-
return (
|
|
39146
|
-
return new Response(getShellHtml(), {
|
|
39339
|
+
return async (req, _url, ctx) => {
|
|
39340
|
+
return new Response(await getShellHtml(req), {
|
|
39147
39341
|
headers: {
|
|
39148
39342
|
...ctx.corsHeaders,
|
|
39149
39343
|
"Content-Type": "text/html",
|
|
39150
|
-
"Cache-Control": "no-cache, must-revalidate"
|
|
39344
|
+
"Cache-Control": "no-cache, must-revalidate, private",
|
|
39345
|
+
Vary: "Cookie, Accept-Language"
|
|
39151
39346
|
}
|
|
39152
39347
|
});
|
|
39153
39348
|
};
|
|
@@ -39188,7 +39383,16 @@ async function startPlatformServer(opts) {
|
|
|
39188
39383
|
mapUrl = u2;
|
|
39189
39384
|
};
|
|
39190
39385
|
const getMapUrl = () => mapUrl;
|
|
39191
|
-
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
|
+
});
|
|
39192
39396
|
const sseClients = new Set;
|
|
39193
39397
|
const fallbackHandler = opts.headless ? headlessFallbackHandler(ws, getManifest) : spaFallbackHandler(getShellHtml);
|
|
39194
39398
|
const notifyReload = (m4) => {
|
|
@@ -39252,7 +39456,7 @@ async function startPlatformServer(opts) {
|
|
|
39252
39456
|
stop: () => server.stop()
|
|
39253
39457
|
};
|
|
39254
39458
|
}
|
|
39255
|
-
const dbPath = opts.dbPath ||
|
|
39459
|
+
const dbPath = opts.dbPath || join23(ws.arcDir, "data", "arc.db");
|
|
39256
39460
|
const baseDbFactory = await resolveDbAdapterFactory(dbPath);
|
|
39257
39461
|
let dbAdapterFactory = baseDbFactory;
|
|
39258
39462
|
if (telemetry) {
|
|
@@ -39297,17 +39501,17 @@ async function startPlatformServer(opts) {
|
|
|
39297
39501
|
async function startPlatform(opts) {
|
|
39298
39502
|
const { ws, devMode } = opts;
|
|
39299
39503
|
const port = opts.port ?? parseInt(process.env.PORT || "5005", 10);
|
|
39300
|
-
const dbPath = opts.dbPath ??
|
|
39504
|
+
const dbPath = opts.dbPath ?? join24(ws.rootDir, ".arc", "data", devMode ? "dev.db" : "prod.db");
|
|
39301
39505
|
let manifest;
|
|
39302
39506
|
if (devMode) {
|
|
39303
39507
|
manifest = await buildAll(ws, { minify: false });
|
|
39304
39508
|
} else {
|
|
39305
|
-
const manifestPath =
|
|
39306
|
-
if (!
|
|
39509
|
+
const manifestPath = join24(ws.arcDir, "manifest.json");
|
|
39510
|
+
if (!existsSync21(manifestPath)) {
|
|
39307
39511
|
err("No build found. Run `arc platform build` first.");
|
|
39308
39512
|
process.exit(1);
|
|
39309
39513
|
}
|
|
39310
|
-
manifest = JSON.parse(
|
|
39514
|
+
manifest = JSON.parse(readFileSync19(manifestPath, "utf-8"));
|
|
39311
39515
|
}
|
|
39312
39516
|
assertProdFederationSecrets(devMode, !!manifest.federation);
|
|
39313
39517
|
const needsPairing = !!manifest.federation || devMode && (opts.map || opts.headless);
|
|
@@ -39391,8 +39595,8 @@ async function startMapTool(ws, port, authToken, platformServer, platform3) {
|
|
|
39391
39595
|
try {
|
|
39392
39596
|
const { startMapServer, MAP_SCHEMA_VERSION } = await import("@arcote.tech/arc-map");
|
|
39393
39597
|
const { getContext } = await import("@arcote.tech/platform");
|
|
39394
|
-
const globs = ws.packages.map((p3) =>
|
|
39395
|
-
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;
|
|
39396
39600
|
const meta = readAppMeta(ws.rootDir, MAP_SCHEMA_VERSION);
|
|
39397
39601
|
const map = await startMapServer({
|
|
39398
39602
|
getContext: () => getContext(),
|
|
@@ -39427,15 +39631,15 @@ function resolveAppSecret(rootDir) {
|
|
|
39427
39631
|
const fromEnv = process.env.ARC_APP_SECRET;
|
|
39428
39632
|
if (fromEnv)
|
|
39429
39633
|
return fromEnv;
|
|
39430
|
-
const file =
|
|
39634
|
+
const file = join24(rootDir, ".arc", "app-secret");
|
|
39431
39635
|
try {
|
|
39432
|
-
const saved =
|
|
39636
|
+
const saved = readFileSync19(file, "utf-8").trim();
|
|
39433
39637
|
if (saved)
|
|
39434
39638
|
return saved;
|
|
39435
39639
|
} catch {}
|
|
39436
39640
|
const secret = randomBytes2(32).toString("base64url");
|
|
39437
39641
|
try {
|
|
39438
|
-
mkdirSync16(
|
|
39642
|
+
mkdirSync16(join24(rootDir, ".arc"), { recursive: true });
|
|
39439
39643
|
writeFileSync16(file, secret, { mode: 384 });
|
|
39440
39644
|
} catch (e2) {
|
|
39441
39645
|
err(`App secret not persisted (${e2.message}) \u2014 hosts will need re-install after restart`);
|
|
@@ -39446,15 +39650,15 @@ function resolvePairingToken(rootDir) {
|
|
|
39446
39650
|
const fromEnv = process.env.ARC_PAIRING_TOKEN || process.env.ARC_MAP_TOKEN;
|
|
39447
39651
|
if (fromEnv)
|
|
39448
39652
|
return fromEnv;
|
|
39449
|
-
const file =
|
|
39653
|
+
const file = join24(rootDir, ".arc", "pairing-token");
|
|
39450
39654
|
try {
|
|
39451
|
-
const saved =
|
|
39655
|
+
const saved = readFileSync19(file, "utf-8").trim();
|
|
39452
39656
|
if (saved)
|
|
39453
39657
|
return saved;
|
|
39454
39658
|
} catch {}
|
|
39455
39659
|
const token = randomBytes2(16).toString("base64url");
|
|
39456
39660
|
try {
|
|
39457
|
-
mkdirSync16(
|
|
39661
|
+
mkdirSync16(join24(rootDir, ".arc"), { recursive: true });
|
|
39458
39662
|
writeFileSync16(file, token, { mode: 384 });
|
|
39459
39663
|
} catch (e2) {
|
|
39460
39664
|
err(`Pairing token not persisted (${e2.message}) \u2014 it will change on restart`);
|
|
@@ -39463,7 +39667,7 @@ function resolvePairingToken(rootDir) {
|
|
|
39463
39667
|
}
|
|
39464
39668
|
function readAppMeta(rootDir, schemaVersion) {
|
|
39465
39669
|
try {
|
|
39466
|
-
const pkg = JSON.parse(
|
|
39670
|
+
const pkg = JSON.parse(readFileSync19(join24(rootDir, "package.json"), "utf-8"));
|
|
39467
39671
|
return {
|
|
39468
39672
|
schemaVersion,
|
|
39469
39673
|
name: pkg.name ?? rootDir.split("/").pop() ?? "arc-app",
|
|
@@ -39499,8 +39703,8 @@ function attachDevWatcher(ws, platform3, onReload) {
|
|
|
39499
39703
|
}, 300);
|
|
39500
39704
|
};
|
|
39501
39705
|
for (const pkg of ws.packages) {
|
|
39502
|
-
const srcDir =
|
|
39503
|
-
if (!
|
|
39706
|
+
const srcDir = join24(pkg.path, "src");
|
|
39707
|
+
if (!existsSync21(srcDir))
|
|
39504
39708
|
continue;
|
|
39505
39709
|
watch(srcDir, { recursive: true }, (_event, filename) => {
|
|
39506
39710
|
if (!filename || filename.includes(".arc") || filename.endsWith(".d.ts") || filename.includes("node_modules") || filename.includes("dist"))
|
|
@@ -39510,8 +39714,8 @@ function attachDevWatcher(ws, platform3, onReload) {
|
|
|
39510
39714
|
triggerRebuild();
|
|
39511
39715
|
});
|
|
39512
39716
|
}
|
|
39513
|
-
const localesDir =
|
|
39514
|
-
if (
|
|
39717
|
+
const localesDir = join24(ws.rootDir, "locales");
|
|
39718
|
+
if (existsSync21(localesDir)) {
|
|
39515
39719
|
watch(localesDir, { recursive: false }, (_event, filename) => {
|
|
39516
39720
|
if (!filename?.endsWith(".po"))
|
|
39517
39721
|
return;
|
|
@@ -39533,7 +39737,7 @@ async function platformDev(opts = {}) {
|
|
|
39533
39737
|
|
|
39534
39738
|
// src/commands/platform-pairing-token.ts
|
|
39535
39739
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
39536
|
-
import { join as
|
|
39740
|
+
import { join as join25 } from "path";
|
|
39537
39741
|
async function platformPairingToken() {
|
|
39538
39742
|
const ws = resolveWorkspace();
|
|
39539
39743
|
const { context: context2 } = await loadServerContext(ws);
|
|
@@ -39542,7 +39746,7 @@ async function platformPairingToken() {
|
|
|
39542
39746
|
process.exit(1);
|
|
39543
39747
|
}
|
|
39544
39748
|
const isProd = false;
|
|
39545
|
-
const dbPath =
|
|
39749
|
+
const dbPath = join25(ws.rootDir, ".arc", "data", isProd ? "prod.db" : "dev.db");
|
|
39546
39750
|
const handler = await initContextHandler(context2, dbPath);
|
|
39547
39751
|
const code = randomBytes3(24).toString("base64url");
|
|
39548
39752
|
const res = await handler.executeCommand("pairingCode.issue", { code }, null, { internal: true });
|