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.
Files changed (3) hide show
  1. package/README.md +7 -3
  2. package/dist/index.js +44 -44
  3. 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` | Apply schema to DB. Safe by default; destructive changes need confirmation. `--force` applies destructive changes, `--dry-run` previews |
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 additive changes but withholds destructive ones unless
383
- `--force`/confirmed. Prefer `db generate` + `db migrate` for production; `db push` is dev-only.
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.56";
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"] = "^0.45.0";
900
- packageJson.dependencies["drizzle-typebox"] = "^0.1.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"] = "^0.31.5";
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 { pushSchema } = await import("drizzle-kit/api");
3037
- const { statementsToExecute } = await pushSchema(
3038
- imports,
3039
- db,
3040
- schemaFilter
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
- for (const stmt of statements) {
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
- for (const stmt of statements) {
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
- for (const stmt of result.destructive) {
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.56",
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": "./bin/spfn.js"
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/spfn/spfn.git",
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/spfn/spfn/issues"
52
+ "url": "https://github.com/fxylabs/spfn/issues"
52
53
  },
53
- "homepage": "https://github.com/spfn/spfn/tree/main/packages/cli#readme",
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": "^0.31.6",
72
- "drizzle-orm": "^0.45.0",
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
  }