mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
postgres: added pg and postgres, and examples for xata and neon
This commit is contained in:
@@ -23,6 +23,12 @@ function hasColors() {
|
||||
}
|
||||
|
||||
const __consoles = {
|
||||
critical: {
|
||||
prefix: "CRT",
|
||||
color: colors.red,
|
||||
args_color: colors.red,
|
||||
original: console.error,
|
||||
},
|
||||
error: {
|
||||
prefix: "ERR",
|
||||
color: colors.red,
|
||||
|
||||
@@ -36,7 +36,7 @@ export function disableConsoleLog(severities: ConsoleSeverity[] = ["log", "warn"
|
||||
severities.forEach((severity) => {
|
||||
console[severity] = () => null;
|
||||
});
|
||||
$console.setLevel("error");
|
||||
$console.setLevel("critical");
|
||||
}
|
||||
|
||||
export function enableConsoleLog() {
|
||||
|
||||
@@ -2,6 +2,8 @@ import {
|
||||
type AliasableExpression,
|
||||
type ColumnBuilderCallback,
|
||||
type ColumnDataType,
|
||||
type DatabaseIntrospector,
|
||||
type Dialect,
|
||||
type Expression,
|
||||
type Kysely,
|
||||
type KyselyPlugin,
|
||||
@@ -12,7 +14,8 @@ import {
|
||||
type Simplify,
|
||||
sql,
|
||||
} from "kysely";
|
||||
import type { BaseIntrospector } from "./BaseIntrospector";
|
||||
import type { BaseIntrospector, BaseIntrospectorConfig } from "./BaseIntrospector";
|
||||
import type { Constructor } from "core";
|
||||
|
||||
export type QB = SelectQueryBuilder<any, any, any>;
|
||||
|
||||
@@ -159,3 +162,19 @@ export abstract class Connection<DB = any> {
|
||||
// no-op by default
|
||||
}
|
||||
}
|
||||
|
||||
export function customIntrospector<T extends Constructor<Dialect>>(
|
||||
dialect: T,
|
||||
introspector: Constructor<DatabaseIntrospector>,
|
||||
options: BaseIntrospectorConfig = {},
|
||||
) {
|
||||
return {
|
||||
create(...args: ConstructorParameters<T>) {
|
||||
return new (class extends dialect {
|
||||
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
||||
return new introspector(db, options);
|
||||
}
|
||||
})(...args);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ export {
|
||||
type IndexSpec,
|
||||
type DbFunctions,
|
||||
type SchemaResponse,
|
||||
customIntrospector,
|
||||
} from "./Connection";
|
||||
|
||||
// sqlite
|
||||
|
||||
@@ -183,7 +183,7 @@ export class ModuleManager {
|
||||
const context = this.ctx(true);
|
||||
|
||||
for (const key in MODULES) {
|
||||
const moduleConfig = key in initial ? initial[key] : {};
|
||||
const moduleConfig = initial && key in initial ? initial[key] : {};
|
||||
const module = new MODULES[key](moduleConfig, context) as Module;
|
||||
module.setListener(async (c) => {
|
||||
await this.onModuleConfigUpdated(key, c);
|
||||
|
||||
65
bun.lock
65
bun.lock
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"app": {
|
||||
"name": "bknd",
|
||||
"version": "0.12.0",
|
||||
"version": "0.13.0",
|
||||
"bin": "./dist/cli/index.js",
|
||||
"dependencies": {
|
||||
"@cfworker/json-schema": "^4.1.1",
|
||||
@@ -149,19 +149,24 @@
|
||||
},
|
||||
"packages/postgres": {
|
||||
"name": "@bknd/postgres",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"kysely": "^0.27.6",
|
||||
"pg": "^8.14.0",
|
||||
},
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.2.5",
|
||||
"@types/node": "^22.13.10",
|
||||
"@types/pg": "^8.11.11",
|
||||
"@xata.io/client": "^0.0.0-next.v93343b9646f57a1e5c51c35eccf0767c2bb80baa",
|
||||
"@xata.io/kysely": "^0.2.1",
|
||||
"bknd": "workspace:*",
|
||||
"kysely-neon": "^1.3.0",
|
||||
"tsup": "^8.4.0",
|
||||
"typescript": "^5.8.2",
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"kysely": "^0.27.6",
|
||||
"kysely-postgres-js": "^2.0.0",
|
||||
"pg": "^8.14.0",
|
||||
"postgres": "^3.4.7",
|
||||
},
|
||||
},
|
||||
"packages/sqlocal": {
|
||||
"name": "@bknd/sqlocal",
|
||||
@@ -791,6 +796,8 @@
|
||||
|
||||
"@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="],
|
||||
|
||||
"@neondatabase/serverless": ["@neondatabase/serverless@0.4.26", "", { "dependencies": { "@types/pg": "8.6.6" } }, "sha512-6DYEKos2GYn8NTgcJf33BLAx//LcgqzHVavQWe6ZkaDqmEq0I0Xtub6pzwFdq9iayNdCj7e2b0QKr5a8QKB8kQ=="],
|
||||
|
||||
"@next/env": ["@next/env@15.2.1", "", {}, "sha512-JmY0qvnPuS2NCWOz2bbby3Pe0VzdAQ7XpEB6uLIHmtXNfAsAO0KLQLkuAoc42Bxbo3/jMC3dcn9cdf+piCcG2Q=="],
|
||||
|
||||
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aWXT+5KEREoy3K5AKtiKwioeblmOvFFjd+F3dVleLvvLiQ/mD//jOOuUcx5hzcO9ISSw4lrqtUPntTpK32uXXQ=="],
|
||||
@@ -1395,6 +1402,10 @@
|
||||
|
||||
"@web3-storage/multipart-parser": ["@web3-storage/multipart-parser@1.0.0", "", {}, "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw=="],
|
||||
|
||||
"@xata.io/client": ["@xata.io/client@0.0.0-next.v93343b9646f57a1e5c51c35eccf0767c2bb80baa", "", { "peerDependencies": { "typescript": ">=4.5" } }, "sha512-4Js4SAKwmmOPmZVIS1l2K8XVGGkUOi8L1jXuagDfeUX56n95wfA4xYMSmsVS0RLMmRWI4UM4bp5UcFJxwbFYGw=="],
|
||||
|
||||
"@xata.io/kysely": ["@xata.io/kysely@0.2.1", "", { "dependencies": { "@xata.io/client": "0.30.1" }, "peerDependencies": { "kysely": "*" } }, "sha512-0+WBcFkBSNEu11wVTyJyeNMOPUuolDKJMjXQr1nheHTNZLfsL0qKshTZOKIC/bGInjepGA7DQ/HFeKDHe5CDpA=="],
|
||||
|
||||
"@xyflow/react": ["@xyflow/react@12.4.4", "", { "dependencies": { "@xyflow/system": "0.0.52", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-9RZ9dgKZNJOlbrXXST5HPb5TcXPOIDGondjwcjDro44OQRPl1E0ZRPTeWPGaQtVjbg4WpR4BUYwOeshNI2TuVg=="],
|
||||
|
||||
"@xyflow/system": ["@xyflow/system@0.0.52", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-pJBMaoh/GEebIABWEIxAai0yf57dm+kH7J/Br+LnLFPuJL87Fhcmm4KFWd/bCUy/kCWUg+2/yFAGY0AUHRPOnQ=="],
|
||||
@@ -2543,6 +2554,10 @@
|
||||
|
||||
"kysely-d1": ["kysely-d1@0.3.0", "", { "peerDependencies": { "kysely": "*" } }, "sha512-9wTbE6ooLiYtBa4wPg9e4fjfcmvRtgE/2j9pAjYrIq+iz+EsH/Hj9YbtxpEXA6JoRgfulVQ1EtGj6aycGGRpYw=="],
|
||||
|
||||
"kysely-neon": ["kysely-neon@1.3.0", "", { "peerDependencies": { "@neondatabase/serverless": "^0.4.3", "kysely": "0.x.x", "ws": "^8.13.0" }, "optionalPeers": ["ws"] }, "sha512-CIIlbmqpIXVJDdBEYtEOwbmALag0jmqYrGfBeM4cHKb9AgBGs+X1SvXUZ8TqkDacQEqEZN2XtsDoUkcMIISjHw=="],
|
||||
|
||||
"kysely-postgres-js": ["kysely-postgres-js@2.0.0", "", { "peerDependencies": { "kysely": ">= 0.24.0 < 1", "postgres": ">= 3.4.0 < 4" } }, "sha512-R1tWx6/x3tSatWvsmbHJxpBZYhNNxcnMw52QzZaHKg7ZOWtHib4iZyEaw4gb2hNKVctWQ3jfMxZT/ZaEMK6kBQ=="],
|
||||
|
||||
"language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="],
|
||||
|
||||
"language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
|
||||
@@ -2905,7 +2920,7 @@
|
||||
|
||||
"pg-protocol": ["pg-protocol@1.8.0", "", {}, "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g=="],
|
||||
|
||||
"pg-types": ["pg-types@4.0.2", "", { "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", "postgres-array": "~3.0.1", "postgres-bytea": "~3.0.0", "postgres-date": "~2.1.0", "postgres-interval": "^3.0.0", "postgres-range": "^1.1.1" } }, "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng=="],
|
||||
"pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||
|
||||
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
|
||||
|
||||
@@ -2961,13 +2976,15 @@
|
||||
|
||||
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
||||
|
||||
"postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="],
|
||||
"postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="],
|
||||
|
||||
"postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="],
|
||||
"postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
||||
|
||||
"postgres-date": ["postgres-date@2.1.0", "", {}, "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="],
|
||||
"postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="],
|
||||
|
||||
"postgres-interval": ["postgres-interval@3.0.0", "", {}, "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="],
|
||||
"postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
||||
|
||||
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||
|
||||
"postgres-range": ["postgres-range@1.1.4", "", {}, "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w=="],
|
||||
|
||||
@@ -3847,8 +3864,6 @@
|
||||
|
||||
"@babel/runtime/regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
|
||||
|
||||
"@bknd/postgres/@types/bun": ["@types/bun@1.2.5", "", { "dependencies": { "bun-types": "1.2.5" } }, "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg=="],
|
||||
|
||||
"@bundled-es-modules/cookie/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||
|
||||
"@bundled-es-modules/tough-cookie/tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="],
|
||||
@@ -3909,6 +3924,8 @@
|
||||
|
||||
"@libsql/kysely-libsql/@libsql/client": ["@libsql/client@0.8.1", "", { "dependencies": { "@libsql/core": "^0.8.1", "@libsql/hrana-client": "^0.6.2", "js-base64": "^3.7.5", "libsql": "^0.3.10", "promise-limit": "^2.7.0" } }, "sha512-xGg0F4iTDFpeBZ0r4pA6icGsYa5rG6RAG+i/iLDnpCAnSuTqEWMDdPlVseiq4Z/91lWI9jvvKKiKpovqJ1kZWA=="],
|
||||
|
||||
"@neondatabase/serverless/@types/pg": ["@types/pg@8.6.6", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw=="],
|
||||
|
||||
"@plasmicapp/query/swr": ["swr@1.3.0", "", { "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw=="],
|
||||
|
||||
"@remix-run/node/cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
||||
@@ -4065,6 +4082,8 @@
|
||||
|
||||
"@types/jest/pretty-format": ["pretty-format@25.5.0", "", { "dependencies": { "@jest/types": "^25.5.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" } }, "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ=="],
|
||||
|
||||
"@types/pg/pg-types": ["pg-types@4.0.2", "", { "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", "postgres-array": "~3.0.1", "postgres-bytea": "~3.0.0", "postgres-date": "~2.1.0", "postgres-interval": "^3.0.0", "postgres-range": "^1.1.1" } }, "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng=="],
|
||||
|
||||
"@typescript-eslint/experimental-utils/eslint-utils": ["eslint-utils@2.1.0", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||
@@ -4125,6 +4144,8 @@
|
||||
|
||||
"@wdio/utils/decamelize": ["decamelize@6.0.0", "", {}, "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA=="],
|
||||
|
||||
"@xata.io/kysely/@xata.io/client": ["@xata.io/client@0.30.1", "", { "peerDependencies": { "typescript": ">=4.5" } }, "sha512-dAzDPHmIfenVIpF39m1elmW5ngjWu2mO8ZqJBN7dmYdXr98uhPANfLdVZnc3mUNG+NH37LqY1dSO862hIo2oRw=="],
|
||||
|
||||
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||
|
||||
"acorn-globals/acorn": ["acorn@6.4.2", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="],
|
||||
@@ -4467,8 +4488,6 @@
|
||||
|
||||
"peek-stream/duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="],
|
||||
|
||||
"pg/pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||
|
||||
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
|
||||
|
||||
"pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
||||
@@ -4741,6 +4760,14 @@
|
||||
|
||||
"@types/jest/pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"@types/pg/pg-types/postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="],
|
||||
|
||||
"@types/pg/pg-types/postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="],
|
||||
|
||||
"@types/pg/pg-types/postgres-date": ["postgres-date@2.1.0", "", {}, "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="],
|
||||
|
||||
"@types/pg/pg-types/postgres-interval": ["postgres-interval@3.0.0", "", {}, "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
|
||||
"@verdaccio/local-storage-legacy/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
|
||||
@@ -4929,14 +4956,6 @@
|
||||
|
||||
"peek-stream/duplexify/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"pg/pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
||||
|
||||
"pg/pg-types/postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="],
|
||||
|
||||
"pg/pg-types/postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
||||
|
||||
"pg/pg-types/postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||
|
||||
"progress-estimator/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
|
||||
|
||||
"progress-estimator/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# Postgres adapter for `bknd` (experimental)
|
||||
This packages adds an adapter to use a Postgres database with [`bknd`](https://github.com/bknd-io/bknd). It is based on [`pg`](https://github.com/brianc/node-postgres) and the driver included in [`kysely`](https://github.com/kysely-org/kysely).
|
||||
This packages adds an adapter to use a Postgres database with [`bknd`](https://github.com/bknd-io/bknd). It works with both `pg` and `postgres` drivers, and supports custom postgres connections.
|
||||
* works with any Postgres database (tested with Supabase, Neon, Xata, and RDS)
|
||||
* choose between `pg` and `postgres` drivers
|
||||
* create custom postgres connections with any kysely postgres dialect
|
||||
|
||||
## Installation
|
||||
Install the adapter with:
|
||||
@@ -7,44 +10,93 @@ Install the adapter with:
|
||||
npm install @bknd/postgres
|
||||
```
|
||||
|
||||
## Usage
|
||||
## Using `pg` driver
|
||||
Install the [`pg`](https://github.com/brianc/node-postgres) driver with:
|
||||
```bash
|
||||
npm install pg
|
||||
```
|
||||
|
||||
Create a connection:
|
||||
|
||||
```ts
|
||||
import { PostgresConnection } from "@bknd/postgres";
|
||||
import { pg } from "@bknd/postgres";
|
||||
|
||||
const connection = new PostgresConnection({
|
||||
// accepts `pg` configuration
|
||||
const connection = pg({
|
||||
host: "localhost",
|
||||
port: 5432,
|
||||
user: "postgres",
|
||||
password: "postgres",
|
||||
database: "bknd",
|
||||
database: "postgres",
|
||||
});
|
||||
|
||||
// or with a connection string
|
||||
const connection = pg({
|
||||
connectionString: "postgres://postgres:postgres@localhost:5432/postgres",
|
||||
});
|
||||
```
|
||||
|
||||
Use the connection depending on which framework or runtime you are using. E.g., when using `createApp`, you can use the connection as follows:
|
||||
## Using `postgres` driver
|
||||
|
||||
```ts
|
||||
import { createApp } from "bknd";
|
||||
import { PostgresConnection } from "@bknd/postgres";
|
||||
|
||||
const connection = new PostgresConnection();
|
||||
const app = createApp({ connection });
|
||||
Install the [`postgres`](https://github.com/porsager/postgres) driver with:
|
||||
```bash
|
||||
npm install postgres
|
||||
```
|
||||
|
||||
Or if you're using it with a framework, say Next.js, you can add the connection object to where you're initializating the app:
|
||||
Create a connection:
|
||||
|
||||
```ts
|
||||
// e.g. in src/app/api/[[...bknd]]/route.ts
|
||||
import { serve } from "bknd/adapter/nextjs";
|
||||
import { PostgresConnection } from "@bknd/postgres";
|
||||
import { postgresJs } from "@bknd/postgres";
|
||||
|
||||
const connection = new PostgresConnection();
|
||||
const handler = serve({
|
||||
connection
|
||||
})
|
||||
|
||||
// ...
|
||||
// accepts `postgres` configuration
|
||||
const connection = postgresJs("postgres://postgres:postgres@localhost:5432/postgres");
|
||||
```
|
||||
|
||||
For more information about how to integrate Next.js in general, check out the [Next.js documentation](https://docs.bknd.io/integration/nextjs).
|
||||
## Using custom postgres dialects
|
||||
|
||||
You can create a custom kysely postgres dialect by using the `createCustomPostgresConnection` function.
|
||||
|
||||
```ts
|
||||
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||
|
||||
const connection = createCustomPostgresConnection(MyDialect)({
|
||||
// your custom dialect configuration
|
||||
supports: {
|
||||
batching: true
|
||||
},
|
||||
excludeTables: ["my_table"],
|
||||
plugins: [new MyKyselyPlugin()],
|
||||
});
|
||||
```
|
||||
|
||||
### Custom `neon` connection
|
||||
|
||||
```typescript
|
||||
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||
import { NeonDialect } from "kysely-neon";
|
||||
|
||||
const connection = createCustomPostgresConnection(NeonDialect)({
|
||||
connectionString: process.env.NEON,
|
||||
});
|
||||
```
|
||||
|
||||
### Custom `xata` connection
|
||||
|
||||
```typescript
|
||||
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||
import { XataDialect } from "@xata.io/kysely";
|
||||
import { buildClient } from "@xata.io/client";
|
||||
|
||||
const client = buildClient();
|
||||
const xata = new client({
|
||||
databaseURL: process.env.XATA_URL,
|
||||
apiKey: process.env.XATA_API_KEY,
|
||||
branch: process.env.XATA_BRANCH,
|
||||
});
|
||||
|
||||
const connection = createCustomPostgresConnection(XataDialect, {
|
||||
supports: {
|
||||
batching: false,
|
||||
},
|
||||
})({ xata });
|
||||
```
|
||||
14
packages/postgres/examples/neon.ts
Normal file
14
packages/postgres/examples/neon.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { serve } from "bknd/adapter/bun";
|
||||
import { createCustomPostgresConnection } from "../src";
|
||||
import { NeonDialect } from "kysely-neon";
|
||||
|
||||
const neon = createCustomPostgresConnection(NeonDialect);
|
||||
|
||||
export default serve({
|
||||
connection: neon({
|
||||
connectionString: process.env.NEON,
|
||||
}),
|
||||
// ignore this, it's only required within this repository
|
||||
// because bknd is installed via "workspace:*"
|
||||
distPath: "../../app/dist",
|
||||
});
|
||||
24
packages/postgres/examples/xata.ts
Normal file
24
packages/postgres/examples/xata.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { serve } from "bknd/adapter/bun";
|
||||
import { createCustomPostgresConnection } from "../src";
|
||||
import { XataDialect } from "@xata.io/kysely";
|
||||
import { buildClient } from "@xata.io/client";
|
||||
|
||||
const client = buildClient();
|
||||
const xata = new client({
|
||||
databaseURL: process.env.XATA_URL,
|
||||
apiKey: process.env.XATA_API_KEY,
|
||||
branch: process.env.XATA_BRANCH,
|
||||
});
|
||||
|
||||
const connection = createCustomPostgresConnection(XataDialect, {
|
||||
supports: {
|
||||
batching: false,
|
||||
},
|
||||
})({ xata });
|
||||
|
||||
export default serve({
|
||||
connection,
|
||||
// ignore this, it's only required within this repository
|
||||
// because bknd is installed via "workspace:*"
|
||||
distPath: "../../../app/dist",
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bknd/postgres",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
@@ -17,15 +17,20 @@
|
||||
"docker:start": "docker run --rm --name bknd-test-postgres -d -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres -e POSTGRES_DB=bknd -p 5430:5432 postgres:17",
|
||||
"docker:stop": "docker stop bknd-test-postgres"
|
||||
},
|
||||
"dependencies": {
|
||||
"optionalDependencies": {
|
||||
"kysely": "^0.27.6",
|
||||
"kysely-postgres-js": "^2.0.0",
|
||||
"pg": "^8.14.0",
|
||||
"kysely": "^0.27.6"
|
||||
"postgres": "^3.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.2.5",
|
||||
"@types/node": "^22.13.10",
|
||||
"@types/pg": "^8.11.11",
|
||||
"@xata.io/client": "^0.0.0-next.v93343b9646f57a1e5c51c35eccf0767c2bb80baa",
|
||||
"@xata.io/kysely": "^0.2.1",
|
||||
"bknd": "workspace:*",
|
||||
"kysely-neon": "^1.3.0",
|
||||
"tsup": "^8.4.0",
|
||||
"typescript": "^5.8.2"
|
||||
},
|
||||
@@ -36,7 +41,7 @@
|
||||
"clean": true,
|
||||
"minify": true,
|
||||
"dts": true,
|
||||
"external": ["bknd", "pg", "kysely"]
|
||||
"external": ["bknd", "pg", "kysely", "kysely-postgres-js"]
|
||||
},
|
||||
"files": ["dist", "README.md", "!*.map", "!metafile*.json"]
|
||||
}
|
||||
|
||||
32
packages/postgres/src/PgPostgresConnection.ts
Normal file
32
packages/postgres/src/PgPostgresConnection.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Kysely, PostgresDialect } from "kysely";
|
||||
import { PostgresIntrospector } from "./PostgresIntrospector";
|
||||
import { PostgresConnection, plugins } from "./PostgresConnection";
|
||||
import { customIntrospector } from "bknd/data";
|
||||
import $pg from "pg";
|
||||
|
||||
export type PgPostgresConnectionConfig = $pg.PoolConfig;
|
||||
|
||||
export class PgPostgresConnection extends PostgresConnection {
|
||||
private pool: $pg.Pool;
|
||||
|
||||
constructor(config: PgPostgresConnectionConfig) {
|
||||
const pool = new $pg.Pool(config);
|
||||
const kysely = new Kysely({
|
||||
dialect: customIntrospector(PostgresDialect, PostgresIntrospector, {
|
||||
excludeTables: [],
|
||||
}).create({ pool }),
|
||||
plugins,
|
||||
});
|
||||
|
||||
super(kysely);
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
override async close(): Promise<void> {
|
||||
await this.pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
export function pg(config: PgPostgresConnectionConfig): PgPostgresConnection {
|
||||
return new PgPostgresConnection(config);
|
||||
}
|
||||
@@ -1,56 +1,33 @@
|
||||
import { Connection, type FieldSpec, type SchemaResponse } from "bknd/data";
|
||||
import { Connection, type DbFunctions, type FieldSpec, type SchemaResponse } from "bknd/data";
|
||||
import {
|
||||
ParseJSONResultsPlugin,
|
||||
type ColumnDataType,
|
||||
type ColumnDefinitionBuilder,
|
||||
type DatabaseIntrospector,
|
||||
Kysely,
|
||||
ParseJSONResultsPlugin,
|
||||
PostgresDialect,
|
||||
type Kysely,
|
||||
type KyselyPlugin,
|
||||
type SelectQueryBuilder,
|
||||
} from "kysely";
|
||||
import { jsonArrayFrom, jsonBuildObject, jsonObjectFrom } from "kysely/helpers/postgres";
|
||||
import pg from "pg";
|
||||
import { PostgresIntrospector } from "./PostgresIntrospector";
|
||||
|
||||
export type PostgresConnectionConfig = pg.PoolConfig;
|
||||
export type QB = SelectQueryBuilder<any, any, any>;
|
||||
|
||||
const plugins = [new ParseJSONResultsPlugin()];
|
||||
export const plugins = [new ParseJSONResultsPlugin()];
|
||||
|
||||
class CustomPostgresDialect extends PostgresDialect {
|
||||
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
||||
return new PostgresIntrospector(db, {
|
||||
excludeTables: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class PostgresConnection extends Connection {
|
||||
export abstract class PostgresConnection<DB = any> extends Connection<DB> {
|
||||
protected override readonly supported = {
|
||||
batching: true,
|
||||
};
|
||||
private pool: pg.Pool;
|
||||
|
||||
constructor(config: PostgresConnectionConfig) {
|
||||
const pool = new pg.Pool(config);
|
||||
const kysely = new Kysely({
|
||||
dialect: new CustomPostgresDialect({
|
||||
pool,
|
||||
}),
|
||||
plugins,
|
||||
//log: ["query", "error"],
|
||||
});
|
||||
|
||||
constructor(kysely: Kysely<DB>, fn?: Partial<DbFunctions>, _plugins?: KyselyPlugin[]) {
|
||||
super(
|
||||
kysely,
|
||||
{
|
||||
fn ?? {
|
||||
jsonArrayFrom,
|
||||
jsonBuildObject,
|
||||
jsonObjectFrom,
|
||||
},
|
||||
plugins,
|
||||
_plugins ?? plugins,
|
||||
);
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
override getFieldSchema(spec: FieldSpec): SchemaResponse {
|
||||
@@ -90,10 +67,6 @@ export class PostgresConnection extends Connection {
|
||||
];
|
||||
}
|
||||
|
||||
override async close(): Promise<void> {
|
||||
await this.pool.end();
|
||||
}
|
||||
|
||||
protected override async batch<Queries extends QB[]>(
|
||||
queries: [...Queries],
|
||||
): Promise<{
|
||||
|
||||
41
packages/postgres/src/PostgresJsConnection.ts
Normal file
41
packages/postgres/src/PostgresJsConnection.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Kysely } from "kysely";
|
||||
import { PostgresIntrospector } from "./PostgresIntrospector";
|
||||
import { PostgresConnection, plugins } from "./PostgresConnection";
|
||||
import { customIntrospector } from "bknd/data";
|
||||
import { PostgresJSDialect } from "kysely-postgres-js";
|
||||
import $postgresJs, { type Sql, type Options, type PostgresType } from "postgres";
|
||||
|
||||
export type PostgresJsConfig = Options<Record<string, PostgresType>>;
|
||||
|
||||
export class PostgresJsConnection extends PostgresConnection {
|
||||
private postgres: Sql;
|
||||
|
||||
constructor(opts: { postgres: Sql }) {
|
||||
const kysely = new Kysely({
|
||||
dialect: customIntrospector(PostgresJSDialect, PostgresIntrospector, {
|
||||
excludeTables: [],
|
||||
}).create({ postgres: opts.postgres }),
|
||||
plugins,
|
||||
});
|
||||
|
||||
super(kysely);
|
||||
this.postgres = opts.postgres;
|
||||
}
|
||||
|
||||
override async close(): Promise<void> {
|
||||
await this.postgres.end();
|
||||
}
|
||||
}
|
||||
|
||||
export function postgresJs(
|
||||
connectionString: string,
|
||||
config?: PostgresJsConfig,
|
||||
): PostgresJsConnection;
|
||||
export function postgresJs(config: PostgresJsConfig): PostgresJsConnection;
|
||||
export function postgresJs(
|
||||
first: PostgresJsConfig | string,
|
||||
second?: PostgresJsConfig,
|
||||
): PostgresJsConnection {
|
||||
const postgres = typeof first === "string" ? $postgresJs(first, second) : $postgresJs(first);
|
||||
return new PostgresJsConnection({ postgres });
|
||||
}
|
||||
43
packages/postgres/src/custom.ts
Normal file
43
packages/postgres/src/custom.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { Constructor } from "bknd/core";
|
||||
import { customIntrospector, type DbFunctions } from "bknd/data";
|
||||
import { Kysely, type Dialect, type KyselyPlugin } from "kysely";
|
||||
import { plugins, PostgresConnection } from "./PostgresConnection";
|
||||
import { PostgresIntrospector } from "./PostgresIntrospector";
|
||||
|
||||
export type CustomPostgresConnection = {
|
||||
supports?: PostgresConnection["supported"];
|
||||
fn?: Partial<DbFunctions>;
|
||||
plugins?: KyselyPlugin[];
|
||||
excludeTables?: string[];
|
||||
};
|
||||
|
||||
export function createCustomPostgresConnection<
|
||||
T extends Constructor<Dialect>,
|
||||
C extends ConstructorParameters<T>[0],
|
||||
>(
|
||||
dialect: Constructor<Dialect>,
|
||||
options?: CustomPostgresConnection,
|
||||
): (config: C) => PostgresConnection<any> {
|
||||
const supported = {
|
||||
batching: true,
|
||||
...((options?.supports ?? {}) as any),
|
||||
};
|
||||
|
||||
return (config: C) =>
|
||||
new (class extends PostgresConnection<any> {
|
||||
protected override readonly supported = supported;
|
||||
|
||||
constructor(config: C) {
|
||||
super(
|
||||
new Kysely({
|
||||
dialect: customIntrospector(dialect, PostgresIntrospector, {
|
||||
excludeTables: options?.excludeTables ?? [],
|
||||
}).create(config),
|
||||
plugins: options?.plugins ?? plugins,
|
||||
}),
|
||||
options?.fn,
|
||||
options?.plugins,
|
||||
);
|
||||
}
|
||||
})(config);
|
||||
}
|
||||
@@ -1,2 +1,5 @@
|
||||
export { PostgresConnection, type PostgresConnectionConfig } from "./PostgresConnection";
|
||||
export { pg, PgPostgresConnection, type PgPostgresConnectionConfig } from "./PgPostgresConnection";
|
||||
export { PostgresIntrospector } from "./PostgresIntrospector";
|
||||
export { PostgresConnection, type QB, plugins } from "./PostgresConnection";
|
||||
export { postgresJs, PostgresJsConnection, type PostgresJsConfig } from "./PostgresJsConnection";
|
||||
export { createCustomPostgresConnection } from "./custom";
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { describe, it, expect } from "bun:test";
|
||||
|
||||
import { PostgresConnection } from "../src";
|
||||
import { createConnection, cleanDatabase } from "./setup";
|
||||
|
||||
describe(PostgresConnection, () => {
|
||||
it("should connect to the database", async () => {
|
||||
const connection = createConnection();
|
||||
expect(await connection.ping()).toBe(true);
|
||||
});
|
||||
|
||||
it("should clean the database", async () => {
|
||||
const connection = createConnection();
|
||||
await cleanDatabase(connection);
|
||||
|
||||
const tables = await connection.getIntrospector().getTables();
|
||||
expect(tables).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -1,113 +0,0 @@
|
||||
import { describe, it, expect, beforeAll, afterAll, afterEach } from "bun:test";
|
||||
|
||||
import { createApp } from "bknd";
|
||||
import * as proto from "bknd/data";
|
||||
|
||||
import { createConnection, cleanDatabase } from "./setup";
|
||||
import type { PostgresConnection } from "../src";
|
||||
|
||||
let connection: PostgresConnection;
|
||||
beforeAll(async () => {
|
||||
connection = createConnection();
|
||||
await cleanDatabase(connection);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await cleanDatabase(connection);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await connection.close();
|
||||
});
|
||||
|
||||
describe("integration", () => {
|
||||
it("should create app and ping", async () => {
|
||||
const app = createApp({
|
||||
connection,
|
||||
});
|
||||
await app.build();
|
||||
|
||||
expect(app.version()).toBeDefined();
|
||||
expect(await app.em.ping()).toBe(true);
|
||||
});
|
||||
|
||||
it("should create a basic schema", async () => {
|
||||
const schema = proto.em(
|
||||
{
|
||||
posts: proto.entity("posts", {
|
||||
title: proto.text().required(),
|
||||
content: proto.text(),
|
||||
}),
|
||||
comments: proto.entity("comments", {
|
||||
content: proto.text(),
|
||||
}),
|
||||
},
|
||||
(fns, s) => {
|
||||
fns.relation(s.comments).manyToOne(s.posts);
|
||||
fns.index(s.posts).on(["title"], true);
|
||||
},
|
||||
);
|
||||
|
||||
const app = createApp({
|
||||
connection,
|
||||
initialConfig: {
|
||||
data: schema.toJSON(),
|
||||
},
|
||||
});
|
||||
|
||||
await app.build();
|
||||
|
||||
expect(app.em.entities.length).toBe(2);
|
||||
expect(app.em.entities.map((e) => e.name)).toEqual(["posts", "comments"]);
|
||||
|
||||
const api = app.getApi();
|
||||
|
||||
expect(
|
||||
(
|
||||
await api.data.createMany("posts", [
|
||||
{
|
||||
title: "Hello",
|
||||
content: "World",
|
||||
},
|
||||
{
|
||||
title: "Hello 2",
|
||||
content: "World 2",
|
||||
},
|
||||
])
|
||||
).data,
|
||||
).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
title: "Hello",
|
||||
content: "World",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Hello 2",
|
||||
content: "World 2",
|
||||
},
|
||||
] as any);
|
||||
|
||||
// try to create an existing
|
||||
expect(
|
||||
(
|
||||
await api.data.createOne("posts", {
|
||||
title: "Hello",
|
||||
})
|
||||
).ok,
|
||||
).toBe(false);
|
||||
|
||||
// add a comment to a post
|
||||
await api.data.createOne("comments", {
|
||||
content: "Hello",
|
||||
posts_id: 1,
|
||||
});
|
||||
|
||||
// and then query using a `with` property
|
||||
const result = await api.data.readMany("posts", { with: ["comments"] });
|
||||
expect(result.length).toBe(2);
|
||||
expect(result[0].comments.length).toBe(1);
|
||||
expect(result[0].comments[0].content).toBe("Hello");
|
||||
expect(result[1].comments.length).toBe(0);
|
||||
});
|
||||
});
|
||||
16
packages/postgres/test/pg.test.ts
Normal file
16
packages/postgres/test/pg.test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { describe } from "bun:test";
|
||||
import { pg } from "../src/PgPostgresConnection";
|
||||
import { testSuite } from "./suite";
|
||||
|
||||
describe("pg", () => {
|
||||
testSuite({
|
||||
createConnection: () =>
|
||||
pg({
|
||||
host: "localhost",
|
||||
port: 5430,
|
||||
user: "postgres",
|
||||
password: "postgres",
|
||||
database: "bknd",
|
||||
}),
|
||||
});
|
||||
});
|
||||
16
packages/postgres/test/postgresjs.test.ts
Normal file
16
packages/postgres/test/postgresjs.test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { describe } from "bun:test";
|
||||
import { postgresJs } from "../src/PostgresJsConnection";
|
||||
import { testSuite } from "./suite";
|
||||
|
||||
describe("postgresjs", () => {
|
||||
testSuite({
|
||||
createConnection: () =>
|
||||
postgresJs({
|
||||
host: "localhost",
|
||||
port: 5430,
|
||||
user: "postgres",
|
||||
password: "postgres",
|
||||
database: "bknd",
|
||||
}),
|
||||
});
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { Kysely } from "kysely";
|
||||
import { PostgresConnection, PostgresIntrospector, type PostgresConnectionConfig } from "../src";
|
||||
|
||||
export const info = {
|
||||
host: "localhost",
|
||||
port: 5430,
|
||||
user: "postgres",
|
||||
password: "postgres",
|
||||
database: "bknd",
|
||||
};
|
||||
|
||||
export function createConnection(config: PostgresConnectionConfig = {}) {
|
||||
return new PostgresConnection({
|
||||
...info,
|
||||
...config,
|
||||
});
|
||||
}
|
||||
|
||||
export async function cleanDatabase(connection: PostgresConnection) {
|
||||
const kysely = connection.kysely;
|
||||
|
||||
// drop all tables & create new schema
|
||||
await kysely.schema.dropSchema("public").ifExists().cascade().execute();
|
||||
await kysely.schema.createSchema("public").execute();
|
||||
}
|
||||
155
packages/postgres/test/suite.ts
Normal file
155
packages/postgres/test/suite.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { describe, beforeAll, afterAll, expect, it, afterEach } from "bun:test";
|
||||
import type { PostgresConnection } from "../src";
|
||||
import { createApp } from "bknd";
|
||||
import * as proto from "bknd/data";
|
||||
import { disableConsoleLog, enableConsoleLog } from "bknd/utils";
|
||||
|
||||
export type TestSuiteConfig = {
|
||||
createConnection: () => InstanceType<typeof PostgresConnection>;
|
||||
cleanDatabase?: (connection: InstanceType<typeof PostgresConnection>) => Promise<void>;
|
||||
};
|
||||
|
||||
export async function defaultCleanDatabase(connection: InstanceType<typeof PostgresConnection>) {
|
||||
const kysely = connection.kysely;
|
||||
|
||||
// drop all tables & create new schema
|
||||
await kysely.schema.dropSchema("public").ifExists().cascade().execute();
|
||||
await kysely.schema.createSchema("public").execute();
|
||||
}
|
||||
|
||||
async function cleanDatabase(
|
||||
connection: InstanceType<typeof PostgresConnection>,
|
||||
config: TestSuiteConfig,
|
||||
) {
|
||||
if (config.cleanDatabase) {
|
||||
await config.cleanDatabase(connection);
|
||||
} else {
|
||||
await defaultCleanDatabase(connection);
|
||||
}
|
||||
}
|
||||
|
||||
export function testSuite(config: TestSuiteConfig) {
|
||||
beforeAll(() => disableConsoleLog(["log", "warn", "error"]));
|
||||
afterAll(() => enableConsoleLog());
|
||||
|
||||
describe("base", () => {
|
||||
it("should connect to the database", async () => {
|
||||
const connection = config.createConnection();
|
||||
expect(await connection.ping()).toBe(true);
|
||||
});
|
||||
|
||||
it("should clean the database", async () => {
|
||||
const connection = config.createConnection();
|
||||
await cleanDatabase(connection, config);
|
||||
|
||||
const tables = await connection.getIntrospector().getTables();
|
||||
expect(tables).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("integration", () => {
|
||||
let connection: PostgresConnection;
|
||||
beforeAll(async () => {
|
||||
connection = config.createConnection();
|
||||
await cleanDatabase(connection, config);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await cleanDatabase(connection, config);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await connection.close();
|
||||
});
|
||||
|
||||
it("should create app and ping", async () => {
|
||||
const app = createApp({
|
||||
connection,
|
||||
});
|
||||
await app.build();
|
||||
|
||||
expect(app.version()).toBeDefined();
|
||||
expect(await app.em.ping()).toBe(true);
|
||||
});
|
||||
|
||||
it("should create a basic schema", async () => {
|
||||
const schema = proto.em(
|
||||
{
|
||||
posts: proto.entity("posts", {
|
||||
title: proto.text().required(),
|
||||
content: proto.text(),
|
||||
}),
|
||||
comments: proto.entity("comments", {
|
||||
content: proto.text(),
|
||||
}),
|
||||
},
|
||||
(fns, s) => {
|
||||
fns.relation(s.comments).manyToOne(s.posts);
|
||||
fns.index(s.posts).on(["title"], true);
|
||||
},
|
||||
);
|
||||
|
||||
const app = createApp({
|
||||
connection,
|
||||
initialConfig: {
|
||||
data: schema.toJSON(),
|
||||
},
|
||||
});
|
||||
|
||||
await app.build();
|
||||
|
||||
expect(app.em.entities.length).toBe(2);
|
||||
expect(app.em.entities.map((e) => e.name)).toEqual(["posts", "comments"]);
|
||||
|
||||
const api = app.getApi();
|
||||
|
||||
expect(
|
||||
(
|
||||
await api.data.createMany("posts", [
|
||||
{
|
||||
title: "Hello",
|
||||
content: "World",
|
||||
},
|
||||
{
|
||||
title: "Hello 2",
|
||||
content: "World 2",
|
||||
},
|
||||
])
|
||||
).data,
|
||||
).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
title: "Hello",
|
||||
content: "World",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Hello 2",
|
||||
content: "World 2",
|
||||
},
|
||||
] as any);
|
||||
|
||||
// try to create an existing
|
||||
expect(
|
||||
(
|
||||
await api.data.createOne("posts", {
|
||||
title: "Hello",
|
||||
})
|
||||
).ok,
|
||||
).toBe(false);
|
||||
|
||||
// add a comment to a post
|
||||
await api.data.createOne("comments", {
|
||||
content: "Hello",
|
||||
posts_id: 1,
|
||||
});
|
||||
|
||||
// and then query using a `with` property
|
||||
const result = await api.data.readMany("posts", { with: ["comments"] });
|
||||
expect(result.length).toBe(2);
|
||||
expect(result[0].comments.length).toBe(1);
|
||||
expect(result[0].comments[0].content).toBe("Hello");
|
||||
expect(result[1].comments.length).toBe(0);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user