@arcote.tech/arc-cli 0.5.8 → 0.6.1
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 +942 -426
- package/package.json +7 -7
- package/runtime/Dockerfile +29 -0
- package/runtime/build-and-push.sh +23 -0
- package/runtime/entrypoint.sh +58 -0
- package/src/builder/access-extractor.ts +127 -0
- package/src/builder/dependency-collector.ts +155 -0
- package/src/commands/build-shell.ts +152 -0
- package/src/commands/platform-deploy.ts +21 -5
- package/src/commands/platform-start.ts +36 -5
- package/src/deploy/ansible.ts +26 -23
- package/src/deploy/bootstrap.ts +11 -5
- package/src/deploy/compose.ts +31 -13
- package/src/deploy/config.ts +9 -4
- package/src/deploy/remote-state.ts +7 -0
- package/src/deploy/remote-sync.ts +199 -78
- package/src/deploy/ssh.ts +14 -4
- package/src/deploy/terraform.ts +42 -22
- package/src/index.ts +11 -0
- package/src/platform/deploy-api.ts +303 -90
- package/src/platform/shared.ts +25 -1
package/dist/index.js
CHANGED
|
@@ -21686,7 +21686,7 @@ var emitWarning = (msg, type, code, fn) => {
|
|
|
21686
21686
|
var AC = globalThis.AbortController;
|
|
21687
21687
|
var AS = globalThis.AbortSignal;
|
|
21688
21688
|
if (typeof AC === "undefined") {
|
|
21689
|
-
AS = class
|
|
21689
|
+
AS = class AbortSignal2 {
|
|
21690
21690
|
onabort;
|
|
21691
21691
|
_onabort = [];
|
|
21692
21692
|
reason;
|
|
@@ -26129,9 +26129,110 @@ ${colors2.yellow}Type declaration errors:${colors2.reset}`);
|
|
|
26129
26129
|
}
|
|
26130
26130
|
}
|
|
26131
26131
|
|
|
26132
|
+
// src/commands/build-shell.ts
|
|
26133
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, rmSync } from "fs";
|
|
26134
|
+
import { join as join3 } from "path";
|
|
26135
|
+
var REACT_ENTRIES = [
|
|
26136
|
+
["react", `export * from "react";
|
|
26137
|
+
import * as React from "react";
|
|
26138
|
+
export default React;`],
|
|
26139
|
+
["react-dom", `export * from "react-dom";
|
|
26140
|
+
import * as ReactDOM from "react-dom";
|
|
26141
|
+
export default ReactDOM;`],
|
|
26142
|
+
["jsx-runtime", `export * from "react/jsx-runtime";`],
|
|
26143
|
+
["jsx-dev-runtime", `export * from "react/jsx-dev-runtime";`],
|
|
26144
|
+
["react-dom-client", `export { createRoot, hydrateRoot } from "react-dom/client";`]
|
|
26145
|
+
];
|
|
26146
|
+
var SHELL_BASE_EXTERNAL = [
|
|
26147
|
+
"react",
|
|
26148
|
+
"react-dom",
|
|
26149
|
+
"react/jsx-runtime",
|
|
26150
|
+
"react/jsx-dev-runtime",
|
|
26151
|
+
"react-dom/client"
|
|
26152
|
+
];
|
|
26153
|
+
async function buildShell(opts) {
|
|
26154
|
+
const outDir = opts.out;
|
|
26155
|
+
const fromDir = opts.from;
|
|
26156
|
+
if (!existsSync3(fromDir)) {
|
|
26157
|
+
console.error(`[_build-shell] --from not found: ${fromDir}`);
|
|
26158
|
+
process.exit(1);
|
|
26159
|
+
}
|
|
26160
|
+
mkdirSync3(outDir, { recursive: true });
|
|
26161
|
+
const tmpDir = join3(outDir, "_tmp");
|
|
26162
|
+
mkdirSync3(tmpDir, { recursive: true });
|
|
26163
|
+
try {
|
|
26164
|
+
const arcPkgs = discoverArcPackages(fromDir);
|
|
26165
|
+
if (arcPkgs.length === 0) {
|
|
26166
|
+
console.warn("[_build-shell] no @arcote.tech/* packages discovered");
|
|
26167
|
+
}
|
|
26168
|
+
console.log(`[_build-shell] building shell for react + ${arcPkgs.length} @arcote.tech/* package(s)`);
|
|
26169
|
+
await buildReactShell(outDir, tmpDir, fromDir);
|
|
26170
|
+
for (const pkg of arcPkgs) {
|
|
26171
|
+
await buildArcEntry(pkg, arcPkgs, outDir, tmpDir, fromDir);
|
|
26172
|
+
}
|
|
26173
|
+
console.log(`[_build-shell] done \u2192 ${outDir}`);
|
|
26174
|
+
} finally {
|
|
26175
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
26176
|
+
}
|
|
26177
|
+
}
|
|
26178
|
+
function discoverArcPackages(fromDir) {
|
|
26179
|
+
const arcDir = join3(fromDir, "@arcote.tech");
|
|
26180
|
+
if (!existsSync3(arcDir))
|
|
26181
|
+
return [];
|
|
26182
|
+
return readdirSync2(arcDir).filter((name) => existsSync3(join3(arcDir, name, "package.json"))).map((name) => `@arcote.tech/${name}`);
|
|
26183
|
+
}
|
|
26184
|
+
async function buildReactShell(outDir, tmpDir, fromDir) {
|
|
26185
|
+
const eps = [];
|
|
26186
|
+
for (const [name, code] of REACT_ENTRIES) {
|
|
26187
|
+
const f = join3(tmpDir, `${name}.ts`);
|
|
26188
|
+
await Bun.write(f, code);
|
|
26189
|
+
eps.push(f);
|
|
26190
|
+
}
|
|
26191
|
+
const r = await Bun.build({
|
|
26192
|
+
entrypoints: eps,
|
|
26193
|
+
outdir: outDir,
|
|
26194
|
+
splitting: true,
|
|
26195
|
+
format: "esm",
|
|
26196
|
+
target: "browser",
|
|
26197
|
+
naming: "[name].[ext]",
|
|
26198
|
+
root: fromDir
|
|
26199
|
+
});
|
|
26200
|
+
if (!r.success) {
|
|
26201
|
+
for (const l of r.logs)
|
|
26202
|
+
console.error(l);
|
|
26203
|
+
throw new Error("React shell build failed");
|
|
26204
|
+
}
|
|
26205
|
+
}
|
|
26206
|
+
async function buildArcEntry(pkg, allArcPkgs, outDir, tmpDir, fromDir) {
|
|
26207
|
+
const shortName = pkg.replace("@arcote.tech/", "");
|
|
26208
|
+
const otherExternals = allArcPkgs.filter((p) => p !== pkg);
|
|
26209
|
+
const f = join3(tmpDir, `${shortName}.ts`);
|
|
26210
|
+
await Bun.write(f, `export * from "${pkg}";
|
|
26211
|
+
`);
|
|
26212
|
+
const r = await Bun.build({
|
|
26213
|
+
entrypoints: [f],
|
|
26214
|
+
outdir: outDir,
|
|
26215
|
+
format: "esm",
|
|
26216
|
+
target: "browser",
|
|
26217
|
+
naming: "[name].[ext]",
|
|
26218
|
+
root: fromDir,
|
|
26219
|
+
external: [...SHELL_BASE_EXTERNAL, ...otherExternals],
|
|
26220
|
+
define: {
|
|
26221
|
+
ONLY_SERVER: "false",
|
|
26222
|
+
ONLY_BROWSER: "true",
|
|
26223
|
+
ONLY_CLIENT: "true"
|
|
26224
|
+
}
|
|
26225
|
+
});
|
|
26226
|
+
if (!r.success) {
|
|
26227
|
+
for (const l of r.logs)
|
|
26228
|
+
console.error(l);
|
|
26229
|
+
throw new Error(`Shell build failed for ${pkg}`);
|
|
26230
|
+
}
|
|
26231
|
+
}
|
|
26232
|
+
|
|
26132
26233
|
// src/commands/dev.ts
|
|
26133
26234
|
var import_chokidar = __toESM(require_chokidar(), 1);
|
|
26134
|
-
import { dirname as dirname3, join as
|
|
26235
|
+
import { dirname as dirname3, join as join4, relative } from "path";
|
|
26135
26236
|
function getContextForFile(filePath, contexts, configDir) {
|
|
26136
26237
|
const relativePath = relative(configDir, filePath);
|
|
26137
26238
|
for (const context of contexts) {
|
|
@@ -26248,7 +26349,7 @@ ${colors3.yellow}Type declaration errors:${colors3.reset}`);
|
|
|
26248
26349
|
} else {
|
|
26249
26350
|
log("Initial build complete \u2713", "green");
|
|
26250
26351
|
}
|
|
26251
|
-
const watcher = import_chokidar.default.watch([
|
|
26352
|
+
const watcher = import_chokidar.default.watch([join4(configDir, "**/*.ts"), join4(configDir, "**/*.tsx")], {
|
|
26252
26353
|
ignored: [
|
|
26253
26354
|
"**/node_modules/**",
|
|
26254
26355
|
`**/${config.outDir}/**`,
|
|
@@ -26345,24 +26446,24 @@ ${colors3.yellow}Type declaration errors:${colors3.reset}`);
|
|
|
26345
26446
|
}
|
|
26346
26447
|
|
|
26347
26448
|
// src/platform/shared.ts
|
|
26348
|
-
import { copyFileSync, existsSync as
|
|
26349
|
-
import { dirname as dirname6, join as
|
|
26449
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync10, readFileSync as readFileSync11, readdirSync as readdirSync6, rmSync as rmSync3, writeFileSync as writeFileSync10 } from "fs";
|
|
26450
|
+
import { dirname as dirname6, join as join12 } from "path";
|
|
26350
26451
|
|
|
26351
26452
|
// src/builder/module-builder.ts
|
|
26352
26453
|
import { execSync } from "child_process";
|
|
26353
26454
|
import {
|
|
26354
|
-
existsSync as
|
|
26355
|
-
mkdirSync as
|
|
26356
|
-
readFileSync as
|
|
26357
|
-
readdirSync as
|
|
26358
|
-
rmSync,
|
|
26359
|
-
writeFileSync as
|
|
26455
|
+
existsSync as existsSync8,
|
|
26456
|
+
mkdirSync as mkdirSync7,
|
|
26457
|
+
readFileSync as readFileSync8,
|
|
26458
|
+
readdirSync as readdirSync5,
|
|
26459
|
+
rmSync as rmSync2,
|
|
26460
|
+
writeFileSync as writeFileSync7
|
|
26360
26461
|
} from "fs";
|
|
26361
|
-
import { basename as basename2, dirname as dirname5, join as
|
|
26462
|
+
import { basename as basename2, dirname as dirname5, join as join9, relative as relative3 } from "path";
|
|
26362
26463
|
|
|
26363
26464
|
// src/i18n/index.ts
|
|
26364
|
-
import { existsSync as
|
|
26365
|
-
import { dirname as dirname4, join as
|
|
26465
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
26466
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
26366
26467
|
|
|
26367
26468
|
// src/i18n/catalog.ts
|
|
26368
26469
|
function hashMsgid(msgid) {
|
|
@@ -26533,10 +26634,10 @@ function quoteString(s) {
|
|
|
26533
26634
|
}
|
|
26534
26635
|
|
|
26535
26636
|
// src/i18n/compile.ts
|
|
26536
|
-
import { mkdirSync as
|
|
26537
|
-
import { join as
|
|
26637
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, readdirSync as readdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
26638
|
+
import { join as join5 } from "path";
|
|
26538
26639
|
function compileCatalog(poPath) {
|
|
26539
|
-
const content =
|
|
26640
|
+
const content = readFileSync4(poPath, "utf-8");
|
|
26540
26641
|
const entries = parsePo(content);
|
|
26541
26642
|
const result = {};
|
|
26542
26643
|
for (const entry of entries) {
|
|
@@ -26551,13 +26652,13 @@ function compileCatalog(poPath) {
|
|
|
26551
26652
|
return sorted;
|
|
26552
26653
|
}
|
|
26553
26654
|
function compileAllCatalogs(localesDir, outDir) {
|
|
26554
|
-
|
|
26555
|
-
for (const file of
|
|
26655
|
+
mkdirSync4(outDir, { recursive: true });
|
|
26656
|
+
for (const file of readdirSync3(localesDir)) {
|
|
26556
26657
|
if (!file.endsWith(".po"))
|
|
26557
26658
|
continue;
|
|
26558
26659
|
const locale = file.replace(".po", "");
|
|
26559
|
-
const compiled = compileCatalog(
|
|
26560
|
-
|
|
26660
|
+
const compiled = compileCatalog(join5(localesDir, file));
|
|
26661
|
+
writeFileSync4(join5(outDir, `${locale}.json`), JSON.stringify(compiled));
|
|
26561
26662
|
}
|
|
26562
26663
|
}
|
|
26563
26664
|
|
|
@@ -26601,10 +26702,10 @@ function i18nExtractPlugin(collector, rootDir) {
|
|
|
26601
26702
|
|
|
26602
26703
|
// src/i18n/index.ts
|
|
26603
26704
|
function readTranslationsConfig(rootDir) {
|
|
26604
|
-
const pkgPath =
|
|
26605
|
-
if (!
|
|
26705
|
+
const pkgPath = join6(rootDir, "package.json");
|
|
26706
|
+
if (!existsSync5(pkgPath))
|
|
26606
26707
|
return null;
|
|
26607
|
-
const pkg = JSON.parse(
|
|
26708
|
+
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
26608
26709
|
const config = pkg.arc?.translations;
|
|
26609
26710
|
if (!config?.locales?.length)
|
|
26610
26711
|
return null;
|
|
@@ -26617,34 +26718,34 @@ async function finalizeTranslations(rootDir, outDir, collector) {
|
|
|
26617
26718
|
const config = readTranslationsConfig(rootDir);
|
|
26618
26719
|
if (!config || collector.size === 0)
|
|
26619
26720
|
return;
|
|
26620
|
-
const localesJsonDir =
|
|
26621
|
-
|
|
26721
|
+
const localesJsonDir = join6(outDir, "locales");
|
|
26722
|
+
mkdirSync5(localesJsonDir, { recursive: true });
|
|
26622
26723
|
console.log(` Extracted ${collector.size} translatable string(s) for ${config.locales.length} locale(s)`);
|
|
26623
26724
|
for (const locale of config.locales) {
|
|
26624
|
-
const poPath =
|
|
26625
|
-
|
|
26626
|
-
const existing =
|
|
26725
|
+
const poPath = join6(rootDir, "locales", `${locale}.po`);
|
|
26726
|
+
mkdirSync5(dirname4(poPath), { recursive: true });
|
|
26727
|
+
const existing = existsSync5(poPath) ? parsePo(readFileSync5(poPath, "utf-8")) : [];
|
|
26627
26728
|
const merged = mergeCatalog(existing, collector);
|
|
26628
|
-
|
|
26729
|
+
writeFileSync5(poPath, writePo(merged));
|
|
26629
26730
|
const compiled = compileCatalog(poPath);
|
|
26630
|
-
|
|
26731
|
+
writeFileSync5(join6(localesJsonDir, `${locale}.json`), JSON.stringify(compiled));
|
|
26631
26732
|
}
|
|
26632
26733
|
}
|
|
26633
26734
|
|
|
26634
26735
|
// src/builder/build-cache.ts
|
|
26635
|
-
import { existsSync as
|
|
26636
|
-
import { join as
|
|
26736
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
26737
|
+
import { join as join7 } from "path";
|
|
26637
26738
|
var CACHE_VERSION = 1;
|
|
26638
26739
|
var CACHE_FILE = ".build-cache.json";
|
|
26639
26740
|
function emptyCache() {
|
|
26640
26741
|
return { version: CACHE_VERSION, units: {} };
|
|
26641
26742
|
}
|
|
26642
26743
|
function loadBuildCache(arcDir) {
|
|
26643
|
-
const path4 =
|
|
26644
|
-
if (!
|
|
26744
|
+
const path4 = join7(arcDir, CACHE_FILE);
|
|
26745
|
+
if (!existsSync6(path4))
|
|
26645
26746
|
return emptyCache();
|
|
26646
26747
|
try {
|
|
26647
|
-
const raw = JSON.parse(
|
|
26748
|
+
const raw = JSON.parse(readFileSync6(path4, "utf-8"));
|
|
26648
26749
|
if (raw?.version !== CACHE_VERSION || typeof raw.units !== "object") {
|
|
26649
26750
|
return emptyCache();
|
|
26650
26751
|
}
|
|
@@ -26654,15 +26755,15 @@ function loadBuildCache(arcDir) {
|
|
|
26654
26755
|
}
|
|
26655
26756
|
}
|
|
26656
26757
|
function saveBuildCache(arcDir, cache) {
|
|
26657
|
-
|
|
26658
|
-
|
|
26758
|
+
mkdirSync6(arcDir, { recursive: true });
|
|
26759
|
+
writeFileSync6(join7(arcDir, CACHE_FILE), JSON.stringify(cache, null, 2));
|
|
26659
26760
|
}
|
|
26660
26761
|
function isCacheHit(cache, unitId, inputHash, requiredOutputs = []) {
|
|
26661
26762
|
const entry = cache.units[unitId];
|
|
26662
26763
|
if (!entry || entry.inputHash !== inputHash)
|
|
26663
26764
|
return false;
|
|
26664
26765
|
for (const out of requiredOutputs) {
|
|
26665
|
-
if (!
|
|
26766
|
+
if (!existsSync6(out))
|
|
26666
26767
|
return false;
|
|
26667
26768
|
}
|
|
26668
26769
|
return true;
|
|
@@ -26676,8 +26777,8 @@ function updateCache(cache, unitId, inputHash, output = {}) {
|
|
|
26676
26777
|
}
|
|
26677
26778
|
|
|
26678
26779
|
// src/builder/hash.ts
|
|
26679
|
-
import { existsSync as
|
|
26680
|
-
import { join as
|
|
26780
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, readdirSync as readdirSync4, statSync } from "fs";
|
|
26781
|
+
import { join as join8, relative as relative2, sep as sep2 } from "path";
|
|
26681
26782
|
function sha256Hex(bytes) {
|
|
26682
26783
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
26683
26784
|
hasher.update(bytes);
|
|
@@ -26687,21 +26788,21 @@ function sha256OfFiles(paths) {
|
|
|
26687
26788
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
26688
26789
|
const sorted = [...paths].sort();
|
|
26689
26790
|
for (const p of sorted) {
|
|
26690
|
-
if (!
|
|
26791
|
+
if (!existsSync7(p))
|
|
26691
26792
|
continue;
|
|
26692
|
-
hasher.update(
|
|
26793
|
+
hasher.update(readFileSync7(p));
|
|
26693
26794
|
hasher.update("\x00");
|
|
26694
26795
|
}
|
|
26695
26796
|
return hasher.digest("hex");
|
|
26696
26797
|
}
|
|
26697
26798
|
function sha256OfDir(dir, filter2) {
|
|
26698
|
-
if (!
|
|
26799
|
+
if (!existsSync7(dir))
|
|
26699
26800
|
return sha256Hex("");
|
|
26700
26801
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
26701
26802
|
const entries = [];
|
|
26702
26803
|
function walk(absDir) {
|
|
26703
|
-
for (const entry of
|
|
26704
|
-
const abs =
|
|
26804
|
+
for (const entry of readdirSync4(absDir, { withFileTypes: true })) {
|
|
26805
|
+
const abs = join8(absDir, entry.name);
|
|
26705
26806
|
const rel = relative2(dir, abs).split(sep2).join("/");
|
|
26706
26807
|
if (filter2 && !filter2(rel))
|
|
26707
26808
|
continue;
|
|
@@ -26716,7 +26817,7 @@ function sha256OfDir(dir, filter2) {
|
|
|
26716
26817
|
for (const { rel, abs } of entries) {
|
|
26717
26818
|
hasher.update(rel);
|
|
26718
26819
|
hasher.update("\x00");
|
|
26719
|
-
hasher.update(
|
|
26820
|
+
hasher.update(readFileSync7(abs));
|
|
26720
26821
|
hasher.update("\x00");
|
|
26721
26822
|
}
|
|
26722
26823
|
return hasher.digest("hex");
|
|
@@ -26735,17 +26836,17 @@ function stableStringify(value) {
|
|
|
26735
26836
|
return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") + "}";
|
|
26736
26837
|
}
|
|
26737
26838
|
function readInstalledVersion(rootDir, pkgName) {
|
|
26738
|
-
const pkgJson =
|
|
26739
|
-
if (!
|
|
26839
|
+
const pkgJson = join8(rootDir, "node_modules", pkgName, "package.json");
|
|
26840
|
+
if (!existsSync7(pkgJson))
|
|
26740
26841
|
return null;
|
|
26741
26842
|
try {
|
|
26742
|
-
return JSON.parse(
|
|
26843
|
+
return JSON.parse(readFileSync7(pkgJson, "utf-8")).version ?? null;
|
|
26743
26844
|
} catch {
|
|
26744
26845
|
return null;
|
|
26745
26846
|
}
|
|
26746
26847
|
}
|
|
26747
26848
|
function mtimeOf(path4) {
|
|
26748
|
-
if (!
|
|
26849
|
+
if (!existsSync7(path4))
|
|
26749
26850
|
return 0;
|
|
26750
26851
|
try {
|
|
26751
26852
|
return statSync(path4).mtimeMs;
|
|
@@ -26793,40 +26894,40 @@ var SHELL_EXTERNALS = [
|
|
|
26793
26894
|
"@arcote.tech/platform"
|
|
26794
26895
|
];
|
|
26795
26896
|
function discoverPackages(rootDir) {
|
|
26796
|
-
const rootPkg = JSON.parse(
|
|
26897
|
+
const rootPkg = JSON.parse(readFileSync8(join9(rootDir, "package.json"), "utf-8"));
|
|
26797
26898
|
const workspaceGlobs = rootPkg.workspaces ?? [];
|
|
26798
26899
|
const results = [];
|
|
26799
26900
|
for (const glob2 of workspaceGlobs) {
|
|
26800
26901
|
const base2 = glob2.replace("/*", "");
|
|
26801
|
-
const baseDir =
|
|
26802
|
-
if (!
|
|
26902
|
+
const baseDir = join9(rootDir, base2);
|
|
26903
|
+
if (!existsSync8(baseDir))
|
|
26803
26904
|
continue;
|
|
26804
26905
|
let entries;
|
|
26805
26906
|
try {
|
|
26806
|
-
entries =
|
|
26907
|
+
entries = readdirSync5(baseDir);
|
|
26807
26908
|
} catch {
|
|
26808
26909
|
continue;
|
|
26809
26910
|
}
|
|
26810
26911
|
for (const entry of entries) {
|
|
26811
|
-
const pkgPath =
|
|
26812
|
-
if (!
|
|
26912
|
+
const pkgPath = join9(baseDir, entry, "package.json");
|
|
26913
|
+
if (!existsSync8(pkgPath))
|
|
26813
26914
|
continue;
|
|
26814
|
-
const pkg = JSON.parse(
|
|
26915
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
26815
26916
|
if (pkg.name?.startsWith("@arcote.tech/"))
|
|
26816
26917
|
continue;
|
|
26817
|
-
const pkgDir =
|
|
26918
|
+
const pkgDir = join9(baseDir, entry);
|
|
26818
26919
|
const candidates = [
|
|
26819
|
-
|
|
26820
|
-
|
|
26821
|
-
|
|
26822
|
-
|
|
26920
|
+
join9(pkgDir, "src", "index.ts"),
|
|
26921
|
+
join9(pkgDir, "src", "index.tsx"),
|
|
26922
|
+
join9(pkgDir, "index.ts"),
|
|
26923
|
+
join9(pkgDir, "index.tsx")
|
|
26823
26924
|
];
|
|
26824
|
-
const entrypoint = candidates.find((c) =>
|
|
26925
|
+
const entrypoint = candidates.find((c) => existsSync8(c)) ?? null;
|
|
26825
26926
|
if (!entrypoint)
|
|
26826
26927
|
continue;
|
|
26827
26928
|
results.push({
|
|
26828
26929
|
name: pkg.name,
|
|
26829
|
-
path:
|
|
26930
|
+
path: join9(baseDir, entry),
|
|
26830
26931
|
entrypoint,
|
|
26831
26932
|
packageJson: pkg
|
|
26832
26933
|
});
|
|
@@ -26855,7 +26956,7 @@ var sourceFilter = (rel) => {
|
|
|
26855
26956
|
return true;
|
|
26856
26957
|
};
|
|
26857
26958
|
function pkgSourceHash(pkg) {
|
|
26858
|
-
return sha256OfDir(
|
|
26959
|
+
return sha256OfDir(join9(pkg.path, "src"), sourceFilter);
|
|
26859
26960
|
}
|
|
26860
26961
|
function depVersionsHash(rootDir, pkg) {
|
|
26861
26962
|
const peerDeps = Object.keys(pkg.packageJson.peerDependencies ?? {});
|
|
@@ -26868,7 +26969,7 @@ function depVersionsHash(rootDir, pkg) {
|
|
|
26868
26969
|
}
|
|
26869
26970
|
async function buildContextClient(pkg, rootDir, client, cache, noCache) {
|
|
26870
26971
|
const unitId = `context-pkg:${pkg.name}:${client.name}`;
|
|
26871
|
-
const outDir =
|
|
26972
|
+
const outDir = join9(pkg.path, "dist", client.name);
|
|
26872
26973
|
const inputHash = sha256OfJson({
|
|
26873
26974
|
src: pkgSourceHash(pkg),
|
|
26874
26975
|
pkg: pkg.packageJson,
|
|
@@ -26877,7 +26978,7 @@ async function buildContextClient(pkg, rootDir, client, cache, noCache) {
|
|
|
26877
26978
|
target: client.target,
|
|
26878
26979
|
defines: client.defines
|
|
26879
26980
|
});
|
|
26880
|
-
if (!noCache && isCacheHit(cache, unitId, inputHash, [
|
|
26981
|
+
if (!noCache && isCacheHit(cache, unitId, inputHash, [join9(outDir, "main", "index.js")])) {
|
|
26881
26982
|
console.log(` \u2713 cached: ${pkg.name} (${client.name})`);
|
|
26882
26983
|
return { pkgName: pkg.name, client: client.name, declarationErrors: [], cached: true };
|
|
26883
26984
|
}
|
|
@@ -26887,7 +26988,7 @@ async function buildContextClient(pkg, rootDir, client, cache, noCache) {
|
|
|
26887
26988
|
const externals = [...peerDeps, ...deps];
|
|
26888
26989
|
const result = await Bun.build({
|
|
26889
26990
|
entrypoints: [pkg.entrypoint],
|
|
26890
|
-
outdir:
|
|
26991
|
+
outdir: join9(outDir, "main"),
|
|
26891
26992
|
target: client.target,
|
|
26892
26993
|
format: "esm",
|
|
26893
26994
|
naming: "index.[ext]",
|
|
@@ -26925,7 +27026,7 @@ async function buildContextPackages(rootDir, packages, cache, noCache) {
|
|
|
26925
27026
|
return { declarationErrors };
|
|
26926
27027
|
}
|
|
26927
27028
|
async function buildModulesBundle(rootDir, outDir, packages, cache, noCache) {
|
|
26928
|
-
|
|
27029
|
+
mkdirSync7(outDir, { recursive: true });
|
|
26929
27030
|
const unitId = "modules-bundle";
|
|
26930
27031
|
const pkgHashes = packages.map((p) => ({
|
|
26931
27032
|
name: p.name,
|
|
@@ -26942,12 +27043,12 @@ async function buildModulesBundle(rootDir, outDir, packages, cache, noCache) {
|
|
|
26942
27043
|
const modules = [];
|
|
26943
27044
|
for (const { safeName, name } of pkgHashes) {
|
|
26944
27045
|
const file = `${safeName}.js`;
|
|
26945
|
-
const filePath =
|
|
26946
|
-
if (!
|
|
27046
|
+
const filePath = join9(outDir, file);
|
|
27047
|
+
if (!existsSync8(filePath)) {
|
|
26947
27048
|
console.log(` rebuilding modules-bundle: output ${file} missing`);
|
|
26948
27049
|
return await actuallyBuild();
|
|
26949
27050
|
}
|
|
26950
|
-
modules.push({ file, name, hash: existing[safeName] ?? sha256Hex(
|
|
27051
|
+
modules.push({ file, name, hash: existing[safeName] ?? sha256Hex(readFileSync8(filePath)) });
|
|
26951
27052
|
}
|
|
26952
27053
|
console.log(` \u2713 cached: modules-bundle (${modules.length} module(s))`);
|
|
26953
27054
|
return { modules, cached: true };
|
|
@@ -26955,16 +27056,16 @@ async function buildModulesBundle(rootDir, outDir, packages, cache, noCache) {
|
|
|
26955
27056
|
return await actuallyBuild();
|
|
26956
27057
|
async function actuallyBuild() {
|
|
26957
27058
|
console.log(` building: modules-bundle (${packages.length} package(s))`);
|
|
26958
|
-
const tmpDir =
|
|
26959
|
-
|
|
27059
|
+
const tmpDir = join9(outDir, "_entries");
|
|
27060
|
+
mkdirSync7(tmpDir, { recursive: true });
|
|
26960
27061
|
const entrypoints = [];
|
|
26961
27062
|
const fileToName = new Map;
|
|
26962
27063
|
for (const pkg of packages) {
|
|
26963
27064
|
const safeName = basename2(pkg.path);
|
|
26964
27065
|
const moduleName = pkg.name.includes("/") ? pkg.name.split("/").pop() : pkg.name;
|
|
26965
27066
|
fileToName.set(safeName, moduleName);
|
|
26966
|
-
const wrapperFile =
|
|
26967
|
-
|
|
27067
|
+
const wrapperFile = join9(tmpDir, `${safeName}.ts`);
|
|
27068
|
+
writeFileSync7(wrapperFile, `export * from "${pkg.name}";
|
|
26968
27069
|
`);
|
|
26969
27070
|
entrypoints.push(wrapperFile);
|
|
26970
27071
|
}
|
|
@@ -26998,13 +27099,13 @@ async function buildModulesBundle(rootDir, outDir, packages, cache, noCache) {
|
|
|
26998
27099
|
console.error(log2);
|
|
26999
27100
|
throw new Error("Module build failed");
|
|
27000
27101
|
}
|
|
27001
|
-
await finalizeTranslations(rootDir,
|
|
27002
|
-
|
|
27102
|
+
await finalizeTranslations(rootDir, join9(outDir, ".."), i18nCollector);
|
|
27103
|
+
rmSync2(tmpDir, { recursive: true, force: true });
|
|
27003
27104
|
const outputHashes = {};
|
|
27004
27105
|
const modules = result.outputs.filter((o) => o.kind === "entry-point").map((o) => {
|
|
27005
27106
|
const file = basename2(o.path);
|
|
27006
27107
|
const safeName = file.replace(/\.js$/, "");
|
|
27007
|
-
const bytes =
|
|
27108
|
+
const bytes = readFileSync8(o.path);
|
|
27008
27109
|
const hash = sha256Hex(bytes);
|
|
27009
27110
|
outputHashes[safeName] = hash;
|
|
27010
27111
|
return {
|
|
@@ -27018,21 +27119,21 @@ async function buildModulesBundle(rootDir, outDir, packages, cache, noCache) {
|
|
|
27018
27119
|
}
|
|
27019
27120
|
}
|
|
27020
27121
|
async function buildTranslations(rootDir, arcDir, cache, noCache) {
|
|
27021
|
-
const localesDir =
|
|
27022
|
-
if (!
|
|
27122
|
+
const localesDir = join9(rootDir, "locales");
|
|
27123
|
+
if (!existsSync8(localesDir))
|
|
27023
27124
|
return;
|
|
27024
27125
|
const unitId = "translations";
|
|
27025
|
-
const poFiles =
|
|
27126
|
+
const poFiles = readdirSync5(localesDir).filter((f) => f.endsWith(".po")).map((f) => join9(localesDir, f));
|
|
27026
27127
|
if (poFiles.length === 0)
|
|
27027
27128
|
return;
|
|
27028
27129
|
const inputHash = sha256OfFiles(poFiles);
|
|
27029
|
-
if (!noCache && isCacheHit(cache, unitId, inputHash, [
|
|
27130
|
+
if (!noCache && isCacheHit(cache, unitId, inputHash, [join9(arcDir, "locales")])) {
|
|
27030
27131
|
console.log(` \u2713 cached: translations`);
|
|
27031
27132
|
return;
|
|
27032
27133
|
}
|
|
27033
27134
|
console.log(` building: translations (${poFiles.length} catalog(s))`);
|
|
27034
|
-
compileAllCatalogs(localesDir,
|
|
27035
|
-
const jsonFiles =
|
|
27135
|
+
compileAllCatalogs(localesDir, join9(arcDir, "locales"));
|
|
27136
|
+
const jsonFiles = readdirSync5(join9(arcDir, "locales")).filter((f) => f.endsWith(".json")).map((f) => join9(arcDir, "locales", f));
|
|
27036
27137
|
const outputHash = sha256OfFiles(jsonFiles);
|
|
27037
27138
|
updateCache(cache, unitId, inputHash, { outputHash });
|
|
27038
27139
|
}
|
|
@@ -27096,10 +27197,10 @@ var TAILWIND_INPUT_TEMPLATE = (rootRel) => `@import "tailwindcss";
|
|
|
27096
27197
|
}
|
|
27097
27198
|
`;
|
|
27098
27199
|
async function buildStyles(rootDir, arcDir, packages, themePath, cache, noCache) {
|
|
27099
|
-
|
|
27100
|
-
const inputCss =
|
|
27101
|
-
const outputCss =
|
|
27102
|
-
const themeOutput =
|
|
27200
|
+
mkdirSync7(arcDir, { recursive: true });
|
|
27201
|
+
const inputCss = join9(arcDir, "_input.css");
|
|
27202
|
+
const outputCss = join9(arcDir, "styles.css");
|
|
27203
|
+
const themeOutput = join9(arcDir, "theme.css");
|
|
27103
27204
|
const rootRel = relative3(arcDir, rootDir).replace(/\\/g, "/");
|
|
27104
27205
|
const inputCssContent = TAILWIND_INPUT_TEMPLATE(rootRel);
|
|
27105
27206
|
const tsxFilter = (rel) => {
|
|
@@ -27111,15 +27212,15 @@ async function buildStyles(rootDir, arcDir, packages, themePath, cache, noCache)
|
|
|
27111
27212
|
};
|
|
27112
27213
|
const wsHashes = {};
|
|
27113
27214
|
for (const p of packages) {
|
|
27114
|
-
wsHashes[p.name] = sha256OfDir(
|
|
27215
|
+
wsHashes[p.name] = sha256OfDir(join9(p.path, "src"), tsxFilter);
|
|
27115
27216
|
}
|
|
27116
|
-
const platformSrc =
|
|
27117
|
-
const arcDsSrc =
|
|
27217
|
+
const platformSrc = join9(rootDir, "node_modules", "@arcote.tech", "platform", "src");
|
|
27218
|
+
const arcDsSrc = join9(rootDir, "node_modules", "@arcote.tech", "arc-ds", "src");
|
|
27118
27219
|
const frameworkHashes = {
|
|
27119
27220
|
platform: sha256OfDir(platformSrc, tsxFilter),
|
|
27120
27221
|
arcDs: sha256OfDir(arcDsSrc, tsxFilter)
|
|
27121
27222
|
};
|
|
27122
|
-
const themeContent = themePath &&
|
|
27223
|
+
const themeContent = themePath && existsSync8(join9(rootDir, themePath)) ? readFileSync8(join9(rootDir, themePath)) : null;
|
|
27123
27224
|
const themeHash = themeContent ? sha256Hex(themeContent) : null;
|
|
27124
27225
|
const unitId = "styles";
|
|
27125
27226
|
const inputHash = sha256OfJson({
|
|
@@ -27136,21 +27237,198 @@ async function buildStyles(rootDir, arcDir, packages, themePath, cache, noCache)
|
|
|
27136
27237
|
return;
|
|
27137
27238
|
}
|
|
27138
27239
|
console.log(` building: styles`);
|
|
27139
|
-
|
|
27240
|
+
writeFileSync7(inputCss, inputCssContent);
|
|
27140
27241
|
execSync(`bunx @tailwindcss/cli -i ${inputCss} -o ${outputCss} --minify`, {
|
|
27141
27242
|
cwd: rootDir,
|
|
27142
27243
|
stdio: "inherit"
|
|
27143
27244
|
});
|
|
27144
27245
|
if (themePath && themeContent) {
|
|
27145
|
-
|
|
27246
|
+
writeFileSync7(themeOutput, themeContent);
|
|
27146
27247
|
}
|
|
27147
27248
|
const outFiles = [outputCss];
|
|
27148
|
-
if (themePath &&
|
|
27249
|
+
if (themePath && existsSync8(themeOutput))
|
|
27149
27250
|
outFiles.push(themeOutput);
|
|
27150
27251
|
const outputHash = sha256OfFiles(outFiles);
|
|
27151
27252
|
updateCache(cache, unitId, inputHash, { outputHash });
|
|
27152
27253
|
}
|
|
27153
27254
|
|
|
27255
|
+
// src/builder/dependency-collector.ts
|
|
27256
|
+
import { createHash } from "crypto";
|
|
27257
|
+
import { copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
27258
|
+
import { basename as basename3, join as join10 } from "path";
|
|
27259
|
+
var FRAMEWORK_PEERS = [
|
|
27260
|
+
"@arcote.tech/arc",
|
|
27261
|
+
"@arcote.tech/arc-ds",
|
|
27262
|
+
"@arcote.tech/arc-react",
|
|
27263
|
+
"@arcote.tech/platform",
|
|
27264
|
+
"react",
|
|
27265
|
+
"react-dom"
|
|
27266
|
+
];
|
|
27267
|
+
function collectFrameworkDeps(arcDir, rootDir, packages) {
|
|
27268
|
+
mkdirSync8(arcDir, { recursive: true });
|
|
27269
|
+
const versions = resolveFrameworkVersions(rootDir, packages);
|
|
27270
|
+
const manifest = {
|
|
27271
|
+
name: "arc-platform-framework",
|
|
27272
|
+
private: true,
|
|
27273
|
+
type: "module",
|
|
27274
|
+
dependencies: versions
|
|
27275
|
+
};
|
|
27276
|
+
const manifestPath = join10(arcDir, "package.json");
|
|
27277
|
+
writeFileSync8(manifestPath, JSON.stringify(manifest, null, 2) + `
|
|
27278
|
+
`);
|
|
27279
|
+
const rootLock = join10(rootDir, "bun.lock");
|
|
27280
|
+
const targetLock = join10(arcDir, "bun.lock");
|
|
27281
|
+
if (existsSync9(rootLock)) {
|
|
27282
|
+
copyFileSync(rootLock, targetLock);
|
|
27283
|
+
}
|
|
27284
|
+
const hash = sha256OfFiles2([manifestPath, targetLock]);
|
|
27285
|
+
writeFileSync8(join10(arcDir, ".deps-hash"), hash + `
|
|
27286
|
+
`);
|
|
27287
|
+
return { hash, manifestPath };
|
|
27288
|
+
}
|
|
27289
|
+
function collectModuleDeps(arcDir, pkg) {
|
|
27290
|
+
const safeName = basename3(pkg.path);
|
|
27291
|
+
const moduleDir = join10(arcDir, "modules", safeName);
|
|
27292
|
+
mkdirSync8(moduleDir, { recursive: true });
|
|
27293
|
+
const pkgDeps = pkg.packageJson.dependencies ?? {};
|
|
27294
|
+
const filtered = {};
|
|
27295
|
+
const frameworkSet = new Set(FRAMEWORK_PEERS);
|
|
27296
|
+
for (const [name, spec] of Object.entries(pkgDeps)) {
|
|
27297
|
+
if (frameworkSet.has(name))
|
|
27298
|
+
continue;
|
|
27299
|
+
if (spec.startsWith("workspace:"))
|
|
27300
|
+
continue;
|
|
27301
|
+
filtered[name] = spec;
|
|
27302
|
+
}
|
|
27303
|
+
const manifest = {
|
|
27304
|
+
name: `arc-module-${safeName}`,
|
|
27305
|
+
private: true,
|
|
27306
|
+
type: "module",
|
|
27307
|
+
dependencies: filtered
|
|
27308
|
+
};
|
|
27309
|
+
const manifestPath = join10(moduleDir, "package.json");
|
|
27310
|
+
writeFileSync8(manifestPath, JSON.stringify(manifest, null, 2) + `
|
|
27311
|
+
`);
|
|
27312
|
+
const hash = sha256OfFiles2([manifestPath]);
|
|
27313
|
+
writeFileSync8(join10(moduleDir, ".deps-hash"), hash + `
|
|
27314
|
+
`);
|
|
27315
|
+
return { hash, manifestPath };
|
|
27316
|
+
}
|
|
27317
|
+
function resolveFrameworkVersions(rootDir, packages) {
|
|
27318
|
+
const rootPkg = JSON.parse(readFileSync9(join10(rootDir, "package.json"), "utf-8"));
|
|
27319
|
+
const rootDeps = rootPkg.dependencies ?? {};
|
|
27320
|
+
const out = {};
|
|
27321
|
+
for (const name of FRAMEWORK_PEERS) {
|
|
27322
|
+
const rootSpec = rootDeps[name];
|
|
27323
|
+
if (rootSpec) {
|
|
27324
|
+
out[name] = rootSpec;
|
|
27325
|
+
continue;
|
|
27326
|
+
}
|
|
27327
|
+
let found;
|
|
27328
|
+
for (const pkg of packages) {
|
|
27329
|
+
const spec = (pkg.packageJson.dependencies ?? {})[name];
|
|
27330
|
+
if (spec) {
|
|
27331
|
+
found = spec;
|
|
27332
|
+
break;
|
|
27333
|
+
}
|
|
27334
|
+
}
|
|
27335
|
+
out[name] = found ?? "*";
|
|
27336
|
+
}
|
|
27337
|
+
return out;
|
|
27338
|
+
}
|
|
27339
|
+
function sha256OfFiles2(paths) {
|
|
27340
|
+
const hash = createHash("sha256");
|
|
27341
|
+
for (const p of paths.sort()) {
|
|
27342
|
+
if (!existsSync9(p))
|
|
27343
|
+
continue;
|
|
27344
|
+
hash.update(p);
|
|
27345
|
+
hash.update("\x00");
|
|
27346
|
+
hash.update(readFileSync9(p));
|
|
27347
|
+
hash.update("\x00");
|
|
27348
|
+
}
|
|
27349
|
+
return hash.digest("hex");
|
|
27350
|
+
}
|
|
27351
|
+
|
|
27352
|
+
// src/builder/access-extractor.ts
|
|
27353
|
+
var {spawn: spawn2 } = globalThis.Bun;
|
|
27354
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync10, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
27355
|
+
import { tmpdir } from "os";
|
|
27356
|
+
import { basename as basename4, join as join11 } from "path";
|
|
27357
|
+
async function extractAccessMap(arcDir, packages) {
|
|
27358
|
+
const serverBundles = packages.filter((p) => isContextPackage(p.packageJson)).map((p) => {
|
|
27359
|
+
const v06Path = join11(arcDir, "modules", basename4(p.path), "server.js");
|
|
27360
|
+
const legacyPath = join11(p.path, "dist", "server", "main", "index.js");
|
|
27361
|
+
return { name: p.name, path: v06Path, fallback: legacyPath };
|
|
27362
|
+
});
|
|
27363
|
+
const outPath = join11(arcDir, "access.json");
|
|
27364
|
+
mkdirSync9(arcDir, { recursive: true });
|
|
27365
|
+
const workerPath = join11(tmpdir(), `arc-access-extractor-${Date.now()}.mjs`);
|
|
27366
|
+
writeFileSync9(workerPath, WORKER_SOURCE);
|
|
27367
|
+
try {
|
|
27368
|
+
const proc2 = spawn2({
|
|
27369
|
+
cmd: ["bun", "run", workerPath],
|
|
27370
|
+
env: {
|
|
27371
|
+
...process.env,
|
|
27372
|
+
ARC_ACCESS_BUNDLES: JSON.stringify(serverBundles),
|
|
27373
|
+
ARC_ACCESS_OUT: outPath
|
|
27374
|
+
},
|
|
27375
|
+
stdout: "pipe",
|
|
27376
|
+
stderr: "inherit"
|
|
27377
|
+
});
|
|
27378
|
+
const exit = await proc2.exited;
|
|
27379
|
+
if (exit !== 0) {
|
|
27380
|
+
throw new Error(`access-extractor subprocess exited with ${exit}`);
|
|
27381
|
+
}
|
|
27382
|
+
return JSON.parse(readFileSync10(outPath, "utf-8"));
|
|
27383
|
+
} finally {
|
|
27384
|
+
try {
|
|
27385
|
+
unlinkSync2(workerPath);
|
|
27386
|
+
} catch {}
|
|
27387
|
+
}
|
|
27388
|
+
}
|
|
27389
|
+
var WORKER_SOURCE = `
|
|
27390
|
+
import { existsSync } from "node:fs";
|
|
27391
|
+
|
|
27392
|
+
globalThis.ONLY_SERVER = true;
|
|
27393
|
+
|
|
27394
|
+
const bundles = JSON.parse(process.env.ARC_ACCESS_BUNDLES || "[]");
|
|
27395
|
+
const out = process.env.ARC_ACCESS_OUT;
|
|
27396
|
+
if (!out) {
|
|
27397
|
+
console.error("[access-extractor-worker] ARC_ACCESS_OUT not set");
|
|
27398
|
+
process.exit(2);
|
|
27399
|
+
}
|
|
27400
|
+
|
|
27401
|
+
const platform = await import("@arcote.tech/platform");
|
|
27402
|
+
|
|
27403
|
+
for (const { name, path, fallback } of bundles) {
|
|
27404
|
+
const target = existsSync(path) ? path : fallback;
|
|
27405
|
+
if (!target || !existsSync(target)) {
|
|
27406
|
+
// No server bundle on either path \u2014 module has no protected access rules
|
|
27407
|
+
// to discover. Skip silently rather than logging a misleading error.
|
|
27408
|
+
continue;
|
|
27409
|
+
}
|
|
27410
|
+
try {
|
|
27411
|
+
await import(target);
|
|
27412
|
+
} catch (e) {
|
|
27413
|
+
console.error("[access-extractor-worker] failed to import", name, "from", target, e);
|
|
27414
|
+
// Continue \u2014 partial access map is better than total failure.
|
|
27415
|
+
}
|
|
27416
|
+
}
|
|
27417
|
+
|
|
27418
|
+
const result = {};
|
|
27419
|
+
for (const [name, access] of platform.getAllModuleAccess()) {
|
|
27420
|
+
result[name] = {
|
|
27421
|
+
rules: (access.rules ?? []).map((r) => ({
|
|
27422
|
+
token: { name: r.token?.name ?? "" },
|
|
27423
|
+
hasCheck: typeof r.check === "function",
|
|
27424
|
+
})),
|
|
27425
|
+
};
|
|
27426
|
+
}
|
|
27427
|
+
|
|
27428
|
+
const { writeFileSync } = await import("node:fs");
|
|
27429
|
+
writeFileSync(out, JSON.stringify(result, null, 2) + "\\n");
|
|
27430
|
+
`.trim();
|
|
27431
|
+
|
|
27154
27432
|
// src/platform/shared.ts
|
|
27155
27433
|
var C = {
|
|
27156
27434
|
reset: "\x1B[0m",
|
|
@@ -27168,22 +27446,22 @@ function resolveWorkspace() {
|
|
|
27168
27446
|
process.exit(1);
|
|
27169
27447
|
}
|
|
27170
27448
|
const rootDir = dirname6(packageJsonPath);
|
|
27171
|
-
const rootPkg = JSON.parse(
|
|
27449
|
+
const rootPkg = JSON.parse(readFileSync11(packageJsonPath, "utf-8"));
|
|
27172
27450
|
const appName = rootPkg.name ?? "Arc App";
|
|
27173
|
-
const arcDir =
|
|
27451
|
+
const arcDir = join12(rootDir, ".arc", "platform");
|
|
27174
27452
|
log2("Scanning workspaces...");
|
|
27175
27453
|
const packages = discoverPackages(rootDir);
|
|
27176
27454
|
ok(`Found ${packages.length} package(s): ${packages.map((p) => p.name).join(", ")}`);
|
|
27177
|
-
if (packages.length === 0) {
|
|
27455
|
+
if (packages.length === 0 && process.env.ARC_DEPLOY_API !== "1") {
|
|
27178
27456
|
err("No workspace packages found.");
|
|
27179
27457
|
process.exit(1);
|
|
27180
27458
|
}
|
|
27181
27459
|
let manifest;
|
|
27182
27460
|
for (const name of ["manifest.json", "manifest.webmanifest"]) {
|
|
27183
|
-
const manifestPath =
|
|
27184
|
-
if (
|
|
27461
|
+
const manifestPath = join12(rootDir, name);
|
|
27462
|
+
if (existsSync10(manifestPath)) {
|
|
27185
27463
|
try {
|
|
27186
|
-
const data = JSON.parse(
|
|
27464
|
+
const data = JSON.parse(readFileSync11(manifestPath, "utf-8"));
|
|
27187
27465
|
const icons = data.icons;
|
|
27188
27466
|
manifest = {
|
|
27189
27467
|
path: manifestPath,
|
|
@@ -27202,10 +27480,10 @@ function resolveWorkspace() {
|
|
|
27202
27480
|
rootPkg,
|
|
27203
27481
|
appName,
|
|
27204
27482
|
arcDir,
|
|
27205
|
-
modulesDir:
|
|
27206
|
-
shellDir:
|
|
27207
|
-
assetsDir:
|
|
27208
|
-
publicDir:
|
|
27483
|
+
modulesDir: join12(arcDir, "modules"),
|
|
27484
|
+
shellDir: join12(arcDir, "shell"),
|
|
27485
|
+
assetsDir: join12(arcDir, "assets"),
|
|
27486
|
+
publicDir: join12(rootDir, "public"),
|
|
27209
27487
|
packages,
|
|
27210
27488
|
manifest
|
|
27211
27489
|
};
|
|
@@ -27218,14 +27496,23 @@ async function buildAll(ws, opts = {}) {
|
|
|
27218
27496
|
const [, modulesResult] = await Promise.all([
|
|
27219
27497
|
buildContextPackages(ws.rootDir, ws.packages, cache, noCache),
|
|
27220
27498
|
buildModulesBundle(ws.rootDir, ws.modulesDir, ws.packages, cache, noCache),
|
|
27221
|
-
|
|
27499
|
+
buildShell2(ws, cache, noCache),
|
|
27222
27500
|
buildStyles(ws.rootDir, ws.arcDir, ws.packages, themePath, cache, noCache),
|
|
27223
27501
|
copyBrowserAssets(ws, cache, noCache),
|
|
27224
27502
|
buildTranslations(ws.rootDir, ws.arcDir, cache, noCache)
|
|
27225
27503
|
]);
|
|
27226
27504
|
saveBuildCache(ws.arcDir, cache);
|
|
27505
|
+
collectFrameworkDeps(ws.arcDir, ws.rootDir, ws.packages);
|
|
27506
|
+
for (const pkg of ws.packages) {
|
|
27507
|
+
collectModuleDeps(ws.arcDir, pkg);
|
|
27508
|
+
}
|
|
27509
|
+
try {
|
|
27510
|
+
await extractAccessMap(ws.arcDir, ws.packages);
|
|
27511
|
+
} catch (e) {
|
|
27512
|
+
err(`access-extractor failed: ${e.message}`);
|
|
27513
|
+
}
|
|
27227
27514
|
const finalManifest = assembleManifest(ws, modulesResult.modules, cache);
|
|
27228
|
-
|
|
27515
|
+
writeFileSync10(join12(ws.modulesDir, "manifest.json"), JSON.stringify(finalManifest, null, 2));
|
|
27229
27516
|
return finalManifest;
|
|
27230
27517
|
}
|
|
27231
27518
|
function assembleManifest(ws, modules, cache) {
|
|
@@ -27246,35 +27533,35 @@ function assembleManifest(ws, modules, cache) {
|
|
|
27246
27533
|
}
|
|
27247
27534
|
function resolveAssetSource(from, pkgDir, rootDir) {
|
|
27248
27535
|
if (from.startsWith("./") || from.startsWith("../")) {
|
|
27249
|
-
const resolved =
|
|
27250
|
-
return
|
|
27536
|
+
const resolved = join12(pkgDir, from);
|
|
27537
|
+
return existsSync10(resolved) ? resolved : null;
|
|
27251
27538
|
}
|
|
27252
27539
|
const candidates = [
|
|
27253
|
-
|
|
27254
|
-
|
|
27540
|
+
join12(rootDir, "node_modules", from),
|
|
27541
|
+
join12(pkgDir, "node_modules", from)
|
|
27255
27542
|
];
|
|
27256
27543
|
for (const c of candidates) {
|
|
27257
|
-
if (
|
|
27544
|
+
if (existsSync10(c))
|
|
27258
27545
|
return c;
|
|
27259
27546
|
}
|
|
27260
|
-
const bunCacheDir =
|
|
27261
|
-
if (
|
|
27262
|
-
for (const entry of
|
|
27547
|
+
const bunCacheDir = join12(rootDir, "node_modules", ".bun");
|
|
27548
|
+
if (existsSync10(bunCacheDir)) {
|
|
27549
|
+
for (const entry of readdirSync6(bunCacheDir, { withFileTypes: true })) {
|
|
27263
27550
|
if (!entry.isDirectory())
|
|
27264
27551
|
continue;
|
|
27265
|
-
const candidate =
|
|
27266
|
-
if (
|
|
27552
|
+
const candidate = join12(bunCacheDir, entry.name, "node_modules", from);
|
|
27553
|
+
if (existsSync10(candidate))
|
|
27267
27554
|
return candidate;
|
|
27268
27555
|
}
|
|
27269
27556
|
}
|
|
27270
27557
|
return null;
|
|
27271
27558
|
}
|
|
27272
27559
|
function readBrowserAssets(pkgDir) {
|
|
27273
|
-
const pkgJsonPath =
|
|
27274
|
-
if (!
|
|
27560
|
+
const pkgJsonPath = join12(pkgDir, "package.json");
|
|
27561
|
+
if (!existsSync10(pkgJsonPath))
|
|
27275
27562
|
return [];
|
|
27276
27563
|
try {
|
|
27277
|
-
const pkg = JSON.parse(
|
|
27564
|
+
const pkg = JSON.parse(readFileSync11(pkgJsonPath, "utf-8"));
|
|
27278
27565
|
const assets = pkg.arc?.browserAssets;
|
|
27279
27566
|
if (!Array.isArray(assets))
|
|
27280
27567
|
return [];
|
|
@@ -27284,14 +27571,14 @@ function readBrowserAssets(pkgDir) {
|
|
|
27284
27571
|
}
|
|
27285
27572
|
}
|
|
27286
27573
|
function discoverBrowserAssets(ws) {
|
|
27287
|
-
const arcDir =
|
|
27288
|
-
if (!
|
|
27574
|
+
const arcDir = join12(ws.rootDir, "node_modules", "@arcote.tech");
|
|
27575
|
+
if (!existsSync10(arcDir))
|
|
27289
27576
|
return [];
|
|
27290
27577
|
const out = [];
|
|
27291
|
-
for (const entry of
|
|
27578
|
+
for (const entry of readdirSync6(arcDir, { withFileTypes: true })) {
|
|
27292
27579
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
27293
27580
|
continue;
|
|
27294
|
-
const pkgDir =
|
|
27581
|
+
const pkgDir = join12(arcDir, entry.name);
|
|
27295
27582
|
const assets = readBrowserAssets(pkgDir);
|
|
27296
27583
|
for (const asset of assets) {
|
|
27297
27584
|
const src = resolveAssetSource(asset.from, pkgDir, ws.rootDir);
|
|
@@ -27305,7 +27592,7 @@ function discoverBrowserAssets(ws) {
|
|
|
27305
27592
|
return out;
|
|
27306
27593
|
}
|
|
27307
27594
|
async function copyBrowserAssets(ws, cache, noCache) {
|
|
27308
|
-
|
|
27595
|
+
mkdirSync10(ws.assetsDir, { recursive: true });
|
|
27309
27596
|
const assets = discoverBrowserAssets(ws);
|
|
27310
27597
|
if (assets.length === 0)
|
|
27311
27598
|
return;
|
|
@@ -27316,7 +27603,7 @@ async function copyBrowserAssets(ws, cache, noCache) {
|
|
|
27316
27603
|
to: a.to,
|
|
27317
27604
|
mtime: mtimeOf(a.src)
|
|
27318
27605
|
})));
|
|
27319
|
-
const requiredOutputs = assets.map((a) =>
|
|
27606
|
+
const requiredOutputs = assets.map((a) => join12(ws.assetsDir, a.to));
|
|
27320
27607
|
if (!noCache && isCacheHit(cache, unitId, inputHash, requiredOutputs)) {
|
|
27321
27608
|
console.log(` \u2713 cached: browser-assets (${assets.length})`);
|
|
27322
27609
|
return;
|
|
@@ -27324,10 +27611,10 @@ async function copyBrowserAssets(ws, cache, noCache) {
|
|
|
27324
27611
|
console.log(` building: browser-assets (${assets.length})`);
|
|
27325
27612
|
const outputHashes = {};
|
|
27326
27613
|
for (const asset of assets) {
|
|
27327
|
-
const dest =
|
|
27328
|
-
|
|
27329
|
-
|
|
27330
|
-
outputHashes[asset.to] = sha256Hex(
|
|
27614
|
+
const dest = join12(ws.assetsDir, asset.to);
|
|
27615
|
+
mkdirSync10(dirname6(dest), { recursive: true });
|
|
27616
|
+
copyFileSync2(asset.src, dest);
|
|
27617
|
+
outputHashes[asset.to] = sha256Hex(readFileSync11(dest));
|
|
27331
27618
|
}
|
|
27332
27619
|
updateCache(cache, unitId, inputHash, { outputHashes });
|
|
27333
27620
|
}
|
|
@@ -27348,7 +27635,7 @@ function collectArcPeerDeps(packages) {
|
|
|
27348
27635
|
return [short, pkg];
|
|
27349
27636
|
});
|
|
27350
27637
|
}
|
|
27351
|
-
var
|
|
27638
|
+
var REACT_ENTRIES2 = [
|
|
27352
27639
|
[
|
|
27353
27640
|
"react",
|
|
27354
27641
|
`import React from "react";
|
|
@@ -27378,8 +27665,8 @@ export const { createPortal, flushSync } = ReactDOM;`
|
|
|
27378
27665
|
`export { createRoot, hydrateRoot } from "react-dom/client";`
|
|
27379
27666
|
]
|
|
27380
27667
|
];
|
|
27381
|
-
var REACT_OUTPUT_FILES =
|
|
27382
|
-
var
|
|
27668
|
+
var REACT_OUTPUT_FILES = REACT_ENTRIES2.map(([n]) => `${n}.js`);
|
|
27669
|
+
var SHELL_BASE_EXTERNAL2 = [
|
|
27383
27670
|
"react",
|
|
27384
27671
|
"react-dom",
|
|
27385
27672
|
"react/jsx-runtime",
|
|
@@ -27396,27 +27683,27 @@ var sourceFilter2 = (rel) => {
|
|
|
27396
27683
|
return true;
|
|
27397
27684
|
};
|
|
27398
27685
|
function arcPkgSrcHash(rootDir, pkg) {
|
|
27399
|
-
const srcDir =
|
|
27400
|
-
if (
|
|
27686
|
+
const srcDir = join12(rootDir, "node_modules", pkg, "src");
|
|
27687
|
+
if (existsSync10(srcDir))
|
|
27401
27688
|
return sha256OfDir(srcDir, sourceFilter2);
|
|
27402
|
-
return sha256OfDir(
|
|
27689
|
+
return sha256OfDir(join12(rootDir, "node_modules", pkg), sourceFilter2);
|
|
27403
27690
|
}
|
|
27404
27691
|
async function buildShellReact(shellDir, tmpDir, rootDir, cache, noCache) {
|
|
27405
27692
|
const unitId = "shell:react";
|
|
27406
27693
|
const inputHash = sha256OfJson({
|
|
27407
27694
|
react: readInstalledVersion(rootDir, "react"),
|
|
27408
27695
|
"react-dom": readInstalledVersion(rootDir, "react-dom"),
|
|
27409
|
-
entries:
|
|
27696
|
+
entries: REACT_ENTRIES2.map(([k, v]) => [k, v])
|
|
27410
27697
|
});
|
|
27411
|
-
const requiredOutputs = REACT_OUTPUT_FILES.map((f) =>
|
|
27698
|
+
const requiredOutputs = REACT_OUTPUT_FILES.map((f) => join12(shellDir, f));
|
|
27412
27699
|
if (!noCache && isCacheHit(cache, unitId, inputHash, requiredOutputs)) {
|
|
27413
27700
|
console.log(` \u2713 cached: shell:react`);
|
|
27414
27701
|
return;
|
|
27415
27702
|
}
|
|
27416
27703
|
console.log(` building: shell:react`);
|
|
27417
27704
|
const reactEps = [];
|
|
27418
|
-
for (const [name, code] of
|
|
27419
|
-
const f =
|
|
27705
|
+
for (const [name, code] of REACT_ENTRIES2) {
|
|
27706
|
+
const f = join12(tmpDir, `${name}.ts`);
|
|
27420
27707
|
await Bun.write(f, code);
|
|
27421
27708
|
reactEps.push(f);
|
|
27422
27709
|
}
|
|
@@ -27443,16 +27730,16 @@ async function buildShellArcEntry(shortName, pkg, allArcPkgs, shellDir, tmpDir,
|
|
|
27443
27730
|
pkg,
|
|
27444
27731
|
version: readInstalledVersion(rootDir, pkg),
|
|
27445
27732
|
src: arcPkgSrcHash(rootDir, pkg),
|
|
27446
|
-
base:
|
|
27733
|
+
base: SHELL_BASE_EXTERNAL2,
|
|
27447
27734
|
others: [...otherExternals].sort()
|
|
27448
27735
|
});
|
|
27449
|
-
const outputFile =
|
|
27736
|
+
const outputFile = join12(shellDir, `${shortName}.js`);
|
|
27450
27737
|
if (!noCache && isCacheHit(cache, unitId, inputHash, [outputFile])) {
|
|
27451
27738
|
console.log(` \u2713 cached: ${unitId}`);
|
|
27452
27739
|
return;
|
|
27453
27740
|
}
|
|
27454
27741
|
console.log(` building: ${unitId}`);
|
|
27455
|
-
const f =
|
|
27742
|
+
const f = join12(tmpDir, `${shortName}.ts`);
|
|
27456
27743
|
await Bun.write(f, `export * from "${pkg}";
|
|
27457
27744
|
`);
|
|
27458
27745
|
const r = await Bun.build({
|
|
@@ -27461,7 +27748,7 @@ async function buildShellArcEntry(shortName, pkg, allArcPkgs, shellDir, tmpDir,
|
|
|
27461
27748
|
format: "esm",
|
|
27462
27749
|
target: "browser",
|
|
27463
27750
|
naming: "[name].[ext]",
|
|
27464
|
-
external: [...
|
|
27751
|
+
external: [...SHELL_BASE_EXTERNAL2, ...otherExternals],
|
|
27465
27752
|
define: {
|
|
27466
27753
|
ONLY_SERVER: "false",
|
|
27467
27754
|
ONLY_BROWSER: "true",
|
|
@@ -27476,10 +27763,10 @@ async function buildShellArcEntry(shortName, pkg, allArcPkgs, shellDir, tmpDir,
|
|
|
27476
27763
|
const outputHash = sha256OfFiles([outputFile]);
|
|
27477
27764
|
updateCache(cache, unitId, inputHash, { outputHash });
|
|
27478
27765
|
}
|
|
27479
|
-
async function
|
|
27480
|
-
|
|
27481
|
-
const tmpDir =
|
|
27482
|
-
|
|
27766
|
+
async function buildShell2(ws, cache, noCache) {
|
|
27767
|
+
mkdirSync10(ws.shellDir, { recursive: true });
|
|
27768
|
+
const tmpDir = join12(ws.shellDir, "_tmp");
|
|
27769
|
+
mkdirSync10(tmpDir, { recursive: true });
|
|
27483
27770
|
const arcEntries = collectArcPeerDeps(ws.packages);
|
|
27484
27771
|
const allArcPkgs = arcEntries.map(([, pkg]) => pkg);
|
|
27485
27772
|
const tasks = [
|
|
@@ -27487,7 +27774,7 @@ async function buildShell(ws, cache, noCache) {
|
|
|
27487
27774
|
...arcEntries.map(([short, pkg]) => () => buildShellArcEntry(short, pkg, allArcPkgs, ws.shellDir, tmpDir, ws.rootDir, cache, noCache))
|
|
27488
27775
|
];
|
|
27489
27776
|
await pAll(tasks);
|
|
27490
|
-
|
|
27777
|
+
rmSync3(tmpDir, { recursive: true, force: true });
|
|
27491
27778
|
}
|
|
27492
27779
|
async function loadServerContext(packages) {
|
|
27493
27780
|
const ctxPackages = packages.filter((p) => isContextPackage(p.packageJson));
|
|
@@ -27496,13 +27783,13 @@ async function loadServerContext(packages) {
|
|
|
27496
27783
|
globalThis.ONLY_SERVER = true;
|
|
27497
27784
|
globalThis.ONLY_BROWSER = false;
|
|
27498
27785
|
globalThis.ONLY_CLIENT = false;
|
|
27499
|
-
const platformDir =
|
|
27500
|
-
const platformPkg = JSON.parse(
|
|
27501
|
-
const platformEntry =
|
|
27786
|
+
const platformDir = join12(process.cwd(), "node_modules", "@arcote.tech", "platform");
|
|
27787
|
+
const platformPkg = JSON.parse(readFileSync11(join12(platformDir, "package.json"), "utf-8"));
|
|
27788
|
+
const platformEntry = join12(platformDir, platformPkg.main ?? "src/index.ts");
|
|
27502
27789
|
await import(platformEntry);
|
|
27503
27790
|
for (const ctx of ctxPackages) {
|
|
27504
|
-
const serverDist =
|
|
27505
|
-
if (!
|
|
27791
|
+
const serverDist = join12(ctx.path, "dist", "server", "main", "index.js");
|
|
27792
|
+
if (!existsSync10(serverDist)) {
|
|
27506
27793
|
err(`Context server dist not found: ${serverDist}`);
|
|
27507
27794
|
continue;
|
|
27508
27795
|
}
|
|
@@ -27533,19 +27820,19 @@ async function platformBuild(opts = {}) {
|
|
|
27533
27820
|
}
|
|
27534
27821
|
|
|
27535
27822
|
// src/commands/platform-deploy.ts
|
|
27536
|
-
import { existsSync as
|
|
27537
|
-
import { join as
|
|
27823
|
+
import { existsSync as existsSync14, readFileSync as readFileSync14 } from "fs";
|
|
27824
|
+
import { dirname as dirname7, join as join18 } from "path";
|
|
27538
27825
|
|
|
27539
27826
|
// src/deploy/bootstrap.ts
|
|
27540
|
-
import { mkdirSync as
|
|
27827
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync14 } from "fs";
|
|
27541
27828
|
import { tmpdir as tmpdir3 } from "os";
|
|
27542
|
-
import { join as
|
|
27829
|
+
import { join as join16 } from "path";
|
|
27543
27830
|
|
|
27544
27831
|
// src/deploy/ansible.ts
|
|
27545
|
-
|
|
27546
|
-
import { mkdirSync as
|
|
27547
|
-
import { tmpdir } from "os";
|
|
27548
|
-
import { join as
|
|
27832
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
27833
|
+
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
27834
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
27835
|
+
import { join as join13 } from "path";
|
|
27549
27836
|
|
|
27550
27837
|
// src/deploy/assets.ts
|
|
27551
27838
|
var TERRAFORM_MAIN_TF = `terraform {
|
|
@@ -27802,18 +28089,18 @@ var ASSETS = {
|
|
|
27802
28089
|
}
|
|
27803
28090
|
};
|
|
27804
28091
|
async function materializeAssets(targetDir, files) {
|
|
27805
|
-
const { mkdirSync:
|
|
27806
|
-
const { join:
|
|
27807
|
-
|
|
28092
|
+
const { mkdirSync: mkdirSync11, writeFileSync: writeFileSync11 } = await import("fs");
|
|
28093
|
+
const { join: join13 } = await import("path");
|
|
28094
|
+
mkdirSync11(targetDir, { recursive: true });
|
|
27808
28095
|
for (const [name, content] of Object.entries(files)) {
|
|
27809
|
-
|
|
28096
|
+
writeFileSync11(join13(targetDir, name), content);
|
|
27810
28097
|
}
|
|
27811
28098
|
}
|
|
27812
28099
|
|
|
27813
28100
|
// src/deploy/ansible.ts
|
|
27814
28101
|
async function runAnsible(inputs) {
|
|
27815
|
-
const workDir =
|
|
27816
|
-
|
|
28102
|
+
const workDir = join13(tmpdir2(), "arc-deploy", `ansible-${Date.now()}`);
|
|
28103
|
+
mkdirSync11(workDir, { recursive: true });
|
|
27817
28104
|
await materializeAssets(workDir, ASSETS.ansible);
|
|
27818
28105
|
const user = inputs.asRoot ? "root" : inputs.target.user;
|
|
27819
28106
|
const port = inputs.ansible?.sshPort ?? inputs.target.port;
|
|
@@ -27827,29 +28114,23 @@ async function runAnsible(inputs) {
|
|
|
27827
28114
|
""
|
|
27828
28115
|
].join(`
|
|
27829
28116
|
`);
|
|
27830
|
-
|
|
27831
|
-
const
|
|
27832
|
-
|
|
27833
|
-
|
|
27834
|
-
|
|
27835
|
-
|
|
27836
|
-
|
|
27837
|
-
|
|
27838
|
-
|
|
27839
|
-
|
|
27840
|
-
"
|
|
27841
|
-
|
|
27842
|
-
|
|
27843
|
-
|
|
27844
|
-
|
|
27845
|
-
|
|
27846
|
-
],
|
|
27847
|
-
cwd: workDir,
|
|
27848
|
-
stdout: "inherit",
|
|
27849
|
-
stderr: "inherit",
|
|
27850
|
-
env: { ...process.env, ANSIBLE_HOST_KEY_CHECKING: "False" }
|
|
28117
|
+
writeFileSync11(join13(workDir, "inventory.ini"), inventory);
|
|
28118
|
+
const extraVarsJson = JSON.stringify({
|
|
28119
|
+
username: inputs.target.user,
|
|
28120
|
+
ssh_port: port,
|
|
28121
|
+
extra_allowed_ips: inputs.ansible?.extraAllowedIps ?? []
|
|
28122
|
+
});
|
|
28123
|
+
const exit = await new Promise((resolve, reject) => {
|
|
28124
|
+
const proc2 = nodeSpawn("ansible-playbook", ["-i", "inventory.ini", "site.yml", "-e", extraVarsJson], {
|
|
28125
|
+
cwd: workDir,
|
|
28126
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
28127
|
+
env: { ...process.env, ANSIBLE_HOST_KEY_CHECKING: "False" }
|
|
28128
|
+
});
|
|
28129
|
+
proc2.stdout?.on("data", (chunk) => process.stdout.write(chunk));
|
|
28130
|
+
proc2.stderr?.on("data", (chunk) => process.stderr.write(chunk));
|
|
28131
|
+
proc2.on("error", reject);
|
|
28132
|
+
proc2.on("exit", (code) => resolve(code ?? 1));
|
|
27851
28133
|
});
|
|
27852
|
-
const exit = await proc2.exited;
|
|
27853
28134
|
if (exit !== 0) {
|
|
27854
28135
|
throw new Error(`ansible-playbook failed (exit ${exit})`);
|
|
27855
28136
|
}
|
|
@@ -27895,7 +28176,8 @@ function generateCaddyfile(cfg) {
|
|
|
27895
28176
|
}
|
|
27896
28177
|
|
|
27897
28178
|
// src/deploy/compose.ts
|
|
27898
|
-
|
|
28179
|
+
var RESERVED_ENV = new Set(["PORT", "ARC_DEPLOY_API", "ARC_CLI_VERSION"]);
|
|
28180
|
+
function generateCompose({ cfg, cliVersion }) {
|
|
27899
28181
|
const lines = [];
|
|
27900
28182
|
lines.push("# Generated by `arc platform deploy` \u2014 do not edit by hand.");
|
|
27901
28183
|
lines.push("");
|
|
@@ -27916,20 +28198,26 @@ function generateCompose({ cfg }) {
|
|
|
27916
28198
|
lines.push("");
|
|
27917
28199
|
for (const [name, env2] of Object.entries(cfg.envs)) {
|
|
27918
28200
|
lines.push(` arc-${name}:`);
|
|
27919
|
-
lines.push(" image:
|
|
28201
|
+
lines.push(" image: pkrasinski/arc-runtime:1");
|
|
27920
28202
|
lines.push(" restart: unless-stopped");
|
|
27921
|
-
lines.push(` working_dir: /app`);
|
|
27922
28203
|
lines.push(" volumes:");
|
|
27923
|
-
lines.push(` -
|
|
28204
|
+
lines.push(` - arc-platform-${name}:/app/.arc/platform`);
|
|
27924
28205
|
lines.push(` - arc-data-${name}:/app/.arc/data`);
|
|
28206
|
+
lines.push(" - arc-cli-cache:/app/.arc/cli");
|
|
28207
|
+
lines.push(" - arc-bun-cache:/root/.bun/install/cache");
|
|
27925
28208
|
lines.push(" environment:");
|
|
27926
28209
|
lines.push(" PORT: 5005");
|
|
27927
28210
|
lines.push(' ARC_DEPLOY_API: "1"');
|
|
27928
|
-
lines.push(
|
|
27929
|
-
|
|
28211
|
+
lines.push(` ARC_CLI_VERSION: ${JSON.stringify(cliVersion)}`);
|
|
28212
|
+
const userEnv = env2.envVars ?? {};
|
|
28213
|
+
if (!("NODE_ENV" in userEnv)) {
|
|
28214
|
+
lines.push(" NODE_ENV: production");
|
|
28215
|
+
}
|
|
28216
|
+
for (const [k, v] of Object.entries(userEnv)) {
|
|
28217
|
+
if (RESERVED_ENV.has(k))
|
|
28218
|
+
continue;
|
|
27930
28219
|
lines.push(` ${k}: ${JSON.stringify(v)}`);
|
|
27931
28220
|
}
|
|
27932
|
-
lines.push(' command: ["node_modules/.bin/arc", "platform", "start"]');
|
|
27933
28221
|
lines.push(" networks:");
|
|
27934
28222
|
lines.push(" - arc-net");
|
|
27935
28223
|
lines.push(" expose:");
|
|
@@ -27942,7 +28230,10 @@ function generateCompose({ cfg }) {
|
|
|
27942
28230
|
lines.push("volumes:");
|
|
27943
28231
|
lines.push(" caddy_data:");
|
|
27944
28232
|
lines.push(" caddy_config:");
|
|
28233
|
+
lines.push(" arc-cli-cache:");
|
|
28234
|
+
lines.push(" arc-bun-cache:");
|
|
27945
28235
|
for (const [name] of Object.entries(cfg.envs)) {
|
|
28236
|
+
lines.push(` arc-platform-${name}:`);
|
|
27946
28237
|
lines.push(` arc-data-${name}:`);
|
|
27947
28238
|
}
|
|
27948
28239
|
return lines.join(`
|
|
@@ -27951,16 +28242,18 @@ function generateCompose({ cfg }) {
|
|
|
27951
28242
|
}
|
|
27952
28243
|
|
|
27953
28244
|
// src/deploy/terraform.ts
|
|
27954
|
-
|
|
27955
|
-
import {
|
|
27956
|
-
import {
|
|
27957
|
-
import {
|
|
28245
|
+
import { spawn as nodeSpawn2 } from "child_process";
|
|
28246
|
+
import { createHash as createHash2 } from "crypto";
|
|
28247
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "fs";
|
|
28248
|
+
import { homedir } from "os";
|
|
28249
|
+
import { join as join14 } from "path";
|
|
27958
28250
|
async function runTerraform(inputs) {
|
|
27959
|
-
const
|
|
27960
|
-
|
|
28251
|
+
const wsHash = createHash2("sha256").update(inputs.workspaceDir).digest("hex").slice(0, 16);
|
|
28252
|
+
const workDir = join14(homedir(), ".arc-deploy", wsHash, "tf");
|
|
28253
|
+
mkdirSync12(workDir, { recursive: true });
|
|
27961
28254
|
await materializeAssets(workDir, ASSETS.terraform);
|
|
27962
28255
|
const sshPubKey = inputs.tf.sshPublicKey ?? expandHome("~/.ssh/id_ed25519.pub");
|
|
27963
|
-
if (!
|
|
28256
|
+
if (!existsSync11(expandHome(sshPubKey))) {
|
|
27964
28257
|
throw new Error(`SSH public key not found at ${sshPubKey}. Set provision.terraform.sshPublicKey in deploy.arc.json.`);
|
|
27965
28258
|
}
|
|
27966
28259
|
const tfvars = [
|
|
@@ -27973,7 +28266,7 @@ async function runTerraform(inputs) {
|
|
|
27973
28266
|
].join(`
|
|
27974
28267
|
`) + `
|
|
27975
28268
|
`;
|
|
27976
|
-
|
|
28269
|
+
writeFileSync12(join14(workDir, "terraform.tfvars"), tfvars);
|
|
27977
28270
|
await runTf(workDir, ["init", "-input=false", "-no-color"]);
|
|
27978
28271
|
await runTf(workDir, [
|
|
27979
28272
|
"apply",
|
|
@@ -27990,28 +28283,34 @@ async function runTerraform(inputs) {
|
|
|
27990
28283
|
return { serverIp: ip.trim(), serverName: inputs.serverName, workDir };
|
|
27991
28284
|
}
|
|
27992
28285
|
async function runTf(workDir, args) {
|
|
27993
|
-
const
|
|
27994
|
-
|
|
27995
|
-
|
|
27996
|
-
|
|
27997
|
-
|
|
28286
|
+
const exit = await new Promise((resolve, reject) => {
|
|
28287
|
+
const proc2 = nodeSpawn2("terraform", args, {
|
|
28288
|
+
cwd: workDir,
|
|
28289
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
28290
|
+
});
|
|
28291
|
+
proc2.stdout?.on("data", (chunk) => process.stdout.write(chunk));
|
|
28292
|
+
proc2.stderr?.on("data", (chunk) => process.stderr.write(chunk));
|
|
28293
|
+
proc2.on("error", reject);
|
|
28294
|
+
proc2.on("exit", (code) => resolve(code ?? 1));
|
|
27998
28295
|
});
|
|
27999
|
-
const exit = await proc2.exited;
|
|
28000
28296
|
if (exit !== 0) {
|
|
28001
28297
|
throw new Error(`terraform ${args[0]} failed (exit ${exit})`);
|
|
28002
28298
|
}
|
|
28003
28299
|
}
|
|
28004
28300
|
async function runTfCapture(workDir, args) {
|
|
28005
|
-
|
|
28006
|
-
|
|
28007
|
-
|
|
28008
|
-
|
|
28009
|
-
|
|
28301
|
+
let stdout = "";
|
|
28302
|
+
const exit = await new Promise((resolve, reject) => {
|
|
28303
|
+
const proc2 = nodeSpawn2("terraform", args, {
|
|
28304
|
+
cwd: workDir,
|
|
28305
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
28306
|
+
});
|
|
28307
|
+
proc2.stdout?.on("data", (chunk) => {
|
|
28308
|
+
stdout += chunk.toString("utf-8");
|
|
28309
|
+
});
|
|
28310
|
+
proc2.stderr?.on("data", (chunk) => process.stderr.write(chunk));
|
|
28311
|
+
proc2.on("error", reject);
|
|
28312
|
+
proc2.on("exit", (code) => resolve(code ?? 1));
|
|
28010
28313
|
});
|
|
28011
|
-
const [stdout, exit] = await Promise.all([
|
|
28012
|
-
new Response(proc2.stdout).text(),
|
|
28013
|
-
proc2.exited
|
|
28014
|
-
]);
|
|
28015
28314
|
if (exit !== 0) {
|
|
28016
28315
|
throw new Error(`terraform ${args[0]} failed (exit ${exit})`);
|
|
28017
28316
|
}
|
|
@@ -28025,21 +28324,21 @@ function expandHome(p) {
|
|
|
28025
28324
|
}
|
|
28026
28325
|
|
|
28027
28326
|
// src/deploy/config.ts
|
|
28028
|
-
import { existsSync as
|
|
28029
|
-
import { join as
|
|
28327
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync13 } from "fs";
|
|
28328
|
+
import { join as join15 } from "path";
|
|
28030
28329
|
var DEPLOY_CONFIG_FILE = "deploy.arc.json";
|
|
28031
28330
|
function deployConfigPath(rootDir) {
|
|
28032
|
-
return
|
|
28331
|
+
return join15(rootDir, DEPLOY_CONFIG_FILE);
|
|
28033
28332
|
}
|
|
28034
28333
|
function deployConfigExists(rootDir) {
|
|
28035
|
-
return
|
|
28334
|
+
return existsSync12(deployConfigPath(rootDir));
|
|
28036
28335
|
}
|
|
28037
28336
|
function loadDeployConfig(rootDir) {
|
|
28038
28337
|
const path4 = deployConfigPath(rootDir);
|
|
28039
|
-
if (!
|
|
28338
|
+
if (!existsSync12(path4)) {
|
|
28040
28339
|
throw new Error(`Missing ${DEPLOY_CONFIG_FILE} at ${path4}`);
|
|
28041
28340
|
}
|
|
28042
|
-
const raw =
|
|
28341
|
+
const raw = readFileSync12(path4, "utf-8");
|
|
28043
28342
|
let parsed;
|
|
28044
28343
|
try {
|
|
28045
28344
|
parsed = JSON.parse(raw);
|
|
@@ -28050,7 +28349,10 @@ function loadDeployConfig(rootDir) {
|
|
|
28050
28349
|
return validateDeployConfig(expanded);
|
|
28051
28350
|
}
|
|
28052
28351
|
function saveDeployConfig(rootDir, cfg) {
|
|
28053
|
-
|
|
28352
|
+
const path4 = deployConfigPath(rootDir);
|
|
28353
|
+
const raw = existsSync12(path4) ? JSON.parse(readFileSync12(path4, "utf-8")) : {};
|
|
28354
|
+
raw.target = { ...raw.target, ...cfg.target };
|
|
28355
|
+
writeFileSync13(path4, JSON.stringify(raw, null, 2) + `
|
|
28054
28356
|
`);
|
|
28055
28357
|
}
|
|
28056
28358
|
var VAR_REGEX = /\$\{([A-Z0-9_]+)\}|\$([A-Z0-9_]+)/g;
|
|
@@ -28201,24 +28503,26 @@ function cfgErr(path4, expected) {
|
|
|
28201
28503
|
}
|
|
28202
28504
|
|
|
28203
28505
|
// src/deploy/ssh.ts
|
|
28204
|
-
var {spawn:
|
|
28506
|
+
var {spawn: spawn3 } = globalThis.Bun;
|
|
28205
28507
|
async function streamToString(stream2) {
|
|
28206
28508
|
if (!stream2 || typeof stream2 === "number")
|
|
28207
28509
|
return "";
|
|
28208
28510
|
return new Response(stream2).text();
|
|
28209
28511
|
}
|
|
28210
28512
|
function baseSshArgs(target) {
|
|
28211
|
-
const
|
|
28513
|
+
const key = target.sshKey ?? `${process.env.HOME}/.ssh/id_ed25519`;
|
|
28514
|
+
return [
|
|
28212
28515
|
"-o",
|
|
28213
28516
|
"BatchMode=yes",
|
|
28214
28517
|
"-o",
|
|
28215
28518
|
"StrictHostKeyChecking=accept-new",
|
|
28519
|
+
"-o",
|
|
28520
|
+
"IdentitiesOnly=yes",
|
|
28521
|
+
"-i",
|
|
28522
|
+
key,
|
|
28216
28523
|
"-p",
|
|
28217
28524
|
String(target.port)
|
|
28218
28525
|
];
|
|
28219
|
-
if (target.sshKey)
|
|
28220
|
-
args.push("-i", target.sshKey);
|
|
28221
|
-
return args;
|
|
28222
28526
|
}
|
|
28223
28527
|
async function sshExec(target, cmd, opts = {}) {
|
|
28224
28528
|
const args = [
|
|
@@ -28227,7 +28531,7 @@ async function sshExec(target, cmd, opts = {}) {
|
|
|
28227
28531
|
"--",
|
|
28228
28532
|
cmd
|
|
28229
28533
|
];
|
|
28230
|
-
const proc2 =
|
|
28534
|
+
const proc2 = spawn3({
|
|
28231
28535
|
cmd: ["ssh", ...args],
|
|
28232
28536
|
stdin: opts.stdin ? "pipe" : "ignore",
|
|
28233
28537
|
stdout: "pipe",
|
|
@@ -28272,18 +28576,21 @@ async function waitForSsh(target, opts = {}) {
|
|
|
28272
28576
|
throw new Error(`Timed out waiting for SSH on ${target.user}@${target.host}`);
|
|
28273
28577
|
}
|
|
28274
28578
|
async function scpUpload(target, localPath, remotePath) {
|
|
28579
|
+
const key = target.sshKey ?? `${process.env.HOME}/.ssh/id_ed25519`;
|
|
28275
28580
|
const args = [
|
|
28276
28581
|
"-o",
|
|
28277
28582
|
"BatchMode=yes",
|
|
28278
28583
|
"-o",
|
|
28279
28584
|
"StrictHostKeyChecking=accept-new",
|
|
28585
|
+
"-o",
|
|
28586
|
+
"IdentitiesOnly=yes",
|
|
28587
|
+
"-i",
|
|
28588
|
+
key,
|
|
28280
28589
|
"-P",
|
|
28281
28590
|
String(target.port)
|
|
28282
28591
|
];
|
|
28283
|
-
if (target.sshKey)
|
|
28284
|
-
args.push("-i", target.sshKey);
|
|
28285
28592
|
args.push(localPath, `${target.user}@${target.host}:${remotePath}`);
|
|
28286
|
-
const proc2 =
|
|
28593
|
+
const proc2 = spawn3({ cmd: ["scp", ...args], stderr: "pipe" });
|
|
28287
28594
|
const [stderr, exitCode] = await Promise.all([
|
|
28288
28595
|
streamToString(proc2.stderr),
|
|
28289
28596
|
proc2.exited
|
|
@@ -28292,29 +28599,6 @@ async function scpUpload(target, localPath, remotePath) {
|
|
|
28292
28599
|
throw new Error(`scp failed (${exitCode}): ${stderr}`);
|
|
28293
28600
|
}
|
|
28294
28601
|
}
|
|
28295
|
-
async function rsyncDir(target, localDir, remoteDir, opts = {}) {
|
|
28296
|
-
const sshCmdParts = ["ssh", "-p", String(target.port)];
|
|
28297
|
-
if (target.sshKey)
|
|
28298
|
-
sshCmdParts.push("-i", target.sshKey);
|
|
28299
|
-
const sshCmd = sshCmdParts.join(" ");
|
|
28300
|
-
const args = ["-azL", "-e", sshCmd];
|
|
28301
|
-
if (opts.delete)
|
|
28302
|
-
args.push("--delete");
|
|
28303
|
-
const src = localDir.endsWith("/") ? localDir : `${localDir}/`;
|
|
28304
|
-
args.push(src, `${target.user}@${target.host}:${remoteDir}`);
|
|
28305
|
-
const proc2 = spawn4({
|
|
28306
|
-
cmd: ["rsync", ...args],
|
|
28307
|
-
stderr: "pipe",
|
|
28308
|
-
stdout: "pipe"
|
|
28309
|
-
});
|
|
28310
|
-
const [stderr, exitCode] = await Promise.all([
|
|
28311
|
-
streamToString(proc2.stderr),
|
|
28312
|
-
proc2.exited
|
|
28313
|
-
]);
|
|
28314
|
-
if (exitCode !== 0) {
|
|
28315
|
-
throw new Error(`rsync failed (${exitCode}): ${stderr}`);
|
|
28316
|
-
}
|
|
28317
|
-
}
|
|
28318
28602
|
async function openTunnel(target, localPort, remoteHost, remotePort) {
|
|
28319
28603
|
const args = [
|
|
28320
28604
|
...baseSshArgs(target),
|
|
@@ -28323,7 +28607,7 @@ async function openTunnel(target, localPort, remoteHost, remotePort) {
|
|
|
28323
28607
|
`${localPort}:${remoteHost}:${remotePort}`,
|
|
28324
28608
|
`${target.user}@${target.host}`
|
|
28325
28609
|
];
|
|
28326
|
-
const proc2 =
|
|
28610
|
+
const proc2 = spawn3({
|
|
28327
28611
|
cmd: ["ssh", ...args],
|
|
28328
28612
|
stdin: "ignore",
|
|
28329
28613
|
stdout: "pipe",
|
|
@@ -28369,6 +28653,9 @@ async function detectRemoteState(cfg) {
|
|
|
28369
28653
|
return { kind: "unreachable", reason: "target.host not yet set" };
|
|
28370
28654
|
}
|
|
28371
28655
|
if (!await canSsh(cfg.target)) {
|
|
28656
|
+
if (await canSsh({ ...cfg.target, user: "root" })) {
|
|
28657
|
+
return { kind: "no-docker" };
|
|
28658
|
+
}
|
|
28372
28659
|
return { kind: "unreachable", reason: "ssh connection failed" };
|
|
28373
28660
|
}
|
|
28374
28661
|
const dockerCheck = await sshExec(cfg.target, "command -v docker", {
|
|
@@ -28419,7 +28706,8 @@ async function bootstrap(inputs) {
|
|
|
28419
28706
|
const tfOut = await runTerraform({
|
|
28420
28707
|
tf: cfg.provision.terraform,
|
|
28421
28708
|
token,
|
|
28422
|
-
serverName: `arc-${Object.keys(cfg.envs)[0] ?? "host"}
|
|
28709
|
+
serverName: `arc-${Object.keys(cfg.envs)[0] ?? "host"}`,
|
|
28710
|
+
workspaceDir: rootDir
|
|
28423
28711
|
});
|
|
28424
28712
|
ok(`Server provisioned: ${tfOut.serverIp}`);
|
|
28425
28713
|
cfg.target.host = tfOut.serverIp;
|
|
@@ -28430,7 +28718,8 @@ async function bootstrap(inputs) {
|
|
|
28430
28718
|
}
|
|
28431
28719
|
if (state.kind === "unreachable" || state.kind === "no-docker") {
|
|
28432
28720
|
log2("Running Ansible bootstrap (Docker + firewall + SSH hardening)...");
|
|
28433
|
-
const
|
|
28721
|
+
const deployUserWorks = state.kind === "no-docker" && await canSsh(cfg.target);
|
|
28722
|
+
const asRoot = !deployUserWorks;
|
|
28434
28723
|
await runAnsible({
|
|
28435
28724
|
target: cfg.target,
|
|
28436
28725
|
ansible: cfg.provision?.ansible,
|
|
@@ -28450,125 +28739,185 @@ async function bootstrap(inputs) {
|
|
|
28450
28739
|
}
|
|
28451
28740
|
async function upStack(inputs) {
|
|
28452
28741
|
const { cfg } = inputs;
|
|
28453
|
-
const workDir =
|
|
28454
|
-
|
|
28455
|
-
|
|
28456
|
-
|
|
28742
|
+
const workDir = join16(tmpdir3(), "arc-deploy", `stack-${Date.now()}`);
|
|
28743
|
+
mkdirSync13(workDir, { recursive: true });
|
|
28744
|
+
writeFileSync14(join16(workDir, "Caddyfile"), generateCaddyfile(cfg));
|
|
28745
|
+
writeFileSync14(join16(workDir, "docker-compose.yml"), generateCompose({ cfg, cliVersion: inputs.cliVersion }));
|
|
28457
28746
|
await assertExec(cfg.target, `sudo mkdir -p ${cfg.target.remoteDir} && sudo chown ${cfg.target.user}:${cfg.target.user} ${cfg.target.remoteDir}`);
|
|
28458
28747
|
for (const name of Object.keys(cfg.envs)) {
|
|
28459
28748
|
await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/${name}`);
|
|
28460
28749
|
}
|
|
28461
|
-
await scpUpload(cfg.target,
|
|
28462
|
-
await scpUpload(cfg.target,
|
|
28750
|
+
await scpUpload(cfg.target, join16(workDir, "Caddyfile"), `${cfg.target.remoteDir}/Caddyfile`);
|
|
28751
|
+
await scpUpload(cfg.target, join16(workDir, "docker-compose.yml"), `${cfg.target.remoteDir}/docker-compose.yml`);
|
|
28463
28752
|
await assertExec(cfg.target, `cd ${cfg.target.remoteDir} && docker compose pull --ignore-pull-failures && docker compose up -d`);
|
|
28464
28753
|
}
|
|
28465
28754
|
|
|
28466
28755
|
// src/deploy/remote-sync.ts
|
|
28467
|
-
import { existsSync as
|
|
28468
|
-
import {
|
|
28756
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13 } from "fs";
|
|
28757
|
+
import { basename as basename5, join as join17 } from "path";
|
|
28469
28758
|
function diffManifests(local, remote) {
|
|
28470
28759
|
const remoteByName = new Map(remote.modules.map((m) => [m.name, m]));
|
|
28471
28760
|
const changedModules = local.modules.filter((m) => remoteByName.get(m.name)?.hash !== m.hash);
|
|
28472
28761
|
return {
|
|
28473
28762
|
changedModules: [...changedModules],
|
|
28474
|
-
shellChanged: local.shellHash !== remote.shellHash,
|
|
28475
28763
|
stylesChanged: local.stylesHash !== remote.stylesHash
|
|
28476
28764
|
};
|
|
28477
28765
|
}
|
|
28478
28766
|
async function syncEnv(inputs) {
|
|
28479
|
-
const { cfg, env: env2, ws
|
|
28767
|
+
const { cfg, env: env2, ws } = inputs;
|
|
28480
28768
|
const envConfig = cfg.envs[env2];
|
|
28481
28769
|
if (!envConfig)
|
|
28482
28770
|
throw new Error(`Unknown env: ${env2}`);
|
|
28483
|
-
const
|
|
28484
|
-
|
|
28485
|
-
const localManifestPath = join14(ws.modulesDir, "manifest.json");
|
|
28486
|
-
if (!existsSync11(localManifestPath)) {
|
|
28771
|
+
const localManifestPath = join17(ws.modulesDir, "manifest.json");
|
|
28772
|
+
if (!existsSync13(localManifestPath)) {
|
|
28487
28773
|
throw new Error(`Local build missing at ${localManifestPath}. Run arc platform build first.`);
|
|
28488
28774
|
}
|
|
28489
|
-
const localManifest = JSON.parse(
|
|
28490
|
-
const
|
|
28491
|
-
|
|
28775
|
+
const localManifest = JSON.parse(readFileSync13(localManifestPath, "utf-8"));
|
|
28776
|
+
const pkgByName = new Map(ws.packages.map((p) => [p.name.includes("/") ? p.name.split("/").pop() : p.name, p]));
|
|
28777
|
+
let tunnel = await openTunnel(cfg.target, 15500 + hashEnvToOffset(env2), "127.0.0.1", 2019);
|
|
28778
|
+
let restarts = 0;
|
|
28779
|
+
let frameworkChanged = false;
|
|
28492
28780
|
try {
|
|
28493
|
-
const base2 = `http://127.0.0.1:${localPort}/env/${env2}`;
|
|
28494
|
-
const
|
|
28495
|
-
|
|
28496
|
-
|
|
28497
|
-
|
|
28498
|
-
|
|
28499
|
-
const diff = diffManifests(localManifest, remoteManifest);
|
|
28500
|
-
if (diff.shellChanged) {
|
|
28501
|
-
const shellFiles = collectFiles(ws.shellDir);
|
|
28781
|
+
const base2 = () => `http://127.0.0.1:${tunnel.localPort}/env/${env2}`;
|
|
28782
|
+
const localFrameworkHash = readDepsHash(join17(ws.arcDir, ".deps-hash"));
|
|
28783
|
+
const remoteFwRes = await fetch(`${base2()}/api/deploy/framework`);
|
|
28784
|
+
const remoteFw = remoteFwRes.ok ? await remoteFwRes.json() : { depsHash: null };
|
|
28785
|
+
if (localFrameworkHash && localFrameworkHash !== remoteFw.depsHash) {
|
|
28786
|
+
console.log("[arc] Pushing framework deps...");
|
|
28502
28787
|
const form = new FormData;
|
|
28503
|
-
|
|
28504
|
-
|
|
28505
|
-
|
|
28788
|
+
form.append("package.json", new Blob([readFileSync13(join17(ws.arcDir, "package.json"))]), "package.json");
|
|
28789
|
+
const lockPath = join17(ws.arcDir, "bun.lock");
|
|
28790
|
+
if (existsSync13(lockPath)) {
|
|
28791
|
+
form.append("bun.lock", new Blob([readFileSync13(lockPath)]), "bun.lock");
|
|
28506
28792
|
}
|
|
28507
|
-
const
|
|
28793
|
+
const res = await fetch(`${base2()}/api/deploy/framework`, {
|
|
28508
28794
|
method: "POST",
|
|
28509
28795
|
body: form
|
|
28510
28796
|
});
|
|
28511
|
-
if (!
|
|
28512
|
-
throw new Error(`
|
|
28513
|
-
|
|
28514
|
-
|
|
28797
|
+
if (!res.ok) {
|
|
28798
|
+
throw new Error(`framework push failed: ${res.status} ${await res.text()}`);
|
|
28799
|
+
}
|
|
28800
|
+
frameworkChanged = true;
|
|
28801
|
+
const result = await res.json();
|
|
28802
|
+
if (result.needsRestart) {
|
|
28803
|
+
tunnel = await restartAndReopen(cfg, env2, tunnel);
|
|
28804
|
+
restarts += 1;
|
|
28805
|
+
}
|
|
28806
|
+
}
|
|
28807
|
+
const remoteManifestRes = await fetch(`${base2()}/api/deploy/manifest`);
|
|
28808
|
+
const remoteManifest = remoteManifestRes.ok ? await remoteManifestRes.json() : {
|
|
28809
|
+
modules: [],
|
|
28810
|
+
shellHash: "",
|
|
28811
|
+
stylesHash: "",
|
|
28812
|
+
buildTime: ""
|
|
28813
|
+
};
|
|
28814
|
+
const diff = diffManifests(localManifest, remoteManifest);
|
|
28815
|
+
for (const mod of diff.changedModules) {
|
|
28816
|
+
const safeName = sanitizeName(mod.name);
|
|
28817
|
+
const moduleDir = join17(ws.modulesDir, safeName);
|
|
28818
|
+
const browserPath = join17(moduleDir, "browser.js");
|
|
28819
|
+
const serverPath = join17(moduleDir, "server.js");
|
|
28820
|
+
const pkgPath = join17(moduleDir, "package.json");
|
|
28821
|
+
const accessPath = join17(moduleDir, "access.json");
|
|
28822
|
+
const browserActual = existsSync13(browserPath) ? browserPath : join17(ws.modulesDir, `${safeName}.js`);
|
|
28823
|
+
if (!existsSync13(browserActual)) {
|
|
28824
|
+
throw new Error(`Missing browser bundle for module ${mod.name}`);
|
|
28825
|
+
}
|
|
28515
28826
|
const form = new FormData;
|
|
28516
|
-
|
|
28517
|
-
|
|
28518
|
-
|
|
28519
|
-
|
|
28520
|
-
|
|
28827
|
+
form.append("browser.js", new Blob([readFileSync13(browserActual)]), "browser.js");
|
|
28828
|
+
const pkg = pkgByName.get(safeName);
|
|
28829
|
+
if (pkg && isContextPackage(pkg.packageJson) && existsSync13(serverPath)) {
|
|
28830
|
+
form.append("server.js", new Blob([readFileSync13(serverPath)]), "server.js");
|
|
28831
|
+
}
|
|
28832
|
+
if (existsSync13(pkgPath)) {
|
|
28833
|
+
form.append("package.json", new Blob([readFileSync13(pkgPath)]), "package.json");
|
|
28834
|
+
}
|
|
28835
|
+
if (existsSync13(accessPath)) {
|
|
28836
|
+
form.append("access.json", new Blob([readFileSync13(accessPath)]), "access.json");
|
|
28521
28837
|
}
|
|
28522
|
-
|
|
28838
|
+
console.log(`[arc] Pushing module ${safeName}...`);
|
|
28839
|
+
const res = await fetch(`${base2()}/api/deploy/modules/${safeName}`, {
|
|
28523
28840
|
method: "POST",
|
|
28524
28841
|
body: form
|
|
28525
28842
|
});
|
|
28526
|
-
if (!
|
|
28527
|
-
throw new Error(`
|
|
28843
|
+
if (!res.ok) {
|
|
28844
|
+
throw new Error(`module ${safeName} push failed: ${res.status} ${await res.text()}`);
|
|
28845
|
+
}
|
|
28528
28846
|
}
|
|
28529
|
-
if (diff.
|
|
28847
|
+
if (diff.stylesChanged) {
|
|
28848
|
+
console.log("[arc] Pushing styles...");
|
|
28530
28849
|
const form = new FormData;
|
|
28531
|
-
for (const
|
|
28532
|
-
const p =
|
|
28533
|
-
|
|
28850
|
+
for (const name of ["styles.css", "theme.css"]) {
|
|
28851
|
+
const p = join17(ws.arcDir, name);
|
|
28852
|
+
if (existsSync13(p)) {
|
|
28853
|
+
form.append(name, new Blob([readFileSync13(p)]), name);
|
|
28854
|
+
}
|
|
28534
28855
|
}
|
|
28535
|
-
const
|
|
28856
|
+
const res = await fetch(`${base2()}/api/deploy/styles`, {
|
|
28536
28857
|
method: "POST",
|
|
28537
28858
|
body: form
|
|
28538
28859
|
});
|
|
28539
|
-
if (!
|
|
28540
|
-
throw new Error(`
|
|
28860
|
+
if (!res.ok) {
|
|
28861
|
+
throw new Error(`styles push failed: ${res.status} ${await res.text()}`);
|
|
28862
|
+
}
|
|
28541
28863
|
}
|
|
28542
|
-
const
|
|
28864
|
+
const commitRes = await fetch(`${base2()}/api/deploy/manifest`, {
|
|
28543
28865
|
method: "POST",
|
|
28544
28866
|
headers: { "Content-Type": "application/json" },
|
|
28545
28867
|
body: JSON.stringify(localManifest)
|
|
28546
28868
|
});
|
|
28547
|
-
if (!
|
|
28548
|
-
throw new Error(`
|
|
28869
|
+
if (!commitRes.ok) {
|
|
28870
|
+
throw new Error(`manifest commit failed: ${commitRes.status} ${await commitRes.text()}`);
|
|
28871
|
+
}
|
|
28872
|
+
const commit = await commitRes.json();
|
|
28873
|
+
if (commit.needsRestart) {
|
|
28874
|
+
tunnel = await restartAndReopen(cfg, env2, tunnel);
|
|
28875
|
+
restarts += 1;
|
|
28876
|
+
}
|
|
28549
28877
|
return {
|
|
28550
28878
|
env: env2,
|
|
28879
|
+
frameworkChanged,
|
|
28551
28880
|
changedModules: diff.changedModules.map((m) => m.name),
|
|
28552
|
-
|
|
28553
|
-
|
|
28881
|
+
stylesChanged: diff.stylesChanged,
|
|
28882
|
+
restarts
|
|
28554
28883
|
};
|
|
28555
28884
|
} finally {
|
|
28556
28885
|
tunnel.close();
|
|
28557
28886
|
}
|
|
28558
28887
|
}
|
|
28559
|
-
function
|
|
28560
|
-
if (!
|
|
28561
|
-
return
|
|
28562
|
-
|
|
28563
|
-
|
|
28564
|
-
|
|
28565
|
-
|
|
28566
|
-
|
|
28567
|
-
|
|
28568
|
-
|
|
28569
|
-
|
|
28888
|
+
function readDepsHash(path4) {
|
|
28889
|
+
if (!existsSync13(path4))
|
|
28890
|
+
return null;
|
|
28891
|
+
return readFileSync13(path4, "utf-8").trim() || null;
|
|
28892
|
+
}
|
|
28893
|
+
function sanitizeName(name) {
|
|
28894
|
+
return basename5(name);
|
|
28895
|
+
}
|
|
28896
|
+
async function restartAndReopen(cfg, env2, oldTunnel) {
|
|
28897
|
+
console.log(`[arc] Restarting arc-${env2}...`);
|
|
28898
|
+
oldTunnel.close();
|
|
28899
|
+
await assertExec(cfg.target, `docker restart arc-${env2}`);
|
|
28900
|
+
const tunnel = await openTunnel(cfg.target, 15500 + hashEnvToOffset(env2), "127.0.0.1", 2019);
|
|
28901
|
+
await waitForHealthy(`http://127.0.0.1:${tunnel.localPort}/env/${env2}`, 60000);
|
|
28902
|
+
return tunnel;
|
|
28903
|
+
}
|
|
28904
|
+
async function waitForHealthy(baseUrl, timeoutMs) {
|
|
28905
|
+
const deadline = Date.now() + timeoutMs;
|
|
28906
|
+
let lastErr;
|
|
28907
|
+
while (Date.now() < deadline) {
|
|
28908
|
+
try {
|
|
28909
|
+
const res = await fetch(`${baseUrl}/api/deploy/health`, {
|
|
28910
|
+
signal: AbortSignal.timeout(2000)
|
|
28911
|
+
});
|
|
28912
|
+
if (res.ok)
|
|
28913
|
+
return;
|
|
28914
|
+
lastErr = `status ${res.status}`;
|
|
28915
|
+
} catch (e) {
|
|
28916
|
+
lastErr = e;
|
|
28917
|
+
}
|
|
28918
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
28570
28919
|
}
|
|
28571
|
-
|
|
28920
|
+
throw new Error(`Health check timeout: ${String(lastErr)}`);
|
|
28572
28921
|
}
|
|
28573
28922
|
function hashEnvToOffset(env2) {
|
|
28574
28923
|
let h = 0;
|
|
@@ -29274,13 +29623,13 @@ async function platformDeploy(envArg, options = {}) {
|
|
|
29274
29623
|
err(`Unknown env "${envArg}". Known: ${Object.keys(cfg.envs).join(", ")}`);
|
|
29275
29624
|
process.exit(1);
|
|
29276
29625
|
})() : Object.keys(cfg.envs);
|
|
29277
|
-
const manifestPath =
|
|
29278
|
-
const needBuild = options.rebuild || !
|
|
29626
|
+
const manifestPath = join18(ws.modulesDir, "manifest.json");
|
|
29627
|
+
const needBuild = options.rebuild || !existsSync14(manifestPath);
|
|
29279
29628
|
if (needBuild && !options.skipBuild) {
|
|
29280
29629
|
log2("Building platform...");
|
|
29281
29630
|
await buildAll(ws, { noCache: options.rebuild });
|
|
29282
29631
|
ok("Build complete");
|
|
29283
|
-
} else if (!
|
|
29632
|
+
} else if (!existsSync14(manifestPath)) {
|
|
29284
29633
|
err("No build found and --skip-build was set.");
|
|
29285
29634
|
process.exit(1);
|
|
29286
29635
|
}
|
|
@@ -29322,25 +29671,35 @@ async function platformDeploy(envArg, options = {}) {
|
|
|
29322
29671
|
}
|
|
29323
29672
|
}
|
|
29324
29673
|
function readCliVersion() {
|
|
29674
|
+
const candidates = [];
|
|
29675
|
+
const entry = process.argv[1];
|
|
29676
|
+
if (entry) {
|
|
29677
|
+
candidates.push(join18(dirname7(entry), "..", "package.json"));
|
|
29678
|
+
}
|
|
29325
29679
|
try {
|
|
29326
|
-
|
|
29327
|
-
|
|
29328
|
-
|
|
29329
|
-
|
|
29330
|
-
|
|
29680
|
+
candidates.push(join18(import.meta.dir, "..", "..", "package.json"));
|
|
29681
|
+
} catch {}
|
|
29682
|
+
for (const path4 of candidates) {
|
|
29683
|
+
try {
|
|
29684
|
+
const pkg = JSON.parse(readFileSync14(path4, "utf-8"));
|
|
29685
|
+
if (pkg.name === "@arcote.tech/arc-cli" && pkg.version) {
|
|
29686
|
+
return pkg.version;
|
|
29687
|
+
}
|
|
29688
|
+
} catch {}
|
|
29331
29689
|
}
|
|
29690
|
+
return "unknown";
|
|
29332
29691
|
}
|
|
29333
29692
|
async function hashDeployConfig(rootDir) {
|
|
29334
|
-
const p2 =
|
|
29335
|
-
const content =
|
|
29693
|
+
const p2 = join18(rootDir, "deploy.arc.json");
|
|
29694
|
+
const content = readFileSync14(p2);
|
|
29336
29695
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
29337
29696
|
hasher.update(content);
|
|
29338
29697
|
return hasher.digest("hex").slice(0, 16);
|
|
29339
29698
|
}
|
|
29340
29699
|
|
|
29341
29700
|
// src/commands/platform-dev.ts
|
|
29342
|
-
import { existsSync as
|
|
29343
|
-
import { join as
|
|
29701
|
+
import { existsSync as existsSync17, watch } from "fs";
|
|
29702
|
+
import { join as join21 } from "path";
|
|
29344
29703
|
|
|
29345
29704
|
// ../host/src/create-server.ts
|
|
29346
29705
|
var import_jsonwebtoken = __toESM(require_jsonwebtoken(), 1);
|
|
@@ -30790,92 +31149,222 @@ async function createArcServer(config) {
|
|
|
30790
31149
|
};
|
|
30791
31150
|
}
|
|
30792
31151
|
// src/platform/server.ts
|
|
30793
|
-
import { existsSync as
|
|
30794
|
-
import { join as
|
|
31152
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync15 } from "fs";
|
|
31153
|
+
import { join as join20 } from "path";
|
|
30795
31154
|
|
|
30796
31155
|
// src/platform/deploy-api.ts
|
|
30797
|
-
|
|
30798
|
-
import {
|
|
31156
|
+
var {spawn: spawn4 } = globalThis.Bun;
|
|
31157
|
+
import {
|
|
31158
|
+
cpSync,
|
|
31159
|
+
existsSync as existsSync15,
|
|
31160
|
+
mkdirSync as mkdirSync14,
|
|
31161
|
+
readFileSync as readFileSync15,
|
|
31162
|
+
rmSync as rmSync4,
|
|
31163
|
+
writeFileSync as writeFileSync15
|
|
31164
|
+
} from "fs";
|
|
31165
|
+
import { dirname as dirname8, join as join19, normalize as normalize2, resolve } from "path";
|
|
30799
31166
|
function createDeployApiHandler(opts) {
|
|
30800
31167
|
return async (req, url, ctx) => {
|
|
30801
31168
|
const p3 = url.pathname;
|
|
30802
31169
|
if (!p3.startsWith("/api/deploy/"))
|
|
30803
31170
|
return null;
|
|
31171
|
+
const cors = ctx.corsHeaders;
|
|
30804
31172
|
if (p3 === "/api/deploy/health" && req.method === "GET") {
|
|
30805
|
-
return Response.json({ ok: true, modules: opts.getManifest().modules.length }, { headers:
|
|
31173
|
+
return Response.json({ ok: true, modules: opts.getManifest().modules.length }, { headers: cors });
|
|
31174
|
+
}
|
|
31175
|
+
if (p3 === "/api/deploy/framework" && req.method === "GET") {
|
|
31176
|
+
const hashPath = join19(opts.ws.arcDir, ".deps-hash");
|
|
31177
|
+
const depsHash = existsSync15(hashPath) ? readFileSync15(hashPath, "utf-8").trim() : null;
|
|
31178
|
+
return Response.json({ depsHash }, { headers: cors });
|
|
30806
31179
|
}
|
|
30807
31180
|
if (p3 === "/api/deploy/manifest" && req.method === "GET") {
|
|
30808
|
-
return Response.json(opts.getManifest(), { headers:
|
|
31181
|
+
return Response.json(opts.getManifest(), { headers: cors });
|
|
30809
31182
|
}
|
|
30810
|
-
if (p3 === "/api/deploy/
|
|
30811
|
-
const
|
|
30812
|
-
|
|
30813
|
-
|
|
31183
|
+
if (p3 === "/api/deploy/framework" && req.method === "POST") {
|
|
31184
|
+
const form = await req.formData();
|
|
31185
|
+
const pkgFile = form.get("package.json");
|
|
31186
|
+
const lockFile = form.get("bun.lock");
|
|
31187
|
+
if (!isFile(pkgFile) || !isFile(lockFile)) {
|
|
31188
|
+
return badRequest("framework requires package.json + bun.lock", cors);
|
|
31189
|
+
}
|
|
31190
|
+
mkdirSync14(opts.ws.arcDir, { recursive: true });
|
|
31191
|
+
writeFileSync15(join19(opts.ws.arcDir, "package.json"), Buffer.from(await pkgFile.arrayBuffer()));
|
|
31192
|
+
writeFileSync15(join19(opts.ws.arcDir, "bun.lock"), Buffer.from(await lockFile.arrayBuffer()));
|
|
31193
|
+
const start = Date.now();
|
|
31194
|
+
const installOk = await runBun(["install", "--production", "--frozen-lockfile"], opts.ws.arcDir);
|
|
31195
|
+
if (!installOk) {
|
|
31196
|
+
return Response.json({ error: "bun install failed" }, { status: 500, headers: cors });
|
|
31197
|
+
}
|
|
31198
|
+
const cliBin = process.argv[1] ?? "arc";
|
|
31199
|
+
const shellOk = await runBun([
|
|
31200
|
+
"run",
|
|
31201
|
+
cliBin,
|
|
31202
|
+
"_build-shell",
|
|
31203
|
+
"--out",
|
|
31204
|
+
opts.ws.shellDir,
|
|
31205
|
+
"--from",
|
|
31206
|
+
join19(opts.ws.arcDir, "node_modules")
|
|
31207
|
+
], opts.ws.arcDir);
|
|
31208
|
+
if (!shellOk) {
|
|
31209
|
+
return Response.json({ error: "shell build failed" }, { status: 500, headers: cors });
|
|
31210
|
+
}
|
|
31211
|
+
const newHash = sha256Hex(Buffer.concat([
|
|
31212
|
+
readFileSync15(join19(opts.ws.arcDir, "package.json")),
|
|
31213
|
+
readFileSync15(join19(opts.ws.arcDir, "bun.lock"))
|
|
31214
|
+
]));
|
|
31215
|
+
writeFileSync15(join19(opts.ws.arcDir, ".deps-hash"), newHash + `
|
|
31216
|
+
`);
|
|
31217
|
+
return Response.json({
|
|
31218
|
+
changed: true,
|
|
31219
|
+
tookMs: Date.now() - start,
|
|
31220
|
+
needsRestart: true
|
|
31221
|
+
}, { headers: cors });
|
|
31222
|
+
}
|
|
31223
|
+
const moduleMatch = p3.match(/^\/api\/deploy\/modules\/([^/]+)$/);
|
|
31224
|
+
if (moduleMatch && req.method === "POST") {
|
|
31225
|
+
const safeName = sanitizeName2(moduleMatch[1]);
|
|
31226
|
+
if (!safeName)
|
|
31227
|
+
return badRequest("invalid module name", cors);
|
|
31228
|
+
const form = await req.formData();
|
|
31229
|
+
const browser = form.get("browser.js");
|
|
31230
|
+
if (!isFile(browser)) {
|
|
31231
|
+
return badRequest("modules/<name> requires browser.js", cors);
|
|
31232
|
+
}
|
|
31233
|
+
const stagingDir = join19(opts.ws.arcDir, ".staging", "modules", safeName);
|
|
31234
|
+
mkdirSync14(stagingDir, { recursive: true });
|
|
31235
|
+
await writeField(stagingDir, "browser.js", browser);
|
|
31236
|
+
const server = form.get("server.js");
|
|
31237
|
+
if (isFile(server)) {
|
|
31238
|
+
await writeField(stagingDir, "server.js", server);
|
|
31239
|
+
}
|
|
31240
|
+
const pkg = form.get("package.json");
|
|
31241
|
+
if (isFile(pkg)) {
|
|
31242
|
+
await writeField(stagingDir, "package.json", pkg);
|
|
31243
|
+
}
|
|
31244
|
+
const access = form.get("access.json");
|
|
31245
|
+
if (isFile(access)) {
|
|
31246
|
+
await writeField(stagingDir, "access.json", access);
|
|
31247
|
+
}
|
|
31248
|
+
const stagingPkgPath = join19(stagingDir, "package.json");
|
|
31249
|
+
const livePkgPath = join19(opts.ws.arcDir, "modules", safeName, "package.json");
|
|
31250
|
+
const stagingPkgBytes = existsSync15(stagingPkgPath) ? readFileSync15(stagingPkgPath) : Buffer.from("");
|
|
31251
|
+
const livePkgBytes = existsSync15(livePkgPath) ? readFileSync15(livePkgPath) : Buffer.from("");
|
|
31252
|
+
const depsChanged = sha256Hex(stagingPkgBytes) !== sha256Hex(livePkgBytes);
|
|
31253
|
+
writeFileSync15(join19(stagingDir, ".deps-hash"), sha256Hex(stagingPkgBytes) + `
|
|
31254
|
+
`);
|
|
31255
|
+
if (depsChanged && existsSync15(stagingPkgPath)) {
|
|
31256
|
+
const ok2 = await runBun(["install", "--production"], stagingDir);
|
|
31257
|
+
if (!ok2) {
|
|
31258
|
+
rmSync4(stagingDir, { recursive: true, force: true });
|
|
31259
|
+
return Response.json({ error: `bun install failed for module ${safeName}` }, { status: 500, headers: cors });
|
|
31260
|
+
}
|
|
31261
|
+
} else if (!depsChanged) {
|
|
31262
|
+
const liveNodeModules = join19(opts.ws.arcDir, "modules", safeName, "node_modules");
|
|
31263
|
+
if (existsSync15(liveNodeModules)) {
|
|
31264
|
+
cpSync(liveNodeModules, join19(stagingDir, "node_modules"), {
|
|
31265
|
+
recursive: true
|
|
31266
|
+
});
|
|
31267
|
+
}
|
|
30814
31268
|
}
|
|
30815
|
-
|
|
30816
|
-
|
|
30817
|
-
|
|
30818
|
-
|
|
31269
|
+
return Response.json({
|
|
31270
|
+
ok: true,
|
|
31271
|
+
name: safeName,
|
|
31272
|
+
depsChanged,
|
|
31273
|
+
serverIncluded: isFile(server)
|
|
31274
|
+
}, { headers: cors });
|
|
30819
31275
|
}
|
|
30820
|
-
if (p3 === "/api/deploy/
|
|
31276
|
+
if (p3 === "/api/deploy/styles" && req.method === "POST") {
|
|
30821
31277
|
const form = await req.formData();
|
|
30822
|
-
const
|
|
30823
|
-
|
|
31278
|
+
const stagingDir = join19(opts.ws.arcDir, ".staging");
|
|
31279
|
+
mkdirSync14(stagingDir, { recursive: true });
|
|
31280
|
+
const written = [];
|
|
31281
|
+
for (const name of ["styles.css", "theme.css"]) {
|
|
31282
|
+
const f2 = form.get(name);
|
|
31283
|
+
if (isFile(f2)) {
|
|
31284
|
+
await writeField(stagingDir, name, f2);
|
|
31285
|
+
written.push(name);
|
|
31286
|
+
}
|
|
31287
|
+
}
|
|
31288
|
+
return Response.json({ ok: true, written }, { headers: cors });
|
|
30824
31289
|
}
|
|
30825
|
-
if (p3 === "/api/deploy/
|
|
30826
|
-
const
|
|
30827
|
-
|
|
30828
|
-
|
|
30829
|
-
|
|
30830
|
-
|
|
30831
|
-
|
|
30832
|
-
|
|
30833
|
-
|
|
30834
|
-
|
|
30835
|
-
|
|
31290
|
+
if (p3 === "/api/deploy/manifest" && req.method === "POST") {
|
|
31291
|
+
const body = await req.json();
|
|
31292
|
+
if (!validateManifest(body)) {
|
|
31293
|
+
return badRequest("invalid manifest body", cors);
|
|
31294
|
+
}
|
|
31295
|
+
const stagingRoot = join19(opts.ws.arcDir, ".staging");
|
|
31296
|
+
const stagingModules = join19(stagingRoot, "modules");
|
|
31297
|
+
let serverSideChanged = false;
|
|
31298
|
+
if (existsSync15(stagingModules)) {
|
|
31299
|
+
const stagedNames = readDir(stagingModules);
|
|
31300
|
+
for (const name of stagedNames) {
|
|
31301
|
+
const src2 = join19(stagingModules, name);
|
|
31302
|
+
const dst = join19(opts.ws.arcDir, "modules", name);
|
|
31303
|
+
if (existsSync15(join19(src2, "server.js")))
|
|
31304
|
+
serverSideChanged = true;
|
|
31305
|
+
if (existsSync15(dst)) {
|
|
31306
|
+
rmSync4(dst, { recursive: true, force: true });
|
|
31307
|
+
}
|
|
31308
|
+
mkdirSync14(dirname8(dst), { recursive: true });
|
|
31309
|
+
cpSync(src2, dst, { recursive: true });
|
|
30836
31310
|
}
|
|
30837
31311
|
}
|
|
30838
|
-
const
|
|
30839
|
-
|
|
30840
|
-
|
|
30841
|
-
|
|
30842
|
-
|
|
30843
|
-
|
|
31312
|
+
for (const name of ["styles.css", "theme.css"]) {
|
|
31313
|
+
const src2 = join19(stagingRoot, name);
|
|
31314
|
+
if (existsSync15(src2)) {
|
|
31315
|
+
cpSync(src2, join19(opts.ws.arcDir, name));
|
|
31316
|
+
}
|
|
31317
|
+
}
|
|
31318
|
+
writeFileSync15(join19(opts.ws.modulesDir, "manifest.json"), JSON.stringify(body, null, 2));
|
|
31319
|
+
opts.setManifest(body);
|
|
31320
|
+
rmSync4(stagingRoot, { recursive: true, force: true });
|
|
31321
|
+
if (!serverSideChanged) {
|
|
31322
|
+
opts.notifyReload(body);
|
|
30844
31323
|
}
|
|
30845
|
-
return Response.json({
|
|
31324
|
+
return Response.json({
|
|
31325
|
+
ok: true,
|
|
31326
|
+
moduleCount: body.modules.length,
|
|
31327
|
+
needsRestart: serverSideChanged
|
|
31328
|
+
}, { headers: cors });
|
|
30846
31329
|
}
|
|
30847
|
-
return new Response("Not Found", {
|
|
30848
|
-
status: 404,
|
|
30849
|
-
headers: ctx.corsHeaders
|
|
30850
|
-
});
|
|
31330
|
+
return new Response("Not Found", { status: 404, headers: cors });
|
|
30851
31331
|
};
|
|
30852
31332
|
}
|
|
30853
|
-
|
|
30854
|
-
|
|
30855
|
-
for (const [, value] of form.entries()) {
|
|
30856
|
-
if (isFile(value))
|
|
30857
|
-
files.push(value);
|
|
30858
|
-
}
|
|
30859
|
-
return writeUploadedFileList(files, targetDir);
|
|
31333
|
+
function badRequest(msg, cors) {
|
|
31334
|
+
return Response.json({ error: msg }, { status: 400, headers: cors });
|
|
30860
31335
|
}
|
|
30861
31336
|
function isFile(v3) {
|
|
30862
31337
|
return typeof v3 === "object" && v3 !== null && typeof v3.arrayBuffer === "function" && typeof v3.name === "string";
|
|
30863
31338
|
}
|
|
30864
|
-
async function
|
|
30865
|
-
const
|
|
31339
|
+
async function writeField(targetDir, name, file) {
|
|
31340
|
+
const safe = normalize2(name);
|
|
30866
31341
|
const safeRoot = resolve(targetDir);
|
|
30867
|
-
|
|
30868
|
-
|
|
30869
|
-
|
|
30870
|
-
const full = resolve(safeRoot, rel);
|
|
30871
|
-
if (!full.startsWith(safeRoot + "/") && full !== safeRoot) {
|
|
30872
|
-
throw new Error(`Path traversal rejected: ${file.name}`);
|
|
30873
|
-
}
|
|
30874
|
-
mkdirSync11(dirname7(full), { recursive: true });
|
|
30875
|
-
writeFileSync12(full, Buffer.from(await file.arrayBuffer()));
|
|
30876
|
-
written.push(relative5(safeRoot, full) || rel);
|
|
31342
|
+
const full = resolve(safeRoot, safe);
|
|
31343
|
+
if (!full.startsWith(safeRoot + "/") && full !== safeRoot) {
|
|
31344
|
+
throw new Error(`Path traversal rejected: ${name}`);
|
|
30877
31345
|
}
|
|
30878
|
-
|
|
31346
|
+
mkdirSync14(dirname8(full), { recursive: true });
|
|
31347
|
+
writeFileSync15(full, Buffer.from(await file.arrayBuffer()));
|
|
31348
|
+
}
|
|
31349
|
+
function sanitizeName2(name) {
|
|
31350
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name))
|
|
31351
|
+
return null;
|
|
31352
|
+
return name;
|
|
31353
|
+
}
|
|
31354
|
+
function readDir(dir) {
|
|
31355
|
+
if (!existsSync15(dir))
|
|
31356
|
+
return [];
|
|
31357
|
+
return __require("fs").readdirSync(dir);
|
|
31358
|
+
}
|
|
31359
|
+
async function runBun(args, cwd) {
|
|
31360
|
+
const proc2 = spawn4({
|
|
31361
|
+
cmd: ["bun", ...args],
|
|
31362
|
+
cwd,
|
|
31363
|
+
stdout: "inherit",
|
|
31364
|
+
stderr: "inherit"
|
|
31365
|
+
});
|
|
31366
|
+
const exit = await proc2.exited;
|
|
31367
|
+
return exit === 0;
|
|
30879
31368
|
}
|
|
30880
31369
|
function validateManifest(m4) {
|
|
30881
31370
|
if (!m4 || typeof m4 !== "object")
|
|
@@ -30948,7 +31437,7 @@ function getMime(path4) {
|
|
|
30948
31437
|
return MIME[ext2] ?? "application/octet-stream";
|
|
30949
31438
|
}
|
|
30950
31439
|
function serveFile(filePath, headers = {}) {
|
|
30951
|
-
if (!
|
|
31440
|
+
if (!existsSync16(filePath))
|
|
30952
31441
|
return new Response("Not Found", { status: 404 });
|
|
30953
31442
|
return new Response(Bun.file(filePath), {
|
|
30954
31443
|
headers: { "Content-Type": getMime(filePath), ...headers }
|
|
@@ -31037,7 +31526,7 @@ function staticFilesHandler(ws, devMode, moduleAccessMap) {
|
|
|
31037
31526
|
return (_req, url, ctx) => {
|
|
31038
31527
|
const path4 = url.pathname;
|
|
31039
31528
|
if (path4.startsWith("/shell/"))
|
|
31040
|
-
return serveFile(
|
|
31529
|
+
return serveFile(join20(ws.shellDir, path4.slice(7)), ctx.corsHeaders);
|
|
31041
31530
|
if (path4.startsWith("/modules/")) {
|
|
31042
31531
|
const fileWithParams = path4.slice(9);
|
|
31043
31532
|
const filename = fileWithParams.split("?")[0];
|
|
@@ -31049,25 +31538,25 @@ function staticFilesHandler(ws, devMode, moduleAccessMap) {
|
|
|
31049
31538
|
return new Response("Forbidden", { status: 403, headers: ctx.corsHeaders });
|
|
31050
31539
|
}
|
|
31051
31540
|
}
|
|
31052
|
-
return serveFile(
|
|
31541
|
+
return serveFile(join20(ws.modulesDir, filename), {
|
|
31053
31542
|
...ctx.corsHeaders,
|
|
31054
31543
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
|
|
31055
31544
|
});
|
|
31056
31545
|
}
|
|
31057
31546
|
if (path4.startsWith("/locales/"))
|
|
31058
|
-
return serveFile(
|
|
31547
|
+
return serveFile(join20(ws.arcDir, path4.slice(1)), ctx.corsHeaders);
|
|
31059
31548
|
if (path4.startsWith("/assets/"))
|
|
31060
|
-
return serveFile(
|
|
31549
|
+
return serveFile(join20(ws.assetsDir, path4.slice(8)), ctx.corsHeaders);
|
|
31061
31550
|
if (path4 === "/styles.css")
|
|
31062
|
-
return serveFile(
|
|
31551
|
+
return serveFile(join20(ws.arcDir, "styles.css"), ctx.corsHeaders);
|
|
31063
31552
|
if (path4 === "/theme.css")
|
|
31064
|
-
return serveFile(
|
|
31553
|
+
return serveFile(join20(ws.arcDir, "theme.css"), ctx.corsHeaders);
|
|
31065
31554
|
if ((path4 === "/manifest.json" || path4 === "/manifest.webmanifest") && ws.manifest) {
|
|
31066
31555
|
return serveFile(ws.manifest.path, ctx.corsHeaders);
|
|
31067
31556
|
}
|
|
31068
31557
|
if (path4.lastIndexOf(".") > path4.lastIndexOf("/")) {
|
|
31069
|
-
const publicFile =
|
|
31070
|
-
if (
|
|
31558
|
+
const publicFile = join20(ws.publicDir, path4.slice(1));
|
|
31559
|
+
if (existsSync16(publicFile))
|
|
31071
31560
|
return serveFile(publicFile, ctx.corsHeaders);
|
|
31072
31561
|
}
|
|
31073
31562
|
return null;
|
|
@@ -31205,10 +31694,10 @@ async function startPlatformServer(opts) {
|
|
|
31205
31694
|
};
|
|
31206
31695
|
}
|
|
31207
31696
|
const { createBunSQLiteAdapterFactory: createBunSQLiteAdapterFactory2 } = await Promise.resolve().then(() => (init_dist(), exports_dist2));
|
|
31208
|
-
const dbPath = opts.dbPath ||
|
|
31697
|
+
const dbPath = opts.dbPath || join20(ws.arcDir, "data", "arc.db");
|
|
31209
31698
|
const dbDir = dbPath.substring(0, dbPath.lastIndexOf("/"));
|
|
31210
31699
|
if (dbDir)
|
|
31211
|
-
|
|
31700
|
+
mkdirSync15(dbDir, { recursive: true });
|
|
31212
31701
|
const arcServer = await createArcServer({
|
|
31213
31702
|
context,
|
|
31214
31703
|
dbAdapterFactory: createBunSQLiteAdapterFactory2(dbPath),
|
|
@@ -31251,7 +31740,7 @@ async function platformDev(opts = {}) {
|
|
|
31251
31740
|
manifest,
|
|
31252
31741
|
context,
|
|
31253
31742
|
moduleAccess,
|
|
31254
|
-
dbPath:
|
|
31743
|
+
dbPath: join21(ws.rootDir, ".arc", "data", "dev.db"),
|
|
31255
31744
|
devMode: true,
|
|
31256
31745
|
arcEntries
|
|
31257
31746
|
});
|
|
@@ -31282,8 +31771,8 @@ async function platformDev(opts = {}) {
|
|
|
31282
31771
|
}, 300);
|
|
31283
31772
|
};
|
|
31284
31773
|
for (const pkg of ws.packages) {
|
|
31285
|
-
const srcDir =
|
|
31286
|
-
if (!
|
|
31774
|
+
const srcDir = join21(pkg.path, "src");
|
|
31775
|
+
if (!existsSync17(srcDir))
|
|
31287
31776
|
continue;
|
|
31288
31777
|
watch(srcDir, { recursive: true }, (_event, filename) => {
|
|
31289
31778
|
if (!filename || filename.includes(".arc") || filename.endsWith(".d.ts") || filename.includes("node_modules") || filename.includes("dist"))
|
|
@@ -31293,8 +31782,8 @@ async function platformDev(opts = {}) {
|
|
|
31293
31782
|
triggerRebuild();
|
|
31294
31783
|
});
|
|
31295
31784
|
}
|
|
31296
|
-
const localesDir =
|
|
31297
|
-
if (
|
|
31785
|
+
const localesDir = join21(ws.rootDir, "locales");
|
|
31786
|
+
if (existsSync17(localesDir)) {
|
|
31298
31787
|
watch(localesDir, { recursive: false }, (_event, filename) => {
|
|
31299
31788
|
if (!filename?.endsWith(".po"))
|
|
31300
31789
|
return;
|
|
@@ -31310,17 +31799,41 @@ async function platformDev(opts = {}) {
|
|
|
31310
31799
|
}
|
|
31311
31800
|
|
|
31312
31801
|
// src/commands/platform-start.ts
|
|
31313
|
-
import { existsSync as
|
|
31314
|
-
import { join as
|
|
31802
|
+
import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
|
|
31803
|
+
import { join as join22 } from "path";
|
|
31315
31804
|
async function platformStart() {
|
|
31316
31805
|
const ws = resolveWorkspace();
|
|
31317
31806
|
const port = parseInt(process.env.PORT || "5005", 10);
|
|
31318
|
-
const
|
|
31319
|
-
|
|
31320
|
-
|
|
31321
|
-
|
|
31807
|
+
const deployApi = process.env.ARC_DEPLOY_API === "1";
|
|
31808
|
+
const manifestPath = join22(ws.modulesDir, "manifest.json");
|
|
31809
|
+
if (!existsSync18(manifestPath)) {
|
|
31810
|
+
if (!deployApi) {
|
|
31811
|
+
err("No build found. Run `arc platform build` first.");
|
|
31812
|
+
process.exit(1);
|
|
31813
|
+
}
|
|
31814
|
+
log2("Pre-deploy mode \u2014 no manifest yet, awaiting first /api/deploy/*");
|
|
31815
|
+
const emptyManifest = {
|
|
31816
|
+
modules: [],
|
|
31817
|
+
shellHash: "",
|
|
31818
|
+
stylesHash: "",
|
|
31819
|
+
buildTime: new Date().toISOString()
|
|
31820
|
+
};
|
|
31821
|
+
const platform4 = await startPlatformServer({
|
|
31822
|
+
ws,
|
|
31823
|
+
port,
|
|
31824
|
+
manifest: emptyManifest,
|
|
31825
|
+
context: null,
|
|
31826
|
+
moduleAccess: new Map,
|
|
31827
|
+
dbPath: join22(ws.rootDir, ".arc", "data", "prod.db"),
|
|
31828
|
+
devMode: false,
|
|
31829
|
+
deployApi: true,
|
|
31830
|
+
arcEntries: []
|
|
31831
|
+
});
|
|
31832
|
+
ok(`Pre-deploy server on http://localhost:${port}`);
|
|
31833
|
+
registerSignalCleanup(platform4);
|
|
31834
|
+
return;
|
|
31322
31835
|
}
|
|
31323
|
-
const manifest = JSON.parse(
|
|
31836
|
+
const manifest = JSON.parse(readFileSync16(manifestPath, "utf-8"));
|
|
31324
31837
|
log2("Loading server context...");
|
|
31325
31838
|
const { context, moduleAccess } = await loadServerContext(ws.packages);
|
|
31326
31839
|
if (context) {
|
|
@@ -31329,7 +31842,6 @@ async function platformStart() {
|
|
|
31329
31842
|
log2("No context \u2014 server endpoints skipped");
|
|
31330
31843
|
}
|
|
31331
31844
|
const arcEntries = collectArcPeerDeps(ws.packages);
|
|
31332
|
-
const deployApi = process.env.ARC_DEPLOY_API === "1";
|
|
31333
31845
|
if (deployApi)
|
|
31334
31846
|
ok("Deploy API enabled (/api/deploy/*)");
|
|
31335
31847
|
const platform3 = await startPlatformServer({
|
|
@@ -31338,7 +31850,7 @@ async function platformStart() {
|
|
|
31338
31850
|
manifest,
|
|
31339
31851
|
context,
|
|
31340
31852
|
moduleAccess,
|
|
31341
|
-
dbPath:
|
|
31853
|
+
dbPath: join22(ws.rootDir, ".arc", "data", "prod.db"),
|
|
31342
31854
|
devMode: false,
|
|
31343
31855
|
deployApi,
|
|
31344
31856
|
arcEntries
|
|
@@ -31346,6 +31858,9 @@ async function platformStart() {
|
|
|
31346
31858
|
ok(`Server on http://localhost:${port}`);
|
|
31347
31859
|
if (platform3.contextHandler)
|
|
31348
31860
|
ok("Commands, queries, WebSocket \u2014 all on same port");
|
|
31861
|
+
registerSignalCleanup(platform3);
|
|
31862
|
+
}
|
|
31863
|
+
function registerSignalCleanup(platform3) {
|
|
31349
31864
|
const cleanup = () => {
|
|
31350
31865
|
platform3.stop();
|
|
31351
31866
|
process.exit(0);
|
|
@@ -31364,6 +31879,7 @@ platform3.command("dev").description("Start platform in dev mode (Bun server + V
|
|
|
31364
31879
|
platform3.command("build").description("Build platform for production").option("--no-cache", "Force full rebuild").action((opts) => platformBuild({ noCache: opts.cache === false }));
|
|
31365
31880
|
platform3.command("start").description("Start platform in production mode (requires prior build)").action(platformStart);
|
|
31366
31881
|
platform3.command("deploy [env]").description("Deploy platform to a remote server (reads deploy.arc.json, surveys if missing)").option("--skip-build", "Skip local build step").option("--rebuild", "Force rebuild before deploy").action((env2, opts) => platformDeploy(env2, opts));
|
|
31882
|
+
program2.command("_build-shell", { hidden: true }).description("Build framework shell bundles from a node_modules dir").requiredOption("--out <dir>", "Output directory for shell .js bundles").requiredOption("--from <dir>", "node_modules directory to discover packages in").action((opts) => buildShell(opts));
|
|
31367
31883
|
program2.parse(process.argv);
|
|
31368
31884
|
if (process.argv.length === 2) {
|
|
31369
31885
|
program2.help();
|