mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-15 20:17:22 +00:00
Merge remote-tracking branch 'origin/main' into release/0.18
# Conflicts: # app/package.json
This commit is contained in:
@@ -59,7 +59,7 @@ You can create a custom kysely postgres dialect by using the `createCustomPostgr
|
||||
```ts
|
||||
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||
|
||||
const connection = createCustomPostgresConnection(MyDialect)({
|
||||
const connection = createCustomPostgresConnection("my_postgres_dialect", MyDialect)({
|
||||
// your custom dialect configuration
|
||||
supports: {
|
||||
batching: true
|
||||
@@ -75,7 +75,7 @@ const connection = createCustomPostgresConnection(MyDialect)({
|
||||
import { createCustomPostgresConnection } from "@bknd/postgres";
|
||||
import { NeonDialect } from "kysely-neon";
|
||||
|
||||
const connection = createCustomPostgresConnection(NeonDialect)({
|
||||
const connection = createCustomPostgresConnection("neon", NeonDialect)({
|
||||
connectionString: process.env.NEON,
|
||||
});
|
||||
```
|
||||
@@ -94,7 +94,7 @@ const xata = new client({
|
||||
branch: process.env.XATA_BRANCH,
|
||||
});
|
||||
|
||||
const connection = createCustomPostgresConnection(XataDialect, {
|
||||
const connection = createCustomPostgresConnection("xata", XataDialect, {
|
||||
supports: {
|
||||
batching: false,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bknd/postgres",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
@@ -31,8 +31,7 @@
|
||||
"@xata.io/kysely": "^0.2.1",
|
||||
"bknd": "workspace:*",
|
||||
"kysely-neon": "^1.3.0",
|
||||
"tsup": "^8.4.0",
|
||||
"typescript": "^5.8.2"
|
||||
"tsup": "^8.4.0"
|
||||
},
|
||||
"tsup": {
|
||||
"entry": ["src/index.ts"],
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Kysely, PostgresDialect } from "kysely";
|
||||
import { PostgresIntrospector } from "./PostgresIntrospector";
|
||||
import { PostgresConnection, plugins } from "./PostgresConnection";
|
||||
import { customIntrospector } from "bknd/data";
|
||||
import { customIntrospector } from "bknd";
|
||||
import $pg from "pg";
|
||||
|
||||
export type PgPostgresConnectionConfig = $pg.PoolConfig;
|
||||
|
||||
export class PgPostgresConnection extends PostgresConnection {
|
||||
override name = "pg";
|
||||
private pool: $pg.Pool;
|
||||
|
||||
constructor(config: PgPostgresConnectionConfig) {
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { Connection, type DbFunctions, type FieldSpec, type SchemaResponse } from "bknd/data";
|
||||
import {
|
||||
Connection,
|
||||
type DbFunctions,
|
||||
type FieldSpec,
|
||||
type SchemaResponse,
|
||||
type ConnQuery,
|
||||
type ConnQueryResults,
|
||||
} from "bknd";
|
||||
import {
|
||||
ParseJSONResultsPlugin,
|
||||
type ColumnDataType,
|
||||
@@ -13,12 +20,13 @@ export type QB = SelectQueryBuilder<any, any, any>;
|
||||
|
||||
export const plugins = [new ParseJSONResultsPlugin()];
|
||||
|
||||
export abstract class PostgresConnection<DB = any> extends Connection<DB> {
|
||||
export abstract class PostgresConnection extends Connection {
|
||||
protected override readonly supported = {
|
||||
batching: true,
|
||||
softscans: true,
|
||||
};
|
||||
|
||||
constructor(kysely: Kysely<DB>, fn?: Partial<DbFunctions>, _plugins?: KyselyPlugin[]) {
|
||||
constructor(kysely: Kysely<any>, fn?: Partial<DbFunctions>, _plugins?: KyselyPlugin[]) {
|
||||
super(
|
||||
kysely,
|
||||
fn ?? {
|
||||
@@ -73,13 +81,9 @@ export abstract class PostgresConnection<DB = any> extends Connection<DB> {
|
||||
];
|
||||
}
|
||||
|
||||
protected override async batch<Queries extends QB[]>(
|
||||
queries: [...Queries],
|
||||
): Promise<{
|
||||
[K in keyof Queries]: Awaited<ReturnType<Queries[K]["execute"]>>;
|
||||
}> {
|
||||
override async executeQueries<O extends ConnQuery[]>(...qbs: O): Promise<ConnQueryResults<O>> {
|
||||
return this.kysely.transaction().execute(async (trx) => {
|
||||
return Promise.all(queries.map((q) => trx.executeQuery(q).then((r) => r.rows)));
|
||||
return Promise.all(qbs.map((q) => trx.executeQuery(q)));
|
||||
}) as any;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type SchemaMetadata, sql } from "kysely";
|
||||
import { BaseIntrospector } from "bknd/data";
|
||||
import { BaseIntrospector } from "bknd";
|
||||
|
||||
type PostgresSchemaSpec = {
|
||||
name: string;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { Kysely } from "kysely";
|
||||
import { PostgresIntrospector } from "./PostgresIntrospector";
|
||||
import { PostgresConnection, plugins } from "./PostgresConnection";
|
||||
import { customIntrospector } from "bknd/data";
|
||||
import { customIntrospector } from "bknd";
|
||||
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 {
|
||||
override name = "postgres-js";
|
||||
|
||||
private postgres: Sql;
|
||||
|
||||
constructor(opts: { postgres: Sql }) {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { Constructor } from "bknd/core";
|
||||
import { customIntrospector, type DbFunctions } from "bknd/data";
|
||||
import { customIntrospector, type DbFunctions } from "bknd";
|
||||
import { Kysely, type Dialect, type KyselyPlugin } from "kysely";
|
||||
import { plugins, PostgresConnection } from "./PostgresConnection";
|
||||
import { PostgresIntrospector } from "./PostgresIntrospector";
|
||||
|
||||
export type Constructor<T> = new (...args: any[]) => T;
|
||||
|
||||
export type CustomPostgresConnection = {
|
||||
supports?: PostgresConnection["supported"];
|
||||
fn?: Partial<DbFunctions>;
|
||||
@@ -15,17 +16,19 @@ export function createCustomPostgresConnection<
|
||||
T extends Constructor<Dialect>,
|
||||
C extends ConstructorParameters<T>[0],
|
||||
>(
|
||||
name: string,
|
||||
dialect: Constructor<Dialect>,
|
||||
options?: CustomPostgresConnection,
|
||||
): (config: C) => PostgresConnection<any> {
|
||||
): (config: C) => PostgresConnection {
|
||||
const supported = {
|
||||
batching: true,
|
||||
...((options?.supports ?? {}) as any),
|
||||
};
|
||||
|
||||
return (config: C) =>
|
||||
new (class extends PostgresConnection<any> {
|
||||
protected override readonly supported = supported;
|
||||
new (class extends PostgresConnection {
|
||||
override name = name;
|
||||
override readonly supported = supported;
|
||||
|
||||
constructor(config: C) {
|
||||
super(
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
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 { createApp, em, entity, text } from "bknd";
|
||||
import { disableConsoleLog, enableConsoleLog } from "bknd/utils";
|
||||
// @ts-ignore
|
||||
import { connectionTestSuite } from "$bknd/data/connection/connection-test-suite";
|
||||
// @ts-ignore
|
||||
import { bunTestRunner } from "$bknd/adapter/bun/test";
|
||||
|
||||
export type TestSuiteConfig = {
|
||||
createConnection: () => InstanceType<typeof PostgresConnection>;
|
||||
@@ -12,8 +15,9 @@ export type TestSuiteConfig = {
|
||||
export async function defaultCleanDatabase(connection: InstanceType<typeof PostgresConnection>) {
|
||||
const kysely = connection.kysely;
|
||||
|
||||
// drop all tables & create new schema
|
||||
// drop all tables+indexes & create new schema
|
||||
await kysely.schema.dropSchema("public").ifExists().cascade().execute();
|
||||
await kysely.schema.dropIndex("public").ifExists().cascade().execute();
|
||||
await kysely.schema.createSchema("public").execute();
|
||||
}
|
||||
|
||||
@@ -32,6 +36,23 @@ export function testSuite(config: TestSuiteConfig) {
|
||||
beforeAll(() => disableConsoleLog(["log", "warn", "error"]));
|
||||
afterAll(() => enableConsoleLog());
|
||||
|
||||
// @todo: postgres seems to add multiple indexes, thus failing the test suite
|
||||
/* describe("test suite", () => {
|
||||
connectionTestSuite(bunTestRunner, {
|
||||
makeConnection: () => {
|
||||
const connection = config.createConnection();
|
||||
return {
|
||||
connection,
|
||||
dispose: async () => {
|
||||
await cleanDatabase(connection, config);
|
||||
await connection.close();
|
||||
},
|
||||
};
|
||||
},
|
||||
rawDialectDetails: [],
|
||||
});
|
||||
}); */
|
||||
|
||||
describe("base", () => {
|
||||
it("should connect to the database", async () => {
|
||||
const connection = config.createConnection();
|
||||
@@ -73,14 +94,14 @@ export function testSuite(config: TestSuiteConfig) {
|
||||
});
|
||||
|
||||
it("should create a basic schema", async () => {
|
||||
const schema = proto.em(
|
||||
const schema = em(
|
||||
{
|
||||
posts: proto.entity("posts", {
|
||||
title: proto.text().required(),
|
||||
content: proto.text(),
|
||||
posts: entity("posts", {
|
||||
title: text().required(),
|
||||
content: text(),
|
||||
}),
|
||||
comments: proto.entity("comments", {
|
||||
content: proto.text(),
|
||||
comments: entity("comments", {
|
||||
content: text(),
|
||||
}),
|
||||
},
|
||||
(fns, s) => {
|
||||
@@ -153,20 +174,20 @@ export function testSuite(config: TestSuiteConfig) {
|
||||
});
|
||||
|
||||
it("should support uuid", async () => {
|
||||
const schema = proto.em(
|
||||
const schema = em(
|
||||
{
|
||||
posts: proto.entity(
|
||||
posts: entity(
|
||||
"posts",
|
||||
{
|
||||
title: proto.text().required(),
|
||||
content: proto.text(),
|
||||
title: text().required(),
|
||||
content: text(),
|
||||
},
|
||||
{
|
||||
primary_format: "uuid",
|
||||
},
|
||||
),
|
||||
comments: proto.entity("comments", {
|
||||
content: proto.text(),
|
||||
comments: entity("comments", {
|
||||
content: text(),
|
||||
}),
|
||||
},
|
||||
(fns, s) => {
|
||||
@@ -187,8 +208,8 @@ export function testSuite(config: TestSuiteConfig) {
|
||||
// @ts-expect-error
|
||||
expect(config.data.entities?.posts.fields?.id.config?.format).toBe("uuid");
|
||||
|
||||
const em = app.em;
|
||||
const mutator = em.mutator(em.entity("posts"));
|
||||
const $em = app.em;
|
||||
const mutator = $em.mutator($em.entity("posts"));
|
||||
const data = await mutator.insertOne({ title: "Hello", content: "World" });
|
||||
expect(data.data.id).toBeString();
|
||||
expect(String(data.data.id).length).toBe(36);
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
{
|
||||
"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"]
|
||||
"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,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"$bknd/*": ["../../app/src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["./src/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user