@coreframe/db 0.0.0 → 0.1.1
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/_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js +1 -0
- package/dist/apply-seed.d.ts +8 -0
- package/dist/apply-seed.js +1 -0
- package/dist/check-migrations.d.ts +4 -0
- package/dist/check-migrations.js +1 -0
- package/dist/client.d.ts +30 -0
- package/dist/client.js +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.js +1 -0
- package/dist/drizzle-kit.d.ts +10 -0
- package/dist/drizzle-kit.js +1 -0
- package/dist/ensure.d.ts +9 -0
- package/dist/ensure.js +1 -0
- package/dist/environment.js +1 -0
- package/dist/error.d.ts +4 -0
- package/dist/error.js +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +1 -0
- package/dist/local-postgres.d.ts +67 -0
- package/dist/local-postgres.js +1 -0
- package/dist/migrate-held.d.ts +16 -0
- package/dist/migrate-held.js +9 -0
- package/dist/migrate.d.ts +9 -0
- package/dist/migrate.js +1 -0
- package/dist/migrations.d.ts +24 -0
- package/dist/migrations.js +4 -0
- package/dist/pending-migrations.d.ts +9 -0
- package/dist/pending-migrations.js +1 -0
- package/dist/project.d.ts +23 -0
- package/dist/project.js +1 -0
- package/dist/reset.d.ts +9 -0
- package/dist/reset.js +1 -0
- package/dist/schema-check.d.ts +11 -0
- package/dist/schema-check.js +1 -0
- package/dist/schema-push.d.ts +22 -0
- package/dist/schema-push.js +1 -0
- package/dist/seed.d.ts +19 -0
- package/dist/seed.js +1 -0
- package/dist/setup.d.ts +11 -0
- package/dist/setup.js +1 -0
- package/package.json +38 -10
- package/readme.md +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(){var e=typeof SuppressedError==`function`?SuppressedError:function(e,t){var n=Error();return n.name=`SuppressedError`,n.error=e,n.suppressed=t,n},t={},n=[];function r(e,t){if(t!=null){if(Object(t)!==t)throw TypeError(`using declarations can only be used with objects, functions, null, or undefined.`);if(e)var r=t[Symbol.asyncDispose||Symbol.for(`Symbol.asyncDispose`)];if(r===void 0&&(r=t[Symbol.dispose||Symbol.for(`Symbol.dispose`)],e))var i=r;if(typeof r!=`function`)throw TypeError(`Object is not disposable.`);i&&(r=function(){try{i.call(t)}catch(e){return Promise.reject(e)}}),n.push({v:t,d:r,a:e})}else e&&n.push({d:t,a:e});return t}return{e:t,u:r.bind(null,!1),a:r.bind(null,!0),d:function(){var r,i=this.e,a=0;function o(){for(;r=n.pop();)try{if(!r.a&&a===1)return a=0,n.push(r),Promise.resolve().then(o);if(r.d){var e=r.d.call(r.v);if(r.a)return a|=2,Promise.resolve(e).then(o,s)}else a|=1}catch(e){return s(e)}if(a===1)return i===t?Promise.resolve():Promise.reject(i);if(i!==t)throw i}function s(n){return i=i===t?n:new e(n,i),o()}return o()}}}export{e as default};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/apply-seed.d.ts
|
|
2
|
+
type SeedQuery = object;
|
|
3
|
+
type SeedDatabase = {
|
|
4
|
+
transaction<T>(transaction: (tx: any) => Promise<T>): Promise<T>;
|
|
5
|
+
};
|
|
6
|
+
declare function applySeed(db: SeedDatabase, seed: readonly SeedQuery[]): Promise<void>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { SeedDatabase, SeedQuery, applySeed };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
async function e(e,t){t.length!==0&&await e.transaction(async e=>{let n=e;for(let e of t)await n.execute(e.getSQL())})}export{e as applySeed};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{collectDeployMigrationFiles as e,formatMigrationList as t,formatMigrationScanFailures as n,scanDeployMigrationFiles as r}from"./migrations.js";async function i(){let i=await e();console.log(`Deploy migration files:`),console.log(t(i));let a=await r(i);if(a.length>0)throw Error(`Deploy migrations contain unsafe SQL. Move unsafe statements to held migrations.\n${n(a)}`);console.log(`Deploy migrations passed safety checks.`)}export{i as checkDeployMigrations};
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { SQL } from "drizzle-orm";
|
|
2
|
+
|
|
3
|
+
//#region src/client.d.ts
|
|
4
|
+
type DatabaseDialect = "postgres" | "sqlite";
|
|
5
|
+
type QueryResult<Row extends Record<string, unknown> = Record<string, unknown>> = {
|
|
6
|
+
rowCount?: number | null;
|
|
7
|
+
rows: Row[];
|
|
8
|
+
};
|
|
9
|
+
type DatabaseClient = {
|
|
10
|
+
dialect: DatabaseDialect;
|
|
11
|
+
execute<Row extends Record<string, unknown> = Record<string, unknown>>(statement: string): Promise<QueryResult<Row>>;
|
|
12
|
+
transaction?<Result>(run: (database: DatabaseClient) => Promise<Result>): Promise<Result>;
|
|
13
|
+
close?(): Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
type DrizzleExecutor = {
|
|
16
|
+
execute<Result = unknown>(query: SQL): Result | Promise<Result>;
|
|
17
|
+
};
|
|
18
|
+
type CreateDatabaseAdapterOptions = {
|
|
19
|
+
close?(): Promise<void>;
|
|
20
|
+
database: DrizzleExecutor;
|
|
21
|
+
dialect: DatabaseDialect;
|
|
22
|
+
};
|
|
23
|
+
declare function createDatabaseAdapter({
|
|
24
|
+
close,
|
|
25
|
+
database,
|
|
26
|
+
dialect
|
|
27
|
+
}: CreateDatabaseAdapterOptions): DatabaseClient;
|
|
28
|
+
declare function normalizeQueryResult<Row extends Record<string, unknown> = Record<string, unknown>>(result: unknown): QueryResult<Row>;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { CreateDatabaseAdapterOptions, DatabaseClient, DatabaseDialect, DrizzleExecutor, QueryResult, createDatabaseAdapter, normalizeQueryResult };
|
package/dist/client.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{sql as e}from"drizzle-orm";function t({close:t,database:r,dialect:i}){return{dialect:i,async execute(t){return n(await r.execute(e.raw(t)))},close:t}}function n(e){return Array.isArray(e)?{rows:e}:r(e)?{rowCount:typeof e.rowCount==`number`?e.rowCount:null,rows:e.rows}:{rows:[]}}function r(e){return typeof e==`object`&&!!e&&`rows`in e&&Array.isArray(e.rows)}export{t as createDatabaseAdapter,n as normalizeQueryResult};
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/config.d.ts
|
|
2
|
+
declare const MIGRATIONS_FOLDER = "drizzle";
|
|
3
|
+
declare const HELD_MIGRATIONS_FOLDER = "drizzle-hold";
|
|
4
|
+
declare const NOW: Date;
|
|
5
|
+
declare const SEED_IDS: {
|
|
6
|
+
readonly adminUser: "00000000-0000-4000-8000-000000000001";
|
|
7
|
+
readonly adminCredentialAccount: "00000000-0000-4000-8000-000000000101";
|
|
8
|
+
readonly viewerUser: "00000000-0000-4000-8000-000000000002";
|
|
9
|
+
readonly inactiveUser: "00000000-0000-4000-8000-000000000003";
|
|
10
|
+
readonly unicodeUser: "00000000-0000-4000-8000-000000000004";
|
|
11
|
+
readonly longNameUser: "00000000-0000-4000-8000-000000000005";
|
|
12
|
+
};
|
|
13
|
+
declare const SEED_EMAILS: {
|
|
14
|
+
readonly adminUser: "admin@example.com";
|
|
15
|
+
readonly viewerUser: "viewer@example.com";
|
|
16
|
+
readonly inactiveUser: "inactive@example.com";
|
|
17
|
+
readonly unicodeUser: "unicode@example.com";
|
|
18
|
+
readonly longNameUser: "long-name@example.com";
|
|
19
|
+
};
|
|
20
|
+
type SeedProfile = "default" | "large";
|
|
21
|
+
declare function getSeedProfile(argv?: string[]): SeedProfile;
|
|
22
|
+
declare function generatedUserId(index: number): string;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { HELD_MIGRATIONS_FOLDER, MIGRATIONS_FOLDER, NOW, SEED_EMAILS, SEED_IDS, SeedProfile, generatedUserId, getSeedProfile };
|
package/dist/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`drizzle`,t=`drizzle-hold`,n=new Date(`2026-01-15T12:00:00.000Z`),r={adminUser:`00000000-0000-4000-8000-000000000001`,adminCredentialAccount:`00000000-0000-4000-8000-000000000101`,viewerUser:`00000000-0000-4000-8000-000000000002`,inactiveUser:`00000000-0000-4000-8000-000000000003`,unicodeUser:`00000000-0000-4000-8000-000000000004`,longNameUser:`00000000-0000-4000-8000-000000000005`},i={adminUser:`admin@example.com`,viewerUser:`viewer@example.com`,inactiveUser:`inactive@example.com`,unicodeUser:`unicode@example.com`,longNameUser:`long-name@example.com`};function a(e=process.argv.slice(2)){if(e.includes(`--large`))return`large`;let t=e.find(e=>e.startsWith(`--profile=`))?.slice(10)??`default`;if(t==="default"||t===`large`)return t;throw Error(`Unknown seed profile "${t}". Expected default or large.`)}function o(e){return`00000000-0000-4000-8000-${String(1e3+e).padStart(12,`0`)}`}export{t as HELD_MIGRATIONS_FOLDER,e as MIGRATIONS_FOLDER,n as NOW,i as SEED_EMAILS,r as SEED_IDS,o as generatedUserId,a as getSeedProfile};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/drizzle-kit.d.ts
|
|
2
|
+
type DrizzleKitCommand = "generate" | "migrate" | "studio";
|
|
3
|
+
type RunDrizzleKitCommandOptions = {
|
|
4
|
+
env?: NodeJS.ProcessEnv;
|
|
5
|
+
};
|
|
6
|
+
declare function runDrizzleKitCommand(command: DrizzleKitCommand, args?: string[], {
|
|
7
|
+
env
|
|
8
|
+
}?: RunDrizzleKitCommandOptions): Promise<void>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { runDrizzleKitCommand };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{startProjectPostgres as e}from"./local-postgres.js";import t from"./_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import n from"node:path";import r from"node:process";import{readProjectConfig as i}from"@coreframe/config";import a from"nano-spawn";const o=n.resolve(`node_modules`,`.bin`),s=r.platform===`win32`?`drizzle-kit.cmd`:`drizzle-kit`;async function c(n,a=[],{env:o=r.env}={}){try{var s=t();if((await i()).database?.dialect===`sqlite`){let e=o.TURSO_DATABASE_URL??o.DATABASE_URL;if(!e)throw Error(`TURSO_DATABASE_URL or DATABASE_URL is required for SQLite Drizzle commands.`);await l(a,n,{...o,DATABASE_URL:e});return}await l(a,n,s.a(await e({env:o,useSupervisorLease:!0})).env)}catch(e){s.e=e}finally{await s.d()}}async function l(e,t,r){let i=r.PATH??r.Path??``;await a(s,[t,...e],{env:{...r,PATH:o+n.delimiter+i},stdio:`inherit`})}export{c as runDrizzleKitCommand};
|
package/dist/ensure.d.ts
ADDED
package/dist/ensure.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{SEED_EMAILS as e,SEED_IDS as t}from"./config.js";import{createLocalPostgresDb as n,startProjectPostgres as r}from"./local-postgres.js";import i from"./_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{inferSchemaDialect as a,planLocalSchemaPush as o}from"./schema-push.js";import{seedLocalDb as s}from"./seed.js";import{setupLocalDb as c}from"./setup.js";import{eq as l}from"drizzle-orm";import{importProjectModule as u}from"@coreframe/config";async function d(){return{schema:await u(`src/db/schema.ts`)}}async function f(n,{users:r}){let[i]=await n.select({id:r.id}).from(r).where(l(r.email,e.adminUser)).limit(1);return i?.id===t.adminUser}async function p(e,{schema:t}){let n=await o(a(t),t,e);return n.sqlStatements.length===0?(console.log(`Local database schema already in sync`),{appliedStatements:0,plan:n}):(console.log(`Local database schema drift detected; applying ${n.sqlStatements.length} statement(s)`),await n.apply(),console.log(`Local database schema synced`),{appliedStatements:n.sqlStatements.length,plan:n})}async function m({env:e=process.env}={}){try{var t=i();let a=t.a(await r({env:e,useSupervisorLease:!0})).env,o=await d(),l=await n({env:a});try{if(await h(l,o.schema))console.log(`Local database already set up`);else{console.log(`Local database is missing or unseeded; running setup`),await c(`default`,{env:a});return}(await p(l,o)).appliedStatements>0&&!await h(l,o.schema)&&(console.log(`Local database seed data missing after schema sync; running seed`),await s(void 0,{env:a}))}finally{await l.$client.end()}}catch(e){t.e=e}finally{await t.d()}}async function h(e,t){try{return await f(e,t)}catch{return!1}}export{m as ensureLocalDb};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{access as e,mkdtemp as t,readFile as n,rm as r,stat as i}from"node:fs/promises";import a from"node:process";import{tmpdir as o}from"node:os";const s=a.kill.bind(a),c={access:e,mkdtemp:t,readFile:n,rm:r,stat:i},l={kill:s,tmpdir:o};a.argv,a.cwd.bind(a),a.env,a.platform;export{c as fs,l as os};
|
package/dist/error.d.ts
ADDED
package/dist/error.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DrizzleQueryError as e}from"drizzle-orm/errors";function t(t){if(!(!(t instanceof e)||!t.cause?.message))return`${n(t.message)}\nCause: ${n(t.cause.message)}`}function n(e){return e.split(/\r?\n/,1)[0]||`Unknown error`}export{t as formatDbErrorMessage};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SeedDatabase, SeedQuery, applySeed } from "./apply-seed.js";
|
|
2
|
+
import { checkDeployMigrations } from "./check-migrations.js";
|
|
3
|
+
import { CreateDatabaseAdapterOptions, DatabaseClient, DatabaseDialect, DrizzleExecutor, QueryResult, createDatabaseAdapter, normalizeQueryResult } from "./client.js";
|
|
4
|
+
import { HELD_MIGRATIONS_FOLDER, MIGRATIONS_FOLDER, NOW, SEED_EMAILS, SEED_IDS, SeedProfile, generatedUserId, getSeedProfile } from "./config.js";
|
|
5
|
+
import { runDrizzleKitCommand } from "./drizzle-kit.js";
|
|
6
|
+
import { ensureLocalDb } from "./ensure.js";
|
|
7
|
+
import { formatDbErrorMessage } from "./error.js";
|
|
8
|
+
import { CreateProjectDatabaseOptions, createProjectDeployDatabase, createProjectPostgresDrizzleDatabase } from "./project.js";
|
|
9
|
+
import { LOCAL_POSTGRES_ENV_FLAG, LocalPostgresDb, ProjectPostgresAlreadyRunningError, ProjectPostgresSidecar, StartProjectPostgresOptions, StartTemporaryProjectPostgresOptions, createLocalPostgresDb, createLocalPostgresEnv, getLocalPostgresConnectionString, startProjectPostgres, startTemporaryProjectPostgres, stopProjectPostgres } from "./local-postgres.js";
|
|
10
|
+
import { heldMigrationInsertQuery, heldMigrationSelectQuery, migrateHeldMigrations, validateHeldMigrationHeader } from "./migrate-held.js";
|
|
11
|
+
import { migrateLocalDb } from "./migrate.js";
|
|
12
|
+
import { MigrationFile, MigrationRisk, MigrationScanResult, collectDeployMigrationFiles, collectHeldMigrationFiles, formatMigrationList, formatMigrationScanFailures, scanDeployMigrationFiles, scanMigrationSql } from "./migrations.js";
|
|
13
|
+
import { appliedMigrationsQuery, filterPendingMigrations, readAppliedMigrationNames } from "./pending-migrations.js";
|
|
14
|
+
import { resetLocalDb } from "./reset.js";
|
|
15
|
+
import { checkSchemaPush } from "./schema-check.js";
|
|
16
|
+
import { SchemaPushModule, SchemaPushPlan, inferSchemaDialect, planLocalSchemaPush } from "./schema-push.js";
|
|
17
|
+
import { runProjectSeed, seedLocalDb } from "./seed.js";
|
|
18
|
+
import { setupLocalDb } from "./setup.js";
|
|
19
|
+
export { CreateDatabaseAdapterOptions, CreateProjectDatabaseOptions, DatabaseClient, DatabaseDialect, DrizzleExecutor, HELD_MIGRATIONS_FOLDER, LOCAL_POSTGRES_ENV_FLAG, LocalPostgresDb, MIGRATIONS_FOLDER, MigrationFile, MigrationRisk, MigrationScanResult, NOW, ProjectPostgresAlreadyRunningError, ProjectPostgresSidecar, QueryResult, SEED_EMAILS, SEED_IDS, SchemaPushModule, SchemaPushPlan, SeedDatabase, SeedProfile, SeedQuery, StartProjectPostgresOptions, StartTemporaryProjectPostgresOptions, appliedMigrationsQuery, applySeed, checkDeployMigrations, checkSchemaPush, collectDeployMigrationFiles, collectHeldMigrationFiles, createDatabaseAdapter, createLocalPostgresDb, createLocalPostgresEnv, createProjectDeployDatabase, createProjectPostgresDrizzleDatabase, ensureLocalDb, filterPendingMigrations, formatDbErrorMessage, formatMigrationList, formatMigrationScanFailures, generatedUserId, getLocalPostgresConnectionString, getSeedProfile, heldMigrationInsertQuery, heldMigrationSelectQuery, inferSchemaDialect, migrateHeldMigrations, migrateLocalDb, normalizeQueryResult, planLocalSchemaPush, readAppliedMigrationNames, resetLocalDb, runDrizzleKitCommand, runProjectSeed, scanDeployMigrationFiles, scanMigrationSql, seedLocalDb, setupLocalDb, startProjectPostgres, startTemporaryProjectPostgres, stopProjectPostgres, validateHeldMigrationHeader };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{applySeed as e}from"./apply-seed.js";import{HELD_MIGRATIONS_FOLDER as t,MIGRATIONS_FOLDER as n,NOW as r,SEED_EMAILS as i,SEED_IDS as a,generatedUserId as o,getSeedProfile as s}from"./config.js";import{collectDeployMigrationFiles as c,collectHeldMigrationFiles as l,formatMigrationList as u,formatMigrationScanFailures as d,scanDeployMigrationFiles as f,scanMigrationSql as p}from"./migrations.js";import{checkDeployMigrations as m}from"./check-migrations.js";import{createDatabaseAdapter as h,normalizeQueryResult as g}from"./client.js";import{createProjectDeployDatabase as _,createProjectPostgresDrizzleDatabase as v}from"./project.js";import{LOCAL_POSTGRES_ENV_FLAG as y,ProjectPostgresAlreadyRunningError as b,createLocalPostgresDb as x,createLocalPostgresEnv as S,getLocalPostgresConnectionString as C,startProjectPostgres as w,startTemporaryProjectPostgres as T,stopProjectPostgres as E}from"./local-postgres.js";import{runDrizzleKitCommand as D}from"./drizzle-kit.js";import{inferSchemaDialect as O,planLocalSchemaPush as k}from"./schema-push.js";import{runProjectSeed as A,seedLocalDb as j}from"./seed.js";import{migrateLocalDb as M}from"./migrate.js";import{resetLocalDb as N}from"./reset.js";import{setupLocalDb as P}from"./setup.js";import{ensureLocalDb as F}from"./ensure.js";import{formatDbErrorMessage as I}from"./error.js";import{heldMigrationInsertQuery as L,heldMigrationSelectQuery as R,migrateHeldMigrations as z,validateHeldMigrationHeader as B}from"./migrate-held.js";import{appliedMigrationsQuery as V,filterPendingMigrations as H,readAppliedMigrationNames as U}from"./pending-migrations.js";import{checkSchemaPush as W}from"./schema-check.js";export{t as HELD_MIGRATIONS_FOLDER,y as LOCAL_POSTGRES_ENV_FLAG,n as MIGRATIONS_FOLDER,r as NOW,b as ProjectPostgresAlreadyRunningError,i as SEED_EMAILS,a as SEED_IDS,V as appliedMigrationsQuery,e as applySeed,m as checkDeployMigrations,W as checkSchemaPush,c as collectDeployMigrationFiles,l as collectHeldMigrationFiles,h as createDatabaseAdapter,x as createLocalPostgresDb,S as createLocalPostgresEnv,_ as createProjectDeployDatabase,v as createProjectPostgresDrizzleDatabase,F as ensureLocalDb,H as filterPendingMigrations,I as formatDbErrorMessage,u as formatMigrationList,d as formatMigrationScanFailures,o as generatedUserId,C as getLocalPostgresConnectionString,s as getSeedProfile,L as heldMigrationInsertQuery,R as heldMigrationSelectQuery,O as inferSchemaDialect,z as migrateHeldMigrations,M as migrateLocalDb,g as normalizeQueryResult,k as planLocalSchemaPush,U as readAppliedMigrationNames,N as resetLocalDb,D as runDrizzleKitCommand,A as runProjectSeed,f as scanDeployMigrationFiles,p as scanMigrationSql,j as seedLocalDb,P as setupLocalDb,w as startProjectPostgres,T as startTemporaryProjectPostgres,E as stopProjectPostgres,B as validateHeldMigrationHeader};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { DrizzleExecutor } from "./client.js";
|
|
2
|
+
import { createProjectPostgresDrizzleDatabase } from "./project.js";
|
|
3
|
+
import { CoreframeConfig } from "@coreframe/config";
|
|
4
|
+
import { LocalPostgresEnv, PostgresOutputTarget } from "local-postgres";
|
|
5
|
+
|
|
6
|
+
//#region src/local-postgres.d.ts
|
|
7
|
+
declare const LOCAL_POSTGRES_ENV_FLAG = "COREFRAME_LOCAL_POSTGRES";
|
|
8
|
+
type LocalPostgresDb = Awaited<ReturnType<typeof createProjectPostgresDrizzleDatabase>> & {
|
|
9
|
+
$client: {
|
|
10
|
+
end(): Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
transaction<Result>(run: (database: DrizzleExecutor) => Promise<Result>): Promise<Result>;
|
|
13
|
+
select(selection: Record<string, unknown>): {
|
|
14
|
+
from(table: unknown): {
|
|
15
|
+
where(condition: unknown): {
|
|
16
|
+
limit(count: number): Promise<Array<{
|
|
17
|
+
id: string;
|
|
18
|
+
}>>;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
type LocalPostgresOptions = {
|
|
24
|
+
env?: NodeJS.ProcessEnv;
|
|
25
|
+
};
|
|
26
|
+
type ProjectPostgresSidecar = {
|
|
27
|
+
env: NodeJS.ProcessEnv;
|
|
28
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
type StartProjectPostgresOptions = {
|
|
31
|
+
config?: CoreframeConfig;
|
|
32
|
+
cwd?: string;
|
|
33
|
+
env?: NodeJS.ProcessEnv;
|
|
34
|
+
failIfRunning?: boolean;
|
|
35
|
+
postgresOutput?: PostgresOutputTarget;
|
|
36
|
+
useSupervisorLease?: boolean;
|
|
37
|
+
};
|
|
38
|
+
type StartTemporaryProjectPostgresOptions = Omit<StartProjectPostgresOptions, "useSupervisorLease">;
|
|
39
|
+
declare class ProjectPostgresAlreadyRunningError extends Error {
|
|
40
|
+
readonly dataDir: string;
|
|
41
|
+
readonly pid: number;
|
|
42
|
+
constructor(dataDir: string, pid: number, options?: ErrorOptions);
|
|
43
|
+
}
|
|
44
|
+
declare function startProjectPostgres({
|
|
45
|
+
config,
|
|
46
|
+
cwd,
|
|
47
|
+
env,
|
|
48
|
+
failIfRunning,
|
|
49
|
+
postgresOutput,
|
|
50
|
+
useSupervisorLease
|
|
51
|
+
}?: StartProjectPostgresOptions): Promise<ProjectPostgresSidecar>;
|
|
52
|
+
declare function stopProjectPostgres({
|
|
53
|
+
dataDir,
|
|
54
|
+
pid
|
|
55
|
+
}: ProjectPostgresAlreadyRunningError): Promise<void>;
|
|
56
|
+
declare function startTemporaryProjectPostgres({
|
|
57
|
+
config,
|
|
58
|
+
cwd,
|
|
59
|
+
env
|
|
60
|
+
}?: StartTemporaryProjectPostgresOptions): Promise<ProjectPostgresSidecar>;
|
|
61
|
+
declare function createLocalPostgresEnv(env: NodeJS.ProcessEnv, postgresEnv: LocalPostgresEnv): NodeJS.ProcessEnv;
|
|
62
|
+
declare function createLocalPostgresDb({
|
|
63
|
+
env
|
|
64
|
+
}?: LocalPostgresOptions): Promise<LocalPostgresDb>;
|
|
65
|
+
declare function getLocalPostgresConnectionString(env?: NodeJS.ProcessEnv): string;
|
|
66
|
+
//#endregion
|
|
67
|
+
export { LOCAL_POSTGRES_ENV_FLAG, LocalPostgresDb, ProjectPostgresAlreadyRunningError, ProjectPostgresSidecar, StartProjectPostgresOptions, StartTemporaryProjectPostgresOptions, createLocalPostgresDb, createLocalPostgresEnv, getLocalPostgresConnectionString, startProjectPostgres, startTemporaryProjectPostgres, stopProjectPostgres };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{fs as e,os as t}from"./environment.js";import{createProjectPostgresDrizzleDatabase as n}from"./project.js";import r from"node:path";import{readProjectConfig as i}from"@coreframe/config";import{connectExistingDevSupervisor as a}from"@coreframe/dev-supervisor";import{startPostgres as o}from"local-postgres";import{PostgresDataDirInUseError as s,stopPostgresDataDir as c}from"local-postgres/core";const l=`COREFRAME_LOCAL_POSTGRES`,u=`coreframe`,d=2e3,f=4e3,p=new Map,m={name:`coreframe`,password:`coreframe`};var h=class extends Error{dataDir;pid;constructor(e,t,n){super(`PostgreSQL is already running for this project (PID ${t}). Stop the other top-level \`coreframe dev\` process before starting another.`,n),this.name=`ProjectPostgresAlreadyRunningError`,this.dataDir=e,this.pid=t}};async function g({config:t,cwd:n=process.cwd(),env:s=process.env,failIfRunning:c=!1,postgresOutput:l,useSupervisorLease:f=!1}={}){if(s.COREFRAME_LOCAL_POSTGRES===`1`)return T(s);let p=t??await i({cwd:n});if((p.database?.dialect??`postgres`)!==`postgres`)return T(s);if(f){if(s.COREFRAME_DEV_SUPERVISOR!==`0`){let e=await a({cwd:n,env:s});if(e)try{let t=await e.acquirePostgres({config:p,env:s});return E(e,t.leaseId,t.env)}catch(t){throw e.close(),t}}return v({config:p,cwd:n,env:s})}let g=r.resolve(n,`.coreframe/postgres`),_=await D(g);if(_){if(c)throw new h(g,_.pid);return T(O(s,g,_))}let y=r.join(g,`postgres.log`);await e.rm(y,{force:!0});let b;try{b=await o({dataDir:g,database:u,postgresOutput:l??{filePath:y},postgres:{strategy:`prefer-local`},stopTimeoutMs:d,superuser:m})}catch(e){throw await k(e,l?void 0:y)}return x(s,b)}async function _({dataDir:e,pid:t}){let n=`${e}\0${t}`,r=p.get(n);if(r){await r;return}let i=C(e,t);p.set(n,i);try{await i}finally{p.get(n)===i&&p.delete(n)}}async function v({config:n,cwd:a=process.cwd(),env:s=process.env}={}){let c=(n??await i({cwd:a})).database?.dialect??`postgres`;if(c!==`postgres`)throw Error(`Temporary schema push checks require PostgreSQL, but this project uses ${c}.`);let l=await e.mkdtemp(r.join(t.tmpdir(),`coreframe-schema-check-`));try{let t=x(s,await o({dataDir:l,database:u,postgresOutput:`ignore`,postgres:{strategy:`prefer-local`},stopTimeoutMs:d,superuser:m}));return{env:t.env,async[Symbol.asyncDispose](){await t[Symbol.asyncDispose](),await e.rm(l,{force:!0,recursive:!0})}}}catch(e){throw await w(l,e),e}}function y(e,t){return{...e,...t,[l]:`1`}}async function b({env:e=process.env}={}){let t=j(e);return n({env:{...e,DATABASE_URL:t}})}function x(e,t){let n;return{env:y(e,t.env),async[Symbol.asyncDispose](){n??=S(t),await n}}}async function S(e){let t;try{await e[Symbol.asyncDispose]()}catch(e){t=e}if(e.pid===void 0){if(t)throw t;return}try{await c({dataDir:e.dataDir,expectedPid:e.pid,mode:`immediate`,timeoutMs:f})}catch(n){throw t?AggregateError([t,n],`Failed to stop PostgreSQL process ${e.pid}.`):n}}async function C(e,t){try{await c({dataDir:e,expectedPid:t,mode:`fast`,timeoutMs:f})}catch(n){try{await c({dataDir:e,expectedPid:t,mode:`immediate`,timeoutMs:f})}catch(e){throw AggregateError([n,e],`Failed to stop PostgreSQL process ${t}.`)}}}async function w(t,n){try{await c({dataDir:t,mode:`immediate`,timeoutMs:f})}catch(e){throw AggregateError([n,e],`Temporary PostgreSQL failed to start and could not be stopped for ${t}.`)}await e.rm(t,{force:!0,recursive:!0})}function T(e){return{env:e,async[Symbol.asyncDispose](){}}}function E(e,t,n){let r;return{env:n,async[Symbol.asyncDispose](){r??=(async()=>{try{await e.release(t)}finally{e.close()}})(),await r}}}async function D(n){let i;try{i=await e.readFile(r.join(n,`postmaster.pid`),`utf8`)}catch{return}let[a,,,o,,s]=i.split(/\r?\n/),c=Number(a);if(!Number.isSafeInteger(c)||c<=0)return;try{t.kill(c,0)}catch(e){if(!A(e)||e.code!==`EPERM`)return}let l=Number(o);if(!Number.isSafeInteger(l)||l<=0||l>65535||!s)throw Error(`Cannot read connection details for PostgreSQL process ${c} from ${n}.`);return{host:s,pid:c,port:l}}function O(e,t,{host:n,port:r}){let i=new URL(`postgresql://${n}:${r}/${u}`);return i.username=m.name,i.password=m.password,y(e,{DATABASE_URL:i.href,PGDATA:t,PGDATABASE:u,PGHOST:n,PGPASSWORD:m.password,PGPORT:String(r),PGUSER:m.name})}async function k(t,n){if(t instanceof s)return new h(t.dataDir,t.pid,{cause:t});let r=t instanceof Error?t.message:String(t),i;if(n)try{i=(await e.readFile(n,`utf8`)).trim()||void 0}catch{}let a=i?`\n\nPostgreSQL output:\n${i}`:``,o=n?`\n\nPostgreSQL log: ${n}`:``;return Error(`Local PostgreSQL failed to start: ${r}${a}${o}`,{cause:t})}function A(e){return e instanceof Error}function j(e=process.env){let t=e.DATABASE_URL||M(e);if(!t)throw Error(`DATABASE_URL or PGDATABASE, PGHOST, PGPORT, and PGUSER are required.`);return t}function M(e){let{PGDATABASE:t,PGHOST:n,PGPASSWORD:r,PGPORT:i,PGUSER:a}=e;if(!t||!n||!i||!a)return;let o=new URL(`postgresql://${n}:${i}/${t}`);return o.username=a,r&&(o.password=r),o.href}export{l as LOCAL_POSTGRES_ENV_FLAG,h as ProjectPostgresAlreadyRunningError,b as createLocalPostgresDb,y as createLocalPostgresEnv,j as getLocalPostgresConnectionString,g as startProjectPostgres,v as startTemporaryProjectPostgres,_ as stopProjectPostgres};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DatabaseDialect } from "./client.js";
|
|
2
|
+
|
|
3
|
+
//#region src/migrate-held.d.ts
|
|
4
|
+
type MigrateHeldOptions = {
|
|
5
|
+
argv?: string[];
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
};
|
|
8
|
+
declare function migrateHeldMigrations({
|
|
9
|
+
argv,
|
|
10
|
+
env
|
|
11
|
+
}?: MigrateHeldOptions): Promise<void>;
|
|
12
|
+
declare function validateHeldMigrationHeader(sql: string, relativePath: string): void;
|
|
13
|
+
declare function heldMigrationSelectQuery(dialect: DatabaseDialect, id: string): string;
|
|
14
|
+
declare function heldMigrationInsertQuery(dialect: DatabaseDialect, id: string, sha256: string): string;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { heldMigrationInsertQuery, heldMigrationSelectQuery, migrateHeldMigrations, validateHeldMigrationHeader };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import{collectHeldMigrationFiles as e,formatMigrationList as t}from"./migrations.js";import{createProjectDeployDatabase as n}from"./project.js";import{readFile as r}from"node:fs/promises";import{createHash as i}from"node:crypto";const a=`--confirm=run-held-migrations`,o=[`-- Held migration:`,`-- Safe after:`,`-- Verification:`];async function s({argv:i=process.argv.slice(2),env:o=process.env}={}){let s=await e();if(console.log(`Held migration files:`),console.log(t(s)),!i.includes(a))throw Error(`Held migrations require explicit confirmation. Re-run with ${a}.`);if(s.length===0){console.log(`No held migrations to run.`);return}let d=await n({env:o});try{await u(d);for(let e of s){let t=await r(e.path,`utf8`);c(t,e.relativePath);let n=l(t),i=await d.execute(f(d.dialect,e.id));if(i.rows.length===1){if(i.rows[0]?.sha256!==n)throw Error(`Held migration ${e.relativePath} changed after it was applied.`);console.log(`Skipping already-applied held migration ${e.relativePath}`);continue}console.log(`Applying held migration ${e.relativePath}`),await h(d,async r=>{await r.execute(t),await r.execute(p(d.dialect,e.id,n))})}}finally{await d.close?.()}}function c(e,t){let n=o.filter(t=>!e.includes(t));if(n.length>0)throw Error(`${t} is missing held migration header fields: ${n.join(`, `)}`)}function l(e){return i(`sha256`).update(e).digest(`hex`)}async function u(e){for(let t of d(e.dialect))await e.execute(t)}function d(e){switch(e){case`postgres`:return[`create schema if not exists drizzle`,`create table if not exists drizzle.__held_migrations (
|
|
2
|
+
id text primary key,
|
|
3
|
+
sha256 text not null,
|
|
4
|
+
applied_at timestamptz not null default now()
|
|
5
|
+
)`];case`sqlite`:return[`create table if not exists __held_migrations (
|
|
6
|
+
id text primary key,
|
|
7
|
+
sha256 text not null,
|
|
8
|
+
applied_at text not null default current_timestamp
|
|
9
|
+
)`]}}function f(e,t){return`select sha256 from ${m(e)} where id = ${g(t)}`}function p(e,t,n){return`insert into ${m(e)} (id, sha256) values (${g(t)}, ${g(n)})`}function m(e){switch(e){case`postgres`:return`drizzle.__held_migrations`;case`sqlite`:return`__held_migrations`}}async function h(e,t){if(e.transaction){await e.transaction(t);return}await e.execute(`begin`);try{await t(e),await e.execute(`commit`)}catch(t){throw await e.execute(`rollback`),t}}function g(e){return`'${e.replaceAll(`'`,`''`)}'`}export{p as heldMigrationInsertQuery,f as heldMigrationSelectQuery,s as migrateHeldMigrations,c as validateHeldMigrationHeader};
|
package/dist/migrate.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{MIGRATIONS_FOLDER as e}from"./config.js";import{createLocalPostgresDb as t,startProjectPostgres as n}from"./local-postgres.js";import r from"./_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{access as i}from"node:fs/promises";import{resolve as a}from"node:path";import{migrate as o}from"drizzle-orm/node-postgres/migrator";const s=a(e);async function c({env:e=process.env}={}){try{var a=r();await i(s);let c=await t({env:a.a(await n({env:e,useSupervisorLease:!0})).env});try{await o(c,{migrationsFolder:s}),console.log(`Applied local migrations from ${s}`)}finally{await c.$client.end()}}catch(e){a.e=e}finally{await a.d()}}export{c as migrateLocalDb};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/migrations.d.ts
|
|
2
|
+
type MigrationFile = {
|
|
3
|
+
id: string;
|
|
4
|
+
path: string;
|
|
5
|
+
relativePath: string;
|
|
6
|
+
};
|
|
7
|
+
type MigrationRisk = {
|
|
8
|
+
code: string;
|
|
9
|
+
line: number;
|
|
10
|
+
match: string;
|
|
11
|
+
reason: string;
|
|
12
|
+
};
|
|
13
|
+
type MigrationScanResult = {
|
|
14
|
+
migration: MigrationFile;
|
|
15
|
+
risks: MigrationRisk[];
|
|
16
|
+
};
|
|
17
|
+
declare function collectDeployMigrationFiles(cwd?: string): Promise<MigrationFile[]>;
|
|
18
|
+
declare function collectHeldMigrationFiles(cwd?: string): Promise<MigrationFile[]>;
|
|
19
|
+
declare function scanDeployMigrationFiles(migrations: MigrationFile[]): Promise<MigrationScanResult[]>;
|
|
20
|
+
declare function scanMigrationSql(sql: string): MigrationRisk[];
|
|
21
|
+
declare function formatMigrationScanFailures(results: MigrationScanResult[]): string;
|
|
22
|
+
declare function formatMigrationList(migrations: MigrationFile[]): string;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { MigrationFile, MigrationRisk, MigrationScanResult, collectDeployMigrationFiles, collectHeldMigrationFiles, formatMigrationList, formatMigrationScanFailures, scanDeployMigrationFiles, scanMigrationSql };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{HELD_MIGRATIONS_FOLDER as e,MIGRATIONS_FOLDER as t}from"./config.js";import{access as n,readFile as r,readdir as i}from"node:fs/promises";import{basename as a,join as o,relative as s,resolve as c}from"node:path";const l=[{code:`drop-schema-object`,pattern:/\bdrop\s+(?:table|view|materialized\s+view|index|sequence|type|schema|function|trigger|policy)\b/giu,reason:`drops schema that the currently deployed app may still depend on`},{code:`drop-column`,pattern:/\balter\s+table\b[^;]*?\bdrop\s+column\b/giu,reason:`removes a column that the currently deployed app may still read or write`},{code:`drop-constraint`,pattern:/\balter\s+table\b[^;]*?\bdrop\s+constraint\b/giu,reason:`removes a constraint outside the staged contract phase`},{code:`truncate`,pattern:/\btruncate\b/giu,reason:`removes data outside the deploy-safe migration path`},{code:`rename-table-or-column`,pattern:/\balter\s+table\b[^;]*?\brename(?:\s+column)?\b/giu,reason:`renames schema in a way old and new app versions cannot both address`},{code:`rewrite-column-type`,pattern:/\balter\s+table\b[^;]*?\balter\s+column\b[^;]*?\btype\b/giu,reason:`rewrites a column type outside a staged expand/contract rollout`},{code:`set-not-null`,pattern:/\balter\s+table\b[^;]*?\balter\s+column\b[^;]*?\bset\s+not\s+null\b/giu,reason:`enforces NOT NULL before a separate backfill and verification step`},{code:`set-default`,pattern:/\balter\s+table\b[^;]*?\balter\s+column\b[^;]*?\bset\s+default\b/giu,reason:`changes default write behavior while old app code may still be running`},{code:`broad-update`,pattern:/\bupdate\b/giu,reason:`changes data outside a separately reviewed backfill or held migration`},{code:`broad-delete`,pattern:/\bdelete\s+from\b/giu,reason:`removes data outside a held migration`}];async function u(e=process.cwd()){return _(c(e,t),e)}async function d(t=process.cwd()){return v(c(t,e),t)}async function f(e){return(await Promise.all(e.map(async e=>({migration:e,risks:p(await r(e.path,`utf8`))})))).filter(e=>e.risks.length>0)}function p(e){let t=S(e),n=g(t),r=[];for(let n of l)for(let i of t.matchAll(n.pattern))r.push(T(e,i,n.code,n.reason));for(let i of t.matchAll(/\balter\s+table\s+(?:if\s+exists\s+|only\s+)?(?<table>(?:"[^"]+"|\w+)(?:\s*\.\s*(?:"[^"]+"|\w+))?)\s+add\s+column\b[^;]*?\bnot\s+null\b/giu))n.has(w(i.groups?.table))||r.push(T(e,i,`add-not-null-column`,`adds a required column to an existing table before a separate compatibility rollout`));for(let i of t.matchAll(/\balter\s+table\s+(?:if\s+exists\s+|only\s+)?(?<table>(?:"[^"]+"|\w+)(?:\s*\.\s*(?:"[^"]+"|\w+))?)\s+add\s+constraint\b[^;]*?\b(?:foreign\s+key|unique|primary\s+key|check)\b/giu))n.has(w(i.groups?.table))||r.push(T(e,i,`add-constraint`,`enforces a constraint on an existing table before a separate validation step`));for(let i of t.matchAll(/\bcreate\s+unique\s+index(?:\s+concurrently)?(?:\s+if\s+not\s+exists)?\s+(?:"[^"]+"|\w+)\s+on\s+(?<table>(?:"[^"]+"|\w+)(?:\s*\.\s*(?:"[^"]+"|\w+))?)/giu))n.has(w(i.groups?.table))||r.push(T(e,i,`create-unique-index`,`adds uniqueness to existing data before a separate dedupe and validation step`));return r.sort((e,t)=>e.line-t.line||e.code.localeCompare(t.code))}function m(e){return e.flatMap(e=>e.risks.map(t=>`${e.migration.relativePath}:${t.line} ${t.code}: ${t.reason} (${t.match.trim()})`)).join(`
|
|
2
|
+
`)}function h(e){return e.length===0?`No migration files found.`:e.map(e=>`- ${e.relativePath}`).join(`
|
|
3
|
+
`)}function g(e){let t=new Set;for(let n of e.matchAll(/\bcreate\s+(?:temporary\s+|temp\s+)?table\s+(?:if\s+not\s+exists\s+)?(?<table>(?:"[^"]+"|\w+)(?:\s*\.\s*(?:"[^"]+"|\w+))?)/giu))t.add(w(n.groups?.table));return t}async function _(e,t){let n=await y(e),r=[];for(let i of n){let n=o(e,i.name);if(i.isDirectory()){let e=o(n,`migration.sql`);await b(e)&&r.push(x(e,t,i.name));continue}i.isFile()&&i.name.endsWith(`.sql`)&&r.push(x(n,t,i.name.replace(/\.sql$/u,``)))}return r.sort((e,t)=>e.id.localeCompare(t.id))}async function v(e,t){return(await y(e)).filter(e=>e.isFile()&&e.name.endsWith(`.sql`)).map(n=>x(o(e,n.name),t,n.name.replace(/\.sql$/u,``))).sort((e,t)=>e.id.localeCompare(t.id))}async function y(e){try{return await i(e,{withFileTypes:!0})}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return[];throw e}}async function b(e){try{return await n(e),!0}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return!1;throw e}}function x(e,t,n=a(e,`.sql`)){return{id:n,path:e,relativePath:s(t,e)}}function S(e){return e.replace(/--[^\n\r]*/gu,C).replace(/\/\*[\s\S]*?\*\//gu,C).replace(/\$([A-Za-z_][A-Za-z0-9_]*)?\$[\s\S]*?\$\1\$/gu,C).replace(/'(?:''|[^'])*'/gu,C)}function C(e){return e.replace(/[^\n\r]/gu,` `)}function w(e){return(e??``).split(`.`).map(e=>e.trim().replace(/^"|"$/gu,``).toLowerCase()).at(-1)??``}function T(e,t,n,r){return{code:n,line:E(e,t.index??0),match:e.slice(t.index??0,(t.index??0)+t[0].length),reason:r}}function E(e,t){let n=1;for(let r=0;r<t;r+=1)e[r]===`
|
|
4
|
+
`&&(n+=1);return n}export{u as collectDeployMigrationFiles,d as collectHeldMigrationFiles,h as formatMigrationList,m as formatMigrationScanFailures,f as scanDeployMigrationFiles,p as scanMigrationSql};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DatabaseClient, DatabaseDialect } from "./client.js";
|
|
2
|
+
import { MigrationFile } from "./migrations.js";
|
|
3
|
+
|
|
4
|
+
//#region src/pending-migrations.d.ts
|
|
5
|
+
declare function filterPendingMigrations(migrations: MigrationFile[], appliedNames: Iterable<string>): MigrationFile[];
|
|
6
|
+
declare function readAppliedMigrationNames(database: DatabaseClient): Promise<Set<string>>;
|
|
7
|
+
declare function appliedMigrationsQuery(dialect: DatabaseDialect): "select name from drizzle.__drizzle_migrations where name is not null" | "select name from __drizzle_migrations where name is not null";
|
|
8
|
+
//#endregion
|
|
9
|
+
export { appliedMigrationsQuery, filterPendingMigrations, readAppliedMigrationNames };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e,t){let n=new Set(t);return e.filter(e=>!n.has(e.id))}async function t(e){try{let t=await e.execute(n(e.dialect));return new Set(t.rows.flatMap(e=>e.name?[e.name]:[]))}catch(e){if(r(e))return new Set;throw e}}function n(e){switch(e){case`postgres`:return`select name from drizzle.__drizzle_migrations where name is not null`;case`sqlite`:return`select name from __drizzle_migrations where name is not null`}}function r(e){return e instanceof Error&&(`code`in e&&(e.code===`42P01`||e.code===`3F000`)||e.message.includes(`no such table`)||e.message.includes(`no such schema`))}export{n as appliedMigrationsQuery,e as filterPendingMigrations,t as readAppliedMigrationNames};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { DatabaseClient, DrizzleExecutor } from "./client.js";
|
|
2
|
+
import { DatabaseConfig } from "@coreframe/config";
|
|
3
|
+
|
|
4
|
+
//#region src/project.d.ts
|
|
5
|
+
type CreateProjectDatabaseOptions = {
|
|
6
|
+
config?: DatabaseConfig;
|
|
7
|
+
env?: NodeJS.ProcessEnv;
|
|
8
|
+
};
|
|
9
|
+
type DrizzleDatabase = DrizzleExecutor & {
|
|
10
|
+
$client?: {
|
|
11
|
+
close?(): Promise<void> | void;
|
|
12
|
+
end?(): Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
declare function createProjectDeployDatabase({
|
|
16
|
+
config,
|
|
17
|
+
env
|
|
18
|
+
}?: CreateProjectDatabaseOptions): Promise<DatabaseClient>;
|
|
19
|
+
declare function createProjectPostgresDrizzleDatabase({
|
|
20
|
+
env
|
|
21
|
+
}?: CreateProjectDatabaseOptions): Promise<DrizzleDatabase>;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { CreateProjectDatabaseOptions, createProjectDeployDatabase, createProjectPostgresDrizzleDatabase };
|
package/dist/project.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createDatabaseAdapter as e}from"./client.js";import{importProjectDependency as t,importProjectModule as n,readProjectConfig as r}from"@coreframe/config";const i=`src/db/relations.ts`;async function a({config:e,env:t=process.env}={}){let n=(e??(await r()).database)?.dialect??`postgres`;if(n===`postgres`)return s({env:t});if(n===`sqlite`)return c({env:t});throw Error(`Unsupported database dialect "${n}".`)}async function o({env:e=process.env}={}){let r=e.DATABASE_URL;if(!r)throw Error(`DATABASE_URL is required to create the PostgreSQL database.`);let[{drizzle:a},{relations:o}]=await Promise.all([t(`drizzle-orm/node-postgres`),n(i)]);return a({connection:{connectionString:r,max:1},relations:o})}async function s({env:t}){let n=await o({env:t});return e({database:n,dialect:`postgres`,close:()=>n.$client?.end?.()??Promise.resolve()})}async function c({env:r}){let a=r.TURSO_DATABASE_URL??r.DATABASE_URL;if(!a)throw Error(`TURSO_DATABASE_URL or DATABASE_URL is required to create the deploy database.`);let[{createClient:o},{drizzle:s},{relations:c}]=await Promise.all([t(`@libsql/client`),t(`drizzle-orm/libsql`),n(i)]),l=o({url:a,authToken:r.TURSO_AUTH_TOKEN});return e({database:s({client:l,relations:c}),dialect:`sqlite`,close:async()=>{await l.close?.()}})}export{a as createProjectDeployDatabase,o as createProjectPostgresDrizzleDatabase};
|
package/dist/reset.d.ts
ADDED
package/dist/reset.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createLocalPostgresDb as e,startProjectPostgres as t}from"./local-postgres.js";import n from"./_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{sql as r}from"drizzle-orm";async function i({env:i=process.env}={}){try{var a=n();let o=await e({env:a.a(await t({env:i,useSupervisorLease:!0})).env});try{await o.transaction(async e=>{await e.execute(r`drop schema if exists public cascade`),await e.execute(r`drop schema if exists drizzle cascade`),await e.execute(r`create schema public`)}),console.log(`Reset local PostgreSQL schema`)}finally{await o.$client.end()}}catch(e){a.e=e}finally{await a.d()}}export{i as resetLocalDb};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/schema-check.d.ts
|
|
2
|
+
type CheckSchemaPushOptions = {
|
|
3
|
+
env?: NodeJS.ProcessEnv;
|
|
4
|
+
seed?: boolean;
|
|
5
|
+
};
|
|
6
|
+
declare function checkSchemaPush({
|
|
7
|
+
env,
|
|
8
|
+
seed
|
|
9
|
+
}?: CheckSchemaPushOptions): Promise<void>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { checkSchemaPush };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createLocalPostgresDb as e,startTemporaryProjectPostgres as t}from"./local-postgres.js";import n from"./_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{inferSchemaDialect as r,planLocalSchemaPush as i}from"./schema-push.js";import{runProjectSeed as a}from"./seed.js";import{importProjectModule as o}from"@coreframe/config";async function s({env:s=process.env,seed:l=!1}={}){try{var u=n();console.log(`Checking Drizzle schema against a throwaway PostgreSQL database`);let d=u.a(await t({env:s})),f=await o(`src/db/schema.ts`),p=await e({env:d.env});try{let e=r(f);if(e!==`postgres`)throw Error(`Temporary schema push checks require PostgreSQL schema, but found ${e}.`);let t=await i(e,f,p);t.sqlStatements.length===0?console.log(`Drizzle schema check passed with no changes`):(console.log(`Applying ${t.sqlStatements.length} statement(s) to throwaway database`),await t.apply(),console.log(`Drizzle schema check passed`)),l&&(console.log(`Seeding throwaway database`),await a(`default`,{env:d.env}),console.log(`Throwaway database seed check passed`))}finally{await c(p)}}catch(e){u.e=e}finally{await u.d()}}function c(e){return e.$client.end()}export{s as checkSchemaPush};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DatabaseDialect } from "./client.js";
|
|
2
|
+
|
|
3
|
+
//#region src/schema-push.d.ts
|
|
4
|
+
type SchemaPushPlan = {
|
|
5
|
+
sqlStatements: string[];
|
|
6
|
+
hints: Array<{
|
|
7
|
+
hint: string;
|
|
8
|
+
statement?: string;
|
|
9
|
+
}>;
|
|
10
|
+
apply(): Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
type SchemaPushModule = {
|
|
13
|
+
pushSchema(schema: Record<string, unknown>, db: never, migrationsConfig?: {
|
|
14
|
+
table?: string;
|
|
15
|
+
schema?: string;
|
|
16
|
+
}): Promise<SchemaPushPlan>;
|
|
17
|
+
};
|
|
18
|
+
type ImportSchemaPushModule = (dialect: DatabaseDialect) => Promise<SchemaPushModule>;
|
|
19
|
+
declare function planLocalSchemaPush(dialect: DatabaseDialect, schema: Record<string, unknown>, db: unknown, importModule?: ImportSchemaPushModule): Promise<SchemaPushPlan>;
|
|
20
|
+
declare function inferSchemaDialect(schema: Record<string, unknown>): DatabaseDialect;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { SchemaPushModule, SchemaPushPlan, inferSchemaDialect, planLocalSchemaPush };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{resolve as e}from"node:path";import{is as t}from"drizzle-orm";import{pathToFileURL as n}from"node:url";import{PgMaterializedView as r,PgTable as i,PgView as a}from"drizzle-orm/pg-core";import{SQLiteTable as o,SQLiteView as s}from"drizzle-orm/sqlite-core";import{resolve as c}from"import-meta-resolve";const l={postgres:`drizzle-kit/payload/postgres`,sqlite:`drizzle-kit/payload/sqlite`};async function u(e,t,n,r=f){let{pushSchema:i}=await r(e);return i(t,n)}function d(e){let t=!1,n=!1;for(let r of Object.values(e))t||=p(r),n||=m(r);if(t&&n)throw Error(`Local database schema mixes PostgreSQL and SQLite entities.`);if(t)return`postgres`;if(n)return`sqlite`;throw Error(`Could not infer local database dialect from src/db/schema.ts.`)}async function f(t){return await import(c(l[t],n(e(`package.json`)).href))}function p(e){return t(e,i)||t(e,a)||t(e,r)}function m(e){return t(e,o)||t(e,s)}export{d as inferSchemaDialect,u as planLocalSchemaPush};
|
package/dist/seed.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SeedProfile } from "./config.js";
|
|
2
|
+
|
|
3
|
+
//#region src/seed.d.ts
|
|
4
|
+
type SeedLocalDbOptions = {
|
|
5
|
+
env?: NodeJS.ProcessEnv;
|
|
6
|
+
};
|
|
7
|
+
type RunProjectSeedOptions = {
|
|
8
|
+
env: NodeJS.ProcessEnv;
|
|
9
|
+
seedEntryPoint?: string;
|
|
10
|
+
};
|
|
11
|
+
declare function seedLocalDb(profile?: SeedProfile, {
|
|
12
|
+
env
|
|
13
|
+
}?: SeedLocalDbOptions): Promise<void>;
|
|
14
|
+
declare function runProjectSeed(profile: SeedProfile | undefined, {
|
|
15
|
+
env,
|
|
16
|
+
seedEntryPoint
|
|
17
|
+
}: RunProjectSeedOptions): Promise<void>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { runProjectSeed, seedLocalDb };
|
package/dist/seed.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getSeedProfile as e}from"./config.js";import{startProjectPostgres as t}from"./local-postgres.js";import n from"./_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{importProjectModule as r,readProjectConfig as i}from"@coreframe/config";async function a(r=e(),{env:a=process.env}={}){try{var c=n();let e=await i(),l=s(e);await o(r,{env:c.a(await t({config:e,env:a,useSupervisorLease:!0})).env,seedEntryPoint:l}),console.log(`Seeded local database with "${r}" profile`)}catch(e){c.e=e}finally{await c.d()}}async function o(t=e(),{env:n,seedEntryPoint:a}){a??=s(await i()),await l(n,async()=>{await c(await r(a),a)({env:n,profile:t})})}function s(e){let t=e.database?.seed;if(!t)throw Error(`coreframe.config.ts must configure database.seed before running db seed.`);return t}function c(e,t){let n=e.default??e.seed??e.seedLocalDb;if(!n)throw Error(`${t} must export a default seed function, seed function, or seedLocalDb function.`);return n}async function l(e,t){let n=new Map;for(let[t,r]of Object.entries(e))n.set(t,process.env[t]),r===void 0?delete process.env[t]:process.env[t]=r;try{await t()}finally{for(let[e,t]of n)t===void 0?delete process.env[e]:process.env[e]=t}}export{o as runProjectSeed,a as seedLocalDb};
|
package/dist/setup.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SeedProfile } from "./config.js";
|
|
2
|
+
|
|
3
|
+
//#region src/setup.d.ts
|
|
4
|
+
type SetupLocalDbOptions = {
|
|
5
|
+
env?: NodeJS.ProcessEnv;
|
|
6
|
+
};
|
|
7
|
+
declare function setupLocalDb(profile?: SeedProfile, {
|
|
8
|
+
env
|
|
9
|
+
}?: SetupLocalDbOptions): Promise<void>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { setupLocalDb };
|
package/dist/setup.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{startProjectPostgres as e}from"./local-postgres.js";import t from"./_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{seedLocalDb as n}from"./seed.js";import{migrateLocalDb as r}from"./migrate.js";import{resetLocalDb as i}from"./reset.js";async function a(a=`default`,{env:o=process.env}={}){try{var s=t();let c=s.a(await e({env:o,useSupervisorLease:!0})).env;await i({env:c}),await r({env:c}),await n(a,{env:c})}catch(e){s.e=e}finally{await s.d()}}export{a as setupLocalDb};
|
package/package.json
CHANGED
|
@@ -1,13 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coreframe/db",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Database setup, migration, seeding, and local PostgreSQL utilities for Coreframe apps",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/airlock-labs/coreframe.git",
|
|
8
|
+
"directory": "node/db"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"drizzle-orm": "1.0.0-rc.4",
|
|
22
|
+
"import-meta-resolve": "^4.2.0",
|
|
23
|
+
"local-postgres": "0.3.1",
|
|
24
|
+
"nano-spawn": "^2.1.0",
|
|
25
|
+
"@coreframe/config": "^0.1.1",
|
|
26
|
+
"@coreframe/dev-supervisor": "^0.1.1"
|
|
8
27
|
},
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^26.1.0",
|
|
30
|
+
"tsdown": "0.22.3",
|
|
31
|
+
"typescript": "6.0.3",
|
|
32
|
+
"vitest": "^4.1.9"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsdown",
|
|
36
|
+
"test": "vitest run --reporter=minimal",
|
|
37
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
38
|
+
"format": "oxlint --fix && oxfmt",
|
|
39
|
+
"lint": "oxlint"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/readme.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Coming soon...
|