@supacloud/cli 0.58.5 → 0.59.0
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/README.md +33 -0
- package/dist/index.js +1388 -55
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -206726,7 +206726,11 @@ var ACTION_POLICY = {
|
|
|
206726
206726
|
write: ["send", "receive", "ack", "release", "fail", "retry", "delete_message", "update_settings"]
|
|
206727
206727
|
},
|
|
206728
206728
|
ai: { local: ["show_skill", "install_skill"] },
|
|
206729
|
-
app: {
|
|
206729
|
+
app: {
|
|
206730
|
+
local: ["init", "generate", "compile", "check", "graph", "explain", "export-tools", "context", "doctor", "fix", "plan", "build"],
|
|
206731
|
+
read: ["status"],
|
|
206732
|
+
write: ["upload", "configure", "deploy", "rollback", "reconcile", "retire"]
|
|
206733
|
+
},
|
|
206730
206734
|
db: { local: ["lint", "explain"], read: ["module_check"] },
|
|
206731
206735
|
dev: { read: ["status"], write: ["sync", "watch", "migrate"] }
|
|
206732
206736
|
};
|
|
@@ -221490,8 +221494,8 @@ function registerAiTools(server2) {
|
|
|
221490
221494
|
|
|
221491
221495
|
// src/shared/tools/app-tools.ts
|
|
221492
221496
|
import { existsSync as existsSync10 } from "node:fs";
|
|
221493
|
-
import { mkdir as
|
|
221494
|
-
import { basename as basename8, dirname as
|
|
221497
|
+
import { mkdir as mkdir5, readFile as readFile4, readdir as readdir5, writeFile as writeFile5 } from "node:fs/promises";
|
|
221498
|
+
import { basename as basename8, dirname as dirname10, join as join14, relative as relative9, resolve as resolve14 } from "node:path";
|
|
221495
221499
|
|
|
221496
221500
|
// node_modules/@supacloud/compiler/dist/index.js
|
|
221497
221501
|
var ts7 = __toESM(require_typescript(), 1);
|
|
@@ -226886,18 +226890,29 @@ import { createHash as createHash7 } from "node:crypto";
|
|
|
226886
226890
|
import { dirname as dirname22, relative as relative5, sep as sep5 } from "node:path";
|
|
226887
226891
|
var ts6 = __toESM(require_typescript(), 1);
|
|
226888
226892
|
var ts5 = __toESM(require_typescript(), 1);
|
|
226893
|
+
import { createHash as createHash62 } from "node:crypto";
|
|
226889
226894
|
import { access as access2, readFile as readFile22 } from "node:fs/promises";
|
|
226890
226895
|
import { join as join42 } from "node:path";
|
|
226891
226896
|
import { existsSync as existsSync22, readFileSync as readFileSync22 } from "node:fs";
|
|
226892
226897
|
import { dirname as dirname32, join as join32, relative as relative32, resolve as resolve22, sep as sep32 } from "node:path";
|
|
226893
226898
|
var ts11 = __toESM(require_typescript(), 1);
|
|
226899
|
+
import { mkdir as mkdir3, mkdtemp, readFile as readFile5, realpath as realpath3, rename as rename3, rm as rm2 } from "node:fs/promises";
|
|
226900
|
+
import { readFileSync as readFileSync32 } from "node:fs";
|
|
226901
|
+
import { dirname as dirname7, join as join92, relative as relative7, resolve as resolve112, sep as sep7 } from "node:path";
|
|
226902
|
+
import { createHash as createHash72 } from "node:crypto";
|
|
226903
|
+
import { constants as constants2 } from "node:fs";
|
|
226904
|
+
import { lstat as lstat2, mkdir as mkdir22, open as open3, readFile as readFile3, readdir as readdir3, realpath as realpath4, rename as rename22, rm as rm3, writeFile as writeFile22 } from "node:fs/promises";
|
|
226905
|
+
import { dirname as dirname42, isAbsolute as isAbsolute4, join as join52, relative as relative52, resolve as resolve62, sep as sep52 } from "node:path";
|
|
226906
|
+
import { isAbsolute as isAbsolute22 } from "node:path";
|
|
226894
226907
|
import { existsSync as existsSync32 } from "node:fs";
|
|
226895
226908
|
import { dirname as dirname52, join as join62, relative as relative6, sep as sep6 } from "node:path";
|
|
226896
226909
|
var ts10 = __toESM(require_typescript(), 1);
|
|
226897
226910
|
var ts9 = __toESM(require_typescript(), 1);
|
|
226911
|
+
import { join as join72, resolve as resolve82 } from "node:path";
|
|
226898
226912
|
var ts12 = __toESM(require_typescript(), 1);
|
|
226913
|
+
import { resolve as resolve102 } from "node:path";
|
|
226899
226914
|
import { randomUUID } from "node:crypto";
|
|
226900
|
-
import { lstat as
|
|
226915
|
+
import { lstat as lstat22, readFile as readFile6, realpath as realpath42, rename as rename4, unlink as unlink2, writeFile as writeFile3 } from "node:fs/promises";
|
|
226901
226916
|
import { dirname as dirname8, isAbsolute as isAbsolute32, relative as relative8, resolve as resolve122, sep as sep8 } from "node:path";
|
|
226902
226917
|
var ts13 = __toESM(require_typescript(), 1);
|
|
226903
226918
|
import { existsSync as existsSync52 } from "node:fs";
|
|
@@ -239275,6 +239290,387 @@ function parseDeliveryOptions(value) {
|
|
|
239275
239290
|
}
|
|
239276
239291
|
return value;
|
|
239277
239292
|
}
|
|
239293
|
+
function parseDeliveryPlanResult(value) {
|
|
239294
|
+
if (!Check22(DeliveryPlanResultSchema2, value))
|
|
239295
|
+
throw new Error("Invalid delivery plan result.");
|
|
239296
|
+
return value;
|
|
239297
|
+
}
|
|
239298
|
+
var compare2 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
239299
|
+
var sorted = (values) => [...new Set(values)].sort(compare2);
|
|
239300
|
+
function diagnostic(error) {
|
|
239301
|
+
return {
|
|
239302
|
+
severity: error.severity,
|
|
239303
|
+
code: error.code,
|
|
239304
|
+
message: error.message,
|
|
239305
|
+
...error.file === undefined ? {} : { file: error.file },
|
|
239306
|
+
...error.line === undefined ? {} : { line: error.line },
|
|
239307
|
+
...error.suggestion === undefined ? {} : { suggestion: error.suggestion }
|
|
239308
|
+
};
|
|
239309
|
+
}
|
|
239310
|
+
function createDeliveryPlan(graph, input) {
|
|
239311
|
+
let options;
|
|
239312
|
+
try {
|
|
239313
|
+
options = parseDeliveryOptions(input);
|
|
239314
|
+
} catch (error) {
|
|
239315
|
+
if (!(error instanceof DeliveryConfigurationError))
|
|
239316
|
+
throw error;
|
|
239317
|
+
return { ok: false, plan: null, written: [], diagnostics: [{
|
|
239318
|
+
severity: "error",
|
|
239319
|
+
code: error.code,
|
|
239320
|
+
message: error.message,
|
|
239321
|
+
suggestion: "Use DeliveryOptionsSchema or defineSupacloudConfig({ delivery: ... })."
|
|
239322
|
+
}] };
|
|
239323
|
+
}
|
|
239324
|
+
const diagnostics = [
|
|
239325
|
+
...(graph.diagnostics ?? []).map(diagnostic),
|
|
239326
|
+
...validateGraph(graph, { strict: true }).map(diagnostic)
|
|
239327
|
+
];
|
|
239328
|
+
const fail = (code, message, suggestion) => {
|
|
239329
|
+
diagnostics.push({ severity: "error", code, message, suggestion });
|
|
239330
|
+
};
|
|
239331
|
+
const modules = new Map(graph.modules.map((module) => [module.name, module]));
|
|
239332
|
+
const declarations = new Map;
|
|
239333
|
+
const httpOwners = new Map;
|
|
239334
|
+
const jobOwners = new Map;
|
|
239335
|
+
for (const target of [...options.targets ?? []].sort((a, b) => compare2(a.name, b.name))) {
|
|
239336
|
+
if (declarations.has(target.name)) {
|
|
239337
|
+
fail("delivery-duplicate-target", `Target "${target.name}" is declared more than once.`, "Give each target a unique name.");
|
|
239338
|
+
continue;
|
|
239339
|
+
}
|
|
239340
|
+
declarations.set(target.name, target);
|
|
239341
|
+
if (target.name === "api" && target.kind !== "api" || target.name === "jobs" && target.kind !== "jobs") {
|
|
239342
|
+
fail("delivery-reserved-target", `"${target.name}" is reserved for its matching workload kind.`, "Rename the target or use the matching kind.");
|
|
239343
|
+
}
|
|
239344
|
+
for (const name of sorted(target.modules)) {
|
|
239345
|
+
if (!modules.has(name)) {
|
|
239346
|
+
fail("delivery-unknown-module", `Target "${target.name}" refers to unknown module "${name}".`, "Select a module name from the compiler graph.");
|
|
239347
|
+
continue;
|
|
239348
|
+
}
|
|
239349
|
+
const owners = target.kind === "jobs" ? jobOwners : httpOwners;
|
|
239350
|
+
const existing = owners.get(name);
|
|
239351
|
+
if (existing !== undefined) {
|
|
239352
|
+
fail("delivery-ownership-conflict", `Module "${name}" has competing ${target.kind === "jobs" ? "job" : "HTTP"} owners "${existing}" and "${target.name}".`, "Assign each module's HTTP routes and jobs to at most one target of each workload category.");
|
|
239353
|
+
} else
|
|
239354
|
+
owners.set(name, target.name);
|
|
239355
|
+
}
|
|
239356
|
+
}
|
|
239357
|
+
const targets = new Map;
|
|
239358
|
+
function getTarget(name, fallback) {
|
|
239359
|
+
const existing = targets.get(name);
|
|
239360
|
+
if (existing)
|
|
239361
|
+
return existing;
|
|
239362
|
+
const declaration = declarations.get(name);
|
|
239363
|
+
const kind = declaration?.kind ?? fallback;
|
|
239364
|
+
const isolation = declaration?.isolation ?? (kind === "jobs" ? "process" : "shared");
|
|
239365
|
+
const target = {
|
|
239366
|
+
name,
|
|
239367
|
+
kind,
|
|
239368
|
+
isolation,
|
|
239369
|
+
roots: [],
|
|
239370
|
+
modules: [],
|
|
239371
|
+
routes: [],
|
|
239372
|
+
jobs: [],
|
|
239373
|
+
externalTokens: [],
|
|
239374
|
+
requirements: {
|
|
239375
|
+
processIsolation: isolation === "process",
|
|
239376
|
+
durableQueue: kind === "jobs",
|
|
239377
|
+
capabilities: sorted(declaration?.capabilities ?? [])
|
|
239378
|
+
},
|
|
239379
|
+
runtimeStatus: options.runtime === undefined ? "unchecked" : "declared-compatible"
|
|
239380
|
+
};
|
|
239381
|
+
targets.set(name, target);
|
|
239382
|
+
return target;
|
|
239383
|
+
}
|
|
239384
|
+
const publicRoutes = new Map;
|
|
239385
|
+
const publicJobs = new Map;
|
|
239386
|
+
for (const module of [...graph.modules].sort((a, b) => compare2(a.name, b.name))) {
|
|
239387
|
+
for (const controller of module.controllers) {
|
|
239388
|
+
for (const route of controller.routes) {
|
|
239389
|
+
const owner = httpOwners.get(module.name);
|
|
239390
|
+
const target = getTarget(owner ?? "api", "api");
|
|
239391
|
+
const path = joinRoutePaths(controller.path, route.path);
|
|
239392
|
+
const key = `${route.method} ${path.replace(/:[^/]+/g, ":param")}`;
|
|
239393
|
+
const existing = publicRoutes.get(key);
|
|
239394
|
+
if (existing !== undefined) {
|
|
239395
|
+
fail("delivery-route-conflict", `Public route "${key}" is owned by both "${existing}" and "${target.name}".`, "Give public routes unambiguous method/path contracts before splitting targets.");
|
|
239396
|
+
}
|
|
239397
|
+
publicRoutes.set(key, target.name);
|
|
239398
|
+
target.roots.push(module.name);
|
|
239399
|
+
target.routes.push({
|
|
239400
|
+
module: module.name,
|
|
239401
|
+
controller: controller.className,
|
|
239402
|
+
handler: route.handler,
|
|
239403
|
+
method: route.method,
|
|
239404
|
+
path,
|
|
239405
|
+
...route.command === undefined ? {} : { command: route.command },
|
|
239406
|
+
reason: owner === undefined ? "default-http" : "explicit-module"
|
|
239407
|
+
});
|
|
239408
|
+
}
|
|
239409
|
+
}
|
|
239410
|
+
for (const job of module.jobs ?? []) {
|
|
239411
|
+
const owner = jobOwners.get(module.name);
|
|
239412
|
+
const target = getTarget(owner ?? "jobs", "jobs");
|
|
239413
|
+
if (publicJobs.has(job.name)) {
|
|
239414
|
+
fail("delivery-job-conflict", `Job "${job.name}" is declared more than once.`, "Use globally unique job names.");
|
|
239415
|
+
}
|
|
239416
|
+
publicJobs.set(job.name, target.name);
|
|
239417
|
+
target.roots.push(module.name);
|
|
239418
|
+
target.jobs.push({
|
|
239419
|
+
module: module.name,
|
|
239420
|
+
name: job.name,
|
|
239421
|
+
className: job.className,
|
|
239422
|
+
serviceKey: job.serviceKey,
|
|
239423
|
+
reason: owner === undefined ? "declared-job" : "explicit-module"
|
|
239424
|
+
});
|
|
239425
|
+
}
|
|
239426
|
+
}
|
|
239427
|
+
for (const declaration of declarations.values()) {
|
|
239428
|
+
if (!targets.has(declaration.name)) {
|
|
239429
|
+
fail("delivery-empty-target", `Target "${declaration.name}" owns no ${declaration.kind === "jobs" ? "jobs" : "HTTP routes"}.`, "Select modules containing the intended workload or remove the target declaration.");
|
|
239430
|
+
}
|
|
239431
|
+
}
|
|
239432
|
+
if (targets.size === 0 && declarations.size === 0)
|
|
239433
|
+
getTarget("api", "api");
|
|
239434
|
+
for (const target of targets.values()) {
|
|
239435
|
+
let visit = function(name) {
|
|
239436
|
+
if (visiting.has(name)) {
|
|
239437
|
+
fail("delivery-import-cycle", `Target "${target.name}" contains an import cycle at "${name}".`, "Remove cyclic module imports.");
|
|
239438
|
+
return;
|
|
239439
|
+
}
|
|
239440
|
+
if (closure.has(name))
|
|
239441
|
+
return;
|
|
239442
|
+
const module = modules.get(name);
|
|
239443
|
+
if (!module) {
|
|
239444
|
+
fail("delivery-missing-import", `Target "${target.name}" requires missing module "${name}".`, "Declare or correct the imported module.");
|
|
239445
|
+
return;
|
|
239446
|
+
}
|
|
239447
|
+
visiting.add(name);
|
|
239448
|
+
for (const imported of sorted(module.imports))
|
|
239449
|
+
visit(imported);
|
|
239450
|
+
visiting.delete(name);
|
|
239451
|
+
closure.set(name, module);
|
|
239452
|
+
};
|
|
239453
|
+
target.roots = sorted(target.roots);
|
|
239454
|
+
const closure = new Map;
|
|
239455
|
+
const visiting = new Set;
|
|
239456
|
+
for (const root of target.roots)
|
|
239457
|
+
visit(root);
|
|
239458
|
+
target.modules = sorted(closure.keys()).map((name) => ({
|
|
239459
|
+
name,
|
|
239460
|
+
reason: target.roots.includes(name) ? "owner" : "dependency",
|
|
239461
|
+
importedBy: sorted([...closure.values()].filter((module) => module.imports.includes(name)).map((module) => module.name))
|
|
239462
|
+
}));
|
|
239463
|
+
const dependencies = new Set([...closure.values()].flatMap((module) => [
|
|
239464
|
+
...module.providers.flatMap((provider) => provider.deps),
|
|
239465
|
+
...module.controllers.flatMap((controller) => controller.deps)
|
|
239466
|
+
]));
|
|
239467
|
+
target.externalTokens = sorted(graph.externalTokens.filter((token) => dependencies.has(token)));
|
|
239468
|
+
target.routes.sort((a, b) => compare2(JSON.stringify(a), JSON.stringify(b)));
|
|
239469
|
+
target.jobs.sort((a, b) => compare2(JSON.stringify(a), JSON.stringify(b)));
|
|
239470
|
+
const runtime = options.runtime;
|
|
239471
|
+
if (target.requirements.durableQueue && runtime?.durableQueue !== true) {
|
|
239472
|
+
fail("delivery-queue-required", `Target "${target.name}" requires a declared durable queue.`, "Configure a durable queue adapter and declare runtime.durableQueue; do not run jobs in an HTTP background Promise.");
|
|
239473
|
+
}
|
|
239474
|
+
if (target.requirements.processIsolation && runtime?.processIsolation !== true) {
|
|
239475
|
+
fail("delivery-isolation-required", `Target "${target.name}" requires declared process isolation.`, "Select a process-isolated host and declare runtime.processIsolation. A Worker or function name alone is insufficient.");
|
|
239476
|
+
}
|
|
239477
|
+
for (const capability of target.requirements.capabilities) {
|
|
239478
|
+
if (!runtime?.capabilities.includes(capability)) {
|
|
239479
|
+
fail("delivery-capability-required", `Target "${target.name}" requires capability reference "${capability}".`, "Configure the named adapter or credential boundary and declare its reference, never its secret value.");
|
|
239480
|
+
}
|
|
239481
|
+
}
|
|
239482
|
+
}
|
|
239483
|
+
const canonicalDiagnostics = [...new Map(diagnostics.map((item) => [JSON.stringify(item), item])).values()].sort((a, b) => compare2(JSON.stringify(a), JSON.stringify(b)));
|
|
239484
|
+
if (canonicalDiagnostics.some((item) => item.severity === "error")) {
|
|
239485
|
+
return parseDeliveryPlanResult({ ok: false, plan: null, diagnostics: canonicalDiagnostics, written: [] });
|
|
239486
|
+
}
|
|
239487
|
+
const topology = {
|
|
239488
|
+
schemaVersion: 1,
|
|
239489
|
+
policyVersion: "module-workload-v1",
|
|
239490
|
+
digestScope: "topology-only",
|
|
239491
|
+
deploymentReady: false,
|
|
239492
|
+
targets: [...targets.values()].sort((a, b) => compare2(a.name, b.name))
|
|
239493
|
+
};
|
|
239494
|
+
const topologyDigest = createHash62("sha256").update(JSON.stringify(topology)).digest("hex");
|
|
239495
|
+
return parseDeliveryPlanResult({
|
|
239496
|
+
ok: true,
|
|
239497
|
+
plan: { ...topology, topologyDigest },
|
|
239498
|
+
diagnostics: canonicalDiagnostics,
|
|
239499
|
+
written: []
|
|
239500
|
+
});
|
|
239501
|
+
}
|
|
239502
|
+
async function planDeliveryProject(options, delivery) {
|
|
239503
|
+
try {
|
|
239504
|
+
parseDeliveryOptions(delivery);
|
|
239505
|
+
} catch (error) {
|
|
239506
|
+
if (!(error instanceof DeliveryConfigurationError))
|
|
239507
|
+
throw error;
|
|
239508
|
+
return createDeliveryPlan({ modules: [], externalTokens: [] }, delivery);
|
|
239509
|
+
}
|
|
239510
|
+
const checked = await checkProject(options);
|
|
239511
|
+
return createDeliveryPlan({ ...checked.graph, diagnostics: checked.diagnostics }, delivery);
|
|
239512
|
+
}
|
|
239513
|
+
function formatDeliveryPlan(result) {
|
|
239514
|
+
const lines = result.ok ? [
|
|
239515
|
+
`DELIVERY PLAN ${result.plan.topologyDigest}`,
|
|
239516
|
+
"Preview only: topology digest is not a build hash or deployment approval.",
|
|
239517
|
+
...result.plan.targets.flatMap((target) => [
|
|
239518
|
+
`${target.name} (${target.kind}, ${target.isolation}): ${target.routes.length} route(s), ${target.jobs.length} job(s)`,
|
|
239519
|
+
` modules: ${target.modules.map((module) => module.name).join(", ") || "-"}`,
|
|
239520
|
+
...target.routes.map((route) => ` ${route.method} ${route.path} [${route.reason}]`),
|
|
239521
|
+
...target.jobs.map((job) => ` job ${job.name} [${job.reason}]`)
|
|
239522
|
+
])
|
|
239523
|
+
] : ["Delivery planning failed; no files written."];
|
|
239524
|
+
return [...lines, ...result.diagnostics.map((item) => `${item.severity} ${item.code}: ${item.message}`)].join(`
|
|
239525
|
+
`);
|
|
239526
|
+
}
|
|
239527
|
+
var digest3 = (value) => createHash72("sha256").update(value).digest("hex");
|
|
239528
|
+
function canonical22(value) {
|
|
239529
|
+
return JSON.stringify(value, (_key, item) => item !== null && typeof item === "object" && !Array.isArray(item) ? Object.fromEntries(Object.entries(item).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) : item);
|
|
239530
|
+
}
|
|
239531
|
+
function inside2(parent, path) {
|
|
239532
|
+
const part = relative52(parent, path);
|
|
239533
|
+
return !isAbsolute4(part) && part !== ".." && !part.startsWith(`..${sep52}`);
|
|
239534
|
+
}
|
|
239535
|
+
async function exists2(path) {
|
|
239536
|
+
try {
|
|
239537
|
+
await lstat2(path);
|
|
239538
|
+
return true;
|
|
239539
|
+
} catch (error) {
|
|
239540
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
239541
|
+
return false;
|
|
239542
|
+
throw error;
|
|
239543
|
+
}
|
|
239544
|
+
}
|
|
239545
|
+
async function noLinks2(anchor, path) {
|
|
239546
|
+
if (!inside2(anchor, path))
|
|
239547
|
+
throw new Error("Delivery path escapes its owned root.");
|
|
239548
|
+
let current = anchor;
|
|
239549
|
+
for (const part of relative52(anchor, path).split(sep52).filter(Boolean)) {
|
|
239550
|
+
current = join52(current, part);
|
|
239551
|
+
if (!await exists2(current))
|
|
239552
|
+
continue;
|
|
239553
|
+
if ((await lstat2(current)).isSymbolicLink())
|
|
239554
|
+
throw new Error("Delivery paths cannot contain symbolic links.");
|
|
239555
|
+
}
|
|
239556
|
+
}
|
|
239557
|
+
async function readOwned(anchor, path) {
|
|
239558
|
+
await noLinks2(anchor, path);
|
|
239559
|
+
const stat = await lstat2(path);
|
|
239560
|
+
if (!stat.isFile())
|
|
239561
|
+
throw new Error("Expected a regular delivery input or artifact.");
|
|
239562
|
+
return readFile3(path);
|
|
239563
|
+
}
|
|
239564
|
+
async function readBoundedOwned2(root, path, maximum) {
|
|
239565
|
+
await noLinks2(root, path);
|
|
239566
|
+
const file = await open3(path, constants2.O_RDONLY | constants2.O_NONBLOCK | constants2.O_NOFOLLOW);
|
|
239567
|
+
try {
|
|
239568
|
+
const stat = await file.stat();
|
|
239569
|
+
if (!stat.isFile() || stat.size > maximum)
|
|
239570
|
+
throw new Error("Invalid artifact size.");
|
|
239571
|
+
const buffer = Buffer.alloc(stat.size + 1);
|
|
239572
|
+
let length = 0;
|
|
239573
|
+
while (length < buffer.length) {
|
|
239574
|
+
const { bytesRead } = await file.read(buffer, length, buffer.length - length, length);
|
|
239575
|
+
if (bytesRead === 0)
|
|
239576
|
+
break;
|
|
239577
|
+
length += bytesRead;
|
|
239578
|
+
}
|
|
239579
|
+
if (length !== stat.size)
|
|
239580
|
+
throw new Error("Artifact changed while reading.");
|
|
239581
|
+
return buffer.subarray(0, length);
|
|
239582
|
+
} finally {
|
|
239583
|
+
await file.close();
|
|
239584
|
+
}
|
|
239585
|
+
}
|
|
239586
|
+
async function reserveOutput(project, requested) {
|
|
239587
|
+
if (!inside2(project, requested))
|
|
239588
|
+
throw new Error("Delivery output must be inside the application project.");
|
|
239589
|
+
const base = await realpath4(project);
|
|
239590
|
+
const path = resolve62(base, relative52(project, requested));
|
|
239591
|
+
if (relative52(base, path).split(sep52).some((part) => part === ".git" || part === "node_modules")) {
|
|
239592
|
+
throw new Error("Delivery output cannot use repository or dependency internals.");
|
|
239593
|
+
}
|
|
239594
|
+
await noLinks2(base, path);
|
|
239595
|
+
await mkdir22(path, { recursive: true });
|
|
239596
|
+
const marker = join52(path, "owner.json");
|
|
239597
|
+
const owner = canonical22({ producer: "@supacloud/compiler/delivery-build-v1", project: base });
|
|
239598
|
+
if (!await exists2(marker)) {
|
|
239599
|
+
if ((await readdir3(path)).length > 0)
|
|
239600
|
+
throw new Error("Refusing to adopt an unowned delivery directory.");
|
|
239601
|
+
await writeFile22(marker, owner, { flag: "wx" });
|
|
239602
|
+
} else if (new TextDecoder().decode(await readOwned(base, marker)) !== owner) {
|
|
239603
|
+
throw new Error("Delivery output is owned by a different project or producer.");
|
|
239604
|
+
}
|
|
239605
|
+
const lockPath = join52(path, "build.lock");
|
|
239606
|
+
await noLinks2(base, lockPath);
|
|
239607
|
+
const lock = await open3(lockPath, "wx");
|
|
239608
|
+
return {
|
|
239609
|
+
path,
|
|
239610
|
+
async release() {
|
|
239611
|
+
await lock.close();
|
|
239612
|
+
await rm3(lockPath);
|
|
239613
|
+
}
|
|
239614
|
+
};
|
|
239615
|
+
}
|
|
239616
|
+
async function writeArtifact(root, path, content) {
|
|
239617
|
+
if (!Check22(DeliveryFilePathSchema2, path))
|
|
239618
|
+
throw new Error("Invalid artifact path.");
|
|
239619
|
+
const destination = join52(root, path);
|
|
239620
|
+
await noLinks2(root, destination);
|
|
239621
|
+
await mkdir22(dirname42(destination), { recursive: true });
|
|
239622
|
+
await writeFile22(destination, content, { flag: "wx" });
|
|
239623
|
+
}
|
|
239624
|
+
async function artifactInventory(root) {
|
|
239625
|
+
const files = [];
|
|
239626
|
+
async function visit(directory) {
|
|
239627
|
+
for (const entry of await readdir3(directory, { withFileTypes: true })) {
|
|
239628
|
+
const path = join52(directory, entry.name);
|
|
239629
|
+
if (entry.isSymbolicLink())
|
|
239630
|
+
throw new Error("Artifact contains a symbolic link.");
|
|
239631
|
+
if (entry.isDirectory())
|
|
239632
|
+
await visit(path);
|
|
239633
|
+
else {
|
|
239634
|
+
const content = await readOwned(root, path);
|
|
239635
|
+
files.push({ path: relative52(root, path).split(sep52).join("/"), sha256: digest3(content), bytes: content.length });
|
|
239636
|
+
}
|
|
239637
|
+
}
|
|
239638
|
+
}
|
|
239639
|
+
await visit(root);
|
|
239640
|
+
return files.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
239641
|
+
}
|
|
239642
|
+
async function sourceInventory(root, output) {
|
|
239643
|
+
const inputs = new Map;
|
|
239644
|
+
async function visit(directory) {
|
|
239645
|
+
for (const entry of await readdir3(directory, { withFileTypes: true })) {
|
|
239646
|
+
const path = join52(directory, entry.name);
|
|
239647
|
+
if (inside2(output, path) || entry.name === "node_modules" || entry.name === ".git")
|
|
239648
|
+
continue;
|
|
239649
|
+
if (entry.isDirectory())
|
|
239650
|
+
await visit(path);
|
|
239651
|
+
else if (/\.(?:[cm]?[jt]sx?|json)$/.test(entry.name)) {
|
|
239652
|
+
inputs.set(path, digest3(await readOwned(root, path)));
|
|
239653
|
+
}
|
|
239654
|
+
}
|
|
239655
|
+
}
|
|
239656
|
+
await visit(root);
|
|
239657
|
+
return inputs;
|
|
239658
|
+
}
|
|
239659
|
+
async function publishPointer(root, content) {
|
|
239660
|
+
const path = join52(root, "delivery.manifest.json");
|
|
239661
|
+
if (await exists2(path) && new TextDecoder().decode(await readOwned(root, path)) === content)
|
|
239662
|
+
return false;
|
|
239663
|
+
const temporary = join52(root, "delivery.manifest.pending");
|
|
239664
|
+
await noLinks2(root, path);
|
|
239665
|
+
await noLinks2(root, temporary);
|
|
239666
|
+
await writeFile22(temporary, content, { flag: "wx" });
|
|
239667
|
+
try {
|
|
239668
|
+
await rename22(temporary, path);
|
|
239669
|
+
} finally {
|
|
239670
|
+
await rm3(temporary, { force: true });
|
|
239671
|
+
}
|
|
239672
|
+
return true;
|
|
239673
|
+
}
|
|
239278
239674
|
var closed2 = { additionalProperties: false };
|
|
239279
239675
|
var digest22 = String22({ pattern: "^[a-f0-9]{64}$" });
|
|
239280
239676
|
var DeliveryObjectSchema2 = _Object_3({
|
|
@@ -239328,6 +239724,45 @@ var DeliveryBuildResultSchema2 = Union4([
|
|
|
239328
239724
|
manifest: Null3()
|
|
239329
239725
|
}, closed2)
|
|
239330
239726
|
]);
|
|
239727
|
+
function deliveryObjectDigest2(object) {
|
|
239728
|
+
return digest3(canonical22({
|
|
239729
|
+
inputDigest: object.inputDigest,
|
|
239730
|
+
files: object.files,
|
|
239731
|
+
...object.entryKind !== "compiled-module-factory" ? { entryKind: object.entryKind } : {}
|
|
239732
|
+
}));
|
|
239733
|
+
}
|
|
239734
|
+
function parseDeliveryBuildManifest2(value) {
|
|
239735
|
+
if (!Check22(DeliveryBuildManifestSchema2, value))
|
|
239736
|
+
throw new Error("Invalid delivery build manifest.");
|
|
239737
|
+
if (new Set(value.objects.map((item) => item.name)).size !== value.objects.length) {
|
|
239738
|
+
throw new Error("Duplicate delivery object owner.");
|
|
239739
|
+
}
|
|
239740
|
+
if (canonical22(value.objects.map((item) => item.name).sort()) !== canonical22(value.plan.targets.map((item) => item.name).sort())) {
|
|
239741
|
+
throw new Error("Delivery objects do not match planned targets.");
|
|
239742
|
+
}
|
|
239743
|
+
for (const object of value.objects) {
|
|
239744
|
+
const target = value.plan.targets.find((target) => target.name === object.name);
|
|
239745
|
+
if (object.entryKind === "bun-worker-application" && target?.kind !== "jobs" || object.entryKind === "bun-http-application" && target?.kind === "jobs") {
|
|
239746
|
+
throw new Error("Delivery executable kind does not match its target.");
|
|
239747
|
+
}
|
|
239748
|
+
if (new Set(object.files.map((file) => file.path)).size !== object.files.length || !object.files.some((file) => file.path === object.entrypoint) || deliveryObjectDigest2(object) !== object.objectId) {
|
|
239749
|
+
throw new Error("Invalid delivery object inventory.");
|
|
239750
|
+
}
|
|
239751
|
+
}
|
|
239752
|
+
const routes = value.plan.targets.flatMap((target) => target.routes.map((route) => ({ method: route.method, path: route.path, target: target.name })));
|
|
239753
|
+
const jobs = value.plan.targets.flatMap((target) => target.jobs.map((job) => ({ name: job.name, target: target.name })));
|
|
239754
|
+
if (canonical22(value.routes) !== canonical22(routes) || canonical22(value.jobs) !== canonical22(jobs)) {
|
|
239755
|
+
throw new Error("Delivery mappings do not match planned ownership.");
|
|
239756
|
+
}
|
|
239757
|
+
return value;
|
|
239758
|
+
}
|
|
239759
|
+
function parseDeliveryBuildResult(value) {
|
|
239760
|
+
if (!Check22(DeliveryBuildResultSchema2, value))
|
|
239761
|
+
throw new Error("Invalid delivery build result.");
|
|
239762
|
+
if (value.ok)
|
|
239763
|
+
parseDeliveryBuildManifest2(value.manifest);
|
|
239764
|
+
return value;
|
|
239765
|
+
}
|
|
239331
239766
|
init_generate();
|
|
239332
239767
|
var Metrics22 = {
|
|
239333
239768
|
assign: 0,
|
|
@@ -244749,6 +245184,7 @@ var SnapshotSchema = _Object_22({
|
|
|
244749
245184
|
}, closed22))
|
|
244750
245185
|
}, closed22))
|
|
244751
245186
|
}, closed22);
|
|
245187
|
+
var EXECUTION_SNAPSHOT_MAX_BYTES = 1048576;
|
|
244752
245188
|
var indices = _Array_22(Integer32({ minimum: 0 }));
|
|
244753
245189
|
var WireSnapshotSchema = _Object_22({
|
|
244754
245190
|
version: Literal22(1),
|
|
@@ -244765,6 +245201,144 @@ var WireSnapshotSchema = _Object_22({
|
|
|
244765
245201
|
diagnostics: indices
|
|
244766
245202
|
}, closed22))
|
|
244767
245203
|
}, closed22);
|
|
245204
|
+
function executionSourceFile(file) {
|
|
245205
|
+
if (!file || isAbsolute22(file) || /^[A-Za-z][A-Za-z0-9+.-]*:/.test(file) || file.startsWith("\\") || file.split(/[\\/]/).includes("..") || file.length > 512 || /[\u0000-\u001f\u007f]/.test(file))
|
|
245206
|
+
return;
|
|
245207
|
+
return file;
|
|
245208
|
+
}
|
|
245209
|
+
function createExecutionSnapshot(graph, subjects = graph.modules.map((module) => module.name)) {
|
|
245210
|
+
return {
|
|
245211
|
+
version: 1,
|
|
245212
|
+
plans: createExecutionPlans(graph),
|
|
245213
|
+
commandAliases: graph.modules.flatMap((module) => module.commands.map((command) => ({ module: module.name, name: command.name, alias: command.className }))),
|
|
245214
|
+
contexts: subjects.map((subject) => {
|
|
245215
|
+
const context = createContextPack(graph, subject);
|
|
245216
|
+
const owner = graph.modules.find((module) => module.name === context.subject);
|
|
245217
|
+
return {
|
|
245218
|
+
subject: context.subject,
|
|
245219
|
+
aliases: [...new Set([
|
|
245220
|
+
owner.className,
|
|
245221
|
+
...owner.providers.flatMap((provider) => [provider.token, ...provider.useClass ? [provider.useClass] : []]),
|
|
245222
|
+
...owner.controllers.map((controller) => controller.className),
|
|
245223
|
+
...owner.commands.flatMap((command) => [command.name, command.className]),
|
|
245224
|
+
...(owner.jobs ?? []).flatMap((job) => [job.name, job.className, job.serviceKey]),
|
|
245225
|
+
...owner.queries.flatMap((query) => [query.name, query.className])
|
|
245226
|
+
])],
|
|
245227
|
+
modules: context.modules.map((module) => ({
|
|
245228
|
+
name: module.name,
|
|
245229
|
+
...executionSourceFile(module.file) ? { file: executionSourceFile(module.file) } : {}
|
|
245230
|
+
})),
|
|
245231
|
+
files: context.files,
|
|
245232
|
+
diagnostics: context.diagnostics.map((diagnostic) => {
|
|
245233
|
+
const repair = createDiagnosticRepairPlan([diagnostic])[0];
|
|
245234
|
+
const file = executionSourceFile(diagnostic.file);
|
|
245235
|
+
return {
|
|
245236
|
+
code: diagnostic.code,
|
|
245237
|
+
severity: diagnostic.severity,
|
|
245238
|
+
...file ? { file } : {},
|
|
245239
|
+
...Number.isSafeInteger(diagnostic.line) && diagnostic.line > 0 ? { line: diagnostic.line } : {},
|
|
245240
|
+
...repair ? { repair: { type: repair.type, readiness: repair.readiness } } : {}
|
|
245241
|
+
};
|
|
245242
|
+
})
|
|
245243
|
+
};
|
|
245244
|
+
})
|
|
245245
|
+
};
|
|
245246
|
+
}
|
|
245247
|
+
function parseExecutionSnapshot(value) {
|
|
245248
|
+
if (!Check42(WireSnapshotSchema, value))
|
|
245249
|
+
throw new Error("Invalid execution snapshot.");
|
|
245250
|
+
function select(table, indices) {
|
|
245251
|
+
return indices.map((index) => {
|
|
245252
|
+
const item = table[index];
|
|
245253
|
+
if (item === undefined)
|
|
245254
|
+
throw new Error("Invalid execution snapshot reference.");
|
|
245255
|
+
return item;
|
|
245256
|
+
});
|
|
245257
|
+
}
|
|
245258
|
+
const expanded = {
|
|
245259
|
+
version: value.version,
|
|
245260
|
+
plans: value.plans,
|
|
245261
|
+
commandAliases: value.commandAliases,
|
|
245262
|
+
contexts: value.contexts.map((context) => ({
|
|
245263
|
+
subject: context.subject,
|
|
245264
|
+
aliases: context.aliases,
|
|
245265
|
+
modules: select(value.modules, context.modules),
|
|
245266
|
+
files: select(value.files, context.files),
|
|
245267
|
+
diagnostics: select(value.diagnostics, context.diagnostics)
|
|
245268
|
+
}))
|
|
245269
|
+
};
|
|
245270
|
+
if (new Set(expanded.contexts.map((context) => context.subject)).size !== expanded.contexts.length || expanded.contexts.some((context) => [
|
|
245271
|
+
...context.files,
|
|
245272
|
+
...context.modules.flatMap((module) => module.file ?? []),
|
|
245273
|
+
...context.diagnostics.flatMap((diagnostic) => diagnostic.file ?? [])
|
|
245274
|
+
].some((file) => executionSourceFile(file) !== file))) {
|
|
245275
|
+
throw new Error("Invalid execution snapshot.");
|
|
245276
|
+
}
|
|
245277
|
+
return expanded;
|
|
245278
|
+
}
|
|
245279
|
+
function dictionary() {
|
|
245280
|
+
const values = [];
|
|
245281
|
+
const indices = new Map;
|
|
245282
|
+
return {
|
|
245283
|
+
values,
|
|
245284
|
+
add(value) {
|
|
245285
|
+
const key = JSON.stringify(value);
|
|
245286
|
+
const found = indices.get(key);
|
|
245287
|
+
if (found !== undefined)
|
|
245288
|
+
return found;
|
|
245289
|
+
const index = values.length;
|
|
245290
|
+
indices.set(key, index);
|
|
245291
|
+
values.push(value);
|
|
245292
|
+
return index;
|
|
245293
|
+
}
|
|
245294
|
+
};
|
|
245295
|
+
}
|
|
245296
|
+
function serializedExecutionSnapshot(graph) {
|
|
245297
|
+
const snapshot = createExecutionSnapshot(graph);
|
|
245298
|
+
const modules = dictionary();
|
|
245299
|
+
const files = dictionary();
|
|
245300
|
+
const diagnostics = dictionary();
|
|
245301
|
+
const contexts = snapshot.contexts.map((context) => ({
|
|
245302
|
+
subject: context.subject,
|
|
245303
|
+
aliases: context.aliases,
|
|
245304
|
+
modules: context.modules.map((module) => modules.add(module)),
|
|
245305
|
+
files: context.files.flatMap((file) => executionSourceFile(file) ?? []).map((file) => files.add(file)),
|
|
245306
|
+
diagnostics: context.diagnostics.map((diagnostic) => diagnostics.add(diagnostic))
|
|
245307
|
+
}));
|
|
245308
|
+
const wire = {
|
|
245309
|
+
version: 1,
|
|
245310
|
+
plans: snapshot.plans,
|
|
245311
|
+
commandAliases: snapshot.commandAliases,
|
|
245312
|
+
modules: modules.values,
|
|
245313
|
+
files: files.values,
|
|
245314
|
+
diagnostics: diagnostics.values,
|
|
245315
|
+
contexts
|
|
245316
|
+
};
|
|
245317
|
+
const json = JSON.stringify(wire);
|
|
245318
|
+
if (Buffer.byteLength(json, "utf8") > EXECUTION_SNAPSHOT_MAX_BYTES)
|
|
245319
|
+
throw new Error("Execution snapshot exceeds byte budget.");
|
|
245320
|
+
parseExecutionSnapshot(wire);
|
|
245321
|
+
return json;
|
|
245322
|
+
}
|
|
245323
|
+
function renderDeliveryTarget(graph, target, options) {
|
|
245324
|
+
const included = new Set(target.modules.map((module) => module.name));
|
|
245325
|
+
const projected = {
|
|
245326
|
+
...graph,
|
|
245327
|
+
modules: graph.modules.filter((module) => included.has(module.name)).map((module) => ({
|
|
245328
|
+
...module,
|
|
245329
|
+
controllers: module.controllers.map((controller) => ({
|
|
245330
|
+
...controller,
|
|
245331
|
+
routes: controller.routes.filter((route) => target.routes.some((owned) => owned.module === module.name && owned.controller === controller.className && owned.handler === route.handler && owned.method === route.method && owned.path === joinRoutePaths(controller.path, route.path)))
|
|
245332
|
+
})),
|
|
245333
|
+
jobs: (module.jobs ?? []).filter((job) => target.jobs.some((owned) => owned.module === module.name && owned.name === job.name && owned.className === job.className))
|
|
245334
|
+
})),
|
|
245335
|
+
externalTokens: target.externalTokens
|
|
245336
|
+
};
|
|
245337
|
+
return {
|
|
245338
|
+
...renderApplication(projected, { ...options, treeShakeUnusedProviders: false }),
|
|
245339
|
+
executionSnapshot: serializedExecutionSnapshot(projected)
|
|
245340
|
+
};
|
|
245341
|
+
}
|
|
244768
245342
|
var closed3 = { additionalProperties: false };
|
|
244769
245343
|
var requestSchema = _Object_22({
|
|
244770
245344
|
parentPid: Integer32({ minimum: 1 }),
|
|
@@ -244783,6 +245357,66 @@ var responseSchema2 = Union22([
|
|
|
244783
245357
|
}, closed3),
|
|
244784
245358
|
_Object_22({ ok: Literal22(false) }, closed3)
|
|
244785
245359
|
]);
|
|
245360
|
+
function parseDeliveryBundleResponse(input) {
|
|
245361
|
+
if (!Check42(responseSchema2, input))
|
|
245362
|
+
throw new Error("Invalid isolated bundle response.");
|
|
245363
|
+
return input;
|
|
245364
|
+
}
|
|
245365
|
+
async function bundleDeliveryTarget(name, code, project, generatedDirectory, options) {
|
|
245366
|
+
const worker = join72(import.meta.dir, `delivery-bundle-worker.${import.meta.path.endsWith(".ts") ? "ts" : "js"}`);
|
|
245367
|
+
const environment = {};
|
|
245368
|
+
for (const name of ["PATH", "HOME", "TMPDIR", "TMP", "TEMP", "SystemRoot"]) {
|
|
245369
|
+
const value = process.env[name];
|
|
245370
|
+
if (value !== undefined)
|
|
245371
|
+
environment[name] = value;
|
|
245372
|
+
}
|
|
245373
|
+
const child = Bun.spawn([process.execPath, "--no-env-file", worker], {
|
|
245374
|
+
cwd: project,
|
|
245375
|
+
env: environment,
|
|
245376
|
+
stdin: "pipe",
|
|
245377
|
+
stdout: "pipe",
|
|
245378
|
+
stderr: "pipe"
|
|
245379
|
+
});
|
|
245380
|
+
let interrupted = false;
|
|
245381
|
+
const terminate = () => {
|
|
245382
|
+
interrupted = true;
|
|
245383
|
+
child.kill("SIGTERM");
|
|
245384
|
+
};
|
|
245385
|
+
process.on("SIGINT", terminate);
|
|
245386
|
+
process.on("SIGTERM", terminate);
|
|
245387
|
+
const timeout = setTimeout(() => {
|
|
245388
|
+
interrupted = true;
|
|
245389
|
+
child.kill("SIGKILL");
|
|
245390
|
+
}, 300000);
|
|
245391
|
+
try {
|
|
245392
|
+
const request = { parentPid: process.pid, name, code, project, generatedDirectory, options };
|
|
245393
|
+
child.stdin.write(JSON.stringify(request));
|
|
245394
|
+
child.stdin.end();
|
|
245395
|
+
const [stdout, , status] = await Promise.all([
|
|
245396
|
+
new Response(child.stdout).text(),
|
|
245397
|
+
new Response(child.stderr).text(),
|
|
245398
|
+
child.exited
|
|
245399
|
+
]);
|
|
245400
|
+
if (interrupted || status !== 0)
|
|
245401
|
+
throw new Error("Isolated delivery bundle failed.");
|
|
245402
|
+
const response = parseDeliveryBundleResponse(JSON.parse(stdout));
|
|
245403
|
+
if (!response.ok)
|
|
245404
|
+
throw new Error("Isolated delivery bundle failed.");
|
|
245405
|
+
return {
|
|
245406
|
+
files: new Map(response.files.map(([path, bytes]) => [path, new Uint8Array(Buffer.from(bytes, "base64"))])),
|
|
245407
|
+
inputs: new Map(response.inputs),
|
|
245408
|
+
runtimeImports: response.runtimeImports
|
|
245409
|
+
};
|
|
245410
|
+
} finally {
|
|
245411
|
+
clearTimeout(timeout);
|
|
245412
|
+
process.removeListener("SIGINT", terminate);
|
|
245413
|
+
process.removeListener("SIGTERM", terminate);
|
|
245414
|
+
if (child.exitCode === null) {
|
|
245415
|
+
child.kill("SIGKILL");
|
|
245416
|
+
await child.exited;
|
|
245417
|
+
}
|
|
245418
|
+
}
|
|
245419
|
+
}
|
|
244786
245420
|
var ApplicationIdSchema3 = String22({ pattern: "^[A-Za-z0-9_-]{1,64}$" });
|
|
244787
245421
|
var ApplicationReleaseIdSchema3 = String22({ pattern: "^[a-f0-9]{64}$" });
|
|
244788
245422
|
var ApplicationReleaseRecordSchema3 = _Object_3({
|
|
@@ -244876,6 +245510,7 @@ var ApplicationActivationRetirementResultSchema3 = _Object_3({
|
|
|
244876
245510
|
activation_id: ApplicationActivationIdSchema3,
|
|
244877
245511
|
retired_at: String22()
|
|
244878
245512
|
}, { additionalProperties: false });
|
|
245513
|
+
var APPLICATION_RUNTIME_PROBE_PATH2 = "/.well-known/supacloud/runtime";
|
|
244879
245514
|
var ApplicationRuntimeIdentitySchema2 = _Object_3({
|
|
244880
245515
|
schema: Literal3("supacloud.application-runtime.v1"),
|
|
244881
245516
|
project_ref: String22({ pattern: "^[a-z0-9-]{1,20}$" }),
|
|
@@ -244930,13 +245565,627 @@ var archiveSchema2 = _Object_3({
|
|
|
244930
245565
|
dataRecovery: Literal3("separate-required"),
|
|
244931
245566
|
migrations: _Array_2(entrySchema2, { minItems: 1, maxItems: 128 })
|
|
244932
245567
|
}, { additionalProperties: false });
|
|
245568
|
+
function renderDeliveryRuntimeIdentity() {
|
|
245569
|
+
return `
|
|
245570
|
+
function deliveryRuntimeIdentity(kind: "http" | "worker") {
|
|
245571
|
+
const env = deliveryProcess.env;
|
|
245572
|
+
if (env.SUPACLOUD_ACTIVATION_ID === undefined) return null;
|
|
245573
|
+
const identity = {
|
|
245574
|
+
schema: "supacloud.application-runtime.v1",
|
|
245575
|
+
project_ref: env.SUPACLOUD_PROJECT_REF,
|
|
245576
|
+
application_id: env.SUPACLOUD_APPLICATION_ID,
|
|
245577
|
+
environment_id: env.SUPACLOUD_ENVIRONMENT_ID,
|
|
245578
|
+
release_id: env.SUPACLOUD_RELEASE_ID,
|
|
245579
|
+
activation_id: env.SUPACLOUD_ACTIVATION_ID,
|
|
245580
|
+
target: env.SUPACLOUD_TARGET,
|
|
245581
|
+
object_id: env.SUPACLOUD_OBJECT_ID,
|
|
245582
|
+
kind, pid: deliveryProcess.pid,
|
|
245583
|
+
};
|
|
245584
|
+
const matches = (value: string | undefined, pattern: RegExp) => typeof value === "string" && pattern.test(value);
|
|
245585
|
+
if (!matches(identity.project_ref, /^[a-z0-9-]{1,20}$/)
|
|
245586
|
+
|| !matches(identity.application_id, /^[A-Za-z0-9_-]{1,64}$/)
|
|
245587
|
+
|| !matches(identity.environment_id, /^[A-Za-z0-9_-]{1,64}$/)
|
|
245588
|
+
|| !matches(identity.release_id, /^[a-f0-9]{64}$/)
|
|
245589
|
+
|| !matches(identity.activation_id, /^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/)
|
|
245590
|
+
|| !matches(identity.target, /^[a-z][a-z0-9-]{0,62}$/)
|
|
245591
|
+
|| !matches(identity.object_id, /^[a-f0-9]{64}$/)) throw new Error("Invalid managed runtime identity.");
|
|
245592
|
+
return identity;
|
|
245593
|
+
}
|
|
245594
|
+
`;
|
|
245595
|
+
}
|
|
245596
|
+
function renderDeliveryHttpEntry(generatedDirectory, hostPath) {
|
|
245597
|
+
const specifier = relativeImportPath(generatedDirectory, hostPath);
|
|
245598
|
+
return `
|
|
245599
|
+
import { serve as serveDeliveryHttp } from "bun";
|
|
245600
|
+
import deliveryProcess from "node:process";
|
|
245601
|
+
import { createDeliveryApplication } from ${JSON.stringify(specifier)};
|
|
245602
|
+
${renderDeliveryRuntimeIdentity()}
|
|
245603
|
+
|
|
245604
|
+
interface DeliveryHttpHost {
|
|
245605
|
+
fetch(request: Request): Response | Promise<Response>;
|
|
245606
|
+
close(): void | Promise<void>;
|
|
245607
|
+
ready?(): boolean | Promise<boolean>;
|
|
245608
|
+
}
|
|
245609
|
+
|
|
245610
|
+
async function startDeliveryHttp(signal: AbortSignal, shutdownTimeout: number, startDeadline: () => void) {
|
|
245611
|
+
const identity = deliveryRuntimeIdentity("http");
|
|
245612
|
+
const port = Number(deliveryProcess.env.PORT ?? "3000");
|
|
245613
|
+
const hostname = deliveryProcess.env.HOST ?? "127.0.0.1";
|
|
245614
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535 || hostname.length === 0) {
|
|
245615
|
+
throw new Error("Invalid delivery HTTP listener configuration.");
|
|
245616
|
+
}
|
|
245617
|
+
const createHost: (
|
|
245618
|
+
modules: ReturnType<typeof createCompiledModules>, lifecycle: { signal: AbortSignal },
|
|
245619
|
+
) => DeliveryHttpHost | Promise<DeliveryHttpHost> = createDeliveryApplication;
|
|
245620
|
+
const host = await createHost(createCompiledModules(), { signal });
|
|
245621
|
+
if (!host || typeof host.close !== "function") {
|
|
245622
|
+
throw new Error("Invalid delivery HTTP host.");
|
|
245623
|
+
}
|
|
245624
|
+
let closing: Promise<void> | undefined;
|
|
245625
|
+
const closeHost = () => {
|
|
245626
|
+
startDeadline();
|
|
245627
|
+
return closing ??= Promise.resolve().then(() => host.close());
|
|
245628
|
+
};
|
|
245629
|
+
if (typeof host.fetch !== "function") {
|
|
245630
|
+
await closeHost();
|
|
245631
|
+
throw new Error("Invalid delivery HTTP host.");
|
|
245632
|
+
}
|
|
245633
|
+
if (host.ready !== undefined && typeof host.ready !== "function") {
|
|
245634
|
+
await closeHost();
|
|
245635
|
+
throw new Error("Invalid delivery HTTP readiness hook.");
|
|
245636
|
+
}
|
|
245637
|
+
if (signal.aborted) { await closeHost(); return undefined; }
|
|
245638
|
+
let server: ReturnType<typeof serveDeliveryHttp>;
|
|
245639
|
+
try {
|
|
245640
|
+
server = serveDeliveryHttp({
|
|
245641
|
+
hostname, port,
|
|
245642
|
+
development: false,
|
|
245643
|
+
fetch: async (request) => {
|
|
245644
|
+
if (identity && new URL(request.url).pathname === ${JSON.stringify(APPLICATION_RUNTIME_PROBE_PATH2)}) {
|
|
245645
|
+
if (request.method !== "GET") return new Response(null, { status: 405 });
|
|
245646
|
+
const ready = !signal.aborted && (host.ready ? await host.ready() === true : true) && !signal.aborted;
|
|
245647
|
+
return Response.json({ identity, ready }, {
|
|
245648
|
+
status: ready ? 200 : 503, headers: { "cache-control": "no-store" },
|
|
245649
|
+
});
|
|
245650
|
+
}
|
|
245651
|
+
return host.fetch(request);
|
|
245652
|
+
},
|
|
245653
|
+
error: () => new Response("Internal Server Error", { status: 500 }),
|
|
245654
|
+
});
|
|
245655
|
+
} catch {
|
|
245656
|
+
await closeHost();
|
|
245657
|
+
throw new Error("Delivery HTTP listener failed.");
|
|
245658
|
+
}
|
|
245659
|
+
let stopping: Promise<void> | undefined;
|
|
245660
|
+
return {
|
|
245661
|
+
url: server.url,
|
|
245662
|
+
stop() {
|
|
245663
|
+
stopping ??= (async () => {
|
|
245664
|
+
let forced = false;
|
|
245665
|
+
const drainLimit = setTimeout(() => {
|
|
245666
|
+
forced = true;
|
|
245667
|
+
void server.stop(true).catch(() => {});
|
|
245668
|
+
}, Math.max(1, Math.floor(shutdownTimeout / 2)));
|
|
245669
|
+
// Forced connection closure is not a successful graceful shutdown.
|
|
245670
|
+
try { await server.stop(false); }
|
|
245671
|
+
finally { clearTimeout(drainLimit); await closeHost(); }
|
|
245672
|
+
if (forced) throw new Error("Delivery HTTP drain deadline exceeded.");
|
|
245673
|
+
})();
|
|
245674
|
+
return stopping;
|
|
245675
|
+
},
|
|
245676
|
+
};
|
|
245677
|
+
}
|
|
245678
|
+
|
|
245679
|
+
{
|
|
245680
|
+
const lifecycle = new AbortController();
|
|
245681
|
+
const shutdownTimeout = Number(deliveryProcess.env.SHUTDOWN_TIMEOUT_MS ?? "10000");
|
|
245682
|
+
let running: Awaited<ReturnType<typeof startDeliveryHttp>>;
|
|
245683
|
+
let deadline: ReturnType<typeof setTimeout> | undefined;
|
|
245684
|
+
const finish = (failed: boolean) => {
|
|
245685
|
+
clearTimeout(deadline);
|
|
245686
|
+
deliveryProcess.removeListener("SIGINT", requestShutdown);
|
|
245687
|
+
deliveryProcess.removeListener("SIGTERM", requestShutdown);
|
|
245688
|
+
deliveryProcess.exit(failed ? 1 : 0);
|
|
245689
|
+
};
|
|
245690
|
+
const stop = () => {
|
|
245691
|
+
void running?.stop().then(
|
|
245692
|
+
() => finish(false),
|
|
245693
|
+
() => { console.error("Delivery HTTP shutdown failed."); finish(true); },
|
|
245694
|
+
);
|
|
245695
|
+
};
|
|
245696
|
+
const startDeadline = () => {
|
|
245697
|
+
if (deadline !== undefined) return;
|
|
245698
|
+
deadline = setTimeout(() => {
|
|
245699
|
+
console.error("Delivery HTTP shutdown deadline exceeded.");
|
|
245700
|
+
finish(true);
|
|
245701
|
+
}, shutdownTimeout);
|
|
245702
|
+
};
|
|
245703
|
+
const requestShutdown = () => {
|
|
245704
|
+
if (lifecycle.signal.aborted) return;
|
|
245705
|
+
// Start the deadline before invoking user-owned abort listeners.
|
|
245706
|
+
startDeadline();
|
|
245707
|
+
lifecycle.abort();
|
|
245708
|
+
stop();
|
|
245709
|
+
};
|
|
245710
|
+
try {
|
|
245711
|
+
if (!Number.isInteger(shutdownTimeout) || shutdownTimeout < 1 || shutdownTimeout > 300000) {
|
|
245712
|
+
throw new Error("Invalid delivery HTTP shutdown configuration.");
|
|
245713
|
+
}
|
|
245714
|
+
deliveryProcess.on("SIGINT", requestShutdown);
|
|
245715
|
+
deliveryProcess.on("SIGTERM", requestShutdown);
|
|
245716
|
+
running = await startDeliveryHttp(lifecycle.signal, shutdownTimeout, startDeadline);
|
|
245717
|
+
if (lifecycle.signal.aborted) {
|
|
245718
|
+
if (running) stop();
|
|
245719
|
+
else finish(false);
|
|
245720
|
+
} else if (running) {
|
|
245721
|
+
console.log(JSON.stringify({ event: "delivery-http-listening", url: running.url.href }));
|
|
245722
|
+
}
|
|
245723
|
+
} catch {
|
|
245724
|
+
console.error("Delivery HTTP startup failed.");
|
|
245725
|
+
finish(true);
|
|
245726
|
+
}
|
|
245727
|
+
}
|
|
245728
|
+
`;
|
|
245729
|
+
}
|
|
245730
|
+
function renderDeliveryWorkerEntry(generatedDirectory, hostPath) {
|
|
245731
|
+
const specifier = relativeImportPath(generatedDirectory, hostPath);
|
|
245732
|
+
return `
|
|
245733
|
+
import deliveryProcess from "node:process";
|
|
245734
|
+
import { createDeliveryWorker } from ${JSON.stringify(specifier)};
|
|
245735
|
+
${renderDeliveryRuntimeIdentity()}
|
|
245736
|
+
|
|
245737
|
+
interface DeliveryWorkerHost {
|
|
245738
|
+
start(): void | Promise<void>;
|
|
245739
|
+
close(): void | Promise<void>;
|
|
245740
|
+
failure?: Promise<never>;
|
|
245741
|
+
}
|
|
245742
|
+
|
|
245743
|
+
{
|
|
245744
|
+
const lifecycle = new AbortController();
|
|
245745
|
+
const shutdownTimeout = Number(deliveryProcess.env.SHUTDOWN_TIMEOUT_MS ?? "10000");
|
|
245746
|
+
let host: DeliveryWorkerHost | undefined;
|
|
245747
|
+
let running = false;
|
|
245748
|
+
let runtimeFailed = false;
|
|
245749
|
+
let closing: Promise<void> | undefined;
|
|
245750
|
+
let deadline: ReturnType<typeof setTimeout> | undefined;
|
|
245751
|
+
let keepAlive: ReturnType<typeof setInterval> | undefined;
|
|
245752
|
+
const finish = (failed: boolean) => {
|
|
245753
|
+
clearTimeout(deadline);
|
|
245754
|
+
clearInterval(keepAlive);
|
|
245755
|
+
deliveryProcess.removeListener("SIGINT", requestShutdown);
|
|
245756
|
+
deliveryProcess.removeListener("SIGTERM", requestShutdown);
|
|
245757
|
+
deliveryProcess.removeListener("uncaughtException", runtimeFailure);
|
|
245758
|
+
deliveryProcess.removeListener("unhandledRejection", runtimeFailure);
|
|
245759
|
+
deliveryProcess.exit(failed ? 1 : 0);
|
|
245760
|
+
};
|
|
245761
|
+
const startDeadline = () => {
|
|
245762
|
+
if (deadline !== undefined) return;
|
|
245763
|
+
deadline = setTimeout(() => {
|
|
245764
|
+
console.error("Delivery worker shutdown deadline exceeded.");
|
|
245765
|
+
finish(true);
|
|
245766
|
+
}, shutdownTimeout);
|
|
245767
|
+
};
|
|
245768
|
+
const closeHost = () => {
|
|
245769
|
+
startDeadline();
|
|
245770
|
+
return closing ??= Promise.resolve().then(() => host?.close());
|
|
245771
|
+
};
|
|
245772
|
+
const finishShutdown = async () => {
|
|
245773
|
+
await closeHost();
|
|
245774
|
+
// Observe errors queued by abort listeners before declaring graceful shutdown.
|
|
245775
|
+
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
245776
|
+
finish(runtimeFailed);
|
|
245777
|
+
};
|
|
245778
|
+
const requestShutdown = () => {
|
|
245779
|
+
if (lifecycle.signal.aborted) return;
|
|
245780
|
+
startDeadline();
|
|
245781
|
+
try { lifecycle.abort(); }
|
|
245782
|
+
catch { runtimeFailed = true; console.error("Delivery worker runtime failed."); }
|
|
245783
|
+
// A host may need close() to unblock start(). Success still waits for start.
|
|
245784
|
+
if (host) void closeHost().catch(() => { runtimeFailed = true; });
|
|
245785
|
+
if (running) {
|
|
245786
|
+
void finishShutdown().catch(() => {
|
|
245787
|
+
console.error("Delivery worker shutdown failed.");
|
|
245788
|
+
finish(true);
|
|
245789
|
+
});
|
|
245790
|
+
}
|
|
245791
|
+
};
|
|
245792
|
+
const runtimeFailure = () => {
|
|
245793
|
+
runtimeFailed = true;
|
|
245794
|
+
console.error("Delivery worker runtime failed.");
|
|
245795
|
+
requestShutdown();
|
|
245796
|
+
};
|
|
245797
|
+
try {
|
|
245798
|
+
const identity = deliveryRuntimeIdentity("worker");
|
|
245799
|
+
if (!Number.isInteger(shutdownTimeout) || shutdownTimeout < 1 || shutdownTimeout > 300000) {
|
|
245800
|
+
throw new Error("Invalid delivery worker shutdown configuration.");
|
|
245801
|
+
}
|
|
245802
|
+
deliveryProcess.on("SIGINT", requestShutdown);
|
|
245803
|
+
deliveryProcess.on("SIGTERM", requestShutdown);
|
|
245804
|
+
deliveryProcess.on("uncaughtException", runtimeFailure);
|
|
245805
|
+
deliveryProcess.on("unhandledRejection", runtimeFailure);
|
|
245806
|
+
const createHost: (
|
|
245807
|
+
modules: ReturnType<typeof createCompiledModules>, lifecycle: { signal: AbortSignal },
|
|
245808
|
+
) => DeliveryWorkerHost | Promise<DeliveryWorkerHost> = createDeliveryWorker;
|
|
245809
|
+
const candidate = await createHost(createCompiledModules(), { signal: lifecycle.signal });
|
|
245810
|
+
if (!candidate || typeof candidate.close !== "function") throw new Error("Invalid delivery worker host.");
|
|
245811
|
+
host = candidate;
|
|
245812
|
+
if (typeof host.start !== "function") throw new Error("Invalid delivery worker host.");
|
|
245813
|
+
if (host.failure !== undefined) {
|
|
245814
|
+
if (!host.failure || typeof host.failure.then !== "function") throw new Error("Invalid worker failure signal.");
|
|
245815
|
+
// A fulfilled fatal channel is also invalid; neither outcome may leave a dead worker alive.
|
|
245816
|
+
void host.failure.then(runtimeFailure, runtimeFailure);
|
|
245817
|
+
}
|
|
245818
|
+
if (!lifecycle.signal.aborted) await host.start();
|
|
245819
|
+
if (lifecycle.signal.aborted) {
|
|
245820
|
+
await finishShutdown();
|
|
245821
|
+
} else {
|
|
245822
|
+
running = true;
|
|
245823
|
+
keepAlive = setInterval(() => {}, 1000);
|
|
245824
|
+
console.log(JSON.stringify({ event: "delivery-worker-started", ...(identity ? { identity } : {}) }));
|
|
245825
|
+
}
|
|
245826
|
+
} catch {
|
|
245827
|
+
if (host) {
|
|
245828
|
+
try { await closeHost(); }
|
|
245829
|
+
catch { console.error("Delivery worker cleanup failed."); }
|
|
245830
|
+
}
|
|
245831
|
+
console.error("Delivery worker startup failed.");
|
|
245832
|
+
finish(true);
|
|
245833
|
+
}
|
|
245834
|
+
}
|
|
245835
|
+
`;
|
|
245836
|
+
}
|
|
245837
|
+
async function prepareDeliveryMigrations(project, generatedRoot, declarations = []) {
|
|
245838
|
+
const entries = [];
|
|
245839
|
+
const files = new Map;
|
|
245840
|
+
const inputs = new Map;
|
|
245841
|
+
let totalBytes = 0;
|
|
245842
|
+
for (const declaration of [...declarations].sort((a, b) => BigInt(a.version) < BigInt(b.version) ? -1 : BigInt(a.version) > BigInt(b.version) ? 1 : 0)) {
|
|
245843
|
+
const source = resolve102(project, declaration.source);
|
|
245844
|
+
if (!inside2(project, source) || inside2(generatedRoot, source))
|
|
245845
|
+
throw new Error("Invalid migration source.");
|
|
245846
|
+
const bytes = await readBoundedOwned2(project, source, 1048576);
|
|
245847
|
+
totalBytes += bytes.length;
|
|
245848
|
+
if (totalBytes > 16777216 || !new TextDecoder("utf-8", { fatal: true }).decode(bytes).trim()) {
|
|
245849
|
+
throw new Error("Invalid migration content or byte budget.");
|
|
245850
|
+
}
|
|
245851
|
+
const hash = digest3(bytes);
|
|
245852
|
+
const path = `bundle/migrations/${declaration.executor}/${declaration.version}_${declaration.name}.sql`;
|
|
245853
|
+
entries.push({
|
|
245854
|
+
version: declaration.version,
|
|
245855
|
+
name: declaration.name,
|
|
245856
|
+
executor: declaration.executor,
|
|
245857
|
+
path: path.slice("bundle/".length),
|
|
245858
|
+
sha256: hash,
|
|
245859
|
+
bytes: bytes.length
|
|
245860
|
+
});
|
|
245861
|
+
files.set(path, bytes);
|
|
245862
|
+
inputs.set(source, hash);
|
|
245863
|
+
}
|
|
245864
|
+
if (entries.length) {
|
|
245865
|
+
files.set("bundle/migrations.json", new TextEncoder().encode(canonical22({
|
|
245866
|
+
version: 1,
|
|
245867
|
+
digestScope: "raw-sql-bytes",
|
|
245868
|
+
compatibility: "not-proven",
|
|
245869
|
+
executionPerformed: false,
|
|
245870
|
+
dataRecovery: "separate-required",
|
|
245871
|
+
migrations: entries
|
|
245872
|
+
})));
|
|
245873
|
+
}
|
|
245874
|
+
return { entries, files, inputs };
|
|
245875
|
+
}
|
|
245876
|
+
var producer = "@supacloud/compiler/delivery-build-v1";
|
|
245877
|
+
async function buildDeliveryProject(options, delivery) {
|
|
245878
|
+
const bundledTargets = [];
|
|
245879
|
+
let resultDiagnostics = [];
|
|
245880
|
+
const failed = (diagnostics) => {
|
|
245881
|
+
resultDiagnostics = [...diagnostics];
|
|
245882
|
+
return { ok: false, manifest: null, diagnostics: resultDiagnostics, written: [], bundledTargets };
|
|
245883
|
+
};
|
|
245884
|
+
let phase = "configuration";
|
|
245885
|
+
let output;
|
|
245886
|
+
const temporary = [];
|
|
245887
|
+
try {
|
|
245888
|
+
const settings = parseDeliveryOptions(delivery);
|
|
245889
|
+
if (typeof Bun === "undefined")
|
|
245890
|
+
throw new Error("Independent delivery builds require Bun.");
|
|
245891
|
+
const configPath = ts11.findConfigFile(resolve112(options.rootDir), ts11.sys.fileExists);
|
|
245892
|
+
if (!configPath)
|
|
245893
|
+
throw new Error("Independent delivery builds require a project tsconfig.json.");
|
|
245894
|
+
const lexicalProject = dirname7(configPath);
|
|
245895
|
+
const project = await realpath3(lexicalProject);
|
|
245896
|
+
const sourceRoot = await realpath3(options.rootDir);
|
|
245897
|
+
if (!inside2(project, sourceRoot))
|
|
245898
|
+
throw new Error("Application sources must be inside the project.");
|
|
245899
|
+
const requested = resolve112(options.outDir, "delivery");
|
|
245900
|
+
if (!inside2(lexicalProject, requested))
|
|
245901
|
+
throw new Error("Output must be inside the application project.");
|
|
245902
|
+
const buildRoot = resolve112(project, relative7(lexicalProject, requested));
|
|
245903
|
+
const generatedRoot = resolve112(project, relative7(lexicalProject, options.outDir));
|
|
245904
|
+
if (inside2(generatedRoot, sourceRoot))
|
|
245905
|
+
throw new Error("Output must not contain the application source root.");
|
|
245906
|
+
const configInputs = new Map;
|
|
245907
|
+
const parsedConfig = ts11.getParsedCommandLineOfConfigFile(await realpath3(configPath), {}, {
|
|
245908
|
+
...ts11.sys,
|
|
245909
|
+
readFile(path) {
|
|
245910
|
+
const contents = readFileSync32(path, "utf8");
|
|
245911
|
+
configInputs.set(resolve112(path), digest3(contents));
|
|
245912
|
+
return contents;
|
|
245913
|
+
},
|
|
245914
|
+
onUnRecoverableConfigFileDiagnostic() {
|
|
245915
|
+
throw new Error("Invalid project TypeScript configuration.");
|
|
245916
|
+
}
|
|
245917
|
+
});
|
|
245918
|
+
if (!parsedConfig || parsedConfig.errors.length > 0)
|
|
245919
|
+
throw new Error("Invalid project TypeScript configuration.");
|
|
245920
|
+
phase = "compiler-checks";
|
|
245921
|
+
const sources = await sourceInventory(sourceRoot, generatedRoot);
|
|
245922
|
+
const checked = await checkProject(options);
|
|
245923
|
+
const planned = createDeliveryPlan({ ...checked.graph, diagnostics: checked.diagnostics }, settings);
|
|
245924
|
+
if (!planned.ok)
|
|
245925
|
+
return failed(planned.diagnostics);
|
|
245926
|
+
if (options.graphql && checked.mismatches.some((mismatch) => mismatch.startsWith("graphql"))) {
|
|
245927
|
+
throw new Error("Refresh GraphQL artifacts with compile before building independent targets.");
|
|
245928
|
+
}
|
|
245929
|
+
const assets = settings.build?.assets ?? [];
|
|
245930
|
+
for (const asset of assets) {
|
|
245931
|
+
if (!planned.plan.targets.some((target) => target.name === asset.target)) {
|
|
245932
|
+
throw new Error("An asset refers to an unknown delivery target.");
|
|
245933
|
+
}
|
|
245934
|
+
}
|
|
245935
|
+
const assetOwners = assets.map((asset) => `${asset.target}/${asset.path}`);
|
|
245936
|
+
if (new Set(assetOwners).size !== assetOwners.length)
|
|
245937
|
+
throw new Error("Duplicate target asset path.");
|
|
245938
|
+
const httpApplications = settings.build?.httpApplications ?? [];
|
|
245939
|
+
const workerApplications = settings.build?.workerApplications ?? [];
|
|
245940
|
+
const hosts = new Map;
|
|
245941
|
+
for (const [kind, applications] of [["http", httpApplications], ["worker", workerApplications]]) {
|
|
245942
|
+
for (const app of applications) {
|
|
245943
|
+
if (hosts.has(app.target))
|
|
245944
|
+
throw new Error("Duplicate delivery application host.");
|
|
245945
|
+
const target = planned.plan.targets.find((target) => target.name === app.target);
|
|
245946
|
+
if (!target || (kind === "worker" ? target.kind !== "jobs" : target.kind === "jobs")) {
|
|
245947
|
+
throw new Error("Delivery host kind does not match its target.");
|
|
245948
|
+
}
|
|
245949
|
+
const path = resolve112(sourceRoot, app.source);
|
|
245950
|
+
if (inside2(generatedRoot, path) || !/\.[cm]?ts$/.test(path))
|
|
245951
|
+
throw new Error("Hosts require application TypeScript source.");
|
|
245952
|
+
hosts.set(app.target, { path, kind, hash: digest3(await readOwned(sourceRoot, path)) });
|
|
245953
|
+
}
|
|
245954
|
+
}
|
|
245955
|
+
phase = "migration-inputs";
|
|
245956
|
+
const migrations = await prepareDeliveryMigrations(project, generatedRoot, settings.build?.migrations);
|
|
245957
|
+
phase = "output-ownership";
|
|
245958
|
+
output = await reserveOutput(project, buildRoot);
|
|
245959
|
+
const objectRoot = join92(output.path, "objects");
|
|
245960
|
+
await noLinks2(output.path, objectRoot);
|
|
245961
|
+
await mkdir3(objectRoot, { recursive: true });
|
|
245962
|
+
const pointer = join92(output.path, "delivery.manifest.json");
|
|
245963
|
+
const previous = await exists2(pointer) ? parseDeliveryBuildManifest2(JSON.parse(new TextDecoder().decode(await readOwned(output.path, pointer)))) : undefined;
|
|
245964
|
+
const objects = [];
|
|
245965
|
+
const candidates = [];
|
|
245966
|
+
const snapshots = new Map([...sources, ...configInputs]);
|
|
245967
|
+
for (const [path, hash] of migrations.inputs) {
|
|
245968
|
+
const previous = snapshots.get(path);
|
|
245969
|
+
if (previous !== undefined && previous !== hash)
|
|
245970
|
+
throw new Error("Migration changed during analysis.");
|
|
245971
|
+
snapshots.set(path, hash);
|
|
245972
|
+
}
|
|
245973
|
+
for (const host of hosts.values()) {
|
|
245974
|
+
const previous = snapshots.get(host.path);
|
|
245975
|
+
if (previous !== undefined && previous !== host.hash)
|
|
245976
|
+
throw new Error("Delivery host changed during analysis.");
|
|
245977
|
+
snapshots.set(host.path, host.hash);
|
|
245978
|
+
}
|
|
245979
|
+
const configuration = {};
|
|
245980
|
+
let directory = project;
|
|
245981
|
+
while (true) {
|
|
245982
|
+
for (const name of ["package.json", "bun.lock", "bun.lockb", "package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bunfig.toml", "tsconfig.json"]) {
|
|
245983
|
+
const path = join92(directory, name);
|
|
245984
|
+
if (await exists2(path)) {
|
|
245985
|
+
const bytes = await readFile5(path);
|
|
245986
|
+
const before = snapshots.get(path);
|
|
245987
|
+
if (before !== undefined && before !== digest3(bytes))
|
|
245988
|
+
throw new Error("Configuration changed during analysis; retry.");
|
|
245989
|
+
snapshots.set(path, digest3(bytes));
|
|
245990
|
+
configuration[relative7(project, path).split(sep7).join("/")] = digest3(bytes);
|
|
245991
|
+
}
|
|
245992
|
+
}
|
|
245993
|
+
const parent = dirname7(directory);
|
|
245994
|
+
if (parent === directory)
|
|
245995
|
+
break;
|
|
245996
|
+
directory = parent;
|
|
245997
|
+
}
|
|
245998
|
+
const toolchain = {
|
|
245999
|
+
producer,
|
|
246000
|
+
bun: Bun.version,
|
|
246001
|
+
revision: Bun.revision,
|
|
246002
|
+
compiler: digest3(canonical22(Object.fromEntries([...await sourceInventory(import.meta.dir, join92(import.meta.dir, "node_modules"))].map(([path, hash]) => [relative7(import.meta.dir, path), hash]))))
|
|
246003
|
+
};
|
|
246004
|
+
for (const target of planned.plan.targets) {
|
|
246005
|
+
phase = `target:${target.name}`;
|
|
246006
|
+
const stage = await mkdtemp(join92(objectRoot, ".build-"));
|
|
246007
|
+
temporary.push(stage);
|
|
246008
|
+
const generatedDirectory = join92(objectRoot, "0".repeat(64), "generated");
|
|
246009
|
+
const rendered = renderDeliveryTarget(checked.graph, target, {
|
|
246010
|
+
...options,
|
|
246011
|
+
rootDir: sourceRoot,
|
|
246012
|
+
outDir: generatedDirectory
|
|
246013
|
+
});
|
|
246014
|
+
const host = hosts.get(target.name);
|
|
246015
|
+
const entryKind = host?.kind === "http" ? "bun-http-application" : host?.kind === "worker" ? "bun-worker-application" : "compiled-module-factory";
|
|
246016
|
+
const applicationCode = rendered.applicationCode + (host?.kind === "http" ? renderDeliveryHttpEntry(generatedDirectory, host.path) : host?.kind === "worker" ? renderDeliveryWorkerEntry(generatedDirectory, host.path) : "");
|
|
246017
|
+
const generated = {
|
|
246018
|
+
"application.ts": applicationCode,
|
|
246019
|
+
...rendered.clientCode === undefined ? {} : { "client.ts": rendered.clientCode },
|
|
246020
|
+
...rendered.permissionsCode === undefined ? {} : { "permissions.ts": rendered.permissionsCode }
|
|
246021
|
+
};
|
|
246022
|
+
const unsafe = scanGeneratedArtifacts(generated, true);
|
|
246023
|
+
if (unsafe.some((item) => item.severity === "error"))
|
|
246024
|
+
return failed(unsafe);
|
|
246025
|
+
for (const [name, contents] of Object.entries(generated))
|
|
246026
|
+
await writeArtifact(stage, `generated/${name}`, contents);
|
|
246027
|
+
{
|
|
246028
|
+
const program = ts11.createProgram({
|
|
246029
|
+
rootNames: [
|
|
246030
|
+
...parsedConfig.fileNames.filter((path) => !inside2(generatedRoot, path)),
|
|
246031
|
+
...Object.keys(generated).map((name) => join92(stage, "generated", name))
|
|
246032
|
+
],
|
|
246033
|
+
options: { ...parsedConfig.options, rootDir: project },
|
|
246034
|
+
...parsedConfig.projectReferences ? { projectReferences: parsedConfig.projectReferences } : {}
|
|
246035
|
+
});
|
|
246036
|
+
const diagnostics = ts11.getPreEmitDiagnostics(program);
|
|
246037
|
+
if (diagnostics.some((item) => item.category === ts11.DiagnosticCategory.Error)) {
|
|
246038
|
+
return failed(diagnostics.filter((item) => item.category === ts11.DiagnosticCategory.Error).map((item) => ({
|
|
246039
|
+
severity: "error",
|
|
246040
|
+
code: "delivery-generated-type-error",
|
|
246041
|
+
message: ts11.flattenDiagnosticMessageText(item.messageText, `
|
|
246042
|
+
`),
|
|
246043
|
+
...item.file ? { file: item.file.fileName } : {}
|
|
246044
|
+
})));
|
|
246045
|
+
}
|
|
246046
|
+
}
|
|
246047
|
+
phase = `target:${target.name}:bundle`;
|
|
246048
|
+
const bundled = await bundleDeliveryTarget(target.name, applicationCode, project, generatedDirectory, settings);
|
|
246049
|
+
phase = `target:${target.name}:host-resolution`;
|
|
246050
|
+
if (host && bundled.inputs.get(host.path) !== host.hash) {
|
|
246051
|
+
throw new Error("Delivery host resolution did not match its declared source.");
|
|
246052
|
+
}
|
|
246053
|
+
bundledTargets.push(target.name);
|
|
246054
|
+
phase = `target:${target.name}:inventory`;
|
|
246055
|
+
for (const [path, hash] of bundled.inputs) {
|
|
246056
|
+
const previousHash = snapshots.get(path);
|
|
246057
|
+
if (previousHash !== undefined && previousHash !== hash)
|
|
246058
|
+
throw new Error("Shared source changed between target builds; retry.");
|
|
246059
|
+
snapshots.set(path, hash);
|
|
246060
|
+
}
|
|
246061
|
+
for (const [path, bytes] of bundled.files)
|
|
246062
|
+
await writeArtifact(stage, path, bytes);
|
|
246063
|
+
await writeArtifact(stage, "bundle/package.json", canonical22({ type: "module", private: true }));
|
|
246064
|
+
await writeArtifact(stage, "bundle/app.manifest.json", rendered.manifestJson);
|
|
246065
|
+
await writeArtifact(stage, "bundle/execution-context.json", rendered.executionSnapshot);
|
|
246066
|
+
for (const [path, bytes] of migrations.files)
|
|
246067
|
+
await writeArtifact(stage, path, bytes);
|
|
246068
|
+
await writeArtifact(stage, "bundle/target.json", canonical22({
|
|
246069
|
+
target,
|
|
246070
|
+
entryKind,
|
|
246071
|
+
deploymentReady: false
|
|
246072
|
+
}));
|
|
246073
|
+
const assetInputs = {};
|
|
246074
|
+
for (const asset of assets.filter((asset) => asset.target === target.name)) {
|
|
246075
|
+
const path = resolve112(sourceRoot, asset.source);
|
|
246076
|
+
const bytes = await readOwned(sourceRoot, path);
|
|
246077
|
+
const hash = digest3(bytes);
|
|
246078
|
+
const previousHash = snapshots.get(path);
|
|
246079
|
+
if (previousHash !== undefined && previousHash !== hash)
|
|
246080
|
+
throw new Error("Asset changed during build; retry.");
|
|
246081
|
+
snapshots.set(path, hash);
|
|
246082
|
+
assetInputs[asset.source] = hash;
|
|
246083
|
+
await writeArtifact(stage, `bundle/assets/${asset.path}`, bytes);
|
|
246084
|
+
}
|
|
246085
|
+
const inputDigest = digest3(canonical22({
|
|
246086
|
+
target,
|
|
246087
|
+
generated,
|
|
246088
|
+
toolchain,
|
|
246089
|
+
configuration,
|
|
246090
|
+
build: {
|
|
246091
|
+
minify: settings.build?.minify ?? true,
|
|
246092
|
+
environmentContract: settings.build?.environmentContract,
|
|
246093
|
+
migrations: settings.build?.migrations,
|
|
246094
|
+
httpApplication: httpApplications.find((app) => app.target === target.name),
|
|
246095
|
+
workerApplication: workerApplications.find((app) => app.target === target.name),
|
|
246096
|
+
assets: assets.filter((asset) => asset.target === target.name)
|
|
246097
|
+
},
|
|
246098
|
+
inputs: Object.fromEntries([...bundled.inputs].map(([path, hash]) => [relative7(project, path).split(sep7).join("/"), hash])),
|
|
246099
|
+
assets: assetInputs,
|
|
246100
|
+
...migrations.entries.length ? { migrations: migrations.entries } : {}
|
|
246101
|
+
}));
|
|
246102
|
+
const files = await artifactInventory(stage);
|
|
246103
|
+
const object = {
|
|
246104
|
+
name: target.name,
|
|
246105
|
+
inputDigest,
|
|
246106
|
+
objectId: deliveryObjectDigest2({ inputDigest, files, entryKind }),
|
|
246107
|
+
entrypoint: "bundle/index.js",
|
|
246108
|
+
entryKind,
|
|
246109
|
+
runtimeImports: bundled.runtimeImports,
|
|
246110
|
+
files
|
|
246111
|
+
};
|
|
246112
|
+
objects.push(object);
|
|
246113
|
+
candidates.push({ path: stage, object });
|
|
246114
|
+
}
|
|
246115
|
+
phase = "source-consistency";
|
|
246116
|
+
if (canonical22(Object.fromEntries(sources)) !== canonical22(Object.fromEntries(await sourceInventory(sourceRoot, generatedRoot))))
|
|
246117
|
+
throw new Error("Application sources changed during analysis; retry.");
|
|
246118
|
+
for (const [path, hash] of snapshots) {
|
|
246119
|
+
if (digest3(await readFile5(path)) !== hash)
|
|
246120
|
+
throw new Error("Build inputs changed before publication; retry.");
|
|
246121
|
+
}
|
|
246122
|
+
const manifest = parseDeliveryBuildManifest2({
|
|
246123
|
+
schemaVersion: 1,
|
|
246124
|
+
producer,
|
|
246125
|
+
deploymentReady: false,
|
|
246126
|
+
plan: planned.plan,
|
|
246127
|
+
objects,
|
|
246128
|
+
routes: planned.plan.targets.flatMap((target) => target.routes.map((route) => ({ method: route.method, path: route.path, target: target.name }))),
|
|
246129
|
+
jobs: planned.plan.targets.flatMap((target) => target.jobs.map((job) => ({ name: job.name, target: target.name })))
|
|
246130
|
+
});
|
|
246131
|
+
phase = "artifact-integrity";
|
|
246132
|
+
for (const candidate of candidates) {
|
|
246133
|
+
const destination = join92(objectRoot, candidate.object.objectId);
|
|
246134
|
+
await noLinks2(output.path, destination);
|
|
246135
|
+
if (await exists2(destination) && canonical22(await artifactInventory(destination)) !== canonical22(candidate.object.files)) {
|
|
246136
|
+
throw new Error("An immutable artifact was modified. Refusing to overwrite it.");
|
|
246137
|
+
}
|
|
246138
|
+
}
|
|
246139
|
+
const written = [];
|
|
246140
|
+
for (const candidate of candidates) {
|
|
246141
|
+
const destination = join92(objectRoot, candidate.object.objectId);
|
|
246142
|
+
if (!await exists2(destination)) {
|
|
246143
|
+
await rename3(candidate.path, destination);
|
|
246144
|
+
written.push(...candidate.object.files.map((file) => join92(destination, file.path)));
|
|
246145
|
+
}
|
|
246146
|
+
}
|
|
246147
|
+
phase = "manifest-publication";
|
|
246148
|
+
if (await publishPointer(output.path, canonical22(manifest) + `
|
|
246149
|
+
`))
|
|
246150
|
+
written.push(pointer);
|
|
246151
|
+
const unchangedTargets = objects.filter((object) => previous?.objects.some((old) => old.name === object.name && old.objectId === object.objectId)).map((object) => object.name);
|
|
246152
|
+
resultDiagnostics = [...planned.diagnostics];
|
|
246153
|
+
return parseDeliveryBuildResult({
|
|
246154
|
+
ok: true,
|
|
246155
|
+
manifest,
|
|
246156
|
+
diagnostics: resultDiagnostics,
|
|
246157
|
+
written,
|
|
246158
|
+
bundledTargets,
|
|
246159
|
+
unchangedTargets,
|
|
246160
|
+
changedTargets: objects.filter((object) => !unchangedTargets.includes(object.name)).map((object) => object.name),
|
|
246161
|
+
removedTargets: previous?.objects.filter((old) => !objects.some((object) => object.name === old.name)).map((old) => old.name) ?? []
|
|
246162
|
+
});
|
|
246163
|
+
} catch {
|
|
246164
|
+
return failed([{
|
|
246165
|
+
severity: "error",
|
|
246166
|
+
code: "delivery-build-failed",
|
|
246167
|
+
message: `Independent delivery build failed during ${phase}.`,
|
|
246168
|
+
suggestion: phase === "configuration" ? "Use Bun, a project tsconfig, valid delivery options and a project-local output directory." : phase === "output-ownership" ? "Use an empty output namespace or this project's owned directory; resolve locks and symlinks explicitly." : "Check compiler/type diagnostics, static imports, declared asset paths and artifact integrity; retry only after resolving the failure."
|
|
246169
|
+
}]);
|
|
246170
|
+
} finally {
|
|
246171
|
+
const cleaned = await Promise.allSettled(temporary.map((path) => rm2(path, { recursive: true, force: true })));
|
|
246172
|
+
try {
|
|
246173
|
+
await output?.release();
|
|
246174
|
+
} catch {
|
|
246175
|
+
resultDiagnostics.push({ severity: "warn", code: "delivery-cleanup-failed", message: "Output lock cleanup failed; inspect the owned build directory before retrying." });
|
|
246176
|
+
}
|
|
246177
|
+
if (cleaned.some((result) => result.status === "rejected")) {
|
|
246178
|
+
resultDiagnostics.push({ severity: "warn", code: "delivery-cleanup-failed", message: "Some inactive staging directories could not be removed." });
|
|
246179
|
+
}
|
|
246180
|
+
}
|
|
246181
|
+
}
|
|
244933
246182
|
async function applyDiagnosticFix(fix, options = {}) {
|
|
244934
246183
|
if (!fix || typeof fix.targetFile !== "string")
|
|
244935
246184
|
throw new Error("Invalid DiagnosticFix");
|
|
244936
|
-
const root = await
|
|
246185
|
+
const root = await realpath42(options.rootDir ?? process.cwd());
|
|
244937
246186
|
const file = resolve122(root, fix.targetFile);
|
|
244938
|
-
const stat = await
|
|
244939
|
-
const resolved = await
|
|
246187
|
+
const stat = await lstat22(file);
|
|
246188
|
+
const resolved = await realpath42(file);
|
|
244940
246189
|
const relativePath = relative8(root, resolved);
|
|
244941
246190
|
if (stat.isSymbolicLink() || !stat.isFile() || isAbsolute32(relativePath) || relativePath === ".." || relativePath.startsWith(`..${sep8}`)) {
|
|
244942
246191
|
throw new Error("Fix target must be a regular file inside rootDir");
|
|
@@ -245396,8 +246645,8 @@ function buildToolDefinitions(manifest) {
|
|
|
245396
246645
|
}
|
|
245397
246646
|
|
|
245398
246647
|
// src/shared/tools/app-starter.ts
|
|
245399
|
-
import { lstat as lstat3, mkdir as
|
|
245400
|
-
import { basename as basename7, dirname as
|
|
246648
|
+
import { lstat as lstat3, mkdir as mkdir4, readdir as readdir4, writeFile as writeFile4 } from "node:fs/promises";
|
|
246649
|
+
import { basename as basename7, dirname as dirname9, join as join13, resolve as resolve13 } from "node:path";
|
|
245401
246650
|
|
|
245402
246651
|
// src/shared/tools/app-starter-environment.ts
|
|
245403
246652
|
var STARTER_ENVIRONMENT = String.raw`import { existsSync, readFileSync } from "node:fs";
|
|
@@ -248047,24 +249296,45 @@ async function initializeAppProject(options) {
|
|
|
248047
249296
|
if (!/^[a-z][a-z0-9-]*$/.test(name) || name.length > 100) {
|
|
248048
249297
|
throw new Error("Project name must be lowercase kebab-case (1-100 characters)");
|
|
248049
249298
|
}
|
|
248050
|
-
await
|
|
249299
|
+
await mkdir4(root, { recursive: true });
|
|
248051
249300
|
const stat = await lstat3(root);
|
|
248052
249301
|
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
248053
249302
|
throw new Error("Project root must be a real directory");
|
|
248054
|
-
if ((await
|
|
249303
|
+
if ((await readdir4(root)).some((entry) => entry !== ".git")) {
|
|
248055
249304
|
throw new Error("app init requires an empty directory (an existing .git directory is allowed)");
|
|
248056
249305
|
}
|
|
248057
249306
|
const template = options.template ?? "command";
|
|
248058
249307
|
const files = template === "command" ? appStarterFiles(name) : appTemplateFiles(name, template);
|
|
248059
249308
|
for (const [relativePath, content] of Object.entries(files)) {
|
|
248060
249309
|
const path = join13(root, relativePath);
|
|
248061
|
-
await
|
|
249310
|
+
await mkdir4(dirname9(path), { recursive: true });
|
|
248062
249311
|
await writeFile4(path, content, { encoding: "utf8", flag: "wx" });
|
|
248063
249312
|
}
|
|
248064
249313
|
return { root, name, files: Object.keys(files) };
|
|
248065
249314
|
}
|
|
248066
249315
|
|
|
248067
249316
|
// src/shared/tools/app-tools.ts
|
|
249317
|
+
var REMOTE_APP_ACTIONS = {
|
|
249318
|
+
upload: "upload_release",
|
|
249319
|
+
configure: "put_configuration",
|
|
249320
|
+
deploy: "activate_release",
|
|
249321
|
+
status: "get_runtime",
|
|
249322
|
+
rollback: "activate_release",
|
|
249323
|
+
reconcile: "reconcile_activation",
|
|
249324
|
+
retire: "retire_activation"
|
|
249325
|
+
};
|
|
249326
|
+
var REMOTE_APP_DESCRIPTIONS = {
|
|
249327
|
+
ref: "[upload/configure/deploy/status/rollback/reconcile/retire] Project ref (defaults to context)",
|
|
249328
|
+
id: "[upload/configure/deploy/status/rollback/reconcile/retire] Application ID",
|
|
249329
|
+
environment_id: "[configure/deploy/status/rollback/reconcile/retire] Environment ID",
|
|
249330
|
+
configuration_id: "[deploy/rollback] Required immutable configuration revision",
|
|
249331
|
+
activation_id: "[deploy/rollback/reconcile/retire] Required explicit activation ID",
|
|
249332
|
+
expected_activation_id: "[deploy/rollback] Required current activation ID, or absent for first activation",
|
|
249333
|
+
configuration_path: "[configure] Configuration write JSON including revision and expected revision",
|
|
249334
|
+
manifest_path: "[upload] Local delivery.manifest.json",
|
|
249335
|
+
release_id: "[deploy/rollback/reconcile] Required immutable application release ID"
|
|
249336
|
+
};
|
|
249337
|
+
var REMOTE_APP_SCHEMA = Object.fromEntries(Object.entries(APPLICATION_TOOL_SCHEMA).filter(([name]) => name !== "action").map(([name, schema]) => [name, optional(schema, REMOTE_APP_DESCRIPTIONS[name])]));
|
|
248068
249338
|
async function initProject(args) {
|
|
248069
249339
|
if (args.force)
|
|
248070
249340
|
throw new Error("app init never overwrites files; choose an empty directory");
|
|
@@ -248108,7 +249378,7 @@ async function writeScaffold(path, content, force) {
|
|
|
248108
249378
|
await writeFile5(path, content, "utf8");
|
|
248109
249379
|
return "overwritten";
|
|
248110
249380
|
}
|
|
248111
|
-
await
|
|
249381
|
+
await mkdir5(dirname10(path), { recursive: true });
|
|
248112
249382
|
await writeFile5(path, content, "utf8");
|
|
248113
249383
|
return "created";
|
|
248114
249384
|
}
|
|
@@ -248267,7 +249537,7 @@ async function readAllowedDependencies(root) {
|
|
|
248267
249537
|
if (!existsSync10(path))
|
|
248268
249538
|
return { dependencies: [], devDependencies: [] };
|
|
248269
249539
|
try {
|
|
248270
|
-
const parsed = JSON.parse(await
|
|
249540
|
+
const parsed = JSON.parse(await readFile4(path, "utf8"));
|
|
248271
249541
|
return {
|
|
248272
249542
|
dependencies: Object.keys(parsed.dependencies ?? {}).sort(),
|
|
248273
249543
|
devDependencies: Object.keys(parsed.devDependencies ?? {}).sort()
|
|
@@ -248344,7 +249614,7 @@ async function findUnwiredContracts(rootDir) {
|
|
|
248344
249614
|
async function walk(directory) {
|
|
248345
249615
|
let entries;
|
|
248346
249616
|
try {
|
|
248347
|
-
entries = await
|
|
249617
|
+
entries = await readdir5(directory, { withFileTypes: true });
|
|
248348
249618
|
} catch {
|
|
248349
249619
|
return;
|
|
248350
249620
|
}
|
|
@@ -248362,17 +249632,17 @@ async function findUnwiredContracts(rootDir) {
|
|
|
248362
249632
|
const contracts = files.filter((file) => file.endsWith(".contract.ts"));
|
|
248363
249633
|
if (contracts.length === 0)
|
|
248364
249634
|
return [];
|
|
248365
|
-
const otherSources = await Promise.all(files.filter((file) => !contracts.includes(file)).map(async (file) => await
|
|
249635
|
+
const otherSources = await Promise.all(files.filter((file) => !contracts.includes(file)).map(async (file) => await readFile4(file, "utf8").catch(() => "")));
|
|
248366
249636
|
const unwired = [];
|
|
248367
249637
|
for (const contract of contracts) {
|
|
248368
|
-
const source = await
|
|
249638
|
+
const source = await readFile4(contract, "utf8").catch(() => "");
|
|
248369
249639
|
const symbols = [...source.matchAll(/export\s+const\s+([A-Za-z_$][A-Za-z0-9_$]*)/g)].map((match) => match[1]);
|
|
248370
249640
|
if (symbols.length === 0)
|
|
248371
249641
|
continue;
|
|
248372
249642
|
const moduleName = basename8(contract).replace(/\.ts$/, "");
|
|
248373
249643
|
const wired = otherSources.some((text) => text.includes(moduleName) || symbols.some((symbol) => new RegExp(`\\b${symbol}\\b`).test(text)));
|
|
248374
249644
|
if (!wired)
|
|
248375
|
-
unwired.push({ file:
|
|
249645
|
+
unwired.push({ file: relative9(rootDir, contract), symbols });
|
|
248376
249646
|
}
|
|
248377
249647
|
return unwired;
|
|
248378
249648
|
}
|
|
@@ -248413,7 +249683,7 @@ async function runFix(args) {
|
|
|
248413
249683
|
const fixPath = args.fix?.trim();
|
|
248414
249684
|
if (!fixPath)
|
|
248415
249685
|
throw new Error("app fix requires --fix <path-to-diagnostic-fix.json>");
|
|
248416
|
-
const parsed = JSON.parse(await
|
|
249686
|
+
const parsed = JSON.parse(await readFile4(resolve14(root, fixPath), "utf8"));
|
|
248417
249687
|
const write = args.write === true;
|
|
248418
249688
|
const applied = await applyDiagnosticFix(parsed, { rootDir: root, dryRun: !write });
|
|
248419
249689
|
return textResult(JSON.stringify({
|
|
@@ -248479,7 +249749,7 @@ async function readManifest(root) {
|
|
|
248479
249749
|
if (!existsSync10(manifestPath)) {
|
|
248480
249750
|
throw new Error(`Manifest not found: ${manifestPath}(先运行 app compile 生成)`);
|
|
248481
249751
|
}
|
|
248482
|
-
const parsed = JSON.parse(await
|
|
249752
|
+
const parsed = JSON.parse(await readFile4(manifestPath, "utf8"));
|
|
248483
249753
|
if (!Array.isArray(parsed.modules)) {
|
|
248484
249754
|
throw new Error(`Invalid manifest: ${manifestPath}`);
|
|
248485
249755
|
}
|
|
@@ -248666,7 +249936,7 @@ async function runExportTools(args) {
|
|
|
248666
249936
|
const manifest = await readManifest(root);
|
|
248667
249937
|
const definitions = buildToolDefinitions(manifest);
|
|
248668
249938
|
const outDir = resolve14(args.out_dir || join14(root, "generated"));
|
|
248669
|
-
await
|
|
249939
|
+
await mkdir5(outDir, { recursive: true });
|
|
248670
249940
|
if (args.format === "json") {
|
|
248671
249941
|
return textResult(JSON.stringify(definitions, null, 2));
|
|
248672
249942
|
}
|
|
@@ -248685,8 +249955,39 @@ async function runExportTools(args) {
|
|
|
248685
249955
|
`);
|
|
248686
249956
|
return textResult(summary);
|
|
248687
249957
|
}
|
|
248688
|
-
async function
|
|
249958
|
+
async function runDelivery(args) {
|
|
249959
|
+
const root = resolve14(args.root || process.cwd());
|
|
249960
|
+
const loaded = await loadSupacloudConfig(root);
|
|
249961
|
+
const options = compileOptionsFromConfig({
|
|
249962
|
+
...loaded,
|
|
249963
|
+
...args.out_dir === undefined ? {} : { outDir: resolve14(root, args.out_dir) },
|
|
249964
|
+
...args.include === undefined ? {} : { include: parseInclude(args.include) },
|
|
249965
|
+
...args.strict === undefined ? {} : { strict: args.strict }
|
|
249966
|
+
}, root);
|
|
249967
|
+
if (args.action === "plan") {
|
|
249968
|
+
const result = await planDeliveryProject(options, loaded.delivery);
|
|
249969
|
+
return textResult(args.format === "json" ? JSON.stringify(result, null, 2) : formatDeliveryPlan(result), !result.ok);
|
|
249970
|
+
}
|
|
249971
|
+
const result = await buildDeliveryProject(options, loaded.delivery);
|
|
249972
|
+
return textResult(JSON.stringify(result, null, 2), !result.ok);
|
|
249973
|
+
}
|
|
249974
|
+
async function runAppTool(request, options = {}) {
|
|
249975
|
+
if (Object.hasOwn(REMOTE_APP_ACTIONS, request.action)) {
|
|
249976
|
+
const delegate = options.getApplications?.();
|
|
249977
|
+
if (!delegate)
|
|
249978
|
+
return textResult("App remote actions require a Management API context.", true);
|
|
249979
|
+
const action = REMOTE_APP_ACTIONS[request.action];
|
|
249980
|
+
const args = parseToolArguments(APPLICATION_TOOL_SCHEMA, {
|
|
249981
|
+
...request,
|
|
249982
|
+
action,
|
|
249983
|
+
ref: request.ref ?? options.projectRef
|
|
249984
|
+
});
|
|
249985
|
+
return delegate(args);
|
|
249986
|
+
}
|
|
248689
249987
|
switch (request.action) {
|
|
249988
|
+
case "plan":
|
|
249989
|
+
case "build":
|
|
249990
|
+
return runDelivery(request);
|
|
248690
249991
|
case "init":
|
|
248691
249992
|
return initProject(request);
|
|
248692
249993
|
case "generate":
|
|
@@ -248711,24 +250012,45 @@ async function runAppTool(request) {
|
|
|
248711
250012
|
return textResult(`Unknown app action: ${String(request.action)}`, true);
|
|
248712
250013
|
}
|
|
248713
250014
|
}
|
|
248714
|
-
function registerAppTools(server2) {
|
|
248715
|
-
server2.tool("app", "
|
|
248716
|
-
|
|
250015
|
+
function registerAppTools(server2, options = {}) {
|
|
250016
|
+
server2.tool("app", "Application authoring and delivery. Plan is read-only; build, upload and configure never deploy. Deploy/rollback explicitly activate a release; rollback never downgrades schema.", {
|
|
250017
|
+
...REMOTE_APP_SCHEMA,
|
|
250018
|
+
action: withDescription(stringEnum([
|
|
250019
|
+
"init",
|
|
250020
|
+
"generate",
|
|
250021
|
+
"compile",
|
|
250022
|
+
"check",
|
|
250023
|
+
"graph",
|
|
250024
|
+
"explain",
|
|
250025
|
+
"export-tools",
|
|
250026
|
+
"context",
|
|
250027
|
+
"doctor",
|
|
250028
|
+
"fix",
|
|
250029
|
+
"plan",
|
|
250030
|
+
"build",
|
|
250031
|
+
"upload",
|
|
250032
|
+
"configure",
|
|
250033
|
+
"deploy",
|
|
250034
|
+
"status",
|
|
250035
|
+
"rollback",
|
|
250036
|
+
"reconcile",
|
|
250037
|
+
"retire"
|
|
250038
|
+
]), "App action; upload/configure only prepare, rollback activates an explicit old release without schema downgrade"),
|
|
248717
250039
|
kind: optional(stringEnum(["module", "command", "query", "controller", "job", "contract"]), "[generate] Scaffold kind"),
|
|
248718
250040
|
template: optional(stringEnum(["http", "command", "edge"]), "[init] Golden-path template (default: command)"),
|
|
248719
250041
|
name: optional(Type.String(), "[init/generate] Project or object name"),
|
|
248720
250042
|
module: optional(Type.String(), "[generate] Target feature module (required for command/query/controller)"),
|
|
248721
250043
|
dir: optional(Type.String(), "[generate] Feature root directory (default: src/features)"),
|
|
248722
250044
|
force: optional(Type.Boolean(), "[generate] Overwrite existing files"),
|
|
248723
|
-
root: optional(Type.String(), "Project directory containing supacloud.config.ts (default: current directory)"),
|
|
250045
|
+
root: optional(Type.String(), "[plan/build/compile/check] Project directory containing supacloud.config.ts (default: current directory)"),
|
|
248724
250046
|
include: optional(Type.String(), "[compile/check] Comma-separated glob patterns for source files"),
|
|
248725
|
-
out_dir: optional(Type.String(), "[compile/export-tools] Output directory (default:
|
|
250047
|
+
out_dir: optional(Type.String(), "[compile/plan/build/export-tools] Output directory (default: configured outDir)"),
|
|
248726
250048
|
strict: optional(Type.Boolean(), "[compile/check] Promote warnings to errors"),
|
|
248727
|
-
format: optional(stringEnum(["text", "json"]), "[graph/export-tools] Output format (default: text)"),
|
|
250049
|
+
format: optional(stringEnum(["text", "json"]), "[plan/graph/export-tools] Output format (default: text)"),
|
|
248728
250050
|
target: optional(Type.String(), "[explain/context] Provider class name / token name / command name / job name / module name"),
|
|
248729
250051
|
fix: optional(Type.String(), "[fix] Path to a DiagnosticFix JSON file produced by `doctor --format json`"),
|
|
248730
250052
|
write: optional(Type.Boolean(), "[fix] Write the fix to disk (default: preview only)")
|
|
248731
|
-
}, runAppTool);
|
|
250053
|
+
}, (request) => runAppTool(request, options));
|
|
248732
250054
|
}
|
|
248733
250055
|
var APP_ALIAS_ACTIONS = ["generate", "compile", "check", "graph", "explain", "context", "doctor", "fix"];
|
|
248734
250056
|
function registerAppAliases(server2) {
|
|
@@ -250034,7 +251356,7 @@ var MUTATION_TOOL_SCHEMA = {
|
|
|
250034
251356
|
import { execFileSync } from "node:child_process";
|
|
250035
251357
|
import { createHash as createHash8 } from "node:crypto";
|
|
250036
251358
|
import { existsSync as existsSync12, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "node:fs";
|
|
250037
|
-
import { dirname as
|
|
251359
|
+
import { dirname as dirname11, join as join16, relative as relative10, resolve as resolve17 } from "node:path";
|
|
250038
251360
|
var SAFE_PROJECT_REF = /^[A-Za-z0-9_-]{1,64}$/;
|
|
250039
251361
|
var BACKUP_ID = /^logical-full_[A-Za-z0-9_-]{1,64}_[a-f0-9]{32}$/;
|
|
250040
251362
|
var SHA256 = /^[a-f0-9]{64}$/;
|
|
@@ -250428,7 +251750,7 @@ function handleScopeRebind(args) {
|
|
|
250428
251750
|
writeFileSync3(filePath, formatted, "utf8");
|
|
250429
251751
|
}
|
|
250430
251752
|
scopes.push({
|
|
250431
|
-
file:
|
|
251753
|
+
file: relative10(cwd, filePath),
|
|
250432
251754
|
task_id: typeof parsed.taskId === "string" ? parsed.taskId : "",
|
|
250433
251755
|
target_project_ref: typeof parsed.targetProjectRef === "string" ? parsed.targetProjectRef : "",
|
|
250434
251756
|
previous_base_commit: previousBaseCommit,
|
|
@@ -250482,7 +251804,7 @@ function handleScopeInspect(args) {
|
|
|
250482
251804
|
const scopeSha256 = genericScopedReleaseScopeSha256(parsed);
|
|
250483
251805
|
const git = gitCommitInfo(cwd);
|
|
250484
251806
|
return releaseControlSuccess("release.scope_inspect", {
|
|
250485
|
-
file:
|
|
251807
|
+
file: relative10(cwd, filePath),
|
|
250486
251808
|
scope: {
|
|
250487
251809
|
task_id: parsed.taskId,
|
|
250488
251810
|
target_project_ref: parsed.targetProjectRef,
|
|
@@ -250532,7 +251854,7 @@ function handleScopeCreate(args, fallbackRef) {
|
|
|
250532
251854
|
}
|
|
250533
251855
|
const targetBaseCommit = resolveGitBaseCommit(cwd, args.base_commit);
|
|
250534
251856
|
const filePath = resolve17(cwd, args.file.trim());
|
|
250535
|
-
mkdirSync4(
|
|
251857
|
+
mkdirSync4(dirname11(filePath), { recursive: true });
|
|
250536
251858
|
const scope = {
|
|
250537
251859
|
baseCommit: targetBaseCommit,
|
|
250538
251860
|
excludedFunctions: parseCommaList(args.excluded_functions),
|
|
@@ -250548,7 +251870,7 @@ function handleScopeCreate(args, fallbackRef) {
|
|
|
250548
251870
|
`;
|
|
250549
251871
|
writeFileSync3(filePath, formatted, "utf8");
|
|
250550
251872
|
return releaseControlSuccess("release.scope_create", {
|
|
250551
|
-
file:
|
|
251873
|
+
file: relative10(cwd, filePath),
|
|
250552
251874
|
task_id: scope.taskId,
|
|
250553
251875
|
target_project_ref: projectRef,
|
|
250554
251876
|
base_commit: targetBaseCommit,
|
|
@@ -250753,10 +252075,10 @@ function registerReleaseTools(server2, http, options = {}) {
|
|
|
250753
252075
|
}
|
|
250754
252076
|
|
|
250755
252077
|
// src/shared/tools/deploy-tools.ts
|
|
250756
|
-
import { access as access3, lstat as lstat4, mkdtemp, opendir, readFile as
|
|
252078
|
+
import { access as access3, lstat as lstat4, mkdtemp as mkdtemp2, opendir, readFile as readFile7, rm as rm4, writeFile as writeFile6 } from "node:fs/promises";
|
|
250757
252079
|
import { createHash as createHash9 } from "node:crypto";
|
|
250758
252080
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
250759
|
-
import { basename as basename9, dirname as
|
|
252081
|
+
import { basename as basename9, dirname as dirname12, join as join17, relative as relative11, resolve as resolve18, sep as sep9 } from "node:path";
|
|
250760
252082
|
import { spawn as spawn3 } from "node:child_process";
|
|
250761
252083
|
|
|
250762
252084
|
// node_modules/fflate/esm/index.mjs
|
|
@@ -251440,7 +252762,7 @@ function frontendDeployment(candidate) {
|
|
|
251440
252762
|
}
|
|
251441
252763
|
async function readJson(path) {
|
|
251442
252764
|
try {
|
|
251443
|
-
return JSON.parse(await
|
|
252765
|
+
return JSON.parse(await readFile7(path, "utf8"));
|
|
251444
252766
|
} catch (error) {
|
|
251445
252767
|
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
251446
252768
|
return null;
|
|
@@ -251452,7 +252774,7 @@ async function findDeployConfigRoot(startDirectory) {
|
|
|
251452
252774
|
while (true) {
|
|
251453
252775
|
if (await pathExists(join17(directory, "supacloud.json")))
|
|
251454
252776
|
return directory;
|
|
251455
|
-
const parent =
|
|
252777
|
+
const parent = dirname12(directory);
|
|
251456
252778
|
if (parent === directory)
|
|
251457
252779
|
return resolve18(startDirectory);
|
|
251458
252780
|
directory = parent;
|
|
@@ -251541,8 +252863,8 @@ async function defaultBuildCommand(projectDirectory, configRoot) {
|
|
|
251541
252863
|
return "yarn build";
|
|
251542
252864
|
if (directory === configRoot)
|
|
251543
252865
|
break;
|
|
251544
|
-
const parent =
|
|
251545
|
-
if (parent === directory || !directory.startsWith(`${configRoot}${
|
|
252866
|
+
const parent = dirname12(directory);
|
|
252867
|
+
if (parent === directory || !directory.startsWith(`${configRoot}${sep9}`))
|
|
251546
252868
|
break;
|
|
251547
252869
|
directory = parent;
|
|
251548
252870
|
}
|
|
@@ -251573,7 +252895,7 @@ function selectDeployment(deployments, requestedId, projectName) {
|
|
|
251573
252895
|
}
|
|
251574
252896
|
function targetRoot(configRoot, target, name) {
|
|
251575
252897
|
const root = resolve18(configRoot, target.root || (name === "frontend" ? "." : name));
|
|
251576
|
-
const prefix = `${resolve18(configRoot)}${
|
|
252898
|
+
const prefix = `${resolve18(configRoot)}${sep9}`;
|
|
251577
252899
|
if (root !== resolve18(configRoot) && !root.startsWith(prefix)) {
|
|
251578
252900
|
throw new Error(`Deploy target '${name}' root must stay inside the repository root`);
|
|
251579
252901
|
}
|
|
@@ -251595,7 +252917,7 @@ function selectTarget(configRoot, invocationDirectory, config, requestedName) {
|
|
|
251595
252917
|
throw new Error(`Deploy target '${requestedName}' was not found in supacloud.json`);
|
|
251596
252918
|
return { name: selected[0], config: selected[1], root: targetRoot(configRoot, selected[1], selected[0]) };
|
|
251597
252919
|
}
|
|
251598
|
-
const containing = targets.map(([name, target]) => ({ name, target, root: targetRoot(configRoot, target, name) })).filter((candidate) => invocationDirectory === candidate.root || invocationDirectory.startsWith(`${candidate.root}${
|
|
252920
|
+
const containing = targets.map(([name, target]) => ({ name, target, root: targetRoot(configRoot, target, name) })).filter((candidate) => invocationDirectory === candidate.root || invocationDirectory.startsWith(`${candidate.root}${sep9}`)).sort((left, right) => right.root.length - left.root.length);
|
|
251599
252921
|
if (containing.length === 1 || containing.length > 1 && containing[0].root !== containing[1].root) {
|
|
251600
252922
|
const selected = containing[0];
|
|
251601
252923
|
return { name: selected.name, config: selected.target, root: selected.root };
|
|
@@ -251627,7 +252949,7 @@ function defaultOutputDirectory(framework) {
|
|
|
251627
252949
|
function resolveInsideProject(projectDirectory, candidate, allowRoot = false) {
|
|
251628
252950
|
const resolved = resolve18(projectDirectory, candidate);
|
|
251629
252951
|
const projectRoot = resolve18(projectDirectory);
|
|
251630
|
-
const projectPrefix = `${projectRoot}${
|
|
252952
|
+
const projectPrefix = `${projectRoot}${sep9}`;
|
|
251631
252953
|
if (!allowRoot && resolved === projectRoot || resolved !== projectRoot && !resolved.startsWith(projectPrefix)) {
|
|
251632
252954
|
throw new Error("Frontend output directory must stay inside the project directory");
|
|
251633
252955
|
}
|
|
@@ -251675,18 +252997,18 @@ async function collectArchiveFiles(root) {
|
|
|
251675
252997
|
let bytes = 0;
|
|
251676
252998
|
const files = {};
|
|
251677
252999
|
for (const path of paths) {
|
|
251678
|
-
const data = new Uint8Array(await
|
|
253000
|
+
const data = new Uint8Array(await readFile7(path));
|
|
251679
253001
|
bytes += data.byteLength;
|
|
251680
253002
|
if (bytes > MAX_SOURCE_BYTES)
|
|
251681
253003
|
throw new Error(`Frontend output exceeds ${MAX_SOURCE_BYTES} bytes`);
|
|
251682
|
-
const archivePath =
|
|
253004
|
+
const archivePath = relative11(root, path).split(sep9).join("/");
|
|
251683
253005
|
files[archivePath] = [data, { mtime: FIXED_ZIP_MTIME }];
|
|
251684
253006
|
}
|
|
251685
253007
|
return { files, bytes, count: paths.length };
|
|
251686
253008
|
}
|
|
251687
253009
|
async function createFrontendArchive(outputDirectory) {
|
|
251688
253010
|
const collected = await collectArchiveFiles(outputDirectory);
|
|
251689
|
-
const temporaryDirectory = await
|
|
253011
|
+
const temporaryDirectory = await mkdtemp2(join17(tmpdir2(), "supacloud-deploy-"));
|
|
251690
253012
|
const archivePath = join17(temporaryDirectory, `${basename9(outputDirectory)}.zip`);
|
|
251691
253013
|
try {
|
|
251692
253014
|
const archiveBytes = zipSync(collected.files, { level: 6, mtime: FIXED_ZIP_MTIME });
|
|
@@ -251697,10 +253019,10 @@ async function createFrontendArchive(outputDirectory) {
|
|
|
251697
253019
|
sha256,
|
|
251698
253020
|
sourceBytes: collected.bytes,
|
|
251699
253021
|
fileCount: collected.count,
|
|
251700
|
-
cleanup: () =>
|
|
253022
|
+
cleanup: () => rm4(temporaryDirectory, { recursive: true, force: true })
|
|
251701
253023
|
};
|
|
251702
253024
|
} catch (error) {
|
|
251703
|
-
await
|
|
253025
|
+
await rm4(temporaryDirectory, { recursive: true, force: true });
|
|
251704
253026
|
throw error;
|
|
251705
253027
|
}
|
|
251706
253028
|
}
|
|
@@ -251851,7 +253173,7 @@ function registerDeployTools(server2, http, options = {}) {
|
|
|
251851
253173
|
} else {
|
|
251852
253174
|
report("build", "skipped");
|
|
251853
253175
|
}
|
|
251854
|
-
report("package",
|
|
253176
|
+
report("package", relative11(projectDirectory, outputDirectory) || ".");
|
|
251855
253177
|
const archive = await createFrontendArchive(outputDirectory);
|
|
251856
253178
|
try {
|
|
251857
253179
|
const inventory = payload(await listFrontendReleases(http, projectRef, selected.id, undefined, 100));
|
|
@@ -251911,8 +253233,8 @@ function registerDeployTools(server2, http, options = {}) {
|
|
|
251911
253233
|
// src/shared/tools/remote-dev-tools.ts
|
|
251912
253234
|
import { spawn as spawn4 } from "node:child_process";
|
|
251913
253235
|
import { existsSync as existsSync13 } from "node:fs";
|
|
251914
|
-
import { readFile as
|
|
251915
|
-
import { readdir as
|
|
253236
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
253237
|
+
import { readdir as readdir6 } from "node:fs/promises";
|
|
251916
253238
|
import { createHash as createHash10 } from "node:crypto";
|
|
251917
253239
|
import { join as join18, resolve as resolve20 } from "node:path";
|
|
251918
253240
|
var SAFE_TOKEN = /^[A-Za-z0-9._:@/+,-]+$/;
|
|
@@ -251992,7 +253314,7 @@ async function readProjectConfig3(root) {
|
|
|
251992
253314
|
if (!existsSync13(file))
|
|
251993
253315
|
return {};
|
|
251994
253316
|
try {
|
|
251995
|
-
const parsed = JSON.parse(await
|
|
253317
|
+
const parsed = JSON.parse(await readFile8(file, "utf8"));
|
|
251996
253318
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
251997
253319
|
} catch (error) {
|
|
251998
253320
|
throw new Error(`Invalid supacloud.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -252005,7 +253327,7 @@ async function readDevConfig(root) {
|
|
|
252005
253327
|
async function sourceFingerprint(root) {
|
|
252006
253328
|
const files = [];
|
|
252007
253329
|
const visit = async (directory) => {
|
|
252008
|
-
const entries = await
|
|
253330
|
+
const entries = await readdir6(directory, { withFileTypes: true });
|
|
252009
253331
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
252010
253332
|
const path = join18(directory, entry.name);
|
|
252011
253333
|
if (entry.isDirectory() && ![".git", "node_modules", "dist", "generated", ".supacloud"].includes(entry.name))
|
|
@@ -252018,7 +253340,7 @@ async function sourceFingerprint(root) {
|
|
|
252018
253340
|
const hash = createHash10("sha256");
|
|
252019
253341
|
for (const file of files) {
|
|
252020
253342
|
hash.update(file.slice(root.length).replace(/\\/g, "/"));
|
|
252021
|
-
hash.update(await
|
|
253343
|
+
hash.update(await readFile8(file));
|
|
252022
253344
|
}
|
|
252023
253345
|
return hash.digest("hex");
|
|
252024
253346
|
}
|
|
@@ -252182,7 +253504,7 @@ function registerRemoteDevTools(server2, options = {}) {
|
|
|
252182
253504
|
// package.json
|
|
252183
253505
|
var package_default8 = {
|
|
252184
253506
|
name: "@supacloud/cli",
|
|
252185
|
-
version: "0.
|
|
253507
|
+
version: "0.59.0",
|
|
252186
253508
|
description: "Project-scoped CLI for SupaCloud users",
|
|
252187
253509
|
type: "module",
|
|
252188
253510
|
main: "./dist/index.js",
|
|
@@ -252486,6 +253808,13 @@ EXAMPLES
|
|
|
252486
253808
|
${preferredCommand} app generate --kind job --module billing --name sync-orders
|
|
252487
253809
|
${preferredCommand} app generate --kind contract --module billing --name issue-invoice
|
|
252488
253810
|
${preferredCommand} app compile --root .
|
|
253811
|
+
${preferredCommand} app plan --root . --format json
|
|
253812
|
+
${preferredCommand} app build --root .
|
|
253813
|
+
${preferredCommand} app upload --ref abc123 --id orders --manifest_path generated/delivery/delivery.manifest.json
|
|
253814
|
+
${preferredCommand} app configure --ref abc123 --id orders --environment_id test --configuration_path configuration.json
|
|
253815
|
+
${preferredCommand} app deploy --ref abc123 --id orders --environment_id test --release_id <sha256> --configuration_id <uuid> --activation_id <uuid> --expected_activation_id absent
|
|
253816
|
+
${preferredCommand} app status --ref abc123 --id orders --environment_id test
|
|
253817
|
+
${preferredCommand} app rollback --ref abc123 --id orders --environment_id test --release_id <old-sha256> --configuration_id <uuid> --activation_id <new-uuid> --expected_activation_id <current-uuid>
|
|
252489
253818
|
${preferredCommand} app check --root . --strict
|
|
252490
253819
|
${preferredCommand} app graph --root . --format json
|
|
252491
253820
|
${preferredCommand} app explain --target CaseService
|
|
@@ -252728,6 +254057,10 @@ function createCliTools(context, confirmProduction) {
|
|
|
252728
254057
|
})));
|
|
252729
254058
|
assign(captureTools((server2) => registerFrontendTools(server2, http)));
|
|
252730
254059
|
assign(captureTools((server2) => registerApplicationTools(server2, http)));
|
|
254060
|
+
Object.assign(tools, captureTools((server2) => registerAppTools(server2, {
|
|
254061
|
+
getApplications: () => tools.applications?.callback,
|
|
254062
|
+
projectRef: context.projectRef || undefined
|
|
254063
|
+
})));
|
|
252731
254064
|
assign(captureTools((server2) => registerDeployTools(server2, http, {
|
|
252732
254065
|
projectRef: context.projectRef || undefined,
|
|
252733
254066
|
cwd: process.cwd(),
|