@supacloud/cli 0.70.1 → 0.71.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 +1041 -699
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -185129,7 +185129,7 @@ function printComment(comment) {
185129
185129
  # ` + comment.replace(/\n/g, `
185130
185130
  # `);
185131
185131
  }
185132
- function join9(maybeArray, separator) {
185132
+ function join10(maybeArray, separator) {
185133
185133
  return maybeArray ? maybeArray.filter((x) => x).join(separator || "") : "";
185134
185134
  }
185135
185135
  function hasMultilineItems(maybeArray) {
@@ -185150,7 +185150,7 @@ function addDescription(cb) {
185150
185150
  if (node.kind.includes("Definition") && commentsRegistry[key]) {
185151
185151
  items.push(...commentsRegistry[key]);
185152
185152
  }
185153
- return join9([...items.map(printComment), node.description, cb(node, _key, _parent, path, ancestors)], `
185153
+ return join10([...items.map(printComment), node.description, cb(node, _key, _parent, path, ancestors)], `
185154
185154
  `);
185155
185155
  };
185156
185156
  }
@@ -185160,7 +185160,7 @@ function indent(maybeString) {
185160
185160
  }
185161
185161
  function block(array) {
185162
185162
  return array && array.length !== 0 ? `{
185163
- ${indent(join9(array, `
185163
+ ${indent(join10(array, `
185164
185164
  `))}
185165
185165
  }` : "";
185166
185166
  }
@@ -185255,43 +185255,43 @@ var init_comments = __esm(() => {
185255
185255
  Name: { leave: (node) => node.value },
185256
185256
  Variable: { leave: (node) => "$" + node.name },
185257
185257
  Document: {
185258
- leave: (node) => join9(node.definitions, `
185258
+ leave: (node) => join10(node.definitions, `
185259
185259
 
185260
185260
  `)
185261
185261
  },
185262
185262
  OperationDefinition: {
185263
185263
  leave: (node) => {
185264
- const varDefs = wrap("(", join9(node.variableDefinitions, ", "), ")");
185265
- const prefix = join9([node.operation, join9([node.name, varDefs]), join9(node.directives, " ")], " ");
185264
+ const varDefs = wrap("(", join10(node.variableDefinitions, ", "), ")");
185265
+ const prefix = join10([node.operation, join10([node.name, varDefs]), join10(node.directives, " ")], " ");
185266
185266
  return prefix + " " + node.selectionSet;
185267
185267
  }
185268
185268
  },
185269
185269
  VariableDefinition: {
185270
- leave: ({ variable, type, defaultValue, directives }) => variable + ": " + type + wrap(" = ", defaultValue) + wrap(" ", join9(directives, " "))
185270
+ leave: ({ variable, type, defaultValue, directives }) => variable + ": " + type + wrap(" = ", defaultValue) + wrap(" ", join10(directives, " "))
185271
185271
  },
185272
185272
  SelectionSet: { leave: ({ selections }) => block(selections) },
185273
185273
  Field: {
185274
185274
  leave({ alias, name, arguments: args, directives, selectionSet }) {
185275
185275
  const prefix = wrap("", alias, ": ") + name;
185276
- let argsLine = prefix + wrap("(", join9(args, ", "), ")");
185276
+ let argsLine = prefix + wrap("(", join10(args, ", "), ")");
185277
185277
  if (argsLine.length > MAX_LINE_LENGTH) {
185278
185278
  argsLine = prefix + wrap(`(
185279
- `, indent(join9(args, `
185279
+ `, indent(join10(args, `
185280
185280
  `)), `
185281
185281
  )`);
185282
185282
  }
185283
- return join9([argsLine, join9(directives, " "), selectionSet], " ");
185283
+ return join10([argsLine, join10(directives, " "), selectionSet], " ");
185284
185284
  }
185285
185285
  },
185286
185286
  Argument: { leave: ({ name, value }) => name + ": " + value },
185287
185287
  FragmentSpread: {
185288
- leave: ({ name, directives }) => "..." + name + wrap(" ", join9(directives, " "))
185288
+ leave: ({ name, directives }) => "..." + name + wrap(" ", join10(directives, " "))
185289
185289
  },
185290
185290
  InlineFragment: {
185291
- leave: ({ typeCondition, directives, selectionSet }) => join9(["...", wrap("on ", typeCondition), join9(directives, " "), selectionSet], " ")
185291
+ leave: ({ typeCondition, directives, selectionSet }) => join10(["...", wrap("on ", typeCondition), join10(directives, " "), selectionSet], " ")
185292
185292
  },
185293
185293
  FragmentDefinition: {
185294
- leave: ({ name, typeCondition, variableDefinitions, directives, selectionSet }) => `fragment ${name}${wrap("(", join9(variableDefinitions, ", "), ")")} ` + `on ${typeCondition} ${wrap("", join9(directives, " "), " ")}` + selectionSet
185294
+ leave: ({ name, typeCondition, variableDefinitions, directives, selectionSet }) => `fragment ${name}${wrap("(", join10(variableDefinitions, ", "), ")")} ` + `on ${typeCondition} ${wrap("", join10(directives, " "), " ")}` + selectionSet
185295
185295
  },
185296
185296
  IntValue: { leave: ({ value }) => value },
185297
185297
  FloatValue: { leave: ({ value }) => value },
@@ -185306,101 +185306,101 @@ var init_comments = __esm(() => {
185306
185306
  BooleanValue: { leave: ({ value }) => value ? "true" : "false" },
185307
185307
  NullValue: { leave: () => "null" },
185308
185308
  EnumValue: { leave: ({ value }) => value },
185309
- ListValue: { leave: ({ values }) => "[" + join9(values, ", ") + "]" },
185310
- ObjectValue: { leave: ({ fields }) => "{" + join9(fields, ", ") + "}" },
185309
+ ListValue: { leave: ({ values }) => "[" + join10(values, ", ") + "]" },
185310
+ ObjectValue: { leave: ({ fields }) => "{" + join10(fields, ", ") + "}" },
185311
185311
  ObjectField: { leave: ({ name, value }) => name + ": " + value },
185312
185312
  Directive: {
185313
- leave: ({ name, arguments: args }) => "@" + name + wrap("(", join9(args, ", "), ")")
185313
+ leave: ({ name, arguments: args }) => "@" + name + wrap("(", join10(args, ", "), ")")
185314
185314
  },
185315
185315
  NamedType: { leave: ({ name }) => name },
185316
185316
  ListType: { leave: ({ type }) => "[" + type + "]" },
185317
185317
  NonNullType: { leave: ({ type }) => type + "!" },
185318
185318
  SchemaDefinition: {
185319
- leave: ({ directives, operationTypes }) => join9(["schema", join9(directives, " "), block(operationTypes)], " ")
185319
+ leave: ({ directives, operationTypes }) => join10(["schema", join10(directives, " "), block(operationTypes)], " ")
185320
185320
  },
185321
185321
  OperationTypeDefinition: {
185322
185322
  leave: ({ operation, type }) => operation + ": " + type
185323
185323
  },
185324
185324
  ScalarTypeDefinition: {
185325
- leave: ({ name, directives }) => join9(["scalar", name, join9(directives, " ")], " ")
185325
+ leave: ({ name, directives }) => join10(["scalar", name, join10(directives, " ")], " ")
185326
185326
  },
185327
185327
  ObjectTypeDefinition: {
185328
- leave: ({ name, interfaces, directives, fields }) => join9([
185328
+ leave: ({ name, interfaces, directives, fields }) => join10([
185329
185329
  "type",
185330
185330
  name,
185331
- wrap("implements ", join9(interfaces, " & ")),
185332
- join9(directives, " "),
185331
+ wrap("implements ", join10(interfaces, " & ")),
185332
+ join10(directives, " "),
185333
185333
  block(fields)
185334
185334
  ], " ")
185335
185335
  },
185336
185336
  FieldDefinition: {
185337
185337
  leave: ({ name, arguments: args, type, directives }) => name + (hasMultilineItems(args) ? wrap(`(
185338
- `, indent(join9(args, `
185338
+ `, indent(join10(args, `
185339
185339
  `)), `
185340
- )`) : wrap("(", join9(args, ", "), ")")) + ": " + type + wrap(" ", join9(directives, " "))
185340
+ )`) : wrap("(", join10(args, ", "), ")")) + ": " + type + wrap(" ", join10(directives, " "))
185341
185341
  },
185342
185342
  InputValueDefinition: {
185343
- leave: ({ name, type, defaultValue, directives }) => join9([name + ": " + type, wrap("= ", defaultValue), join9(directives, " ")], " ")
185343
+ leave: ({ name, type, defaultValue, directives }) => join10([name + ": " + type, wrap("= ", defaultValue), join10(directives, " ")], " ")
185344
185344
  },
185345
185345
  InterfaceTypeDefinition: {
185346
- leave: ({ name, interfaces, directives, fields }) => join9([
185346
+ leave: ({ name, interfaces, directives, fields }) => join10([
185347
185347
  "interface",
185348
185348
  name,
185349
- wrap("implements ", join9(interfaces, " & ")),
185350
- join9(directives, " "),
185349
+ wrap("implements ", join10(interfaces, " & ")),
185350
+ join10(directives, " "),
185351
185351
  block(fields)
185352
185352
  ], " ")
185353
185353
  },
185354
185354
  UnionTypeDefinition: {
185355
- leave: ({ name, directives, types }) => join9(["union", name, join9(directives, " "), wrap("= ", join9(types, " | "))], " ")
185355
+ leave: ({ name, directives, types }) => join10(["union", name, join10(directives, " "), wrap("= ", join10(types, " | "))], " ")
185356
185356
  },
185357
185357
  EnumTypeDefinition: {
185358
- leave: ({ name, directives, values }) => join9(["enum", name, join9(directives, " "), block(values)], " ")
185358
+ leave: ({ name, directives, values }) => join10(["enum", name, join10(directives, " "), block(values)], " ")
185359
185359
  },
185360
185360
  EnumValueDefinition: {
185361
- leave: ({ name, directives }) => join9([name, join9(directives, " ")], " ")
185361
+ leave: ({ name, directives }) => join10([name, join10(directives, " ")], " ")
185362
185362
  },
185363
185363
  InputObjectTypeDefinition: {
185364
- leave: ({ name, directives, fields }) => join9(["input", name, join9(directives, " "), block(fields)], " ")
185364
+ leave: ({ name, directives, fields }) => join10(["input", name, join10(directives, " "), block(fields)], " ")
185365
185365
  },
185366
185366
  DirectiveDefinition: {
185367
185367
  leave: ({ name, arguments: args, repeatable, locations }) => "directive @" + name + (hasMultilineItems(args) ? wrap(`(
185368
- `, indent(join9(args, `
185368
+ `, indent(join10(args, `
185369
185369
  `)), `
185370
- )`) : wrap("(", join9(args, ", "), ")")) + (repeatable ? " repeatable" : "") + " on " + join9(locations, " | ")
185370
+ )`) : wrap("(", join10(args, ", "), ")")) + (repeatable ? " repeatable" : "") + " on " + join10(locations, " | ")
185371
185371
  },
185372
185372
  SchemaExtension: {
185373
- leave: ({ directives, operationTypes }) => join9(["extend schema", join9(directives, " "), block(operationTypes)], " ")
185373
+ leave: ({ directives, operationTypes }) => join10(["extend schema", join10(directives, " "), block(operationTypes)], " ")
185374
185374
  },
185375
185375
  ScalarTypeExtension: {
185376
- leave: ({ name, directives }) => join9(["extend scalar", name, join9(directives, " ")], " ")
185376
+ leave: ({ name, directives }) => join10(["extend scalar", name, join10(directives, " ")], " ")
185377
185377
  },
185378
185378
  ObjectTypeExtension: {
185379
- leave: ({ name, interfaces, directives, fields }) => join9([
185379
+ leave: ({ name, interfaces, directives, fields }) => join10([
185380
185380
  "extend type",
185381
185381
  name,
185382
- wrap("implements ", join9(interfaces, " & ")),
185383
- join9(directives, " "),
185382
+ wrap("implements ", join10(interfaces, " & ")),
185383
+ join10(directives, " "),
185384
185384
  block(fields)
185385
185385
  ], " ")
185386
185386
  },
185387
185387
  InterfaceTypeExtension: {
185388
- leave: ({ name, interfaces, directives, fields }) => join9([
185388
+ leave: ({ name, interfaces, directives, fields }) => join10([
185389
185389
  "extend interface",
185390
185390
  name,
185391
- wrap("implements ", join9(interfaces, " & ")),
185392
- join9(directives, " "),
185391
+ wrap("implements ", join10(interfaces, " & ")),
185392
+ join10(directives, " "),
185393
185393
  block(fields)
185394
185394
  ], " ")
185395
185395
  },
185396
185396
  UnionTypeExtension: {
185397
- leave: ({ name, directives, types }) => join9(["extend union", name, join9(directives, " "), wrap("= ", join9(types, " | "))], " ")
185397
+ leave: ({ name, directives, types }) => join10(["extend union", name, join10(directives, " "), wrap("= ", join10(types, " | "))], " ")
185398
185398
  },
185399
185399
  EnumTypeExtension: {
185400
- leave: ({ name, directives, values }) => join9(["extend enum", name, join9(directives, " "), block(values)], " ")
185400
+ leave: ({ name, directives, values }) => join10(["extend enum", name, join10(directives, " "), block(values)], " ")
185401
185401
  },
185402
185402
  InputObjectTypeExtension: {
185403
- leave: ({ name, directives, fields }) => join9(["extend input", name, join9(directives, " "), block(fields)], " ")
185403
+ leave: ({ name, directives, fields }) => join10(["extend input", name, join10(directives, " "), block(fields)], " ")
185404
185404
  }
185405
185405
  };
185406
185406
  printDocASTReducerWithComments = Object.keys(printDocASTReducer).reduce((prev, key) => ({
@@ -188960,7 +188960,7 @@ var require_parse_filepath = __commonJS(function(exports, module) {
188960
188960
  });
188961
188961
 
188962
188962
  // node_modules/@graphql-codegen/visitor-plugin-common/esm/imports.js
188963
- import { dirname as dirname5, isAbsolute as isAbsolute4, join as join10, relative as relative5, resolve as resolve12 } from "path";
188963
+ import { dirname as dirname5, isAbsolute as isAbsolute4, join as join11, relative as relative5, resolve as resolve13 } from "path";
188964
188964
  function generateFragmentImportStatement(statement, kind) {
188965
188965
  const { importSource: fragmentImportSource, ...rest } = statement;
188966
188966
  const { identifiers, path, namespace } = fragmentImportSource;
@@ -188994,8 +188994,8 @@ function generateImportStatement(statement) {
188994
188994
  function resolveImportPath(baseDir, outputPath, sourcePath) {
188995
188995
  const shouldAbsolute = !sourcePath.startsWith("~");
188996
188996
  if (shouldAbsolute) {
188997
- const absGeneratedFilePath = resolve12(baseDir, outputPath);
188998
- const absImportFilePath = resolve12(baseDir, sourcePath);
188997
+ const absGeneratedFilePath = resolve13(baseDir, outputPath);
188998
+ const absImportFilePath = resolve13(baseDir, sourcePath);
188999
188999
  return resolveRelativeImport(absGeneratedFilePath, absImportFilePath);
189000
189000
  }
189001
189001
  return sourcePath.replace(`~`, "");
@@ -189011,7 +189011,7 @@ function resolveRelativeImport(from, to) {
189011
189011
  }
189012
189012
  function clearExtension(path) {
189013
189013
  const parsedPath = import_parse_filepath.default(path);
189014
- return join10(parsedPath.dir, parsedPath.name).replace(/\\/g, "/");
189014
+ return join11(parsedPath.dir, parsedPath.name).replace(/\\/g, "/");
189015
189015
  }
189016
189016
  function fixLocalFilePath(path) {
189017
189017
  return path.startsWith("..") ? path : `./${path}`;
@@ -190212,7 +190212,7 @@ var init_esm4 = __esm(() => {
190212
190212
  });
190213
190213
 
190214
190214
  // node_modules/@graphql-codegen/visitor-plugin-common/esm/client-side-base-visitor.js
190215
- import { basename as basename7, extname } from "path";
190215
+ import { basename as basename8, extname } from "path";
190216
190216
  var import_dependency_graph, import_graphql54, import_graphql_tag, DocumentMode, EXTENSIONS_TO_REMOVE, ClientSideBaseVisitor;
190217
190217
  var init_client_side_base_visitor = __esm(() => {
190218
190218
  init_dist();
@@ -190534,7 +190534,7 @@ var init_client_side_base_visitor = __esm(() => {
190534
190534
  case DocumentMode.external: {
190535
190535
  if (this._collectedOperations.length > 0) {
190536
190536
  if (this.config.importDocumentNodeExternallyFrom === "near-operation-file" && this._documents.length === 1) {
190537
- let documentPath = `./${this.clearExtension(basename7(this._documents[0].location || ""))}`;
190537
+ let documentPath = `./${this.clearExtension(basename8(this._documents[0].location || ""))}`;
190538
190538
  documentPath += normalizeImportExtension({
190539
190539
  emitLegacyCommonJSImports: this.config.emitLegacyCommonJSImports,
190540
190540
  importExtension: this.config.importExtension
@@ -207626,7 +207626,7 @@ var ACTION_POLICY = {
207626
207626
  },
207627
207627
  database: {
207628
207628
  read: ["list_tables", "describe_columns", "list_indexes", "list_constraints", "list_extensions", "extension_catalog", "rls_status", "rls_policies", "list_auth_users", "get_auth_user", "connections", "stats", "slow_queries", "list_migrations", "migration_inventory", "migration_import", "delivery_migration_plan", "project_url", "generate_types", "database_lint", "db_lint", "rpc_catalog", "list_rpcs"],
207629
- local: ["lint_migrations", "lint"],
207629
+ local: ["lint_migrations", "lint", "rebase_migrations"],
207630
207630
  write: ["query", "execute", "apply_migration", "push_migrations", "baseline_migrations", "create_table_rls", "enable_extension", "disable_extension"]
207631
207631
  },
207632
207632
  supabase: {
@@ -208205,7 +208205,7 @@ class HttpTransport {
208205
208205
  // src/shared/tools/database-tools.ts
208206
208206
  import { createHash as createHash3 } from "node:crypto";
208207
208207
  import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
208208
- import { basename as basename3, join as join4 } from "node:path";
208208
+ import { basename as basename4, join as join5 } from "node:path";
208209
208209
 
208210
208210
  // node_modules/@supacloud/delivery/src/delivery-artifact.ts
208211
208211
  import { readdir as readdir2, realpath as realpath2 } from "node:fs/promises";
@@ -209054,6 +209054,748 @@ async function readDeliveryExecutableArchive(manifestPath) {
209054
209054
  return { manifest, objects };
209055
209055
  }
209056
209056
 
209057
+ // node_modules/@supacloud/db/dist/index.js
209058
+ import { createHash as createHash22 } from "node:crypto";
209059
+ import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat, writeFile as writeFile2 } from "node:fs/promises";
209060
+ import { basename as basename2, join as join3, resolve as resolve5 } from "node:path";
209061
+ var TABLES_SQL = `
209062
+ SELECT n.nspname AS schema,
209063
+ c.relname AS name,
209064
+ c.relrowsecurity AS rls_enabled,
209065
+ c.relforcerowsecurity AS rls_forced
209066
+ FROM pg_class c
209067
+ JOIN pg_namespace n ON n.oid = c.relnamespace
209068
+ WHERE c.relkind = 'r'
209069
+ AND n.nspname = ANY($1)
209070
+ ORDER BY n.nspname, c.relname
209071
+ `;
209072
+ var POLICIES_SQL = `
209073
+ SELECT n.nspname AS schema,
209074
+ c.relname AS table,
209075
+ p.polname AS name,
209076
+ p.polcmd AS command,
209077
+ ARRAY(
209078
+ SELECT CASE WHEN r = 0 THEN 'PUBLIC' ELSE pg_get_userbyid(r) END
209079
+ FROM unnest(p.polroles) AS r
209080
+ ) AS roles,
209081
+ pg_get_expr(p.polqual, p.polrelid) AS using_expr,
209082
+ pg_get_expr(p.polwithcheck, p.polrelid) AS check_expr
209083
+ FROM pg_policy p
209084
+ JOIN pg_class c ON c.oid = p.polrelid
209085
+ JOIN pg_namespace n ON n.oid = c.relnamespace
209086
+ WHERE n.nspname = ANY($1)
209087
+ ORDER BY n.nspname, c.relname, p.polname
209088
+ `;
209089
+ var FUNCTIONS_SQL = `
209090
+ SELECT n.nspname AS schema,
209091
+ p.proname AS name,
209092
+ p.prosecdef AS security_definer,
209093
+ p.proconfig AS config,
209094
+ l.lanname AS language
209095
+ FROM pg_proc p
209096
+ JOIN pg_namespace n ON n.oid = p.pronamespace
209097
+ JOIN pg_language l ON l.oid = p.prolang
209098
+ WHERE n.nspname = ANY($1)
209099
+ ORDER BY n.nspname, p.proname
209100
+ `;
209101
+ var TRIGGERS_SQL = `
209102
+ SELECT n.nspname AS schema,
209103
+ c.relname AS table,
209104
+ t.tgname AS name,
209105
+ t.tgenabled <> 'D' AS enabled
209106
+ FROM pg_trigger t
209107
+ JOIN pg_class c ON c.oid = t.tgrelid
209108
+ JOIN pg_namespace n ON n.oid = c.relnamespace
209109
+ WHERE NOT t.tgisinternal
209110
+ AND n.nspname = ANY($1)
209111
+ ORDER BY n.nspname, c.relname, t.tgname
209112
+ `;
209113
+ var GRANTS_SQL = `
209114
+ SELECT table_schema AS object_schema,
209115
+ table_name AS object_name,
209116
+ privilege_type AS privilege,
209117
+ grantee
209118
+ FROM information_schema.role_table_grants
209119
+ WHERE table_schema = ANY($1)
209120
+ UNION ALL
209121
+ SELECT routine_schema AS object_schema,
209122
+ routine_name AS object_name,
209123
+ privilege_type AS privilege,
209124
+ grantee
209125
+ FROM information_schema.routine_privileges
209126
+ WHERE routine_schema = ANY($1)
209127
+ `;
209128
+ var POLCMD_MAP = {
209129
+ r: "select",
209130
+ a: "insert",
209131
+ w: "update",
209132
+ d: "delete",
209133
+ "*": "all"
209134
+ };
209135
+ function extractSearchPath(config) {
209136
+ if (!config)
209137
+ return null;
209138
+ const entry = config.find((item) => item.startsWith("search_path="));
209139
+ if (!entry)
209140
+ return null;
209141
+ return entry.slice("search_path=".length);
209142
+ }
209143
+ async function readCatalog(executor, schemas = ["public"]) {
209144
+ const params = [schemas];
209145
+ const [tableRows, policyRows, functionRows, triggerRows, grantRows] = await Promise.all([
209146
+ executor.query(TABLES_SQL, params),
209147
+ executor.query(POLICIES_SQL, params),
209148
+ executor.query(FUNCTIONS_SQL, params),
209149
+ executor.query(TRIGGERS_SQL, params),
209150
+ executor.query(GRANTS_SQL, params)
209151
+ ]);
209152
+ return {
209153
+ tables: tableRows.map((row) => ({
209154
+ schema: row.schema,
209155
+ name: row.name,
209156
+ rlsEnabled: row.rls_enabled,
209157
+ rlsForced: row.rls_forced
209158
+ })),
209159
+ policies: policyRows.map((row) => ({
209160
+ schema: row.schema,
209161
+ table: row.table,
209162
+ name: row.name,
209163
+ command: POLCMD_MAP[row.command] ?? "all",
209164
+ roles: row.roles ?? [],
209165
+ usingExpr: row.using_expr ?? undefined,
209166
+ checkExpr: row.check_expr ?? undefined
209167
+ })),
209168
+ functions: functionRows.map((row) => ({
209169
+ schema: row.schema,
209170
+ name: row.name,
209171
+ security: row.security_definer ? "definer" : "invoker",
209172
+ searchPath: extractSearchPath(row.config),
209173
+ language: row.language
209174
+ })),
209175
+ triggers: triggerRows.map((row) => ({
209176
+ schema: row.schema,
209177
+ table: row.table,
209178
+ name: row.name,
209179
+ enabled: row.enabled
209180
+ })),
209181
+ grants: grantRows.map((row) => ({
209182
+ objectSchema: row.object_schema,
209183
+ objectName: row.object_name,
209184
+ privilege: row.privilege,
209185
+ grantee: row.grantee
209186
+ }))
209187
+ };
209188
+ }
209189
+ function splitQualifiedName(name) {
209190
+ const dot = name.indexOf(".");
209191
+ if (dot === -1)
209192
+ return ["public", name];
209193
+ return [name.slice(0, dot), name.slice(dot + 1)];
209194
+ }
209195
+ function isFixedSearchPath(searchPath) {
209196
+ if (searchPath === null)
209197
+ return false;
209198
+ const parts = searchPath.split(",").map((part) => part.trim().replace(/^"|"$/g, ""));
209199
+ return parts.every((part) => part !== "" && part.toLowerCase() !== "pg_temp");
209200
+ }
209201
+ function reconcileModule(module, catalog) {
209202
+ const issues = [];
209203
+ const ownedTables = new Set(module.tables);
209204
+ const push = (severity, code, object, message) => issues.push({ severity, code, object, message });
209205
+ for (const policy of module.policies) {
209206
+ const [schema, table] = splitQualifiedName(policy.table);
209207
+ const found = catalog.policies.some((cp) => cp.schema === schema && cp.table === table && cp.name === policy.name);
209208
+ if (!found) {
209209
+ push("error", "missing-policy", `${policy.table}.${policy.name}`, `声明的策略 ${policy.name} 在表 ${policy.table} 的 catalog 中不存在`);
209210
+ }
209211
+ }
209212
+ const declaredPolicyKeys = new Set(module.policies.map((p) => `${p.table}::${p.name}`));
209213
+ for (const cp of catalog.policies) {
209214
+ const qualified = `${cp.schema}.${cp.table}`;
209215
+ if (ownedTables.has(qualified) && !declaredPolicyKeys.has(`${qualified}::${cp.name}`)) {
209216
+ push("warn", "undeclared-policy", `${qualified}.${cp.name}`, `归属表 ${qualified} 上存在未声明的策略 ${cp.name},可能发生漂移`);
209217
+ }
209218
+ }
209219
+ for (const fn of module.functions) {
209220
+ const [schema, name] = splitQualifiedName(fn.name);
209221
+ const cf = catalog.functions.find((f) => f.schema === schema && f.name === name);
209222
+ if (!cf) {
209223
+ push("error", "missing-function", fn.name, `声明的函数 ${fn.name} 在 catalog 中不存在`);
209224
+ continue;
209225
+ }
209226
+ if (cf.security !== fn.security) {
209227
+ push("warn", "security-mismatch", fn.name, `函数 ${fn.name} 声明为 security ${fn.security},catalog 实际为 ${cf.security}`);
209228
+ }
209229
+ const effectiveDefiner = fn.security === "definer" || cf.security === "definer";
209230
+ if (effectiveDefiner && !isFixedSearchPath(cf.searchPath)) {
209231
+ push("error", "definer-without-search-path", fn.name, `security definer 函数 ${fn.name} 未设置固定 search_path(当前: ${cf.searchPath ?? "未设置"})`);
209232
+ }
209233
+ }
209234
+ for (const trigger of module.triggers) {
209235
+ const [schema, table] = splitQualifiedName(trigger.table);
209236
+ const found = catalog.triggers.some((ct) => ct.schema === schema && ct.table === table && ct.name === trigger.name);
209237
+ if (!found) {
209238
+ push("error", "missing-trigger", `${trigger.table}.${trigger.name}`, `声明的触发器 ${trigger.name} 在表 ${trigger.table} 的 catalog 中不存在`);
209239
+ }
209240
+ }
209241
+ const declaredTriggerKeys = new Set(module.triggers.map((t) => `${t.table}::${t.name}`));
209242
+ for (const ct of catalog.triggers) {
209243
+ const qualified = `${ct.schema}.${ct.table}`;
209244
+ if (ownedTables.has(qualified) && !declaredTriggerKeys.has(`${qualified}::${ct.name}`)) {
209245
+ push("warn", "undeclared-trigger", `${qualified}.${ct.name}`, `归属表 ${qualified} 上存在未声明的触发器 ${ct.name}${ct.enabled ? "" : "(已禁用)"},可能发生漂移`);
209246
+ }
209247
+ }
209248
+ for (const table of module.tables) {
209249
+ const [schema, name] = splitQualifiedName(table);
209250
+ const ct = catalog.tables.find((t) => t.schema === schema && t.name === name);
209251
+ if (ct && !ct.rlsEnabled) {
209252
+ push("error", "rls-disabled", table, `归属表 ${table} 未开启行级安全(relrowsecurity = false)`);
209253
+ }
209254
+ }
209255
+ for (const grant of catalog.grants) {
209256
+ const qualified = `${grant.objectSchema}.${grant.objectName}`;
209257
+ if (ownedTables.has(qualified) && grant.grantee.toUpperCase() === "PUBLIC") {
209258
+ push("error", "wildcard-grant", qualified, `归属表 ${qualified} 存在授予 PUBLIC 的 ${grant.privilege} 权限`);
209259
+ }
209260
+ }
209261
+ for (const grant of module.grants) {
209262
+ const [schema, name] = splitQualifiedName(grant.object);
209263
+ const found = catalog.grants.some((cg) => cg.objectSchema === schema && cg.objectName === name && cg.privilege.toLowerCase() === grant.privilege.toLowerCase() && cg.grantee.toLowerCase() === grant.role.toLowerCase());
209264
+ if (!found) {
209265
+ push("warn", "grant-drift", grant.object, `声明的授权 ${grant.privilege} ON ${grant.object} TO ${grant.role} 在 catalog 中不存在`);
209266
+ }
209267
+ }
209268
+ return {
209269
+ module: module.name,
209270
+ issues,
209271
+ ok: !issues.some((issue) => issue.severity === "error")
209272
+ };
209273
+ }
209274
+ var SECURITY_DEFINER_RE = /\bsecurity\s+definer\b/i;
209275
+ var SET_SEARCH_PATH_RE = /\bset\s+search_path\b/i;
209276
+ var GRANT_TO_PUBLIC_RE = /\bgrant\b[^;]*\bto\s+public\b/i;
209277
+ var DROP_WITHOUT_IF_EXISTS_RE = /\bdrop\s+(?:table|column)\s+(?!if\s+exists\b)/i;
209278
+ var ENABLE_RLS_RE = /\benable\s+row\s+level\s+security\b/i;
209279
+ var CREATE_POLICY_RE = /\bcreate\s+policy\b/i;
209280
+ var DROP_POLICY_IF_EXISTS_RE = /\bdrop\s+policy\s+if\s+exists\b/i;
209281
+ function lineOf(sql, index) {
209282
+ let line = 1;
209283
+ for (let i = 0;i < index; i += 1) {
209284
+ if (sql.charCodeAt(i) === 10)
209285
+ line += 1;
209286
+ }
209287
+ return line;
209288
+ }
209289
+ function lintSql(sql, file) {
209290
+ const issues = [];
209291
+ const definer = SECURITY_DEFINER_RE.exec(sql);
209292
+ if (definer && !SET_SEARCH_PATH_RE.test(sql)) {
209293
+ issues.push({
209294
+ severity: "error",
209295
+ code: "definer-no-search-path",
209296
+ message: "security definer 函数必须显式 set search_path,避免 search_path 劫持",
209297
+ file,
209298
+ line: lineOf(sql, definer.index)
209299
+ });
209300
+ }
209301
+ const grantPublic = GRANT_TO_PUBLIC_RE.exec(sql);
209302
+ if (grantPublic) {
209303
+ issues.push({
209304
+ severity: "error",
209305
+ code: "grant-to-public",
209306
+ message: "禁止将权限授予 PUBLIC 角色",
209307
+ file,
209308
+ line: lineOf(sql, grantPublic.index)
209309
+ });
209310
+ }
209311
+ const drop = DROP_WITHOUT_IF_EXISTS_RE.exec(sql);
209312
+ if (drop) {
209313
+ issues.push({
209314
+ severity: "warn",
209315
+ code: "drop-without-if-exists",
209316
+ message: "drop table/column 建议使用 if exists,保证迁移可重入",
209317
+ file,
209318
+ line: lineOf(sql, drop.index)
209319
+ });
209320
+ }
209321
+ const createPolicy = CREATE_POLICY_RE.exec(sql);
209322
+ if (createPolicy) {
209323
+ const dropPolicy = DROP_POLICY_IF_EXISTS_RE.exec(sql);
209324
+ if (!dropPolicy || dropPolicy.index > createPolicy.index) {
209325
+ issues.push({
209326
+ severity: "warn",
209327
+ code: "non-idempotent-policy",
209328
+ message: "create policy 前缺少 drop policy if exists,策略不可重复执行",
209329
+ file,
209330
+ line: lineOf(sql, createPolicy.index)
209331
+ });
209332
+ }
209333
+ }
209334
+ return issues;
209335
+ }
209336
+ async function lintModule(module, readFile) {
209337
+ const issues = [];
209338
+ const sources = new Set;
209339
+ for (const decl of [
209340
+ ...module.policies,
209341
+ ...module.functions,
209342
+ ...module.triggers,
209343
+ ...module.grants
209344
+ ]) {
209345
+ sources.add(decl.source);
209346
+ }
209347
+ const contents = new Map;
209348
+ await Promise.all([...sources].map(async (path) => {
209349
+ contents.set(path, await readFile(path));
209350
+ }));
209351
+ for (const [file, sql] of contents) {
209352
+ issues.push(...lintSql(sql, file));
209353
+ }
209354
+ if (module.policies.length > 0) {
209355
+ const anyEnable = module.policies.some((policy) => ENABLE_RLS_RE.test(contents.get(policy.source) ?? ""));
209356
+ if (!anyEnable) {
209357
+ issues.push({
209358
+ severity: "warn",
209359
+ code: "missing-rls-enable",
209360
+ message: `模块 ${module.name} 声明了 ${module.policies.length} 条策略,但所有策略源文件都没有 enable row level security`,
209361
+ file: module.policies[0].source
209362
+ });
209363
+ }
209364
+ }
209365
+ for (const policy of module.policies) {
209366
+ if (!policy.tests || policy.tests.length === 0) {
209367
+ issues.push({
209368
+ severity: "warn",
209369
+ code: "policy-without-test",
209370
+ message: `策略 ${policy.name} 未声明测试文件`,
209371
+ file: policy.source
209372
+ });
209373
+ }
209374
+ }
209375
+ for (const fn of module.functions) {
209376
+ if (!fn.tests || fn.tests.length === 0) {
209377
+ issues.push({
209378
+ severity: "warn",
209379
+ code: "policy-without-test",
209380
+ message: `函数 ${fn.name} 未声明测试文件`,
209381
+ file: fn.source
209382
+ });
209383
+ }
209384
+ }
209385
+ return issues;
209386
+ }
209387
+ function buildDatabaseManifest(modules) {
209388
+ return {
209389
+ version: 1,
209390
+ modules: modules.map((module) => ({
209391
+ name: module.name,
209392
+ tables: module.tables,
209393
+ policies: module.policies,
209394
+ functions: module.functions,
209395
+ triggers: module.triggers,
209396
+ grants: module.grants
209397
+ }))
209398
+ };
209399
+ }
209400
+ function describePolicy(moduleName, policy) {
209401
+ const lines = [
209402
+ `对象: ${policy.table}.${policy.name}`,
209403
+ "类型: 策略 (policy)",
209404
+ `所属模块: ${moduleName}`,
209405
+ `表: ${policy.table}`,
209406
+ `操作: ${policy.operation}`,
209407
+ `角色: ${policy.roles.join(", ")}`,
209408
+ `源文件: ${policy.source}`
209409
+ ];
209410
+ if (policy.tests && policy.tests.length > 0)
209411
+ lines.push(`测试: ${policy.tests.join(", ")}`);
209412
+ return lines.join(`
209413
+ `);
209414
+ }
209415
+ function describeFunction(moduleName, fn) {
209416
+ const lines = [
209417
+ `对象: ${fn.name}`,
209418
+ "类型: 函数 (function)",
209419
+ `所属模块: ${moduleName}`,
209420
+ `源文件: ${fn.source}`,
209421
+ `安全模式: ${fn.security}`
209422
+ ];
209423
+ if (fn.permission)
209424
+ lines.push(`权限: ${fn.permission}`);
209425
+ if (fn.transaction)
209426
+ lines.push(`事务: ${fn.transaction}`);
209427
+ if (fn.audit)
209428
+ lines.push(`审计: ${fn.audit}`);
209429
+ if (fn.idempotency)
209430
+ lines.push(`幂等: ${fn.idempotency}`);
209431
+ if (fn.tests && fn.tests.length > 0)
209432
+ lines.push(`测试: ${fn.tests.join(", ")}`);
209433
+ return lines.join(`
209434
+ `);
209435
+ }
209436
+ function describeTrigger(moduleName, trigger) {
209437
+ return [
209438
+ `对象: ${trigger.name}`,
209439
+ "类型: 触发器 (trigger)",
209440
+ `所属模块: ${moduleName}`,
209441
+ `表: ${trigger.table}`,
209442
+ `源文件: ${trigger.source}`
209443
+ ].join(`
209444
+ `);
209445
+ }
209446
+ function describeGrant(moduleName, grant) {
209447
+ return [
209448
+ `对象: ${grant.object}`,
209449
+ "类型: 授权 (grant)",
209450
+ `所属模块: ${moduleName}`,
209451
+ `权限: ${grant.privilege}`,
209452
+ `角色: ${grant.role}`,
209453
+ `源文件: ${grant.source}`
209454
+ ].join(`
209455
+ `);
209456
+ }
209457
+ function explainObject(manifest, name) {
209458
+ for (const module of manifest.modules) {
209459
+ for (const policy of module.policies) {
209460
+ if (policy.name === name || `${policy.table}.${policy.name}` === name) {
209461
+ return describePolicy(module.name, policy);
209462
+ }
209463
+ }
209464
+ for (const fn of module.functions) {
209465
+ if (fn.name === name)
209466
+ return describeFunction(module.name, fn);
209467
+ }
209468
+ for (const trigger of module.triggers) {
209469
+ if (trigger.name === name || `${trigger.table}.${trigger.name}` === name) {
209470
+ return describeTrigger(module.name, trigger);
209471
+ }
209472
+ }
209473
+ for (const table of module.tables) {
209474
+ if (table === name) {
209475
+ return [
209476
+ `对象: ${table}`,
209477
+ "类型: 表 (table)",
209478
+ `所属模块: ${module.name}`
209479
+ ].join(`
209480
+ `);
209481
+ }
209482
+ }
209483
+ for (const grant of module.grants) {
209484
+ if (grant.object === name)
209485
+ return describeGrant(module.name, grant);
209486
+ }
209487
+ }
209488
+ return `未找到对象: ${name}`;
209489
+ }
209490
+ var COMMAND_PERSISTENCE_SQL = `
209491
+ DO $runtime$
209492
+ BEGIN
209493
+ IF to_regprocedure('supacloud_workflows.start_run(uuid,text,text,text,jsonb,integer)') IS NULL
209494
+ OR to_regprocedure('supacloud_commands.status(jsonb)') IS NULL THEN
209495
+ RAISE EXCEPTION 'Install the SupaCloud Commands/Workflow/PGMQ runtime before command persistence';
209496
+ END IF;
209497
+ END
209498
+ $runtime$;
209499
+ CREATE SCHEMA IF NOT EXISTS supacloud_commands;
209500
+ REVOKE ALL ON SCHEMA supacloud_commands FROM PUBLIC;
209501
+ CREATE TABLE IF NOT EXISTS supacloud_commands.execution_receipts (
209502
+ tenant_id text NOT NULL,
209503
+ actor_id text NOT NULL,
209504
+ command text NOT NULL,
209505
+ operation_key text NOT NULL,
209506
+ kind text NOT NULL CHECK (kind IN ('transactional', 'external')),
209507
+ input_fingerprint text NOT NULL CHECK (input_fingerprint ~ '^[a-f0-9]{64}$'),
209508
+ input_payload text,
209509
+ dispatch_key uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
209510
+ status text NOT NULL CHECK (status IN ('pending', 'unknown', 'confirmed', 'rejected')),
209511
+ audit_state text NOT NULL CHECK (audit_state IN ('pending', 'complete')),
209512
+ result jsonb,
209513
+ created_at timestamptz NOT NULL DEFAULT now(),
209514
+ updated_at timestamptz NOT NULL DEFAULT now(),
209515
+ PRIMARY KEY (tenant_id, actor_id, command, operation_key),
209516
+ CHECK ((status = 'confirmed') = (result IS NOT NULL)),
209517
+ CONSTRAINT execution_receipts_audit_terminal_check CHECK (
209518
+ (audit_state <> 'complete' OR status IN ('confirmed', 'rejected'))
209519
+ AND (status <> 'rejected' OR audit_state = 'complete')),
209520
+ CONSTRAINT execution_receipts_transactional_check CHECK (
209521
+ kind <> 'transactional' OR (status = 'confirmed' AND audit_state = 'complete'))
209522
+ );
209523
+ CREATE TABLE IF NOT EXISTS supacloud_commands.execution_audit (
209524
+ tenant_id text NOT NULL,
209525
+ actor_id text NOT NULL,
209526
+ command text NOT NULL,
209527
+ operation_key text NOT NULL,
209528
+ event text NOT NULL,
209529
+ details jsonb NOT NULL,
209530
+ created_at timestamptz NOT NULL DEFAULT now(),
209531
+ PRIMARY KEY (tenant_id, actor_id, command, operation_key),
209532
+ FOREIGN KEY (tenant_id, actor_id, command, operation_key)
209533
+ REFERENCES supacloud_commands.execution_receipts (tenant_id, actor_id, command, operation_key)
209534
+ );
209535
+ REVOKE ALL ON supacloud_commands.execution_receipts, supacloud_commands.execution_audit FROM PUBLIC;
209536
+ CREATE OR REPLACE FUNCTION supacloud_commands.protect_terminal_execution()
209537
+ RETURNS trigger LANGUAGE plpgsql SET search_path = '' AS $terminal$
209538
+ BEGIN
209539
+ IF OLD.status IN ('confirmed','rejected') AND (
209540
+ NEW.status IS DISTINCT FROM OLD.status OR NEW.result IS DISTINCT FROM OLD.result
209541
+ OR (OLD.audit_state = 'complete' AND NEW.audit_state IS DISTINCT FROM OLD.audit_state)) THEN
209542
+ RAISE EXCEPTION 'SUPACLOUD_COMMAND_TERMINAL_RECEIPT';
209543
+ END IF;
209544
+ RETURN NEW;
209545
+ END
209546
+ $terminal$;
209547
+ CREATE OR REPLACE TRIGGER execution_terminal_guard
209548
+ BEFORE UPDATE ON supacloud_commands.execution_receipts
209549
+ FOR EACH ROW EXECUTE FUNCTION supacloud_commands.protect_terminal_execution();
209550
+
209551
+ CREATE OR REPLACE FUNCTION supacloud_commands.require_rejection_audit()
209552
+ RETURNS trigger LANGUAGE plpgsql SET search_path = '' AS $rejection$
209553
+ BEGIN
209554
+ IF NEW.status = 'rejected' AND NOT EXISTS (
209555
+ SELECT FROM supacloud_commands.execution_audit a
209556
+ WHERE a.tenant_id=NEW.tenant_id AND a.actor_id=NEW.actor_id
209557
+ AND a.command=NEW.command AND a.operation_key=NEW.operation_key
209558
+ ) THEN
209559
+ RAISE EXCEPTION 'SUPACLOUD_COMMAND_REJECTION_AUDIT_REQUIRED';
209560
+ END IF;
209561
+ RETURN NEW;
209562
+ END
209563
+ $rejection$;
209564
+ DROP TRIGGER IF EXISTS execution_rejection_audit ON supacloud_commands.execution_receipts;
209565
+ CREATE CONSTRAINT TRIGGER execution_rejection_audit
209566
+ AFTER INSERT OR UPDATE ON supacloud_commands.execution_receipts
209567
+ DEFERRABLE INITIALLY DEFERRED
209568
+ FOR EACH ROW WHEN (NEW.status = 'rejected')
209569
+ EXECUTE FUNCTION supacloud_commands.require_rejection_audit();
209570
+ CREATE OR REPLACE FUNCTION supacloud_commands.enqueue_execution_recovery()
209571
+ RETURNS trigger LANGUAGE plpgsql SET search_path = '' AS $recovery$
209572
+ DECLARE submission jsonb;
209573
+ BEGIN
209574
+ PERFORM pg_advisory_xact_lock(hashtextextended(NEW.dispatch_key::text,0));
209575
+ SELECT supacloud_commands.snapshot(NEW.dispatch_key,false) INTO submission;
209576
+ IF submission IS NOT NULL THEN
209577
+ IF submission->>'commandType' IS DISTINCT FROM NEW.command
209578
+ OR submission->>'actorId' IS DISTINCT FROM NEW.actor_id
209579
+ OR submission->>'tenantId' IS DISTINCT FROM NEW.tenant_id
209580
+ OR NEW.operation_key <> NEW.dispatch_key::text THEN
209581
+ RAISE EXCEPTION 'SUPACLOUD_COMMAND_IDENTITY_CONFLICT';
209582
+ END IF;
209583
+ RETURN NEW;
209584
+ END IF;
209585
+ IF NEW.kind = 'external' AND NEW.status <> 'rejected'
209586
+ AND (NEW.status <> 'confirmed' OR NEW.audit_state <> 'complete') THEN
209587
+ PERFORM supacloud_workflows.start_run(
209588
+ NEW.dispatch_key, 'supacloud.command.reconcile', '1', 'reconcile',
209589
+ jsonb_build_object('commandId', NEW.dispatch_key, 'tenantId', NEW.tenant_id,
209590
+ 'actorId', NEW.actor_id, 'command', NEW.command, 'operationId', NEW.operation_key),
209591
+ 20
209592
+ );
209593
+ END IF;
209594
+ RETURN NEW;
209595
+ END
209596
+ $recovery$;
209597
+ CREATE OR REPLACE TRIGGER execution_recovery_enqueue
209598
+ AFTER INSERT ON supacloud_commands.execution_receipts
209599
+ FOR EACH ROW EXECUTE FUNCTION supacloud_commands.enqueue_execution_recovery();
209600
+
209601
+ CREATE OR REPLACE FUNCTION supacloud_commands.execution_status(request jsonb)
209602
+ RETURNS jsonb LANGUAGE sql STABLE SECURITY DEFINER SET search_path = '' AS $status$
209603
+ SELECT jsonb_build_object('kind','execution','commandId',r.dispatch_key,
209604
+ 'execution',jsonb_build_object('tenantId',r.tenant_id,'actorId',r.actor_id,
209605
+ 'command',r.command,'operationId',r.operation_key,'dispatchKey',r.dispatch_key,
209606
+ 'status',r.status,'audit',r.audit_state)
209607
+ || CASE WHEN r.status='confirmed' THEN jsonb_build_object('result',r.result) ELSE '{}'::jsonb END,
209608
+ 'workflow',CASE WHEN w.id IS NULL THEN NULL ELSE jsonb_build_object('runId',w.id,'status',w.status) END)
209609
+ FROM supacloud_commands.execution_receipts r
209610
+ LEFT JOIN supacloud_workflows.runs w ON w.id=r.dispatch_key
209611
+ WHERE CASE WHEN request ? 'commandId' THEN r.dispatch_key::text=request->>'commandId'
209612
+ ELSE r.tenant_id=request->>'tenantId' AND r.actor_id=request->>'actorId'
209613
+ AND r.command=request->>'command' AND r.operation_key=request->>'operationId' END
209614
+ $status$;
209615
+ REVOKE ALL ON FUNCTION supacloud_commands.execution_status(jsonb) FROM PUBLIC;
209616
+ `;
209617
+ var COMMAND_PERSISTENCE_UPGRADE_SQL = `
209618
+ ALTER TABLE supacloud_commands.execution_receipts
209619
+ ADD COLUMN IF NOT EXISTS input_fingerprint text,
209620
+ ADD COLUMN IF NOT EXISTS input_payload text;
209621
+ DO $migration$
209622
+ BEGIN
209623
+ IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='supacloud_commands'
209624
+ AND table_name='execution_receipts' AND column_name='input_key') THEN
209625
+ EXECUTE 'UPDATE supacloud_commands.execution_receipts
209626
+ SET input_fingerprint=encode(sha256(convert_to(input_key,''UTF8'')),''hex''),input_payload=input_key
209627
+ WHERE input_fingerprint IS NULL';
209628
+ ALTER TABLE supacloud_commands.execution_receipts DROP COLUMN input_key;
209629
+ END IF;
209630
+ END
209631
+ $migration$;
209632
+ ALTER TABLE supacloud_commands.execution_receipts ALTER COLUMN input_fingerprint SET NOT NULL;
209633
+ ALTER TABLE supacloud_commands.execution_receipts
209634
+ DROP CONSTRAINT IF EXISTS execution_receipts_status_check,
209635
+ DROP CONSTRAINT IF EXISTS execution_receipts_check1,
209636
+ DROP CONSTRAINT IF EXISTS execution_receipts_transactional_check,
209637
+ DROP CONSTRAINT IF EXISTS execution_receipts_audit_terminal_check;
209638
+ ALTER TABLE supacloud_commands.execution_receipts
209639
+ ADD CONSTRAINT execution_receipts_status_check CHECK (status IN ('pending','unknown','confirmed','rejected')),
209640
+ ADD CONSTRAINT execution_receipts_transactional_check CHECK (
209641
+ kind <> 'transactional' OR (status = 'confirmed' AND audit_state = 'complete')),
209642
+ ADD CONSTRAINT execution_receipts_audit_terminal_check CHECK (
209643
+ (audit_state <> 'complete' OR status IN ('confirmed','rejected'))
209644
+ AND (status <> 'rejected' OR audit_state = 'complete'));
209645
+ DO $constraint$
209646
+ BEGIN
209647
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint
209648
+ WHERE conrelid='supacloud_commands.execution_receipts'::regclass
209649
+ AND conname='execution_receipts_input_fingerprint_check') THEN
209650
+ ALTER TABLE supacloud_commands.execution_receipts
209651
+ ADD CONSTRAINT execution_receipts_input_fingerprint_check CHECK (input_fingerprint ~ '^[a-f0-9]{64}$');
209652
+ END IF;
209653
+ END
209654
+ $constraint$;
209655
+ ${COMMAND_PERSISTENCE_SQL}
209656
+ DO $backfill$
209657
+ DECLARE r record;
209658
+ BEGIN
209659
+ FOR r IN SELECT * FROM supacloud_commands.execution_receipts
209660
+ WHERE kind='external' AND status<>'rejected' AND (status<>'confirmed' OR audit_state<>'complete')
209661
+ AND NOT EXISTS(SELECT FROM supacloud_workflows.runs w WHERE w.id=dispatch_key)
209662
+ LOOP
209663
+ PERFORM supacloud_workflows.start_run(
209664
+ r.dispatch_key,'supacloud.command.reconcile','1','reconcile',
209665
+ jsonb_build_object('commandId',r.dispatch_key,'tenantId',r.tenant_id,
209666
+ 'actorId',r.actor_id,'command',r.command,'operationId',r.operation_key),20
209667
+ );
209668
+ END LOOP;
209669
+ END
209670
+ $backfill$;
209671
+ DROP INDEX IF EXISTS supacloud_commands.execution_pending_recovery;
209672
+ ALTER TABLE supacloud_commands.execution_receipts
209673
+ DROP COLUMN IF EXISTS lease_id,
209674
+ DROP COLUMN IF EXISTS lease_until,
209675
+ DROP COLUMN IF EXISTS next_attempt_at,
209676
+ DROP COLUMN IF EXISTS recovery_attempts;
209677
+ `;
209678
+ var MAX_MIGRATION_VERSION = 9223372036854775807n;
209679
+ function sha2562(value) {
209680
+ return createHash22("sha256").update(value).digest("hex");
209681
+ }
209682
+ function normalizedSql(value) {
209683
+ return value.replace(/\r\n?/g, `
209684
+ `).trimEnd() + `
209685
+ `;
209686
+ }
209687
+ function validVersion(value) {
209688
+ if (!/^\d{1,19}$/.test(value))
209689
+ return false;
209690
+ const version = BigInt(value);
209691
+ return version > 0n && version <= MAX_MIGRATION_VERSION;
209692
+ }
209693
+ function migrationVersion(file) {
209694
+ const match = basename2(file).match(/^(\d{8,19})[_-]/);
209695
+ if (!match || !validVersion(match[1]))
209696
+ throw new Error(`Invalid migration version in ${file}`);
209697
+ return BigInt(match[1]).toString();
209698
+ }
209699
+ function migrationName(file) {
209700
+ return basename2(file, ".sql").replace(/^\d{8,19}[_-]/, "");
209701
+ }
209702
+ function sortMigrations(migrations) {
209703
+ const sorted = [...migrations].sort((left, right) => {
209704
+ const leftVersion = BigInt(left.version);
209705
+ const rightVersion = BigInt(right.version);
209706
+ if (leftVersion !== rightVersion)
209707
+ return leftVersion < rightVersion ? -1 : 1;
209708
+ return left.file.localeCompare(right.file);
209709
+ });
209710
+ for (let index = 1;index < sorted.length; index += 1) {
209711
+ if (sorted[index - 1].version === sorted[index].version) {
209712
+ throw new Error(`Duplicate migration version ${sorted[index].version}`);
209713
+ }
209714
+ }
209715
+ return sorted;
209716
+ }
209717
+ function planMigrationRebase(migrations, options, baselineSql) {
209718
+ const sorted = sortMigrations([...migrations]);
209719
+ if (!sorted.length)
209720
+ throw new Error("No migration files found");
209721
+ if (!validVersion(options.baselineVersion))
209722
+ throw new Error("Invalid baseline version");
209723
+ if (options.retainAfterVersion !== undefined && !validVersion(options.retainAfterVersion)) {
209724
+ throw new Error("Invalid retain-after version");
209725
+ }
209726
+ const baselineName = options.baselineName?.trim() || "reconstructed_schema";
209727
+ if (!/^[a-z0-9][a-z0-9_-]*$/.test(baselineName))
209728
+ throw new Error("Invalid baseline name");
209729
+ const baselineMigration = `${options.baselineVersion}_${baselineName}.sql`;
209730
+ if (sorted.some((migration) => migration.version === options.baselineVersion)) {
209731
+ throw new Error(`Baseline version ${options.baselineVersion} collides with existing history`);
209732
+ }
209733
+ const retainAfter = options.retainAfterVersion === undefined ? null : BigInt(options.retainAfterVersion);
209734
+ const retained = sorted.filter((migration) => retainAfter !== null && BigInt(migration.version) > retainAfter);
209735
+ const archived = sorted.filter((migration) => !retained.includes(migration));
209736
+ const normalizedBaseline = normalizedSql(baselineSql);
209737
+ if (!normalizedBaseline.trim())
209738
+ throw new Error("Baseline SQL is empty");
209739
+ if (!/\bCREATE\b/i.test(normalizedBaseline))
209740
+ throw new Error("Baseline SQL does not look like a schema snapshot");
209741
+ const sourceHistory = sorted.map((migration) => ({
209742
+ file: migration.file,
209743
+ version: migration.version,
209744
+ name: migration.name,
209745
+ sha256: sha2562(normalizedSql(migration.sql))
209746
+ }));
209747
+ return {
209748
+ version: 1,
209749
+ sourceDirectory: options.sourceDirectory,
209750
+ outputDirectory: options.outputDirectory,
209751
+ baselineFile: options.baselineFile,
209752
+ baselineMigration,
209753
+ retainedMigrations: retained.map((migration) => migration.file),
209754
+ archivedMigrations: archived.map((migration) => migration.file),
209755
+ sourceHistorySha256: sha2562(JSON.stringify(sourceHistory)),
209756
+ baselineSha256: sha2562(normalizedBaseline),
209757
+ warnings: [
209758
+ "The baseline must come from a schema-only snapshot of the verified target database.",
209759
+ "Data backfills, reference data and non-replayable side effects need a separate review.",
209760
+ "Archive the old migration directory until clean replay and remote read-back have passed."
209761
+ ]
209762
+ };
209763
+ }
209764
+ async function rebaseMigrations(options) {
209765
+ const sourceDirectory = resolve5(options.sourceDirectory);
209766
+ const outputDirectory = resolve5(options.outputDirectory);
209767
+ const baselineFile = resolve5(options.baselineFile);
209768
+ if (!(await stat(sourceDirectory)).isDirectory())
209769
+ throw new Error("Source migration directory is not a directory");
209770
+ if (!(await stat(baselineFile)).isFile())
209771
+ throw new Error("Baseline file is not a file");
209772
+ if (sourceDirectory === outputDirectory)
209773
+ throw new Error("Output directory must be separate from source directory");
209774
+ const files = (await readdir3(sourceDirectory)).filter((file) => file.endsWith(".sql")).sort();
209775
+ const migrations = await Promise.all(files.map(async (file) => {
209776
+ const sql = await readFile2(join3(sourceDirectory, file), "utf8");
209777
+ return { file, version: migrationVersion(file), name: migrationName(file), sql };
209778
+ }));
209779
+ const plan = planMigrationRebase(migrations, { ...options, sourceDirectory, outputDirectory, baselineFile }, await readFile2(baselineFile, "utf8"));
209780
+ await mkdir2(outputDirectory, { recursive: true });
209781
+ await writeFile2(join3(outputDirectory, plan.baselineMigration), normalizedSql(await readFile2(baselineFile, "utf8")));
209782
+ for (const migration of migrations.filter((item) => plan.retainedMigrations.includes(item.file))) {
209783
+ await writeFile2(join3(outputDirectory, migration.file), normalizedSql(migration.sql));
209784
+ }
209785
+ await writeFile2(join3(outputDirectory, "migration-rebase.manifest.json"), JSON.stringify({
209786
+ ...plan,
209787
+ generatedAt: new Date().toISOString(),
209788
+ sourceHistory: migrations.map((migration) => ({
209789
+ file: migration.file,
209790
+ version: migration.version,
209791
+ name: migration.name,
209792
+ sha256: sha2562(normalizedSql(migration.sql))
209793
+ }))
209794
+ }, null, 2) + `
209795
+ `);
209796
+ return plan;
209797
+ }
209798
+
209057
209799
  // src/shared/project-ref.ts
209058
209800
  var PROJECT_REF_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
209059
209801
  function projectRefPathSegment(ref, operation) {
@@ -209782,19 +210524,19 @@ function formatMigrationRiskReport(analysis) {
209782
210524
 
209783
210525
  // src/shared/tools/migration-import.ts
209784
210526
  import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync, statSync } from "node:fs";
209785
- import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
209786
- import { basename as basename2, join as join3 } from "node:path";
210527
+ import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
210528
+ import { basename as basename3, join as join4 } from "node:path";
209787
210529
  async function writeMigrationImportPlan(plan, outputDirectory) {
209788
210530
  if (!outputDirectory.trim())
209789
210531
  throw new Error("Migration import output directory is required");
209790
- await mkdir2(outputDirectory, { recursive: true });
210532
+ await mkdir3(outputDirectory, { recursive: true });
209791
210533
  const written = [];
209792
210534
  for (const file of plan.files) {
209793
210535
  if (!/^[A-Za-z0-9_.-]+\.sql$/.test(file.file) || file.file.includes("..")) {
209794
210536
  throw new Error(`Unsafe migration import filename: ${file.file}`);
209795
210537
  }
209796
- const path = join3(outputDirectory, file.file);
209797
- await writeFile2(path, file.sql, { flag: "wx", encoding: "utf8" });
210538
+ const path = join4(outputDirectory, file.file);
210539
+ await writeFile3(path, file.sql, { flag: "wx", encoding: "utf8" });
209798
210540
  written.push(path);
209799
210541
  }
209800
210542
  return written;
@@ -209805,8 +210547,8 @@ function record3(value) {
209805
210547
  function sqlFile(file, root, source) {
209806
210548
  return {
209807
210549
  file,
209808
- name: basename2(file, ".sql"),
209809
- sql: readFileSync2(join3(root, file), "utf8"),
210550
+ name: basename3(file, ".sql"),
210551
+ sql: readFileSync2(join4(root, file), "utf8"),
209810
210552
  source: "sql"
209811
210553
  };
209812
210554
  }
@@ -209908,7 +210650,7 @@ function createMigrationImportPlan(input) {
209908
210650
  }
209909
210651
 
209910
210652
  // src/shared/tools/database-tools.ts
209911
- var MAX_MIGRATION_VERSION = 9223372036854775807n;
210653
+ var MAX_MIGRATION_VERSION2 = 9223372036854775807n;
209912
210654
  var FALLBACK_MIGRATION_VERSION_BASE = 8000000000000000000n;
209913
210655
  var FALLBACK_MIGRATION_VERSION_RANGE = 1000000000000000000n;
209914
210656
  var FALLBACK_MIGRATION_VERSION_LIMIT = FALLBACK_MIGRATION_VERSION_BASE + FALLBACK_MIGRATION_VERSION_RANGE;
@@ -209918,7 +210660,7 @@ function isMigrationInventoryVersion(version) {
209918
210660
  if (typeof version !== "string" || !/^\d{1,19}$/.test(version))
209919
210661
  return false;
209920
210662
  const numericVersion = BigInt(version);
209921
- return numericVersion >= 1n && numericVersion <= MAX_MIGRATION_VERSION && numericVersion.toString() === version;
210663
+ return numericVersion >= 1n && numericVersion <= MAX_MIGRATION_VERSION2 && numericVersion.toString() === version;
209922
210664
  }
209923
210665
  function isMigrationInventoryName(name) {
209924
210666
  return name === null || typeof name === "string" && name.length <= 255 && name.length > 0 && name.trim() === name;
@@ -210013,28 +210755,28 @@ function migrationInventoryResponse(response) {
210013
210755
  return { content: [{ type: "text", text: JSON.stringify(inventory, null, 2) }] };
210014
210756
  }
210015
210757
  function readMigrationFile(dir, file) {
210016
- const rawBytes = readFileSync3(join4(dir, file));
210758
+ const rawBytes = readFileSync3(join5(dir, file));
210017
210759
  return {
210018
210760
  file,
210019
- name: basename3(file, ".sql"),
210761
+ name: basename4(file, ".sql"),
210020
210762
  version: migrationVersionFromFilename(file),
210021
210763
  sql: Buffer.from(rawBytes).toString("utf8"),
210022
210764
  rawBytes
210023
210765
  };
210024
210766
  }
210025
210767
  function migrationVersionFromFilename(file) {
210026
- const match = basename3(file).match(/^(\d{8,20})[_-]/);
210768
+ const match = basename4(file).match(/^(\d{8,20})[_-]/);
210027
210769
  if (match) {
210028
210770
  const version = BigInt(match[1]);
210029
- if (version < 1n || version > MAX_MIGRATION_VERSION) {
210030
- throw new Error(`Invalid migration version '${match[1]}' in ${basename3(file)}: expected 1..${MAX_MIGRATION_VERSION}`);
210771
+ if (version < 1n || version > MAX_MIGRATION_VERSION2) {
210772
+ throw new Error(`Invalid migration version '${match[1]}' in ${basename4(file)}: expected 1..${MAX_MIGRATION_VERSION2}`);
210031
210773
  }
210032
210774
  if (version >= FALLBACK_MIGRATION_VERSION_BASE && version < FALLBACK_MIGRATION_VERSION_LIMIT) {
210033
- throw new Error(`Invalid migration version '${match[1]}' in ${basename3(file)}: version range ${FALLBACK_MIGRATION_VERSION_BASE}..${FALLBACK_MIGRATION_VERSION_LIMIT - 1n} is reserved for non-timestamp migrations`);
210775
+ throw new Error(`Invalid migration version '${match[1]}' in ${basename4(file)}: version range ${FALLBACK_MIGRATION_VERSION_BASE}..${FALLBACK_MIGRATION_VERSION_LIMIT - 1n} is reserved for non-timestamp migrations`);
210034
210776
  }
210035
210777
  return version.toString();
210036
210778
  }
210037
- const digest = createHash3("sha256").update(basename3(file)).digest("hex");
210779
+ const digest = createHash3("sha256").update(basename4(file)).digest("hex");
210038
210780
  const offset = BigInt(`0x${digest}`) % FALLBACK_MIGRATION_VERSION_RANGE;
210039
210781
  return (FALLBACK_MIGRATION_VERSION_BASE + offset).toString();
210040
210782
  }
@@ -210310,7 +211052,7 @@ function vectorWarningsForPendingMigrations(migrations, vectorEnabled) {
210310
211052
  }
210311
211053
  function registerDatabaseTools(server2, http, config = {}) {
210312
211054
  const { localOnly = false, readOnly = false, projectRef } = config;
210313
- const localActions = ["lint_migrations", "lint"];
211055
+ const localActions = ["lint_migrations", "lint", "rebase_migrations"];
210314
211056
  const readActions = [
210315
211057
  "query",
210316
211058
  "list_tables",
@@ -210348,7 +211090,7 @@ Actions: ${allActions.join(", ")}${localOnly ? " (local-only mode)" : readOnly ?
210348
211090
  file: optional(String2(), "[query/execute/apply_migration/lint_migrations/lint] Read SQL from local file path (avoids shell escaping issues with $$ and multi-statement DDL)"),
210349
211091
  mode: optional(stringEnum(["read", "migration", "admin"]), "[query/execute] SQL execution mode: read, migration, or admin. execute defaults to migration; query defaults to read/auto"),
210350
211092
  admin: optional(Boolean2(), "[query/execute] Admin execution flag (required when mode=admin)"),
210351
- dir: optional(String2(), "[push_migrations/baseline_migrations/lint_migrations/lint] Directory containing .sql migration files (default: supabase/migrations)"),
211093
+ dir: optional(String2(), "[push_migrations/baseline_migrations/lint_migrations/lint/rebase_migrations] Directory containing .sql migration files (default: supabase/migrations)"),
210352
211094
  dry_run: optional(Boolean2(), "[push_migrations/baseline_migrations] Preview changes without applying them"),
210353
211095
  strict: optional(Boolean2(), "[push_migrations/lint_migrations/lint/database_lint] Exit with error if high-risk issues or migrations are detected"),
210354
211096
  fail_on_high: optional(Boolean2(), "[push_migrations/lint_migrations/lint/database_lint] Alias for --strict"),
@@ -210366,6 +211108,10 @@ Actions: ${allActions.join(", ")}${localOnly ? " (local-only mode)" : readOnly ?
210366
211108
  metadata_file: optional(String2(), "[migration_import] Hasura metadata JSON file"),
210367
211109
  output_dir: optional(String2(), "[migration_import] Output migration directory when write=true"),
210368
211110
  write: optional(Boolean2(), "[migration_import] Write the reviewed plan to output_dir; never applies SQL"),
211111
+ baseline_file: optional(String2(), "[rebase_migrations] Reviewed schema-only snapshot to use as the new baseline"),
211112
+ baseline_version: optional(String2(), "[rebase_migrations] New migration version for the baseline"),
211113
+ baseline_name: optional(String2(), "[rebase_migrations] New baseline migration name"),
211114
+ retain_after_version: optional(String2(), "[rebase_migrations] Retain migrations newer than this version"),
210369
211115
  columns: optional(String2(), "[create_table_rls] Column definitions"),
210370
211116
  policy_mode: optional(stringEnum(["deny_all", "owner"]), "[create_table_rls] RLS policy mode (default: deny_all)"),
210371
211117
  owner_column: optional(String2(), "[create_table_rls owner] UUID owner column matched to auth.uid()"),
@@ -210383,6 +211129,34 @@ Actions: ${allActions.join(", ")}${localOnly ? " (local-only mode)" : readOnly ?
210383
211129
  const ref = args.ref || projectRef;
210384
211130
  const schema = args.schema || "public";
210385
211131
  const schemas = args.schemas || ["public"];
211132
+ if (action === "rebase_migrations") {
211133
+ const sourceDirectory = args.dir || "supabase/migrations";
211134
+ const outputDirectory = args.output_dir;
211135
+ const baselineFile = args.baseline_file;
211136
+ const baselineVersion = args.baseline_version;
211137
+ if (typeof outputDirectory !== "string" || typeof baselineFile !== "string" || typeof baselineVersion !== "string") {
211138
+ throw new Error("rebase_migrations requires output_dir, baseline_file and baseline_version");
211139
+ }
211140
+ const options = {
211141
+ sourceDirectory,
211142
+ outputDirectory,
211143
+ baselineFile,
211144
+ baselineVersion,
211145
+ ...typeof args.baseline_name === "string" ? { baselineName: args.baseline_name } : {},
211146
+ ...typeof args.retain_after_version === "string" ? { retainAfterVersion: args.retain_after_version } : {}
211147
+ };
211148
+ if (args.write !== true) {
211149
+ const files = readdirSync2(sourceDirectory).filter((file) => file.endsWith(".sql")).sort();
211150
+ const migrations = files.map((file) => {
211151
+ const migration = readMigrationFile(sourceDirectory, file);
211152
+ return { file, version: migration.version, name: migration.name, sql: migration.sql };
211153
+ });
211154
+ const plan = planMigrationRebase(migrations, options, readFileSync3(baselineFile, "utf8"));
211155
+ return { content: [{ type: "text", text: JSON.stringify({ mode: "plan", ...plan }, null, 2) }] };
211156
+ }
211157
+ const plan = await rebaseMigrations(options);
211158
+ return { content: [{ type: "text", text: JSON.stringify({ mode: "written", ...plan }, null, 2) }] };
211159
+ }
210386
211160
  if (action === "delivery_migration_plan") {
210387
211161
  const failure = (code, httpStatus = null) => ({
210388
211162
  isError: true,
@@ -211988,7 +212762,7 @@ function registerOAuthClientTools(server2, http) {
211988
212762
 
211989
212763
  // src/shared/tools/storage-tools.ts
211990
212764
  import { existsSync as existsSync4, openAsBlob, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
211991
- import { basename as basename4, resolve as resolve5 } from "node:path";
212765
+ import { basename as basename5, resolve as resolve6 } from "node:path";
211992
212766
  var MAX_BUCKET_ID_LENGTH = 100;
211993
212767
  var MAX_MIME_TYPE_COUNT = 100;
211994
212768
  var MAX_MIME_TYPE_LENGTH = 255;
@@ -212447,7 +213221,7 @@ Actions: status, list_buckets, get_bucket, create_bucket, update_bucket, delete_
212447
213221
  if (typeof rawFilePath !== "string" || !rawFilePath.trim()) {
212448
213222
  throw new Error(`'file_path' (or '--file') required for '${action}'`);
212449
213223
  }
212450
- const resolvedFilePath = resolve5(process.cwd(), rawFilePath.trim());
213224
+ const resolvedFilePath = resolve6(process.cwd(), rawFilePath.trim());
212451
213225
  if (!existsSync4(resolvedFilePath)) {
212452
213226
  throw new Error(`File not found: ${resolvedFilePath}`);
212453
213227
  }
@@ -212455,7 +213229,7 @@ Actions: status, list_buckets, get_bucket, create_bucket, update_bucket, delete_
212455
213229
  if (!stat.isFile()) {
212456
213230
  throw new Error(`Path is not a regular file: ${resolvedFilePath}`);
212457
213231
  }
212458
- const filename = typeof args.filename === "string" && args.filename.trim() ? args.filename.trim() : basename4(resolvedFilePath);
213232
+ const filename = typeof args.filename === "string" && args.filename.trim() ? args.filename.trim() : basename5(resolvedFilePath);
212459
213233
  const mimeType = typeof args.mime_type === "string" && args.mime_type.trim() ? args.mime_type.trim() : "application/octet-stream";
212460
213234
  let timeoutMs = DEFAULT_UPLOAD_TIMEOUT_MS;
212461
213235
  if (args.timeout_ms !== undefined) {
@@ -212503,7 +213277,7 @@ import {
212503
213277
  writeFileSync
212504
213278
  } from "node:fs";
212505
213279
  import { tmpdir } from "node:os";
212506
- import { basename as basename5, join as join5, relative as relative2, resolve as resolve6, sep as sep2 } from "node:path";
213280
+ import { basename as basename6, join as join6, relative as relative2, resolve as resolve7, sep as sep2 } from "node:path";
212507
213281
 
212508
213282
  // src/shared/tools/edge-function-response.ts
212509
213283
  var CANONICAL_VERSION_PATTERN = /^(?:0|[1-9][0-9]*)$/;
@@ -212806,13 +213580,13 @@ function scaffoldFunction(pathArg, slug, framework) {
212806
213580
  if (typeof framework !== "string" || !FUNCTION_FRAMEWORKS2.includes(framework)) {
212807
213581
  throw new Error("'framework' required for 'scaffold'");
212808
213582
  }
212809
- const target = resolve6(typeof pathArg === "string" ? pathArg : join5("supabase", "functions", slug));
213583
+ const target = resolve7(typeof pathArg === "string" ? pathArg : join6("supabase", "functions", slug));
212810
213584
  if (existsSync5(target))
212811
213585
  throw new Error(`Scaffold target already exists: ${target}`);
212812
213586
  const files = scaffoldFiles(framework, slug);
212813
213587
  for (const [relativePath, contents] of Object.entries(files)) {
212814
- const destination = join5(target, relativePath);
212815
- mkdirSync(resolve6(destination, ".."), { recursive: true });
213588
+ const destination = join6(target, relativePath);
213589
+ mkdirSync(resolve7(destination, ".."), { recursive: true });
212816
213590
  writeFileSync(destination, contents, { flag: "wx" });
212817
213591
  }
212818
213592
  return JSON.stringify({ success: true, framework, path: target, files: Object.keys(files) }, null, 2);
@@ -212857,7 +213631,7 @@ function assertExpectedSha256(bytes, expectedSha256) {
212857
213631
  }
212858
213632
  }
212859
213633
  function readVerifiedPrebundledCode(pathArg, expectedSha256) {
212860
- const descriptor = openSync(resolve6(pathArg), fsConstants.O_RDONLY | fsConstants.O_NONBLOCK);
213634
+ const descriptor = openSync(resolve7(pathArg), fsConstants.O_RDONLY | fsConstants.O_NONBLOCK);
212861
213635
  try {
212862
213636
  const identityBeforeRead = openFileIdentity(descriptor);
212863
213637
  const bytes = readFileSync5(descriptor);
@@ -212883,7 +213657,7 @@ function readBundleDirectory(root, directory = root) {
212883
213657
  const files = {};
212884
213658
  const entries = readdirSync3(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
212885
213659
  for (const entry of entries) {
212886
- const entryPath = join5(directory, entry.name);
213660
+ const entryPath = join6(directory, entry.name);
212887
213661
  const bundlePath = canonicalBundlePath(root, entryPath);
212888
213662
  const entryState = lstatSync(entryPath);
212889
213663
  if (entryState.isSymbolicLink())
@@ -212906,7 +213680,7 @@ function preparedBundleFiles(args) {
212906
213680
  }
212907
213681
  if (typeof bundleDirectory !== "string")
212908
213682
  return files;
212909
- const resolvedDirectory = resolve6(bundleDirectory);
213683
+ const resolvedDirectory = resolve7(bundleDirectory);
212910
213684
  const rootState = lstatSync(resolvedDirectory);
212911
213685
  if (rootState.isSymbolicLink() || !rootState.isDirectory()) {
212912
213686
  throw new Error("'--bundle-dir' must be a regular directory and must not be a symlink");
@@ -212944,8 +213718,8 @@ async function runBunBuild(args) {
212944
213718
  }
212945
213719
  async function bundleEdgeFunctionPath(pathArg) {
212946
213720
  const entrypoint = resolveEntrypoint(pathArg);
212947
- const tmpDir = mkdtempSync(join5(tmpdir(), "supacloud-edge-"));
212948
- const outfile = join5(tmpDir, `${basename5(entrypoint).replace(/\.[^.]+$/, "") || "index"}.js`);
213721
+ const tmpDir = mkdtempSync(join6(tmpdir(), "supacloud-edge-"));
213722
+ const outfile = join6(tmpDir, `${basename6(entrypoint).replace(/\.[^.]+$/, "") || "index"}.js`);
212949
213723
  try {
212950
213724
  const { stderr } = await runBunBuild([entrypoint, "--target", "bun", "--outfile", outfile]);
212951
213725
  const outFileHandle = Bun.file(outfile);
@@ -213008,11 +213782,11 @@ function rejectActionSpecificFlags(action, args) {
213008
213782
  }
213009
213783
  }
213010
213784
  function resolveEntrypoint(pathArg) {
213011
- const resolved = resolve6(pathArg);
213785
+ const resolved = resolve7(pathArg);
213012
213786
  const stat = statSync4(resolved);
213013
213787
  if (!stat.isDirectory())
213014
213788
  return resolved;
213015
- const entrypoint = join5(resolved, "index.ts");
213789
+ const entrypoint = join6(resolved, "index.ts");
213016
213790
  if (!existsSync5(entrypoint)) {
213017
213791
  throw new Error(`Directory provided but no index.ts found at ${entrypoint}`);
213018
213792
  }
@@ -213316,7 +214090,7 @@ function functionSourceOutput(slug, sourceCode, output) {
213316
214090
  if (!output) {
213317
214091
  return { content: [{ type: "text", text: JSON.stringify({ code: sourceCode }, null, 2) }] };
213318
214092
  }
213319
- const outputPath = resolve6(output);
214093
+ const outputPath = resolve7(output);
213320
214094
  writeFileSync(outputPath, sourceCode, { flag: "wx" });
213321
214095
  return {
213322
214096
  content: [{
@@ -213553,8 +214327,8 @@ Actions: list, get_config, deploy, deploy_bundle, config, source, activate, dele
213553
214327
  });
213554
214328
  const hasFunctionConfig = () => Object.keys(functionConfig()).length > 0;
213555
214329
  const checkSyntax = async (sourceCode) => {
213556
- const tmpDir = mkdtempSync(join5(tmpdir(), "supacloud-edge-check-"));
213557
- const tmpFile = join5(tmpDir, "index.ts");
214330
+ const tmpDir = mkdtempSync(join6(tmpdir(), "supacloud-edge-check-"));
214331
+ const tmpFile = join6(tmpDir, "index.ts");
213558
214332
  writeFileSync(tmpFile, sourceCode);
213559
214333
  try {
213560
214334
  await runBunBuild([tmpFile, "--external", "*"]);
@@ -213845,13 +214619,13 @@ ${JSON.stringify(r.data, null, 2)}` : `❌ Failed (${r.status}): ${JSON.stringif
213845
214619
 
213846
214620
  // src/shared/tools/frontend-tools.ts
213847
214621
  import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
213848
- import { basename as basename6 } from "node:path";
214622
+ import { basename as basename7 } from "node:path";
213849
214623
 
213850
214624
  // src/shared/tools/frontend-release-control.ts
213851
214625
  import { createHash as createHash5 } from "node:crypto";
213852
214626
  import { constants as fsConstants2 } from "node:fs";
213853
214627
  import { open as open2 } from "node:fs/promises";
213854
- import { resolve as resolve7 } from "node:path";
214628
+ import { resolve as resolve8 } from "node:path";
213855
214629
  var PROJECT_REF_PATTERN3 = /^[A-Za-z0-9_-]{1,20}$/u;
213856
214630
  var DEPLOYMENT_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/u;
213857
214631
  var RELEASE_ID_PATTERN = /^[0-9a-f]{64}$/u;
@@ -213991,7 +214765,7 @@ async function archiveSha256(handle, sizeBytes) {
213991
214765
  return hash.digest("hex");
213992
214766
  }
213993
214767
  async function verifiedArchive(path) {
213994
- const archivePath = resolve7(path);
214768
+ const archivePath = resolve8(path);
213995
214769
  const handle = await open2(archivePath, fsConstants2.O_RDONLY | fsConstants2.O_NOFOLLOW);
213996
214770
  try {
213997
214771
  const before = await handle.stat({ bigint: true });
@@ -214388,7 +215162,7 @@ Actions: list, get, create, update, delete, deploy_git, deploy_upload, redeploy,
214388
215162
  }
214389
215163
  const zipBuffer = readFileSync6(zip_path);
214390
215164
  const form = new FormData;
214391
- form.append("file", new Blob([zipBuffer], { type: "application/zip" }), basename6(zip_path));
215165
+ form.append("file", new Blob([zipBuffer], { type: "application/zip" }), basename7(zip_path));
214392
215166
  text = ok(await http.postMultipart(`/v1/projects/${ref}/frontend/deployments/${id}/deploy/upload`, form));
214393
215167
  break;
214394
215168
  case "redeploy":
@@ -216143,7 +216917,7 @@ function registerBranchTools(server2, http, options = {}) {
216143
216917
 
216144
216918
  // src/shared/tools/supabase-cli-tools.ts
216145
216919
  import { chmodSync, existsSync as existsSync7, mkdirSync as mkdirSync2, statSync as statSync5, writeFileSync as writeFileSync2 } from "node:fs";
216146
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6, resolve as resolve8 } from "node:path";
216920
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join7, resolve as resolve9 } from "node:path";
216147
216921
  var SENSITIVE_ENV_KEY = /(?:^|_)(?:PASSWORD|PASS|SECRET|TOKEN|KEY|CREDENTIALS?|AUTHORIZATION|AUTH|SESSION|COOKIE|BEARER|DB_URI|DB_URL|DSN|DATABASE_URL|DATABASE_URI|CONNECTION_STRING|CONNECTION_URI)(?:_|$)/i;
216148
216922
  var VALID_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
216149
216923
  var VALID_MIGRATION_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]{0,100}$/;
@@ -216188,12 +216962,12 @@ function schemaArguments(schema) {
216188
216962
  function workdirArguments(workdir) {
216189
216963
  if (!workdir)
216190
216964
  throw new Error("A workdir is required");
216191
- return ["--workdir", resolve8(workdir)];
216965
+ return ["--workdir", resolve9(workdir)];
216192
216966
  }
216193
216967
  function resolveOutputPath(workdir, file, label) {
216194
216968
  if (!file || /[\r\n\0]/.test(file))
216195
216969
  throw new Error(`${label} file is required`);
216196
- return isAbsolute2(file) ? resolve8(file) : resolve8(workdir, file);
216970
+ return isAbsolute2(file) ? resolve9(file) : resolve9(workdir, file);
216197
216971
  }
216198
216972
  function databaseTargetArguments(databaseUrl) {
216199
216973
  return databaseUrl ? ["--db-url", requirePostgresUrl(databaseUrl)] : ["--local"];
@@ -216367,7 +217141,7 @@ function actionArguments(request, workdir) {
216367
217141
  function buildOfficialSupabaseArgs(request) {
216368
217142
  if (request.action === "version")
216369
217143
  return ["--version"];
216370
- const workdir = request.workdir ? resolve8(request.workdir) : undefined;
217144
+ const workdir = request.workdir ? resolve9(request.workdir) : undefined;
216371
217145
  if (!workdir)
216372
217146
  throw new Error("A workdir is required");
216373
217147
  return [...actionArguments(request, workdir), ...workdirArguments(workdir)];
@@ -216444,13 +217218,13 @@ function resolveOfficialSupabaseCommand(workdir, environment = process.env) {
216444
217218
  }
216445
217219
  return ["npx", "--yes", `supabase@${version}`];
216446
217220
  }
216447
- const localPackageEntry = join6(resolve8(workdir), "node_modules", "supabase", "dist", "supabase.js");
217221
+ const localPackageEntry = join7(resolve9(workdir), "node_modules", "supabase", "dist", "supabase.js");
216448
217222
  if (existsSync7(localPackageEntry))
216449
217223
  return [process.execPath, localPackageEntry];
216450
217224
  return ["supabase"];
216451
217225
  }
216452
217226
  function resolveExistingWorkdir(workdirInput, fallback) {
216453
- const workdir = resolve8(workdirInput || fallback);
217227
+ const workdir = resolve9(workdirInput || fallback);
216454
217228
  if (!existsSync7(workdir) || !statSync5(workdir).isDirectory()) {
216455
217229
  throw new Error(`Supabase workdir not found: ${workdir}`);
216456
217230
  }
@@ -216551,7 +217325,7 @@ async function executeMigrationPush(request, runtime) {
216551
217325
  if (!projectRef)
216552
217326
  return missingProjectRefResult();
216553
217327
  const workdir = resolveExistingWorkdir(request.workdir, runtime.fallbackWorkdir);
216554
- const migrationDirectory = resolve8(workdir, request.dir || "supabase/migrations");
217328
+ const migrationDirectory = resolve9(workdir, request.dir || "supabase/migrations");
216555
217329
  const migrationResponse = await pushMigrations({
216556
217330
  action: "push_migrations",
216557
217331
  ref: projectRef,
@@ -216664,11 +217438,11 @@ function registerSupabaseCliTools(server2, options = {}) {
216664
217438
 
216665
217439
  // src/shared/tools/lite-cli-tools.ts
216666
217440
  import { existsSync as existsSync9, statSync as statSync7 } from "node:fs";
216667
- import { resolve as resolve10 } from "node:path";
217441
+ import { resolve as resolve11 } from "node:path";
216668
217442
 
216669
217443
  // src/shared/tools/lite-cli-command.ts
216670
217444
  import { existsSync as existsSync8, readFileSync as readFileSync7, statSync as statSync6 } from "node:fs";
216671
- import { isAbsolute as isAbsolute3, join as join7, relative as relative3, resolve as resolve9 } from "node:path";
217445
+ import { isAbsolute as isAbsolute3, join as join8, relative as relative3, resolve as resolve10 } from "node:path";
216672
217446
  function isRecord3(value) {
216673
217447
  return value !== null && typeof value === "object" && !Array.isArray(value);
216674
217448
  }
@@ -216679,8 +217453,8 @@ function resolveLiteCommand(workdir, environment = process.env) {
216679
217453
  throw new Error("Invalid SUPACLOUD_LITE_CLI_BIN");
216680
217454
  return [explicitBinary];
216681
217455
  }
216682
- const packageRoot = join7(resolve9(workdir), "node_modules", "@supacloud", "lite");
216683
- const manifestPath = join7(packageRoot, "package.json");
217456
+ const packageRoot = join8(resolve10(workdir), "node_modules", "@supacloud", "lite");
217457
+ const manifestPath = join8(packageRoot, "package.json");
216684
217458
  if (!existsSync8(manifestPath))
216685
217459
  return ["supacloud-lite"];
216686
217460
  const manifest = JSON.parse(readFileSync7(manifestPath, "utf8"));
@@ -216691,7 +217465,7 @@ function resolveLiteCommand(workdir, environment = process.env) {
216691
217465
  if (typeof entry !== "string" || !entry.trim() || entry.includes("\x00") || isAbsolute3(entry)) {
216692
217466
  throw new Error("Invalid installed @supacloud/lite bin");
216693
217467
  }
216694
- const executable = resolve9(packageRoot, entry);
217468
+ const executable = resolve10(packageRoot, entry);
216695
217469
  const packageRelative = relative3(packageRoot, executable);
216696
217470
  if (!packageRelative || packageRelative === ".." || packageRelative.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute3(packageRelative)) {
216697
217471
  throw new Error("Installed @supacloud/lite bin must stay inside its package");
@@ -216703,7 +217477,7 @@ function resolveLiteCommand(workdir, environment = process.env) {
216703
217477
  }
216704
217478
  // src/shared/tools/lite-cli-tools.ts
216705
217479
  function requireWorkdir(workdir, fallback) {
216706
- const resolved = resolve10(workdir || fallback);
217480
+ const resolved = resolve11(workdir || fallback);
216707
217481
  if (!existsSync9(resolved) || !statSync7(resolved).isDirectory()) {
216708
217482
  throw new Error(`Lite workdir not found: ${resolved}`);
216709
217483
  }
@@ -216917,13 +217691,13 @@ import {
216917
217691
  rmSync as rmSync2
216918
217692
  } from "node:fs";
216919
217693
  import { homedir as homedir2 } from "node:os";
216920
- import { dirname as dirname4, join as join8, relative as relative4, resolve as resolve11, sep as sep3 } from "node:path";
217694
+ import { dirname as dirname4, join as join9, relative as relative4, resolve as resolve12, sep as sep3 } from "node:path";
216921
217695
  import { fileURLToPath } from "node:url";
216922
217696
  var SKILL_NAME = "supacloud-cli";
216923
217697
  function regularFiles(rootDirectory, currentDirectory = rootDirectory) {
216924
217698
  const files = [];
216925
217699
  for (const directoryEntry of readdirSync4(currentDirectory, { withFileTypes: true })) {
216926
- const entryPath = join8(currentDirectory, directoryEntry.name);
217700
+ const entryPath = join9(currentDirectory, directoryEntry.name);
216927
217701
  if (directoryEntry.isSymbolicLink())
216928
217702
  throw new Error(`Skill directories cannot contain symlinks: ${entryPath}`);
216929
217703
  if (directoryEntry.isDirectory())
@@ -216940,7 +217714,7 @@ function directoriesMatch(sourceDirectory, destinationDirectory) {
216940
217714
  const destinationFiles = regularFiles(destinationDirectory);
216941
217715
  if (sourceFiles.join("\x00") !== destinationFiles.join("\x00"))
216942
217716
  return false;
216943
- return sourceFiles.every((file) => readFileSync8(join8(sourceDirectory, file)).equals(readFileSync8(join8(destinationDirectory, file))));
217717
+ return sourceFiles.every((file) => readFileSync8(join9(sourceDirectory, file)).equals(readFileSync8(join9(destinationDirectory, file))));
216944
217718
  }
216945
217719
  function backupTimestamp(now) {
216946
217720
  return now.toISOString().replace(/[-:.]/g, "");
@@ -216957,8 +217731,8 @@ function availableBackupDirectory(destinationDirectory, now) {
216957
217731
  }
216958
217732
  function stagedSkill(sourceDirectory, targetRoot) {
216959
217733
  mkdirSync3(targetRoot, { recursive: true });
216960
- const stagingRoot = mkdtempSync2(join8(targetRoot, ".supacloud-cli-install-"));
216961
- const stagingSkill = join8(stagingRoot, SKILL_NAME);
217734
+ const stagingRoot = mkdtempSync2(join9(targetRoot, ".supacloud-cli-install-"));
217735
+ const stagingSkill = join9(stagingRoot, SKILL_NAME);
216962
217736
  try {
216963
217737
  cpSync(sourceDirectory, stagingSkill, { recursive: true, errorOnExist: true });
216964
217738
  } catch (error) {
@@ -216990,13 +217764,13 @@ function replaceSkill(sourceDirectory, targetRoot, destinationDirectory, backupD
216990
217764
  }
216991
217765
  }
216992
217766
  function skillSummary(request, action, files, backupDirectory) {
216993
- const sourceDirectory = resolve11(request.sourceDirectory);
216994
- const targetRoot = resolve11(request.targetRoot);
217767
+ const sourceDirectory = resolve12(request.sourceDirectory);
217768
+ const targetRoot = resolve12(request.targetRoot);
216995
217769
  return {
216996
217770
  name: SKILL_NAME,
216997
217771
  sourceDirectory,
216998
217772
  targetRoot,
216999
- destinationDirectory: join8(targetRoot, SKILL_NAME),
217773
+ destinationDirectory: join9(targetRoot, SKILL_NAME),
217000
217774
  action,
217001
217775
  mode: request.mode,
217002
217776
  changed: action !== "none",
@@ -217005,10 +217779,10 @@ function skillSummary(request, action, files, backupDirectory) {
217005
217779
  };
217006
217780
  }
217007
217781
  function installSkill(request) {
217008
- const sourceDirectory = resolve11(request.sourceDirectory);
217009
- const targetRoot = resolve11(request.targetRoot);
217010
- const destinationDirectory = join8(targetRoot, SKILL_NAME);
217011
- if (!existsSync10(join8(sourceDirectory, "SKILL.md"))) {
217782
+ const sourceDirectory = resolve12(request.sourceDirectory);
217783
+ const targetRoot = resolve12(request.targetRoot);
217784
+ const destinationDirectory = join9(targetRoot, SKILL_NAME);
217785
+ if (!existsSync10(join9(sourceDirectory, "SKILL.md"))) {
217012
217786
  throw new Error(`Bundled SupaCloud CLI skill not found: ${sourceDirectory}`);
217013
217787
  }
217014
217788
  const files = regularFiles(sourceDirectory);
@@ -217023,17 +217797,17 @@ function installSkill(request) {
217023
217797
  return installReplacementSkill(request, files);
217024
217798
  }
217025
217799
  function installNewSkill(request, files) {
217026
- const sourceDirectory = resolve11(request.sourceDirectory);
217027
- const targetRoot = resolve11(request.targetRoot);
217800
+ const sourceDirectory = resolve12(request.sourceDirectory);
217801
+ const targetRoot = resolve12(request.targetRoot);
217028
217802
  if (request.mode === "write") {
217029
- createSkill(sourceDirectory, targetRoot, join8(targetRoot, SKILL_NAME));
217803
+ createSkill(sourceDirectory, targetRoot, join9(targetRoot, SKILL_NAME));
217030
217804
  }
217031
217805
  return skillSummary(request, "create", files, null);
217032
217806
  }
217033
217807
  function installReplacementSkill(request, files) {
217034
- const sourceDirectory = resolve11(request.sourceDirectory);
217035
- const targetRoot = resolve11(request.targetRoot);
217036
- const destinationDirectory = join8(targetRoot, SKILL_NAME);
217808
+ const sourceDirectory = resolve12(request.sourceDirectory);
217809
+ const targetRoot = resolve12(request.targetRoot);
217810
+ const destinationDirectory = join9(targetRoot, SKILL_NAME);
217037
217811
  const backupDirectory = availableBackupDirectory(destinationDirectory, request.now);
217038
217812
  if (request.mode === "write") {
217039
217813
  replaceSkill(sourceDirectory, targetRoot, destinationDirectory, backupDirectory);
@@ -217042,15 +217816,15 @@ function installReplacementSkill(request, files) {
217042
217816
  }
217043
217817
  function resolveDefaultCodexSkillRoot(environment = process.env, homeDirectory = homedir2()) {
217044
217818
  const codexHome = environment.CODEX_HOME?.trim();
217045
- return join8(resolve11(codexHome || join8(homeDirectory, ".codex")), "skills");
217819
+ return join9(resolve12(codexHome || join9(homeDirectory, ".codex")), "skills");
217046
217820
  }
217047
217821
  function resolveBundledSkillDirectory(moduleUrl = import.meta.url) {
217048
217822
  const moduleDirectory = dirname4(fileURLToPath(moduleUrl));
217049
217823
  const candidates = [
217050
- resolve11(moduleDirectory, "../../../skills", SKILL_NAME),
217051
- resolve11(moduleDirectory, "../skills", SKILL_NAME)
217824
+ resolve12(moduleDirectory, "../../../skills", SKILL_NAME),
217825
+ resolve12(moduleDirectory, "../skills", SKILL_NAME)
217052
217826
  ];
217053
- const skillDirectory = candidates.find((candidate) => existsSync10(join8(candidate, "SKILL.md")));
217827
+ const skillDirectory = candidates.find((candidate) => existsSync10(join9(candidate, "SKILL.md")));
217054
217828
  if (!skillDirectory)
217055
217829
  throw new Error("Bundled SupaCloud CLI skill is missing from this installation");
217056
217830
  return skillDirectory;
@@ -217072,7 +217846,7 @@ function registerAiTools(server2) {
217072
217846
  name: SKILL_NAME,
217073
217847
  sourceDirectory,
217074
217848
  defaultTargetRoot,
217075
- defaultDestination: join8(defaultTargetRoot, SKILL_NAME)
217849
+ defaultDestination: join9(defaultTargetRoot, SKILL_NAME)
217076
217850
  });
217077
217851
  }
217078
217852
  return textResponse(installSkill({
@@ -217087,13 +217861,13 @@ function registerAiTools(server2) {
217087
217861
 
217088
217862
  // src/shared/tools/app-tools.ts
217089
217863
  import { existsSync as existsSync13, readFileSync as readFileSync10 } from "node:fs";
217090
- import { mkdir as mkdir6, readFile as readFile9, readdir as readdir6, writeFile as writeFile5 } from "node:fs/promises";
217091
- import { basename as basename9, dirname as dirname13, join as join16, relative as relative13, resolve as resolve24 } from "node:path";
217864
+ import { mkdir as mkdir7, readFile as readFile9, readdir as readdir7, writeFile as writeFile6 } from "node:fs/promises";
217865
+ import { basename as basename10, dirname as dirname13, join as join17, relative as relative13, resolve as resolve24 } from "node:path";
217092
217866
 
217093
217867
  // node_modules/@supacloud/compiler/dist/index.js
217094
217868
  import { dirname as dirname22, relative as relative6, sep as sep4 } from "node:path";
217095
217869
  import { createHash as createHash42 } from "node:crypto";
217096
- import { access, mkdir as mkdir3, rename as rename2, unlink, writeFile as writeFile3 } from "node:fs/promises";
217870
+ import { access, mkdir as mkdir4, rename as rename2, unlink, writeFile as writeFile4 } from "node:fs/promises";
217097
217871
  import { join as join22 } from "node:path";
217098
217872
  var ts10 = __toESM(require_typescript(), 1);
217099
217873
  var ts11 = __toESM(require_typescript(), 1);
@@ -217103,7 +217877,7 @@ import { resolve as resolve52 } from "node:path";
217103
217877
  import { resolve as resolve62 } from "node:path";
217104
217878
  import { createHash as createHash52 } from "node:crypto";
217105
217879
  import { createHash as createHash62 } from "node:crypto";
217106
- import { readFile as readFile2 } from "node:fs/promises";
217880
+ import { readFile as readFile3 } from "node:fs/promises";
217107
217881
  import { relative as relative42, sep as sep42 } from "node:path";
217108
217882
 
217109
217883
  // node_modules/@graphql-codegen/core/esm/codegen.js
@@ -219494,7 +220268,7 @@ var findTransformedDefinitions = (visitedResult) => {
219494
220268
  // node_modules/@supacloud/compiler/dist/index.js
219495
220269
  import { createHash as createHash8 } from "node:crypto";
219496
220270
  import { constants as constants2 } from "node:fs";
219497
- import { lstat as lstat2, mkdir as mkdir22, open as open3, readFile as readFile3, readdir as readdir3, realpath as realpath3, rename as rename22, rm as rm2, writeFile as writeFile22 } from "node:fs/promises";
220271
+ import { lstat as lstat2, mkdir as mkdir22, open as open3, readFile as readFile32, readdir as readdir4, realpath as realpath3, rename as rename22, rm as rm2, writeFile as writeFile22 } from "node:fs/promises";
219498
220272
  import { dirname as dirname42, isAbsolute as isAbsolute5, join as join52, relative as relative52, resolve as resolve82, sep as sep5 } from "node:path";
219499
220273
  import { existsSync as existsSync32 } from "node:fs";
219500
220274
  import { dirname as dirname52, join as join62, relative as relative62, sep as sep6 } from "node:path";
@@ -219513,9 +220287,9 @@ var ts3 = __toESM(require_typescript(), 1);
219513
220287
  var ts5 = __toESM(require_typescript(), 1);
219514
220288
  import { createHash as createHash32 } from "node:crypto";
219515
220289
  import { relative as relative22, resolve as resolvePath, sep as sep22 } from "node:path";
219516
- import { createHash as createHash22 } from "node:crypto";
220290
+ import { createHash as createHash23 } from "node:crypto";
219517
220291
  import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
219518
- import { dirname as dirname6, join as join11, resolve as resolve13 } from "node:path";
220292
+ import { dirname as dirname6, join as join12, resolve as resolve15 } from "node:path";
219519
220293
  import { createHash as createHash7 } from "node:crypto";
219520
220294
  import { resolve as resolve22 } from "node:path";
219521
220295
  var ts9 = __toESM(require_typescript(), 1);
@@ -219538,7 +220312,7 @@ import { existsSync as existsSync42, watch } from "node:fs";
219538
220312
  import { dirname as dirname9, relative as relative10, resolve as resolve16, sep as sep10 } from "node:path";
219539
220313
  import { createHash as createHash10 } from "node:crypto";
219540
220314
  import { access as access3, readdir as readdir32, readFile as readFile7 } from "node:fs/promises";
219541
- import { isAbsolute as isAbsolute42, relative as relative9, resolve as resolve15, sep as sep9 } from "node:path";
220315
+ import { isAbsolute as isAbsolute42, relative as relative9, resolve as resolve152, sep as sep9 } from "node:path";
219542
220316
  var ts16 = __toESM(require_typescript(), 1);
219543
220317
  import { existsSync as existsSync52 } from "node:fs";
219544
220318
  import { join as join112, resolve as resolve21 } from "node:path";
@@ -220993,7 +221767,7 @@ function renderApplication(graph, options) {
220993
221767
  };
220994
221768
  }
220995
221769
  async function writeRenderedApplication(rendered, options) {
220996
- await mkdir3(options.outDir, { recursive: true });
221770
+ await mkdir4(options.outDir, { recursive: true });
220997
221771
  const applicationPath = join22(options.outDir, "application.ts");
220998
221772
  const manifestPath = join22(options.outDir, "app.manifest.json");
220999
221773
  const contractsManifestPath = join22(options.outDir, "contracts.manifest.json");
@@ -221035,7 +221809,7 @@ async function writeFileAtomic(path, content) {
221035
221809
  if (typeof Bun !== "undefined" && typeof Bun.write === "function") {
221036
221810
  await Bun.write(temporaryPath, content);
221037
221811
  } else {
221038
- await writeFile3(temporaryPath, content, "utf8");
221812
+ await writeFile4(temporaryPath, content, "utf8");
221039
221813
  }
221040
221814
  await rename2(temporaryPath, path);
221041
221815
  } catch (error) {
@@ -223767,7 +224541,7 @@ async function renderGraphql(options) {
223767
224541
  let schema;
223768
224542
  let schemaContent;
223769
224543
  try {
223770
- schemaContent = await readFile2(schemaPath, "utf8");
224544
+ schemaContent = await readFile3(schemaPath, "utf8");
223771
224545
  Object.assign(contract, graphqlSchemaHashes(schemaContent));
223772
224546
  if (schemaPath.endsWith(".json")) {
223773
224547
  const json = JSON.parse(schemaContent);
@@ -223786,7 +224560,7 @@ async function renderGraphql(options) {
223786
224560
  const documents = [];
223787
224561
  for (const path of paths.slice(1)) {
223788
224562
  try {
223789
- documents.push({ location: path, document: import_graphql64.parse(new import_graphql64.Source(await readFile2(path, "utf8"), path)) });
224563
+ documents.push({ location: path, document: import_graphql64.parse(new import_graphql64.Source(await readFile3(path, "utf8"), path)) });
223790
224564
  } catch (error) {
223791
224565
  diagnostic("graphql-document-invalid", error, path);
223792
224566
  }
@@ -232984,7 +233758,7 @@ async function readOwned(anchor, path) {
232984
233758
  const stat = await lstat2(path);
232985
233759
  if (!stat.isFile())
232986
233760
  throw new Error("Expected a regular delivery input or artifact.");
232987
- return readFile3(path);
233761
+ return readFile32(path);
232988
233762
  }
232989
233763
  async function readBoundedOwned2(root, path, maximum) {
232990
233764
  await noLinks2(root, path);
@@ -233021,7 +233795,7 @@ async function reserveOutput(project, requested) {
233021
233795
  const marker = join52(path, "owner.json");
233022
233796
  const owner = canonical2({ producer: "@supacloud/compiler/delivery-build-v1", project: base });
233023
233797
  if (!await exists2(marker)) {
233024
- if ((await readdir3(path)).length > 0)
233798
+ if ((await readdir4(path)).length > 0)
233025
233799
  throw new Error("Refusing to adopt an unowned delivery directory.");
233026
233800
  await writeFile22(marker, owner, { flag: "wx" });
233027
233801
  } else if (new TextDecoder().decode(await readOwned(base, marker)) !== owner) {
@@ -233049,7 +233823,7 @@ async function writeArtifact(root, path, content) {
233049
233823
  async function artifactInventory(root) {
233050
233824
  const files = [];
233051
233825
  async function visit(directory) {
233052
- for (const entry of await readdir3(directory, { withFileTypes: true })) {
233826
+ for (const entry of await readdir4(directory, { withFileTypes: true })) {
233053
233827
  const path = join52(directory, entry.name);
233054
233828
  if (entry.isSymbolicLink())
233055
233829
  throw new Error("Artifact contains a symbolic link.");
@@ -233067,7 +233841,7 @@ async function artifactInventory(root) {
233067
233841
  async function sourceInventory(root, output) {
233068
233842
  const inputs = new Map;
233069
233843
  async function visit(directory) {
233070
- for (const entry of await readdir3(directory, { withFileTypes: true })) {
233844
+ for (const entry of await readdir4(directory, { withFileTypes: true })) {
233071
233845
  const path = join52(directory, entry.name);
233072
233846
  if (inside2(output, path) || entry.name === "node_modules" || entry.name === ".git")
233073
233847
  continue;
@@ -242899,7 +243673,7 @@ var init_application_activation = __esm2(() => {
242899
243673
  }, { additionalProperties: false });
242900
243674
  });
242901
243675
  var closed6;
242902
- var sha2562;
243676
+ var sha2563;
242903
243677
  var isoDate2;
242904
243678
  var status2;
242905
243679
  var evidenceStatus2;
@@ -242914,7 +243688,7 @@ var init_deployment_evidence = __esm2(() => {
242914
243688
  init_application_configuration();
242915
243689
  init_application_release();
242916
243690
  closed6 = { additionalProperties: false };
242917
- sha2562 = String22({ pattern: "^[a-f0-9]{64}$" });
243691
+ sha2563 = String22({ pattern: "^[a-f0-9]{64}$" });
242918
243692
  isoDate2 = String22({ minLength: 20, maxLength: 40 });
242919
243693
  status2 = Union2([
242920
243694
  Literal2("confirmed"),
@@ -242933,7 +243707,7 @@ var init_deployment_evidence = __esm2(() => {
242933
243707
  topology: Union2([Literal2("single-node"), Literal2("distributed")]),
242934
243708
  migration: _Object_2({
242935
243709
  status: status2,
242936
- inventory_sha256: Union2([sha2562, Null2()]),
243710
+ inventory_sha256: Union2([sha2563, Null2()]),
242937
243711
  compatibility: Union2([
242938
243712
  Literal2("verified"),
242939
243713
  Literal2("not-proven"),
@@ -242981,9 +243755,9 @@ var init_deployment_evidence = __esm2(() => {
242981
243755
  }, closed6),
242982
243756
  source: _Object_2({
242983
243757
  commit_sha: Union2([String22({ pattern: "^[0-9a-f]{7,64}$" }), Null2()]),
242984
- manifest_sha256: sha2562,
243758
+ manifest_sha256: sha2563,
242985
243759
  contract_schema: Union2([String22({ minLength: 1, maxLength: 128 }), Null2()]),
242986
- environment_binding_version: Union2([sha2562, Null2()])
243760
+ environment_binding_version: Union2([sha2563, Null2()])
242987
243761
  }, closed6),
242988
243762
  database: DatabaseProviderSchema2,
242989
243763
  components: _Array_2(DeploymentComponentEvidenceSchema2, { minItems: 1, maxItems: 32 }),
@@ -243487,7 +244261,7 @@ function decoratedDeclaration(decorator) {
243487
244261
  };
243488
244262
  }
243489
244263
  function createIncrementalProgramSession(projectRoot) {
243490
- const rootDir = resolve13(projectRoot);
244264
+ const rootDir = resolve15(projectRoot);
243491
244265
  let projectConfig = readProjectConfig(rootDir);
243492
244266
  let projectConfigKey = configKey(projectConfig);
243493
244267
  let builder;
@@ -243516,8 +244290,8 @@ function createIncrementalProgramSession(projectRoot) {
243516
244290
  }
243517
244291
  projectConfig = nextProjectConfig;
243518
244292
  projectConfigKey = nextProjectConfigKey;
243519
- const normalizedRoots = [...new Set(rootNames.map((file) => resolve13(rootDir, file)))].sort();
243520
- const normalizedChanged = [...new Set(changedPaths.map((file) => resolve13(rootDir, file)))];
244293
+ const normalizedRoots = [...new Set(rootNames.map((file) => resolve15(rootDir, file)))].sort();
244294
+ const normalizedChanged = [...new Set(changedPaths.map((file) => resolve15(rootDir, file)))];
243521
244295
  const invalidatedPaths = new Set(normalizedChanged.map(canonical3));
243522
244296
  for (const sourceFile of oldSourceFiles.values()) {
243523
244297
  if (sourceVersion(sourceFile.fileName) !== sourceFileVersion(sourceFile)) {
@@ -243618,7 +244392,7 @@ function createHost(options, rootDir, sourceFileCache, invalidatedPaths, invalid
243618
244392
  return host;
243619
244393
  }
243620
244394
  function canonical3(fileName) {
243621
- const normalized = resolve13(fileName);
244395
+ const normalized = resolve15(fileName);
243622
244396
  return ts2.sys.useCaseSensitiveFileNames ? normalized : normalized.toLowerCase();
243623
244397
  }
243624
244398
  function sourceFileParseKey(languageVersion) {
@@ -243636,7 +244410,7 @@ function configKey(config) {
243636
244410
  });
243637
244411
  }
243638
244412
  function hashText(text) {
243639
- return createHash22("sha1").update(text).digest("hex");
244413
+ return createHash23("sha1").update(text).digest("hex");
243640
244414
  }
243641
244415
  function sourceVersion(fileName) {
243642
244416
  try {
@@ -243650,7 +244424,7 @@ function sourceFileVersion(sourceFile) {
243650
244424
  return typeof descriptor?.value === "string" ? descriptor.value : undefined;
243651
244425
  }
243652
244426
  function readProjectConfig(rootDir) {
243653
- const configPath = join11(rootDir, "tsconfig.json");
244427
+ const configPath = join12(rootDir, "tsconfig.json");
243654
244428
  if (!existsSync11(configPath)) {
243655
244429
  return {
243656
244430
  options: {
@@ -243960,7 +244734,7 @@ function withDiagnosticMetadata(diagnostic) {
243960
244734
  function nodeText(node) {
243961
244735
  return node.getText(node.getSourceFile());
243962
244736
  }
243963
- function lineOf(node) {
244737
+ function lineOf2(node) {
243964
244738
  const sourceFile = node.getSourceFile();
243965
244739
  return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
243966
244740
  }
@@ -244054,7 +244828,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
244054
244828
  options,
244055
244829
  className: cls.name?.text ?? "<anonymous>",
244056
244830
  file: sf.fileName,
244057
- line: lineOf(cls)
244831
+ line: lineOf2(cls)
244058
244832
  });
244059
244833
  }
244060
244834
  }
@@ -244073,7 +244847,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
244073
244847
  options: arg,
244074
244848
  className: variableName3(parent),
244075
244849
  file: sf.fileName,
244076
- line: lineOf(parent)
244850
+ line: lineOf2(parent)
244077
244851
  });
244078
244852
  }
244079
244853
  }
@@ -244184,7 +244958,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
244184
244958
  if (injectable?.providedIn === "root") {
244185
244959
  const { deps, optionalDeps, selfDeps, skipSelfDeps, hostDeps, functionalInjects, missing } = classDeps(classInfo.decl, ctx);
244186
244960
  const file = sourcePath(ctx.rootDir, classInfo.file);
244187
- const line = lineOf(classInfo.decl);
244961
+ const line = lineOf2(classInfo.decl);
244188
244962
  if (missing) {
244189
244963
  warn(ctx, "missing-deps", `root provider ${name} 的部分构造依赖无法静态解析`, file, line);
244190
244964
  }
@@ -244231,8 +245005,8 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
244231
245005
  const rpc = checkedRpc(meta, ctx);
244232
245006
  const audit = stringLiteralProp(meta, "audit");
244233
245007
  const standaloneName = stringLiteralProp(meta, "name") ?? classInfo.decl.name?.text ?? name;
244234
- const standaloneUses = parseInfraResourceUseRefs(infraResourceProp(meta, "uses", ctx), ctx, `command ${standaloneName}`, sourcePath(ctx.rootDir, classInfo.file), lineOf(classInfo.decl));
244235
- rejectUndeclaredResourceUses(standaloneUses, standaloneResourceNames, ctx, `command ${standaloneName}`, sourcePath(ctx.rootDir, classInfo.file), lineOf(classInfo.decl));
245008
+ const standaloneUses = parseInfraResourceUseRefs(infraResourceProp(meta, "uses", ctx), ctx, `command ${standaloneName}`, sourcePath(ctx.rootDir, classInfo.file), lineOf2(classInfo.decl));
245009
+ rejectUndeclaredResourceUses(standaloneUses, standaloneResourceNames, ctx, `command ${standaloneName}`, sourcePath(ctx.rootDir, classInfo.file), lineOf2(classInfo.decl));
244236
245010
  standaloneCommands.push({
244237
245011
  className: classInfo.decl.name?.text ?? name,
244238
245012
  name: standaloneName,
@@ -244432,7 +245206,7 @@ function parseTokenVariable(decl, file) {
244432
245206
  if (nodeText(init.expression) !== "InjectionToken")
244433
245207
  return;
244434
245208
  const [nameArg, optionsArg] = init.arguments ?? [];
244435
- const info = { name: variableName3(decl), file, line: lineOf(decl) };
245209
+ const info = { name: variableName3(decl), file, line: lineOf2(decl) };
244436
245210
  if (nameArg && ts7.isStringLiteral(nameArg)) {
244437
245211
  info.stringName = nameArg.text;
244438
245212
  }
@@ -244488,7 +245262,7 @@ function parseModule(candidate, nameByNode, ctx) {
244488
245262
  }
244489
245263
  if (ts7.isCallExpression(el)) {
244490
245264
  const helper = nodeText(el.expression).split(".").pop() ?? nodeText(el.expression);
244491
- warn(ctx, "unsupported-provider-helper", `无法静态展开 provider helper '${helper}';请改用显式 Provider 或实现编译器支持的 helper`, sourcePath(ctx.rootDir, el.getSourceFile().fileName), lineOf(el));
245265
+ warn(ctx, "unsupported-provider-helper", `无法静态展开 provider helper '${helper}';请改用显式 Provider 或实现编译器支持的 helper`, sourcePath(ctx.rootDir, el.getSourceFile().fileName), lineOf2(el));
244492
245266
  continue;
244493
245267
  }
244494
245268
  const provider = parseProvider(el, exportsSet, ctx);
@@ -244524,7 +245298,7 @@ function parseModule(candidate, nameByNode, ctx) {
244524
245298
  ...hasInitHook(decl) ? { hasOnInit: true } : {},
244525
245299
  exported: exportsSet.has(className),
244526
245300
  file: sourcePath(ctx.rootDir, decl.getSourceFile().fileName),
244527
- line: lineOf(decl),
245301
+ line: lineOf2(decl),
244528
245302
  importPath: modulePath(ctx.rootDir, decl.getSourceFile().fileName)
244529
245303
  });
244530
245304
  }
@@ -244571,8 +245345,8 @@ function parseModule(candidate, nameByNode, ctx) {
244571
245345
  const rpc = checkedRpc(meta, ctx);
244572
245346
  const audit = stringLiteralProp(meta, "audit");
244573
245347
  const commandName = stringLiteralProp(meta, "name") ?? cls.name?.text ?? "<anonymous>";
244574
- const uses = parseInfraResourceUseRefs(infraResourceProp(meta, "uses", ctx), ctx, `command ${commandName}`, sourcePath(ctx.rootDir, cls.getSourceFile().fileName), lineOf(cls));
244575
- rejectUndeclaredResourceUses(uses, moduleResourceNames, ctx, `command ${commandName}`, sourcePath(ctx.rootDir, cls.getSourceFile().fileName), lineOf(cls));
245348
+ const uses = parseInfraResourceUseRefs(infraResourceProp(meta, "uses", ctx), ctx, `command ${commandName}`, sourcePath(ctx.rootDir, cls.getSourceFile().fileName), lineOf2(cls));
245349
+ rejectUndeclaredResourceUses(uses, moduleResourceNames, ctx, `command ${commandName}`, sourcePath(ctx.rootDir, cls.getSourceFile().fileName), lineOf2(cls));
244576
245350
  commands.push({
244577
245351
  className: cls.name?.text ?? "<anonymous>",
244578
245352
  name: commandName,
@@ -244601,7 +245375,7 @@ function parseModule(candidate, nameByNode, ctx) {
244601
245375
  code: "invalid-job-scope",
244602
245376
  message: `job ${className} 不能使用 request scope;Job 只能使用 application 或 job scope`,
244603
245377
  file: sourcePath(ctx.rootDir, cls.getSourceFile().fileName),
244604
- line: lineOf(cls),
245378
+ line: lineOf2(cls),
244605
245379
  suggestion: "移除 request scope,或改用 application/job scope。",
244606
245380
  errorCode: "SC4007",
244607
245381
  docsUrl: "https://supacloud.dev/errors/SC4007"
@@ -244609,8 +245383,8 @@ function parseModule(candidate, nameByNode, ctx) {
244609
245383
  }
244610
245384
  const aspects = parseAspectRefs(getProp(meta, "aspects"), ctx, `job ${className}`);
244611
245385
  const contract = parseJobOptions(meta, className, ctx);
244612
- const jobUses = parseInfraResourceUseRefs(infraResourceProp(meta, "uses", ctx), ctx, `job ${stringLiteralProp(meta, "name") ?? className}`, sourcePath(ctx.rootDir, cls.getSourceFile().fileName), lineOf(cls));
244613
- rejectUndeclaredResourceUses(jobUses, moduleResourceNames, ctx, `job ${stringLiteralProp(meta, "name") ?? className}`, sourcePath(ctx.rootDir, cls.getSourceFile().fileName), lineOf(cls));
245386
+ const jobUses = parseInfraResourceUseRefs(infraResourceProp(meta, "uses", ctx), ctx, `job ${stringLiteralProp(meta, "name") ?? className}`, sourcePath(ctx.rootDir, cls.getSourceFile().fileName), lineOf2(cls));
245387
+ rejectUndeclaredResourceUses(jobUses, moduleResourceNames, ctx, `job ${stringLiteralProp(meta, "name") ?? className}`, sourcePath(ctx.rootDir, cls.getSourceFile().fileName), lineOf2(cls));
244614
245388
  jobs.push({
244615
245389
  className,
244616
245390
  name: stringLiteralProp(meta, "name") ?? className,
@@ -244676,7 +245450,7 @@ function parseFeatureSpec(input, ctx, seen = new Set) {
244676
245450
  code: "invalid-feature-spec",
244677
245451
  message: "Feature spec must use static name, states and transition objects.",
244678
245452
  file: sourcePath(ctx.rootDir, input.getSourceFile().fileName),
244679
- line: lineOf(input)
245453
+ line: lineOf2(input)
244680
245454
  });
244681
245455
  return;
244682
245456
  };
@@ -244722,7 +245496,7 @@ function parseFeatureSpec(input, ctx, seen = new Set) {
244722
245496
  ...audit === undefined ? {} : { audit }
244723
245497
  });
244724
245498
  }
244725
- return { name, states, transitions, file: sourcePath(ctx.rootDir, input.getSourceFile().fileName), line: lineOf(input) };
245499
+ return { name, states, transitions, file: sourcePath(ctx.rootDir, input.getSourceFile().fileName), line: lineOf2(input) };
244726
245500
  }
244727
245501
  function resolveStaticObjectLiteral(input, ctx, seen = new Set) {
244728
245502
  if (!input || seen.has(input))
@@ -244779,7 +245553,7 @@ function checkedCommandMode(object, property, ctx, command) {
244779
245553
  docsUrl: "https://supacloud.dev/errors/SC4012",
244780
245554
  message: `${command}.${property} must be the explicit literal "required" or "none"; invalid governance cannot be disabled silently.`,
244781
245555
  file,
244782
- line: lineOf(expression),
245556
+ line: lineOf2(expression),
244783
245557
  suggestion: `Choose the intended ${property} policy explicitly; use set_command_mode with value "required" or "none".`,
244784
245558
  fix: {
244785
245559
  type: "set_command_mode",
@@ -244794,7 +245568,7 @@ function checkedCommandMode(object, property, ctx, command) {
244794
245568
  }
244795
245569
  function parseProvider(el, exportsSet, ctx) {
244796
245570
  const file = sourcePath(ctx.rootDir, el.getSourceFile().fileName);
244797
- const line = lineOf(el);
245571
+ const line = lineOf2(el);
244798
245572
  const unwrappedEl = unwrapForwardRef(el);
244799
245573
  if (ts7.isIdentifier(unwrappedEl)) {
244800
245574
  const decl = resolveDeclaration(unwrappedEl, ctx)[0];
@@ -244998,7 +245772,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
244998
245772
  const helper = nodeText(expression.expression).split(".").pop();
244999
245773
  const args = expression.arguments;
245000
245774
  const file = sourcePath(ctx.rootDir, expression.getSourceFile().fileName);
245001
- const line = lineOf(expression);
245775
+ const line = lineOf2(expression);
245002
245776
  if (helper === "provideToken") {
245003
245777
  const tokenExpr = args[0];
245004
245778
  const valueExpr = args[1];
@@ -245238,7 +246012,7 @@ function parseController(input, ctx) {
245238
246012
  const { deps, optionalDeps, selfDeps, skipSelfDeps, hostDeps, functionalInjects, missing } = classDeps(decl, ctx);
245239
246013
  const file = sourcePath(ctx.rootDir, decl.getSourceFile().fileName);
245240
246014
  if (missing) {
245241
- warn(ctx, "missing-deps", `controller ${decl.name?.text} 的部分构造依赖无法静态解析`, file, lineOf(decl));
246015
+ warn(ctx, "missing-deps", `controller ${decl.name?.text} 的部分构造依赖无法静态解析`, file, lineOf2(decl));
245242
246016
  }
245243
246017
  const injectable = parseInjectableOptions(decl, ctx);
245244
246018
  const routes = [];
@@ -245472,7 +246246,7 @@ function parseController(input, ctx) {
245472
246246
  errorCode: "SC3026",
245473
246247
  docsUrl: "https://supacloud.dev/errors/SC3026",
245474
246248
  file,
245475
- line: lineOf(optionsObject),
246249
+ line: lineOf2(optionsObject),
245476
246250
  message: `Route ${route.handler} declares both response and responses; choose the status-map responses field.`,
245477
246251
  suggestion: "Remove response and represent the successful response as responses: { 200: Schema }."
245478
246252
  });
@@ -245521,7 +246295,7 @@ function parseController(input, ctx) {
245521
246295
  errorCode: "SC3025",
245522
246296
  docsUrl: "https://supacloud.dev/errors/SC3025",
245523
246297
  file,
245524
- line: lineOf(property.name),
246298
+ line: lineOf2(property.name),
245525
246299
  message: `Route ${route.handler} uses unsupported response selector "${status}". Use an HTTP status (100-599), a status family such as 4XX/5XX, or default.`
245526
246300
  });
245527
246301
  continue;
@@ -245535,7 +246309,7 @@ function parseController(input, ctx) {
245535
246309
  errorCode: "SC3027",
245536
246310
  docsUrl: "https://supacloud.dev/errors/SC3027",
245537
246311
  file,
245538
- line: lineOf(property.name),
246312
+ line: lineOf2(property.name),
245539
246313
  message: `Route ${route.handler} declares duplicate response selectors "${existingSelector}" and "${status}".`,
245540
246314
  suggestion: `Keep one ${canonical} selector so runtime, clients and OpenAPI choose the same schema.`
245541
246315
  });
@@ -245725,7 +246499,7 @@ function jobOptionError(ctx, code, message, node, errorCode, suggestion) {
245725
246499
  code,
245726
246500
  message,
245727
246501
  file: sourcePath(ctx.rootDir, node.getSourceFile().fileName),
245728
- line: lineOf(node),
246502
+ line: lineOf2(node),
245729
246503
  ...suggestion === undefined ? {} : { suggestion },
245730
246504
  errorCode,
245731
246505
  docsUrl: `https://supacloud.dev/errors/${errorCode}`
@@ -245774,7 +246548,7 @@ function checkedRpc(meta, ctx) {
245774
246548
  severity: "error",
245775
246549
  code: "invalid-command-rpc",
245776
246550
  file: sourcePath(ctx.rootDir, meta.getSourceFile().fileName),
245777
- line: lineOf(meta),
246551
+ line: lineOf2(meta),
245778
246552
  message: "Command rpc must be a non-empty string literal identifying a configured adapter.",
245779
246553
  suggestion: "Declare a named RPC adapter and its tested persistence capabilities."
245780
246554
  });
@@ -246090,7 +246864,7 @@ function parseAspectRefs(expression, ctx, owner) {
246090
246864
  code: "dynamic-aspect-reference",
246091
246865
  message: `${owner} 的 aspects 必须是显式数组字面量,并且每一项必须是可解析的函数引用`,
246092
246866
  file: sourcePath(ctx.rootDir, expression.getSourceFile().fileName),
246093
- line: lineOf(expression),
246867
+ line: lineOf2(expression),
246094
246868
  suggestion: "使用 aspects: [auditAspect, transactionAspect],不要使用变量、调用表达式或字符串 pointcut。",
246095
246869
  errorCode: "SC4010",
246096
246870
  docsUrl: "https://supacloud.dev/errors/SC4010"
@@ -246105,7 +246879,7 @@ function parseAspectRefs(expression, ctx, owner) {
246105
246879
  code: "dynamic-aspect-reference",
246106
246880
  message: `${owner} 的 aspects 只能包含显式的函数标识符引用,无法静态编译 '${nodeText(element)}'`,
246107
246881
  file: sourcePath(ctx.rootDir, element.getSourceFile().fileName),
246108
- line: lineOf(element),
246882
+ line: lineOf2(element),
246109
246883
  suggestion: "将 aspect 直接写入数组,例如 aspects: [auditAspect]。",
246110
246884
  errorCode: "SC4010",
246111
246885
  docsUrl: "https://supacloud.dev/errors/SC4010"
@@ -246119,7 +246893,7 @@ function parseAspectRefs(expression, ctx, owner) {
246119
246893
  code: "invalid-aspect-reference",
246120
246894
  message: `${owner} 引用了 '${element.text}',但它不是可静态解析的 aspect 函数`,
246121
246895
  file: sourcePath(ctx.rootDir, element.getSourceFile().fileName),
246122
- line: lineOf(element),
246896
+ line: lineOf2(element),
246123
246897
  suggestion: "aspect 必须是函数声明、箭头函数或函数表达式的直接引用。",
246124
246898
  errorCode: "SC4011",
246125
246899
  docsUrl: "https://supacloud.dev/errors/SC4011"
@@ -246244,7 +247018,7 @@ function infraResourceProp(obj, name, ctx) {
246244
247018
  return;
246245
247019
  const member = members[0];
246246
247020
  if (members.length !== 1 || !ts7.isPropertyAssignment(member) || ts7.isComputedPropertyName(member.name) || obj.properties.some(ts7.isSpreadAssignment)) {
246247
- pushResourceError(ctx, "unknown-resource", `${name} 必须是唯一、显式的静态属性,且不能被对象展开覆盖`, sourcePath(ctx.rootDir, obj.getSourceFile().fileName), lineOf(member), `使用 ${name}: [...],不要使用简写属性、计算属性或对象展开。`);
247021
+ pushResourceError(ctx, "unknown-resource", `${name} 必须是唯一、显式的静态属性,且不能被对象展开覆盖`, sourcePath(ctx.rootDir, obj.getSourceFile().fileName), lineOf2(member), `使用 ${name}: [...],不要使用简写属性、计算属性或对象展开。`);
246248
247022
  return;
246249
247023
  }
246250
247024
  return member.initializer;
@@ -246254,7 +247028,7 @@ function indexInfraResource(cls, className, sf, ctx) {
246254
247028
  if (!decorator)
246255
247029
  return;
246256
247030
  const file = sourcePath(ctx.rootDir, sf.fileName);
246257
- const line = lineOf(cls);
247031
+ const line = lineOf2(cls);
246258
247032
  const options = decoratorObjectArg(decorator);
246259
247033
  if (!options || options.properties.some((property) => !ts7.isPropertyAssignment(property) || ts7.isComputedPropertyName(property.name)) || new Set(options.properties.map((property) => property.name && propertyName(property.name))).size !== options.properties.length) {
246260
247034
  pushResourceError(ctx, "invalid-resource-kind", "@InfraResource 需要静态的 { name, kind } 对象参数", file, line, '使用 @InfraResource({ name: "orders-db", kind: "database" }),不要使用对象展开或重复属性。');
@@ -246312,7 +247086,7 @@ function parseModuleInfraResources(value, ctx, owner, file, line) {
246312
247086
  return names;
246313
247087
  }
246314
247088
  for (const element of array.elements) {
246315
- const resource = resolveInfraResourceRef(element, ctx, owner, file, lineOf(element));
247089
+ const resource = resolveInfraResourceRef(element, ctx, owner, file, lineOf2(element));
246316
247090
  if (resource)
246317
247091
  names.add(resource);
246318
247092
  }
@@ -246330,15 +247104,15 @@ function parseInfraResourceUseRefs(value, ctx, owner, file, line) {
246330
247104
  for (const element of array.elements) {
246331
247105
  const entry = unwrapInfraResourceExpression(element);
246332
247106
  if (!ts7.isObjectLiteralExpression(entry) || entry.properties.some((property) => !ts7.isPropertyAssignment(property) || ts7.isComputedPropertyName(property.name) || !["resource", "operations"].includes(propertyName(property.name))) || new Set(entry.properties.map((property) => property.name && propertyName(property.name))).size !== entry.properties.length) {
246333
- pushResourceError(ctx, "unknown-resource", `${owner} 的 uses 项必须是显式的 { resource, operations } 对象`, file, lineOf(element), "使用显式属性,不要使用对象/数组展开、简写、计算属性或重复属性。");
247107
+ pushResourceError(ctx, "unknown-resource", `${owner} 的 uses 项必须是显式的 { resource, operations } 对象`, file, lineOf2(element), "使用显式属性,不要使用对象/数组展开、简写、计算属性或重复属性。");
246334
247108
  continue;
246335
247109
  }
246336
247110
  const resourceEl = getProp(entry, "resource");
246337
247111
  if (!resourceEl) {
246338
- pushResourceError(ctx, "unknown-resource", `${owner} 的 uses 项缺少 resource`, file, lineOf(entry), "在该 uses 项中显式指定 resource: ResourceClass。");
247112
+ pushResourceError(ctx, "unknown-resource", `${owner} 的 uses 项缺少 resource`, file, lineOf2(entry), "在该 uses 项中显式指定 resource: ResourceClass。");
246339
247113
  continue;
246340
247114
  }
246341
- const resource = resolveInfraResourceRef(resourceEl, ctx, owner, file, lineOf(resourceEl));
247115
+ const resource = resolveInfraResourceRef(resourceEl, ctx, owner, file, lineOf2(resourceEl));
246342
247116
  if (!resource)
246343
247117
  continue;
246344
247118
  const operationsEl = getProp(entry, "operations");
@@ -246348,14 +247122,14 @@ function parseInfraResourceUseRefs(value, ctx, owner, file, line) {
246348
247122
  } else {
246349
247123
  const operationArray = unwrapInfraResourceExpression(operationsEl);
246350
247124
  if (!ts7.isArrayLiteralExpression(operationArray) || operationArray.elements.length === 0) {
246351
- pushResourceError(ctx, "invalid-resource-operation", `${owner} 对资源 '${resource}' 的 operations 必须是非空静态数组`, file, lineOf(operationsEl), "仅省略 operations 时默认 read;显式填写时必须包含有效操作。");
247125
+ pushResourceError(ctx, "invalid-resource-operation", `${owner} 对资源 '${resource}' 的 operations 必须是非空静态数组`, file, lineOf2(operationsEl), "仅省略 operations 时默认 read;显式填写时必须包含有效操作。");
246352
247126
  continue;
246353
247127
  }
246354
247128
  let invalid = false;
246355
247129
  for (const operationEl of operationArray.elements) {
246356
247130
  const operation = unwrapInfraResourceExpression(operationEl);
246357
247131
  if (!ts7.isStringLiteral(operation) || !isInfraResourceOperation(operation.text)) {
246358
- pushResourceError(ctx, "invalid-resource-operation", `${owner} 对资源 '${resource}' 声明了无效操作 '${nodeText(operationEl)}'`, file, lineOf(operationEl), `operations 只能包含字符串字面量 ${INFRA_RESOURCE_OPERATIONS.join(" | ")}。`);
247132
+ pushResourceError(ctx, "invalid-resource-operation", `${owner} 对资源 '${resource}' 声明了无效操作 '${nodeText(operationEl)}'`, file, lineOf2(operationEl), `operations 只能包含字符串字面量 ${INFRA_RESOURCE_OPERATIONS.join(" | ")}。`);
246359
247133
  invalid = true;
246360
247134
  continue;
246361
247135
  }
@@ -247973,11 +248747,11 @@ async function readSourceBytes(path) {
247973
248747
  return readFile7(path);
247974
248748
  }
247975
248749
  async function updateSnapshot(previous, options, changedPaths) {
247976
- const rootDir = resolve15(options.rootDir);
247977
- const outDir = resolve15(options.outDir);
248750
+ const rootDir = resolve152(options.rootDir);
248751
+ const outDir = resolve152(options.outDir);
247978
248752
  const files = { ...previous.files };
247979
248753
  await Promise.all(changedPaths.map(async (changedPath) => {
247980
- const absolutePath = isAbsolute42(changedPath) ? resolve15(changedPath) : resolve15(rootDir, changedPath);
248754
+ const absolutePath = isAbsolute42(changedPath) ? resolve152(changedPath) : resolve152(rootDir, changedPath);
247981
248755
  const relativeChangedPath = relative9(rootDir, absolutePath);
247982
248756
  if (relativeChangedPath === ".." || relativeChangedPath.startsWith(`..${sep9}`))
247983
248757
  return;
@@ -247995,8 +248769,8 @@ async function updateSnapshot(previous, options, changedPaths) {
247995
248769
  return { files, optionsKey: optionsKeyOf(options) };
247996
248770
  }
247997
248771
  async function createSnapshot(options) {
247998
- const rootDir = resolve15(options.rootDir);
247999
- const outDir = resolve15(options.outDir);
248772
+ const rootDir = resolve152(options.rootDir);
248773
+ const outDir = resolve152(options.outDir);
248000
248774
  const paths = await listSourceFiles(rootDir, outDir);
248001
248775
  const files = {};
248002
248776
  const entries = await Promise.all(paths.map(async (path) => {
@@ -248019,8 +248793,8 @@ async function createSnapshot(options) {
248019
248793
  }
248020
248794
  function optionsKeyOf(options) {
248021
248795
  return JSON.stringify({
248022
- rootDir: resolve15(options.rootDir),
248023
- outDir: resolve15(options.outDir),
248796
+ rootDir: resolve152(options.rootDir),
248797
+ outDir: resolve152(options.outDir),
248024
248798
  include: options.include,
248025
248799
  strict: options.strict,
248026
248800
  writeOnError: options.writeOnError,
@@ -248044,7 +248818,7 @@ async function listSourceFiles(rootDir, outDir) {
248044
248818
  const result = [];
248045
248819
  const visit = async (directory) => {
248046
248820
  for (const entry of await readdir32(directory, { withFileTypes: true })) {
248047
- const path = resolve15(directory, entry.name);
248821
+ const path = resolve152(directory, entry.name);
248048
248822
  if (entry.isDirectory()) {
248049
248823
  if (entry.name === "node_modules" || entry.name === ".git" || path === outDir)
248050
248824
  continue;
@@ -248618,7 +249392,7 @@ var starter_metadata_default = {
248618
249392
  version: "0.8.0"
248619
249393
  },
248620
249394
  db: {
248621
- version: "0.13.2",
249395
+ version: "0.14.0",
248622
249396
  peerDependencies: {
248623
249397
  "drizzle-orm": "1.0.0-rc.5-169397b"
248624
249398
  },
@@ -248726,8 +249500,8 @@ test("cancelled stream consumers release their subscription", async () => {
248726
249500
  `;
248727
249501
 
248728
249502
  // src/shared/tools/app-starter.ts
248729
- import { lstat as lstat3, mkdir as mkdir4, readdir as readdir4, writeFile as writeFile4 } from "node:fs/promises";
248730
- import { basename as basename8, dirname as dirname10, join as join12, resolve as resolve17 } from "node:path";
249503
+ import { lstat as lstat3, mkdir as mkdir5, readdir as readdir5, writeFile as writeFile5 } from "node:fs/promises";
249504
+ import { basename as basename9, dirname as dirname10, join as join13, resolve as resolve17 } from "node:path";
248731
249505
 
248732
249506
  // src/shared/tools/app-starter-environment.ts
248733
249507
  var STARTER_ENVIRONMENT = String.raw`import { existsSync, readFileSync } from "node:fs";
@@ -251271,15 +252045,15 @@ and governance; TypeScript checks both application and generated source.
251271
252045
  }
251272
252046
  async function initializeAppProject(options) {
251273
252047
  const root = resolve17(options.root ?? process.cwd());
251274
- const name = options.name ?? basename8(root);
252048
+ const name = options.name ?? basename9(root);
251275
252049
  if (!/^[a-z][a-z0-9-]*$/.test(name) || name.length > 100) {
251276
252050
  throw new Error("Project name must be lowercase kebab-case (1-100 characters)");
251277
252051
  }
251278
- await mkdir4(root, { recursive: true });
252052
+ await mkdir5(root, { recursive: true });
251279
252053
  const stat = await lstat3(root);
251280
252054
  if (stat.isSymbolicLink() || !stat.isDirectory())
251281
252055
  throw new Error("Project root must be a real directory");
251282
- if ((await readdir4(root)).some((entry) => entry !== ".git")) {
252056
+ if ((await readdir5(root)).some((entry) => entry !== ".git")) {
251283
252057
  throw new Error("app init requires an empty directory (an existing .git directory is allowed)");
251284
252058
  }
251285
252059
  const template = options.template ?? "minimal";
@@ -251288,16 +252062,16 @@ async function initializeAppProject(options) {
251288
252062
  "@supabase/supabase-js": sdkMetadata.peerDependencies["@supabase/supabase-js"]
251289
252063
  });
251290
252064
  for (const [relativePath, content] of Object.entries(files)) {
251291
- const path = join12(root, relativePath);
251292
- await mkdir4(dirname10(path), { recursive: true });
251293
- await writeFile4(path, content, { encoding: "utf8", flag: "wx" });
252065
+ const path = join13(root, relativePath);
252066
+ await mkdir5(dirname10(path), { recursive: true });
252067
+ await writeFile5(path, content, { encoding: "utf8", flag: "wx" });
251294
252068
  }
251295
252069
  return { root, name, files: Object.keys(files) };
251296
252070
  }
251297
252071
 
251298
252072
  // src/shared/tools/app-database-check.ts
251299
252073
  import { existsSync as existsSync12 } from "node:fs";
251300
- import { join as join13, resolve as resolve18 } from "node:path";
252074
+ import { join as join14, resolve as resolve18 } from "node:path";
251301
252075
  var CHECK_ENTRY = `
251302
252076
  import { createRequire } from "node:module";
251303
252077
  import { join } from "node:path";
@@ -251309,7 +252083,7 @@ await import(pathToFileURL(cli).href);
251309
252083
  `;
251310
252084
  function checkAppDatabaseSources(projectRoot) {
251311
252085
  const root = resolve18(projectRoot);
251312
- if (!existsSync12(join13(root, "database.sources.json")))
252086
+ if (!existsSync12(join14(root, "database.sources.json")))
251313
252087
  return;
251314
252088
  const name = "database-source-contracts";
251315
252089
  try {
@@ -251440,7 +252214,7 @@ test("${name} controller preserves an asynchronous read failure", async () => {
251440
252214
  // src/shared/tools/app-local-dev.ts
251441
252215
  import { spawn } from "node:child_process";
251442
252216
  import { readFile as readFile4 } from "node:fs/promises";
251443
- import { join as join14, resolve as resolve19 } from "node:path";
252217
+ import { join as join15, resolve as resolve19 } from "node:path";
251444
252218
  function localDatabaseUrl(value) {
251445
252219
  try {
251446
252220
  if (!value || /[\x00-\x20\x7f]/.test(value))
@@ -251499,7 +252273,7 @@ async function runLocalDevelopment(options) {
251499
252273
  throw new Error("Inherited remote context must be complete and explicitly tagged with SUPACLOUD_ENV=test");
251500
252274
  }
251501
252275
  const script = profile === "integration" ? "dev:integration" : "dev";
251502
- const manifest = JSON.parse(await readFile4(join14(root, "package.json"), "utf8"));
252276
+ const manifest = JSON.parse(await readFile4(join15(root, "package.json"), "utf8"));
251503
252277
  const scripts = manifest && typeof manifest === "object" && "scripts" in manifest ? manifest.scripts : undefined;
251504
252278
  const command = scripts && typeof scripts === "object" ? Reflect.get(scripts, script) : undefined;
251505
252279
  if (typeof command !== "string" || !command.trim()) {
@@ -251587,7 +252361,7 @@ async function runLocalDevelopment(options) {
251587
252361
  }
251588
252362
 
251589
252363
  // src/shared/tools/app-verification-plan.ts
251590
- import { readdir as readdir5, realpath as realpath5 } from "node:fs/promises";
252364
+ import { readdir as readdir6, realpath as realpath5 } from "node:fs/promises";
251591
252365
  import { dirname as dirname11, isAbsolute as isAbsolute6, relative as relative11, resolve as resolve20 } from "node:path";
251592
252366
  function inside3(root, file) {
251593
252367
  const path = relative11(root, file).replaceAll("\\", "/");
@@ -251618,7 +252392,7 @@ async function createVerificationPlan(root, graph, target, sourceRoot = root) {
251618
252392
  manual.push("A source directory is outside the project; select its owning project explicitly");
251619
252393
  continue;
251620
252394
  }
251621
- for (const entry of await readdir5(actual, { withFileTypes: true })) {
252395
+ for (const entry of await readdir6(actual, { withFileTypes: true })) {
251622
252396
  if (entry.isFile() && /\.(test|spec)\.[cm]?[jt]sx?$/.test(entry.name)) {
251623
252397
  tests.add(relative11(project, resolve20(actual, entry.name)).replaceAll("\\", "/"));
251624
252398
  }
@@ -251648,8 +252422,8 @@ async function createVerificationPlan(root, graph, target, sourceRoot = root) {
251648
252422
 
251649
252423
  // src/shared/tools/app-scaffold-writes.ts
251650
252424
  import { randomUUID as randomUUID2 } from "node:crypto";
251651
- import { link, lstat as lstat4, mkdir as mkdir5, open as open4, readFile as readFile8, realpath as realpath6, rename as rename5, rmdir, unlink as unlink3 } from "node:fs/promises";
251652
- import { dirname as dirname12, isAbsolute as isAbsolute7, join as join15, relative as relative12, resolve as resolve23, sep as sep11, win32 } from "node:path";
252425
+ import { link, lstat as lstat4, mkdir as mkdir6, open as open4, readFile as readFile8, realpath as realpath6, rename as rename5, rmdir, unlink as unlink3 } from "node:fs/promises";
252426
+ import { dirname as dirname12, isAbsolute as isAbsolute7, join as join16, relative as relative12, resolve as resolve23, sep as sep11, win32 } from "node:path";
251653
252427
 
251654
252428
  class ScaffoldError extends Error {
251655
252429
  code;
@@ -251682,7 +252456,7 @@ async function snapshot(root, path) {
251682
252456
  const parts = relative12(root, target).split(sep11);
251683
252457
  let current = root;
251684
252458
  for (const [index, part] of parts.entries()) {
251685
- current = join15(current, part);
252459
+ current = join16(current, part);
251686
252460
  let stat;
251687
252461
  try {
251688
252462
  stat = await lstat4(current);
@@ -251732,7 +252506,7 @@ async function planScaffoldWrites(root, writes) {
251732
252506
  return Object.freeze({ root: canonical, writes: Object.freeze(planned) });
251733
252507
  }
251734
252508
  async function applyScaffoldWrites(plan) {
251735
- const lockPath = join15(plan.root, ".supacloud-generate.lock");
252509
+ const lockPath = join16(plan.root, ".supacloud-generate.lock");
251736
252510
  const lock = await open4(lockPath, "wx").catch((error) => {
251737
252511
  if (fsCode(error) === "EEXIST")
251738
252512
  throw new ScaffoldError("SCAFFOLD_BUSY", "Another generator owns .supacloud-generate.lock; no files written");
@@ -251753,9 +252527,9 @@ async function applyScaffoldWrites(plan) {
251753
252527
  const target = scaffoldPath(plan.root, write.path);
251754
252528
  let current = plan.root;
251755
252529
  for (const part of relative12(plan.root, dirname12(target)).split(sep11).filter(Boolean)) {
251756
- current = join15(current, part);
252530
+ current = join16(current, part);
251757
252531
  try {
251758
- await mkdir5(current);
252532
+ await mkdir6(current);
251759
252533
  directories.push(current);
251760
252534
  } catch (error) {
251761
252535
  if (fsCode(error) !== "EEXIST")
@@ -251986,19 +252760,19 @@ async function generateScaffold(args) {
251986
252760
  let contractName;
251987
252761
  if (kind === "module" || kind === "resource") {
251988
252762
  const name = requireIdentifier(args.name, "name");
251989
- modulePath = join16(dir, name, `${name}.module.ts`).replaceAll("\\", "/");
252763
+ modulePath = join17(dir, name, `${name}.module.ts`).replaceAll("\\", "/");
251990
252764
  moduleSymbol = `${pascalName2(name)}Module`;
251991
252765
  if (kind === "module")
251992
252766
  writes.push({ path: modulePath, content: moduleScaffold(name), overwrite: args.force === true });
251993
252767
  else
251994
252768
  for (const [file, content] of Object.entries(resourceScaffold(name, pascalName2(name)))) {
251995
- writes.push({ path: join16(dir, name, file).replaceAll("\\", "/"), content });
252769
+ writes.push({ path: join17(dir, name, file).replaceAll("\\", "/"), content });
251996
252770
  }
251997
252771
  } else {
251998
252772
  const moduleName = requireIdentifier(args.module, "module");
251999
252773
  const name = kind === "controller" ? moduleName : requireIdentifier(args.name, "name");
252000
252774
  const subdir = kind === "command" ? "commands" : kind === "query" ? "queries" : kind === "job" ? "jobs" : kind === "contract" ? "contracts" : "";
252001
- const path = join16(dir, moduleName, subdir, `${name}.${kind}.ts`).replaceAll("\\", "/");
252775
+ const path = join17(dir, moduleName, subdir, `${name}.${kind}.ts`).replaceAll("\\", "/");
252002
252776
  if (kind === "controller" && existsSync13(scaffoldPath(root, path))) {
252003
252777
  throw new ScaffoldError("SCAFFOLD_EXISTS", `Controller already exists: ${path}(请手工合并路由到现有 controller)`);
252004
252778
  }
@@ -252048,7 +252822,7 @@ async function generateScaffold(args) {
252048
252822
  written: !args.dry_run,
252049
252823
  changes
252050
252824
  }, null, 2));
252051
- const lines = changes.map((change) => `${args.dry_run ? "PREVIEW" : "✅"} ${change.action}: ${join16(plan.root, change.path)}`);
252825
+ const lines = changes.map((change) => `${args.dry_run ? "PREVIEW" : "✅"} ${change.action}: ${join17(plan.root, change.path)}`);
252052
252826
  if (args.dry_run)
252053
252827
  lines.push("No files written. Omit --dry-run to apply this generation.");
252054
252828
  if (kind === "resource")
@@ -252084,12 +252858,12 @@ function parseInclude(include) {
252084
252858
  return patterns && patterns.length > 0 ? patterns : undefined;
252085
252859
  }
252086
252860
  function sourceRoot(args, root, configured) {
252087
- const hasConfig = ["ts", "mts", "js", "mjs"].some((extension) => existsSync13(join16(root, `supacloud.config.${extension}`)));
252861
+ const hasConfig = ["ts", "mts", "js", "mjs"].some((extension) => existsSync13(join17(root, `supacloud.config.${extension}`)));
252088
252862
  return args.root && !hasConfig ? "." : configured;
252089
252863
  }
252090
252864
  async function projectCompileConfig(args) {
252091
252865
  const root = resolve24(args.root || process.cwd());
252092
- const configFile = ["ts", "mts", "js", "mjs"].map((extension) => join16(root, `supacloud.config.${extension}`)).find((candidate) => existsSync13(candidate)) ?? null;
252866
+ const configFile = ["ts", "mts", "js", "mjs"].map((extension) => join17(root, `supacloud.config.${extension}`)).find((candidate) => existsSync13(candidate)) ?? null;
252093
252867
  const loadedConfig = await loadSupacloudConfig(root);
252094
252868
  const defaults = resolveSupacloudConfig(loadedConfig, root);
252095
252869
  const configuredRoot = sourceRoot(args, root, loadedConfig.root);
@@ -252106,7 +252880,7 @@ async function projectCompileConfig(args) {
252106
252880
  return { root, sourceDir: compileOptions.rootDir, configFile, outDir, result };
252107
252881
  }
252108
252882
  async function readAllowedDependencies(root) {
252109
- const path = join16(root, "package.json");
252883
+ const path = join17(root, "package.json");
252110
252884
  if (!existsSync13(path))
252111
252885
  return { dependencies: [], devDependencies: [] };
252112
252886
  try {
@@ -252187,14 +252961,14 @@ async function findUnwiredContracts(rootDir) {
252187
252961
  async function walk(directory) {
252188
252962
  let entries;
252189
252963
  try {
252190
- entries = await readdir6(directory, { withFileTypes: true });
252964
+ entries = await readdir7(directory, { withFileTypes: true });
252191
252965
  } catch {
252192
252966
  return;
252193
252967
  }
252194
252968
  for (const entry of entries) {
252195
252969
  if (skip.has(entry.name))
252196
252970
  continue;
252197
- const path = join16(directory, entry.name);
252971
+ const path = join17(directory, entry.name);
252198
252972
  if (entry.isDirectory())
252199
252973
  await walk(path);
252200
252974
  else if (entry.isFile() && entry.name.endsWith(".ts"))
@@ -252212,7 +252986,7 @@ async function findUnwiredContracts(rootDir) {
252212
252986
  const symbols = [...source.matchAll(/export\s+const\s+([A-Za-z_$][A-Za-z0-9_$]*)/g)].map((match) => match[1]);
252213
252987
  if (symbols.length === 0)
252214
252988
  continue;
252215
- const moduleName = basename9(contract).replace(/\.ts$/, "");
252989
+ const moduleName = basename10(contract).replace(/\.ts$/, "");
252216
252990
  const wired = otherSources.some((text) => text.includes(moduleName) || symbols.some((symbol) => new RegExp(`\\b${symbol}\\b`).test(text)));
252217
252991
  if (!wired)
252218
252992
  unwired.push({ file: relative13(rootDir, contract), symbols });
@@ -252305,7 +253079,7 @@ function devDatabase(profile, explicit) {
252305
253079
  }
252306
253080
  function readManifestModules(outDir) {
252307
253081
  try {
252308
- const parsed = JSON.parse(readFileSync10(join16(outDir, "app.manifest.json"), "utf8"));
253082
+ const parsed = JSON.parse(readFileSync10(join17(outDir, "app.manifest.json"), "utf8"));
252309
253083
  return Array.isArray(parsed.modules) ? parsed.modules.map((module) => module.name) : [];
252310
253084
  } catch {
252311
253085
  return [];
@@ -252512,7 +253286,7 @@ async function runCheck(args) {
252512
253286
  async function readManifest(root, args) {
252513
253287
  const config = await loadSupacloudConfig(root);
252514
253288
  const outDir = args.out_dir ? resolve24(root, args.out_dir) : resolveSupacloudConfig(config, root).outDir;
252515
- const manifestPath = join16(outDir, "app.manifest.json");
253289
+ const manifestPath = join17(outDir, "app.manifest.json");
252516
253290
  if (!existsSync13(manifestPath)) {
252517
253291
  throw new Error(`Manifest not found: ${manifestPath}(先运行 app compile 生成)`);
252518
253292
  }
@@ -252702,15 +253476,15 @@ async function runExportTools(args) {
252702
253476
  const root = resolve24(args.root || process.cwd());
252703
253477
  const manifest = await readManifest(root, { ...args, out_dir: undefined });
252704
253478
  const definitions = buildToolDefinitions(manifest);
252705
- const outDir = resolve24(args.out_dir || join16(root, "generated"));
253479
+ const outDir = resolve24(args.out_dir || join17(root, "generated"));
252706
253480
  if (args.format === "json") {
252707
253481
  return textResult(JSON.stringify(definitions, null, 2));
252708
253482
  }
252709
- await mkdir6(outDir, { recursive: true });
252710
- const openaiPath = join16(outDir, "tool-definitions.openai.json");
252711
- const mcpPath = join16(outDir, "tool-definitions.mcp.json");
252712
- await writeFile5(openaiPath, JSON.stringify(definitions.openai, null, 2), "utf8");
252713
- await writeFile5(mcpPath, JSON.stringify(definitions.mcp, null, 2), "utf8");
253483
+ await mkdir7(outDir, { recursive: true });
253484
+ const openaiPath = join17(outDir, "tool-definitions.openai.json");
253485
+ const mcpPath = join17(outDir, "tool-definitions.mcp.json");
253486
+ await writeFile6(openaiPath, JSON.stringify(definitions.openai, null, 2), "utf8");
253487
+ await writeFile6(mcpPath, JSON.stringify(definitions.mcp, null, 2), "utf8");
252714
253488
  const summary = [
252715
253489
  `exported ${definitions.openai.length} tool(s):`,
252716
253490
  ` openai → ${openaiPath}`,
@@ -252904,440 +253678,7 @@ function registerAppAliases(server2) {
252904
253678
 
252905
253679
  // src/shared/tools/db-governance-tools.ts
252906
253680
  import { existsSync as existsSync14 } from "node:fs";
252907
- import { join as join17, resolve as resolve25 } from "node:path";
252908
-
252909
- // node_modules/@supacloud/db/dist/index.js
252910
- var TABLES_SQL = `
252911
- SELECT n.nspname AS schema,
252912
- c.relname AS name,
252913
- c.relrowsecurity AS rls_enabled,
252914
- c.relforcerowsecurity AS rls_forced
252915
- FROM pg_class c
252916
- JOIN pg_namespace n ON n.oid = c.relnamespace
252917
- WHERE c.relkind = 'r'
252918
- AND n.nspname = ANY($1)
252919
- ORDER BY n.nspname, c.relname
252920
- `;
252921
- var POLICIES_SQL = `
252922
- SELECT n.nspname AS schema,
252923
- c.relname AS table,
252924
- p.polname AS name,
252925
- p.polcmd AS command,
252926
- ARRAY(
252927
- SELECT CASE WHEN r = 0 THEN 'PUBLIC' ELSE pg_get_userbyid(r) END
252928
- FROM unnest(p.polroles) AS r
252929
- ) AS roles,
252930
- pg_get_expr(p.polqual, p.polrelid) AS using_expr,
252931
- pg_get_expr(p.polwithcheck, p.polrelid) AS check_expr
252932
- FROM pg_policy p
252933
- JOIN pg_class c ON c.oid = p.polrelid
252934
- JOIN pg_namespace n ON n.oid = c.relnamespace
252935
- WHERE n.nspname = ANY($1)
252936
- ORDER BY n.nspname, c.relname, p.polname
252937
- `;
252938
- var FUNCTIONS_SQL = `
252939
- SELECT n.nspname AS schema,
252940
- p.proname AS name,
252941
- p.prosecdef AS security_definer,
252942
- p.proconfig AS config,
252943
- l.lanname AS language
252944
- FROM pg_proc p
252945
- JOIN pg_namespace n ON n.oid = p.pronamespace
252946
- JOIN pg_language l ON l.oid = p.prolang
252947
- WHERE n.nspname = ANY($1)
252948
- ORDER BY n.nspname, p.proname
252949
- `;
252950
- var TRIGGERS_SQL = `
252951
- SELECT n.nspname AS schema,
252952
- c.relname AS table,
252953
- t.tgname AS name,
252954
- t.tgenabled <> 'D' AS enabled
252955
- FROM pg_trigger t
252956
- JOIN pg_class c ON c.oid = t.tgrelid
252957
- JOIN pg_namespace n ON n.oid = c.relnamespace
252958
- WHERE NOT t.tgisinternal
252959
- AND n.nspname = ANY($1)
252960
- ORDER BY n.nspname, c.relname, t.tgname
252961
- `;
252962
- var GRANTS_SQL = `
252963
- SELECT table_schema AS object_schema,
252964
- table_name AS object_name,
252965
- privilege_type AS privilege,
252966
- grantee
252967
- FROM information_schema.role_table_grants
252968
- WHERE table_schema = ANY($1)
252969
- UNION ALL
252970
- SELECT routine_schema AS object_schema,
252971
- routine_name AS object_name,
252972
- privilege_type AS privilege,
252973
- grantee
252974
- FROM information_schema.routine_privileges
252975
- WHERE routine_schema = ANY($1)
252976
- `;
252977
- var POLCMD_MAP = {
252978
- r: "select",
252979
- a: "insert",
252980
- w: "update",
252981
- d: "delete",
252982
- "*": "all"
252983
- };
252984
- function extractSearchPath(config) {
252985
- if (!config)
252986
- return null;
252987
- const entry = config.find((item) => item.startsWith("search_path="));
252988
- if (!entry)
252989
- return null;
252990
- return entry.slice("search_path=".length);
252991
- }
252992
- async function readCatalog(executor, schemas = ["public"]) {
252993
- const params = [schemas];
252994
- const [tableRows, policyRows, functionRows, triggerRows, grantRows] = await Promise.all([
252995
- executor.query(TABLES_SQL, params),
252996
- executor.query(POLICIES_SQL, params),
252997
- executor.query(FUNCTIONS_SQL, params),
252998
- executor.query(TRIGGERS_SQL, params),
252999
- executor.query(GRANTS_SQL, params)
253000
- ]);
253001
- return {
253002
- tables: tableRows.map((row) => ({
253003
- schema: row.schema,
253004
- name: row.name,
253005
- rlsEnabled: row.rls_enabled,
253006
- rlsForced: row.rls_forced
253007
- })),
253008
- policies: policyRows.map((row) => ({
253009
- schema: row.schema,
253010
- table: row.table,
253011
- name: row.name,
253012
- command: POLCMD_MAP[row.command] ?? "all",
253013
- roles: row.roles ?? [],
253014
- usingExpr: row.using_expr ?? undefined,
253015
- checkExpr: row.check_expr ?? undefined
253016
- })),
253017
- functions: functionRows.map((row) => ({
253018
- schema: row.schema,
253019
- name: row.name,
253020
- security: row.security_definer ? "definer" : "invoker",
253021
- searchPath: extractSearchPath(row.config),
253022
- language: row.language
253023
- })),
253024
- triggers: triggerRows.map((row) => ({
253025
- schema: row.schema,
253026
- table: row.table,
253027
- name: row.name,
253028
- enabled: row.enabled
253029
- })),
253030
- grants: grantRows.map((row) => ({
253031
- objectSchema: row.object_schema,
253032
- objectName: row.object_name,
253033
- privilege: row.privilege,
253034
- grantee: row.grantee
253035
- }))
253036
- };
253037
- }
253038
- function splitQualifiedName(name) {
253039
- const dot = name.indexOf(".");
253040
- if (dot === -1)
253041
- return ["public", name];
253042
- return [name.slice(0, dot), name.slice(dot + 1)];
253043
- }
253044
- function isFixedSearchPath(searchPath) {
253045
- if (searchPath === null)
253046
- return false;
253047
- const parts = searchPath.split(",").map((part) => part.trim().replace(/^"|"$/g, ""));
253048
- return parts.every((part) => part !== "" && part.toLowerCase() !== "pg_temp");
253049
- }
253050
- function reconcileModule(module, catalog) {
253051
- const issues = [];
253052
- const ownedTables = new Set(module.tables);
253053
- const push = (severity, code, object, message) => issues.push({ severity, code, object, message });
253054
- for (const policy of module.policies) {
253055
- const [schema, table] = splitQualifiedName(policy.table);
253056
- const found = catalog.policies.some((cp) => cp.schema === schema && cp.table === table && cp.name === policy.name);
253057
- if (!found) {
253058
- push("error", "missing-policy", `${policy.table}.${policy.name}`, `声明的策略 ${policy.name} 在表 ${policy.table} 的 catalog 中不存在`);
253059
- }
253060
- }
253061
- const declaredPolicyKeys = new Set(module.policies.map((p) => `${p.table}::${p.name}`));
253062
- for (const cp of catalog.policies) {
253063
- const qualified = `${cp.schema}.${cp.table}`;
253064
- if (ownedTables.has(qualified) && !declaredPolicyKeys.has(`${qualified}::${cp.name}`)) {
253065
- push("warn", "undeclared-policy", `${qualified}.${cp.name}`, `归属表 ${qualified} 上存在未声明的策略 ${cp.name},可能发生漂移`);
253066
- }
253067
- }
253068
- for (const fn of module.functions) {
253069
- const [schema, name] = splitQualifiedName(fn.name);
253070
- const cf = catalog.functions.find((f) => f.schema === schema && f.name === name);
253071
- if (!cf) {
253072
- push("error", "missing-function", fn.name, `声明的函数 ${fn.name} 在 catalog 中不存在`);
253073
- continue;
253074
- }
253075
- if (cf.security !== fn.security) {
253076
- push("warn", "security-mismatch", fn.name, `函数 ${fn.name} 声明为 security ${fn.security},catalog 实际为 ${cf.security}`);
253077
- }
253078
- const effectiveDefiner = fn.security === "definer" || cf.security === "definer";
253079
- if (effectiveDefiner && !isFixedSearchPath(cf.searchPath)) {
253080
- push("error", "definer-without-search-path", fn.name, `security definer 函数 ${fn.name} 未设置固定 search_path(当前: ${cf.searchPath ?? "未设置"})`);
253081
- }
253082
- }
253083
- for (const trigger of module.triggers) {
253084
- const [schema, table] = splitQualifiedName(trigger.table);
253085
- const found = catalog.triggers.some((ct) => ct.schema === schema && ct.table === table && ct.name === trigger.name);
253086
- if (!found) {
253087
- push("error", "missing-trigger", `${trigger.table}.${trigger.name}`, `声明的触发器 ${trigger.name} 在表 ${trigger.table} 的 catalog 中不存在`);
253088
- }
253089
- }
253090
- const declaredTriggerKeys = new Set(module.triggers.map((t) => `${t.table}::${t.name}`));
253091
- for (const ct of catalog.triggers) {
253092
- const qualified = `${ct.schema}.${ct.table}`;
253093
- if (ownedTables.has(qualified) && !declaredTriggerKeys.has(`${qualified}::${ct.name}`)) {
253094
- push("warn", "undeclared-trigger", `${qualified}.${ct.name}`, `归属表 ${qualified} 上存在未声明的触发器 ${ct.name}${ct.enabled ? "" : "(已禁用)"},可能发生漂移`);
253095
- }
253096
- }
253097
- for (const table of module.tables) {
253098
- const [schema, name] = splitQualifiedName(table);
253099
- const ct = catalog.tables.find((t) => t.schema === schema && t.name === name);
253100
- if (ct && !ct.rlsEnabled) {
253101
- push("error", "rls-disabled", table, `归属表 ${table} 未开启行级安全(relrowsecurity = false)`);
253102
- }
253103
- }
253104
- for (const grant of catalog.grants) {
253105
- const qualified = `${grant.objectSchema}.${grant.objectName}`;
253106
- if (ownedTables.has(qualified) && grant.grantee.toUpperCase() === "PUBLIC") {
253107
- push("error", "wildcard-grant", qualified, `归属表 ${qualified} 存在授予 PUBLIC 的 ${grant.privilege} 权限`);
253108
- }
253109
- }
253110
- for (const grant of module.grants) {
253111
- const [schema, name] = splitQualifiedName(grant.object);
253112
- const found = catalog.grants.some((cg) => cg.objectSchema === schema && cg.objectName === name && cg.privilege.toLowerCase() === grant.privilege.toLowerCase() && cg.grantee.toLowerCase() === grant.role.toLowerCase());
253113
- if (!found) {
253114
- push("warn", "grant-drift", grant.object, `声明的授权 ${grant.privilege} ON ${grant.object} TO ${grant.role} 在 catalog 中不存在`);
253115
- }
253116
- }
253117
- return {
253118
- module: module.name,
253119
- issues,
253120
- ok: !issues.some((issue) => issue.severity === "error")
253121
- };
253122
- }
253123
- var SECURITY_DEFINER_RE = /\bsecurity\s+definer\b/i;
253124
- var SET_SEARCH_PATH_RE = /\bset\s+search_path\b/i;
253125
- var GRANT_TO_PUBLIC_RE = /\bgrant\b[^;]*\bto\s+public\b/i;
253126
- var DROP_WITHOUT_IF_EXISTS_RE = /\bdrop\s+(?:table|column)\s+(?!if\s+exists\b)/i;
253127
- var ENABLE_RLS_RE = /\benable\s+row\s+level\s+security\b/i;
253128
- var CREATE_POLICY_RE = /\bcreate\s+policy\b/i;
253129
- var DROP_POLICY_IF_EXISTS_RE = /\bdrop\s+policy\s+if\s+exists\b/i;
253130
- function lineOf2(sql, index) {
253131
- let line = 1;
253132
- for (let i = 0;i < index; i += 1) {
253133
- if (sql.charCodeAt(i) === 10)
253134
- line += 1;
253135
- }
253136
- return line;
253137
- }
253138
- function lintSql(sql, file) {
253139
- const issues = [];
253140
- const definer = SECURITY_DEFINER_RE.exec(sql);
253141
- if (definer && !SET_SEARCH_PATH_RE.test(sql)) {
253142
- issues.push({
253143
- severity: "error",
253144
- code: "definer-no-search-path",
253145
- message: "security definer 函数必须显式 set search_path,避免 search_path 劫持",
253146
- file,
253147
- line: lineOf2(sql, definer.index)
253148
- });
253149
- }
253150
- const grantPublic = GRANT_TO_PUBLIC_RE.exec(sql);
253151
- if (grantPublic) {
253152
- issues.push({
253153
- severity: "error",
253154
- code: "grant-to-public",
253155
- message: "禁止将权限授予 PUBLIC 角色",
253156
- file,
253157
- line: lineOf2(sql, grantPublic.index)
253158
- });
253159
- }
253160
- const drop = DROP_WITHOUT_IF_EXISTS_RE.exec(sql);
253161
- if (drop) {
253162
- issues.push({
253163
- severity: "warn",
253164
- code: "drop-without-if-exists",
253165
- message: "drop table/column 建议使用 if exists,保证迁移可重入",
253166
- file,
253167
- line: lineOf2(sql, drop.index)
253168
- });
253169
- }
253170
- const createPolicy = CREATE_POLICY_RE.exec(sql);
253171
- if (createPolicy) {
253172
- const dropPolicy = DROP_POLICY_IF_EXISTS_RE.exec(sql);
253173
- if (!dropPolicy || dropPolicy.index > createPolicy.index) {
253174
- issues.push({
253175
- severity: "warn",
253176
- code: "non-idempotent-policy",
253177
- message: "create policy 前缺少 drop policy if exists,策略不可重复执行",
253178
- file,
253179
- line: lineOf2(sql, createPolicy.index)
253180
- });
253181
- }
253182
- }
253183
- return issues;
253184
- }
253185
- async function lintModule(module, readFile) {
253186
- const issues = [];
253187
- const sources = new Set;
253188
- for (const decl of [
253189
- ...module.policies,
253190
- ...module.functions,
253191
- ...module.triggers,
253192
- ...module.grants
253193
- ]) {
253194
- sources.add(decl.source);
253195
- }
253196
- const contents = new Map;
253197
- await Promise.all([...sources].map(async (path) => {
253198
- contents.set(path, await readFile(path));
253199
- }));
253200
- for (const [file, sql] of contents) {
253201
- issues.push(...lintSql(sql, file));
253202
- }
253203
- if (module.policies.length > 0) {
253204
- const anyEnable = module.policies.some((policy) => ENABLE_RLS_RE.test(contents.get(policy.source) ?? ""));
253205
- if (!anyEnable) {
253206
- issues.push({
253207
- severity: "warn",
253208
- code: "missing-rls-enable",
253209
- message: `模块 ${module.name} 声明了 ${module.policies.length} 条策略,但所有策略源文件都没有 enable row level security`,
253210
- file: module.policies[0].source
253211
- });
253212
- }
253213
- }
253214
- for (const policy of module.policies) {
253215
- if (!policy.tests || policy.tests.length === 0) {
253216
- issues.push({
253217
- severity: "warn",
253218
- code: "policy-without-test",
253219
- message: `策略 ${policy.name} 未声明测试文件`,
253220
- file: policy.source
253221
- });
253222
- }
253223
- }
253224
- for (const fn of module.functions) {
253225
- if (!fn.tests || fn.tests.length === 0) {
253226
- issues.push({
253227
- severity: "warn",
253228
- code: "policy-without-test",
253229
- message: `函数 ${fn.name} 未声明测试文件`,
253230
- file: fn.source
253231
- });
253232
- }
253233
- }
253234
- return issues;
253235
- }
253236
- function buildDatabaseManifest(modules) {
253237
- return {
253238
- version: 1,
253239
- modules: modules.map((module) => ({
253240
- name: module.name,
253241
- tables: module.tables,
253242
- policies: module.policies,
253243
- functions: module.functions,
253244
- triggers: module.triggers,
253245
- grants: module.grants
253246
- }))
253247
- };
253248
- }
253249
- function describePolicy(moduleName, policy) {
253250
- const lines = [
253251
- `对象: ${policy.table}.${policy.name}`,
253252
- "类型: 策略 (policy)",
253253
- `所属模块: ${moduleName}`,
253254
- `表: ${policy.table}`,
253255
- `操作: ${policy.operation}`,
253256
- `角色: ${policy.roles.join(", ")}`,
253257
- `源文件: ${policy.source}`
253258
- ];
253259
- if (policy.tests && policy.tests.length > 0)
253260
- lines.push(`测试: ${policy.tests.join(", ")}`);
253261
- return lines.join(`
253262
- `);
253263
- }
253264
- function describeFunction(moduleName, fn) {
253265
- const lines = [
253266
- `对象: ${fn.name}`,
253267
- "类型: 函数 (function)",
253268
- `所属模块: ${moduleName}`,
253269
- `源文件: ${fn.source}`,
253270
- `安全模式: ${fn.security}`
253271
- ];
253272
- if (fn.permission)
253273
- lines.push(`权限: ${fn.permission}`);
253274
- if (fn.transaction)
253275
- lines.push(`事务: ${fn.transaction}`);
253276
- if (fn.audit)
253277
- lines.push(`审计: ${fn.audit}`);
253278
- if (fn.idempotency)
253279
- lines.push(`幂等: ${fn.idempotency}`);
253280
- if (fn.tests && fn.tests.length > 0)
253281
- lines.push(`测试: ${fn.tests.join(", ")}`);
253282
- return lines.join(`
253283
- `);
253284
- }
253285
- function describeTrigger(moduleName, trigger) {
253286
- return [
253287
- `对象: ${trigger.name}`,
253288
- "类型: 触发器 (trigger)",
253289
- `所属模块: ${moduleName}`,
253290
- `表: ${trigger.table}`,
253291
- `源文件: ${trigger.source}`
253292
- ].join(`
253293
- `);
253294
- }
253295
- function describeGrant(moduleName, grant) {
253296
- return [
253297
- `对象: ${grant.object}`,
253298
- "类型: 授权 (grant)",
253299
- `所属模块: ${moduleName}`,
253300
- `权限: ${grant.privilege}`,
253301
- `角色: ${grant.role}`,
253302
- `源文件: ${grant.source}`
253303
- ].join(`
253304
- `);
253305
- }
253306
- function explainObject(manifest, name) {
253307
- for (const module of manifest.modules) {
253308
- for (const policy of module.policies) {
253309
- if (policy.name === name || `${policy.table}.${policy.name}` === name) {
253310
- return describePolicy(module.name, policy);
253311
- }
253312
- }
253313
- for (const fn of module.functions) {
253314
- if (fn.name === name)
253315
- return describeFunction(module.name, fn);
253316
- }
253317
- for (const trigger of module.triggers) {
253318
- if (trigger.name === name || `${trigger.table}.${trigger.name}` === name) {
253319
- return describeTrigger(module.name, trigger);
253320
- }
253321
- }
253322
- for (const table of module.tables) {
253323
- if (table === name) {
253324
- return [
253325
- `对象: ${table}`,
253326
- "类型: 表 (table)",
253327
- `所属模块: ${module.name}`
253328
- ].join(`
253329
- `);
253330
- }
253331
- }
253332
- for (const grant of module.grants) {
253333
- if (grant.object === name)
253334
- return describeGrant(module.name, grant);
253335
- }
253336
- }
253337
- return `未找到对象: ${name}`;
253338
- }
253339
-
253340
- // src/shared/tools/db-governance-tools.ts
253681
+ import { join as join18, resolve as resolve25 } from "node:path";
253341
253682
  function textResult2(text, isError = false) {
253342
253683
  return { isError, content: [{ type: "text", text }] };
253343
253684
  }
@@ -253356,7 +253697,7 @@ function normalizeModule(candidate, sourcePath) {
253356
253697
  };
253357
253698
  }
253358
253699
  async function loadDatabaseModules(root, moduleFile) {
253359
- const filePath = moduleFile ? resolve25(root, moduleFile) : join17(root, "db", "modules.ts");
253700
+ const filePath = moduleFile ? resolve25(root, moduleFile) : join18(root, "db", "modules.ts");
253360
253701
  if (!existsSync14(filePath)) {
253361
253702
  throw new Error(moduleFile ? `Module file not found: ${filePath}` : `Module file not found: ${filePath}(用 --module_file 指定一个导出 defineDatabaseModule(...) 的文件)`);
253362
253703
  }
@@ -254183,7 +254524,7 @@ var MUTATION_TOOL_SCHEMA = {
254183
254524
  // src/shared/tools/release-tools.ts
254184
254525
  import { createHash as createHash11 } from "node:crypto";
254185
254526
  import { existsSync as existsSync15, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "node:fs";
254186
- import { dirname as dirname14, join as join18, relative as relative14, resolve as resolve27 } from "node:path";
254527
+ import { dirname as dirname14, join as join19, relative as relative14, resolve as resolve27 } from "node:path";
254187
254528
  var SAFE_PROJECT_REF = /^[A-Za-z0-9_-]{1,64}$/;
254188
254529
  var BACKUP_ID = /^logical-full_[A-Za-z0-9_-]{1,64}_[a-f0-9]{32}$/;
254189
254530
  var SHA256 = /^[a-f0-9]{64}$/;
@@ -254511,7 +254852,7 @@ function collectScopeFiles(cwd, file, files, dir, task) {
254511
254852
  const entries = readdirSync5(directory, { withFileTypes: true });
254512
254853
  for (const entry of entries) {
254513
254854
  if (entry.isFile() && entry.name.endsWith(".json")) {
254514
- const fullPath = join18(directory, entry.name);
254855
+ const fullPath = join19(directory, entry.name);
254515
254856
  if (!taskFilter) {
254516
254857
  results.push(fullPath);
254517
254858
  } else if (entry.name.toLowerCase().includes(taskFilter)) {
@@ -254896,10 +255237,10 @@ function registerReleaseTools(server2, http, options = {}) {
254896
255237
  }
254897
255238
 
254898
255239
  // src/shared/tools/deploy-tools.ts
254899
- import { access as access4, lstat as lstat5, mkdtemp as mkdtemp2, opendir, readFile as readFile10, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
255240
+ import { access as access4, lstat as lstat5, mkdtemp as mkdtemp2, opendir, readFile as readFile10, rm as rm3, writeFile as writeFile7 } from "node:fs/promises";
254900
255241
  import { createHash as createHash12 } from "node:crypto";
254901
255242
  import { tmpdir as tmpdir2 } from "node:os";
254902
- import { basename as basename10, dirname as dirname15, join as join19, relative as relative15, resolve as resolve28, sep as sep12 } from "node:path";
255243
+ import { basename as basename11, dirname as dirname15, join as join20, relative as relative15, resolve as resolve28, sep as sep12 } from "node:path";
254903
255244
 
254904
255245
  // node_modules/fflate/esm/index.mjs
254905
255246
  import { createRequire as createRequire3 } from "module";
@@ -255592,7 +255933,7 @@ async function readJson(path) {
255592
255933
  async function findDeployConfigRoot(startDirectory) {
255593
255934
  let directory = resolve28(startDirectory);
255594
255935
  while (true) {
255595
- if (await pathExists(join19(directory, "supacloud.json")))
255936
+ if (await pathExists(join20(directory, "supacloud.json")))
255596
255937
  return directory;
255597
255938
  const parent = dirname15(directory);
255598
255939
  if (parent === directory)
@@ -255639,7 +255980,7 @@ function deployTargetConfig(candidate, name) {
255639
255980
  };
255640
255981
  }
255641
255982
  async function readDeployConfig(configRoot) {
255642
- const configPath = join19(configRoot, "supacloud.json");
255983
+ const configPath = join20(configRoot, "supacloud.json");
255643
255984
  const candidate = await readJson(configPath);
255644
255985
  if (candidate === null)
255645
255986
  return {};
@@ -255661,25 +256002,25 @@ async function readDeployConfig(configRoot) {
255661
256002
  };
255662
256003
  }
255663
256004
  async function packageName(projectDirectory) {
255664
- const candidate = record5(await readJson(join19(projectDirectory, "package.json")));
256005
+ const candidate = record5(await readJson(join20(projectDirectory, "package.json")));
255665
256006
  return typeof candidate?.name === "string" && candidate.name.trim() ? candidate.name.trim() : null;
255666
256007
  }
255667
256008
  async function pathExists(path) {
255668
256009
  return access4(path).then(() => true, () => false);
255669
256010
  }
255670
256011
  async function defaultBuildCommand(projectDirectory, configRoot) {
255671
- const packageJson = record5(await readJson(join19(projectDirectory, "package.json")));
256012
+ const packageJson = record5(await readJson(join20(projectDirectory, "package.json")));
255672
256013
  const scripts = record5(packageJson?.scripts);
255673
256014
  if (typeof scripts?.build !== "string" || !scripts.build.trim()) {
255674
256015
  throw new Error("No build command was configured and package.json has no build script");
255675
256016
  }
255676
256017
  let directory = projectDirectory;
255677
256018
  while (true) {
255678
- if (await pathExists(join19(directory, "bun.lock")) || await pathExists(join19(directory, "bun.lockb")))
256019
+ if (await pathExists(join20(directory, "bun.lock")) || await pathExists(join20(directory, "bun.lockb")))
255679
256020
  return "bun run build";
255680
- if (await pathExists(join19(directory, "pnpm-lock.yaml")))
256021
+ if (await pathExists(join20(directory, "pnpm-lock.yaml")))
255681
256022
  return "pnpm run build";
255682
- if (await pathExists(join19(directory, "yarn.lock")))
256023
+ if (await pathExists(join20(directory, "yarn.lock")))
255683
256024
  return "yarn build";
255684
256025
  if (directory === configRoot)
255685
256026
  break;
@@ -255798,7 +256139,7 @@ async function collectArchiveFiles(root) {
255798
256139
  entries.push(entry);
255799
256140
  entries.sort((left, right) => left.name.localeCompare(right.name, "en"));
255800
256141
  for (const entry of entries) {
255801
- const path = join19(directory, entry.name);
256142
+ const path = join20(directory, entry.name);
255802
256143
  const stats = await lstat5(path);
255803
256144
  if (stats.isSymbolicLink())
255804
256145
  throw new Error(`Frontend output cannot contain symbolic links: ${path}`);
@@ -255827,11 +256168,11 @@ async function collectArchiveFiles(root) {
255827
256168
  }
255828
256169
  async function createFrontendArchive(outputDirectory) {
255829
256170
  const collected = await collectArchiveFiles(outputDirectory);
255830
- const temporaryDirectory = await mkdtemp2(join19(tmpdir2(), "supacloud-deploy-"));
255831
- const archivePath = join19(temporaryDirectory, `${basename10(outputDirectory)}.zip`);
256171
+ const temporaryDirectory = await mkdtemp2(join20(tmpdir2(), "supacloud-deploy-"));
256172
+ const archivePath = join20(temporaryDirectory, `${basename11(outputDirectory)}.zip`);
255832
256173
  try {
255833
256174
  const archiveBytes = zipSync(collected.files, { level: 6, mtime: FIXED_ZIP_MTIME });
255834
- await writeFile6(archivePath, archiveBytes);
256175
+ await writeFile7(archivePath, archiveBytes);
255835
256176
  const sha256 = createHash12("sha256").update(archiveBytes).digest("hex");
255836
256177
  return {
255837
256178
  archivePath,
@@ -256052,9 +256393,9 @@ function registerDeployTools(server2, http, options = {}) {
256052
256393
  // src/shared/tools/remote-dev-tools.ts
256053
256394
  import { existsSync as existsSync16 } from "node:fs";
256054
256395
  import { readFile as readFile11 } from "node:fs/promises";
256055
- import { readdir as readdir7 } from "node:fs/promises";
256396
+ import { readdir as readdir8 } from "node:fs/promises";
256056
256397
  import { createHash as createHash13 } from "node:crypto";
256057
- import { join as join20, resolve as resolve29 } from "node:path";
256398
+ import { join as join21, resolve as resolve29 } from "node:path";
256058
256399
  var SAFE_TOKEN = /^[A-Za-z0-9._:@/+,-]+$/;
256059
256400
  var SAFE_REMOTE_ROOT = /^\/(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+\/?$/;
256060
256401
  var remoteDevToolSchema = {
@@ -256088,7 +256429,7 @@ async function runProcess(executable, args, cwd) {
256088
256429
  function resolveDrizzleCommand(root, configured) {
256089
256430
  if (configured?.trim())
256090
256431
  return configured.trim();
256091
- const local = join20(root, "node_modules", ".bin", "drizzle-kit");
256432
+ const local = join21(root, "node_modules", ".bin", "drizzle-kit");
256092
256433
  return existsSync16(local) ? local : "drizzle-kit";
256093
256434
  }
256094
256435
  function toolFailed(value) {
@@ -256120,7 +256461,7 @@ async function migrateDatabase(args, options) {
256120
256461
  return { ok: true, mode: "dev", action: "migrate", generated: true, applied: true, migrations_dir: migrationsDir, result: applied };
256121
256462
  }
256122
256463
  async function readProjectConfig3(root) {
256123
- const file = join20(root, "supacloud.json");
256464
+ const file = join21(root, "supacloud.json");
256124
256465
  if (!existsSync16(file))
256125
256466
  return {};
256126
256467
  try {
@@ -256137,9 +256478,9 @@ async function readDevConfig(root) {
256137
256478
  async function sourceFingerprint(root) {
256138
256479
  const files = [];
256139
256480
  const visit = async (directory) => {
256140
- const entries = await readdir7(directory, { withFileTypes: true });
256481
+ const entries = await readdir8(directory, { withFileTypes: true });
256141
256482
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
256142
- const path = join20(directory, entry.name);
256483
+ const path = join21(directory, entry.name);
256143
256484
  if (entry.isDirectory() && ![".git", "node_modules", "dist", "generated", ".supacloud"].includes(entry.name))
256144
256485
  await visit(path);
256145
256486
  else if (entry.isFile())
@@ -256167,17 +256508,17 @@ function remoteRoot(value) {
256167
256508
  }
256168
256509
  function targetDirectory(root, target, functionSlug, config = {}) {
256169
256510
  if (target === "db")
256170
- return join20(root, "supabase", "migrations");
256511
+ return join21(root, "supabase", "migrations");
256171
256512
  if (target === "functions") {
256172
256513
  const targets = config.targets && typeof config.targets === "object" ? config.targets : {};
256173
256514
  const match = Object.values(targets).find((entry) => entry?.type === "edge_function" && (!functionSlug || String(entry.slug || "") === functionSlug));
256174
- const base = match?.root ? resolve29(root, String(match.root)) : join20(root, "supabase", "functions");
256175
- return match?.root ? base : functionSlug ? join20(base, safeToken(functionSlug, "function")) : base;
256515
+ const base = match?.root ? resolve29(root, String(match.root)) : join21(root, "supabase", "functions");
256516
+ return match?.root ? base : functionSlug ? join21(base, safeToken(functionSlug, "function")) : base;
256176
256517
  }
256177
256518
  if (target === "frontend") {
256178
256519
  const targets = config.targets && typeof config.targets === "object" ? config.targets : {};
256179
256520
  const match = Object.values(targets).find((entry) => entry?.type === "frontend");
256180
- return match?.root ? resolve29(root, String(match.root)) : join20(root, "apps", "web");
256521
+ return match?.root ? resolve29(root, String(match.root)) : join21(root, "apps", "web");
256181
256522
  }
256182
256523
  return root;
256183
256524
  }
@@ -256314,7 +256655,7 @@ function registerRemoteDevTools(server2, options = {}) {
256314
256655
  // package.json
256315
256656
  var package_default = {
256316
256657
  name: "@supacloud/cli",
256317
- version: "0.70.1",
256658
+ version: "0.71.0",
256318
256659
  description: "Project-scoped CLI for SupaCloud users",
256319
256660
  type: "module",
256320
256661
  main: "./dist/index.js",
@@ -256350,7 +256691,7 @@ var package_default = {
256350
256691
  dependencies: {
256351
256692
  "@supacloud/compiler": "^0.40.1",
256352
256693
  "@supacloud/delivery": "0.8.0",
256353
- "@supacloud/db": "^0.2.0",
256694
+ "@supacloud/db": "^0.14.0",
256354
256695
  fflate: "^0.8.3",
256355
256696
  typebox: "1.3.34"
256356
256697
  },
@@ -256599,6 +256940,7 @@ EXAMPLES
256599
256940
  ${preferredCommand} database query --ref abc123 --file ./queries/vector-search.sql
256600
256941
  ${preferredCommand} database migration_inventory --ref abc123
256601
256942
  ${preferredCommand} database lint_migrations --dir supabase/migrations
256943
+ ${preferredCommand} database rebase_migrations --dir supabase/migrations --baseline_file backups/schema.sql --baseline_version 20261008000000 --output_dir supabase/migrations-rebased
256602
256944
  ${preferredCommand} database push_migrations --ref abc123 --dir supabase/migrations --dry_run
256603
256945
  ${preferredCommand} supabase migration_new --name add_accounts
256604
256946
  ${preferredCommand} supabase db_diff --schema public --name add_accounts