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