@arcote.tech/arc-cli 0.8.1 → 0.8.3
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 +369 -67
- package/package.json +10 -10
- package/src/builder/chunk-planner.ts +14 -0
- package/src/builder/module-builder.ts +432 -30
- package/src/commands/platform-build.ts +6 -1
- package/src/commands/platform-pairing-token.ts +53 -0
- package/src/deploy/caddyfile.ts +3 -0
- package/src/index.ts +14 -2
- package/src/platform/server.ts +6 -8
- package/src/platform/shared.ts +68 -21
- package/src/platform/startup.ts +32 -22
package/dist/index.js
CHANGED
|
@@ -31692,6 +31692,52 @@ function workspaceSourcePlugin(srcByName) {
|
|
|
31692
31692
|
}
|
|
31693
31693
|
};
|
|
31694
31694
|
}
|
|
31695
|
+
var SERVER_ONLY_NODE_BUILTINS = new Set([
|
|
31696
|
+
"crypto",
|
|
31697
|
+
"stream",
|
|
31698
|
+
"zlib",
|
|
31699
|
+
"fs",
|
|
31700
|
+
"fs/promises",
|
|
31701
|
+
"net",
|
|
31702
|
+
"tls",
|
|
31703
|
+
"http",
|
|
31704
|
+
"https",
|
|
31705
|
+
"http2",
|
|
31706
|
+
"dns",
|
|
31707
|
+
"dgram",
|
|
31708
|
+
"child_process",
|
|
31709
|
+
"worker_threads",
|
|
31710
|
+
"cluster",
|
|
31711
|
+
"os",
|
|
31712
|
+
"v8",
|
|
31713
|
+
"vm",
|
|
31714
|
+
"readline",
|
|
31715
|
+
"repl",
|
|
31716
|
+
"tty",
|
|
31717
|
+
"perf_hooks",
|
|
31718
|
+
"inspector",
|
|
31719
|
+
"async_hooks",
|
|
31720
|
+
"diagnostics_channel",
|
|
31721
|
+
"module",
|
|
31722
|
+
"constants"
|
|
31723
|
+
]);
|
|
31724
|
+
function nodeServerBuiltinStubPlugin() {
|
|
31725
|
+
return {
|
|
31726
|
+
name: "node-server-builtin-stub",
|
|
31727
|
+
setup(build2) {
|
|
31728
|
+
build2.onResolve({ filter: /^(node:)?[a-z_/]+$/ }, (args) => {
|
|
31729
|
+
const bare = args.path.replace(/^node:/, "");
|
|
31730
|
+
if (!SERVER_ONLY_NODE_BUILTINS.has(bare))
|
|
31731
|
+
return;
|
|
31732
|
+
return { path: args.path, namespace: "node-server-stub" };
|
|
31733
|
+
});
|
|
31734
|
+
build2.onLoad({ filter: /.*/, namespace: "node-server-stub" }, (args) => ({
|
|
31735
|
+
contents: `const e=()=>{throw new Error("Node builtin '${args.path}' jest server-only \u2014 niedost\u0119pny w przegl\u0105darce");};` + `module.exports=new Proxy({},{get:(_,p)=>p==="__esModule"?false:e});`,
|
|
31736
|
+
loader: "js"
|
|
31737
|
+
}));
|
|
31738
|
+
}
|
|
31739
|
+
};
|
|
31740
|
+
}
|
|
31695
31741
|
function jsxDevShimPlugin() {
|
|
31696
31742
|
return {
|
|
31697
31743
|
name: "jsx-dev-runtime-shim",
|
|
@@ -31813,7 +31859,10 @@ async function buildContextClient(pkg, rootDir, client, cache, noCache) {
|
|
|
31813
31859
|
format: "esm",
|
|
31814
31860
|
naming: "index.[ext]",
|
|
31815
31861
|
external: externals,
|
|
31816
|
-
plugins: [
|
|
31862
|
+
plugins: [
|
|
31863
|
+
jsxDevShimPlugin(),
|
|
31864
|
+
...client.target === "browser" ? [nodeServerBuiltinStubPlugin()] : []
|
|
31865
|
+
],
|
|
31817
31866
|
define: client.defines
|
|
31818
31867
|
});
|
|
31819
31868
|
if (!result.success) {
|
|
@@ -31926,7 +31975,6 @@ async function buildServerApp(rootDir, serverDir, packages, cache, noCache) {
|
|
|
31926
31975
|
try {
|
|
31927
31976
|
result = await Bun.build({
|
|
31928
31977
|
entrypoints: [entrySrc],
|
|
31929
|
-
outdir: serverDir,
|
|
31930
31978
|
target: "bun",
|
|
31931
31979
|
format: "esm",
|
|
31932
31980
|
splitting: true,
|
|
@@ -31955,6 +32003,9 @@ async function buildServerApp(rootDir, serverDir, packages, cache, noCache) {
|
|
|
31955
32003
|
if (basename3(entryOut.path) !== SERVER_ENTRY_FILE) {
|
|
31956
32004
|
throw new Error(`Server app build: unexpected entry name ${basename3(entryOut.path)} (wanted ${SERVER_ENTRY_FILE})`);
|
|
31957
32005
|
}
|
|
32006
|
+
for (const out of result.outputs) {
|
|
32007
|
+
writeFileSync7(join9(serverDir, basename3(out.path)), new Uint8Array(await out.arrayBuffer()));
|
|
32008
|
+
}
|
|
31958
32009
|
const externals = [...recorded].filter(([name]) => !FRAMEWORK_PEER_SET.has(name)).map(([name, version]) => ({ name, version })).sort((a, b) => a.name.localeCompare(b.name));
|
|
31959
32010
|
writeFileSync7(externalsFileAbs, JSON.stringify(externals, null, 2) + `
|
|
31960
32011
|
`);
|
|
@@ -31972,9 +32023,141 @@ function readServerExternals(path4) {
|
|
|
31972
32023
|
return [];
|
|
31973
32024
|
}
|
|
31974
32025
|
}
|
|
32026
|
+
function buildStatsEnabled() {
|
|
32027
|
+
return process.env.ARC_BUILD_STATS === "1";
|
|
32028
|
+
}
|
|
32029
|
+
function classifySource(src) {
|
|
32030
|
+
const nm = src.lastIndexOf("node_modules/");
|
|
32031
|
+
if (nm >= 0) {
|
|
32032
|
+
const rest = src.slice(nm + "node_modules/".length);
|
|
32033
|
+
const parts = rest.split("/");
|
|
32034
|
+
const pkg = parts[0].startsWith("@") ? `${parts[0]}/${parts[1]}` : parts[0];
|
|
32035
|
+
const external = !pkg.startsWith("@ndt/") && !pkg.startsWith("@arcote.tech/");
|
|
32036
|
+
return { pkg, external };
|
|
32037
|
+
}
|
|
32038
|
+
const m = src.match(/(?:^|\/)packages\/([^/]+)\//);
|
|
32039
|
+
if (m)
|
|
32040
|
+
return { pkg: `packages/${m[1]}`, external: false };
|
|
32041
|
+
return { pkg: src.split("/").slice(0, 2).join("/") || src, external: false };
|
|
32042
|
+
}
|
|
32043
|
+
function chunkPkgWeights(map) {
|
|
32044
|
+
const acc = new Map;
|
|
32045
|
+
const sources = map.sources ?? [];
|
|
32046
|
+
const contents = map.sourcesContent ?? [];
|
|
32047
|
+
for (let i = 0;i < sources.length; i++) {
|
|
32048
|
+
const bytes = contents[i]?.length ?? 0;
|
|
32049
|
+
const { pkg, external } = classifySource(sources[i]);
|
|
32050
|
+
const cur = acc.get(pkg) ?? { pkg, bytes: 0, external };
|
|
32051
|
+
cur.bytes += bytes;
|
|
32052
|
+
acc.set(pkg, cur);
|
|
32053
|
+
}
|
|
32054
|
+
return [...acc.values()].sort((a, b) => b.bytes - a.bytes);
|
|
32055
|
+
}
|
|
32056
|
+
var kb = (n) => `${Math.round(n / 1024)} KB`;
|
|
32057
|
+
function stripTrailingSourcemapComment(bytes) {
|
|
32058
|
+
let text = Buffer.from(bytes).toString("utf8");
|
|
32059
|
+
text = text.replace(/\n*\/\/# (?:debugId|sourceMappingURL)=[^\n]*\s*$/, "");
|
|
32060
|
+
if (!text.endsWith(`
|
|
32061
|
+
`))
|
|
32062
|
+
text += `
|
|
32063
|
+
`;
|
|
32064
|
+
return new Uint8Array(Buffer.from(text, "utf8"));
|
|
32065
|
+
}
|
|
32066
|
+
function reportBrowserBuildStats(opts) {
|
|
32067
|
+
const { label, entries, jsByName, mapsByJs } = opts;
|
|
32068
|
+
const staticEdge = /(?:^|[;}\s])import\s*["'](\.\/[^"']+\.js)["']|from\s*["'](\.\/[^"']+\.js)["']/g;
|
|
32069
|
+
const staticImports = (name) => {
|
|
32070
|
+
const bytes = jsByName.get(name);
|
|
32071
|
+
if (!bytes)
|
|
32072
|
+
return [];
|
|
32073
|
+
const s = Buffer.from(bytes).toString("utf8");
|
|
32074
|
+
const out = [];
|
|
32075
|
+
let m;
|
|
32076
|
+
staticEdge.lastIndex = 0;
|
|
32077
|
+
while (m = staticEdge.exec(s))
|
|
32078
|
+
out.push(basename3(m[1] ?? m[2]));
|
|
32079
|
+
return out;
|
|
32080
|
+
};
|
|
32081
|
+
const closure = (start) => {
|
|
32082
|
+
const seen = new Set;
|
|
32083
|
+
const stack = [start];
|
|
32084
|
+
while (stack.length) {
|
|
32085
|
+
const f = stack.pop();
|
|
32086
|
+
if (seen.has(f) || !jsByName.has(f))
|
|
32087
|
+
continue;
|
|
32088
|
+
seen.add(f);
|
|
32089
|
+
stack.push(...staticImports(f));
|
|
32090
|
+
}
|
|
32091
|
+
return seen;
|
|
32092
|
+
};
|
|
32093
|
+
const gz = (bytes) => Bun.gzipSync(bytes, { level: 6 }).length;
|
|
32094
|
+
console.log(`
|
|
32095
|
+
\u2501\u2501\u2501\u2501\u2501 Build stats: ${label} \u2501\u2501\u2501\u2501\u2501`);
|
|
32096
|
+
const globalExt = new Map;
|
|
32097
|
+
for (const entry of entries) {
|
|
32098
|
+
const files = closure(entry.file);
|
|
32099
|
+
let raw = 0;
|
|
32100
|
+
let gzip = 0;
|
|
32101
|
+
const pkgAcc = new Map;
|
|
32102
|
+
for (const f of files) {
|
|
32103
|
+
const bytes = jsByName.get(f);
|
|
32104
|
+
raw += bytes.length;
|
|
32105
|
+
gzip += gz(bytes);
|
|
32106
|
+
const map = mapsByJs.get(f);
|
|
32107
|
+
if (!map)
|
|
32108
|
+
continue;
|
|
32109
|
+
for (const w of chunkPkgWeights(map)) {
|
|
32110
|
+
const cur = pkgAcc.get(w.pkg) ?? { pkg: w.pkg, bytes: 0, external: w.external };
|
|
32111
|
+
cur.bytes += w.bytes;
|
|
32112
|
+
pkgAcc.set(w.pkg, cur);
|
|
32113
|
+
if (w.external) {
|
|
32114
|
+
const g = globalExt.get(w.pkg) ?? { bytes: 0, chunks: new Set, entries: new Set };
|
|
32115
|
+
g.bytes = Math.max(g.bytes, w.bytes);
|
|
32116
|
+
g.chunks.add(f);
|
|
32117
|
+
g.entries.add(entry.name);
|
|
32118
|
+
globalExt.set(w.pkg, g);
|
|
32119
|
+
}
|
|
32120
|
+
}
|
|
32121
|
+
}
|
|
32122
|
+
const top = [...pkgAcc.values()].sort((a, b) => b.bytes - a.bytes).slice(0, 10);
|
|
32123
|
+
console.log(`
|
|
32124
|
+
\u25B8 ${entry.name}: pierwsze wczytanie ${kb(raw)} raw / ${kb(gzip)} gzip (${files.size} plik\xF3w)`);
|
|
32125
|
+
for (const w of top) {
|
|
32126
|
+
const tag = w.external ? "ext" : "wewn";
|
|
32127
|
+
console.log(` ${kb(w.bytes).padStart(8)} [${tag}] ${w.pkg}`);
|
|
32128
|
+
}
|
|
32129
|
+
}
|
|
32130
|
+
const heaviest = [...globalExt.entries()].sort((a, b) => b[1].bytes - a[1].bytes).slice(0, 15);
|
|
32131
|
+
if (heaviest.length) {
|
|
32132
|
+
console.log(`
|
|
32133
|
+
\u25B8 Najci\u0119\u017Csze biblioteki ZEWN\u0118TRZNE (\u017Ar\xF3d\u0142a) i gdzie l\u0105duj\u0105:`);
|
|
32134
|
+
for (const [pkg, g] of heaviest) {
|
|
32135
|
+
console.log(` ${kb(g.bytes).padStart(8)} ${pkg} \u2192 ${[...g.entries].join(", ")}`);
|
|
32136
|
+
}
|
|
32137
|
+
}
|
|
32138
|
+
console.log(`
|
|
32139
|
+
\u2501\u2501\u2501\u2501\u2501 koniec stats: ${label} \u2501\u2501\u2501\u2501\u2501
|
|
32140
|
+
`);
|
|
32141
|
+
}
|
|
32142
|
+
function looksMinified(files) {
|
|
32143
|
+
for (const f of files) {
|
|
32144
|
+
if (!f.name.endsWith(".js") || f.bytes.length < 16 * 1024)
|
|
32145
|
+
continue;
|
|
32146
|
+
const sample = f.bytes.subarray(0, 64 * 1024);
|
|
32147
|
+
let newlines = 0;
|
|
32148
|
+
for (const b of sample)
|
|
32149
|
+
if (b === 10)
|
|
32150
|
+
newlines++;
|
|
32151
|
+
const avgLineLen = sample.length / Math.max(1, newlines);
|
|
32152
|
+
if (avgLineLen < 200)
|
|
32153
|
+
return false;
|
|
32154
|
+
}
|
|
32155
|
+
return true;
|
|
32156
|
+
}
|
|
31975
32157
|
async function buildBrowserApp(rootDir, outDir, plan, cache, noCache, i18nCollector, opts) {
|
|
31976
32158
|
const federated = opts?.federated ?? false;
|
|
31977
32159
|
const exposeRuntime = opts?.exposeRuntime ?? false;
|
|
32160
|
+
const minify = opts?.minify ?? true;
|
|
31978
32161
|
mkdirSync7(outDir, { recursive: true });
|
|
31979
32162
|
const publicMembers = plan.groups.get("public") ?? [];
|
|
31980
32163
|
const protectedGroups = plan.chunks.filter((c) => c !== "public").map((c) => ({ name: c, members: plan.groups.get(c) ?? [] })).filter((g) => g.members.length > 0);
|
|
@@ -31997,10 +32180,11 @@ async function buildBrowserApp(rootDir, outDir, plan, cache, noCache, i18nCollec
|
|
|
31997
32180
|
define: { ONLY_SERVER: "false", ONLY_BROWSER: "true", ONLY_CLIENT: "true" },
|
|
31998
32181
|
federated,
|
|
31999
32182
|
exposeRuntime,
|
|
32183
|
+
minify,
|
|
32000
32184
|
externals: federated ? [...SHELL_EXTERNALS] : [],
|
|
32001
32185
|
builderRev: 3
|
|
32002
32186
|
});
|
|
32003
|
-
if (!noCache && isCacheHit(cache, unitId, inputHash)) {
|
|
32187
|
+
if (!noCache && !buildStatsEnabled() && isCacheHit(cache, unitId, inputHash)) {
|
|
32004
32188
|
const cached = cache.units[unitId]?.outputHashes;
|
|
32005
32189
|
if (cached?._manifest) {
|
|
32006
32190
|
try {
|
|
@@ -32024,24 +32208,27 @@ async function buildBrowserApp(rootDir, outDir, plan, cache, noCache, i18nCollec
|
|
|
32024
32208
|
rmSync3(join9(outDir, f), { force: true });
|
|
32025
32209
|
}
|
|
32026
32210
|
}
|
|
32027
|
-
const tmpDir = join9(
|
|
32028
|
-
mkdirSync7(tmpDir, { recursive: true });
|
|
32211
|
+
const tmpDir = join9(rootDir, ".arc", "tmp", `entries-${unitId}`);
|
|
32029
32212
|
const importLines = (pkgs) => pkgs.map((m) => `import "${m.pkg.name}";`).join(`
|
|
32030
32213
|
`);
|
|
32031
32214
|
const initialEntry = join9(tmpDir, "initial.ts");
|
|
32032
|
-
writeFileSync7(initialEntry, (exposeRuntime ? runtimeExposePrelude() + `
|
|
32033
|
-
` : "") + `${importLines(publicMembers)}
|
|
32034
|
-
export { startApp } from "@arcote.tech/platform";
|
|
32035
|
-
`);
|
|
32036
32215
|
const entryPaths = [initialEntry];
|
|
32037
32216
|
const groupModuleMap = new Map;
|
|
32038
32217
|
for (const g of protectedGroups) {
|
|
32039
|
-
|
|
32040
|
-
writeFileSync7(entry, `${importLines(g.members)}
|
|
32041
|
-
`);
|
|
32042
|
-
entryPaths.push(entry);
|
|
32218
|
+
entryPaths.push(join9(tmpDir, `${g.name}.ts`));
|
|
32043
32219
|
groupModuleMap.set(g.name, g.members.map((m) => m.moduleName));
|
|
32044
32220
|
}
|
|
32221
|
+
const writeEntryFiles = () => {
|
|
32222
|
+
mkdirSync7(tmpDir, { recursive: true });
|
|
32223
|
+
writeFileSync7(initialEntry, (exposeRuntime ? runtimeExposePrelude() + `
|
|
32224
|
+
` : "") + `${importLines(publicMembers)}
|
|
32225
|
+
export { startApp } from "@arcote.tech/platform";
|
|
32226
|
+
`);
|
|
32227
|
+
for (const g of protectedGroups) {
|
|
32228
|
+
writeFileSync7(join9(tmpDir, `${g.name}.ts`), `${importLines(g.members)}
|
|
32229
|
+
`);
|
|
32230
|
+
}
|
|
32231
|
+
};
|
|
32045
32232
|
const allMemberPkgs = new Map;
|
|
32046
32233
|
for (const m of publicMembers)
|
|
32047
32234
|
allMemberPkgs.set(m.pkg.name, m.pkg);
|
|
@@ -32062,11 +32249,13 @@ export { startApp } from "@arcote.tech/platform";
|
|
|
32062
32249
|
`);
|
|
32063
32250
|
patchedPkgJsons.push({ path: pkgJsonPath, original });
|
|
32064
32251
|
}
|
|
32252
|
+
const statsMode = buildStatsEnabled();
|
|
32253
|
+
const mapsByJs = new Map;
|
|
32065
32254
|
let result;
|
|
32066
|
-
|
|
32067
|
-
|
|
32255
|
+
const runBuild = () => {
|
|
32256
|
+
writeEntryFiles();
|
|
32257
|
+
return Bun.build({
|
|
32068
32258
|
entrypoints: entryPaths,
|
|
32069
|
-
outdir: outDir,
|
|
32070
32259
|
splitting: true,
|
|
32071
32260
|
format: "esm",
|
|
32072
32261
|
target: "browser",
|
|
@@ -32074,9 +32263,12 @@ export { startApp } from "@arcote.tech/platform";
|
|
|
32074
32263
|
plugins: [
|
|
32075
32264
|
...federated ? [] : [singleReactPlugin(rootDir)],
|
|
32076
32265
|
jsxDevShimPlugin(),
|
|
32266
|
+
nodeServerBuiltinStubPlugin(),
|
|
32077
32267
|
i18nExtractPlugin(i18nCollector, rootDir)
|
|
32078
32268
|
],
|
|
32079
32269
|
naming: "[name].[ext]",
|
|
32270
|
+
minify,
|
|
32271
|
+
sourcemap: statsMode ? "external" : "none",
|
|
32080
32272
|
define: {
|
|
32081
32273
|
ONLY_SERVER: "false",
|
|
32082
32274
|
ONLY_BROWSER: "true",
|
|
@@ -32084,12 +32276,46 @@ export { startApp } from "@arcote.tech/platform";
|
|
|
32084
32276
|
"process.env.NODE_ENV": '"production"'
|
|
32085
32277
|
}
|
|
32086
32278
|
});
|
|
32279
|
+
};
|
|
32280
|
+
let outFiles = null;
|
|
32281
|
+
try {
|
|
32282
|
+
for (let attempt = 1;; attempt++) {
|
|
32283
|
+
result = await runBuild();
|
|
32284
|
+
if (result.success) {
|
|
32285
|
+
const files = [];
|
|
32286
|
+
mapsByJs.clear();
|
|
32287
|
+
for (const out of result.outputs) {
|
|
32288
|
+
const name = basename3(out.path);
|
|
32289
|
+
if (name.endsWith(".map")) {
|
|
32290
|
+
try {
|
|
32291
|
+
mapsByJs.set(name.replace(/\.map$/, ""), JSON.parse(await out.text()));
|
|
32292
|
+
} catch {}
|
|
32293
|
+
continue;
|
|
32294
|
+
}
|
|
32295
|
+
let bytes = new Uint8Array(await out.arrayBuffer());
|
|
32296
|
+
if (statsMode && name.endsWith(".js")) {
|
|
32297
|
+
bytes = stripTrailingSourcemapComment(bytes);
|
|
32298
|
+
}
|
|
32299
|
+
files.push({ kind: out.kind, name, bytes });
|
|
32300
|
+
}
|
|
32301
|
+
if (!minify || looksMinified(files)) {
|
|
32302
|
+
outFiles = files;
|
|
32303
|
+
break;
|
|
32304
|
+
}
|
|
32305
|
+
}
|
|
32306
|
+
if (attempt >= 3) {
|
|
32307
|
+
if (!result.success)
|
|
32308
|
+
break;
|
|
32309
|
+
throw new Error("Browser app build: output NIEZMINIFIKOWANY mimo minify:true (3 pr\xF3by). Przerwane, \u017Ceby nie wypu\u015Bci\u0107 ci\u0119\u017Ckich bundli.");
|
|
32310
|
+
}
|
|
32311
|
+
console.warn(` ! browser build pass ${attempt} ${result.success ? "niezminifikowany mimo minify:true (wy\u015Bcig minify Bun.build)" : "nieudany (wy\u015Bcig Bun.build \u2014 np. znikni\u0119te pliki entry)"} \u2014 powtarzam`);
|
|
32312
|
+
}
|
|
32087
32313
|
} finally {
|
|
32088
32314
|
for (const p of patchedPkgJsons)
|
|
32089
32315
|
writeFileSync7(p.path, p.original);
|
|
32090
32316
|
}
|
|
32091
32317
|
rmSync3(tmpDir, { recursive: true, force: true });
|
|
32092
|
-
if (!result.success) {
|
|
32318
|
+
if (!result.success || !outFiles) {
|
|
32093
32319
|
for (const log2 of result.logs)
|
|
32094
32320
|
console.error(log2);
|
|
32095
32321
|
throw new Error("Browser app build failed");
|
|
@@ -32098,17 +32324,12 @@ export { startApp } from "@arcote.tech/platform";
|
|
|
32098
32324
|
let initialHash = "";
|
|
32099
32325
|
const groups = {};
|
|
32100
32326
|
const sharedChunks = [];
|
|
32101
|
-
for (const out of
|
|
32102
|
-
const name = basename3(out.path);
|
|
32327
|
+
for (const out of outFiles) {
|
|
32103
32328
|
if (out.kind === "entry-point") {
|
|
32104
|
-
const
|
|
32105
|
-
const
|
|
32106
|
-
const stem = name.replace(/\.js$/, "");
|
|
32329
|
+
const hash = sha256Hex(out.bytes).slice(0, 16);
|
|
32330
|
+
const stem = out.name.replace(/\.js$/, "");
|
|
32107
32331
|
const finalName = `${stem}.${hash}.js`;
|
|
32108
|
-
|
|
32109
|
-
rmSync3(finalPath, { force: true });
|
|
32110
|
-
writeFileSync7(finalPath, bytes);
|
|
32111
|
-
rmSync3(out.path, { force: true });
|
|
32332
|
+
writeFileSync7(join9(outDir, finalName), out.bytes);
|
|
32112
32333
|
if (stem === "initial") {
|
|
32113
32334
|
initialFile = finalName;
|
|
32114
32335
|
initialHash = hash;
|
|
@@ -32120,18 +32341,56 @@ export { startApp } from "@arcote.tech/platform";
|
|
|
32120
32341
|
};
|
|
32121
32342
|
}
|
|
32122
32343
|
} else if (out.kind === "chunk") {
|
|
32123
|
-
|
|
32344
|
+
writeFileSync7(join9(outDir, out.name), out.bytes);
|
|
32345
|
+
sharedChunks.push(out.name);
|
|
32124
32346
|
}
|
|
32125
32347
|
}
|
|
32126
32348
|
if (!initialFile) {
|
|
32127
32349
|
throw new Error("Browser app build: initial entry not found in outputs");
|
|
32128
32350
|
}
|
|
32351
|
+
const written = new Map;
|
|
32352
|
+
for (const out of outFiles) {
|
|
32353
|
+
const name = out.kind === "entry-point" ? `${out.name.replace(/\.js$/, "")}.${sha256Hex(out.bytes).slice(0, 16)}.js` : out.name;
|
|
32354
|
+
if (out.kind === "entry-point" || out.kind === "chunk") {
|
|
32355
|
+
written.set(name, sha256Hex(out.bytes));
|
|
32356
|
+
}
|
|
32357
|
+
}
|
|
32358
|
+
for (const f of readdirSync4(outDir)) {
|
|
32359
|
+
if (!f.endsWith(".js"))
|
|
32360
|
+
continue;
|
|
32361
|
+
const expected = written.get(f);
|
|
32362
|
+
const actual = sha256Hex(readFileSync8(join9(outDir, f)));
|
|
32363
|
+
if (!expected || expected !== actual) {
|
|
32364
|
+
throw new Error(`Browser app build: plik "${f}" w ${outDir} nie zgadza si\u0119 z artefaktami builda ` + `(${expected ? "tre\u015B\u0107 rozjechana z zapisem" : "obcy plik \u2014 co\u015B innego pisze do outDir"}). ` + `Przerwane, \u017Ceby nie wypu\u015Bci\u0107 niesp\xF3jnego builda.`);
|
|
32365
|
+
}
|
|
32366
|
+
}
|
|
32129
32367
|
const manifest = {
|
|
32130
32368
|
initial: { file: initialFile, hash: initialHash },
|
|
32131
32369
|
groups,
|
|
32132
32370
|
sharedChunks,
|
|
32133
32371
|
cached: false
|
|
32134
32372
|
};
|
|
32373
|
+
if (statsMode) {
|
|
32374
|
+
const jsByName = new Map;
|
|
32375
|
+
const mapsByFinal = new Map;
|
|
32376
|
+
const finalNameOf = (out) => out.kind === "entry-point" ? `${out.name.replace(/\.js$/, "")}.${sha256Hex(out.bytes).slice(0, 16)}.js` : out.name;
|
|
32377
|
+
for (const out of outFiles) {
|
|
32378
|
+
const fn = finalNameOf(out);
|
|
32379
|
+
jsByName.set(fn, out.bytes);
|
|
32380
|
+
const map = mapsByJs.get(out.name);
|
|
32381
|
+
if (map)
|
|
32382
|
+
mapsByFinal.set(fn, map);
|
|
32383
|
+
}
|
|
32384
|
+
reportBrowserBuildStats({
|
|
32385
|
+
label: federated ? "browser-fed" : "browser",
|
|
32386
|
+
entries: [
|
|
32387
|
+
{ name: "initial", file: initialFile },
|
|
32388
|
+
...Object.entries(groups).map(([stem, g]) => ({ name: stem, file: g.file }))
|
|
32389
|
+
],
|
|
32390
|
+
jsByName,
|
|
32391
|
+
mapsByJs: mapsByFinal
|
|
32392
|
+
});
|
|
32393
|
+
}
|
|
32135
32394
|
updateCache(cache, unitId, inputHash, {
|
|
32136
32395
|
outputHashes: { _manifest: JSON.stringify(manifest) }
|
|
32137
32396
|
});
|
|
@@ -32433,6 +32692,11 @@ function collectModuleNames(dir, acc) {
|
|
|
32433
32692
|
}
|
|
32434
32693
|
}
|
|
32435
32694
|
}
|
|
32695
|
+
function packageDeclaresModule(pkg) {
|
|
32696
|
+
const names = new Set;
|
|
32697
|
+
collectModuleNames(join11(pkg.path, "src"), names);
|
|
32698
|
+
return names.size > 0;
|
|
32699
|
+
}
|
|
32436
32700
|
function assertOneModulePerPackage(packages) {
|
|
32437
32701
|
const offenders = [];
|
|
32438
32702
|
for (const pkg of packages) {
|
|
@@ -32623,6 +32887,7 @@ function resolveWorkspace() {
|
|
|
32623
32887
|
async function buildAll(ws, opts = {}) {
|
|
32624
32888
|
const cache = loadBuildCache(ws.arcDir);
|
|
32625
32889
|
const noCache = opts.noCache ?? false;
|
|
32890
|
+
const minify = opts.minify ?? true;
|
|
32626
32891
|
const themePath = ws.rootPkg.arc?.theme;
|
|
32627
32892
|
const federation = getFederationConfig(ws);
|
|
32628
32893
|
log2(`Building (concurrency parallel${noCache ? ", no-cache" : ""})...`);
|
|
@@ -32637,8 +32902,9 @@ async function buildAll(ws, opts = {}) {
|
|
|
32637
32902
|
const isHost = Boolean(federation?.host);
|
|
32638
32903
|
const moduleNameOf2 = (name) => name.split("/").pop() ?? name;
|
|
32639
32904
|
const exposureOf = (pkgName) => accessMap.exposure[moduleNameOf2(pkgName)]?.exposure ?? "local";
|
|
32640
|
-
const
|
|
32641
|
-
const
|
|
32905
|
+
const modulePackages = ws.packages.filter(packageDeclaresModule);
|
|
32906
|
+
const localPackages = isHost ? modulePackages : modulePackages.filter((p) => exposureOf(p.name) !== "external");
|
|
32907
|
+
const fedPackages = modulePackages.filter((p) => ["external", "both"].includes(exposureOf(p.name)));
|
|
32642
32908
|
const hasFederatedModules = fedPackages.length > 0;
|
|
32643
32909
|
const needsPeerShell = isHost || hasFederatedModules;
|
|
32644
32910
|
log2();
|
|
@@ -32649,16 +32915,17 @@ async function buildAll(ws, opts = {}) {
|
|
|
32649
32915
|
ok(`Federated modules: ${fedPackages.map((p) => moduleNameOf2(p.name)).join(", ")}`);
|
|
32650
32916
|
}
|
|
32651
32917
|
const i18nCollector = new Map;
|
|
32652
|
-
const
|
|
32653
|
-
buildBrowserApp(ws.rootDir, ws.browserDir, plan, cache, noCache, i18nCollector, {
|
|
32654
|
-
|
|
32655
|
-
|
|
32656
|
-
|
|
32918
|
+
const browserSequence = (async () => {
|
|
32919
|
+
const bundled = await buildBrowserApp(ws.rootDir, ws.browserDir, plan, cache, noCache, i18nCollector, { federated: false, exposeRuntime: isHost, minify });
|
|
32920
|
+
const fed = fedPlan ? await buildBrowserApp(ws.rootDir, join13(ws.arcDir, "browser-fed"), fedPlan, cache, noCache, i18nCollector, { federated: true, unitId: "browser-app-fed", minify }) : undefined;
|
|
32921
|
+
return { bundled, fed };
|
|
32922
|
+
})();
|
|
32923
|
+
const [{ bundled: browserResult, fed: fedResult }, , , , shellResult] = await Promise.all([
|
|
32924
|
+
browserSequence,
|
|
32657
32925
|
buildStyles(ws.rootDir, ws.arcDir, ws.packages, themePath, cache, noCache),
|
|
32658
32926
|
copyBrowserAssets(ws, cache, noCache),
|
|
32659
32927
|
buildTranslations(ws.rootDir, ws.arcDir, cache, noCache),
|
|
32660
|
-
needsPeerShell ? buildPeerShell(ws.rootDir, join13(ws.arcDir, "shell"), cache, noCache) : Promise.resolve(undefined)
|
|
32661
|
-
fedPlan ? buildBrowserApp(ws.rootDir, join13(ws.arcDir, "browser-fed"), fedPlan, cache, noCache, i18nCollector, { federated: true, unitId: "browser-app-fed" }) : Promise.resolve(undefined)
|
|
32928
|
+
needsPeerShell ? buildPeerShell(ws.rootDir, join13(ws.arcDir, "shell"), cache, noCache) : Promise.resolve(undefined)
|
|
32662
32929
|
]);
|
|
32663
32930
|
const { finalizeTranslations: finalizeTranslations3 } = await Promise.resolve().then(() => (init_i18n(), exports_i18n));
|
|
32664
32931
|
await finalizeTranslations3(ws.rootDir, ws.arcDir, i18nCollector);
|
|
@@ -32806,20 +33073,25 @@ async function loadServerContext(ws) {
|
|
|
32806
33073
|
if (!existsSync11(serverEntry)) {
|
|
32807
33074
|
return { context: null, moduleAccess: new Map };
|
|
32808
33075
|
}
|
|
33076
|
+
let serverError;
|
|
32809
33077
|
try {
|
|
32810
33078
|
await import(serverEntry);
|
|
32811
33079
|
} catch (e) {
|
|
32812
33080
|
err(`Failed to load server bundle ${SERVER_ENTRY_FILE}: ${e}`);
|
|
33081
|
+
serverError = e;
|
|
32813
33082
|
}
|
|
32814
33083
|
const { getContext, getAllModuleAccess } = await import(platformEntry);
|
|
32815
33084
|
return {
|
|
32816
33085
|
context: getContext() ?? null,
|
|
32817
|
-
moduleAccess: getAllModuleAccess()
|
|
33086
|
+
moduleAccess: getAllModuleAccess(),
|
|
33087
|
+
serverError
|
|
32818
33088
|
};
|
|
32819
33089
|
}
|
|
32820
33090
|
|
|
32821
33091
|
// src/commands/platform-build.ts
|
|
32822
33092
|
async function platformBuild(opts = {}) {
|
|
33093
|
+
if (opts.stats)
|
|
33094
|
+
process.env.ARC_BUILD_STATS = "1";
|
|
32823
33095
|
const ws = resolveWorkspace();
|
|
32824
33096
|
const manifest = await buildAll(ws, { noCache: opts.noCache });
|
|
32825
33097
|
const groupCount = Object.keys(manifest.groups).length;
|
|
@@ -33247,6 +33519,7 @@ function generateCaddyfile(cfg) {
|
|
|
33247
33519
|
for (const [name, env2] of Object.entries(cfg.envs)) {
|
|
33248
33520
|
lines.push(`${env2.domain} {${tlsDirective}`);
|
|
33249
33521
|
lines.push(...logDirective);
|
|
33522
|
+
lines.push(" encode zstd gzip");
|
|
33250
33523
|
if (observability) {
|
|
33251
33524
|
lines.push(" handle_path /otel/* {");
|
|
33252
33525
|
lines.push(" reverse_proxy otel-collector:4318");
|
|
@@ -38266,6 +38539,14 @@ async function createArcServer(config) {
|
|
|
38266
38539
|
if (contentLength > maxRequestBodySize) {
|
|
38267
38540
|
return Response.json({ error: "Payload too large" }, { status: 413, headers: corsHeaders2 });
|
|
38268
38541
|
}
|
|
38542
|
+
if (url.pathname === "/ws" && req.headers.get("Upgrade") === "websocket") {
|
|
38543
|
+
if (server2.upgrade(req, { data: { clientId: "" } }))
|
|
38544
|
+
return;
|
|
38545
|
+
return new Response("WebSocket upgrade failed", {
|
|
38546
|
+
status: 500,
|
|
38547
|
+
headers: corsHeaders2
|
|
38548
|
+
});
|
|
38549
|
+
}
|
|
38269
38550
|
const authHeader = req.headers.get("Authorization");
|
|
38270
38551
|
let rawToken = authHeader?.replace("Bearer ", "") || null;
|
|
38271
38552
|
if (!rawToken && config.allowTokenInQuery) {
|
|
@@ -38283,14 +38564,6 @@ async function createArcServer(config) {
|
|
|
38283
38564
|
if (rawToken && !tokenPayload) {
|
|
38284
38565
|
return Response.json({ error: "Unauthorized" }, { status: 401, headers: corsHeaders2 });
|
|
38285
38566
|
}
|
|
38286
|
-
if (url.pathname === "/ws" && req.headers.get("Upgrade") === "websocket") {
|
|
38287
|
-
if (server2.upgrade(req, { data: { clientId: "" } }))
|
|
38288
|
-
return;
|
|
38289
|
-
return new Response("WebSocket upgrade failed", {
|
|
38290
|
-
status: 500,
|
|
38291
|
-
headers: corsHeaders2
|
|
38292
|
-
});
|
|
38293
|
-
}
|
|
38294
38567
|
const reqCtx = { rawToken, tokenPayload, corsHeaders: corsHeaders2 };
|
|
38295
38568
|
for (const handler of httpHandlers) {
|
|
38296
38569
|
const response = await handler(req, url, reqCtx);
|
|
@@ -38404,6 +38677,13 @@ init_i18n();
|
|
|
38404
38677
|
import { timingSafeEqual } from "crypto";
|
|
38405
38678
|
import { existsSync as existsSync19, mkdirSync as mkdirSync15 } from "fs";
|
|
38406
38679
|
import { join as join22 } from "path";
|
|
38680
|
+
async function initContextHandler(context2, dbPath) {
|
|
38681
|
+
const factory = await resolveDbAdapterFactory(dbPath);
|
|
38682
|
+
const dbAdapter = factory(context2);
|
|
38683
|
+
const handler = new ContextHandler(context2, dbAdapter);
|
|
38684
|
+
await handler.init();
|
|
38685
|
+
return handler;
|
|
38686
|
+
}
|
|
38407
38687
|
async function resolveDbAdapterFactory(dbPath) {
|
|
38408
38688
|
const databaseUrl = process.env.DATABASE_URL;
|
|
38409
38689
|
if (databaseUrl && databaseUrl.length > 0) {
|
|
@@ -38684,13 +38964,6 @@ function handleDiscovery(req, ctx, ws, manifest, federation, mapUrl) {
|
|
|
38684
38964
|
if (!federation) {
|
|
38685
38965
|
return new Response("Not Found", { status: 404, headers: ctx.corsHeaders });
|
|
38686
38966
|
}
|
|
38687
|
-
const presented = req.headers.get("X-Arc-Pairing-Token");
|
|
38688
|
-
if (!presented || !safeEqual(presented, federation.registrationToken)) {
|
|
38689
|
-
return new Response("Unauthorized", {
|
|
38690
|
-
status: 401,
|
|
38691
|
-
headers: ctx.corsHeaders
|
|
38692
|
-
});
|
|
38693
|
-
}
|
|
38694
38967
|
const fed = manifest.federation;
|
|
38695
38968
|
const platformUrl = process.env.ARC_PUBLIC_URL || new URL(req.url).origin;
|
|
38696
38969
|
return Response.json({
|
|
@@ -39025,7 +39298,7 @@ async function startPlatform(opts) {
|
|
|
39025
39298
|
const dbPath = opts.dbPath ?? join23(ws.rootDir, ".arc", "data", devMode ? "dev.db" : "prod.db");
|
|
39026
39299
|
let manifest;
|
|
39027
39300
|
if (devMode) {
|
|
39028
|
-
manifest = await buildAll(ws);
|
|
39301
|
+
manifest = await buildAll(ws, { minify: false });
|
|
39029
39302
|
} else {
|
|
39030
39303
|
const manifestPath = join23(ws.arcDir, "manifest.json");
|
|
39031
39304
|
if (!existsSync20(manifestPath)) {
|
|
@@ -39041,7 +39314,11 @@ async function startPlatform(opts) {
|
|
|
39041
39314
|
process.env.ARC_APP_SECRET = resolveAppSecret(ws.rootDir);
|
|
39042
39315
|
}
|
|
39043
39316
|
log2("Loading server context...");
|
|
39044
|
-
const { context: context2, moduleAccess } = await loadServerContext(ws);
|
|
39317
|
+
const { context: context2, moduleAccess, serverError } = await loadServerContext(ws);
|
|
39318
|
+
if (serverError) {
|
|
39319
|
+
err("Serwer NIE wstanie: modu\u0142 serwerowy nie za\u0142adowa\u0142 si\u0119 (b\u0142\u0105d wy\u017Cej). " + "Cz\u0119\u015Bciowy kontekst = po\u0142owiczna aplikacja (query rzucaj\u0105 " + "'Element not found'). Napraw przyczyn\u0119 (np. brakuj\u0105ce zmienne " + "\u015Brodowiskowe) i wystartuj ponownie.");
|
|
39320
|
+
process.exit(1);
|
|
39321
|
+
}
|
|
39045
39322
|
if (context2) {
|
|
39046
39323
|
ok("Context loaded");
|
|
39047
39324
|
} else {
|
|
@@ -39136,18 +39413,12 @@ async function startMapTool(ws, port, authToken, platformServer, platform3) {
|
|
|
39136
39413
|
function assertProdFederationSecrets(devMode, hasFederation) {
|
|
39137
39414
|
if (devMode || !hasFederation)
|
|
39138
39415
|
return;
|
|
39139
|
-
|
|
39140
|
-
if (!process.env.ARC_APP_SECRET)
|
|
39141
|
-
missing.push("ARC_APP_SECRET");
|
|
39142
|
-
if (!process.env.ARC_PAIRING_TOKEN && !process.env.ARC_MAP_TOKEN) {
|
|
39143
|
-
missing.push("ARC_PAIRING_TOKEN");
|
|
39144
|
-
}
|
|
39145
|
-
if (missing.length === 0)
|
|
39416
|
+
if (process.env.ARC_PAIRING_TOKEN || process.env.ARC_MAP_TOKEN)
|
|
39146
39417
|
return;
|
|
39147
|
-
err(`Federacja w produkcji wymaga
|
|
39148
|
-
` + ` Bez
|
|
39149
|
-
` + ` recreate \u2014
|
|
39150
|
-
` + ` Ustaw
|
|
39418
|
+
err(`Federacja w produkcji wymaga zmiennej \u015Brodowiskowej ARC_PAIRING_TOKEN.
|
|
39419
|
+
` + ` Bez niej token parowania jest generowany w kontenerze i GINIE przy
|
|
39420
|
+
` + ` pierwszym recreate \u2014 parowania host\xF3w staj\u0105 si\u0119 martwe, bez b\u0142\u0119du.
|
|
39421
|
+
` + ` Ustaw j\u0105 w deployu (deploy.arc.json \u2192 envVars + deploy.arc.<env>.env).`);
|
|
39151
39422
|
process.exit(1);
|
|
39152
39423
|
}
|
|
39153
39424
|
function resolveAppSecret(rootDir) {
|
|
@@ -39213,7 +39484,7 @@ function attachDevWatcher(ws, platform3, onReload) {
|
|
|
39213
39484
|
isRebuilding = true;
|
|
39214
39485
|
log2("Rebuilding...");
|
|
39215
39486
|
try {
|
|
39216
|
-
const next = await buildAll(ws);
|
|
39487
|
+
const next = await buildAll(ws, { minify: false });
|
|
39217
39488
|
platform3.setManifest(next);
|
|
39218
39489
|
platform3.notifyReload(next);
|
|
39219
39490
|
onReload?.();
|
|
@@ -39258,6 +39529,36 @@ async function platformDev(opts = {}) {
|
|
|
39258
39529
|
});
|
|
39259
39530
|
}
|
|
39260
39531
|
|
|
39532
|
+
// src/commands/platform-pairing-token.ts
|
|
39533
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
39534
|
+
import { join as join24 } from "path";
|
|
39535
|
+
async function platformPairingToken() {
|
|
39536
|
+
const ws = resolveWorkspace();
|
|
39537
|
+
const { context: context2 } = await loadServerContext(ws);
|
|
39538
|
+
if (!context2) {
|
|
39539
|
+
err("Brak zbudowanego kontekstu. Uruchom najpierw `arc platform build`.");
|
|
39540
|
+
process.exit(1);
|
|
39541
|
+
}
|
|
39542
|
+
const isProd = false;
|
|
39543
|
+
const dbPath = join24(ws.rootDir, ".arc", "data", isProd ? "prod.db" : "dev.db");
|
|
39544
|
+
const handler = await initContextHandler(context2, dbPath);
|
|
39545
|
+
const code = randomBytes3(24).toString("base64url");
|
|
39546
|
+
const res = await handler.executeCommand("pairingCode.issue", { code }, null, { internal: true });
|
|
39547
|
+
if (!res?.ok) {
|
|
39548
|
+
err(`Nie uda\u0142o si\u0119 zapisa\u0107 kodu parowania${res?.error ? `: ${res.error}` : ""}. ` + "Czy aplikacja wystawia agregat `pairingCode` (federacja)?");
|
|
39549
|
+
process.exit(1);
|
|
39550
|
+
}
|
|
39551
|
+
process.stdout.write(`
|
|
39552
|
+
Kod parowania (jednorazowy):
|
|
39553
|
+
|
|
39554
|
+
${code}
|
|
39555
|
+
|
|
39556
|
+
` + ` Podaj go w ho\u015Bcie przy instalacji aplikacji. Wygasa po pierwszym u\u017Cyciu.
|
|
39557
|
+
|
|
39558
|
+
`);
|
|
39559
|
+
process.exit(0);
|
|
39560
|
+
}
|
|
39561
|
+
|
|
39261
39562
|
// src/commands/platform-start.ts
|
|
39262
39563
|
async function platformStart() {
|
|
39263
39564
|
const ws = resolveWorkspace();
|
|
@@ -39275,8 +39576,9 @@ platform3.command("dev").description("Start platform in dev mode (Bun server + V
|
|
|
39275
39576
|
map: opts.map,
|
|
39276
39577
|
headless: opts.headless
|
|
39277
39578
|
}));
|
|
39278
|
-
platform3.command("build").description("Build platform for production").option("--no-cache", "Force full rebuild").action((opts) => platformBuild({ noCache: opts.cache === false }));
|
|
39579
|
+
platform3.command("build").description("Build platform for production").option("--no-cache", "Force full rebuild").option("--stats", "Wypisz rozbicie chunk\xF3w na biblioteki (per-wej\u015Bcie raw/gzip + najci\u0119\u017Csze liby, ext/wewn). Wymusza pe\u0142ny build.").action((opts) => platformBuild({ noCache: opts.cache === false, stats: opts.stats }));
|
|
39279
39580
|
platform3.command("start").description("Start platform in production mode (requires prior build)").action(platformStart);
|
|
39581
|
+
platform3.command("pairing-token").description("Generate a one-time federation pairing code (writes to DB, prints it)").action(platformPairingToken);
|
|
39280
39582
|
platform3.command("deploy [env]").description("Deploy platform to a remote server (reads deploy.arc.json, surveys if missing)").option("--skip-build", "Skip local build step").option("--rebuild", "Force rebuild before deploy").option("--build-only", "Build the Docker image locally, then exit (no remote push)").option("--image-tag <hash>", "Roll back / pin to an existing image tag instead of building a new one").option("--force-bootstrap", "Re-run Ansible host bootstrap even if the server is already configured").action((env2, opts) => platformDeploy(env2, opts));
|
|
39281
39583
|
program2.parse(process.argv);
|
|
39282
39584
|
if (process.argv.length === 2) {
|