@storyshelf/db-postgres 0.0.0-stage → 0.5.4
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/LICENSE +21 -0
- package/README.md +18 -2
- package/dist/ddl.d.mts +8 -0
- package/dist/ddl.mjs +321 -0
- package/dist/ddl.mjs.map +1 -0
- package/dist/drizzle-factory-pg.mjs +77 -0
- package/dist/drizzle-factory-pg.mjs.map +1 -0
- package/dist/drizzle-factory.mjs +42 -0
- package/dist/drizzle-factory.mjs.map +1 -0
- package/dist/index.d.mts +50 -0
- package/dist/index.mjs +51 -0
- package/dist/index.mjs.map +1 -0
- package/dist/migrate.mjs +91 -0
- package/dist/migrate.mjs.map +1 -0
- package/dist/neon-http.d.mts +27 -0
- package/dist/neon-http.mjs +66 -0
- package/dist/neon-http.mjs.map +1 -0
- package/dist/neon.d.mts +29 -0
- package/dist/neon.mjs +59 -0
- package/dist/neon.mjs.map +1 -0
- package/dist/pg.d.mts +33 -0
- package/dist/pg.mjs +56 -0
- package/dist/pg.mjs.map +1 -0
- package/dist/pglite.d.mts +28 -0
- package/dist/pglite.mjs +54 -0
- package/dist/pglite.mjs.map +1 -0
- package/dist/schema/baseline.d.mts +197 -0
- package/dist/schema/baseline.mjs +29 -0
- package/dist/schema/baseline.mjs.map +1 -0
- package/dist/schema/build.d.mts +380 -0
- package/dist/schema/build.mjs +39 -0
- package/dist/schema/build.mjs.map +1 -0
- package/dist/schema/capture-attempt.d.mts +445 -0
- package/dist/schema/capture-attempt.mjs +58 -0
- package/dist/schema/capture-attempt.mjs.map +1 -0
- package/dist/schema/comment.d.mts +197 -0
- package/dist/schema/comment.mjs +32 -0
- package/dist/schema/comment.mjs.map +1 -0
- package/dist/schema/content-refs.d.mts +89 -0
- package/dist/schema/content-refs.mjs +22 -0
- package/dist/schema/content-refs.mjs.map +1 -0
- package/dist/schema/label.d.mts +262 -0
- package/dist/schema/label.mjs +36 -0
- package/dist/schema/label.mjs.map +1 -0
- package/dist/schema/member.d.mts +129 -0
- package/dist/schema/member.mjs +23 -0
- package/dist/schema/member.mjs.map +1 -0
- package/dist/schema/notification.d.mts +316 -0
- package/dist/schema/notification.mjs +42 -0
- package/dist/schema/notification.mjs.map +1 -0
- package/dist/schema/project-group-mapping.d.mts +110 -0
- package/dist/schema/project-group-mapping.mjs +21 -0
- package/dist/schema/project-group-mapping.mjs.map +1 -0
- package/dist/schema/project.d.mts +337 -0
- package/dist/schema/project.mjs +35 -0
- package/dist/schema/project.mjs.map +1 -0
- package/dist/schema/snapshot.d.mts +452 -0
- package/dist/schema/snapshot.mjs +47 -0
- package/dist/schema/snapshot.mjs.map +1 -0
- package/dist/schema/status-config.d.mts +143 -0
- package/dist/schema/status-config.mjs +26 -0
- package/dist/schema/status-config.mjs.map +1 -0
- package/dist/schema/token.d.mts +143 -0
- package/dist/schema/token.mjs +27 -0
- package/dist/schema/token.mjs.map +1 -0
- package/dist/schema/user-invite.d.mts +125 -0
- package/dist/schema/user-invite.mjs +28 -0
- package/dist/schema/user-invite.mjs.map +1 -0
- package/dist/schema/user.d.mts +146 -0
- package/dist/schema/user.mjs +25 -0
- package/dist/schema/user.mjs.map +1 -0
- package/dist/schema/webhook.d.mts +143 -0
- package/dist/schema/webhook.mjs +26 -0
- package/dist/schema/webhook.mjs.map +1 -0
- package/dist/schema.d.mts +47 -0
- package/dist/schema.mjs +46 -0
- package/dist/schema.mjs.map +1 -0
- package/dist/vercel.d.mts +26 -0
- package/dist/vercel.mjs +48 -0
- package/dist/vercel.mjs.map +1 -0
- package/package.json +134 -4
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { createDrizzlePgAdapter } from "./drizzle-factory-pg.mjs";
|
|
5
|
+
import { runMigrations } from "./migrate.mjs";
|
|
6
|
+
import { schema } from "./schema.mjs";
|
|
7
|
+
import { drizzle } from "drizzle-orm/postgres-js";
|
|
8
|
+
import postgres from "postgres";
|
|
9
|
+
//#region src/index.ts
|
|
10
|
+
/**
|
|
11
|
+
* Create a Postgres-backed DatabaseAdapter using postgres.js and Drizzle ORM.
|
|
12
|
+
*
|
|
13
|
+
* Works with self-hosted Postgres, AWS RDS, GCP Cloud SQL, Supabase, Neon,
|
|
14
|
+
* and Azure Database for PostgreSQL — all speak the standard wire protocol.
|
|
15
|
+
* For provider-specific TLS and pooling notes, see the deployment guide.
|
|
16
|
+
*/
|
|
17
|
+
function createPostgresDatabase(options) {
|
|
18
|
+
const connectionString = options.connectionString ?? options.url;
|
|
19
|
+
if (!options.client && !connectionString) throw new Error("createPostgresDatabase: provide `url` (or `connectionString`) or a preconfigured `client`");
|
|
20
|
+
const client = options.client ?? postgres(connectionString, {
|
|
21
|
+
ssl: options.ssl,
|
|
22
|
+
prepare: options.prepare,
|
|
23
|
+
max: options.max,
|
|
24
|
+
idle_timeout: options.idleTimeout,
|
|
25
|
+
connect_timeout: options.connectTimeout
|
|
26
|
+
});
|
|
27
|
+
const db = drizzle(client, { schema });
|
|
28
|
+
return createDrizzlePgAdapter(db, {
|
|
29
|
+
metadata: {
|
|
30
|
+
name: "Postgres",
|
|
31
|
+
version: globalThis.__PKG_VERSION__ ?? "0.0.0",
|
|
32
|
+
description: "Postgres database adapter (postgres.js + Drizzle)",
|
|
33
|
+
kind: "postgres",
|
|
34
|
+
category: "database"
|
|
35
|
+
},
|
|
36
|
+
tables: schema,
|
|
37
|
+
migrate: async () => {
|
|
38
|
+
await runMigrations(async (sql) => await client.unsafe(sql));
|
|
39
|
+
},
|
|
40
|
+
close: async () => {
|
|
41
|
+
await client.end();
|
|
42
|
+
},
|
|
43
|
+
ping: async () => {
|
|
44
|
+
await client.unsafe("SELECT 1");
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { createPostgresDatabase };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { DatabaseAdapter } from \"@storyshelf/core/adapter/database\";\nimport { drizzle } from \"drizzle-orm/postgres-js\";\nimport postgres from \"postgres\";\nimport { createDrizzlePgAdapter } from \"./drizzle-factory-pg.ts\";\nimport { runMigrations } from \"./migrate.ts\";\nimport { schema } from \"./schema/index.ts\";\n\ndeclare const __PKG_VERSION__: string | undefined;\n\n/**\n * TLS options for Postgres connections. When `true`, the driver negotiates\n * TLS with the server's default CA. When an object, `ca`/`cert`/`key` are\n * forwarded to `postgres.js` for mutual TLS (e.g. GCP Cloud SQL) or custom\n * RDS bundles.\n */\nexport type PostgresSslOptions = boolean | { ca?: string; cert?: string; key?: string };\n\n/**\n * Options for creating a Postgres-backed DatabaseAdapter.\n *\n * `url` (or `connectionString`) is the Postgres connection string\n * (`postgres://user:pass@host:5432/db`). For managed providers, see the\n * deployment guide for per-provider recipes (RDS CA bundle, Cloud SQL\n * mutual TLS, Supabase pooler port `6543` with `prepare: false`, etc.).\n *\n * When `client` is supplied, all other connection options are ignored and\n * the caller owns the lifecycle of the client. This is the hook for\n * advanced setups (IAM token refresh, pre-configured TLS, etc.) and for\n * hermetic tests.\n */\nexport interface PostgresDatabaseOptions {\n url?: string;\n connectionString?: string;\n ssl?: PostgresSslOptions;\n prepare?: boolean;\n max?: number;\n idleTimeout?: number;\n connectTimeout?: number;\n client?: ReturnType<typeof postgres>;\n}\n\n/**\n * Create a Postgres-backed DatabaseAdapter using postgres.js and Drizzle ORM.\n *\n * Works with self-hosted Postgres, AWS RDS, GCP Cloud SQL, Supabase, Neon,\n * and Azure Database for PostgreSQL — all speak the standard wire protocol.\n * For provider-specific TLS and pooling notes, see the deployment guide.\n */\nexport function createPostgresDatabase(options: PostgresDatabaseOptions): DatabaseAdapter {\n const connectionString = options.connectionString ?? options.url;\n if (!options.client && !connectionString) {\n throw new Error(\n \"createPostgresDatabase: provide `url` (or `connectionString`) or a preconfigured `client`\",\n );\n }\n\n const client =\n options.client ??\n postgres(connectionString as string, {\n ssl: options.ssl as never,\n prepare: options.prepare,\n max: options.max,\n idle_timeout: options.idleTimeout,\n connect_timeout: options.connectTimeout,\n });\n\n // oxlint-disable-next-line typescript/no-unsafe-argument -- postgres instance may be from isolated store vs workspace\n const db = drizzle(client as never, { schema });\n\n return createDrizzlePgAdapter(db, {\n metadata: {\n name: \"Postgres\",\n version: (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\",\n description: \"Postgres database adapter (postgres.js + Drizzle)\",\n kind: \"postgres\",\n category: \"database\",\n },\n tables: schema as unknown as DatabaseAdapter[\"tables\"],\n migrate: async () => {\n await runMigrations(async (sql: string) => await client.unsafe(sql));\n },\n close: async () => {\n await client.end();\n },\n ping: async () => {\n await client.unsafe(\"SELECT 1\");\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgDA,SAAgB,uBAAuB,SAAmD;CACxF,MAAM,mBAAmB,QAAQ,oBAAoB,QAAQ;CAC7D,IAAI,CAAC,QAAQ,UAAU,CAAC,kBACtB,MAAM,IAAI,MACR,2FACF;CAGF,MAAM,SACJ,QAAQ,UACR,SAAS,kBAA4B;EACnC,KAAK,QAAQ;EACb,SAAS,QAAQ;EACjB,KAAK,QAAQ;EACb,cAAc,QAAQ;EACtB,iBAAiB,QAAQ;CAC3B,CAAC;CAGH,MAAM,KAAK,QAAQ,QAAiB,EAAE,OAAO,CAAC;CAE9C,OAAO,uBAAuB,IAAI;EAChC,UAAU;GACR,MAAM;GACN,SAAU,WAAuD,mBAAmB;GACpF,aAAa;GACb,MAAM;GACN,UAAU;EACZ;EACA,QAAQ;EACR,SAAS,YAAY;GACnB,MAAM,cAAc,OAAO,QAAgB,MAAM,OAAO,OAAO,GAAG,CAAC;EACrE;EACA,OAAO,YAAY;GACjB,MAAM,OAAO,IAAI;EACnB;EACA,MAAM,YAAY;GAChB,MAAM,OAAO,OAAO,UAAU;EAChC;CACF,CAAC;AACH"}
|
package/dist/migrate.mjs
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { DDL } from "./ddl.mjs";
|
|
5
|
+
//#region src/migrate.ts
|
|
6
|
+
/**
|
|
7
|
+
* Shared Postgres migrations for every `db-postgres` preset.
|
|
8
|
+
*
|
|
9
|
+
* The DDL plus idempotent follow-up ALTERs run through a minimal row-query
|
|
10
|
+
* runner so all drivers (`postgres.js`, `node-postgres`, Neon, …) share one
|
|
11
|
+
* migration path instead of drifting per preset.
|
|
12
|
+
*/
|
|
13
|
+
const STORYBOOK_META_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS storybook_meta TEXT";
|
|
14
|
+
const EXECUTE_PLAY_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS execute_play BOOLEAN NOT NULL DEFAULT false";
|
|
15
|
+
const PLAY_TIMEOUT_MS_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS play_timeout_ms INTEGER NOT NULL DEFAULT 10000";
|
|
16
|
+
const RUN_A11Y_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS run_a11y BOOLEAN NOT NULL DEFAULT false";
|
|
17
|
+
const PROJECT_BROWSER_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS browser TEXT NOT NULL DEFAULT 'chromium'";
|
|
18
|
+
const PROJECT_VIEWPORTS_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS viewports TEXT";
|
|
19
|
+
const PROJECT_AUTOMIGRATE_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS automigrate BOOLEAN NOT NULL DEFAULT false";
|
|
20
|
+
const SNAPSHOT_INFRA_HASH_ALTER = "ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS infra_hash TEXT";
|
|
21
|
+
const SNAPSHOT_INHERITED_ALTER = "ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS inherited BOOLEAN NOT NULL DEFAULT false";
|
|
22
|
+
const SNAPSHOT_BASELINE_ID_ALTER = "ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS baseline_id TEXT";
|
|
23
|
+
const SNAPSHOT_BASELINE_VERSION_ALTER = "ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS baseline_version TEXT";
|
|
24
|
+
const BASELINE_INFRA_HASH_ALTER = "ALTER TABLE baselines ADD COLUMN IF NOT EXISTS infra_hash TEXT";
|
|
25
|
+
const BUILD_AFFECTED_ONLY_ALTER = "ALTER TABLE builds ADD COLUMN IF NOT EXISTS affected_only BOOLEAN NOT NULL DEFAULT true";
|
|
26
|
+
const BUILD_BASELINE_SHA_ALTER = "ALTER TABLE builds ADD COLUMN IF NOT EXISTS baseline_sha TEXT";
|
|
27
|
+
const BUILD_CHANGED_FILES_ALTER = "ALTER TABLE builds ADD COLUMN IF NOT EXISTS changed_files TEXT";
|
|
28
|
+
const BUILD_AFFECTED_PATHS_ALTER = "ALTER TABLE builds ADD COLUMN IF NOT EXISTS affected_import_paths TEXT";
|
|
29
|
+
const WEBHOOK_SECRET_ALTER = "ALTER TABLE webhooks ADD COLUMN IF NOT EXISTS secret_encrypted TEXT NOT NULL DEFAULT ''";
|
|
30
|
+
const WEBHOOK_SECRET_DROP = "ALTER TABLE webhooks DROP COLUMN IF EXISTS secret";
|
|
31
|
+
const TOKEN_USER_ALTER = "ALTER TABLE tokens ADD COLUMN IF NOT EXISTS user_id TEXT REFERENCES users(id) ON DELETE CASCADE";
|
|
32
|
+
const MEMBER_SOURCE_ALTER = "ALTER TABLE project_members ADD COLUMN IF NOT EXISTS source TEXT NOT NULL DEFAULT 'manual'";
|
|
33
|
+
/**
|
|
34
|
+
* Re-runs share `(project_id, git_sha)` by design (ULID build ids), so the
|
|
35
|
+
* lookup index must not be unique. Existing databases created it UNIQUE —
|
|
36
|
+
* drop and recreate it plain.
|
|
37
|
+
*/
|
|
38
|
+
const BUILDS_GITSHA_DEDUP = "DROP INDEX IF EXISTS builds_project_gitsha_idx";
|
|
39
|
+
const BUILDS_GITSHA_INDEX = "CREATE INDEX IF NOT EXISTS builds_project_gitsha_idx ON builds (project_id, git_sha)";
|
|
40
|
+
async function migrateSnapshotColumns(run) {
|
|
41
|
+
await execIgnore(run, SNAPSHOT_INFRA_HASH_ALTER);
|
|
42
|
+
await execIgnore(run, SNAPSHOT_INHERITED_ALTER);
|
|
43
|
+
await execIgnore(run, SNAPSHOT_BASELINE_ID_ALTER);
|
|
44
|
+
await execIgnore(run, SNAPSHOT_BASELINE_VERSION_ALTER);
|
|
45
|
+
}
|
|
46
|
+
async function migrateProjectExtras(run) {
|
|
47
|
+
await execIgnore(run, EXECUTE_PLAY_ALTER);
|
|
48
|
+
await execIgnore(run, PLAY_TIMEOUT_MS_ALTER);
|
|
49
|
+
await execIgnore(run, PROJECT_AUTOMIGRATE_ALTER);
|
|
50
|
+
await migrateSnapshotColumns(run);
|
|
51
|
+
await execIgnore(run, BASELINE_INFRA_HASH_ALTER);
|
|
52
|
+
await execIgnore(run, BUILD_AFFECTED_ONLY_ALTER);
|
|
53
|
+
await execIgnore(run, BUILD_BASELINE_SHA_ALTER);
|
|
54
|
+
await execIgnore(run, BUILD_CHANGED_FILES_ALTER);
|
|
55
|
+
await execIgnore(run, BUILD_AFFECTED_PATHS_ALTER);
|
|
56
|
+
}
|
|
57
|
+
/** Run the full migration set through any row-query runner. */
|
|
58
|
+
async function runMigrations(run) {
|
|
59
|
+
await run(DDL);
|
|
60
|
+
await migrateCoreColumns(run);
|
|
61
|
+
await migrateProjectExtras(run);
|
|
62
|
+
await migrateCommentsTable(run);
|
|
63
|
+
}
|
|
64
|
+
async function migrateCoreColumns(run) {
|
|
65
|
+
await execIgnore(run, STORYBOOK_META_ALTER);
|
|
66
|
+
await execIgnore(run, RUN_A11Y_ALTER);
|
|
67
|
+
await execIgnore(run, PROJECT_BROWSER_ALTER);
|
|
68
|
+
await execIgnore(run, PROJECT_VIEWPORTS_ALTER);
|
|
69
|
+
await execIgnore(run, WEBHOOK_SECRET_ALTER);
|
|
70
|
+
await execIgnore(run, WEBHOOK_SECRET_DROP);
|
|
71
|
+
await execIgnore(run, TOKEN_USER_ALTER);
|
|
72
|
+
await execIgnore(run, MEMBER_SOURCE_ALTER);
|
|
73
|
+
await execIgnore(run, BUILDS_GITSHA_DEDUP);
|
|
74
|
+
await execIgnore(run, BUILDS_GITSHA_INDEX);
|
|
75
|
+
}
|
|
76
|
+
async function execIgnore(run, sql) {
|
|
77
|
+
try {
|
|
78
|
+
await run(sql);
|
|
79
|
+
} catch {}
|
|
80
|
+
}
|
|
81
|
+
async function migrateCommentsTable(run) {
|
|
82
|
+
try {
|
|
83
|
+
const isNullable = (await run("SELECT is_nullable FROM information_schema.columns WHERE table_name='comments' AND column_name='user_id'"))[0]?.is_nullable;
|
|
84
|
+
if (isNullable === "YES" || isNullable === void 0) return;
|
|
85
|
+
await run("ALTER TABLE comments ALTER COLUMN user_id DROP NOT NULL");
|
|
86
|
+
} catch {}
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
export { runMigrations };
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=migrate.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.mjs","names":[],"sources":["../src/migrate.ts"],"sourcesContent":["/**\n * Shared Postgres migrations for every `db-postgres` preset.\n *\n * The DDL plus idempotent follow-up ALTERs run through a minimal row-query\n * runner so all drivers (`postgres.js`, `node-postgres`, Neon, …) share one\n * migration path instead of drifting per preset.\n */\nimport { DDL } from \"./ddl.ts\";\n\n/** Minimal row-query surface a migration runner needs. */\nexport type MigrationRunner = (sql: string) => Promise<unknown[]>;\n\nconst STORYBOOK_META_ALTER = \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS storybook_meta TEXT\";\nconst EXECUTE_PLAY_ALTER =\n \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS execute_play BOOLEAN NOT NULL DEFAULT false\";\nconst PLAY_TIMEOUT_MS_ALTER =\n \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS play_timeout_ms INTEGER NOT NULL DEFAULT 10000\";\nconst RUN_A11Y_ALTER =\n \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS run_a11y BOOLEAN NOT NULL DEFAULT false\";\nconst PROJECT_BROWSER_ALTER =\n \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS browser TEXT NOT NULL DEFAULT 'chromium'\";\nconst PROJECT_VIEWPORTS_ALTER = \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS viewports TEXT\";\nconst PROJECT_AUTOMIGRATE_ALTER =\n \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS automigrate BOOLEAN NOT NULL DEFAULT false\";\nconst SNAPSHOT_INFRA_HASH_ALTER = \"ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS infra_hash TEXT\";\nconst SNAPSHOT_INHERITED_ALTER =\n \"ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS inherited BOOLEAN NOT NULL DEFAULT false\";\nconst SNAPSHOT_BASELINE_ID_ALTER =\n \"ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS baseline_id TEXT\";\nconst SNAPSHOT_BASELINE_VERSION_ALTER =\n \"ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS baseline_version TEXT\";\nconst BASELINE_INFRA_HASH_ALTER = \"ALTER TABLE baselines ADD COLUMN IF NOT EXISTS infra_hash TEXT\";\nconst BUILD_AFFECTED_ONLY_ALTER =\n \"ALTER TABLE builds ADD COLUMN IF NOT EXISTS affected_only BOOLEAN NOT NULL DEFAULT true\";\nconst BUILD_BASELINE_SHA_ALTER = \"ALTER TABLE builds ADD COLUMN IF NOT EXISTS baseline_sha TEXT\";\nconst BUILD_CHANGED_FILES_ALTER = \"ALTER TABLE builds ADD COLUMN IF NOT EXISTS changed_files TEXT\";\nconst BUILD_AFFECTED_PATHS_ALTER =\n \"ALTER TABLE builds ADD COLUMN IF NOT EXISTS affected_import_paths TEXT\";\nconst WEBHOOK_SECRET_ALTER =\n \"ALTER TABLE webhooks ADD COLUMN IF NOT EXISTS secret_encrypted TEXT NOT NULL DEFAULT ''\";\nconst WEBHOOK_SECRET_DROP = \"ALTER TABLE webhooks DROP COLUMN IF EXISTS secret\";\nconst TOKEN_USER_ALTER =\n \"ALTER TABLE tokens ADD COLUMN IF NOT EXISTS user_id TEXT REFERENCES users(id) ON DELETE CASCADE\";\nconst MEMBER_SOURCE_ALTER =\n \"ALTER TABLE project_members ADD COLUMN IF NOT EXISTS source TEXT NOT NULL DEFAULT 'manual'\";\n/**\n * Re-runs share `(project_id, git_sha)` by design (ULID build ids), so the\n * lookup index must not be unique. Existing databases created it UNIQUE —\n * drop and recreate it plain.\n */\nconst BUILDS_GITSHA_DEDUP = \"DROP INDEX IF EXISTS builds_project_gitsha_idx\";\nconst BUILDS_GITSHA_INDEX =\n \"CREATE INDEX IF NOT EXISTS builds_project_gitsha_idx ON builds (project_id, git_sha)\";\n\nasync function migrateSnapshotColumns(run: MigrationRunner): Promise<void> {\n await execIgnore(run, SNAPSHOT_INFRA_HASH_ALTER);\n await execIgnore(run, SNAPSHOT_INHERITED_ALTER);\n await execIgnore(run, SNAPSHOT_BASELINE_ID_ALTER);\n await execIgnore(run, SNAPSHOT_BASELINE_VERSION_ALTER);\n}\n\nasync function migrateProjectExtras(run: MigrationRunner): Promise<void> {\n await execIgnore(run, EXECUTE_PLAY_ALTER);\n await execIgnore(run, PLAY_TIMEOUT_MS_ALTER);\n await execIgnore(run, PROJECT_AUTOMIGRATE_ALTER);\n await migrateSnapshotColumns(run);\n await execIgnore(run, BASELINE_INFRA_HASH_ALTER);\n await execIgnore(run, BUILD_AFFECTED_ONLY_ALTER);\n await execIgnore(run, BUILD_BASELINE_SHA_ALTER);\n await execIgnore(run, BUILD_CHANGED_FILES_ALTER);\n await execIgnore(run, BUILD_AFFECTED_PATHS_ALTER);\n}\n\n/** Run the full migration set through any row-query runner. */\nexport async function runMigrations(run: MigrationRunner): Promise<void> {\n await run(DDL);\n await migrateCoreColumns(run);\n await migrateProjectExtras(run);\n await migrateCommentsTable(run);\n}\n\nasync function migrateCoreColumns(run: MigrationRunner): Promise<void> {\n await execIgnore(run, STORYBOOK_META_ALTER);\n await execIgnore(run, RUN_A11Y_ALTER);\n await execIgnore(run, PROJECT_BROWSER_ALTER);\n await execIgnore(run, PROJECT_VIEWPORTS_ALTER);\n await execIgnore(run, WEBHOOK_SECRET_ALTER);\n await execIgnore(run, WEBHOOK_SECRET_DROP);\n await execIgnore(run, TOKEN_USER_ALTER);\n await execIgnore(run, MEMBER_SOURCE_ALTER);\n await execIgnore(run, BUILDS_GITSHA_DEDUP);\n await execIgnore(run, BUILDS_GITSHA_INDEX);\n}\n\nasync function execIgnore(run: MigrationRunner, sql: string): Promise<void> {\n try {\n await run(sql);\n } catch {\n // idempotent — already migrated\n }\n}\n\nasync function migrateCommentsTable(run: MigrationRunner): Promise<void> {\n try {\n const rows = (await run(\n \"SELECT is_nullable FROM information_schema.columns WHERE table_name='comments' AND column_name='user_id'\",\n )) as { is_nullable: string }[];\n const isNullable = rows[0]?.is_nullable;\n if (isNullable === \"YES\" || isNullable === undefined) {\n return;\n }\n await run(\"ALTER TABLE comments ALTER COLUMN user_id DROP NOT NULL\");\n } catch {\n // already nullable or table missing — ignore\n }\n}\n"],"mappings":";;;;;;;;;;;;AAYA,MAAM,uBAAuB;AAC7B,MAAM,qBACJ;AACF,MAAM,wBACJ;AACF,MAAM,iBACJ;AACF,MAAM,wBACJ;AACF,MAAM,0BAA0B;AAChC,MAAM,4BACJ;AACF,MAAM,4BAA4B;AAClC,MAAM,2BACJ;AACF,MAAM,6BACJ;AACF,MAAM,kCACJ;AACF,MAAM,4BAA4B;AAClC,MAAM,4BACJ;AACF,MAAM,2BAA2B;AACjC,MAAM,4BAA4B;AAClC,MAAM,6BACJ;AACF,MAAM,uBACJ;AACF,MAAM,sBAAsB;AAC5B,MAAM,mBACJ;AACF,MAAM,sBACJ;;;;;;AAMF,MAAM,sBAAsB;AAC5B,MAAM,sBACJ;AAEF,eAAe,uBAAuB,KAAqC;CACzE,MAAM,WAAW,KAAK,yBAAyB;CAC/C,MAAM,WAAW,KAAK,wBAAwB;CAC9C,MAAM,WAAW,KAAK,0BAA0B;CAChD,MAAM,WAAW,KAAK,+BAA+B;AACvD;AAEA,eAAe,qBAAqB,KAAqC;CACvE,MAAM,WAAW,KAAK,kBAAkB;CACxC,MAAM,WAAW,KAAK,qBAAqB;CAC3C,MAAM,WAAW,KAAK,yBAAyB;CAC/C,MAAM,uBAAuB,GAAG;CAChC,MAAM,WAAW,KAAK,yBAAyB;CAC/C,MAAM,WAAW,KAAK,yBAAyB;CAC/C,MAAM,WAAW,KAAK,wBAAwB;CAC9C,MAAM,WAAW,KAAK,yBAAyB;CAC/C,MAAM,WAAW,KAAK,0BAA0B;AAClD;;AAGA,eAAsB,cAAc,KAAqC;CACvE,MAAM,IAAI,GAAG;CACb,MAAM,mBAAmB,GAAG;CAC5B,MAAM,qBAAqB,GAAG;CAC9B,MAAM,qBAAqB,GAAG;AAChC;AAEA,eAAe,mBAAmB,KAAqC;CACrE,MAAM,WAAW,KAAK,oBAAoB;CAC1C,MAAM,WAAW,KAAK,cAAc;CACpC,MAAM,WAAW,KAAK,qBAAqB;CAC3C,MAAM,WAAW,KAAK,uBAAuB;CAC7C,MAAM,WAAW,KAAK,oBAAoB;CAC1C,MAAM,WAAW,KAAK,mBAAmB;CACzC,MAAM,WAAW,KAAK,gBAAgB;CACtC,MAAM,WAAW,KAAK,mBAAmB;CACzC,MAAM,WAAW,KAAK,mBAAmB;CACzC,MAAM,WAAW,KAAK,mBAAmB;AAC3C;AAEA,eAAe,WAAW,KAAsB,KAA4B;CAC1E,IAAI;EACF,MAAM,IAAI,GAAG;CACf,QAAQ,CAER;AACF;AAEA,eAAe,qBAAqB,KAAqC;CACvE,IAAI;EAIF,MAAM,cAAa,MAHC,IAClB,0GACF,EAAA,CACwB,EAAE,EAAE;EAC5B,IAAI,eAAe,SAAS,eAAe,KAAA,GACzC;EAEF,MAAM,IAAI,yDAAyD;CACrE,QAAQ,CAER;AACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { NeonQueryFunction } from "@neondatabase/serverless";
|
|
4
|
+
import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
|
|
5
|
+
//#region src/neon-http.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Options for creating a Neon-HTTP-backed DatabaseAdapter.
|
|
8
|
+
*
|
|
9
|
+
* When `client` (a `neon()` query function) is supplied, `url` is ignored
|
|
10
|
+
* and the caller owns the lifecycle of the client (teardown is a no-op).
|
|
11
|
+
*/
|
|
12
|
+
interface NeonHttpDatabaseOptions {
|
|
13
|
+
url?: string;
|
|
14
|
+
connectionString?: string;
|
|
15
|
+
client?: NeonQueryFunction<false, false>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create a Neon-HTTP-backed DatabaseAdapter using Drizzle ORM.
|
|
19
|
+
*
|
|
20
|
+
* @param options - Either a connection `url` or a preconfigured `neon()`
|
|
21
|
+
* query function.
|
|
22
|
+
* @returns A DatabaseAdapter backed by the Neon database.
|
|
23
|
+
*/
|
|
24
|
+
declare function createNeonHttpDatabase(options: NeonHttpDatabaseOptions): DatabaseAdapter;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { NeonHttpDatabaseOptions, createNeonHttpDatabase };
|
|
27
|
+
//# sourceMappingURL=neon-http.d.mts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { createDrizzlePgAdapter } from "./drizzle-factory-pg.mjs";
|
|
5
|
+
import { runMigrations } from "./migrate.mjs";
|
|
6
|
+
import { schema } from "./schema.mjs";
|
|
7
|
+
import { neon } from "@neondatabase/serverless";
|
|
8
|
+
import { drizzle } from "drizzle-orm/neon-http";
|
|
9
|
+
//#region src/neon-http.ts
|
|
10
|
+
/**
|
|
11
|
+
* Neon HTTP preset for the Postgres database adapter.
|
|
12
|
+
*
|
|
13
|
+
* Same schema, DDL, and Drizzle factory as the `postgres.js` default, but
|
|
14
|
+
* over Neon's fetch-based HTTP driver — no sockets, so it runs in hard
|
|
15
|
+
* serverless and edge runtimes (Cloudflare Workers, Vercel Edge) where
|
|
16
|
+
* WebSockets are unavailable. Requires the optional
|
|
17
|
+
* `@neondatabase/serverless` peer (`nub add @neondatabase/serverless`).
|
|
18
|
+
*
|
|
19
|
+
* Note: HTTP has no interactive transactions; drizzle emulates `transact`
|
|
20
|
+
* sequentially per call. Concurrent writers should prefer `./neon`.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Create a Neon-HTTP-backed DatabaseAdapter using Drizzle ORM.
|
|
24
|
+
*
|
|
25
|
+
* @param options - Either a connection `url` or a preconfigured `neon()`
|
|
26
|
+
* query function.
|
|
27
|
+
* @returns A DatabaseAdapter backed by the Neon database.
|
|
28
|
+
*/
|
|
29
|
+
function createNeonHttpDatabase(options) {
|
|
30
|
+
const { sql } = resolveClient(options);
|
|
31
|
+
const db = drizzle(sql, { schema });
|
|
32
|
+
return createDrizzlePgAdapter(db, {
|
|
33
|
+
metadata: {
|
|
34
|
+
name: "Neon HTTP",
|
|
35
|
+
version: globalThis.__PKG_VERSION__ ?? "0.0.0",
|
|
36
|
+
description: "Neon Postgres database adapter (HTTP driver + Drizzle)",
|
|
37
|
+
kind: "neon-http",
|
|
38
|
+
category: "database"
|
|
39
|
+
},
|
|
40
|
+
tables: schema,
|
|
41
|
+
migrate: async () => {
|
|
42
|
+
await runMigrations(async (statement) => await sql.query(statement));
|
|
43
|
+
},
|
|
44
|
+
close: () => {},
|
|
45
|
+
ping: async () => {
|
|
46
|
+
await sql.query("SELECT 1");
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** Resolve the query function, distinguishing caller-owned from owned. */
|
|
51
|
+
function resolveClient(options) {
|
|
52
|
+
if (options.client) return {
|
|
53
|
+
sql: options.client,
|
|
54
|
+
owned: false
|
|
55
|
+
};
|
|
56
|
+
const connectionString = options.connectionString ?? options.url;
|
|
57
|
+
if (!connectionString) throw new Error("createNeonHttpDatabase: provide `url` (or `connectionString`) or a preconfigured `client`");
|
|
58
|
+
return {
|
|
59
|
+
sql: neon(connectionString),
|
|
60
|
+
owned: true
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { createNeonHttpDatabase };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=neon-http.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neon-http.mjs","names":[],"sources":["../src/neon-http.ts"],"sourcesContent":["/**\n * Neon HTTP preset for the Postgres database adapter.\n *\n * Same schema, DDL, and Drizzle factory as the `postgres.js` default, but\n * over Neon's fetch-based HTTP driver — no sockets, so it runs in hard\n * serverless and edge runtimes (Cloudflare Workers, Vercel Edge) where\n * WebSockets are unavailable. Requires the optional\n * `@neondatabase/serverless` peer (`nub add @neondatabase/serverless`).\n *\n * Note: HTTP has no interactive transactions; drizzle emulates `transact`\n * sequentially per call. Concurrent writers should prefer `./neon`.\n */\nimport { neon, type NeonQueryFunction } from \"@neondatabase/serverless\";\nimport type { DatabaseAdapter } from \"@storyshelf/core/adapter/database\";\nimport { drizzle } from \"drizzle-orm/neon-http\";\nimport { createDrizzlePgAdapter } from \"./drizzle-factory-pg.ts\";\nimport { runMigrations } from \"./migrate.ts\";\nimport { schema } from \"./schema/index.ts\";\n\ndeclare const __PKG_VERSION__: string | undefined;\n\n/**\n * Options for creating a Neon-HTTP-backed DatabaseAdapter.\n *\n * When `client` (a `neon()` query function) is supplied, `url` is ignored\n * and the caller owns the lifecycle of the client (teardown is a no-op).\n */\nexport interface NeonHttpDatabaseOptions {\n url?: string;\n connectionString?: string;\n client?: NeonQueryFunction<false, false>;\n}\n\n/**\n * Create a Neon-HTTP-backed DatabaseAdapter using Drizzle ORM.\n *\n * @param options - Either a connection `url` or a preconfigured `neon()`\n * query function.\n * @returns A DatabaseAdapter backed by the Neon database.\n */\nexport function createNeonHttpDatabase(options: NeonHttpDatabaseOptions): DatabaseAdapter {\n const { sql } = resolveClient(options);\n const db = drizzle(sql, { schema });\n\n return createDrizzlePgAdapter(db, {\n metadata: {\n name: \"Neon HTTP\",\n version: (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\",\n description: \"Neon Postgres database adapter (HTTP driver + Drizzle)\",\n kind: \"neon-http\",\n category: \"database\",\n },\n tables: schema,\n migrate: async () => {\n await runMigrations(async (statement) => await sql.query(statement));\n },\n close: () => {\n // no-op: fetch-based queries hold no connections (owned or not)\n },\n ping: async () => {\n await sql.query(\"SELECT 1\");\n },\n });\n}\n\n/** Resolve the query function, distinguishing caller-owned from owned. */\nfunction resolveClient(options: NeonHttpDatabaseOptions): {\n sql: NeonQueryFunction<false, false>;\n owned: boolean;\n} {\n if (options.client) {\n return { sql: options.client, owned: false };\n }\n const connectionString = options.connectionString ?? options.url;\n if (!connectionString) {\n throw new Error(\n \"createNeonHttpDatabase: provide `url` (or `connectionString`) or a preconfigured `client`\",\n );\n }\n return { sql: neon(connectionString), owned: true };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,uBAAuB,SAAmD;CACxF,MAAM,EAAE,QAAQ,cAAc,OAAO;CACrC,MAAM,KAAK,QAAQ,KAAK,EAAE,OAAO,CAAC;CAElC,OAAO,uBAAuB,IAAI;EAChC,UAAU;GACR,MAAM;GACN,SAAU,WAAuD,mBAAmB;GACpF,aAAa;GACb,MAAM;GACN,UAAU;EACZ;EACA,QAAQ;EACR,SAAS,YAAY;GACnB,MAAM,cAAc,OAAO,cAAc,MAAM,IAAI,MAAM,SAAS,CAAC;EACrE;EACA,aAAa,CAEb;EACA,MAAM,YAAY;GAChB,MAAM,IAAI,MAAM,UAAU;EAC5B;CACF,CAAC;AACH;;AAGA,SAAS,cAAc,SAGrB;CACA,IAAI,QAAQ,QACV,OAAO;EAAE,KAAK,QAAQ;EAAQ,OAAO;CAAM;CAE7C,MAAM,mBAAmB,QAAQ,oBAAoB,QAAQ;CAC7D,IAAI,CAAC,kBACH,MAAM,IAAI,MACR,2FACF;CAEF,OAAO;EAAE,KAAK,KAAK,gBAAgB;EAAG,OAAO;CAAK;AACpD"}
|
package/dist/neon.d.mts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { Pool, PoolClient } from "@neondatabase/serverless";
|
|
4
|
+
import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
|
|
5
|
+
//#region src/neon.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Options for creating a Neon-backed DatabaseAdapter.
|
|
8
|
+
*
|
|
9
|
+
* When `client` (a `Pool` or `PoolClient`) is supplied, all other
|
|
10
|
+
* connection options are ignored and the caller owns the lifecycle of the
|
|
11
|
+
* client (teardown is a no-op). This is the hook for custom WebSocket
|
|
12
|
+
* constructors and hermetic tests.
|
|
13
|
+
*/
|
|
14
|
+
interface NeonDatabaseOptions {
|
|
15
|
+
url?: string;
|
|
16
|
+
connectionString?: string;
|
|
17
|
+
client?: Pool | PoolClient;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a Neon-backed DatabaseAdapter using Drizzle ORM.
|
|
21
|
+
*
|
|
22
|
+
* @param options - Either a connection `url` or a preconfigured Neon
|
|
23
|
+
* `client`.
|
|
24
|
+
* @returns A DatabaseAdapter backed by the Neon database.
|
|
25
|
+
*/
|
|
26
|
+
declare function createNeonDatabase(options: NeonDatabaseOptions): DatabaseAdapter;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { NeonDatabaseOptions, createNeonDatabase };
|
|
29
|
+
//# sourceMappingURL=neon.d.mts.map
|
package/dist/neon.mjs
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { createDrizzlePgAdapter } from "./drizzle-factory-pg.mjs";
|
|
5
|
+
import { runMigrations } from "./migrate.mjs";
|
|
6
|
+
import { schema } from "./schema.mjs";
|
|
7
|
+
import { Pool } from "@neondatabase/serverless";
|
|
8
|
+
import { drizzle } from "drizzle-orm/neon-serverless";
|
|
9
|
+
//#region src/neon.ts
|
|
10
|
+
/**
|
|
11
|
+
* Neon (WebSocket pool) preset for the Postgres database adapter.
|
|
12
|
+
*
|
|
13
|
+
* Same schema, DDL, and Drizzle factory as the `postgres.js` default; only
|
|
14
|
+
* the driver differs (`@neondatabase/serverless` pooled connections work on
|
|
15
|
+
* Node and edge runtimes with a WebSocket constructor). Requires the
|
|
16
|
+
* optional `@neondatabase/serverless` peer (`nub add @neondatabase/serverless`).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Create a Neon-backed DatabaseAdapter using Drizzle ORM.
|
|
20
|
+
*
|
|
21
|
+
* @param options - Either a connection `url` or a preconfigured Neon
|
|
22
|
+
* `client`.
|
|
23
|
+
* @returns A DatabaseAdapter backed by the Neon database.
|
|
24
|
+
*/
|
|
25
|
+
function createNeonDatabase(options) {
|
|
26
|
+
const connectionString = options.connectionString ?? options.url;
|
|
27
|
+
if (!options.client && !connectionString) throw new Error("createNeonDatabase: provide `url` (or `connectionString`) or a preconfigured `client`");
|
|
28
|
+
const owned = !options.client;
|
|
29
|
+
const pool = options.client ?? new Pool({ connectionString });
|
|
30
|
+
const db = drizzle(pool, { schema });
|
|
31
|
+
return createDrizzlePgAdapter(db, {
|
|
32
|
+
metadata: {
|
|
33
|
+
name: "Neon",
|
|
34
|
+
version: globalThis.__PKG_VERSION__ ?? "0.0.0",
|
|
35
|
+
description: "Neon Postgres database adapter (serverless driver + Drizzle)",
|
|
36
|
+
kind: "neon",
|
|
37
|
+
category: "database"
|
|
38
|
+
},
|
|
39
|
+
tables: schema,
|
|
40
|
+
migrate: async () => {
|
|
41
|
+
await runMigrations(async (sql) => (await pool.query(sql)).rows);
|
|
42
|
+
},
|
|
43
|
+
close: async () => {
|
|
44
|
+
if (owned) await endClient(pool);
|
|
45
|
+
},
|
|
46
|
+
ping: async () => {
|
|
47
|
+
await pool.query("SELECT 1");
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/** Close an owned client: pools end, checked-out clients release. */
|
|
52
|
+
async function endClient(pool) {
|
|
53
|
+
if ("end" in pool && typeof pool.end === "function") await pool.end();
|
|
54
|
+
else if ("release" in pool && typeof pool.release === "function") pool.release();
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { createNeonDatabase };
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=neon.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neon.mjs","names":[],"sources":["../src/neon.ts"],"sourcesContent":["/**\n * Neon (WebSocket pool) preset for the Postgres database adapter.\n *\n * Same schema, DDL, and Drizzle factory as the `postgres.js` default; only\n * the driver differs (`@neondatabase/serverless` pooled connections work on\n * Node and edge runtimes with a WebSocket constructor). Requires the\n * optional `@neondatabase/serverless` peer (`nub add @neondatabase/serverless`).\n */\nimport { Pool, type PoolClient } from \"@neondatabase/serverless\";\nimport type { DatabaseAdapter } from \"@storyshelf/core/adapter/database\";\nimport { drizzle } from \"drizzle-orm/neon-serverless\";\nimport { createDrizzlePgAdapter } from \"./drizzle-factory-pg.ts\";\nimport { runMigrations } from \"./migrate.ts\";\nimport { schema } from \"./schema/index.ts\";\n\ndeclare const __PKG_VERSION__: string | undefined;\n\n/**\n * Options for creating a Neon-backed DatabaseAdapter.\n *\n * When `client` (a `Pool` or `PoolClient`) is supplied, all other\n * connection options are ignored and the caller owns the lifecycle of the\n * client (teardown is a no-op). This is the hook for custom WebSocket\n * constructors and hermetic tests.\n */\nexport interface NeonDatabaseOptions {\n url?: string;\n connectionString?: string;\n client?: Pool | PoolClient;\n}\n\n/**\n * Create a Neon-backed DatabaseAdapter using Drizzle ORM.\n *\n * @param options - Either a connection `url` or a preconfigured Neon\n * `client`.\n * @returns A DatabaseAdapter backed by the Neon database.\n */\nexport function createNeonDatabase(options: NeonDatabaseOptions): DatabaseAdapter {\n const connectionString = options.connectionString ?? options.url;\n if (!options.client && !connectionString) {\n throw new Error(\n \"createNeonDatabase: provide `url` (or `connectionString`) or a preconfigured `client`\",\n );\n }\n\n const owned = !options.client;\n const pool = options.client ?? new Pool({ connectionString });\n const db = drizzle(pool, { schema });\n\n return createDrizzlePgAdapter(db, {\n metadata: {\n name: \"Neon\",\n version: (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\",\n description: \"Neon Postgres database adapter (serverless driver + Drizzle)\",\n kind: \"neon\",\n category: \"database\",\n },\n tables: schema,\n migrate: async () => {\n await runMigrations(async (sql) => (await pool.query(sql)).rows as unknown[]);\n },\n close: async () => {\n if (owned) {\n await endClient(pool);\n }\n },\n ping: async () => {\n await pool.query(\"SELECT 1\");\n },\n });\n}\n\n/** Close an owned client: pools end, checked-out clients release. */\nasync function endClient(pool: Pool | PoolClient): Promise<void> {\n if (\"end\" in pool && typeof pool.end === \"function\") {\n await pool.end();\n } else if (\"release\" in pool && typeof pool.release === \"function\") {\n pool.release();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,mBAAmB,SAA+C;CAChF,MAAM,mBAAmB,QAAQ,oBAAoB,QAAQ;CAC7D,IAAI,CAAC,QAAQ,UAAU,CAAC,kBACtB,MAAM,IAAI,MACR,uFACF;CAGF,MAAM,QAAQ,CAAC,QAAQ;CACvB,MAAM,OAAO,QAAQ,UAAU,IAAI,KAAK,EAAE,iBAAiB,CAAC;CAC5D,MAAM,KAAK,QAAQ,MAAM,EAAE,OAAO,CAAC;CAEnC,OAAO,uBAAuB,IAAI;EAChC,UAAU;GACR,MAAM;GACN,SAAU,WAAuD,mBAAmB;GACpF,aAAa;GACb,MAAM;GACN,UAAU;EACZ;EACA,QAAQ;EACR,SAAS,YAAY;GACnB,MAAM,cAAc,OAAO,SAAS,MAAM,KAAK,MAAM,GAAG,EAAA,CAAG,IAAiB;EAC9E;EACA,OAAO,YAAY;GACjB,IAAI,OACF,MAAM,UAAU,IAAI;EAExB;EACA,MAAM,YAAY;GAChB,MAAM,KAAK,MAAM,UAAU;EAC7B;CACF,CAAC;AACH;;AAGA,eAAe,UAAU,MAAwC;CAC/D,IAAI,SAAS,QAAQ,OAAO,KAAK,QAAQ,YACvC,MAAM,KAAK,IAAI;MACV,IAAI,aAAa,QAAQ,OAAO,KAAK,YAAY,YACtD,KAAK,QAAQ;AAEjB"}
|
package/dist/pg.d.mts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { Client, Pool, PoolClient } from "pg";
|
|
4
|
+
import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
|
|
5
|
+
//#region src/pg.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Options for creating a node-postgres-backed DatabaseAdapter.
|
|
8
|
+
*
|
|
9
|
+
* When `client` (a `Pool`, `Client`, or `PoolClient`) is supplied, all other
|
|
10
|
+
* connection options are ignored and the caller owns the lifecycle of the
|
|
11
|
+
* client (teardown is a no-op). This is the hook for IAM token refresh,
|
|
12
|
+
* Cloud SQL connectors, and hermetic tests.
|
|
13
|
+
*/
|
|
14
|
+
interface PgDatabaseOptions {
|
|
15
|
+
url?: string;
|
|
16
|
+
connectionString?: string;
|
|
17
|
+
ssl?: unknown;
|
|
18
|
+
max?: number;
|
|
19
|
+
idleTimeoutMillis?: number;
|
|
20
|
+
connectionTimeoutMillis?: number;
|
|
21
|
+
client?: Pool | Client | PoolClient;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create a node-postgres-backed DatabaseAdapter using Drizzle ORM.
|
|
25
|
+
*
|
|
26
|
+
* @param options - Either a connection `url` (plus pooling/TLS options) or
|
|
27
|
+
* a preconfigured `pg` `client`.
|
|
28
|
+
* @returns A DatabaseAdapter backed by Postgres.
|
|
29
|
+
*/
|
|
30
|
+
declare function createPgDatabase(options: PgDatabaseOptions): DatabaseAdapter;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { PgDatabaseOptions, createPgDatabase };
|
|
33
|
+
//# sourceMappingURL=pg.d.mts.map
|
package/dist/pg.mjs
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { createDrizzlePgAdapter } from "./drizzle-factory-pg.mjs";
|
|
5
|
+
import { runMigrations } from "./migrate.mjs";
|
|
6
|
+
import { schema } from "./schema.mjs";
|
|
7
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
8
|
+
import { Pool } from "pg";
|
|
9
|
+
//#region src/pg.ts
|
|
10
|
+
/**
|
|
11
|
+
* Create a node-postgres-backed DatabaseAdapter using Drizzle ORM.
|
|
12
|
+
*
|
|
13
|
+
* @param options - Either a connection `url` (plus pooling/TLS options) or
|
|
14
|
+
* a preconfigured `pg` `client`.
|
|
15
|
+
* @returns A DatabaseAdapter backed by Postgres.
|
|
16
|
+
*/
|
|
17
|
+
function createPgDatabase(options) {
|
|
18
|
+
const connectionString = options.connectionString ?? options.url;
|
|
19
|
+
if (!options.client && !connectionString) throw new Error("createPgDatabase: provide `url` (or `connectionString`) or a preconfigured `client`");
|
|
20
|
+
const owned = !options.client;
|
|
21
|
+
const pool = options.client ?? new Pool({
|
|
22
|
+
connectionString,
|
|
23
|
+
ssl: options.ssl,
|
|
24
|
+
max: options.max,
|
|
25
|
+
idleTimeoutMillis: options.idleTimeoutMillis,
|
|
26
|
+
connectionTimeoutMillis: options.connectionTimeoutMillis
|
|
27
|
+
});
|
|
28
|
+
const db = drizzle(pool, { schema });
|
|
29
|
+
return createDrizzlePgAdapter(db, {
|
|
30
|
+
metadata: {
|
|
31
|
+
name: "Postgres (pg)",
|
|
32
|
+
version: globalThis.__PKG_VERSION__ ?? "0.0.0",
|
|
33
|
+
description: "Postgres database adapter (node-postgres + Drizzle)",
|
|
34
|
+
kind: "pg",
|
|
35
|
+
category: "database"
|
|
36
|
+
},
|
|
37
|
+
tables: schema,
|
|
38
|
+
migrate: async () => {
|
|
39
|
+
await runMigrations(async (sql) => (await pool.query(sql)).rows);
|
|
40
|
+
},
|
|
41
|
+
close: async () => {
|
|
42
|
+
if (owned) await endClient(pool);
|
|
43
|
+
},
|
|
44
|
+
ping: async () => {
|
|
45
|
+
await pool.query("SELECT 1");
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/** Close an owned client: pools end, bare clients disconnect. */
|
|
50
|
+
async function endClient(pool) {
|
|
51
|
+
if ("end" in pool && typeof pool.end === "function") await pool.end();
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { createPgDatabase };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=pg.mjs.map
|
package/dist/pg.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pg.mjs","names":[],"sources":["../src/pg.ts"],"sourcesContent":["/**\n * node-postgres preset for the Postgres database adapter.\n *\n * Same schema, DDL, and Drizzle factory as the `postgres.js` default; only\n * the driver differs. `node-postgres` uses unnamed portals, so it works\n * behind transaction-mode poolers (PgBouncer, Supavisor, Neon pooled)\n * without `prepare: false`, and it plugs into IAM flows (RDS token\n * password, Cloud SQL connector) that hand out `pg`-shaped clients.\n * Requires the optional `pg` peer (`nub add pg`).\n */\nimport type { DatabaseAdapter } from \"@storyshelf/core/adapter/database\";\nimport { drizzle } from \"drizzle-orm/node-postgres\";\nimport { Pool, type Client, type PoolClient } from \"pg\";\nimport { createDrizzlePgAdapter } from \"./drizzle-factory-pg.ts\";\nimport { runMigrations } from \"./migrate.ts\";\nimport { schema } from \"./schema/index.ts\";\n\ndeclare const __PKG_VERSION__: string | undefined;\n\n/**\n * Options for creating a node-postgres-backed DatabaseAdapter.\n *\n * When `client` (a `Pool`, `Client`, or `PoolClient`) is supplied, all other\n * connection options are ignored and the caller owns the lifecycle of the\n * client (teardown is a no-op). This is the hook for IAM token refresh,\n * Cloud SQL connectors, and hermetic tests.\n */\nexport interface PgDatabaseOptions {\n url?: string;\n connectionString?: string;\n ssl?: unknown;\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n client?: Pool | Client | PoolClient;\n}\n\n/**\n * Create a node-postgres-backed DatabaseAdapter using Drizzle ORM.\n *\n * @param options - Either a connection `url` (plus pooling/TLS options) or\n * a preconfigured `pg` `client`.\n * @returns A DatabaseAdapter backed by Postgres.\n */\nexport function createPgDatabase(options: PgDatabaseOptions): DatabaseAdapter {\n const connectionString = options.connectionString ?? options.url;\n if (!options.client && !connectionString) {\n throw new Error(\n \"createPgDatabase: provide `url` (or `connectionString`) or a preconfigured `client`\",\n );\n }\n\n const owned = !options.client;\n const pool =\n options.client ??\n new Pool({\n connectionString,\n ssl: options.ssl,\n max: options.max,\n idleTimeoutMillis: options.idleTimeoutMillis,\n connectionTimeoutMillis: options.connectionTimeoutMillis,\n });\n const db = drizzle(pool, { schema });\n\n return createDrizzlePgAdapter(db, {\n metadata: {\n name: \"Postgres (pg)\",\n version: (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\",\n description: \"Postgres database adapter (node-postgres + Drizzle)\",\n kind: \"pg\",\n category: \"database\",\n },\n tables: schema,\n migrate: async () => {\n await runMigrations(async (sql) => (await pool.query(sql)).rows);\n },\n close: async () => {\n if (owned) {\n await endClient(pool);\n }\n },\n ping: async () => {\n await pool.query(\"SELECT 1\");\n },\n });\n}\n\n/** Close an owned client: pools end, bare clients disconnect. */\nasync function endClient(pool: Pool | Client | PoolClient): Promise<void> {\n if (\"end\" in pool && typeof pool.end === \"function\") {\n await (pool as Pool).end();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA4CA,SAAgB,iBAAiB,SAA6C;CAC5E,MAAM,mBAAmB,QAAQ,oBAAoB,QAAQ;CAC7D,IAAI,CAAC,QAAQ,UAAU,CAAC,kBACtB,MAAM,IAAI,MACR,qFACF;CAGF,MAAM,QAAQ,CAAC,QAAQ;CACvB,MAAM,OACJ,QAAQ,UACR,IAAI,KAAK;EACP;EACA,KAAK,QAAQ;EACb,KAAK,QAAQ;EACb,mBAAmB,QAAQ;EAC3B,yBAAyB,QAAQ;CACnC,CAAC;CACH,MAAM,KAAK,QAAQ,MAAM,EAAE,OAAO,CAAC;CAEnC,OAAO,uBAAuB,IAAI;EAChC,UAAU;GACR,MAAM;GACN,SAAU,WAAuD,mBAAmB;GACpF,aAAa;GACb,MAAM;GACN,UAAU;EACZ;EACA,QAAQ;EACR,SAAS,YAAY;GACnB,MAAM,cAAc,OAAO,SAAS,MAAM,KAAK,MAAM,GAAG,EAAA,CAAG,IAAI;EACjE;EACA,OAAO,YAAY;GACjB,IAAI,OACF,MAAM,UAAU,IAAI;EAExB;EACA,MAAM,YAAY;GAChB,MAAM,KAAK,MAAM,UAAU;EAC7B;CACF,CAAC;AACH;;AAGA,eAAe,UAAU,MAAiD;CACxE,IAAI,SAAS,QAAQ,OAAO,KAAK,QAAQ,YACvC,MAAO,KAAc,IAAI;AAE7B"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
4
|
+
import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
|
|
5
|
+
//#region src/pglite.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Options for creating a PGlite-backed DatabaseAdapter.
|
|
8
|
+
*
|
|
9
|
+
* When `client` (a `PGlite` instance) is supplied, `dataDir` is ignored and
|
|
10
|
+
* the caller owns the lifecycle of the client (teardown is a no-op). This is
|
|
11
|
+
* the hook for shared instances and for waiting on `client.waitReady` in
|
|
12
|
+
* tests. With no options, an ephemeral in-memory database is created.
|
|
13
|
+
*/
|
|
14
|
+
interface PgliteDatabaseOptions {
|
|
15
|
+
dataDir?: string;
|
|
16
|
+
client?: PGlite;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Create a PGlite-backed DatabaseAdapter using Drizzle ORM.
|
|
20
|
+
*
|
|
21
|
+
* @param options - Either a `dataDir` (use `"memory://"` for an ephemeral
|
|
22
|
+
* in-memory database, the default) or a preconfigured `PGlite` `client`.
|
|
23
|
+
* @returns A DatabaseAdapter backed by the embedded Postgres instance.
|
|
24
|
+
*/
|
|
25
|
+
declare function createPgliteDatabase(options?: PgliteDatabaseOptions): DatabaseAdapter;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { PgliteDatabaseOptions, createPgliteDatabase };
|
|
28
|
+
//# sourceMappingURL=pglite.d.mts.map
|
package/dist/pglite.mjs
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { createDrizzlePgAdapter } from "./drizzle-factory-pg.mjs";
|
|
5
|
+
import { runMigrations } from "./migrate.mjs";
|
|
6
|
+
import { schema } from "./schema.mjs";
|
|
7
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
8
|
+
import { drizzle } from "drizzle-orm/pglite";
|
|
9
|
+
//#region src/pglite.ts
|
|
10
|
+
/**
|
|
11
|
+
* PGlite (embedded Postgres/WASM) preset for the Postgres database adapter.
|
|
12
|
+
*
|
|
13
|
+
* Same schema, DDL, and Drizzle factory as the `postgres.js` default; only
|
|
14
|
+
* the driver differs (in-memory or `memory://` WASM Postgres, no server or
|
|
15
|
+
* sockets). Useful for hermetic tests and zero-infra local dev. Requires
|
|
16
|
+
* the optional `@electric-sql/pglite` peer (`nub add @electric-sql/pglite`).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Create a PGlite-backed DatabaseAdapter using Drizzle ORM.
|
|
20
|
+
*
|
|
21
|
+
* @param options - Either a `dataDir` (use `"memory://"` for an ephemeral
|
|
22
|
+
* in-memory database, the default) or a preconfigured `PGlite` `client`.
|
|
23
|
+
* @returns A DatabaseAdapter backed by the embedded Postgres instance.
|
|
24
|
+
*/
|
|
25
|
+
function createPgliteDatabase(options = {}) {
|
|
26
|
+
const owned = !options.client;
|
|
27
|
+
const client = options.client ?? new PGlite(options.dataDir ?? "memory://");
|
|
28
|
+
const db = drizzle(client, { schema });
|
|
29
|
+
return createDrizzlePgAdapter(db, {
|
|
30
|
+
metadata: {
|
|
31
|
+
name: "PGlite",
|
|
32
|
+
version: globalThis.__PKG_VERSION__ ?? "0.0.0",
|
|
33
|
+
description: "PGlite database adapter (embedded Postgres/WASM + Drizzle)",
|
|
34
|
+
kind: "pglite",
|
|
35
|
+
category: "database"
|
|
36
|
+
},
|
|
37
|
+
tables: schema,
|
|
38
|
+
migrate: async () => {
|
|
39
|
+
await runMigrations(async (sql) => {
|
|
40
|
+
return (await client.query(sql)).rows;
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
close: async () => {
|
|
44
|
+
if (owned) await client.close();
|
|
45
|
+
},
|
|
46
|
+
ping: async () => {
|
|
47
|
+
await client.query("SELECT 1");
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { createPgliteDatabase };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=pglite.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pglite.mjs","names":[],"sources":["../src/pglite.ts"],"sourcesContent":["/**\n * PGlite (embedded Postgres/WASM) preset for the Postgres database adapter.\n *\n * Same schema, DDL, and Drizzle factory as the `postgres.js` default; only\n * the driver differs (in-memory or `memory://` WASM Postgres, no server or\n * sockets). Useful for hermetic tests and zero-infra local dev. Requires\n * the optional `@electric-sql/pglite` peer (`nub add @electric-sql/pglite`).\n */\nimport { PGlite } from \"@electric-sql/pglite\";\nimport type { DatabaseAdapter } from \"@storyshelf/core/adapter/database\";\nimport { drizzle } from \"drizzle-orm/pglite\";\nimport { createDrizzlePgAdapter } from \"./drizzle-factory-pg.ts\";\nimport { runMigrations } from \"./migrate.ts\";\nimport { schema } from \"./schema/index.ts\";\n\ndeclare const __PKG_VERSION__: string | undefined;\n\n/**\n * Options for creating a PGlite-backed DatabaseAdapter.\n *\n * When `client` (a `PGlite` instance) is supplied, `dataDir` is ignored and\n * the caller owns the lifecycle of the client (teardown is a no-op). This is\n * the hook for shared instances and for waiting on `client.waitReady` in\n * tests. With no options, an ephemeral in-memory database is created.\n */\nexport interface PgliteDatabaseOptions {\n dataDir?: string;\n client?: PGlite;\n}\n\n/**\n * Create a PGlite-backed DatabaseAdapter using Drizzle ORM.\n *\n * @param options - Either a `dataDir` (use `\"memory://\"` for an ephemeral\n * in-memory database, the default) or a preconfigured `PGlite` `client`.\n * @returns A DatabaseAdapter backed by the embedded Postgres instance.\n */\nexport function createPgliteDatabase(options: PgliteDatabaseOptions = {}): DatabaseAdapter {\n const owned = !options.client;\n const client = options.client ?? new PGlite(options.dataDir ?? \"memory://\");\n const db = drizzle(client, { schema });\n\n return createDrizzlePgAdapter(db, {\n metadata: {\n name: \"PGlite\",\n version: (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\",\n description: \"PGlite database adapter (embedded Postgres/WASM + Drizzle)\",\n kind: \"pglite\",\n category: \"database\",\n },\n tables: schema,\n migrate: async () => {\n await runMigrations(async (sql) => {\n const result = await client.query(sql);\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- PGlite Row[] vs unknown[]\n return result.rows as unknown as unknown[];\n });\n },\n close: async () => {\n if (owned) {\n await client.close();\n }\n },\n ping: async () => {\n await client.query(\"SELECT 1\");\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAgB,qBAAqB,UAAiC,CAAC,GAAoB;CACzF,MAAM,QAAQ,CAAC,QAAQ;CACvB,MAAM,SAAS,QAAQ,UAAU,IAAI,OAAO,QAAQ,WAAW,WAAW;CAC1E,MAAM,KAAK,QAAQ,QAAQ,EAAE,OAAO,CAAC;CAErC,OAAO,uBAAuB,IAAI;EAChC,UAAU;GACR,MAAM;GACN,SAAU,WAAuD,mBAAmB;GACpF,aAAa;GACb,MAAM;GACN,UAAU;EACZ;EACA,QAAQ;EACR,SAAS,YAAY;GACnB,MAAM,cAAc,OAAO,QAAQ;IAGjC,QAAO,MAFc,OAAO,MAAM,GAAG,EAAA,CAEvB;GAChB,CAAC;EACH;EACA,OAAO,YAAY;GACjB,IAAI,OACF,MAAM,OAAO,MAAM;EAEvB;EACA,MAAM,YAAY;GAChB,MAAM,OAAO,MAAM,UAAU;EAC/B;CACF,CAAC;AACH"}
|