mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-15 20:17:22 +00:00
move postgres to a separate package
This commit is contained in:
@@ -53,6 +53,9 @@ function banner(title: string) {
|
|||||||
console.log("-".repeat(40));
|
console.log("-".repeat(40));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// collection of always-external packages
|
||||||
|
const external = ["bun:test", "@libsql/client"] as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Building backend and general API
|
* Building backend and general API
|
||||||
*/
|
*/
|
||||||
@@ -64,7 +67,7 @@ async function buildApi() {
|
|||||||
watch,
|
watch,
|
||||||
entry: ["src/index.ts", "src/data/index.ts", "src/core/index.ts", "src/core/utils/index.ts"],
|
entry: ["src/index.ts", "src/data/index.ts", "src/core/index.ts", "src/core/utils/index.ts"],
|
||||||
outDir: "dist",
|
outDir: "dist",
|
||||||
external: ["bun:test", "@libsql/client"],
|
external: [...external],
|
||||||
metafile: true,
|
metafile: true,
|
||||||
platform: "browser",
|
platform: "browser",
|
||||||
format: ["esm"],
|
format: ["esm"],
|
||||||
@@ -93,7 +96,7 @@ async function buildUi() {
|
|||||||
sourcemap,
|
sourcemap,
|
||||||
watch,
|
watch,
|
||||||
external: [
|
external: [
|
||||||
"bun:test",
|
...external,
|
||||||
"react",
|
"react",
|
||||||
"react-dom",
|
"react-dom",
|
||||||
"react/jsx-runtime",
|
"react/jsx-runtime",
|
||||||
|
|||||||
@@ -85,7 +85,6 @@
|
|||||||
"kysely-d1": "^0.3.0",
|
"kysely-d1": "^0.3.0",
|
||||||
"open": "^10.1.0",
|
"open": "^10.1.0",
|
||||||
"openapi-types": "^12.1.3",
|
"openapi-types": "^12.1.3",
|
||||||
"pg": "^8.13.3",
|
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.3",
|
||||||
"postcss-preset-mantine": "^1.17.0",
|
"postcss-preset-mantine": "^1.17.0",
|
||||||
"postcss-simple-vars": "^7.0.1",
|
"postcss-simple-vars": "^7.0.1",
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ class CustomD1Dialect extends D1Dialect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class D1Connection extends SqliteConnection {
|
export class D1Connection extends SqliteConnection {
|
||||||
|
protected override readonly supported = {
|
||||||
|
batching: true,
|
||||||
|
};
|
||||||
|
|
||||||
constructor(private config: D1ConnectionConfig) {
|
constructor(private config: D1ConnectionConfig) {
|
||||||
const plugins = [new ParseJSONResultsPlugin()];
|
const plugins = [new ParseJSONResultsPlugin()];
|
||||||
|
|
||||||
@@ -28,14 +32,6 @@ export class D1Connection extends SqliteConnection {
|
|||||||
super(kysely, {}, plugins);
|
super(kysely, {}, plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
override supportsBatching(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
override supportsIndices(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async batch<Queries extends QB[]>(
|
protected override async batch<Queries extends QB[]>(
|
||||||
queries: [...Queries],
|
queries: [...Queries],
|
||||||
): Promise<{
|
): Promise<{
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
type AliasableExpression,
|
type AliasableExpression,
|
||||||
type ColumnBuilderCallback,
|
type ColumnBuilderCallback,
|
||||||
type ColumnDataType,
|
type ColumnDataType,
|
||||||
type DatabaseIntrospector,
|
|
||||||
type Expression,
|
type Expression,
|
||||||
type Kysely,
|
type Kysely,
|
||||||
type KyselyPlugin,
|
type KyselyPlugin,
|
||||||
@@ -77,6 +76,9 @@ const CONN_SYMBOL = Symbol.for("bknd:connection");
|
|||||||
|
|
||||||
export abstract class Connection<DB = any> {
|
export abstract class Connection<DB = any> {
|
||||||
kysely: Kysely<DB>;
|
kysely: Kysely<DB>;
|
||||||
|
protected readonly supported = {
|
||||||
|
batching: false,
|
||||||
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
kysely: Kysely<DB>,
|
kysely: Kysely<DB>,
|
||||||
@@ -101,13 +103,8 @@ export abstract class Connection<DB = any> {
|
|||||||
return this.kysely.introspection as any;
|
return this.kysely.introspection as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
supportsBatching(): boolean {
|
supports(feature: keyof typeof this.supported): boolean {
|
||||||
return false;
|
return this.supported[feature] ?? false;
|
||||||
}
|
|
||||||
|
|
||||||
// @todo: add if only first field is used in index
|
|
||||||
supportsIndices(): boolean {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async ping(): Promise<boolean> {
|
async ping(): Promise<boolean> {
|
||||||
@@ -129,7 +126,7 @@ export abstract class Connection<DB = any> {
|
|||||||
[K in keyof Queries]: Awaited<ReturnType<Queries[K]["execute"]>>;
|
[K in keyof Queries]: Awaited<ReturnType<Queries[K]["execute"]>>;
|
||||||
}> {
|
}> {
|
||||||
// bypass if no client support
|
// bypass if no client support
|
||||||
if (!this.supportsBatching()) {
|
if (!this.supports("batching")) {
|
||||||
const data: any = [];
|
const data: any = [];
|
||||||
for (const q of queries) {
|
for (const q of queries) {
|
||||||
const result = await q.execute();
|
const result = await q.execute();
|
||||||
@@ -151,5 +148,8 @@ export abstract class Connection<DB = any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract getFieldSchema(spec: FieldSpec, strict?: boolean): SchemaResponse;
|
abstract getFieldSchema(spec: FieldSpec, strict?: boolean): SchemaResponse;
|
||||||
abstract close(): Promise<void>;
|
|
||||||
|
async close(): Promise<void> {
|
||||||
|
// no-op by default
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { Connection, type FieldSpec, type SchemaResponse } from "./Connection";
|
import { Connection, type FieldSpec, type SchemaResponse } from "./Connection";
|
||||||
|
|
||||||
export class DummyConnection extends Connection {
|
export class DummyConnection extends Connection {
|
||||||
|
protected override readonly supported = {
|
||||||
|
batching: true,
|
||||||
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(undefined as any);
|
super(undefined as any);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
export { Connection } from "./Connection";
|
|
||||||
export { BaseIntrospector } from "./BaseIntrospector";
|
export { BaseIntrospector } from "./BaseIntrospector";
|
||||||
|
export {
|
||||||
|
Connection,
|
||||||
|
type FieldSpec,
|
||||||
|
type IndexSpec,
|
||||||
|
type DbFunctions,
|
||||||
|
type SchemaResponse,
|
||||||
|
} from "./Connection";
|
||||||
|
|
||||||
// sqlite
|
// sqlite
|
||||||
export { LibsqlConnection, type LibSqlCredentials } from "./sqlite/LibsqlConnection";
|
export { LibsqlConnection, type LibSqlCredentials } from "./sqlite/LibsqlConnection";
|
||||||
export { SqliteConnection } from "./sqlite/SqliteConnection";
|
export { SqliteConnection } from "./sqlite/SqliteConnection";
|
||||||
export { SqliteLocalConnection } from "./sqlite/SqliteLocalConnection";
|
|
||||||
export { SqliteIntrospector } from "./sqlite/SqliteIntrospector";
|
export { SqliteIntrospector } from "./sqlite/SqliteIntrospector";
|
||||||
|
export { SqliteLocalConnection } from "./sqlite/SqliteLocalConnection";
|
||||||
// postgres
|
|
||||||
export { PostgresConnection, type PostgresConnectionConfig } from "./postgres/PostgresConnection";
|
|
||||||
export { PostgresIntrospector } from "./postgres/PostgresIntrospector";
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { type Client, type Config, type InStatement, createClient } from "@libsql/client";
|
import { type Client, type Config, type InStatement, createClient } from "@libsql/client";
|
||||||
import { LibsqlDialect } from "@libsql/kysely-libsql";
|
import { LibsqlDialect } from "@libsql/kysely-libsql";
|
||||||
import { type DatabaseIntrospector, Kysely, ParseJSONResultsPlugin } from "kysely";
|
|
||||||
import { FilterNumericKeysPlugin } from "data/plugins/FilterNumericKeysPlugin";
|
import { FilterNumericKeysPlugin } from "data/plugins/FilterNumericKeysPlugin";
|
||||||
import { KyselyPluginRunner } from "data/plugins/KyselyPluginRunner";
|
import { KyselyPluginRunner } from "data/plugins/KyselyPluginRunner";
|
||||||
|
import { type DatabaseIntrospector, Kysely, ParseJSONResultsPlugin } from "kysely";
|
||||||
import type { QB } from "../Connection";
|
import type { QB } from "../Connection";
|
||||||
import { SqliteConnection } from "./SqliteConnection";
|
import { SqliteConnection } from "./SqliteConnection";
|
||||||
import { SqliteIntrospector } from "./SqliteIntrospector";
|
import { SqliteIntrospector } from "./SqliteIntrospector";
|
||||||
@@ -25,6 +25,9 @@ class CustomLibsqlDialect extends LibsqlDialect {
|
|||||||
|
|
||||||
export class LibsqlConnection extends SqliteConnection {
|
export class LibsqlConnection extends SqliteConnection {
|
||||||
private client: Client;
|
private client: Client;
|
||||||
|
protected override readonly supported = {
|
||||||
|
batching: true,
|
||||||
|
};
|
||||||
|
|
||||||
constructor(client: Client);
|
constructor(client: Client);
|
||||||
constructor(credentials: LibSqlCredentials);
|
constructor(credentials: LibSqlCredentials);
|
||||||
@@ -53,14 +56,6 @@ export class LibsqlConnection extends SqliteConnection {
|
|||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
override supportsBatching(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
override supportsIndices(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getClient(): Client {
|
getClient(): Client {
|
||||||
return this.client;
|
return this.client;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ export class SqliteConnection extends Connection {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
override supportsIndices(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
override getFieldSchema(spec: FieldSpec): SchemaResponse {
|
override getFieldSchema(spec: FieldSpec): SchemaResponse {
|
||||||
this.validateFieldSpecType(spec.type);
|
this.validateFieldSpecType(spec.type);
|
||||||
let type: ColumnDataType = spec.type;
|
let type: ColumnDataType = spec.type;
|
||||||
@@ -47,8 +43,4 @@ export class SqliteConnection extends Connection {
|
|||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
override async close(): Promise<void> {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { type DatabaseIntrospector, ParseJSONResultsPlugin, type SqliteDatabase } from "kysely";
|
import {
|
||||||
import { Kysely, SqliteDialect } from "kysely";
|
type DatabaseIntrospector,
|
||||||
|
Kysely,
|
||||||
|
ParseJSONResultsPlugin,
|
||||||
|
type SqliteDatabase,
|
||||||
|
SqliteDialect,
|
||||||
|
} from "kysely";
|
||||||
import { SqliteConnection } from "./SqliteConnection";
|
import { SqliteConnection } from "./SqliteConnection";
|
||||||
import { SqliteIntrospector } from "./SqliteIntrospector";
|
import { SqliteIntrospector } from "./SqliteIntrospector";
|
||||||
|
|
||||||
@@ -23,8 +28,4 @@ export class SqliteLocalConnection extends SqliteConnection {
|
|||||||
|
|
||||||
super(kysely, {}, plugins);
|
super(kysely, {}, plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
override supportsIndices(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,10 +49,6 @@ export class SchemaManager {
|
|||||||
constructor(private readonly em: EntityManager<any>) {}
|
constructor(private readonly em: EntityManager<any>) {}
|
||||||
|
|
||||||
private getIntrospector() {
|
private getIntrospector() {
|
||||||
if (!this.em.connection.supportsIndices()) {
|
|
||||||
throw new Error("Indices are not supported by the current connection");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.em.connection.getIntrospector();
|
return this.em.connection.getIntrospector();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
bun.lock
47
bun.lock
@@ -83,7 +83,6 @@
|
|||||||
"kysely-d1": "^0.3.0",
|
"kysely-d1": "^0.3.0",
|
||||||
"open": "^10.1.0",
|
"open": "^10.1.0",
|
||||||
"openapi-types": "^12.1.3",
|
"openapi-types": "^12.1.3",
|
||||||
"pg": "^8.13.3",
|
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.3",
|
||||||
"postcss-preset-mantine": "^1.17.0",
|
"postcss-preset-mantine": "^1.17.0",
|
||||||
"postcss-simple-vars": "^7.0.1",
|
"postcss-simple-vars": "^7.0.1",
|
||||||
@@ -139,6 +138,22 @@
|
|||||||
"react-dom": ">=18",
|
"react-dom": ">=18",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"packages/postgres": {
|
||||||
|
"name": "@bknd/postgres",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"dependencies": {
|
||||||
|
"kysely": "^0.27.6",
|
||||||
|
"pg": "^8.12.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "^1.2.5",
|
||||||
|
"@types/node": "^22.13.10",
|
||||||
|
"@types/pg": "^8.11.11",
|
||||||
|
"bknd": "workspace:*",
|
||||||
|
"tsup": "^8.4.0",
|
||||||
|
"typescript": "^5.6.3",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||||
@@ -447,6 +462,8 @@
|
|||||||
|
|
||||||
"@bknd/plasmic": ["@bknd/plasmic@workspace:packages/plasmic"],
|
"@bknd/plasmic": ["@bknd/plasmic@workspace:packages/plasmic"],
|
||||||
|
|
||||||
|
"@bknd/postgres": ["@bknd/postgres@workspace:packages/postgres"],
|
||||||
|
|
||||||
"@bluwy/giget-core": ["@bluwy/giget-core@0.1.2", "", { "dependencies": { "tar": "^6.2.1" } }, "sha512-v9f+ueUOKkZCDKiCm0yxKtYgYNLD9zlKarNux0NSXOvNm94QEYL3RlMpGKgD2hq44pbF2qWqEmHnCvmk56kPJw=="],
|
"@bluwy/giget-core": ["@bluwy/giget-core@0.1.2", "", { "dependencies": { "tar": "^6.2.1" } }, "sha512-v9f+ueUOKkZCDKiCm0yxKtYgYNLD9zlKarNux0NSXOvNm94QEYL3RlMpGKgD2hq44pbF2qWqEmHnCvmk56kPJw=="],
|
||||||
|
|
||||||
"@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="],
|
"@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="],
|
||||||
@@ -1171,6 +1188,8 @@
|
|||||||
|
|
||||||
"@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
|
"@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
|
||||||
|
|
||||||
|
"@types/pg": ["@types/pg@8.11.11", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^4.0.1" } }, "sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw=="],
|
||||||
|
|
||||||
"@types/prettier": ["@types/prettier@1.19.1", "", {}, "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ=="],
|
"@types/prettier": ["@types/prettier@1.19.1", "", {}, "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.0.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g=="],
|
"@types/react": ["@types/react@19.0.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g=="],
|
||||||
@@ -2527,6 +2546,8 @@
|
|||||||
|
|
||||||
"object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
|
"object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
|
||||||
|
|
||||||
|
"obuf": ["obuf@1.1.2", "", {}, "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="],
|
||||||
|
|
||||||
"ohash": ["ohash@1.1.6", "", {}, "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg=="],
|
"ohash": ["ohash@1.1.6", "", {}, "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg=="],
|
||||||
|
|
||||||
"on-exit-leak-free": ["on-exit-leak-free@0.2.0", "", {}, "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg=="],
|
"on-exit-leak-free": ["on-exit-leak-free@0.2.0", "", {}, "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg=="],
|
||||||
@@ -2605,11 +2626,13 @@
|
|||||||
|
|
||||||
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
|
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
|
||||||
|
|
||||||
|
"pg-numeric": ["pg-numeric@1.0.2", "", {}, "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw=="],
|
||||||
|
|
||||||
"pg-pool": ["pg-pool@3.8.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw=="],
|
"pg-pool": ["pg-pool@3.8.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw=="],
|
||||||
|
|
||||||
"pg-protocol": ["pg-protocol@1.8.0", "", {}, "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g=="],
|
"pg-protocol": ["pg-protocol@1.8.0", "", {}, "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g=="],
|
||||||
|
|
||||||
"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=="],
|
"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=="],
|
||||||
|
|
||||||
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
|
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
|
||||||
|
|
||||||
@@ -2661,13 +2684,15 @@
|
|||||||
|
|
||||||
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
||||||
|
|
||||||
"postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
"postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="],
|
||||||
|
|
||||||
"postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="],
|
"postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="],
|
||||||
|
|
||||||
"postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
"postgres-date": ["postgres-date@2.1.0", "", {}, "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="],
|
||||||
|
|
||||||
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
"postgres-interval": ["postgres-interval@3.0.0", "", {}, "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="],
|
||||||
|
|
||||||
|
"postgres-range": ["postgres-range@1.1.4", "", {}, "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w=="],
|
||||||
|
|
||||||
"prelude-ls": ["prelude-ls@1.1.2", "", {}, "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w=="],
|
"prelude-ls": ["prelude-ls@1.1.2", "", {}, "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w=="],
|
||||||
|
|
||||||
@@ -3981,6 +4006,8 @@
|
|||||||
|
|
||||||
"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=="],
|
"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=="],
|
||||||
|
|
||||||
"pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
"pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
"pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
||||||
@@ -4351,6 +4378,14 @@
|
|||||||
|
|
||||||
"ora/log-symbols/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
|
"ora/log-symbols/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
|
||||||
|
|
||||||
|
"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/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=="],
|
"progress-estimator/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
||||||
|
|||||||
50
packages/postgres/README.md
Normal file
50
packages/postgres/README.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Postgres adapter for `bknd` (experimental)
|
||||||
|
This packages adds an adapter to use a Postgres database with `bknd`. It is based on `pg` and the driver included in `kysely`.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Install the adapter with:
|
||||||
|
```bash
|
||||||
|
npm install @bknd/postgres
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Create a connection:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { PostgresConnection } from "@bknd/postgres";
|
||||||
|
|
||||||
|
const connection = new PostgresConnection({
|
||||||
|
host: "localhost",
|
||||||
|
port: 5432,
|
||||||
|
user: "postgres",
|
||||||
|
password: "postgres",
|
||||||
|
database: "bknd",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the connection depending on which framework or runtime you are using. E.g., when using `createApp`, you can use the connection as follows:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createApp } from "bknd";
|
||||||
|
import { PostgresConnection } from "@bknd/postgres";
|
||||||
|
|
||||||
|
const connection = new PostgresConnection();
|
||||||
|
const app = createApp({ connection });
|
||||||
|
```
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// e.g. in src/app/api/[[...bknd]]/route.ts
|
||||||
|
import { serve } from "bknd/adapter/nextjs";
|
||||||
|
import { PostgresConnection } from "@bknd/postgres";
|
||||||
|
|
||||||
|
const connection = new PostgresConnection();
|
||||||
|
const handler = serve({
|
||||||
|
connection
|
||||||
|
})
|
||||||
|
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information about how to integrate Next.js in general, check out the [Next.js documentation](https://docs.bknd.io/integration/nextjs).
|
||||||
37
packages/postgres/package.json
Normal file
37
packages/postgres/package.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"name": "@bknd/postgres",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "module",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"module": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsup",
|
||||||
|
"test": "bun test",
|
||||||
|
"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": {
|
||||||
|
"pg": "^8.12.0",
|
||||||
|
"kysely": "^0.27.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "^1.2.5",
|
||||||
|
"@types/node": "^22.13.10",
|
||||||
|
"@types/pg": "^8.11.11",
|
||||||
|
"bknd": "workspace:*",
|
||||||
|
"tsup": "^8.4.0",
|
||||||
|
"typescript": "^5.6.3"
|
||||||
|
},
|
||||||
|
"tsup": {
|
||||||
|
"entry": ["src/index.ts"],
|
||||||
|
"format": ["esm"],
|
||||||
|
"target": "es2022",
|
||||||
|
"clean": true,
|
||||||
|
"minify": true,
|
||||||
|
"dts": true,
|
||||||
|
"metafile": true,
|
||||||
|
"external": ["bknd", "pg", "kysely"]
|
||||||
|
},
|
||||||
|
"files": ["dist", "!*.map", "!metafile*.json"]
|
||||||
|
}
|
||||||
@@ -1,31 +1,34 @@
|
|||||||
|
import { Connection, type FieldSpec, type SchemaResponse } from "bknd/data";
|
||||||
import {
|
import {
|
||||||
Kysely,
|
|
||||||
PostgresDialect,
|
|
||||||
type DatabaseIntrospector,
|
|
||||||
type ColumnDataType,
|
type ColumnDataType,
|
||||||
type ColumnDefinitionBuilder,
|
type ColumnDefinitionBuilder,
|
||||||
|
type DatabaseIntrospector,
|
||||||
|
Kysely,
|
||||||
ParseJSONResultsPlugin,
|
ParseJSONResultsPlugin,
|
||||||
|
PostgresDialect,
|
||||||
|
type SelectQueryBuilder,
|
||||||
} from "kysely";
|
} from "kysely";
|
||||||
|
import { jsonArrayFrom, jsonBuildObject, jsonObjectFrom } from "kysely/helpers/postgres";
|
||||||
import pg from "pg";
|
import pg from "pg";
|
||||||
import { PostgresIntrospector } from "./PostgresIntrospector";
|
import { PostgresIntrospector } from "./PostgresIntrospector";
|
||||||
import {
|
|
||||||
type FieldSpec,
|
|
||||||
type SchemaResponse,
|
|
||||||
Connection,
|
|
||||||
type QB,
|
|
||||||
} from "data/connection/Connection";
|
|
||||||
|
|
||||||
export type PostgresConnectionConfig = pg.PoolConfig;
|
export type PostgresConnectionConfig = pg.PoolConfig;
|
||||||
|
export type QB = SelectQueryBuilder<any, any, any>;
|
||||||
|
|
||||||
const plugins = [new ParseJSONResultsPlugin()];
|
const plugins = [new ParseJSONResultsPlugin()];
|
||||||
|
|
||||||
class CustomPostgresDialect extends PostgresDialect {
|
class CustomPostgresDialect extends PostgresDialect {
|
||||||
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
||||||
return new PostgresIntrospector(db);
|
return new PostgresIntrospector(db, {
|
||||||
|
excludeTables: [],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PostgresConnection extends Connection {
|
export class PostgresConnection extends Connection {
|
||||||
|
protected override readonly supported = {
|
||||||
|
batching: true,
|
||||||
|
};
|
||||||
private pool: pg.Pool;
|
private pool: pg.Pool;
|
||||||
|
|
||||||
constructor(config: PostgresConnectionConfig) {
|
constructor(config: PostgresConnectionConfig) {
|
||||||
@@ -38,14 +41,18 @@ export class PostgresConnection extends Connection {
|
|||||||
//log: ["query", "error"],
|
//log: ["query", "error"],
|
||||||
});
|
});
|
||||||
|
|
||||||
super(kysely, {}, plugins);
|
super(
|
||||||
|
kysely,
|
||||||
|
{
|
||||||
|
jsonArrayFrom,
|
||||||
|
jsonBuildObject,
|
||||||
|
jsonObjectFrom,
|
||||||
|
},
|
||||||
|
plugins,
|
||||||
|
);
|
||||||
this.pool = pool;
|
this.pool = pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
override supportsIndices(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
override getFieldSchema(spec: FieldSpec): SchemaResponse {
|
override getFieldSchema(spec: FieldSpec): SchemaResponse {
|
||||||
this.validateFieldSpecType(spec.type);
|
this.validateFieldSpecType(spec.type);
|
||||||
let type: ColumnDataType = spec.primary ? "serial" : spec.type;
|
let type: ColumnDataType = spec.primary ? "serial" : spec.type;
|
||||||
@@ -83,10 +90,6 @@ export class PostgresConnection extends Connection {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
override supportsBatching(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
override async close(): Promise<void> {
|
override async close(): Promise<void> {
|
||||||
await this.pool.end();
|
await this.pool.end();
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { type SchemaMetadata, sql } from "kysely";
|
import { type SchemaMetadata, sql } from "kysely";
|
||||||
import { BaseIntrospector } from "data/connection/BaseIntrospector";
|
import { BaseIntrospector } from "bknd/data";
|
||||||
|
|
||||||
type PostgresSchemaSpec = {
|
type PostgresSchemaSpec = {
|
||||||
name: string;
|
name: string;
|
||||||
2
packages/postgres/src/index.ts
Normal file
2
packages/postgres/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { PostgresConnection, type PostgresConnectionConfig } from "./PostgresConnection";
|
||||||
|
export { PostgresIntrospector } from "./PostgresIntrospector";
|
||||||
19
packages/postgres/test/base.test.ts
Normal file
19
packages/postgres/test/base.test.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
113
packages/postgres/test/integration.test.ts
Normal file
113
packages/postgres/test/integration.test.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
25
packages/postgres/test/setup.ts
Normal file
25
packages/postgres/test/setup.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
29
packages/postgres/tsconfig.json
Normal file
29
packages/postgres/tsconfig.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": false,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": false,
|
||||||
|
"target": "ES2022",
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"allowJs": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"declaration": true,
|
||||||
|
"strict": true,
|
||||||
|
"allowUnusedLabels": false,
|
||||||
|
"allowUnreachableCode": false,
|
||||||
|
"exactOptionalPropertyTypes": false,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": false,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["./src/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user