@supacloud/cli 0.62.1 → 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 +370 -140
- 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. */",
|
|
@@ -246887,7 +246888,7 @@ test("partial and untagged inherited remote profiles cannot mix with selected fi
|
|
|
246887
246888
|
// ../compiler/package.json
|
|
246888
246889
|
var package_default = {
|
|
246889
246890
|
name: "@supacloud/compiler",
|
|
246890
|
-
version: "0.31.
|
|
246891
|
+
version: "0.31.1",
|
|
246891
246892
|
description: "Static compiler for @supacloud/app metadata: builds the application graph from AST, validates it, and generates reflection-free factory code",
|
|
246892
246893
|
type: "module",
|
|
246893
246894
|
main: "./dist/index.js",
|
|
@@ -247048,7 +247049,7 @@ var package_default2 = {
|
|
|
247048
247049
|
},
|
|
247049
247050
|
dependencies: {
|
|
247050
247051
|
"@angular/core": "^22.1.5",
|
|
247051
|
-
"@supacloud/contracts": "
|
|
247052
|
+
"@supacloud/contracts": "file:../contracts",
|
|
247052
247053
|
rxjs: "7.8.2",
|
|
247053
247054
|
typebox: "1.3.34"
|
|
247054
247055
|
},
|
|
@@ -248650,16 +248651,22 @@ var package_default5 = {
|
|
|
248650
248651
|
// ../db/package.json
|
|
248651
248652
|
var package_default6 = {
|
|
248652
248653
|
name: "@supacloud/db",
|
|
248653
|
-
version: "0.
|
|
248654
|
+
version: "0.12.0",
|
|
248654
248655
|
description: "Database governance layer for SupaCloud: RLS policies, RPC functions and grants as first-class resources, with manifest/catalog reconciliation",
|
|
248655
248656
|
type: "module",
|
|
248656
248657
|
main: "./dist/index.js",
|
|
248657
248658
|
module: "./dist/index.js",
|
|
248658
248659
|
types: "./dist/index.d.ts",
|
|
248659
248660
|
bin: {
|
|
248660
|
-
"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"
|
|
248661
248663
|
},
|
|
248662
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",
|
|
248663
248670
|
"./sql-analysis": {
|
|
248664
248671
|
types: "./dist/sql-analysis.d.ts",
|
|
248665
248672
|
import: "./dist/sql-analysis.js"
|
|
@@ -248691,7 +248698,7 @@ var package_default6 = {
|
|
|
248691
248698
|
],
|
|
248692
248699
|
scripts: {
|
|
248693
248700
|
build: "bun run clean && bun run build:js && bun run build:types",
|
|
248694
|
-
"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",
|
|
248695
248702
|
"build:types": "tsc -p tsconfig.json --emitDeclarationOnly",
|
|
248696
248703
|
clean: "rm -rf dist",
|
|
248697
248704
|
prepublishOnly: "bun run build",
|
|
@@ -248715,14 +248722,15 @@ var package_default6 = {
|
|
|
248715
248722
|
},
|
|
248716
248723
|
devDependencies: {
|
|
248717
248724
|
"@sinclair/typebox": "0.34.52",
|
|
248718
|
-
"@typescript/typescript6": "^6.0.2",
|
|
248719
248725
|
"drizzle-orm": "1.0.0-rc.4",
|
|
248726
|
+
"drizzle-kit": "1.0.0-rc.4",
|
|
248720
248727
|
"@types/bun": "^1.4.2",
|
|
248721
248728
|
typescript: "^7.0.2"
|
|
248722
248729
|
},
|
|
248723
248730
|
dependencies: {
|
|
248731
|
+
"@typescript/typescript6": "^6.0.2",
|
|
248724
248732
|
"libpg-query": "18.1.4",
|
|
248725
|
-
"@supacloud/contracts": "
|
|
248733
|
+
"@supacloud/contracts": "0.8.0"
|
|
248726
248734
|
},
|
|
248727
248735
|
peerDependencies: {
|
|
248728
248736
|
"drizzle-orm": "1.0.0-rc.4"
|
|
@@ -248733,13 +248741,13 @@ var package_default6 = {
|
|
|
248733
248741
|
}
|
|
248734
248742
|
},
|
|
248735
248743
|
overrides: {
|
|
248736
|
-
"@supacloud/contracts": "
|
|
248744
|
+
"@supacloud/contracts": "0.8.0"
|
|
248737
248745
|
}
|
|
248738
248746
|
};
|
|
248739
248747
|
// ../supacloud-js/package.json
|
|
248740
248748
|
var package_default7 = {
|
|
248741
248749
|
name: "@supacloud/js",
|
|
248742
|
-
version: "0.36.
|
|
248750
|
+
version: "0.36.1",
|
|
248743
248751
|
description: "Platform SDK for SupaCloud, built on top of supabase-js",
|
|
248744
248752
|
type: "module",
|
|
248745
248753
|
main: "./dist/index.cjs",
|
|
@@ -248826,7 +248834,7 @@ var package_default7 = {
|
|
|
248826
248834
|
"@supabase/supabase-js": "^2.115.0"
|
|
248827
248835
|
},
|
|
248828
248836
|
dependencies: {
|
|
248829
|
-
"@supacloud/contracts": "
|
|
248837
|
+
"@supacloud/contracts": "0.8.0",
|
|
248830
248838
|
rxjs: "7.8.2"
|
|
248831
248839
|
},
|
|
248832
248840
|
devDependencies: {
|
|
@@ -248839,11 +248847,133 @@ var package_default7 = {
|
|
|
248839
248847
|
}
|
|
248840
248848
|
};
|
|
248841
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
|
+
|
|
248842
248971
|
// src/shared/tools/app-starter.ts
|
|
248843
248972
|
var json2 = (value) => `${JSON.stringify(value, null, 2)}
|
|
248844
248973
|
`;
|
|
248845
248974
|
function appStarterFiles(name) {
|
|
248846
248975
|
return {
|
|
248976
|
+
...starterDatabaseFiles(),
|
|
248847
248977
|
"package.json": json2({
|
|
248848
248978
|
name,
|
|
248849
248979
|
version: "0.0.0",
|
|
@@ -248854,10 +248984,15 @@ function appStarterFiles(name) {
|
|
|
248854
248984
|
compile: "bun --no-env-file node_modules/@supacloud/compiler/dist/cli.js compile",
|
|
248855
248985
|
"check:generated": "bun --no-env-file node_modules/@supacloud/compiler/dist/cli.js check",
|
|
248856
248986
|
typecheck: "tsc --noEmit",
|
|
248857
|
-
|
|
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",
|
|
248858
248993
|
test: "bun run compile && bun --no-env-file scripts/environment.ts test bun test",
|
|
248859
248994
|
dev: "bun --no-env-file scripts/environment.ts development bun scripts/dev.ts",
|
|
248860
|
-
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",
|
|
248861
248996
|
"env:development": "bun --no-env-file scripts/environment.ts development",
|
|
248862
248997
|
"env:test": "bun --no-env-file scripts/environment.ts test",
|
|
248863
248998
|
"env:staging": "bun --no-env-file scripts/environment.ts staging",
|
|
@@ -248869,6 +249004,7 @@ function appStarterFiles(name) {
|
|
|
248869
249004
|
"@supacloud/commands": `^${package_default4.version}`,
|
|
248870
249005
|
"@supacloud/contracts": `^${package_default5.version}`,
|
|
248871
249006
|
"@supacloud/db": `^${package_default6.version}`,
|
|
249007
|
+
"drizzle-orm": package_default6.peerDependencies["drizzle-orm"],
|
|
248872
249008
|
"@supacloud/js": `^${package_default7.version}`,
|
|
248873
249009
|
"@supabase/supabase-js": package_default7.peerDependencies["@supabase/supabase-js"],
|
|
248874
249010
|
elysia: "2.0.0-beta.19",
|
|
@@ -248876,6 +249012,7 @@ function appStarterFiles(name) {
|
|
|
248876
249012
|
},
|
|
248877
249013
|
devDependencies: {
|
|
248878
249014
|
"@supacloud/compiler": `^${package_default.version}`,
|
|
249015
|
+
"drizzle-kit": package_default6.devDependencies["drizzle-kit"],
|
|
248879
249016
|
"@types/bun": "^1.4.2",
|
|
248880
249017
|
typescript: "^7.0.2"
|
|
248881
249018
|
}
|
|
@@ -248891,13 +249028,15 @@ function appStarterFiles(name) {
|
|
|
248891
249028
|
noEmit: true,
|
|
248892
249029
|
types: ["bun"]
|
|
248893
249030
|
},
|
|
248894
|
-
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"]
|
|
248895
249032
|
}),
|
|
248896
249033
|
"bunfig.toml": `env = false
|
|
248897
249034
|
`,
|
|
248898
249035
|
".gitignore": [
|
|
248899
249036
|
"node_modules/",
|
|
248900
249037
|
"dist/",
|
|
249038
|
+
"output/database-audit/",
|
|
249039
|
+
"db/migration-candidates/",
|
|
248901
249040
|
".env",
|
|
248902
249041
|
".env.*",
|
|
248903
249042
|
"!.env.*.example",
|
|
@@ -248986,7 +249125,7 @@ test("generated query client preserves its read contract without a live database
|
|
|
248986
249125
|
});
|
|
248987
249126
|
`,
|
|
248988
249127
|
"REACTIVE.md": STARTER_REACTIVE_GUIDE,
|
|
248989
|
-
"AGENTS.md": STARTER_REACTIVE_AGENTS,
|
|
249128
|
+
"AGENTS.md": STARTER_REACTIVE_AGENTS + STARTER_DATABASE_GUIDE,
|
|
248990
249129
|
"tests/reactive.test.ts": STARTER_REACTIVE_TEST,
|
|
248991
249130
|
"scripts/environment.ts": STARTER_ENVIRONMENT,
|
|
248992
249131
|
"tests/environment.test.ts": STARTER_ENVIRONMENT_TEST,
|
|
@@ -249088,6 +249227,7 @@ import { ApplicationError, assertFeatureTransition } from "@supacloud/elysia";
|
|
|
249088
249227
|
import { t } from "elysia";
|
|
249089
249228
|
import { VerifyReviewAttachment } from "./attachment";
|
|
249090
249229
|
import { ReviewUploads, ReviewUploadsController, type ReviewUploadPort } from "./uploads";
|
|
249230
|
+
import type { ReviewRow } from "../../db/schema";
|
|
249091
249231
|
|
|
249092
249232
|
export const reviewSpec = defineFeatureSpec({
|
|
249093
249233
|
name: "review",
|
|
@@ -249102,7 +249242,7 @@ export const reviewSpec = defineFeatureSpec({
|
|
|
249102
249242
|
},
|
|
249103
249243
|
});
|
|
249104
249244
|
|
|
249105
|
-
|
|
249245
|
+
type Review = Pick<ReviewRow, "state" | "version">;
|
|
249106
249246
|
// Production implementations must bind both operations to the current transaction.
|
|
249107
249247
|
export interface ReviewStore extends Partial<ReviewUploadPort> {
|
|
249108
249248
|
get(table: string, key: string): unknown;
|
|
@@ -249125,7 +249265,7 @@ export const requireRequest: Aspect = (context, next) => {
|
|
|
249125
249265
|
|
|
249126
249266
|
function readReview(value: unknown): Review {
|
|
249127
249267
|
if (typeof value !== "object" || value === null ||
|
|
249128
|
-
!("state" in value) ||
|
|
249268
|
+
!("state" in value) || (value.state !== "draft" && value.state !== "approved") ||
|
|
249129
249269
|
!("version" in value) || typeof value.version !== "number") {
|
|
249130
249270
|
throw new ApplicationError("Review not found", { status: 404, code: "REVIEW_NOT_FOUND" });
|
|
249131
249271
|
}
|
|
@@ -249252,6 +249392,7 @@ test("the production composition root rejects missing governance adapters", () =
|
|
|
249252
249392
|
|
|
249253
249393
|
\`\`\`sh
|
|
249254
249394
|
bun install
|
|
249395
|
+
bun run db:generate
|
|
249255
249396
|
bun run check
|
|
249256
249397
|
bun run dev
|
|
249257
249398
|
\`\`\`
|
|
@@ -249273,6 +249414,28 @@ compiler-checked command/route/governance bindings, an explicit AOP function,
|
|
|
249273
249414
|
HTTP schemas, permission denial, transaction rollback, idempotency and audit.
|
|
249274
249415
|
It is not a complete Maker-Checker workflow or a production authorization policy.
|
|
249275
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
|
+
|
|
249276
249439
|
## Typed Queries
|
|
249277
249440
|
|
|
249278
249441
|
This starter uses Database First as its only GraphQL server-schema model.
|
|
@@ -249541,6 +249704,8 @@ policy. Invalid transaction/idempotency modes fail compilation instead of
|
|
|
249541
249704
|
silently disabling governance. onExecution receives metadata-only trace events;
|
|
249542
249705
|
durable audit still belongs to the command governance adapter.
|
|
249543
249706
|
|
|
249707
|
+
${STARTER_DATABASE_GUIDE}
|
|
249708
|
+
|
|
249544
249709
|
## CI
|
|
249545
249710
|
|
|
249546
249711
|
After the first compilation, commit generated/. In CI run
|
|
@@ -249574,6 +249739,55 @@ async function initializeAppProject(options) {
|
|
|
249574
249739
|
return { root, name, files: Object.keys(files) };
|
|
249575
249740
|
}
|
|
249576
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
|
+
|
|
249577
249791
|
// src/shared/tools/app-resource.ts
|
|
249578
249792
|
function resourceScaffold(name, className) {
|
|
249579
249793
|
return {
|
|
@@ -249652,7 +249866,7 @@ test("${name} controller preserves an asynchronous read failure", async () => {
|
|
|
249652
249866
|
// src/shared/tools/app-scaffold-writes.ts
|
|
249653
249867
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
249654
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";
|
|
249655
|
-
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";
|
|
249656
249870
|
|
|
249657
249871
|
class ScaffoldError extends Error {
|
|
249658
249872
|
code;
|
|
@@ -249673,7 +249887,7 @@ function scaffoldPath(root, path) {
|
|
|
249673
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")) {
|
|
249674
249888
|
throw new ScaffoldError("SCAFFOLD_PATH_INVALID", "Scaffold path is reserved or not portable");
|
|
249675
249889
|
}
|
|
249676
|
-
const target =
|
|
249890
|
+
const target = resolve17(root, path);
|
|
249677
249891
|
const local = relative11(root, target);
|
|
249678
249892
|
if (!local || local === ".." || local.startsWith(`..${sep11}`) || isAbsolute6(local)) {
|
|
249679
249893
|
throw new ScaffoldError("SCAFFOLD_PATH_INVALID", "Scaffold path is outside the project root");
|
|
@@ -249685,7 +249899,7 @@ async function snapshot(root, path) {
|
|
|
249685
249899
|
const parts = relative11(root, target).split(sep11);
|
|
249686
249900
|
let current = root;
|
|
249687
249901
|
for (const [index, part] of parts.entries()) {
|
|
249688
|
-
current =
|
|
249902
|
+
current = join15(current, part);
|
|
249689
249903
|
let stat;
|
|
249690
249904
|
try {
|
|
249691
249905
|
stat = await lstat4(current);
|
|
@@ -249712,7 +249926,7 @@ function sameFile(a, b) {
|
|
|
249712
249926
|
return a === null ? b === null : b !== null && a.content === b.content && a.dev === b.dev && a.ino === b.ino && a.mode === b.mode;
|
|
249713
249927
|
}
|
|
249714
249928
|
async function planScaffoldWrites(root, writes) {
|
|
249715
|
-
const canonical = await realpath5(
|
|
249929
|
+
const canonical = await realpath5(resolve17(root));
|
|
249716
249930
|
const targets = new Set;
|
|
249717
249931
|
const planned = [];
|
|
249718
249932
|
for (const write of writes) {
|
|
@@ -249735,7 +249949,7 @@ async function planScaffoldWrites(root, writes) {
|
|
|
249735
249949
|
return Object.freeze({ root: canonical, writes: Object.freeze(planned) });
|
|
249736
249950
|
}
|
|
249737
249951
|
async function applyScaffoldWrites(plan) {
|
|
249738
|
-
const lockPath =
|
|
249952
|
+
const lockPath = join15(plan.root, ".supacloud-generate.lock");
|
|
249739
249953
|
const lock = await open4(lockPath, "wx").catch((error) => {
|
|
249740
249954
|
if (fsCode(error) === "EEXIST")
|
|
249741
249955
|
throw new ScaffoldError("SCAFFOLD_BUSY", "Another generator owns .supacloud-generate.lock; no files written");
|
|
@@ -249756,7 +249970,7 @@ async function applyScaffoldWrites(plan) {
|
|
|
249756
249970
|
const target = scaffoldPath(plan.root, write.path);
|
|
249757
249971
|
let current = plan.root;
|
|
249758
249972
|
for (const part of relative11(plan.root, dirname11(target)).split(sep11).filter(Boolean)) {
|
|
249759
|
-
current =
|
|
249973
|
+
current = join15(current, part);
|
|
249760
249974
|
try {
|
|
249761
249975
|
await mkdir5(current);
|
|
249762
249976
|
directories.push(current);
|
|
@@ -249865,6 +250079,7 @@ async function initProject(args) {
|
|
|
249865
250079
|
`Initialized ${name} in ${root} (${files.length} files, template: ${template}).`,
|
|
249866
250080
|
`cd '${root.replaceAll("'", "'\\''")}'`,
|
|
249867
250081
|
"bun install",
|
|
250082
|
+
...template === "command" ? ["bun run db:generate"] : [],
|
|
249868
250083
|
"bun run check",
|
|
249869
250084
|
"bun run dev",
|
|
249870
250085
|
"Local demo only. Production requires persistent governance and identity adapters; see README.md."
|
|
@@ -249971,7 +250186,7 @@ async function generateScaffold(args) {
|
|
|
249971
250186
|
throw new ScaffoldError("SCAFFOLD_OPTION_INVALID", `--${flag.replaceAll("_", "-")} must be boolean`);
|
|
249972
250187
|
}
|
|
249973
250188
|
}
|
|
249974
|
-
const root =
|
|
250189
|
+
const root = resolve18(args.root || process.cwd());
|
|
249975
250190
|
const dir = args.dir ?? "src/features";
|
|
249976
250191
|
if (dir !== ".")
|
|
249977
250192
|
scaffoldPath(root, dir);
|
|
@@ -249987,20 +250202,20 @@ async function generateScaffold(args) {
|
|
|
249987
250202
|
let contractName;
|
|
249988
250203
|
if (kind === "module" || kind === "resource") {
|
|
249989
250204
|
const name = requireIdentifier(args.name, "name");
|
|
249990
|
-
modulePath =
|
|
250205
|
+
modulePath = join16(dir, name, `${name}.module.ts`).replaceAll("\\", "/");
|
|
249991
250206
|
moduleSymbol = `${pascalName2(name)}Module`;
|
|
249992
250207
|
if (kind === "module")
|
|
249993
250208
|
writes.push({ path: modulePath, content: moduleScaffold(name), overwrite: args.force === true });
|
|
249994
250209
|
else
|
|
249995
250210
|
for (const [file, content] of Object.entries(resourceScaffold(name, pascalName2(name)))) {
|
|
249996
|
-
writes.push({ path:
|
|
250211
|
+
writes.push({ path: join16(dir, name, file).replaceAll("\\", "/"), content });
|
|
249997
250212
|
}
|
|
249998
250213
|
} else {
|
|
249999
250214
|
const moduleName = requireIdentifier(args.module, "module");
|
|
250000
250215
|
const name = kind === "controller" ? moduleName : requireIdentifier(args.name, "name");
|
|
250001
250216
|
const subdir = kind === "command" ? "commands" : kind === "query" ? "queries" : kind === "job" ? "jobs" : kind === "contract" ? "contracts" : "";
|
|
250002
|
-
const path =
|
|
250003
|
-
if (kind === "controller" &&
|
|
250217
|
+
const path = join16(dir, moduleName, subdir, `${name}.${kind}.ts`).replaceAll("\\", "/");
|
|
250218
|
+
if (kind === "controller" && existsSync12(scaffoldPath(root, path))) {
|
|
250004
250219
|
throw new ScaffoldError("SCAFFOLD_EXISTS", `Controller already exists: ${path}(请手工合并路由到现有 controller)`);
|
|
250005
250220
|
}
|
|
250006
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);
|
|
@@ -250018,7 +250233,7 @@ async function generateScaffold(args) {
|
|
|
250018
250233
|
if (new RegExp(`\\b${moduleSymbol}\\b`).test(original)) {
|
|
250019
250234
|
throw new ScaffoldError("SCAFFOLD_REGISTRATION_CONFLICT", `Registration target already references ${moduleSymbol}; review the import manually`);
|
|
250020
250235
|
}
|
|
250021
|
-
let importPath = relative12(dirname12(target),
|
|
250236
|
+
let importPath = relative12(dirname12(target), resolve18(root, modulePath)).replaceAll("\\", "/").replace(/\.ts$/, "");
|
|
250022
250237
|
if (!importPath.startsWith("."))
|
|
250023
250238
|
importPath = `./${importPath}`;
|
|
250024
250239
|
const edit = await applyDiagnosticFix({
|
|
@@ -250049,7 +250264,7 @@ async function generateScaffold(args) {
|
|
|
250049
250264
|
written: !args.dry_run,
|
|
250050
250265
|
changes
|
|
250051
250266
|
}, null, 2));
|
|
250052
|
-
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)}`);
|
|
250053
250268
|
if (args.dry_run)
|
|
250054
250269
|
lines.push("No files written. Omit --dry-run to apply this generation.");
|
|
250055
250270
|
if (kind === "resource")
|
|
@@ -250085,17 +250300,17 @@ function parseInclude(include) {
|
|
|
250085
250300
|
return patterns && patterns.length > 0 ? patterns : undefined;
|
|
250086
250301
|
}
|
|
250087
250302
|
function sourceRoot(args, root, configured) {
|
|
250088
|
-
const hasConfig = ["ts", "mts", "js", "mjs"].some((extension) =>
|
|
250303
|
+
const hasConfig = ["ts", "mts", "js", "mjs"].some((extension) => existsSync12(join16(root, `supacloud.config.${extension}`)));
|
|
250089
250304
|
return args.root && !hasConfig ? "." : configured;
|
|
250090
250305
|
}
|
|
250091
250306
|
async function projectCompileConfig(args) {
|
|
250092
|
-
const root =
|
|
250093
|
-
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;
|
|
250094
250309
|
const loadedConfig = await loadSupacloudConfig(root);
|
|
250095
250310
|
const defaults = resolveSupacloudConfig(loadedConfig, root);
|
|
250096
250311
|
const configuredRoot = sourceRoot(args, root, loadedConfig.root);
|
|
250097
250312
|
const include = parseInclude(args.include) ?? loadedConfig.include;
|
|
250098
|
-
const outDir = args.out_dir ?
|
|
250313
|
+
const outDir = args.out_dir ? resolve18(root, args.out_dir) : defaults.outDir;
|
|
250099
250314
|
const result = await checkProject(compileOptionsFromConfig({
|
|
250100
250315
|
...loadedConfig,
|
|
250101
250316
|
...configuredRoot === undefined ? {} : { root: configuredRoot },
|
|
@@ -250106,8 +250321,8 @@ async function projectCompileConfig(args) {
|
|
|
250106
250321
|
return { root, configFile, outDir, result };
|
|
250107
250322
|
}
|
|
250108
250323
|
async function readAllowedDependencies(root) {
|
|
250109
|
-
const path =
|
|
250110
|
-
if (!
|
|
250324
|
+
const path = join16(root, "package.json");
|
|
250325
|
+
if (!existsSync12(path))
|
|
250111
250326
|
return { dependencies: [], devDependencies: [] };
|
|
250112
250327
|
try {
|
|
250113
250328
|
const parsed = JSON.parse(await readFile8(path, "utf8"));
|
|
@@ -250194,7 +250409,7 @@ async function findUnwiredContracts(rootDir) {
|
|
|
250194
250409
|
for (const entry of entries) {
|
|
250195
250410
|
if (skip.has(entry.name))
|
|
250196
250411
|
continue;
|
|
250197
|
-
const path =
|
|
250412
|
+
const path = join16(directory, entry.name);
|
|
250198
250413
|
if (entry.isDirectory())
|
|
250199
250414
|
await walk(path);
|
|
250200
250415
|
else if (entry.isFile() && entry.name.endsWith(".ts"))
|
|
@@ -250222,6 +250437,12 @@ async function findUnwiredContracts(rootDir) {
|
|
|
250222
250437
|
async function runDoctor(args) {
|
|
250223
250438
|
const { root, outDir, result } = await projectCompileConfig(args);
|
|
250224
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
|
+
}
|
|
250225
250446
|
const fixPlan = doctorFixPlan(doctor);
|
|
250226
250447
|
const autoFixable = fixPlan.filter((repair) => repair.readiness === "preview").length;
|
|
250227
250448
|
const inputRequired = fixPlan.filter((repair) => repair.readiness === "input-required").length;
|
|
@@ -250252,11 +250473,11 @@ async function runDoctor(args) {
|
|
|
250252
250473
|
`), doctor.errors > 0);
|
|
250253
250474
|
}
|
|
250254
250475
|
async function runFix(args) {
|
|
250255
|
-
const root =
|
|
250476
|
+
const root = resolve18(args.root || process.cwd());
|
|
250256
250477
|
const fixPath = args.fix?.trim();
|
|
250257
250478
|
if (!fixPath)
|
|
250258
250479
|
throw new Error("app fix requires --fix <path-to-diagnostic-fix.json>");
|
|
250259
|
-
const parsed = JSON.parse(await readFile8(
|
|
250480
|
+
const parsed = JSON.parse(await readFile8(resolve18(root, fixPath), "utf8"));
|
|
250260
250481
|
const write = args.write === true;
|
|
250261
250482
|
const applied = await applyDiagnosticFix(parsed, { rootDir: root, dryRun: !write });
|
|
250262
250483
|
return textResult(JSON.stringify({
|
|
@@ -250299,7 +250520,7 @@ function devDatabase(profile, explicit) {
|
|
|
250299
250520
|
}
|
|
250300
250521
|
function readManifestModules(outDir) {
|
|
250301
250522
|
try {
|
|
250302
|
-
const parsed = JSON.parse(readFileSync9(
|
|
250523
|
+
const parsed = JSON.parse(readFileSync9(join16(outDir, "app.manifest.json"), "utf8"));
|
|
250303
250524
|
return Array.isArray(parsed.modules) ? parsed.modules.map((module) => module.name) : [];
|
|
250304
250525
|
} catch {
|
|
250305
250526
|
return [];
|
|
@@ -250332,12 +250553,12 @@ async function runAppDev(args, options) {
|
|
|
250332
250553
|
throw new ScaffoldError("SCAFFOLD_OPTION_INVALID", "app dev --profile must be fast or integration");
|
|
250333
250554
|
}
|
|
250334
250555
|
const database = devDatabase(profile, args.database_url);
|
|
250335
|
-
const root =
|
|
250556
|
+
const root = resolve18(args.root || process.cwd());
|
|
250336
250557
|
const loadedConfig = await loadSupacloudConfig(root);
|
|
250337
250558
|
const defaults = resolveSupacloudConfig(loadedConfig, root);
|
|
250338
250559
|
const configuredRoot = sourceRoot(args, root, loadedConfig.root);
|
|
250339
250560
|
const include = parseInclude(args.include) ?? loadedConfig.include;
|
|
250340
|
-
const outDir = args.out_dir ?
|
|
250561
|
+
const outDir = args.out_dir ? resolve18(root, args.out_dir) : defaults.outDir;
|
|
250341
250562
|
const watch = args.once !== true && args.watch !== false;
|
|
250342
250563
|
const compileOptions = compileOptionsFromConfig({
|
|
250343
250564
|
...loadedConfig,
|
|
@@ -250423,7 +250644,7 @@ async function runAppDev(args, options) {
|
|
|
250423
250644
|
return report(lastEvent, "stopped");
|
|
250424
250645
|
}
|
|
250425
250646
|
async function runCompile(args) {
|
|
250426
|
-
const root =
|
|
250647
|
+
const root = resolve18(args.root || process.cwd());
|
|
250427
250648
|
const loadedConfig = await loadSupacloudConfig(root);
|
|
250428
250649
|
const defaults = resolveSupacloudConfig(loadedConfig, root);
|
|
250429
250650
|
const configuredRoot = sourceRoot(args, root, loadedConfig.root);
|
|
@@ -250432,7 +250653,7 @@ async function runCompile(args) {
|
|
|
250432
250653
|
...compileOptionsFromConfig({
|
|
250433
250654
|
...loadedConfig,
|
|
250434
250655
|
...configuredRoot === undefined ? {} : { root: configuredRoot },
|
|
250435
|
-
outDir: args.out_dir ?
|
|
250656
|
+
outDir: args.out_dir ? resolve18(root, args.out_dir) : defaults.outDir,
|
|
250436
250657
|
...include === undefined ? {} : { include },
|
|
250437
250658
|
strict: args.strict ?? loadedConfig.strict ?? false
|
|
250438
250659
|
}, root),
|
|
@@ -250456,7 +250677,8 @@ async function runCompile(args) {
|
|
|
250456
250677
|
return textResult(text, hasError);
|
|
250457
250678
|
}
|
|
250458
250679
|
async function runCheck(args) {
|
|
250459
|
-
const root =
|
|
250680
|
+
const root = resolve18(args.root || process.cwd());
|
|
250681
|
+
const database = checkAppDatabaseSources(root);
|
|
250460
250682
|
const loadedConfig = await loadSupacloudConfig(root);
|
|
250461
250683
|
const defaults = resolveSupacloudConfig(loadedConfig, root);
|
|
250462
250684
|
const configuredRoot = sourceRoot(args, root, loadedConfig.root);
|
|
@@ -250464,12 +250686,12 @@ async function runCheck(args) {
|
|
|
250464
250686
|
const config = compileOptionsFromConfig({
|
|
250465
250687
|
...loadedConfig,
|
|
250466
250688
|
...configuredRoot === undefined ? {} : { root: configuredRoot },
|
|
250467
|
-
outDir: args.out_dir ?
|
|
250689
|
+
outDir: args.out_dir ? resolve18(root, args.out_dir) : defaults.outDir,
|
|
250468
250690
|
...include === undefined ? {} : { include },
|
|
250469
250691
|
strict: args.strict ?? loadedConfig.strict ?? false
|
|
250470
250692
|
}, root);
|
|
250471
250693
|
const result = await checkProject(config);
|
|
250472
|
-
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;
|
|
250473
250695
|
if (args.format === "json")
|
|
250474
250696
|
return textResult(JSON.stringify({
|
|
250475
250697
|
version: 1,
|
|
@@ -250479,12 +250701,14 @@ async function runCheck(args) {
|
|
|
250479
250701
|
mismatches: result.mismatches,
|
|
250480
250702
|
upToDate: result.upToDate,
|
|
250481
250703
|
written: [],
|
|
250482
|
-
modules: result.graph.modules.map((module) => module.name)
|
|
250704
|
+
modules: result.graph.modules.map((module) => module.name),
|
|
250705
|
+
...database ? { database } : {}
|
|
250483
250706
|
}, null, 2), hasError);
|
|
250484
250707
|
const summary = `checked ${result.graph.modules.length} module(s), no files written`;
|
|
250485
250708
|
return textResult([
|
|
250486
250709
|
formatDiagnostics(result.diagnostics),
|
|
250487
250710
|
...result.mismatches.map((path) => `generated artifact mismatch: ${path}`),
|
|
250711
|
+
...database ? [`${database.ok ? "OK" : "FAIL"} ${database.name}: ${database.detail}`] : [],
|
|
250488
250712
|
"",
|
|
250489
250713
|
summary
|
|
250490
250714
|
].join(`
|
|
@@ -250492,9 +250716,9 @@ async function runCheck(args) {
|
|
|
250492
250716
|
}
|
|
250493
250717
|
async function readManifest(root, args) {
|
|
250494
250718
|
const config = await loadSupacloudConfig(root);
|
|
250495
|
-
const outDir = args.out_dir ?
|
|
250496
|
-
const manifestPath =
|
|
250497
|
-
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)) {
|
|
250498
250722
|
throw new Error(`Manifest not found: ${manifestPath}(先运行 app compile 生成)`);
|
|
250499
250723
|
}
|
|
250500
250724
|
const parsed = JSON.parse(await readFile8(manifestPath, "utf8"));
|
|
@@ -250537,7 +250761,7 @@ function formatGraphText(manifest) {
|
|
|
250537
250761
|
`);
|
|
250538
250762
|
}
|
|
250539
250763
|
async function runGraph(args) {
|
|
250540
|
-
const root =
|
|
250764
|
+
const root = resolve18(args.root || process.cwd());
|
|
250541
250765
|
const manifest = await readManifest(root, args);
|
|
250542
250766
|
if (args.format === "json")
|
|
250543
250767
|
return textResult(JSON.stringify(manifest, null, 2));
|
|
@@ -250605,7 +250829,7 @@ async function runExplain(args) {
|
|
|
250605
250829
|
const target = args.target?.trim();
|
|
250606
250830
|
if (!target)
|
|
250607
250831
|
throw new Error("app explain requires --target(provider 类名 / token 名 / command 名 / job 名)");
|
|
250608
|
-
const root =
|
|
250832
|
+
const root = resolve18(args.root || process.cwd());
|
|
250609
250833
|
const manifest = await readManifest(root, args);
|
|
250610
250834
|
for (const module of manifest.modules) {
|
|
250611
250835
|
const job = module.jobs?.find((entry) => entry.name === target || entry.className === target);
|
|
@@ -250680,16 +250904,16 @@ async function runExplain(args) {
|
|
|
250680
250904
|
return textResult(`未找到对象: ${target}`, true);
|
|
250681
250905
|
}
|
|
250682
250906
|
async function runExportTools(args) {
|
|
250683
|
-
const root =
|
|
250907
|
+
const root = resolve18(args.root || process.cwd());
|
|
250684
250908
|
const manifest = await readManifest(root, { ...args, out_dir: undefined });
|
|
250685
250909
|
const definitions = buildToolDefinitions(manifest);
|
|
250686
|
-
const outDir =
|
|
250910
|
+
const outDir = resolve18(args.out_dir || join16(root, "generated"));
|
|
250687
250911
|
if (args.format === "json") {
|
|
250688
250912
|
return textResult(JSON.stringify(definitions, null, 2));
|
|
250689
250913
|
}
|
|
250690
250914
|
await mkdir6(outDir, { recursive: true });
|
|
250691
|
-
const openaiPath =
|
|
250692
|
-
const mcpPath =
|
|
250915
|
+
const openaiPath = join16(outDir, "tool-definitions.openai.json");
|
|
250916
|
+
const mcpPath = join16(outDir, "tool-definitions.mcp.json");
|
|
250693
250917
|
await writeFile5(openaiPath, JSON.stringify(definitions.openai, null, 2), "utf8");
|
|
250694
250918
|
await writeFile5(mcpPath, JSON.stringify(definitions.mcp, null, 2), "utf8");
|
|
250695
250919
|
const summary = [
|
|
@@ -250704,11 +250928,17 @@ async function runExportTools(args) {
|
|
|
250704
250928
|
return textResult(summary);
|
|
250705
250929
|
}
|
|
250706
250930
|
async function runDelivery(args) {
|
|
250707
|
-
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
|
+
}
|
|
250708
250938
|
const loaded = await loadSupacloudConfig(root);
|
|
250709
250939
|
const options = compileOptionsFromConfig({
|
|
250710
250940
|
...loaded,
|
|
250711
|
-
...args.out_dir === undefined ? {} : { outDir:
|
|
250941
|
+
...args.out_dir === undefined ? {} : { outDir: resolve18(root, args.out_dir) },
|
|
250712
250942
|
...args.include === undefined ? {} : { include: parseInclude(args.include) },
|
|
250713
250943
|
...args.strict === undefined ? {} : { strict: args.strict }
|
|
250714
250944
|
}, root);
|
|
@@ -250866,8 +251096,8 @@ function registerAppAliases(server2) {
|
|
|
250866
251096
|
}
|
|
250867
251097
|
|
|
250868
251098
|
// src/shared/tools/db-governance-tools.ts
|
|
250869
|
-
import { existsSync as
|
|
250870
|
-
import { join as
|
|
251099
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
251100
|
+
import { join as join17, resolve as resolve20 } from "node:path";
|
|
250871
251101
|
|
|
250872
251102
|
// node_modules/@supacloud/db/dist/index.js
|
|
250873
251103
|
var TABLES_SQL = `
|
|
@@ -251319,8 +251549,8 @@ function normalizeModule(candidate, sourcePath) {
|
|
|
251319
251549
|
};
|
|
251320
251550
|
}
|
|
251321
251551
|
async function loadDatabaseModules(root, moduleFile) {
|
|
251322
|
-
const filePath = moduleFile ?
|
|
251323
|
-
if (!
|
|
251552
|
+
const filePath = moduleFile ? resolve20(root, moduleFile) : join17(root, "db", "modules.ts");
|
|
251553
|
+
if (!existsSync13(filePath)) {
|
|
251324
251554
|
throw new Error(moduleFile ? `Module file not found: ${filePath}` : `Module file not found: ${filePath}(用 --module_file 指定一个导出 defineDatabaseModule(...) 的文件)`);
|
|
251325
251555
|
}
|
|
251326
251556
|
let imported;
|
|
@@ -251368,7 +251598,7 @@ async function runLint(args, root) {
|
|
|
251368
251598
|
const issues = [];
|
|
251369
251599
|
for (const module of selected) {
|
|
251370
251600
|
const readSqlFile = async (path) => {
|
|
251371
|
-
const absolute =
|
|
251601
|
+
const absolute = resolve20(root, path);
|
|
251372
251602
|
try {
|
|
251373
251603
|
return await Bun.file(absolute).text();
|
|
251374
251604
|
} catch (error) {
|
|
@@ -251404,14 +251634,14 @@ var defaultLiteSpawn = async (command, cwd) => {
|
|
|
251404
251634
|
return { exitCode, stdout, stderr };
|
|
251405
251635
|
};
|
|
251406
251636
|
async function runModuleCheckLite(args, root, spawn, liteBinary) {
|
|
251407
|
-
const projectDir =
|
|
251637
|
+
const projectDir = resolve20(args.project_dir?.trim() || root);
|
|
251408
251638
|
const binary = liteBinary === undefined ? Bun.which("supacloud-lite") : liteBinary;
|
|
251409
251639
|
if (!binary) {
|
|
251410
251640
|
throw new Error("db module_check --lite 需要本机可用的 supacloud-lite(npm i -g @supacloud/lite,或加入 PATH)");
|
|
251411
251641
|
}
|
|
251412
251642
|
const command = [binary, "db", "check", "--project-dir", projectDir];
|
|
251413
251643
|
if (args.module_file?.trim())
|
|
251414
|
-
command.push("--module-file",
|
|
251644
|
+
command.push("--module-file", resolve20(root, args.module_file.trim()));
|
|
251415
251645
|
const result = await spawn(command, projectDir);
|
|
251416
251646
|
const output = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join(`
|
|
251417
251647
|
`);
|
|
@@ -251462,7 +251692,7 @@ function registerDbGovernanceTools(server2, options = {}) {
|
|
|
251462
251692
|
lite: optional(Type.Boolean(), "[module_check] Reconcile against a local SupaCloud Lite project (runs supacloud-lite db check)"),
|
|
251463
251693
|
project_dir: optional(Type.String(), "[module_check] Lite project directory (with --lite; default: --root)")
|
|
251464
251694
|
}, async (request) => {
|
|
251465
|
-
const root =
|
|
251695
|
+
const root = resolve20(request.root || fallbackRoot);
|
|
251466
251696
|
switch (request.action) {
|
|
251467
251697
|
case "lint":
|
|
251468
251698
|
return runLint(request, root);
|
|
@@ -251479,7 +251709,7 @@ function registerDbGovernanceTools(server2, options = {}) {
|
|
|
251479
251709
|
// src/shared/tools/scheduled-function-tools.ts
|
|
251480
251710
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
251481
251711
|
import { readFileSync as readFileSync10, statSync as statSync7 } from "node:fs";
|
|
251482
|
-
import { resolve as
|
|
251712
|
+
import { resolve as resolve21 } from "node:path";
|
|
251483
251713
|
import { isDeepStrictEqual } from "node:util";
|
|
251484
251714
|
var HEADER_NAME_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$/;
|
|
251485
251715
|
var ENVIRONMENT_NAME_PATTERN2 = /^[A-Za-z_][A-Za-z0-9_]{0,255}$/;
|
|
@@ -251563,7 +251793,7 @@ function validScheduledFunctionCron(expression) {
|
|
|
251563
251793
|
function readScheduleBodyFile(bodyPathInput) {
|
|
251564
251794
|
if (!bodyPathInput.trim())
|
|
251565
251795
|
throw new Error("'body_file' must be a path");
|
|
251566
|
-
const bodyPath =
|
|
251796
|
+
const bodyPath = resolve21(bodyPathInput);
|
|
251567
251797
|
const bodyStat = statSync7(bodyPath);
|
|
251568
251798
|
if (!bodyStat.isFile() || bodyStat.size > MAX_BODY_FILE_BYTES) {
|
|
251569
251799
|
throw new Error("Scheduled Function body file must be a regular file no larger than 1 MiB");
|
|
@@ -252146,8 +252376,8 @@ var MUTATION_TOOL_SCHEMA = {
|
|
|
252146
252376
|
// src/shared/tools/release-tools.ts
|
|
252147
252377
|
import { execFileSync } from "node:child_process";
|
|
252148
252378
|
import { createHash as createHash10 } from "node:crypto";
|
|
252149
|
-
import { existsSync as
|
|
252150
|
-
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";
|
|
252151
252381
|
var SAFE_PROJECT_REF = /^[A-Za-z0-9_-]{1,64}$/;
|
|
252152
252382
|
var BACKUP_ID = /^logical-full_[A-Za-z0-9_-]{1,64}_[a-f0-9]{32}$/;
|
|
252153
252383
|
var SHA256 = /^[a-f0-9]{64}$/;
|
|
@@ -252441,15 +252671,15 @@ function collectScopeFiles(cwd, file, files, dir, task) {
|
|
|
252441
252671
|
const results = [];
|
|
252442
252672
|
const resolvePathWithFallbacks = (filePath) => {
|
|
252443
252673
|
const trimmed = filePath.trim();
|
|
252444
|
-
const resolved =
|
|
252445
|
-
if (
|
|
252674
|
+
const resolved = resolve23(cwd, trimmed);
|
|
252675
|
+
if (existsSync14(resolved))
|
|
252446
252676
|
return resolved;
|
|
252447
252677
|
const candidates = [
|
|
252448
|
-
|
|
252449
|
-
|
|
252450
|
-
|
|
252678
|
+
resolve23(cwd, "supacloud/fa/release-scopes", trimmed),
|
|
252679
|
+
resolve23(cwd, "release-scopes", trimmed),
|
|
252680
|
+
resolve23(cwd, "scopes", trimmed)
|
|
252451
252681
|
];
|
|
252452
|
-
const found = candidates.find(
|
|
252682
|
+
const found = candidates.find(existsSync14);
|
|
252453
252683
|
if (found)
|
|
252454
252684
|
return found;
|
|
252455
252685
|
throw new Error(`Scope file not found: ${filePath}`);
|
|
@@ -252470,18 +252700,18 @@ function collectScopeFiles(cwd, file, files, dir, task) {
|
|
|
252470
252700
|
if (!taskFilter && (!dir || typeof dir !== "string" || !dir.trim())) {
|
|
252471
252701
|
throw new Error("At least one of --file, --files, or --task is required for scope_rebind");
|
|
252472
252702
|
}
|
|
252473
|
-
const candidates = typeof dir === "string" && dir.trim() ? [
|
|
252474
|
-
|
|
252475
|
-
|
|
252476
|
-
|
|
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")
|
|
252477
252707
|
];
|
|
252478
252708
|
for (const directory of candidates) {
|
|
252479
|
-
if (
|
|
252709
|
+
if (existsSync14(directory)) {
|
|
252480
252710
|
try {
|
|
252481
252711
|
const entries = readdirSync4(directory, { withFileTypes: true });
|
|
252482
252712
|
for (const entry of entries) {
|
|
252483
252713
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
252484
|
-
const fullPath =
|
|
252714
|
+
const fullPath = join18(directory, entry.name);
|
|
252485
252715
|
if (!taskFilter) {
|
|
252486
252716
|
results.push(fullPath);
|
|
252487
252717
|
} else if (entry.name.toLowerCase().includes(taskFilter)) {
|
|
@@ -252507,7 +252737,7 @@ function collectScopeFiles(cwd, file, files, dir, task) {
|
|
|
252507
252737
|
return [...new Set(results)];
|
|
252508
252738
|
}
|
|
252509
252739
|
function handleScopeRebind(args) {
|
|
252510
|
-
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();
|
|
252511
252741
|
const targetBaseCommit = resolveGitBaseCommit(cwd, args.base_commit);
|
|
252512
252742
|
const scopeFiles = collectScopeFiles(cwd, args.file, args.files, args.dir, args.task);
|
|
252513
252743
|
const dryRun = args.dry_run === true;
|
|
@@ -252559,19 +252789,19 @@ function handleScopeRebind(args) {
|
|
|
252559
252789
|
});
|
|
252560
252790
|
}
|
|
252561
252791
|
function handleScopeInspect(args) {
|
|
252562
|
-
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();
|
|
252563
252793
|
if (typeof args.file !== "string" || !args.file.trim()) {
|
|
252564
252794
|
throw new Error("'file' is required for scope_inspect");
|
|
252565
252795
|
}
|
|
252566
252796
|
const trimmed = args.file.trim();
|
|
252567
|
-
let filePath =
|
|
252568
|
-
if (!
|
|
252797
|
+
let filePath = resolve23(cwd, trimmed);
|
|
252798
|
+
if (!existsSync14(filePath)) {
|
|
252569
252799
|
const candidates = [
|
|
252570
|
-
|
|
252571
|
-
|
|
252572
|
-
|
|
252800
|
+
resolve23(cwd, "supacloud/fa/release-scopes", trimmed),
|
|
252801
|
+
resolve23(cwd, "release-scopes", trimmed),
|
|
252802
|
+
resolve23(cwd, "scopes", trimmed)
|
|
252573
252803
|
];
|
|
252574
|
-
const found = candidates.find(
|
|
252804
|
+
const found = candidates.find(existsSync14);
|
|
252575
252805
|
if (found)
|
|
252576
252806
|
filePath = found;
|
|
252577
252807
|
else
|
|
@@ -252629,7 +252859,7 @@ function parseCommaList(value) {
|
|
|
252629
252859
|
return [];
|
|
252630
252860
|
}
|
|
252631
252861
|
function handleScopeCreate(args, fallbackRef) {
|
|
252632
|
-
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();
|
|
252633
252863
|
if (typeof args.file !== "string" || !args.file.trim()) {
|
|
252634
252864
|
throw new Error("'file' is required for scope_create");
|
|
252635
252865
|
}
|
|
@@ -252644,7 +252874,7 @@ function handleScopeCreate(args, fallbackRef) {
|
|
|
252644
252874
|
throw new Error("'ref' is invalid for release controls");
|
|
252645
252875
|
}
|
|
252646
252876
|
const targetBaseCommit = resolveGitBaseCommit(cwd, args.base_commit);
|
|
252647
|
-
const filePath =
|
|
252877
|
+
const filePath = resolve23(cwd, args.file.trim());
|
|
252648
252878
|
mkdirSync4(dirname13(filePath), { recursive: true });
|
|
252649
252879
|
const scope = {
|
|
252650
252880
|
baseCommit: targetBaseCommit,
|
|
@@ -252869,7 +253099,7 @@ function registerReleaseTools(server2, http, options = {}) {
|
|
|
252869
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";
|
|
252870
253100
|
import { createHash as createHash11 } from "node:crypto";
|
|
252871
253101
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
252872
|
-
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";
|
|
252873
253103
|
import { spawn as spawn3 } from "node:child_process";
|
|
252874
253104
|
|
|
252875
253105
|
// node_modules/fflate/esm/index.mjs
|
|
@@ -253561,13 +253791,13 @@ async function readJson(path) {
|
|
|
253561
253791
|
}
|
|
253562
253792
|
}
|
|
253563
253793
|
async function findDeployConfigRoot(startDirectory) {
|
|
253564
|
-
let directory =
|
|
253794
|
+
let directory = resolve24(startDirectory);
|
|
253565
253795
|
while (true) {
|
|
253566
|
-
if (await pathExists(
|
|
253796
|
+
if (await pathExists(join19(directory, "supacloud.json")))
|
|
253567
253797
|
return directory;
|
|
253568
253798
|
const parent = dirname14(directory);
|
|
253569
253799
|
if (parent === directory)
|
|
253570
|
-
return
|
|
253800
|
+
return resolve24(startDirectory);
|
|
253571
253801
|
directory = parent;
|
|
253572
253802
|
}
|
|
253573
253803
|
}
|
|
@@ -253610,7 +253840,7 @@ function deployTargetConfig(candidate, name) {
|
|
|
253610
253840
|
};
|
|
253611
253841
|
}
|
|
253612
253842
|
async function readDeployConfig(configRoot) {
|
|
253613
|
-
const configPath =
|
|
253843
|
+
const configPath = join19(configRoot, "supacloud.json");
|
|
253614
253844
|
const candidate = await readJson(configPath);
|
|
253615
253845
|
if (candidate === null)
|
|
253616
253846
|
return {};
|
|
@@ -253632,25 +253862,25 @@ async function readDeployConfig(configRoot) {
|
|
|
253632
253862
|
};
|
|
253633
253863
|
}
|
|
253634
253864
|
async function packageName(projectDirectory) {
|
|
253635
|
-
const candidate = record4(await readJson(
|
|
253865
|
+
const candidate = record4(await readJson(join19(projectDirectory, "package.json")));
|
|
253636
253866
|
return typeof candidate?.name === "string" && candidate.name.trim() ? candidate.name.trim() : null;
|
|
253637
253867
|
}
|
|
253638
253868
|
async function pathExists(path) {
|
|
253639
253869
|
return access4(path).then(() => true, () => false);
|
|
253640
253870
|
}
|
|
253641
253871
|
async function defaultBuildCommand(projectDirectory, configRoot) {
|
|
253642
|
-
const packageJson = record4(await readJson(
|
|
253872
|
+
const packageJson = record4(await readJson(join19(projectDirectory, "package.json")));
|
|
253643
253873
|
const scripts = record4(packageJson?.scripts);
|
|
253644
253874
|
if (typeof scripts?.build !== "string" || !scripts.build.trim()) {
|
|
253645
253875
|
throw new Error("No build command was configured and package.json has no build script");
|
|
253646
253876
|
}
|
|
253647
253877
|
let directory = projectDirectory;
|
|
253648
253878
|
while (true) {
|
|
253649
|
-
if (await pathExists(
|
|
253879
|
+
if (await pathExists(join19(directory, "bun.lock")) || await pathExists(join19(directory, "bun.lockb")))
|
|
253650
253880
|
return "bun run build";
|
|
253651
|
-
if (await pathExists(
|
|
253881
|
+
if (await pathExists(join19(directory, "pnpm-lock.yaml")))
|
|
253652
253882
|
return "pnpm run build";
|
|
253653
|
-
if (await pathExists(
|
|
253883
|
+
if (await pathExists(join19(directory, "yarn.lock")))
|
|
253654
253884
|
return "yarn build";
|
|
253655
253885
|
if (directory === configRoot)
|
|
253656
253886
|
break;
|
|
@@ -253685,9 +253915,9 @@ function selectDeployment(deployments, requestedId, projectName) {
|
|
|
253685
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`);
|
|
253686
253916
|
}
|
|
253687
253917
|
function targetRoot(configRoot, target, name) {
|
|
253688
|
-
const root =
|
|
253689
|
-
const prefix = `${
|
|
253690
|
-
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)) {
|
|
253691
253921
|
throw new Error(`Deploy target '${name}' root must stay inside the repository root`);
|
|
253692
253922
|
}
|
|
253693
253923
|
return root;
|
|
@@ -253738,8 +253968,8 @@ function defaultOutputDirectory(framework) {
|
|
|
253738
253968
|
}
|
|
253739
253969
|
}
|
|
253740
253970
|
function resolveInsideProject(projectDirectory, candidate, allowRoot = false) {
|
|
253741
|
-
const resolved =
|
|
253742
|
-
const projectRoot =
|
|
253971
|
+
const resolved = resolve24(projectDirectory, candidate);
|
|
253972
|
+
const projectRoot = resolve24(projectDirectory);
|
|
253743
253973
|
const projectPrefix = `${projectRoot}${sep12}`;
|
|
253744
253974
|
if (!allowRoot && resolved === projectRoot || resolved !== projectRoot && !resolved.startsWith(projectPrefix)) {
|
|
253745
253975
|
throw new Error("Frontend output directory must stay inside the project directory");
|
|
@@ -253770,7 +254000,7 @@ async function collectArchiveFiles(root) {
|
|
|
253770
254000
|
entries.push(entry);
|
|
253771
254001
|
entries.sort((left, right) => left.name.localeCompare(right.name, "en"));
|
|
253772
254002
|
for (const entry of entries) {
|
|
253773
|
-
const path =
|
|
254003
|
+
const path = join19(directory, entry.name);
|
|
253774
254004
|
const stats = await lstat5(path);
|
|
253775
254005
|
if (stats.isSymbolicLink())
|
|
253776
254006
|
throw new Error(`Frontend output cannot contain symbolic links: ${path}`);
|
|
@@ -253799,8 +254029,8 @@ async function collectArchiveFiles(root) {
|
|
|
253799
254029
|
}
|
|
253800
254030
|
async function createFrontendArchive(outputDirectory) {
|
|
253801
254031
|
const collected = await collectArchiveFiles(outputDirectory);
|
|
253802
|
-
const temporaryDirectory = await mkdtemp2(
|
|
253803
|
-
const archivePath =
|
|
254032
|
+
const temporaryDirectory = await mkdtemp2(join19(tmpdir2(), "supacloud-deploy-"));
|
|
254033
|
+
const archivePath = join19(temporaryDirectory, `${basename9(outputDirectory)}.zip`);
|
|
253804
254034
|
try {
|
|
253805
254035
|
const archiveBytes = zipSync(collected.files, { level: 6, mtime: FIXED_ZIP_MTIME });
|
|
253806
254036
|
await writeFile6(archivePath, archiveBytes);
|
|
@@ -253850,7 +254080,7 @@ function deployResponse(result, json) {
|
|
|
253850
254080
|
}
|
|
253851
254081
|
function registerDeployTools(server2, http, options = {}) {
|
|
253852
254082
|
server2.tool("deploy", "Build and publish the linked frontend with one command", deployToolSchema, async (args) => {
|
|
253853
|
-
const invocationDirectory =
|
|
254083
|
+
const invocationDirectory = resolve24(String(args.cwd || options.cwd || process.cwd()));
|
|
253854
254084
|
const projectRef = String(args.ref || options.projectRef || "").trim();
|
|
253855
254085
|
if (!projectRef)
|
|
253856
254086
|
throw new Error("A linked project ref is required. Set SUPACLOUD_PROJECT_REF or pass --ref");
|
|
@@ -254023,11 +254253,11 @@ function registerDeployTools(server2, http, options = {}) {
|
|
|
254023
254253
|
|
|
254024
254254
|
// src/shared/tools/remote-dev-tools.ts
|
|
254025
254255
|
import { spawn as spawn4 } from "node:child_process";
|
|
254026
|
-
import { existsSync as
|
|
254256
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
254027
254257
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
254028
254258
|
import { readdir as readdir6 } from "node:fs/promises";
|
|
254029
254259
|
import { createHash as createHash12 } from "node:crypto";
|
|
254030
|
-
import { join as
|
|
254260
|
+
import { join as join20, resolve as resolve25 } from "node:path";
|
|
254031
254261
|
var SAFE_TOKEN = /^[A-Za-z0-9._:@/+,-]+$/;
|
|
254032
254262
|
var SAFE_REMOTE_ROOT = /^\/(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+\/?$/;
|
|
254033
254263
|
var remoteDevToolSchema = {
|
|
@@ -254069,21 +254299,21 @@ function runProcess(executable, args, cwd) {
|
|
|
254069
254299
|
function resolveDrizzleCommand(root, configured) {
|
|
254070
254300
|
if (configured?.trim())
|
|
254071
254301
|
return configured.trim();
|
|
254072
|
-
const local =
|
|
254073
|
-
return
|
|
254302
|
+
const local = join20(root, "node_modules", ".bin", "drizzle-kit");
|
|
254303
|
+
return existsSync15(local) ? local : "drizzle-kit";
|
|
254074
254304
|
}
|
|
254075
254305
|
function toolFailed(value) {
|
|
254076
254306
|
return value?.isError === true || value?.content?.some((chunk) => typeof chunk?.text === "string" && chunk.text.trimStart().startsWith("❌"));
|
|
254077
254307
|
}
|
|
254078
254308
|
async function migrateDatabase(args, options) {
|
|
254079
|
-
const root =
|
|
254309
|
+
const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
|
|
254080
254310
|
const projectConfig = await readProjectConfig3(root);
|
|
254081
254311
|
const config = projectConfig.dev || {};
|
|
254082
254312
|
const database = config.database || {};
|
|
254083
254313
|
const execute = options.execute || ((command, commandArgs, cwd) => runProcess(command, commandArgs, cwd));
|
|
254084
|
-
const drizzleConfig =
|
|
254314
|
+
const drizzleConfig = resolve25(root, String(args.drizzle_config || database.drizzleConfig || "drizzle.config.ts"));
|
|
254085
254315
|
const migrationsDir = String(args.migrations_dir || database.migrationsDir || "supabase/migrations");
|
|
254086
|
-
if (!
|
|
254316
|
+
if (!existsSync15(drizzleConfig))
|
|
254087
254317
|
throw new Error(`Drizzle config not found: ${drizzleConfig}`);
|
|
254088
254318
|
const generated = await execute(resolveDrizzleCommand(root, typeof args.drizzle_bin === "string" ? args.drizzle_bin : database.drizzleBin), ["generate", "--config", drizzleConfig], root);
|
|
254089
254319
|
if (generated.exitCode !== 0)
|
|
@@ -254101,8 +254331,8 @@ async function migrateDatabase(args, options) {
|
|
|
254101
254331
|
return { ok: true, mode: "dev", action: "migrate", generated: true, applied: true, migrations_dir: migrationsDir, result: applied };
|
|
254102
254332
|
}
|
|
254103
254333
|
async function readProjectConfig3(root) {
|
|
254104
|
-
const file =
|
|
254105
|
-
if (!
|
|
254334
|
+
const file = join20(root, "supacloud.json");
|
|
254335
|
+
if (!existsSync15(file))
|
|
254106
254336
|
return {};
|
|
254107
254337
|
try {
|
|
254108
254338
|
const parsed = JSON.parse(await readFile10(file, "utf8"));
|
|
@@ -254120,7 +254350,7 @@ async function sourceFingerprint(root) {
|
|
|
254120
254350
|
const visit = async (directory) => {
|
|
254121
254351
|
const entries = await readdir6(directory, { withFileTypes: true });
|
|
254122
254352
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
254123
|
-
const path =
|
|
254353
|
+
const path = join20(directory, entry.name);
|
|
254124
254354
|
if (entry.isDirectory() && ![".git", "node_modules", "dist", "generated", ".supacloud"].includes(entry.name))
|
|
254125
254355
|
await visit(path);
|
|
254126
254356
|
else if (entry.isFile())
|
|
@@ -254148,17 +254378,17 @@ function remoteRoot(value) {
|
|
|
254148
254378
|
}
|
|
254149
254379
|
function targetDirectory(root, target, functionSlug, config = {}) {
|
|
254150
254380
|
if (target === "db")
|
|
254151
|
-
return
|
|
254381
|
+
return join20(root, "supabase", "migrations");
|
|
254152
254382
|
if (target === "functions") {
|
|
254153
254383
|
const targets = config.targets && typeof config.targets === "object" ? config.targets : {};
|
|
254154
254384
|
const match = Object.values(targets).find((entry) => entry?.type === "edge_function" && (!functionSlug || String(entry.slug || "") === functionSlug));
|
|
254155
|
-
const base = match?.root ?
|
|
254156
|
-
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;
|
|
254157
254387
|
}
|
|
254158
254388
|
if (target === "frontend") {
|
|
254159
254389
|
const targets = config.targets && typeof config.targets === "object" ? config.targets : {};
|
|
254160
254390
|
const match = Object.values(targets).find((entry) => entry?.type === "frontend");
|
|
254161
|
-
return match?.root ?
|
|
254391
|
+
return match?.root ? resolve25(root, String(match.root)) : join20(root, "apps", "web");
|
|
254162
254392
|
}
|
|
254163
254393
|
return root;
|
|
254164
254394
|
}
|
|
@@ -254197,10 +254427,10 @@ function reloadCommand(config, target, projectRef, functionSlug) {
|
|
|
254197
254427
|
return [...command.split(" "), "--project-ref", safeToken(projectRef || "test", "project_ref"), "--target", targetConfigPath(target), ...functionSlug ? ["--function", safeToken(functionSlug, "function")] : []];
|
|
254198
254428
|
}
|
|
254199
254429
|
function sshArgs(connection, remoteCommand) {
|
|
254200
|
-
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];
|
|
254201
254431
|
}
|
|
254202
254432
|
function rsyncArgs(connection, source, destination, excludes, deleteRemote) {
|
|
254203
|
-
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`];
|
|
254204
254434
|
if (deleteRemote)
|
|
254205
254435
|
args.push("--delete-delay");
|
|
254206
254436
|
for (const exclude of excludes) {
|
|
@@ -254212,17 +254442,17 @@ function rsyncArgs(connection, source, destination, excludes, deleteRemote) {
|
|
|
254212
254442
|
return args;
|
|
254213
254443
|
}
|
|
254214
254444
|
async function syncOnce(args, options) {
|
|
254215
|
-
const root =
|
|
254445
|
+
const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
|
|
254216
254446
|
const projectConfig = await readProjectConfig3(root);
|
|
254217
254447
|
const config = projectConfig.dev || {};
|
|
254218
254448
|
const target = String(args.target || "project");
|
|
254219
254449
|
const source = targetDirectory(root, target, typeof args.function === "string" ? args.function : undefined, projectConfig);
|
|
254220
|
-
if (!
|
|
254450
|
+
if (!existsSync15(source))
|
|
254221
254451
|
throw new Error(`Dev source directory not found: ${source}`);
|
|
254222
254452
|
let compiled = false;
|
|
254223
254453
|
if (config.compile === true && target !== "db") {
|
|
254224
|
-
const compileRoot =
|
|
254225
|
-
const compileOutDir =
|
|
254454
|
+
const compileRoot = resolve25(root, config.compileRoot || ".");
|
|
254455
|
+
const compileOutDir = resolve25(root, config.compileOutDir || "generated");
|
|
254226
254456
|
const compilation = await compileProject({
|
|
254227
254457
|
rootDir: compileRoot,
|
|
254228
254458
|
outDir: compileOutDir,
|
|
@@ -254260,7 +254490,7 @@ function registerRemoteDevTools(server2, options = {}) {
|
|
|
254260
254490
|
if (["production", "prod"].includes((options.environment || "").toLowerCase()))
|
|
254261
254491
|
throw new Error("Remote dev mode is forbidden for production environments");
|
|
254262
254492
|
if (args.action === "status") {
|
|
254263
|
-
const root =
|
|
254493
|
+
const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
|
|
254264
254494
|
const config = await readDevConfig(root);
|
|
254265
254495
|
const connection = connectionArgs(options, config, args);
|
|
254266
254496
|
const execute = options.execute || ((command, commandArgs, cwd) => runProcess(command, commandArgs, cwd));
|
|
@@ -254274,7 +254504,7 @@ function registerRemoteDevTools(server2, options = {}) {
|
|
|
254274
254504
|
return { content: [{ type: "text", text: JSON.stringify(await migrateDatabase(args, options), null, 2) }] };
|
|
254275
254505
|
}
|
|
254276
254506
|
if (args.action === "watch") {
|
|
254277
|
-
const root =
|
|
254507
|
+
const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
|
|
254278
254508
|
const interval = Math.max(100, Math.min(1e4, Number(args.interval_ms || 300)));
|
|
254279
254509
|
let fingerprint = "";
|
|
254280
254510
|
let lastSync = null;
|
|
@@ -254295,7 +254525,7 @@ function registerRemoteDevTools(server2, options = {}) {
|
|
|
254295
254525
|
// package.json
|
|
254296
254526
|
var package_default8 = {
|
|
254297
254527
|
name: "@supacloud/cli",
|
|
254298
|
-
version: "0.
|
|
254528
|
+
version: "0.63.0",
|
|
254299
254529
|
description: "Project-scoped CLI for SupaCloud users",
|
|
254300
254530
|
type: "module",
|
|
254301
254531
|
main: "./dist/index.js",
|
|
@@ -254330,7 +254560,7 @@ var package_default8 = {
|
|
|
254330
254560
|
},
|
|
254331
254561
|
dependencies: {
|
|
254332
254562
|
"@sinclair/typebox": "^0.34.52",
|
|
254333
|
-
"@supacloud/compiler": "^0.31.
|
|
254563
|
+
"@supacloud/compiler": "^0.31.1",
|
|
254334
254564
|
"@supacloud/delivery": "0.4.0",
|
|
254335
254565
|
"@supacloud/db": "^0.2.0",
|
|
254336
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.31.
|
|
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",
|