spfn 0.2.0-beta.56 → 0.2.0-beta.58
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/README.md +7 -3
- package/dist/index.js +44 -44
- package/package.json +11 -8
package/README.md
CHANGED
|
@@ -69,6 +69,9 @@ already exists). See [Scaffold structure](#scaffold-structure) for what lands on
|
|
|
69
69
|
|--------|-------------|
|
|
70
70
|
| `-y, --yes` | Skip prompts, use defaults |
|
|
71
71
|
|
|
72
|
+
Generated projects pin `drizzle-orm` and `drizzle-kit` to `1.0.0-rc.4`, matching
|
|
73
|
+
`@spfn/core` and the rest of the published SPFN database packages.
|
|
74
|
+
|
|
72
75
|
### `spfn add <package>`
|
|
73
76
|
|
|
74
77
|
Installs an SPFN ecosystem package and applies its pre-built migrations. The package
|
|
@@ -167,7 +170,7 @@ loaded `.env` chain.
|
|
|
167
170
|
| Subcommand | Description |
|
|
168
171
|
|------------|-------------|
|
|
169
172
|
| `db generate` (`g`) | Generate migrations from schema changes (timestamp-prefixed) |
|
|
170
|
-
| `db push` |
|
|
173
|
+
| `db push` | Diff with Drizzle Kit's current PostgreSQL engine and apply the selected DDL atomically. Destructive changes need confirmation; `--force` applies them, `--dry-run` previews |
|
|
171
174
|
| `db migrate` (`m`) | Run pending migrations. `--with-backup` snapshots first |
|
|
172
175
|
| `db studio` | Open Drizzle Studio. `-p, --port` (auto-finds a free port) |
|
|
173
176
|
| `db check` | Verify the database connection |
|
|
@@ -379,8 +382,9 @@ type ships from `spfn` (`@type {import('spfn').SpfnConfig}`).
|
|
|
379
382
|
- **`spfn start` needs a prior `spfn build`.** It hard-fails without `.spfn/server`,
|
|
380
383
|
`.spfn/prod-server.mjs`, and `.next`.
|
|
381
384
|
- **Destructive DB commands are guarded.** `db drop` double-confirms (and verifies the
|
|
382
|
-
target); `db push` applies
|
|
383
|
-
`--force`/confirmed. Prefer `db generate` + `db migrate` for
|
|
385
|
+
target); `db push` applies the selected statements in one transaction and withholds
|
|
386
|
+
destructive ones unless `--force`/confirmed. Prefer `db generate` + `db migrate` for
|
|
387
|
+
production; `db push` is dev-only.
|
|
384
388
|
- **`spfn add` requires a scoped package name** (must contain `/`) and only applies
|
|
385
389
|
migrations when `DATABASE_URL` is set — otherwise it skips with a hint.
|
|
386
390
|
- **Package manager is auto-detected from lockfiles.** If detection is wrong (e.g. mixed
|
package/dist/index.js
CHANGED
|
@@ -869,7 +869,7 @@ var init_deployment_config = __esm({
|
|
|
869
869
|
|
|
870
870
|
// src/utils/version.ts
|
|
871
871
|
function getCliVersion() {
|
|
872
|
-
return "0.2.0-beta.
|
|
872
|
+
return "0.2.0-beta.58";
|
|
873
873
|
}
|
|
874
874
|
function getTagFromVersion(version) {
|
|
875
875
|
const match = version.match(/-([a-z]+)\./i);
|
|
@@ -896,8 +896,8 @@ async function setupPackageJson(cwd, packageJsonPath, packageJson, packageManage
|
|
|
896
896
|
const spfnTag = getSpfnTag();
|
|
897
897
|
packageJson.dependencies["@spfn/core"] = spfnTag;
|
|
898
898
|
packageJson.dependencies["@sinclair/typebox"] = "^0.34.0";
|
|
899
|
-
packageJson.dependencies["drizzle-orm"] = "
|
|
900
|
-
packageJson.dependencies["drizzle-typebox"] = "^0.
|
|
899
|
+
packageJson.dependencies["drizzle-orm"] = "1.0.0-rc.4";
|
|
900
|
+
packageJson.dependencies["drizzle-typebox"] = "^0.3.3";
|
|
901
901
|
packageJson.dependencies["postgres"] = "^3.4.0";
|
|
902
902
|
packageJson.dependencies["pg"] = "^8.16.3";
|
|
903
903
|
packageJson.dependencies["spfn"] = spfnTag;
|
|
@@ -908,7 +908,7 @@ async function setupPackageJson(cwd, packageJsonPath, packageJson, packageManage
|
|
|
908
908
|
packageJson.devDependencies["@types/node"] = "^20.11.0";
|
|
909
909
|
packageJson.devDependencies["tsx"] = "^4.20.6";
|
|
910
910
|
packageJson.devDependencies["tsup"] = "^8.5.0";
|
|
911
|
-
packageJson.devDependencies["drizzle-kit"] = "
|
|
911
|
+
packageJson.devDependencies["drizzle-kit"] = "1.0.0-rc.4";
|
|
912
912
|
packageJson.devDependencies["dotenv"] = "^17.2.3";
|
|
913
913
|
if (!packageJson.scripts["build"]) {
|
|
914
914
|
packageJson.scripts["build"] = "next build --turbopack";
|
|
@@ -1461,7 +1461,7 @@ async function executeFunctionMigrations(functionMigrations) {
|
|
|
1461
1461
|
throw new Error("DATABASE_URL not found in environment");
|
|
1462
1462
|
}
|
|
1463
1463
|
const connection = postgres.default(env2.DATABASE_URL, { max: 1 });
|
|
1464
|
-
const db = drizzle(connection);
|
|
1464
|
+
const db = drizzle({ client: connection });
|
|
1465
1465
|
try {
|
|
1466
1466
|
await migrateLegacyTable(db, functionMigrations);
|
|
1467
1467
|
for (const func of functionMigrations) {
|
|
@@ -2886,7 +2886,7 @@ async function createPushConnection() {
|
|
|
2886
2886
|
max: 1,
|
|
2887
2887
|
...shouldRelaxDbTls(env.DATABASE_URL) ? { ssl: { rejectUnauthorized: false } } : {}
|
|
2888
2888
|
});
|
|
2889
|
-
const db = drizzle(pool);
|
|
2889
|
+
const db = drizzle({ client: pool });
|
|
2890
2890
|
return {
|
|
2891
2891
|
db,
|
|
2892
2892
|
close: () => pool.end()
|
|
@@ -3002,6 +3002,32 @@ function displayApplySummary(applied, skipped) {
|
|
|
3002
3002
|
}
|
|
3003
3003
|
|
|
3004
3004
|
// src/commands/db/push.ts
|
|
3005
|
+
async function resolvePushPlan(imports, db, schemaFilter) {
|
|
3006
|
+
const { pushSchema } = await import("drizzle-kit/api-postgres");
|
|
3007
|
+
const { sqlStatements, hints } = await pushSchema(
|
|
3008
|
+
imports,
|
|
3009
|
+
db,
|
|
3010
|
+
{
|
|
3011
|
+
schemas: schemaFilter,
|
|
3012
|
+
tables: [],
|
|
3013
|
+
entities: void 0,
|
|
3014
|
+
extensions: []
|
|
3015
|
+
}
|
|
3016
|
+
);
|
|
3017
|
+
const pushHints = hints;
|
|
3018
|
+
const hintStatements = pushHints.flatMap((hint) => hint.statement ? [hint.statement] : []);
|
|
3019
|
+
return {
|
|
3020
|
+
statements: [...hintStatements, ...sqlStatements],
|
|
3021
|
+
hints: pushHints
|
|
3022
|
+
};
|
|
3023
|
+
}
|
|
3024
|
+
async function applyStatements(db, statements) {
|
|
3025
|
+
await db.transaction(async (tx) => {
|
|
3026
|
+
for (const statement of statements) {
|
|
3027
|
+
await tx.execute(sql.raw(statement));
|
|
3028
|
+
}
|
|
3029
|
+
});
|
|
3030
|
+
}
|
|
3005
3031
|
async function dbPush(options = {}) {
|
|
3006
3032
|
validateDatabasePrerequisites();
|
|
3007
3033
|
loadEnv3();
|
|
@@ -3033,27 +3059,11 @@ async function dbPush(options = {}) {
|
|
|
3033
3059
|
const schemaFilter = Array.from(detectedSchemas);
|
|
3034
3060
|
const { db, close } = await createPushConnection();
|
|
3035
3061
|
try {
|
|
3036
|
-
const {
|
|
3037
|
-
const
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
);
|
|
3042
|
-
const managedSchemaSet = new Set(schemaFilter);
|
|
3043
|
-
const patched = statementsToExecute.filter((s) => {
|
|
3044
|
-
const dropMatch = s.match(/^\s*DROP\s+SCHEMA\s+"?([^"\s;]+)"?/i);
|
|
3045
|
-
if (dropMatch && managedSchemaSet.has(dropMatch[1])) {
|
|
3046
|
-
console.log(chalk14.dim(` [skip] DROP SCHEMA "${dropMatch[1]}" \u2014 managed schema, ignoring`));
|
|
3047
|
-
return false;
|
|
3048
|
-
}
|
|
3049
|
-
return true;
|
|
3050
|
-
}).map(
|
|
3051
|
-
(s) => s.replace(/^CREATE SCHEMA(?!\s+IF\s+NOT\s+EXISTS)/i, "CREATE SCHEMA IF NOT EXISTS")
|
|
3052
|
-
);
|
|
3053
|
-
const ensureSchemas = schemaFilter.filter((s) => s !== "public").map((s) => `CREATE SCHEMA IF NOT EXISTS "${s}";
|
|
3054
|
-
`);
|
|
3055
|
-
const statements = [...ensureSchemas, ...patched];
|
|
3056
|
-
if (patched.length === 0) {
|
|
3062
|
+
const { statements, hints } = await resolvePushPlan(imports, db, schemaFilter);
|
|
3063
|
+
for (const hint of hints) {
|
|
3064
|
+
console.log(chalk14.yellow(`\u26A0\uFE0F ${hint.hint}`));
|
|
3065
|
+
}
|
|
3066
|
+
if (statements.length === 0) {
|
|
3057
3067
|
console.log(chalk14.green("\u2705 No changes detected \u2014 database is up to date\n"));
|
|
3058
3068
|
await applyFunctionMigrations();
|
|
3059
3069
|
return;
|
|
@@ -3066,25 +3076,14 @@ async function dbPush(options = {}) {
|
|
|
3066
3076
|
displayClassifiedStatements(result);
|
|
3067
3077
|
if (options.force) {
|
|
3068
3078
|
console.log(chalk14.dim("\n--force: applying all changes..."));
|
|
3069
|
-
|
|
3070
|
-
await db.execute(sql.raw(stmt));
|
|
3071
|
-
}
|
|
3079
|
+
await applyStatements(db, statements);
|
|
3072
3080
|
displayApplySummary(statements.length, 0);
|
|
3073
3081
|
} else if (result.destructive.length === 0) {
|
|
3074
|
-
|
|
3075
|
-
await db.execute(sql.raw(stmt));
|
|
3076
|
-
}
|
|
3082
|
+
await applyStatements(db, statements);
|
|
3077
3083
|
displayApplySummary(statements.length, 0);
|
|
3078
3084
|
} else {
|
|
3079
3085
|
const destructiveSet = new Set(result.destructive.map((s) => s.sql));
|
|
3080
3086
|
const nonDestructive = statements.filter((s) => !destructiveSet.has(s));
|
|
3081
|
-
if (nonDestructive.length > 0) {
|
|
3082
|
-
for (const stmt of nonDestructive) {
|
|
3083
|
-
await db.execute(sql.raw(stmt));
|
|
3084
|
-
}
|
|
3085
|
-
console.log(chalk14.green(`
|
|
3086
|
-
\u2705 Applied ${nonDestructive.length} safe statement(s)`));
|
|
3087
|
-
}
|
|
3088
3087
|
console.log(chalk14.red(`
|
|
3089
3088
|
\u274C ${result.destructive.length} destructive change(s) require confirmation:`));
|
|
3090
3089
|
for (const stmt of result.destructive) {
|
|
@@ -3098,11 +3097,12 @@ async function dbPush(options = {}) {
|
|
|
3098
3097
|
initial: false
|
|
3099
3098
|
});
|
|
3100
3099
|
if (confirm) {
|
|
3101
|
-
|
|
3102
|
-
await db.execute(sql.raw(stmt.sql));
|
|
3103
|
-
}
|
|
3100
|
+
await applyStatements(db, statements);
|
|
3104
3101
|
displayApplySummary(statements.length, 0);
|
|
3105
3102
|
} else {
|
|
3103
|
+
if (nonDestructive.length > 0) {
|
|
3104
|
+
await applyStatements(db, nonDestructive);
|
|
3105
|
+
}
|
|
3106
3106
|
displayApplySummary(nonDestructive.length, result.destructive.length);
|
|
3107
3107
|
console.log(chalk14.dim("Tip: Use --force to apply all changes without prompting.\n"));
|
|
3108
3108
|
}
|
|
@@ -3547,7 +3547,7 @@ async function dbMigrate(options = {}) {
|
|
|
3547
3547
|
const projectMigrationsDir = join20(process.cwd(), "src/server/drizzle");
|
|
3548
3548
|
if (existsSync21(projectMigrationsDir)) {
|
|
3549
3549
|
const projConn = postgres.default(env4.DATABASE_URL, { max: 1 });
|
|
3550
|
-
const projDb = drizzle(projConn);
|
|
3550
|
+
const projDb = drizzle({ client: projConn });
|
|
3551
3551
|
try {
|
|
3552
3552
|
console.log(chalk19.blue("\u{1F4E6} Running project migrations..."));
|
|
3553
3553
|
await migrate(projDb, {
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spfn",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.58",
|
|
4
4
|
"description": "Superfunction CLI - Add SPFN to your Next.js project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"spfn": "
|
|
7
|
+
"spfn": "bin/spfn.js"
|
|
8
8
|
},
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"files": [
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "tsup && node scripts/copy-templates.js",
|
|
16
|
+
"test": "vitest run",
|
|
16
17
|
"prepack": "node -e \"require('fs').copyFileSync('../../LICENSE','LICENSE')\"",
|
|
17
18
|
"dev": "tsup --watch",
|
|
18
19
|
"type-check": "tsc --noEmit",
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"license": "MIT",
|
|
41
42
|
"repository": {
|
|
42
43
|
"type": "git",
|
|
43
|
-
"url": "https://github.com/
|
|
44
|
+
"url": "git+https://github.com/fxylabs/spfn.git",
|
|
44
45
|
"directory": "packages/cli"
|
|
45
46
|
},
|
|
46
47
|
"publishConfig": {
|
|
@@ -48,9 +49,9 @@
|
|
|
48
49
|
"tag": "beta"
|
|
49
50
|
},
|
|
50
51
|
"bugs": {
|
|
51
|
-
"url": "https://github.com/
|
|
52
|
+
"url": "https://github.com/fxylabs/spfn/issues"
|
|
52
53
|
},
|
|
53
|
-
"homepage": "https://github.com/
|
|
54
|
+
"homepage": "https://github.com/fxylabs/spfn/tree/main/packages/cli#readme",
|
|
54
55
|
"engines": {
|
|
55
56
|
"node": ">=18.18.0"
|
|
56
57
|
},
|
|
@@ -68,8 +69,8 @@
|
|
|
68
69
|
"chokidar": "^4.0.3",
|
|
69
70
|
"commander": "^11.1.0",
|
|
70
71
|
"dotenv": "^17.2.3",
|
|
71
|
-
"drizzle-kit": "
|
|
72
|
-
"drizzle-orm": "
|
|
72
|
+
"drizzle-kit": "1.0.0-rc.4",
|
|
73
|
+
"drizzle-orm": "1.0.0-rc.4",
|
|
73
74
|
"execa": "^8.0.1",
|
|
74
75
|
"fs-extra": "^11.2.0",
|
|
75
76
|
"ora": "^7.0.1",
|
|
@@ -83,6 +84,7 @@
|
|
|
83
84
|
"@napi-rs/keyring": "^1.1.0"
|
|
84
85
|
},
|
|
85
86
|
"devDependencies": {
|
|
87
|
+
"@electric-sql/pglite": "^0.5.4",
|
|
86
88
|
"@spfn/core": "workspace:*",
|
|
87
89
|
"@types/fs-extra": "^11.0.4",
|
|
88
90
|
"@types/node": "^20.11.0",
|
|
@@ -90,6 +92,7 @@
|
|
|
90
92
|
"@types/prompts": "^2.4.9",
|
|
91
93
|
"concurrently": "^9.2.1",
|
|
92
94
|
"tsx": "^4.20.6",
|
|
93
|
-
"typescript": "^5.3.3"
|
|
95
|
+
"typescript": "^5.3.3",
|
|
96
|
+
"vitest": "^4.0.6"
|
|
94
97
|
}
|
|
95
98
|
}
|