@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.
Files changed (3) hide show
  1. package/README.md +33 -0
  2. package/dist/index.js +1388 -55
  3. 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: { local: ["init", "generate", "compile", "check", "graph", "explain", "export-tools", "context", "doctor", "fix"] },
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 mkdir4, readFile as readFile3, readdir as readdir4, writeFile as writeFile5 } from "node:fs/promises";
221494
- import { basename as basename8, dirname as dirname9, join as join14, relative as relative7, resolve as resolve14 } from "node:path";
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 lstat2, readFile as readFile6, realpath as realpath4, rename as rename4, unlink as unlink2, writeFile as writeFile3 } from "node:fs/promises";
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 realpath4(options.rootDir ?? process.cwd());
246185
+ const root = await realpath42(options.rootDir ?? process.cwd());
244937
246186
  const file = resolve122(root, fix.targetFile);
244938
- const stat = await lstat2(file);
244939
- const resolved = await realpath4(file);
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 mkdir3, readdir as readdir3, writeFile as writeFile4 } from "node:fs/promises";
245400
- import { basename as basename7, dirname as dirname7, join as join13, resolve as resolve13 } from "node:path";
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 mkdir3(root, { recursive: true });
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 readdir3(root)).some((entry) => entry !== ".git")) {
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 mkdir3(dirname7(path), { recursive: true });
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 mkdir4(dirname9(path), { recursive: true });
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 readFile3(path, "utf8"));
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 readdir4(directory, { withFileTypes: true });
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 readFile3(file, "utf8").catch(() => "")));
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 readFile3(contract, "utf8").catch(() => "");
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: relative7(rootDir, contract), symbols });
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 readFile3(resolve14(root, fixPath), "utf8"));
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 readFile3(manifestPath, "utf8"));
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 mkdir4(outDir, { recursive: true });
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 runAppTool(request) {
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", "Local @supacloud/app framework commands: init, scaffold, compile, check, graph, explain, export-tools, AI context/doctor and fix. Actions: init, generate, compile, check, graph, explain, export-tools, context, doctor, fix", {
248716
- action: withDescription(stringEnum(["init", "generate", "compile", "check", "graph", "explain", "export-tools", "context", "doctor", "fix"]), "App action"),
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: <root>/generated)"),
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 dirname10, join as join16, relative as relative9, resolve as resolve17 } from "node:path";
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: relative9(cwd, filePath),
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: relative9(cwd, filePath),
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(dirname10(filePath), { recursive: true });
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: relative9(cwd, filePath),
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 readFile4, rm as rm2, writeFile as writeFile6 } from "node:fs/promises";
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 dirname11, join as join17, relative as relative10, resolve as resolve18, sep as sep7 } from "node:path";
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 readFile4(path, "utf8"));
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 = dirname11(directory);
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 = dirname11(directory);
251545
- if (parent === directory || !directory.startsWith(`${configRoot}${sep7}`))
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)}${sep7}`;
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}${sep7}`)).sort((left, right) => right.root.length - left.root.length);
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}${sep7}`;
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 readFile4(path));
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 = relative10(root, path).split(sep7).join("/");
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 mkdtemp(join17(tmpdir2(), "supacloud-deploy-"));
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: () => rm2(temporaryDirectory, { recursive: true, force: true })
253022
+ cleanup: () => rm4(temporaryDirectory, { recursive: true, force: true })
251701
253023
  };
251702
253024
  } catch (error) {
251703
- await rm2(temporaryDirectory, { recursive: true, force: true });
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", relative10(projectDirectory, outputDirectory) || ".");
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 readFile5 } from "node:fs/promises";
251915
- import { readdir as readdir5 } from "node:fs/promises";
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 readFile5(file, "utf8"));
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 readdir5(directory, { withFileTypes: true });
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 readFile5(file));
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.58.5",
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(),