@supacloud/cli 0.62.0 → 0.63.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 (2) hide show
  1. package/dist/index.js +383 -142
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -217356,6 +217356,7 @@ function confirmedFunctionDeletion(payload, expectation) {
217356
217356
  var execFileAsync = promisify(execFile);
217357
217357
  var SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/;
217358
217358
  var FORBIDDEN_BUNDLE_SEGMENTS = new Set(["node_modules", ".git"]);
217359
+ var EDGE_FUNCTION_DEPLOY_TIMEOUT_MS = 5 * 60000;
217359
217360
  var FUNCTION_FRAMEWORKS2 = ["fetch", "elysia", "hono", "sveltekit-function"];
217360
217361
  function scaffoldFiles(framework, slug) {
217361
217362
  if (framework === "elysia") {
@@ -218217,7 +218218,7 @@ ${deployCheck.err}`;
218217
218218
  expected_active_version: expectedActiveVersion,
218218
218219
  expected_activation_id: expectedActivationId,
218219
218220
  ...functionConfig()
218220
- });
218221
+ }, { timeoutMs: EDGE_FUNCTION_DEPLOY_TIMEOUT_MS });
218221
218222
  return functionMutationResponse({
218222
218223
  operation: "edge_functions.deploy",
218223
218224
  projectRef: ref,
@@ -218236,7 +218237,7 @@ ${deployCheck.err}`;
218236
218237
  expected_active_version: expectedActiveVersion,
218237
218238
  expected_activation_id: expectedActivationId,
218238
218239
  ...functionConfig()
218239
- });
218240
+ }, { timeoutMs: EDGE_FUNCTION_DEPLOY_TIMEOUT_MS });
218240
218241
  return functionMutationResponse({
218241
218242
  operation: "edge_functions.deploy_bundle",
218242
218243
  projectRef: ref,
@@ -221518,9 +221519,9 @@ function registerAiTools(server2) {
221518
221519
  }
221519
221520
 
221520
221521
  // src/shared/tools/app-tools.ts
221521
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
221522
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
221522
221523
  import { mkdir as mkdir6, readFile as readFile8, readdir as readdir5, writeFile as writeFile5 } from "node:fs/promises";
221523
- import { basename as basename8, dirname as dirname12, join as join15, relative as relative12, resolve as resolve17 } from "node:path";
221524
+ import { basename as basename8, dirname as dirname12, join as join16, relative as relative12, resolve as resolve18 } from "node:path";
221524
221525
 
221525
221526
  // node_modules/@supacloud/compiler/dist/index.js
221526
221527
  import { dirname as dirname22, relative as relative6, sep as sep4 } from "node:path";
@@ -229355,7 +229356,7 @@ function renderClient(graph, options) {
229355
229356
  "",
229356
229357
  "export interface ApiClientConfig {",
229357
229358
  " baseUrl?: string;",
229358
- " fetch?: typeof fetch;",
229359
+ " fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;",
229359
229360
  " headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);",
229360
229361
  " interceptors?: HttpInterceptorFn[];",
229361
229362
  " /** Normalize response objects like Elysia's default schema validator. */",
@@ -230875,6 +230876,15 @@ export function parseGraphqlResult<Name extends keyof GraphqlQueryResults>(
230875
230876
  `;
230876
230877
  }
230877
230878
  var init_graphql_runtime = () => {};
230879
+ function graphqlSchemaHashes(content) {
230880
+ const hash = (text) => createHash52("sha256").update(text).digest("hex");
230881
+ return {
230882
+ schemaHash: hash(content),
230883
+ schemaNormalizedHash: hash(content.replace(/\r\n?/g, `
230884
+ `))
230885
+ };
230886
+ }
230887
+ var init_graphql_schema_hashes = () => {};
230878
230888
  var exports_graphql = {};
230879
230889
  __export2(exports_graphql, {
230880
230890
  renderGraphql: () => renderGraphql
@@ -230919,7 +230929,7 @@ async function renderGraphql(options) {
230919
230929
  let schemaContent;
230920
230930
  try {
230921
230931
  schemaContent = await readFile2(schemaPath, "utf8");
230922
- contract.schemaHash = createHash52("sha256").update(schemaContent).digest("hex");
230932
+ Object.assign(contract, graphqlSchemaHashes(schemaContent));
230923
230933
  if (schemaPath.endsWith(".json")) {
230924
230934
  const json = JSON.parse(schemaContent);
230925
230935
  schema = import_graphql99.buildClientSchema(json.data ?? json);
@@ -231051,6 +231061,7 @@ var init_graphql = __esm2(() => {
231051
231061
  init_graphql_inputs();
231052
231062
  init_graphql_options();
231053
231063
  init_graphql_runtime();
231064
+ init_graphql_schema_hashes();
231054
231065
  });
231055
231066
  var Metrics2;
231056
231067
  var init_metrics = __esm2(() => {
@@ -241498,6 +241509,7 @@ var init_release_evidence = __esm2(() => {
241498
241509
  var init_graphql_schema = __esm2(() => {
241499
241510
  init_generate();
241500
241511
  init_graphql_options();
241512
+ init_graphql_schema_hashes();
241501
241513
  });
241502
241514
  var init_database_contracts = __esm2(() => {
241503
241515
  init_graphql();
@@ -246876,7 +246888,7 @@ test("partial and untagged inherited remote profiles cannot mix with selected fi
246876
246888
  // ../compiler/package.json
246877
246889
  var package_default = {
246878
246890
  name: "@supacloud/compiler",
246879
- version: "0.30.0",
246891
+ version: "0.31.1",
246880
246892
  description: "Static compiler for @supacloud/app metadata: builds the application graph from AST, validates it, and generates reflection-free factory code",
246881
246893
  type: "module",
246882
246894
  main: "./dist/index.js",
@@ -246949,7 +246961,7 @@ var package_default = {
246949
246961
  // ../app/package.json
246950
246962
  var package_default2 = {
246951
246963
  name: "@supacloud/app",
246952
- version: "0.21.0",
246964
+ version: "0.21.1",
246953
246965
  description: "Angular-style application metadata for SupaCloud: modules, DI tokens, providers, scopes, controllers and commands",
246954
246966
  type: "module",
246955
246967
  main: "./dist/index.js",
@@ -247037,7 +247049,7 @@ var package_default2 = {
247037
247049
  },
247038
247050
  dependencies: {
247039
247051
  "@angular/core": "^22.1.5",
247040
- "@supacloud/contracts": "0.8.0",
247052
+ "@supacloud/contracts": "file:../contracts",
247041
247053
  rxjs: "7.8.2",
247042
247054
  typebox: "1.3.34"
247043
247055
  },
@@ -247051,7 +247063,7 @@ var package_default2 = {
247051
247063
  // ../elysia/package.json
247052
247064
  var package_default3 = {
247053
247065
  name: "@supacloud/elysia",
247054
- version: "0.23.0",
247066
+ version: "0.23.1",
247055
247067
  description: "Elysia runtime adapter for SupaCloud compiled modules: application/request scopes, route registration and validation",
247056
247068
  type: "module",
247057
247069
  main: "./dist/index.js",
@@ -248639,16 +248651,22 @@ var package_default5 = {
248639
248651
  // ../db/package.json
248640
248652
  var package_default6 = {
248641
248653
  name: "@supacloud/db",
248642
- version: "0.11.0",
248654
+ version: "0.12.0",
248643
248655
  description: "Database governance layer for SupaCloud: RLS policies, RPC functions and grants as first-class resources, with manifest/catalog reconciliation",
248644
248656
  type: "module",
248645
248657
  main: "./dist/index.js",
248646
248658
  module: "./dist/index.js",
248647
248659
  types: "./dist/index.d.ts",
248648
248660
  bin: {
248649
- "supacloud-sql-impact": "./dist/sql-impact-cli.js"
248661
+ "supacloud-sql-impact": "./dist/sql-impact-cli.js",
248662
+ "supacloud-db": "./dist/source-contracts-cli.js"
248650
248663
  },
248651
248664
  exports: {
248665
+ "./source-contracts": {
248666
+ types: "./dist/source-contracts.d.ts",
248667
+ import: "./dist/source-contracts.js"
248668
+ },
248669
+ "./source-contracts-cli": "./dist/source-contracts-cli.js",
248652
248670
  "./sql-analysis": {
248653
248671
  types: "./dist/sql-analysis.d.ts",
248654
248672
  import: "./dist/sql-analysis.js"
@@ -248680,7 +248698,7 @@ var package_default6 = {
248680
248698
  ],
248681
248699
  scripts: {
248682
248700
  build: "bun run clean && bun run build:js && bun run build:types",
248683
- "build:js": "bun build src/index.ts src/command-bun.ts src/drizzle.ts src/drizzle-bun.ts src/sql-analysis.ts src/sql-impact-cli.ts --outdir dist --target node --external bun --external @supacloud/contracts --external drizzle-orm --external libpg-query",
248701
+ "build:js": "bun build src/index.ts src/command-bun.ts src/drizzle.ts src/drizzle-bun.ts src/sql-analysis.ts src/sql-impact-cli.ts src/source-contracts.ts src/source-contracts-cli.ts --outdir dist --target node --external bun --external @supacloud/contracts --external drizzle-orm --external libpg-query --external @typescript/typescript6",
248684
248702
  "build:types": "tsc -p tsconfig.json --emitDeclarationOnly",
248685
248703
  clean: "rm -rf dist",
248686
248704
  prepublishOnly: "bun run build",
@@ -248704,14 +248722,15 @@ var package_default6 = {
248704
248722
  },
248705
248723
  devDependencies: {
248706
248724
  "@sinclair/typebox": "0.34.52",
248707
- "@typescript/typescript6": "^6.0.2",
248708
248725
  "drizzle-orm": "1.0.0-rc.4",
248726
+ "drizzle-kit": "1.0.0-rc.4",
248709
248727
  "@types/bun": "^1.4.2",
248710
248728
  typescript: "^7.0.2"
248711
248729
  },
248712
248730
  dependencies: {
248731
+ "@typescript/typescript6": "^6.0.2",
248713
248732
  "libpg-query": "18.1.4",
248714
- "@supacloud/contracts": "file:../contracts"
248733
+ "@supacloud/contracts": "0.8.0"
248715
248734
  },
248716
248735
  peerDependencies: {
248717
248736
  "drizzle-orm": "1.0.0-rc.4"
@@ -248722,13 +248741,13 @@ var package_default6 = {
248722
248741
  }
248723
248742
  },
248724
248743
  overrides: {
248725
- "@supacloud/contracts": "file:../contracts"
248744
+ "@supacloud/contracts": "0.8.0"
248726
248745
  }
248727
248746
  };
248728
248747
  // ../supacloud-js/package.json
248729
248748
  var package_default7 = {
248730
248749
  name: "@supacloud/js",
248731
- version: "0.36.0",
248750
+ version: "0.36.1",
248732
248751
  description: "Platform SDK for SupaCloud, built on top of supabase-js",
248733
248752
  type: "module",
248734
248753
  main: "./dist/index.cjs",
@@ -248828,11 +248847,133 @@ var package_default7 = {
248828
248847
  }
248829
248848
  };
248830
248849
 
248850
+ // src/shared/tools/app-starter-database.ts
248851
+ var STARTER_DATABASE_CONFIG = {
248852
+ version: 1,
248853
+ schema: ["db/schema.ts"],
248854
+ functions: "db/functions",
248855
+ migrations: "migrations",
248856
+ contracts: "db/contracts",
248857
+ audit: "output/database-audit",
248858
+ consumers: ["src", "scripts", "tests"],
248859
+ auditConsumers: [],
248860
+ role: "starter_review_http"
248861
+ };
248862
+ var STARTER_DATABASE_SCHEMA = `import { sql } from "drizzle-orm";
248863
+ import { boolean, check, integer, jsonb, pgTable, primaryKey, text, uuid } from "drizzle-orm/pg-core";
248864
+
248865
+ // Structure for the reviewed starter migrations. Grants/policies stay in those migrations.
248866
+ export const application = pgTable.withRLS("starter_application", {
248867
+ singleton: boolean().default(true).primaryKey(),
248868
+ projectId: text("project_id").notNull(),
248869
+ tenantId: text("tenant_id").notNull(),
248870
+ }, table => [
248871
+ check("starter_application_singleton_check", sql\`\${table.singleton}\`),
248872
+ check("starter_application_project_id_check", sql\`length(\${table.projectId}) > 0\`),
248873
+ check("starter_application_tenant_id_check", sql\`length(\${table.tenantId}) > 0\`),
248874
+ ]);
248875
+
248876
+ export const reviews = pgTable.withRLS("starter_reviews", {
248877
+ id: text().primaryKey(),
248878
+ ownerId: text("owner_id").notNull(),
248879
+ state: text({ enum: ["draft", "approved"] }).notNull(),
248880
+ version: integer().notNull(),
248881
+ }, table => [
248882
+ check("starter_reviews_state_check", sql\`\${table.state} IN ('draft', 'approved')\`),
248883
+ check("starter_reviews_version_check", sql\`\${table.version} > 0\`),
248884
+ ]);
248885
+
248886
+ export const members = pgTable.withRLS("starter_members", {
248887
+ subject: text().primaryKey(),
248888
+ enabled: boolean().default(true).notNull(),
248889
+ canApprove: boolean("can_approve").default(false).notNull(),
248890
+ storageSubject: uuid("storage_subject").unique(),
248891
+ });
248892
+
248893
+ export const attachments = pgTable.withRLS("starter_attachments", {
248894
+ reviewId: text("review_id").primaryKey().references(() => reviews.id),
248895
+ artifactId: uuid("artifact_id").notNull().unique(),
248896
+ ownerId: text("owner_id").notNull(),
248897
+ runId: uuid("run_id").notNull().unique(),
248898
+ objectPath: text("object_path").notNull().unique(),
248899
+ });
248900
+
248901
+ export const attachmentResults = pgTable.withRLS("starter_attachment_results", {
248902
+ reviewId: text("review_id").notNull().references(() => reviews.id),
248903
+ version: integer().notNull(),
248904
+ artifactId: uuid("artifact_id").notNull(),
248905
+ result: jsonb().$type<unknown>().notNull(),
248906
+ }, table => [
248907
+ primaryKey({ columns: [table.reviewId, table.version, table.artifactId] }),
248908
+ check("starter_attachment_results_version_check", sql\`\${table.version} >= 2\`),
248909
+ ]);
248910
+
248911
+ export type ReviewRow = typeof reviews.$inferSelect;
248912
+ export type NewReview = typeof reviews.$inferInsert;
248913
+ `;
248914
+ var STARTER_DATABASE_GUIDE = `
248915
+ ## Database Source Workflow
248916
+
248917
+ Drizzle owns table declarations and inferred row types in db/schema.ts.
248918
+ Use the existing @supacloud/db/drizzle-bun adapters for typed queries and governed
248919
+ transactions; never replace permission, idempotency or audit checks with plain CRUD.
248920
+ Opaque JSON still needs runtime decoding.
248921
+
248922
+ After installing dependencies, run \`bun run db:generate\` once and commit db/contracts/.
248923
+ The default \`bun run check\` and build fail on stale database contracts before any
248924
+ compiler regeneration. After an intentional source change, run db:generate again,
248925
+ review the diff, then run check. Ordinary checks are offline.
248926
+
248927
+ - Maintain one schema-qualified function with explicit grants per db/functions/*.sql.
248928
+ - Preserve migrations/ and its existing executor/ledger. Add forward migrations only.
248929
+ - \`bun run db:diff\` generates Drizzle drafts under db/migration-candidates/, never
248930
+ into the deployment directory. The starter has existing SQL history, so establish
248931
+ and review a matching Drizzle baseline before promoting any draft; the first
248932
+ draft is not automatically an incremental production migration.
248933
+ - \`bun run db:pull\` requires an explicitly selected DATABASE_URL and writes only
248934
+ output/database-audit/drizzle-candidate/. Review candidates; never overwrite source blindly.
248935
+ - Full SQL dumps belong under the ignored output/database-audit/ directory. They are
248936
+ audit/recovery artifacts, never ordinary type, application or test inputs.
248937
+ - \`bun run db:assess\` explains adoption/findings without writing or connecting.
248938
+ - GraphQL/PostgREST exports remain legitimate protocol contracts. This check is not
248939
+ proof of deployed schema, effective role permissions or complete RLS equivalence.
248940
+
248941
+ Do not introduce any/@ts-ignore to bypass a contract. Use Drizzle-inferred types,
248942
+ preserve bigint/numeric precision and decode unknown results at the boundary.
248943
+ `;
248944
+ function starterDatabaseFiles() {
248945
+ return {
248946
+ "database.sources.json": JSON.stringify(STARTER_DATABASE_CONFIG, null, 2) + `
248947
+ `,
248948
+ "db/schema.ts": STARTER_DATABASE_SCHEMA,
248949
+ "db/functions/README.md": `Maintain one schema-qualified function and its explicit EXECUTE ACL per SQL file. No RPC functions are needed by the initial transactional starter.
248950
+ `,
248951
+ "drizzle.config.ts": `import { defineConfig } from "drizzle-kit";
248952
+ export default defineConfig({
248953
+ dialect: "postgresql",
248954
+ schema: "./db/schema.ts",
248955
+ out: "./db/migration-candidates",
248956
+ });
248957
+ `,
248958
+ "drizzle.pull.config.ts": `import { defineConfig } from "drizzle-kit";
248959
+ const url = process.env.DATABASE_URL;
248960
+ if (!url) throw new Error("Select an explicit DATABASE_URL for introspection");
248961
+ export default defineConfig({
248962
+ dialect: "postgresql",
248963
+ schema: "./db/schema.ts",
248964
+ out: "./output/database-audit/drizzle-candidate",
248965
+ dbCredentials: { url },
248966
+ });
248967
+ `
248968
+ };
248969
+ }
248970
+
248831
248971
  // src/shared/tools/app-starter.ts
248832
248972
  var json2 = (value) => `${JSON.stringify(value, null, 2)}
248833
248973
  `;
248834
248974
  function appStarterFiles(name) {
248835
248975
  return {
248976
+ ...starterDatabaseFiles(),
248836
248977
  "package.json": json2({
248837
248978
  name,
248838
248979
  version: "0.0.0",
@@ -248843,10 +248984,15 @@ function appStarterFiles(name) {
248843
248984
  compile: "bun --no-env-file node_modules/@supacloud/compiler/dist/cli.js compile",
248844
248985
  "check:generated": "bun --no-env-file node_modules/@supacloud/compiler/dist/cli.js check",
248845
248986
  typecheck: "tsc --noEmit",
248846
- check: "bun run compile && bun run check:generated && bun run typecheck && bun run test",
248987
+ "db:generate": "supacloud-db generate",
248988
+ "db:check": "supacloud-db check",
248989
+ "db:assess": "supacloud-db assess",
248990
+ "db:diff": "drizzle-kit generate --config drizzle.config.ts",
248991
+ "db:pull": "drizzle-kit pull --config drizzle.pull.config.ts",
248992
+ check: "bun run db:check && bun run compile && bun run check:generated && bun run typecheck && bun run test",
248847
248993
  test: "bun run compile && bun --no-env-file scripts/environment.ts test bun test",
248848
248994
  dev: "bun --no-env-file scripts/environment.ts development bun scripts/dev.ts",
248849
- build: "bun run compile && bun run typecheck && bun build src/application.ts --target bun --minify --outdir dist",
248995
+ build: "bun run db:check && bun run compile && bun run typecheck && bun build src/application.ts --target bun --minify --outdir dist",
248850
248996
  "env:development": "bun --no-env-file scripts/environment.ts development",
248851
248997
  "env:test": "bun --no-env-file scripts/environment.ts test",
248852
248998
  "env:staging": "bun --no-env-file scripts/environment.ts staging",
@@ -248858,6 +249004,7 @@ function appStarterFiles(name) {
248858
249004
  "@supacloud/commands": `^${package_default4.version}`,
248859
249005
  "@supacloud/contracts": `^${package_default5.version}`,
248860
249006
  "@supacloud/db": `^${package_default6.version}`,
249007
+ "drizzle-orm": package_default6.peerDependencies["drizzle-orm"],
248861
249008
  "@supacloud/js": `^${package_default7.version}`,
248862
249009
  "@supabase/supabase-js": package_default7.peerDependencies["@supabase/supabase-js"],
248863
249010
  elysia: "2.0.0-beta.19",
@@ -248865,6 +249012,7 @@ function appStarterFiles(name) {
248865
249012
  },
248866
249013
  devDependencies: {
248867
249014
  "@supacloud/compiler": `^${package_default.version}`,
249015
+ "drizzle-kit": package_default6.devDependencies["drizzle-kit"],
248868
249016
  "@types/bun": "^1.4.2",
248869
249017
  typescript: "^7.0.2"
248870
249018
  }
@@ -248880,13 +249028,15 @@ function appStarterFiles(name) {
248880
249028
  noEmit: true,
248881
249029
  types: ["bun"]
248882
249030
  },
248883
- include: ["src/**/*.ts", "scripts/**/*.ts", "tests/**/*.ts", "generated/**/*.ts", "supacloud.config.ts"]
249031
+ include: ["src/**/*.ts", "scripts/**/*.ts", "tests/**/*.ts", "generated/**/*.ts", "db/schema.ts", "db/contracts/**/*.ts", "supacloud.config.ts", "drizzle*.config.ts"]
248884
249032
  }),
248885
249033
  "bunfig.toml": `env = false
248886
249034
  `,
248887
249035
  ".gitignore": [
248888
249036
  "node_modules/",
248889
249037
  "dist/",
249038
+ "output/database-audit/",
249039
+ "db/migration-candidates/",
248890
249040
  ".env",
248891
249041
  ".env.*",
248892
249042
  "!.env.*.example",
@@ -248975,7 +249125,7 @@ test("generated query client preserves its read contract without a live database
248975
249125
  });
248976
249126
  `,
248977
249127
  "REACTIVE.md": STARTER_REACTIVE_GUIDE,
248978
- "AGENTS.md": STARTER_REACTIVE_AGENTS,
249128
+ "AGENTS.md": STARTER_REACTIVE_AGENTS + STARTER_DATABASE_GUIDE,
248979
249129
  "tests/reactive.test.ts": STARTER_REACTIVE_TEST,
248980
249130
  "scripts/environment.ts": STARTER_ENVIRONMENT,
248981
249131
  "tests/environment.test.ts": STARTER_ENVIRONMENT_TEST,
@@ -249077,6 +249227,7 @@ import { ApplicationError, assertFeatureTransition } from "@supacloud/elysia";
249077
249227
  import { t } from "elysia";
249078
249228
  import { VerifyReviewAttachment } from "./attachment";
249079
249229
  import { ReviewUploads, ReviewUploadsController, type ReviewUploadPort } from "./uploads";
249230
+ import type { ReviewRow } from "../../db/schema";
249080
249231
 
249081
249232
  export const reviewSpec = defineFeatureSpec({
249082
249233
  name: "review",
@@ -249091,7 +249242,7 @@ export const reviewSpec = defineFeatureSpec({
249091
249242
  },
249092
249243
  });
249093
249244
 
249094
- interface Review { state: string; version: number }
249245
+ type Review = Pick<ReviewRow, "state" | "version">;
249095
249246
  // Production implementations must bind both operations to the current transaction.
249096
249247
  export interface ReviewStore extends Partial<ReviewUploadPort> {
249097
249248
  get(table: string, key: string): unknown;
@@ -249114,7 +249265,7 @@ export const requireRequest: Aspect = (context, next) => {
249114
249265
 
249115
249266
  function readReview(value: unknown): Review {
249116
249267
  if (typeof value !== "object" || value === null ||
249117
- !("state" in value) || typeof value.state !== "string" ||
249268
+ !("state" in value) || (value.state !== "draft" && value.state !== "approved") ||
249118
249269
  !("version" in value) || typeof value.version !== "number") {
249119
249270
  throw new ApplicationError("Review not found", { status: 404, code: "REVIEW_NOT_FOUND" });
249120
249271
  }
@@ -249241,6 +249392,7 @@ test("the production composition root rejects missing governance adapters", () =
249241
249392
 
249242
249393
  \`\`\`sh
249243
249394
  bun install
249395
+ bun run db:generate
249244
249396
  bun run check
249245
249397
  bun run dev
249246
249398
  \`\`\`
@@ -249262,6 +249414,28 @@ compiler-checked command/route/governance bindings, an explicit AOP function,
249262
249414
  HTTP schemas, permission denial, transaction rollback, idempotency and audit.
249263
249415
  It is not a complete Maker-Checker workflow or a production authorization policy.
249264
249416
 
249417
+ ## Frontend SDK Boundary
249418
+
249419
+ Use @supacloud/js as the platform SDK around your existing user-scoped
249420
+ @supabase/supabase-js client. Pass that same client to existing UI providers;
249421
+ supacloud.supabase exposes it unchanged. Keep UI routing, forms and query
249422
+ caches in your frontend framework or svadmin, not in a new DI container.
249423
+
249424
+ For generated/client.ts, supply createAuthenticatedFetch from
249425
+ @supacloud/js/contracts as its fetch option. Resolve the current user token
249426
+ from the same Supabase session for each request; do not capture a token at
249427
+ startup or introduce a second session store. Use a fixed trusted HTTPS API
249428
+ origin. The authenticated transport rejects missing credentials and redirects
249429
+ and does not replay a write after 401. Its underlying fetch must also be
249430
+ single-attempt.
249431
+
249432
+ Reuse the same session resolver for generated/graphql.ts when adopting queries.
249433
+ The local memory demo is not this authenticated production integration.
249434
+ Never use supacloud.commands or supacloud.workflows from a browser action:
249435
+ those RPCs require service-role credentials. Browser business commands use
249436
+ application endpoints, optionally composed through @supacloud/js/contracts.
249437
+ Management API tokens and service-role keys remain on trusted servers.
249438
+
249265
249439
  ## Typed Queries
249266
249440
 
249267
249441
  This starter uses Database First as its only GraphQL server-schema model.
@@ -249530,6 +249704,8 @@ policy. Invalid transaction/idempotency modes fail compilation instead of
249530
249704
  silently disabling governance. onExecution receives metadata-only trace events;
249531
249705
  durable audit still belongs to the command governance adapter.
249532
249706
 
249707
+ ${STARTER_DATABASE_GUIDE}
249708
+
249533
249709
  ## CI
249534
249710
 
249535
249711
  After the first compilation, commit generated/. In CI run
@@ -249563,6 +249739,55 @@ async function initializeAppProject(options) {
249563
249739
  return { root, name, files: Object.keys(files) };
249564
249740
  }
249565
249741
 
249742
+ // src/shared/tools/app-database-check.ts
249743
+ import { spawnSync } from "node:child_process";
249744
+ import { existsSync as existsSync11 } from "node:fs";
249745
+ import { join as join14, resolve as resolve16 } from "node:path";
249746
+ var CHECK_ENTRY = `
249747
+ import { createRequire } from "node:module";
249748
+ import { join } from "node:path";
249749
+ import { pathToFileURL } from "node:url";
249750
+ const root = process.argv[1];
249751
+ const cli = createRequire(join(root, "package.json")).resolve("@supacloud/db/source-contracts-cli");
249752
+ process.argv = [process.execPath, cli, "check", "--root", root];
249753
+ await import(pathToFileURL(cli).href);
249754
+ `;
249755
+ function checkAppDatabaseSources(projectRoot) {
249756
+ const root = resolve16(projectRoot);
249757
+ if (!existsSync11(join14(root, "database.sources.json")))
249758
+ return;
249759
+ const name = "database-source-contracts";
249760
+ try {
249761
+ const result = spawnSync(process.versions.bun ? "bun" : process.execPath, [
249762
+ ...process.versions.bun ? ["--no-env-file"] : ["--input-type=module"],
249763
+ "--eval",
249764
+ CHECK_ENTRY,
249765
+ "--",
249766
+ root
249767
+ ], { cwd: root, encoding: "utf8", timeout: 30000, maxBuffer: 4 * 1024 * 1024 });
249768
+ if (result.error)
249769
+ throw result.error;
249770
+ const report = JSON.parse(result.stdout);
249771
+ if (!report || typeof report !== "object" || !("scope" in report) || report.scope !== "local-source-contracts" || !("ok" in report) || typeof report.ok !== "boolean" || !("findings" in report) || !Array.isArray(report.findings)) {
249772
+ throw new Error("Invalid database source check response");
249773
+ }
249774
+ const messages = [];
249775
+ for (const finding of report.findings) {
249776
+ if (!finding || typeof finding !== "object" || !("file" in finding) || typeof finding.file !== "string" || !("message" in finding) || typeof finding.message !== "string")
249777
+ throw new Error("Invalid source finding");
249778
+ messages.push(`${finding.file}: ${finding.message}`);
249779
+ }
249780
+ return {
249781
+ name,
249782
+ ok: result.status === 0 && report.ok && !messages.length,
249783
+ detail: messages.join(`
249784
+ `) || (result.status === 0 && report.ok ? "Offline database contracts and boundaries are current" : "Database source check failed")
249785
+ };
249786
+ } catch {
249787
+ return { name, ok: false, detail: "Cannot run the project database checker. Install the project runtime and @supacloud/db with source-contracts support, then run `supacloud-db check --root .` for diagnostics." };
249788
+ }
249789
+ }
249790
+
249566
249791
  // src/shared/tools/app-resource.ts
249567
249792
  function resourceScaffold(name, className) {
249568
249793
  return {
@@ -249641,7 +249866,7 @@ test("${name} controller preserves an asynchronous read failure", async () => {
249641
249866
  // src/shared/tools/app-scaffold-writes.ts
249642
249867
  import { randomUUID as randomUUID2 } from "node:crypto";
249643
249868
  import { link, lstat as lstat4, mkdir as mkdir5, open as open4, readFile as readFile4, realpath as realpath5, rename as rename5, rmdir, unlink as unlink3 } from "node:fs/promises";
249644
- import { dirname as dirname11, isAbsolute as isAbsolute6, join as join14, relative as relative11, resolve as resolve16, sep as sep11, win32 } from "node:path";
249869
+ import { dirname as dirname11, isAbsolute as isAbsolute6, join as join15, relative as relative11, resolve as resolve17, sep as sep11, win32 } from "node:path";
249645
249870
 
249646
249871
  class ScaffoldError extends Error {
249647
249872
  code;
@@ -249662,7 +249887,7 @@ function scaffoldPath(root, path) {
249662
249887
  if (parts.some((part) => /[<>:"|?*\u0000-\u001f]/.test(part) || /[ .]$/.test(part) && part !== "." && part !== ".." || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part) || part.toLowerCase() === ".git" || part.toLowerCase() === ".supacloud-generate.lock")) {
249663
249888
  throw new ScaffoldError("SCAFFOLD_PATH_INVALID", "Scaffold path is reserved or not portable");
249664
249889
  }
249665
- const target = resolve16(root, path);
249890
+ const target = resolve17(root, path);
249666
249891
  const local = relative11(root, target);
249667
249892
  if (!local || local === ".." || local.startsWith(`..${sep11}`) || isAbsolute6(local)) {
249668
249893
  throw new ScaffoldError("SCAFFOLD_PATH_INVALID", "Scaffold path is outside the project root");
@@ -249674,7 +249899,7 @@ async function snapshot(root, path) {
249674
249899
  const parts = relative11(root, target).split(sep11);
249675
249900
  let current = root;
249676
249901
  for (const [index, part] of parts.entries()) {
249677
- current = join14(current, part);
249902
+ current = join15(current, part);
249678
249903
  let stat;
249679
249904
  try {
249680
249905
  stat = await lstat4(current);
@@ -249701,7 +249926,7 @@ function sameFile(a, b) {
249701
249926
  return a === null ? b === null : b !== null && a.content === b.content && a.dev === b.dev && a.ino === b.ino && a.mode === b.mode;
249702
249927
  }
249703
249928
  async function planScaffoldWrites(root, writes) {
249704
- const canonical = await realpath5(resolve16(root));
249929
+ const canonical = await realpath5(resolve17(root));
249705
249930
  const targets = new Set;
249706
249931
  const planned = [];
249707
249932
  for (const write of writes) {
@@ -249724,7 +249949,7 @@ async function planScaffoldWrites(root, writes) {
249724
249949
  return Object.freeze({ root: canonical, writes: Object.freeze(planned) });
249725
249950
  }
249726
249951
  async function applyScaffoldWrites(plan) {
249727
- const lockPath = join14(plan.root, ".supacloud-generate.lock");
249952
+ const lockPath = join15(plan.root, ".supacloud-generate.lock");
249728
249953
  const lock = await open4(lockPath, "wx").catch((error) => {
249729
249954
  if (fsCode(error) === "EEXIST")
249730
249955
  throw new ScaffoldError("SCAFFOLD_BUSY", "Another generator owns .supacloud-generate.lock; no files written");
@@ -249745,7 +249970,7 @@ async function applyScaffoldWrites(plan) {
249745
249970
  const target = scaffoldPath(plan.root, write.path);
249746
249971
  let current = plan.root;
249747
249972
  for (const part of relative11(plan.root, dirname11(target)).split(sep11).filter(Boolean)) {
249748
- current = join14(current, part);
249973
+ current = join15(current, part);
249749
249974
  try {
249750
249975
  await mkdir5(current);
249751
249976
  directories.push(current);
@@ -249854,6 +250079,7 @@ async function initProject(args) {
249854
250079
  `Initialized ${name} in ${root} (${files.length} files, template: ${template}).`,
249855
250080
  `cd '${root.replaceAll("'", "'\\''")}'`,
249856
250081
  "bun install",
250082
+ ...template === "command" ? ["bun run db:generate"] : [],
249857
250083
  "bun run check",
249858
250084
  "bun run dev",
249859
250085
  "Local demo only. Production requires persistent governance and identity adapters; see README.md."
@@ -249960,7 +250186,7 @@ async function generateScaffold(args) {
249960
250186
  throw new ScaffoldError("SCAFFOLD_OPTION_INVALID", `--${flag.replaceAll("_", "-")} must be boolean`);
249961
250187
  }
249962
250188
  }
249963
- const root = resolve17(args.root || process.cwd());
250189
+ const root = resolve18(args.root || process.cwd());
249964
250190
  const dir = args.dir ?? "src/features";
249965
250191
  if (dir !== ".")
249966
250192
  scaffoldPath(root, dir);
@@ -249976,20 +250202,20 @@ async function generateScaffold(args) {
249976
250202
  let contractName;
249977
250203
  if (kind === "module" || kind === "resource") {
249978
250204
  const name = requireIdentifier(args.name, "name");
249979
- modulePath = join15(dir, name, `${name}.module.ts`).replaceAll("\\", "/");
250205
+ modulePath = join16(dir, name, `${name}.module.ts`).replaceAll("\\", "/");
249980
250206
  moduleSymbol = `${pascalName2(name)}Module`;
249981
250207
  if (kind === "module")
249982
250208
  writes.push({ path: modulePath, content: moduleScaffold(name), overwrite: args.force === true });
249983
250209
  else
249984
250210
  for (const [file, content] of Object.entries(resourceScaffold(name, pascalName2(name)))) {
249985
- writes.push({ path: join15(dir, name, file).replaceAll("\\", "/"), content });
250211
+ writes.push({ path: join16(dir, name, file).replaceAll("\\", "/"), content });
249986
250212
  }
249987
250213
  } else {
249988
250214
  const moduleName = requireIdentifier(args.module, "module");
249989
250215
  const name = kind === "controller" ? moduleName : requireIdentifier(args.name, "name");
249990
250216
  const subdir = kind === "command" ? "commands" : kind === "query" ? "queries" : kind === "job" ? "jobs" : kind === "contract" ? "contracts" : "";
249991
- const path = join15(dir, moduleName, subdir, `${name}.${kind}.ts`).replaceAll("\\", "/");
249992
- if (kind === "controller" && existsSync11(scaffoldPath(root, path))) {
250217
+ const path = join16(dir, moduleName, subdir, `${name}.${kind}.ts`).replaceAll("\\", "/");
250218
+ if (kind === "controller" && existsSync12(scaffoldPath(root, path))) {
249993
250219
  throw new ScaffoldError("SCAFFOLD_EXISTS", `Controller already exists: ${path}(请手工合并路由到现有 controller)`);
249994
250220
  }
249995
250221
  const content = kind === "command" ? commandScaffold(moduleName, name) : kind === "query" ? queryScaffold(moduleName, name) : kind === "job" ? jobScaffold(moduleName, name) : kind === "contract" ? contractScaffold(moduleName, name) : controllerScaffold(moduleName);
@@ -250007,7 +250233,7 @@ async function generateScaffold(args) {
250007
250233
  if (new RegExp(`\\b${moduleSymbol}\\b`).test(original)) {
250008
250234
  throw new ScaffoldError("SCAFFOLD_REGISTRATION_CONFLICT", `Registration target already references ${moduleSymbol}; review the import manually`);
250009
250235
  }
250010
- let importPath = relative12(dirname12(target), resolve17(root, modulePath)).replaceAll("\\", "/").replace(/\.ts$/, "");
250236
+ let importPath = relative12(dirname12(target), resolve18(root, modulePath)).replaceAll("\\", "/").replace(/\.ts$/, "");
250011
250237
  if (!importPath.startsWith("."))
250012
250238
  importPath = `./${importPath}`;
250013
250239
  const edit = await applyDiagnosticFix({
@@ -250038,7 +250264,7 @@ async function generateScaffold(args) {
250038
250264
  written: !args.dry_run,
250039
250265
  changes
250040
250266
  }, null, 2));
250041
- const lines = changes.map((change) => `${args.dry_run ? "PREVIEW" : "✅"} ${change.action}: ${join15(plan.root, change.path)}`);
250267
+ const lines = changes.map((change) => `${args.dry_run ? "PREVIEW" : "✅"} ${change.action}: ${join16(plan.root, change.path)}`);
250042
250268
  if (args.dry_run)
250043
250269
  lines.push("No files written. Omit --dry-run to apply this generation.");
250044
250270
  if (kind === "resource")
@@ -250074,17 +250300,17 @@ function parseInclude(include) {
250074
250300
  return patterns && patterns.length > 0 ? patterns : undefined;
250075
250301
  }
250076
250302
  function sourceRoot(args, root, configured) {
250077
- const hasConfig = ["ts", "mts", "js", "mjs"].some((extension) => existsSync11(join15(root, `supacloud.config.${extension}`)));
250303
+ const hasConfig = ["ts", "mts", "js", "mjs"].some((extension) => existsSync12(join16(root, `supacloud.config.${extension}`)));
250078
250304
  return args.root && !hasConfig ? "." : configured;
250079
250305
  }
250080
250306
  async function projectCompileConfig(args) {
250081
- const root = resolve17(args.root || process.cwd());
250082
- const configFile = ["ts", "mts", "js", "mjs"].map((extension) => join15(root, `supacloud.config.${extension}`)).find((candidate) => existsSync11(candidate)) ?? null;
250307
+ const root = resolve18(args.root || process.cwd());
250308
+ const configFile = ["ts", "mts", "js", "mjs"].map((extension) => join16(root, `supacloud.config.${extension}`)).find((candidate) => existsSync12(candidate)) ?? null;
250083
250309
  const loadedConfig = await loadSupacloudConfig(root);
250084
250310
  const defaults = resolveSupacloudConfig(loadedConfig, root);
250085
250311
  const configuredRoot = sourceRoot(args, root, loadedConfig.root);
250086
250312
  const include = parseInclude(args.include) ?? loadedConfig.include;
250087
- const outDir = args.out_dir ? resolve17(root, args.out_dir) : defaults.outDir;
250313
+ const outDir = args.out_dir ? resolve18(root, args.out_dir) : defaults.outDir;
250088
250314
  const result = await checkProject(compileOptionsFromConfig({
250089
250315
  ...loadedConfig,
250090
250316
  ...configuredRoot === undefined ? {} : { root: configuredRoot },
@@ -250095,8 +250321,8 @@ async function projectCompileConfig(args) {
250095
250321
  return { root, configFile, outDir, result };
250096
250322
  }
250097
250323
  async function readAllowedDependencies(root) {
250098
- const path = join15(root, "package.json");
250099
- if (!existsSync11(path))
250324
+ const path = join16(root, "package.json");
250325
+ if (!existsSync12(path))
250100
250326
  return { dependencies: [], devDependencies: [] };
250101
250327
  try {
250102
250328
  const parsed = JSON.parse(await readFile8(path, "utf8"));
@@ -250183,7 +250409,7 @@ async function findUnwiredContracts(rootDir) {
250183
250409
  for (const entry of entries) {
250184
250410
  if (skip.has(entry.name))
250185
250411
  continue;
250186
- const path = join15(directory, entry.name);
250412
+ const path = join16(directory, entry.name);
250187
250413
  if (entry.isDirectory())
250188
250414
  await walk(path);
250189
250415
  else if (entry.isFile() && entry.name.endsWith(".ts"))
@@ -250211,6 +250437,12 @@ async function findUnwiredContracts(rootDir) {
250211
250437
  async function runDoctor(args) {
250212
250438
  const { root, outDir, result } = await projectCompileConfig(args);
250213
250439
  const doctor = doctorProject(root, outDir, result.graph, result.upToDate, result.diagnostics);
250440
+ const database = checkAppDatabaseSources(root);
250441
+ if (database) {
250442
+ doctor.checks.push(database);
250443
+ if (!database.ok)
250444
+ doctor.errors += 1;
250445
+ }
250214
250446
  const fixPlan = doctorFixPlan(doctor);
250215
250447
  const autoFixable = fixPlan.filter((repair) => repair.readiness === "preview").length;
250216
250448
  const inputRequired = fixPlan.filter((repair) => repair.readiness === "input-required").length;
@@ -250241,11 +250473,11 @@ async function runDoctor(args) {
250241
250473
  `), doctor.errors > 0);
250242
250474
  }
250243
250475
  async function runFix(args) {
250244
- const root = resolve17(args.root || process.cwd());
250476
+ const root = resolve18(args.root || process.cwd());
250245
250477
  const fixPath = args.fix?.trim();
250246
250478
  if (!fixPath)
250247
250479
  throw new Error("app fix requires --fix <path-to-diagnostic-fix.json>");
250248
- const parsed = JSON.parse(await readFile8(resolve17(root, fixPath), "utf8"));
250480
+ const parsed = JSON.parse(await readFile8(resolve18(root, fixPath), "utf8"));
250249
250481
  const write = args.write === true;
250250
250482
  const applied = await applyDiagnosticFix(parsed, { rootDir: root, dryRun: !write });
250251
250483
  return textResult(JSON.stringify({
@@ -250288,7 +250520,7 @@ function devDatabase(profile, explicit) {
250288
250520
  }
250289
250521
  function readManifestModules(outDir) {
250290
250522
  try {
250291
- const parsed = JSON.parse(readFileSync9(join15(outDir, "app.manifest.json"), "utf8"));
250523
+ const parsed = JSON.parse(readFileSync9(join16(outDir, "app.manifest.json"), "utf8"));
250292
250524
  return Array.isArray(parsed.modules) ? parsed.modules.map((module) => module.name) : [];
250293
250525
  } catch {
250294
250526
  return [];
@@ -250321,12 +250553,12 @@ async function runAppDev(args, options) {
250321
250553
  throw new ScaffoldError("SCAFFOLD_OPTION_INVALID", "app dev --profile must be fast or integration");
250322
250554
  }
250323
250555
  const database = devDatabase(profile, args.database_url);
250324
- const root = resolve17(args.root || process.cwd());
250556
+ const root = resolve18(args.root || process.cwd());
250325
250557
  const loadedConfig = await loadSupacloudConfig(root);
250326
250558
  const defaults = resolveSupacloudConfig(loadedConfig, root);
250327
250559
  const configuredRoot = sourceRoot(args, root, loadedConfig.root);
250328
250560
  const include = parseInclude(args.include) ?? loadedConfig.include;
250329
- const outDir = args.out_dir ? resolve17(root, args.out_dir) : defaults.outDir;
250561
+ const outDir = args.out_dir ? resolve18(root, args.out_dir) : defaults.outDir;
250330
250562
  const watch = args.once !== true && args.watch !== false;
250331
250563
  const compileOptions = compileOptionsFromConfig({
250332
250564
  ...loadedConfig,
@@ -250412,7 +250644,7 @@ async function runAppDev(args, options) {
250412
250644
  return report(lastEvent, "stopped");
250413
250645
  }
250414
250646
  async function runCompile(args) {
250415
- const root = resolve17(args.root || process.cwd());
250647
+ const root = resolve18(args.root || process.cwd());
250416
250648
  const loadedConfig = await loadSupacloudConfig(root);
250417
250649
  const defaults = resolveSupacloudConfig(loadedConfig, root);
250418
250650
  const configuredRoot = sourceRoot(args, root, loadedConfig.root);
@@ -250421,7 +250653,7 @@ async function runCompile(args) {
250421
250653
  ...compileOptionsFromConfig({
250422
250654
  ...loadedConfig,
250423
250655
  ...configuredRoot === undefined ? {} : { root: configuredRoot },
250424
- outDir: args.out_dir ? resolve17(root, args.out_dir) : defaults.outDir,
250656
+ outDir: args.out_dir ? resolve18(root, args.out_dir) : defaults.outDir,
250425
250657
  ...include === undefined ? {} : { include },
250426
250658
  strict: args.strict ?? loadedConfig.strict ?? false
250427
250659
  }, root),
@@ -250445,7 +250677,8 @@ async function runCompile(args) {
250445
250677
  return textResult(text, hasError);
250446
250678
  }
250447
250679
  async function runCheck(args) {
250448
- const root = resolve17(args.root || process.cwd());
250680
+ const root = resolve18(args.root || process.cwd());
250681
+ const database = checkAppDatabaseSources(root);
250449
250682
  const loadedConfig = await loadSupacloudConfig(root);
250450
250683
  const defaults = resolveSupacloudConfig(loadedConfig, root);
250451
250684
  const configuredRoot = sourceRoot(args, root, loadedConfig.root);
@@ -250453,12 +250686,12 @@ async function runCheck(args) {
250453
250686
  const config = compileOptionsFromConfig({
250454
250687
  ...loadedConfig,
250455
250688
  ...configuredRoot === undefined ? {} : { root: configuredRoot },
250456
- outDir: args.out_dir ? resolve17(root, args.out_dir) : defaults.outDir,
250689
+ outDir: args.out_dir ? resolve18(root, args.out_dir) : defaults.outDir,
250457
250690
  ...include === undefined ? {} : { include },
250458
250691
  strict: args.strict ?? loadedConfig.strict ?? false
250459
250692
  }, root);
250460
250693
  const result = await checkProject(config);
250461
- const hasError = result.diagnostics.some((diagnostic) => diagnostic.severity === "error") || result.mismatches.length > 0;
250694
+ const hasError = result.diagnostics.some((diagnostic) => diagnostic.severity === "error") || result.mismatches.length > 0 || database?.ok === false;
250462
250695
  if (args.format === "json")
250463
250696
  return textResult(JSON.stringify({
250464
250697
  version: 1,
@@ -250468,12 +250701,14 @@ async function runCheck(args) {
250468
250701
  mismatches: result.mismatches,
250469
250702
  upToDate: result.upToDate,
250470
250703
  written: [],
250471
- modules: result.graph.modules.map((module) => module.name)
250704
+ modules: result.graph.modules.map((module) => module.name),
250705
+ ...database ? { database } : {}
250472
250706
  }, null, 2), hasError);
250473
250707
  const summary = `checked ${result.graph.modules.length} module(s), no files written`;
250474
250708
  return textResult([
250475
250709
  formatDiagnostics(result.diagnostics),
250476
250710
  ...result.mismatches.map((path) => `generated artifact mismatch: ${path}`),
250711
+ ...database ? [`${database.ok ? "OK" : "FAIL"} ${database.name}: ${database.detail}`] : [],
250477
250712
  "",
250478
250713
  summary
250479
250714
  ].join(`
@@ -250481,9 +250716,9 @@ async function runCheck(args) {
250481
250716
  }
250482
250717
  async function readManifest(root, args) {
250483
250718
  const config = await loadSupacloudConfig(root);
250484
- const outDir = args.out_dir ? resolve17(root, args.out_dir) : resolveSupacloudConfig(config, root).outDir;
250485
- const manifestPath = join15(outDir, "app.manifest.json");
250486
- if (!existsSync11(manifestPath)) {
250719
+ const outDir = args.out_dir ? resolve18(root, args.out_dir) : resolveSupacloudConfig(config, root).outDir;
250720
+ const manifestPath = join16(outDir, "app.manifest.json");
250721
+ if (!existsSync12(manifestPath)) {
250487
250722
  throw new Error(`Manifest not found: ${manifestPath}(先运行 app compile 生成)`);
250488
250723
  }
250489
250724
  const parsed = JSON.parse(await readFile8(manifestPath, "utf8"));
@@ -250526,7 +250761,7 @@ function formatGraphText(manifest) {
250526
250761
  `);
250527
250762
  }
250528
250763
  async function runGraph(args) {
250529
- const root = resolve17(args.root || process.cwd());
250764
+ const root = resolve18(args.root || process.cwd());
250530
250765
  const manifest = await readManifest(root, args);
250531
250766
  if (args.format === "json")
250532
250767
  return textResult(JSON.stringify(manifest, null, 2));
@@ -250594,7 +250829,7 @@ async function runExplain(args) {
250594
250829
  const target = args.target?.trim();
250595
250830
  if (!target)
250596
250831
  throw new Error("app explain requires --target(provider 类名 / token 名 / command 名 / job 名)");
250597
- const root = resolve17(args.root || process.cwd());
250832
+ const root = resolve18(args.root || process.cwd());
250598
250833
  const manifest = await readManifest(root, args);
250599
250834
  for (const module of manifest.modules) {
250600
250835
  const job = module.jobs?.find((entry) => entry.name === target || entry.className === target);
@@ -250669,16 +250904,16 @@ async function runExplain(args) {
250669
250904
  return textResult(`未找到对象: ${target}`, true);
250670
250905
  }
250671
250906
  async function runExportTools(args) {
250672
- const root = resolve17(args.root || process.cwd());
250907
+ const root = resolve18(args.root || process.cwd());
250673
250908
  const manifest = await readManifest(root, { ...args, out_dir: undefined });
250674
250909
  const definitions = buildToolDefinitions(manifest);
250675
- const outDir = resolve17(args.out_dir || join15(root, "generated"));
250910
+ const outDir = resolve18(args.out_dir || join16(root, "generated"));
250676
250911
  if (args.format === "json") {
250677
250912
  return textResult(JSON.stringify(definitions, null, 2));
250678
250913
  }
250679
250914
  await mkdir6(outDir, { recursive: true });
250680
- const openaiPath = join15(outDir, "tool-definitions.openai.json");
250681
- const mcpPath = join15(outDir, "tool-definitions.mcp.json");
250915
+ const openaiPath = join16(outDir, "tool-definitions.openai.json");
250916
+ const mcpPath = join16(outDir, "tool-definitions.mcp.json");
250682
250917
  await writeFile5(openaiPath, JSON.stringify(definitions.openai, null, 2), "utf8");
250683
250918
  await writeFile5(mcpPath, JSON.stringify(definitions.mcp, null, 2), "utf8");
250684
250919
  const summary = [
@@ -250693,11 +250928,17 @@ async function runExportTools(args) {
250693
250928
  return textResult(summary);
250694
250929
  }
250695
250930
  async function runDelivery(args) {
250696
- const root = resolve17(args.root || process.cwd());
250931
+ const root = resolve18(args.root || process.cwd());
250932
+ if (args.action === "build") {
250933
+ const database = checkAppDatabaseSources(root);
250934
+ if (database && !database.ok) {
250935
+ return textResult(JSON.stringify({ ok: false, database, written: [] }, null, 2), true);
250936
+ }
250937
+ }
250697
250938
  const loaded = await loadSupacloudConfig(root);
250698
250939
  const options = compileOptionsFromConfig({
250699
250940
  ...loaded,
250700
- ...args.out_dir === undefined ? {} : { outDir: resolve17(root, args.out_dir) },
250941
+ ...args.out_dir === undefined ? {} : { outDir: resolve18(root, args.out_dir) },
250701
250942
  ...args.include === undefined ? {} : { include: parseInclude(args.include) },
250702
250943
  ...args.strict === undefined ? {} : { strict: args.strict }
250703
250944
  }, root);
@@ -250855,8 +251096,8 @@ function registerAppAliases(server2) {
250855
251096
  }
250856
251097
 
250857
251098
  // src/shared/tools/db-governance-tools.ts
250858
- import { existsSync as existsSync12 } from "node:fs";
250859
- import { join as join16, resolve as resolve18 } from "node:path";
251099
+ import { existsSync as existsSync13 } from "node:fs";
251100
+ import { join as join17, resolve as resolve20 } from "node:path";
250860
251101
 
250861
251102
  // node_modules/@supacloud/db/dist/index.js
250862
251103
  var TABLES_SQL = `
@@ -251308,8 +251549,8 @@ function normalizeModule(candidate, sourcePath) {
251308
251549
  };
251309
251550
  }
251310
251551
  async function loadDatabaseModules(root, moduleFile) {
251311
- const filePath = moduleFile ? resolve18(root, moduleFile) : join16(root, "db", "modules.ts");
251312
- if (!existsSync12(filePath)) {
251552
+ const filePath = moduleFile ? resolve20(root, moduleFile) : join17(root, "db", "modules.ts");
251553
+ if (!existsSync13(filePath)) {
251313
251554
  throw new Error(moduleFile ? `Module file not found: ${filePath}` : `Module file not found: ${filePath}(用 --module_file 指定一个导出 defineDatabaseModule(...) 的文件)`);
251314
251555
  }
251315
251556
  let imported;
@@ -251357,7 +251598,7 @@ async function runLint(args, root) {
251357
251598
  const issues = [];
251358
251599
  for (const module of selected) {
251359
251600
  const readSqlFile = async (path) => {
251360
- const absolute = resolve18(root, path);
251601
+ const absolute = resolve20(root, path);
251361
251602
  try {
251362
251603
  return await Bun.file(absolute).text();
251363
251604
  } catch (error) {
@@ -251393,14 +251634,14 @@ var defaultLiteSpawn = async (command, cwd) => {
251393
251634
  return { exitCode, stdout, stderr };
251394
251635
  };
251395
251636
  async function runModuleCheckLite(args, root, spawn, liteBinary) {
251396
- const projectDir = resolve18(args.project_dir?.trim() || root);
251637
+ const projectDir = resolve20(args.project_dir?.trim() || root);
251397
251638
  const binary = liteBinary === undefined ? Bun.which("supacloud-lite") : liteBinary;
251398
251639
  if (!binary) {
251399
251640
  throw new Error("db module_check --lite 需要本机可用的 supacloud-lite(npm i -g @supacloud/lite,或加入 PATH)");
251400
251641
  }
251401
251642
  const command = [binary, "db", "check", "--project-dir", projectDir];
251402
251643
  if (args.module_file?.trim())
251403
- command.push("--module-file", resolve18(root, args.module_file.trim()));
251644
+ command.push("--module-file", resolve20(root, args.module_file.trim()));
251404
251645
  const result = await spawn(command, projectDir);
251405
251646
  const output = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join(`
251406
251647
  `);
@@ -251451,7 +251692,7 @@ function registerDbGovernanceTools(server2, options = {}) {
251451
251692
  lite: optional(Type.Boolean(), "[module_check] Reconcile against a local SupaCloud Lite project (runs supacloud-lite db check)"),
251452
251693
  project_dir: optional(Type.String(), "[module_check] Lite project directory (with --lite; default: --root)")
251453
251694
  }, async (request) => {
251454
- const root = resolve18(request.root || fallbackRoot);
251695
+ const root = resolve20(request.root || fallbackRoot);
251455
251696
  switch (request.action) {
251456
251697
  case "lint":
251457
251698
  return runLint(request, root);
@@ -251468,7 +251709,7 @@ function registerDbGovernanceTools(server2, options = {}) {
251468
251709
  // src/shared/tools/scheduled-function-tools.ts
251469
251710
  import { randomUUID as randomUUID3 } from "node:crypto";
251470
251711
  import { readFileSync as readFileSync10, statSync as statSync7 } from "node:fs";
251471
- import { resolve as resolve20 } from "node:path";
251712
+ import { resolve as resolve21 } from "node:path";
251472
251713
  import { isDeepStrictEqual } from "node:util";
251473
251714
  var HEADER_NAME_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$/;
251474
251715
  var ENVIRONMENT_NAME_PATTERN2 = /^[A-Za-z_][A-Za-z0-9_]{0,255}$/;
@@ -251552,7 +251793,7 @@ function validScheduledFunctionCron(expression) {
251552
251793
  function readScheduleBodyFile(bodyPathInput) {
251553
251794
  if (!bodyPathInput.trim())
251554
251795
  throw new Error("'body_file' must be a path");
251555
- const bodyPath = resolve20(bodyPathInput);
251796
+ const bodyPath = resolve21(bodyPathInput);
251556
251797
  const bodyStat = statSync7(bodyPath);
251557
251798
  if (!bodyStat.isFile() || bodyStat.size > MAX_BODY_FILE_BYTES) {
251558
251799
  throw new Error("Scheduled Function body file must be a regular file no larger than 1 MiB");
@@ -252135,8 +252376,8 @@ var MUTATION_TOOL_SCHEMA = {
252135
252376
  // src/shared/tools/release-tools.ts
252136
252377
  import { execFileSync } from "node:child_process";
252137
252378
  import { createHash as createHash10 } from "node:crypto";
252138
- import { existsSync as existsSync13, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "node:fs";
252139
- import { dirname as dirname13, join as join17, relative as relative13, resolve as resolve21 } from "node:path";
252379
+ import { existsSync as existsSync14, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "node:fs";
252380
+ import { dirname as dirname13, join as join18, relative as relative13, resolve as resolve23 } from "node:path";
252140
252381
  var SAFE_PROJECT_REF = /^[A-Za-z0-9_-]{1,64}$/;
252141
252382
  var BACKUP_ID = /^logical-full_[A-Za-z0-9_-]{1,64}_[a-f0-9]{32}$/;
252142
252383
  var SHA256 = /^[a-f0-9]{64}$/;
@@ -252430,15 +252671,15 @@ function collectScopeFiles(cwd, file, files, dir, task) {
252430
252671
  const results = [];
252431
252672
  const resolvePathWithFallbacks = (filePath) => {
252432
252673
  const trimmed = filePath.trim();
252433
- const resolved = resolve21(cwd, trimmed);
252434
- if (existsSync13(resolved))
252674
+ const resolved = resolve23(cwd, trimmed);
252675
+ if (existsSync14(resolved))
252435
252676
  return resolved;
252436
252677
  const candidates = [
252437
- resolve21(cwd, "supacloud/fa/release-scopes", trimmed),
252438
- resolve21(cwd, "release-scopes", trimmed),
252439
- resolve21(cwd, "scopes", trimmed)
252678
+ resolve23(cwd, "supacloud/fa/release-scopes", trimmed),
252679
+ resolve23(cwd, "release-scopes", trimmed),
252680
+ resolve23(cwd, "scopes", trimmed)
252440
252681
  ];
252441
- const found = candidates.find(existsSync13);
252682
+ const found = candidates.find(existsSync14);
252442
252683
  if (found)
252443
252684
  return found;
252444
252685
  throw new Error(`Scope file not found: ${filePath}`);
@@ -252459,18 +252700,18 @@ function collectScopeFiles(cwd, file, files, dir, task) {
252459
252700
  if (!taskFilter && (!dir || typeof dir !== "string" || !dir.trim())) {
252460
252701
  throw new Error("At least one of --file, --files, or --task is required for scope_rebind");
252461
252702
  }
252462
- const candidates = typeof dir === "string" && dir.trim() ? [resolve21(cwd, dir.trim())] : [
252463
- resolve21(cwd, "supacloud/fa/release-scopes"),
252464
- resolve21(cwd, "release-scopes"),
252465
- resolve21(cwd, "scopes")
252703
+ const candidates = typeof dir === "string" && dir.trim() ? [resolve23(cwd, dir.trim())] : [
252704
+ resolve23(cwd, "supacloud/fa/release-scopes"),
252705
+ resolve23(cwd, "release-scopes"),
252706
+ resolve23(cwd, "scopes")
252466
252707
  ];
252467
252708
  for (const directory of candidates) {
252468
- if (existsSync13(directory)) {
252709
+ if (existsSync14(directory)) {
252469
252710
  try {
252470
252711
  const entries = readdirSync4(directory, { withFileTypes: true });
252471
252712
  for (const entry of entries) {
252472
252713
  if (entry.isFile() && entry.name.endsWith(".json")) {
252473
- const fullPath = join17(directory, entry.name);
252714
+ const fullPath = join18(directory, entry.name);
252474
252715
  if (!taskFilter) {
252475
252716
  results.push(fullPath);
252476
252717
  } else if (entry.name.toLowerCase().includes(taskFilter)) {
@@ -252496,7 +252737,7 @@ function collectScopeFiles(cwd, file, files, dir, task) {
252496
252737
  return [...new Set(results)];
252497
252738
  }
252498
252739
  function handleScopeRebind(args) {
252499
- const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve21(args.cwd.trim()) : process.cwd();
252740
+ const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve23(args.cwd.trim()) : process.cwd();
252500
252741
  const targetBaseCommit = resolveGitBaseCommit(cwd, args.base_commit);
252501
252742
  const scopeFiles = collectScopeFiles(cwd, args.file, args.files, args.dir, args.task);
252502
252743
  const dryRun = args.dry_run === true;
@@ -252548,19 +252789,19 @@ function handleScopeRebind(args) {
252548
252789
  });
252549
252790
  }
252550
252791
  function handleScopeInspect(args) {
252551
- const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve21(args.cwd.trim()) : process.cwd();
252792
+ const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve23(args.cwd.trim()) : process.cwd();
252552
252793
  if (typeof args.file !== "string" || !args.file.trim()) {
252553
252794
  throw new Error("'file' is required for scope_inspect");
252554
252795
  }
252555
252796
  const trimmed = args.file.trim();
252556
- let filePath = resolve21(cwd, trimmed);
252557
- if (!existsSync13(filePath)) {
252797
+ let filePath = resolve23(cwd, trimmed);
252798
+ if (!existsSync14(filePath)) {
252558
252799
  const candidates = [
252559
- resolve21(cwd, "supacloud/fa/release-scopes", trimmed),
252560
- resolve21(cwd, "release-scopes", trimmed),
252561
- resolve21(cwd, "scopes", trimmed)
252800
+ resolve23(cwd, "supacloud/fa/release-scopes", trimmed),
252801
+ resolve23(cwd, "release-scopes", trimmed),
252802
+ resolve23(cwd, "scopes", trimmed)
252562
252803
  ];
252563
- const found = candidates.find(existsSync13);
252804
+ const found = candidates.find(existsSync14);
252564
252805
  if (found)
252565
252806
  filePath = found;
252566
252807
  else
@@ -252618,7 +252859,7 @@ function parseCommaList(value) {
252618
252859
  return [];
252619
252860
  }
252620
252861
  function handleScopeCreate(args, fallbackRef) {
252621
- const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve21(args.cwd.trim()) : process.cwd();
252862
+ const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve23(args.cwd.trim()) : process.cwd();
252622
252863
  if (typeof args.file !== "string" || !args.file.trim()) {
252623
252864
  throw new Error("'file' is required for scope_create");
252624
252865
  }
@@ -252633,7 +252874,7 @@ function handleScopeCreate(args, fallbackRef) {
252633
252874
  throw new Error("'ref' is invalid for release controls");
252634
252875
  }
252635
252876
  const targetBaseCommit = resolveGitBaseCommit(cwd, args.base_commit);
252636
- const filePath = resolve21(cwd, args.file.trim());
252877
+ const filePath = resolve23(cwd, args.file.trim());
252637
252878
  mkdirSync4(dirname13(filePath), { recursive: true });
252638
252879
  const scope = {
252639
252880
  baseCommit: targetBaseCommit,
@@ -252858,7 +253099,7 @@ function registerReleaseTools(server2, http, options = {}) {
252858
253099
  import { access as access4, lstat as lstat5, mkdtemp as mkdtemp2, opendir, readFile as readFile9, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
252859
253100
  import { createHash as createHash11 } from "node:crypto";
252860
253101
  import { tmpdir as tmpdir2 } from "node:os";
252861
- import { basename as basename9, dirname as dirname14, join as join18, relative as relative14, resolve as resolve23, sep as sep12 } from "node:path";
253102
+ import { basename as basename9, dirname as dirname14, join as join19, relative as relative14, resolve as resolve24, sep as sep12 } from "node:path";
252862
253103
  import { spawn as spawn3 } from "node:child_process";
252863
253104
 
252864
253105
  // node_modules/fflate/esm/index.mjs
@@ -253550,13 +253791,13 @@ async function readJson(path) {
253550
253791
  }
253551
253792
  }
253552
253793
  async function findDeployConfigRoot(startDirectory) {
253553
- let directory = resolve23(startDirectory);
253794
+ let directory = resolve24(startDirectory);
253554
253795
  while (true) {
253555
- if (await pathExists(join18(directory, "supacloud.json")))
253796
+ if (await pathExists(join19(directory, "supacloud.json")))
253556
253797
  return directory;
253557
253798
  const parent = dirname14(directory);
253558
253799
  if (parent === directory)
253559
- return resolve23(startDirectory);
253800
+ return resolve24(startDirectory);
253560
253801
  directory = parent;
253561
253802
  }
253562
253803
  }
@@ -253599,7 +253840,7 @@ function deployTargetConfig(candidate, name) {
253599
253840
  };
253600
253841
  }
253601
253842
  async function readDeployConfig(configRoot) {
253602
- const configPath = join18(configRoot, "supacloud.json");
253843
+ const configPath = join19(configRoot, "supacloud.json");
253603
253844
  const candidate = await readJson(configPath);
253604
253845
  if (candidate === null)
253605
253846
  return {};
@@ -253621,25 +253862,25 @@ async function readDeployConfig(configRoot) {
253621
253862
  };
253622
253863
  }
253623
253864
  async function packageName(projectDirectory) {
253624
- const candidate = record4(await readJson(join18(projectDirectory, "package.json")));
253865
+ const candidate = record4(await readJson(join19(projectDirectory, "package.json")));
253625
253866
  return typeof candidate?.name === "string" && candidate.name.trim() ? candidate.name.trim() : null;
253626
253867
  }
253627
253868
  async function pathExists(path) {
253628
253869
  return access4(path).then(() => true, () => false);
253629
253870
  }
253630
253871
  async function defaultBuildCommand(projectDirectory, configRoot) {
253631
- const packageJson = record4(await readJson(join18(projectDirectory, "package.json")));
253872
+ const packageJson = record4(await readJson(join19(projectDirectory, "package.json")));
253632
253873
  const scripts = record4(packageJson?.scripts);
253633
253874
  if (typeof scripts?.build !== "string" || !scripts.build.trim()) {
253634
253875
  throw new Error("No build command was configured and package.json has no build script");
253635
253876
  }
253636
253877
  let directory = projectDirectory;
253637
253878
  while (true) {
253638
- if (await pathExists(join18(directory, "bun.lock")) || await pathExists(join18(directory, "bun.lockb")))
253879
+ if (await pathExists(join19(directory, "bun.lock")) || await pathExists(join19(directory, "bun.lockb")))
253639
253880
  return "bun run build";
253640
- if (await pathExists(join18(directory, "pnpm-lock.yaml")))
253881
+ if (await pathExists(join19(directory, "pnpm-lock.yaml")))
253641
253882
  return "pnpm run build";
253642
- if (await pathExists(join18(directory, "yarn.lock")))
253883
+ if (await pathExists(join19(directory, "yarn.lock")))
253643
253884
  return "yarn build";
253644
253885
  if (directory === configRoot)
253645
253886
  break;
@@ -253674,9 +253915,9 @@ function selectDeployment(deployments, requestedId, projectName) {
253674
253915
  throw new Error(deployments.length === 0 ? "No frontend deployments exist for this project" : `Multiple frontend deployments found (${ids}). Set frontend.id in supacloud.json or pass --id`);
253675
253916
  }
253676
253917
  function targetRoot(configRoot, target, name) {
253677
- const root = resolve23(configRoot, target.root || (name === "frontend" ? "." : name));
253678
- const prefix = `${resolve23(configRoot)}${sep12}`;
253679
- if (root !== resolve23(configRoot) && !root.startsWith(prefix)) {
253918
+ const root = resolve24(configRoot, target.root || (name === "frontend" ? "." : name));
253919
+ const prefix = `${resolve24(configRoot)}${sep12}`;
253920
+ if (root !== resolve24(configRoot) && !root.startsWith(prefix)) {
253680
253921
  throw new Error(`Deploy target '${name}' root must stay inside the repository root`);
253681
253922
  }
253682
253923
  return root;
@@ -253727,8 +253968,8 @@ function defaultOutputDirectory(framework) {
253727
253968
  }
253728
253969
  }
253729
253970
  function resolveInsideProject(projectDirectory, candidate, allowRoot = false) {
253730
- const resolved = resolve23(projectDirectory, candidate);
253731
- const projectRoot = resolve23(projectDirectory);
253971
+ const resolved = resolve24(projectDirectory, candidate);
253972
+ const projectRoot = resolve24(projectDirectory);
253732
253973
  const projectPrefix = `${projectRoot}${sep12}`;
253733
253974
  if (!allowRoot && resolved === projectRoot || resolved !== projectRoot && !resolved.startsWith(projectPrefix)) {
253734
253975
  throw new Error("Frontend output directory must stay inside the project directory");
@@ -253759,7 +254000,7 @@ async function collectArchiveFiles(root) {
253759
254000
  entries.push(entry);
253760
254001
  entries.sort((left, right) => left.name.localeCompare(right.name, "en"));
253761
254002
  for (const entry of entries) {
253762
- const path = join18(directory, entry.name);
254003
+ const path = join19(directory, entry.name);
253763
254004
  const stats = await lstat5(path);
253764
254005
  if (stats.isSymbolicLink())
253765
254006
  throw new Error(`Frontend output cannot contain symbolic links: ${path}`);
@@ -253788,8 +254029,8 @@ async function collectArchiveFiles(root) {
253788
254029
  }
253789
254030
  async function createFrontendArchive(outputDirectory) {
253790
254031
  const collected = await collectArchiveFiles(outputDirectory);
253791
- const temporaryDirectory = await mkdtemp2(join18(tmpdir2(), "supacloud-deploy-"));
253792
- const archivePath = join18(temporaryDirectory, `${basename9(outputDirectory)}.zip`);
254032
+ const temporaryDirectory = await mkdtemp2(join19(tmpdir2(), "supacloud-deploy-"));
254033
+ const archivePath = join19(temporaryDirectory, `${basename9(outputDirectory)}.zip`);
253793
254034
  try {
253794
254035
  const archiveBytes = zipSync(collected.files, { level: 6, mtime: FIXED_ZIP_MTIME });
253795
254036
  await writeFile6(archivePath, archiveBytes);
@@ -253839,7 +254080,7 @@ function deployResponse(result, json) {
253839
254080
  }
253840
254081
  function registerDeployTools(server2, http, options = {}) {
253841
254082
  server2.tool("deploy", "Build and publish the linked frontend with one command", deployToolSchema, async (args) => {
253842
- const invocationDirectory = resolve23(String(args.cwd || options.cwd || process.cwd()));
254083
+ const invocationDirectory = resolve24(String(args.cwd || options.cwd || process.cwd()));
253843
254084
  const projectRef = String(args.ref || options.projectRef || "").trim();
253844
254085
  if (!projectRef)
253845
254086
  throw new Error("A linked project ref is required. Set SUPACLOUD_PROJECT_REF or pass --ref");
@@ -254012,11 +254253,11 @@ function registerDeployTools(server2, http, options = {}) {
254012
254253
 
254013
254254
  // src/shared/tools/remote-dev-tools.ts
254014
254255
  import { spawn as spawn4 } from "node:child_process";
254015
- import { existsSync as existsSync14 } from "node:fs";
254256
+ import { existsSync as existsSync15 } from "node:fs";
254016
254257
  import { readFile as readFile10 } from "node:fs/promises";
254017
254258
  import { readdir as readdir6 } from "node:fs/promises";
254018
254259
  import { createHash as createHash12 } from "node:crypto";
254019
- import { join as join19, resolve as resolve24 } from "node:path";
254260
+ import { join as join20, resolve as resolve25 } from "node:path";
254020
254261
  var SAFE_TOKEN = /^[A-Za-z0-9._:@/+,-]+$/;
254021
254262
  var SAFE_REMOTE_ROOT = /^\/(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+\/?$/;
254022
254263
  var remoteDevToolSchema = {
@@ -254058,21 +254299,21 @@ function runProcess(executable, args, cwd) {
254058
254299
  function resolveDrizzleCommand(root, configured) {
254059
254300
  if (configured?.trim())
254060
254301
  return configured.trim();
254061
- const local = join19(root, "node_modules", ".bin", "drizzle-kit");
254062
- return existsSync14(local) ? local : "drizzle-kit";
254302
+ const local = join20(root, "node_modules", ".bin", "drizzle-kit");
254303
+ return existsSync15(local) ? local : "drizzle-kit";
254063
254304
  }
254064
254305
  function toolFailed(value) {
254065
254306
  return value?.isError === true || value?.content?.some((chunk) => typeof chunk?.text === "string" && chunk.text.trimStart().startsWith("❌"));
254066
254307
  }
254067
254308
  async function migrateDatabase(args, options) {
254068
- const root = resolve24(String(args.project_dir || options.cwd || process.cwd()));
254309
+ const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
254069
254310
  const projectConfig = await readProjectConfig3(root);
254070
254311
  const config = projectConfig.dev || {};
254071
254312
  const database = config.database || {};
254072
254313
  const execute = options.execute || ((command, commandArgs, cwd) => runProcess(command, commandArgs, cwd));
254073
- const drizzleConfig = resolve24(root, String(args.drizzle_config || database.drizzleConfig || "drizzle.config.ts"));
254314
+ const drizzleConfig = resolve25(root, String(args.drizzle_config || database.drizzleConfig || "drizzle.config.ts"));
254074
254315
  const migrationsDir = String(args.migrations_dir || database.migrationsDir || "supabase/migrations");
254075
- if (!existsSync14(drizzleConfig))
254316
+ if (!existsSync15(drizzleConfig))
254076
254317
  throw new Error(`Drizzle config not found: ${drizzleConfig}`);
254077
254318
  const generated = await execute(resolveDrizzleCommand(root, typeof args.drizzle_bin === "string" ? args.drizzle_bin : database.drizzleBin), ["generate", "--config", drizzleConfig], root);
254078
254319
  if (generated.exitCode !== 0)
@@ -254090,8 +254331,8 @@ async function migrateDatabase(args, options) {
254090
254331
  return { ok: true, mode: "dev", action: "migrate", generated: true, applied: true, migrations_dir: migrationsDir, result: applied };
254091
254332
  }
254092
254333
  async function readProjectConfig3(root) {
254093
- const file = join19(root, "supacloud.json");
254094
- if (!existsSync14(file))
254334
+ const file = join20(root, "supacloud.json");
254335
+ if (!existsSync15(file))
254095
254336
  return {};
254096
254337
  try {
254097
254338
  const parsed = JSON.parse(await readFile10(file, "utf8"));
@@ -254109,7 +254350,7 @@ async function sourceFingerprint(root) {
254109
254350
  const visit = async (directory) => {
254110
254351
  const entries = await readdir6(directory, { withFileTypes: true });
254111
254352
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
254112
- const path = join19(directory, entry.name);
254353
+ const path = join20(directory, entry.name);
254113
254354
  if (entry.isDirectory() && ![".git", "node_modules", "dist", "generated", ".supacloud"].includes(entry.name))
254114
254355
  await visit(path);
254115
254356
  else if (entry.isFile())
@@ -254137,17 +254378,17 @@ function remoteRoot(value) {
254137
254378
  }
254138
254379
  function targetDirectory(root, target, functionSlug, config = {}) {
254139
254380
  if (target === "db")
254140
- return join19(root, "supabase", "migrations");
254381
+ return join20(root, "supabase", "migrations");
254141
254382
  if (target === "functions") {
254142
254383
  const targets = config.targets && typeof config.targets === "object" ? config.targets : {};
254143
254384
  const match = Object.values(targets).find((entry) => entry?.type === "edge_function" && (!functionSlug || String(entry.slug || "") === functionSlug));
254144
- const base = match?.root ? resolve24(root, String(match.root)) : join19(root, "supabase", "functions");
254145
- return match?.root ? base : functionSlug ? join19(base, safeToken(functionSlug, "function")) : base;
254385
+ const base = match?.root ? resolve25(root, String(match.root)) : join20(root, "supabase", "functions");
254386
+ return match?.root ? base : functionSlug ? join20(base, safeToken(functionSlug, "function")) : base;
254146
254387
  }
254147
254388
  if (target === "frontend") {
254148
254389
  const targets = config.targets && typeof config.targets === "object" ? config.targets : {};
254149
254390
  const match = Object.values(targets).find((entry) => entry?.type === "frontend");
254150
- return match?.root ? resolve24(root, String(match.root)) : join19(root, "apps", "web");
254391
+ return match?.root ? resolve25(root, String(match.root)) : join20(root, "apps", "web");
254151
254392
  }
254152
254393
  return root;
254153
254394
  }
@@ -254186,10 +254427,10 @@ function reloadCommand(config, target, projectRef, functionSlug) {
254186
254427
  return [...command.split(" "), "--project-ref", safeToken(projectRef || "test", "project_ref"), "--target", targetConfigPath(target), ...functionSlug ? ["--function", safeToken(functionSlug, "function")] : []];
254187
254428
  }
254188
254429
  function sshArgs(connection, remoteCommand) {
254189
- return ["-p", String(connection.port), "-i", resolve24(connection.key), "-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=yes", `${connection.user}@${connection.host}`, ...remoteCommand];
254430
+ return ["-p", String(connection.port), "-i", resolve25(connection.key), "-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=yes", `${connection.user}@${connection.host}`, ...remoteCommand];
254190
254431
  }
254191
254432
  function rsyncArgs(connection, source, destination, excludes, deleteRemote) {
254192
- const args = ["-az", "--checksum", "--partial", "--protect-args", "-e", `ssh -p ${connection.port} -i ${resolve24(connection.key)} -o BatchMode=yes -o StrictHostKeyChecking=yes`];
254433
+ const args = ["-az", "--checksum", "--partial", "--protect-args", "-e", `ssh -p ${connection.port} -i ${resolve25(connection.key)} -o BatchMode=yes -o StrictHostKeyChecking=yes`];
254193
254434
  if (deleteRemote)
254194
254435
  args.push("--delete-delay");
254195
254436
  for (const exclude of excludes) {
@@ -254201,17 +254442,17 @@ function rsyncArgs(connection, source, destination, excludes, deleteRemote) {
254201
254442
  return args;
254202
254443
  }
254203
254444
  async function syncOnce(args, options) {
254204
- const root = resolve24(String(args.project_dir || options.cwd || process.cwd()));
254445
+ const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
254205
254446
  const projectConfig = await readProjectConfig3(root);
254206
254447
  const config = projectConfig.dev || {};
254207
254448
  const target = String(args.target || "project");
254208
254449
  const source = targetDirectory(root, target, typeof args.function === "string" ? args.function : undefined, projectConfig);
254209
- if (!existsSync14(source))
254450
+ if (!existsSync15(source))
254210
254451
  throw new Error(`Dev source directory not found: ${source}`);
254211
254452
  let compiled = false;
254212
254453
  if (config.compile === true && target !== "db") {
254213
- const compileRoot = resolve24(root, config.compileRoot || ".");
254214
- const compileOutDir = resolve24(root, config.compileOutDir || "generated");
254454
+ const compileRoot = resolve25(root, config.compileRoot || ".");
254455
+ const compileOutDir = resolve25(root, config.compileOutDir || "generated");
254215
254456
  const compilation = await compileProject({
254216
254457
  rootDir: compileRoot,
254217
254458
  outDir: compileOutDir,
@@ -254249,7 +254490,7 @@ function registerRemoteDevTools(server2, options = {}) {
254249
254490
  if (["production", "prod"].includes((options.environment || "").toLowerCase()))
254250
254491
  throw new Error("Remote dev mode is forbidden for production environments");
254251
254492
  if (args.action === "status") {
254252
- const root = resolve24(String(args.project_dir || options.cwd || process.cwd()));
254493
+ const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
254253
254494
  const config = await readDevConfig(root);
254254
254495
  const connection = connectionArgs(options, config, args);
254255
254496
  const execute = options.execute || ((command, commandArgs, cwd) => runProcess(command, commandArgs, cwd));
@@ -254263,7 +254504,7 @@ function registerRemoteDevTools(server2, options = {}) {
254263
254504
  return { content: [{ type: "text", text: JSON.stringify(await migrateDatabase(args, options), null, 2) }] };
254264
254505
  }
254265
254506
  if (args.action === "watch") {
254266
- const root = resolve24(String(args.project_dir || options.cwd || process.cwd()));
254507
+ const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
254267
254508
  const interval = Math.max(100, Math.min(1e4, Number(args.interval_ms || 300)));
254268
254509
  let fingerprint = "";
254269
254510
  let lastSync = null;
@@ -254284,7 +254525,7 @@ function registerRemoteDevTools(server2, options = {}) {
254284
254525
  // package.json
254285
254526
  var package_default8 = {
254286
254527
  name: "@supacloud/cli",
254287
- version: "0.62.0",
254528
+ version: "0.63.0",
254288
254529
  description: "Project-scoped CLI for SupaCloud users",
254289
254530
  type: "module",
254290
254531
  main: "./dist/index.js",
@@ -254319,7 +254560,7 @@ var package_default8 = {
254319
254560
  },
254320
254561
  dependencies: {
254321
254562
  "@sinclair/typebox": "^0.34.52",
254322
- "@supacloud/compiler": "^0.30.0",
254563
+ "@supacloud/compiler": "^0.31.1",
254323
254564
  "@supacloud/delivery": "0.4.0",
254324
254565
  "@supacloud/db": "^0.2.0",
254325
254566
  fflate: "^0.8.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.62.0",
3
+ "version": "0.63.0",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@sinclair/typebox": "^0.34.52",
38
- "@supacloud/compiler": "^0.30.0",
38
+ "@supacloud/compiler": "^0.31.1",
39
39
  "@supacloud/delivery": "0.4.0",
40
40
  "@supacloud/db": "^0.2.0",
41
41
  "fflate": "^0.8.3",