2 Commits
Author SHA1 Message Date
theoludwig d826ffe978 docs: add Bun.SQL support 2026-10-16 19:13:03 +02:00
theoludwig 6e1dafbfce docs: add PGlite support
Kysely's built-in PGliteDialect uses the same introspector as PostgreSQL, so KyselyTypegenPostgresDialect works with it as-is.
2026-10-09 21:22:35 +02:00
11 changed files with 634 additions and 203 deletions

No files matched your search

+7
View File
@@ -24,9 +24,16 @@ jobs:
cache: "npm"
- run: "node --version"
- name: "Setup Bun"
uses: "oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6" # v2.2.0
with:
bun-version: "1.4.3"
- run: "bun --revision"
- run: "npm clean-install"
# - run: "node --run lint:typescript" # already covered by oxlint
- run: "node --run lint:oxlint"
- run: "node --run lint:oxfmt"
- run: "node --run test"
- run: "node --run test:bun"
- run: "node --run build"
+1
View File
@@ -47,6 +47,7 @@ node --run lint:oxfmt
# Tests
node --run test
node --run test:bun
# Build
node --run build
+64 -2
View File
@@ -16,7 +16,7 @@ Why `kysely-typegen` if there is already `kysely-codegen`? Comparison:
| **Dependencies** | 35 total | 0 (no runtime dependencies) |
| **Type** | CLI | Library/Programmatic Usage |
| **Code Size/Maintainability** | Heavy | Lightweight/Simple and straightforward (string manipulation instead of complex AST) |
| **Database Support** | PostgreSQL, MySQL, SQLite, MSSQL, LibSQL | PostgreSQL, MySQL, SQLite (**can be easily extended to more**) |
| **Database Support** | PostgreSQL, MySQL, SQLite, MSSQL, LibSQL | PostgreSQL, PGlite, MySQL, SQLite (**can be easily extended to more**) |
`kysely-typegen` is a **library** (not a CLI), which means you are in control of where and how to run it, and is designed to be **extensible**, easy to add support for more database dialects.
@@ -50,7 +50,7 @@ Create your Kysely database instance and export a `databaseTypegen` for the scri
#### PostgreSQL
`KyselyTypegenPostgresDialect` works with any Kysely PostgreSQL dialect, including the built-in [`PostgresDialect`](https://kysely.dev/docs/dialects/postgres) (using [`pg`](https://npmx.dev/package/pg)) and [`PostgresJSDialect`](https://npmx.dev/package/kysely-postgres-js) (using [`postgres`](https://npmx.dev/package/postgres)). The example below uses `kysely-postgres-js`.
`KyselyTypegenPostgresDialect` works with any Kysely PostgreSQL dialect, including the built-in [`PostgresDialect`](https://kysely.dev/docs/dialects/postgres) (using [`pg`](https://npmx.dev/package/pg)) and [`PostgresJSDialect`](https://npmx.dev/package/kysely-postgres-js) (using [`postgres`](https://npmx.dev/package/postgres) or [`Bun.SQL`](#bunsql)). The example below uses `kysely-postgres-js`.
```sh
npm install kysely-postgres-js postgres
@@ -79,6 +79,62 @@ export const database = new Kysely<DB>({ dialect })
export const databaseTypegen = new KyselyTypegenPostgresDialect({ database })
```
#### Bun.SQL
With [Bun](https://bun.com/), pass the built-in [`Bun.SQL`](https://bun.com/docs/runtime/sql) client to `PostgresJSDialect` instead of `postgres`, so no extra driver is required.
```sh
bun add kysely-postgres-js
```
```ts
// database.ts
import Bun from "bun"
import { Kysely } from "kysely"
import { PostgresJSDialect } from "kysely-postgres-js"
import { KyselyTypegenPostgresDialect } from "kysely-typegen/postgres"
import type { DB } from "./codegen.ts"
const dialect = new PostgresJSDialect({
postgres: new Bun.SQL({
adapter: "postgres",
database: process.env["DATABASE_NAME"] ?? "database",
hostname: process.env["DATABASE_HOST"] ?? "localhost",
username: process.env["DATABASE_USER"] ?? "user",
password: process.env["DATABASE_PASSWORD"] ?? "password",
port: Number.parseInt(process.env["DATABASE_PORT"] ?? "5432", 10),
}),
})
export const database = new Kysely<DB>({ dialect })
export const databaseTypegen = new KyselyTypegenPostgresDialect({ database })
```
#### PGlite
[PGlite](https://pglite.dev/) is PostgreSQL compiled to WebAssembly that runs in-process. Use Kysely's built-in [`PGliteDialect`](https://kysely-org.github.io/kysely-apidoc/classes/PGliteDialect.html) with `KyselyTypegenPostgresDialect`: PGlite is introspected exactly like PostgreSQL. Because `kysely-typegen` is a library, you pass it the `Kysely` instance you already have, so it works with in-process databases like PGlite, including in-memory ones, without a connection string or CLI-specific dialect support.
```sh
npm install @electric-sql/pglite
```
```ts
// database.ts
import { PGlite } from "@electric-sql/pglite"
import { Kysely, PGliteDialect } from "kysely"
import { KyselyTypegenPostgresDialect } from "kysely-typegen/postgres"
import type { DB } from "./codegen.ts"
const dialect = new PGliteDialect({
pglite: new PGlite(process.env["DATABASE_PATH"] ?? "./pgdata"),
})
export const database = new Kysely<DB>({ dialect })
export const databaseTypegen = new KyselyTypegenPostgresDialect({ database })
```
#### MySQL
```sh
@@ -155,6 +211,12 @@ Run with Node.js (using `--env-file` to load environment variables, no `dotenv`
node --env-file=.env scripts/typegen.ts
```
Or run with Bun (loads `.env` automatically):
```sh
bun scripts/typegen.ts
```
### Using the type definitions
Import `DB` into `new Kysely<DB>`, and you're done!
+30
View File
@@ -9,9 +9,11 @@
"version": "0.0.0-development",
"license": "MIT",
"devDependencies": {
"@electric-sql/pglite": "0.5.8",
"@testcontainers/mysql": "12.2.0",
"@testcontainers/postgresql": "12.2.0",
"@types/better-sqlite3": "9.6.0",
"@types/bun": "1.4.3",
"@types/node": "26.6.4",
"@types/pg": "8.23.1",
"better-sqlite3": "13.0.3",
@@ -126,6 +128,13 @@
"node": ">=0.1.90"
}
},
"node_modules/@electric-sql/pglite": {
"version": "0.5.8",
"resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.5.8.tgz",
"integrity": "sha512-n9tsbUOhwx2epK1V0ZG9Ar4SHWUju04dhmzZXiSBXwBoleOvIfals33NAaWgagQVAL4Rbvx/Ptsu3P+pA09f6Q==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/@grpc/grpc-js": {
"version": "1.14.5",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.5.tgz",
@@ -2085,6 +2094,16 @@
"@types/node": "*"
}
},
"node_modules/@types/bun": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.4.3.tgz",
"integrity": "sha512-N67Pqy8JXbAY/j0tNraS3V5mubGvc6rrrNDmDCtZLlFzccbUJPDb0tvgUBC5q4m8ybkP93Jcn1QB+RYQ6PvBhw==",
"dev": true,
"license": "MIT",
"dependencies": {
"bun-types": "1.4.3"
}
},
"node_modules/@types/docker-modem": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz",
@@ -3469,6 +3488,17 @@
"node": ">=10.0.0"
}
},
"node_modules/bun-types": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.4.3.tgz",
"integrity": "sha512-tinRJNGOmUN+tEAsn2y2e942n9JHRHR42iUYzDH75NmhJqVBokGSQkkfSFdNWwsANIdrK6ACH9t01M452YyTwg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*",
"undici-types": "*"
}
},
"node_modules/byline": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz",
+4 -1
View File
@@ -36,14 +36,17 @@
"lint:oxlint": "oxlint .",
"lint:oxfmt": "oxfmt . --check",
"oxfmt": "oxfmt .",
"test": "node --test",
"test": "node --test \"src/_test/*.test.ts\"",
"test:bun": "bun test src/_test/bun",
"build": "tsdown",
"release": "semantic-release"
},
"devDependencies": {
"@electric-sql/pglite": "0.5.8",
"@testcontainers/mysql": "12.2.0",
"@testcontainers/postgresql": "12.2.0",
"@types/better-sqlite3": "9.6.0",
"@types/bun": "1.4.3",
"@types/node": "26.6.4",
"@types/pg": "8.23.1",
"better-sqlite3": "13.0.3",
@@ -1,3 +1,121 @@
exports[`typegen PGlite > generate types matching snapshot 1`] = `
{
"lines": [
"// This file was automatically generated by \`kysely-typegen\`.",
"// Do not edit this file manually.",
"",
"import type { ColumnType } from \\"kysely\\"",
"",
"export type Generated<T> = T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>",
"",
"export type Timestamp = ColumnType<Date, Date | string, Date | string>",
"",
"export type Numeric = ColumnType<string, number | string, number | string>",
"",
"export type Int8 = ColumnType<string, bigint | number | string, bigint | number | string>",
"",
"export type Json = JsonValue",
"",
"export type JsonArray = JsonValue[]",
"",
"export interface JsonObject {",
" [x: string]: JsonValue | undefined",
"}",
"",
"export type JsonPrimitive = boolean | number | string | null",
"",
"export type JsonValue = JsonArray | JsonObject | JsonPrimitive",
"",
"export type Currency = \\"EUR\\" | \\"GBP\\" | \\"USD\\"",
"",
"export type OrderStatus = \\"cancelled\\" | \\"paid\\" | \\"pending\\" | \\"shipped\\"",
"",
"export type UserRole = \\"admin\\" | \\"guest\\" | \\"member\\"",
"",
"export interface AllTypes {",
" colBit: string",
" colBool: boolean",
" colBoolDefault: Generated<boolean>",
" colBoolNullable: boolean | null",
" colBox: string",
" colBpchar: string",
" colBytea: Buffer",
" colCidr: string",
" colDate: Timestamp",
" colFloat4: number",
" colFloat8: number",
" colInet: string",
" colInt2: number",
" colInt4: number",
" colInt8: Int8",
" colJson: Json",
" colJsonb: Json",
" colJsonbDefault: Generated<Json>",
" colLine: string",
" colLseg: string",
" colMacaddr: string",
" colMoney: string",
" colNumeric: Numeric",
" colOid: number",
" colPath: string",
" colPoint: unknown",
" colPolygon: string",
" colText: string",
" colTextNullable: string | null",
" colTime: string",
" colTimestamp: Timestamp",
" colTimestampDefault: Generated<Timestamp>",
" colTimestamptz: Timestamp",
" colTimetz: string",
" colTsquery: string",
" colTsvector: string",
" colUuid: string",
" colVarbit: string",
" colVarchar: string",
" colXml: string",
" createdAt: Generated<Timestamp>",
" id: Generated<string>",
" updatedAt: Timestamp | null",
"}",
"",
"export interface Orders {",
" /**",
" * Total amount in cents.",
" *",
" * Excludes taxes.",
" */",
" amountCents: number",
" createdAt: Generated<Timestamp>",
" currency: Generated<Currency>",
" id: Generated<Int8>",
" /** Free text, may contain *\\\\/ characters. */",
" note: string | null",
" status: Generated<OrderStatus>",
" userId: string",
"}",
"",
"export interface Users {",
" createdAt: Generated<Timestamp>",
" /** Contact email address. */",
" email: string | null",
" id: Generated<string>",
" isActive: Generated<boolean>",
" role: Generated<UserRole>",
" username: string",
"}",
"",
"export interface DB {",
" AllTypes: AllTypes",
" Orders: Orders",
" Users: Users",
"}"
],
"tablesCount": 3,
"enumsCount": 3,
"inlineEnumsCount": 0
}
`;
exports[`typegen PostgreSQL > generate types matching snapshot (kysely-postgres-js) 1`] = `
{
"lines": [
+200
View File
@@ -0,0 +1,200 @@
import type { Kysely } from "kysely"
import { sql } from "kysely"
export const POSTGRES_IMAGE =
"docker.io/postgres:18.4@sha256:f7ce845ee6873dd84be93c9828fe0d1fab0f9707dc9ac569694657398b290bce"
export const createSchema = async (database: Kysely<any>): Promise<void> => {
await database.schema.createType("Currency").asEnum(["EUR", "USD", "GBP"]).execute()
await database.schema.createType("UserRole").asEnum(["admin", "member", "guest"]).execute()
await database.schema
.createType("OrderStatus")
.asEnum(["pending", "paid", "shipped", "cancelled"])
.execute()
await database.schema
.createTable("AllTypes")
.addColumn("id", "uuid", (column) => {
return column
.notNull()
.primaryKey()
.defaultTo(sql`uuidv7()`)
})
.addColumn("colBool", "boolean", (column) => {
return column.notNull()
})
.addColumn("colBoolNullable", "boolean")
.addColumn("colBoolDefault", "boolean", (column) => {
return column.notNull().defaultTo(false)
})
.addColumn("colInt2", "smallint", (column) => {
return column.notNull()
})
.addColumn("colInt4", "integer", (column) => {
return column.notNull()
})
.addColumn("colInt8", "bigint", (column) => {
return column.notNull()
})
.addColumn("colFloat4", "real", (column) => {
return column.notNull()
})
.addColumn("colFloat8", "double precision", (column) => {
return column.notNull()
})
.addColumn("colNumeric", sql`numeric(12, 2)`, (column) => {
return column.notNull()
})
.addColumn("colMoney", sql`money`, (column) => {
return column.notNull()
})
.addColumn("colText", "text", (column) => {
return column.notNull()
})
.addColumn("colVarchar", "varchar(255)", (column) => {
return column.notNull()
})
.addColumn("colBpchar", sql`char(10)`, (column) => {
return column.notNull()
})
.addColumn("colBytea", "bytea", (column) => {
return column.notNull()
})
.addColumn("colDate", "date", (column) => {
return column.notNull()
})
.addColumn("colTime", "time", (column) => {
return column.notNull()
})
.addColumn("colTimetz", sql`timetz`, (column) => {
return column.notNull()
})
.addColumn("colTimestamp", "timestamp", (column) => {
return column.notNull()
})
.addColumn("colTimestamptz", "timestamptz", (column) => {
return column.notNull()
})
.addColumn("colJson", "json", (column) => {
return column.notNull()
})
.addColumn("colJsonb", "jsonb", (column) => {
return column.notNull()
})
.addColumn("colUuid", "uuid", (column) => {
return column.notNull()
})
.addColumn("colInet", sql`inet`, (column) => {
return column.notNull()
})
.addColumn("colCidr", sql`cidr`, (column) => {
return column.notNull()
})
.addColumn("colMacaddr", sql`macaddr`, (column) => {
return column.notNull()
})
.addColumn("colBit", sql`bit(8)`, (column) => {
return column.notNull()
})
.addColumn("colVarbit", sql`varbit(16)`, (column) => {
return column.notNull()
})
.addColumn("colXml", sql`xml`, (column) => {
return column.notNull()
})
.addColumn("colTsvector", sql`tsvector`, (column) => {
return column.notNull()
})
.addColumn("colTsquery", sql`tsquery`, (column) => {
return column.notNull()
})
.addColumn("colPoint", sql`point`, (column) => {
return column.notNull()
})
.addColumn("colLine", sql`line`, (column) => {
return column.notNull()
})
.addColumn("colLseg", sql`lseg`, (column) => {
return column.notNull()
})
.addColumn("colBox", sql`box`, (column) => {
return column.notNull()
})
.addColumn("colPath", sql`path`, (column) => {
return column.notNull()
})
.addColumn("colPolygon", sql`polygon`, (column) => {
return column.notNull()
})
.addColumn("colOid", sql`oid`, (column) => {
return column.notNull()
})
.addColumn("colTextNullable", "text")
.addColumn("colJsonbDefault", "jsonb", (column) => {
return column.notNull().defaultTo(sql`'{}'::jsonb`)
})
.addColumn("colTimestampDefault", "timestamp", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.addColumn("createdAt", "timestamptz", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.addColumn("updatedAt", "timestamptz")
.execute()
await database.schema
.createTable("Users")
.addColumn("id", "uuid", (column) => {
return column
.notNull()
.primaryKey()
.defaultTo(sql`uuidv7()`)
})
.addColumn("username", "varchar(50)", (column) => {
return column.notNull().unique()
})
.addColumn("email", "text")
.addColumn("role", sql`"UserRole"`, (column) => {
return column.notNull().defaultTo("member")
})
.addColumn("isActive", "boolean", (column) => {
return column.notNull().defaultTo(true)
})
.addColumn("createdAt", "timestamptz", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.execute()
await database.schema
.createTable("Orders")
.addColumn("id", "bigserial", (column) => {
return column.notNull().primaryKey()
})
.addColumn("userId", "uuid", (column) => {
return column.notNull().references("Users.id")
})
.addColumn("status", sql`"OrderStatus"`, (column) => {
return column.notNull().defaultTo("pending")
})
.addColumn("currency", sql`"Currency"`, (column) => {
return column.notNull().defaultTo("EUR")
})
.addColumn("amountCents", "integer", (column) => {
return column.notNull()
})
.addColumn("note", "text")
.addColumn("createdAt", "timestamptz", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.execute()
await sql`comment on column ${sql.ref("Users.email")} is ${sql.lit("Contact email address.")}`.execute(
database,
)
await sql`comment on column ${sql.ref("Orders.amountCents")} is ${sql.lit("Total amount in cents.\n\nExcludes taxes.")}`.execute(
database,
)
await sql`comment on column ${sql.ref("Orders.note")} is ${sql.lit("Free text, may contain */ characters.")}`.execute(
database,
)
}
@@ -0,0 +1,119 @@
// Bun Snapshot v1, https://bun.sh/docs/test/snapshots
exports[`typegen PostgreSQL (Bun.SQL) generate types matching snapshot 1`] = `
{
"enumsCount": 3,
"inlineEnumsCount": 0,
"lines": [
"// This file was automatically generated by \`kysely-typegen\`.",
"// Do not edit this file manually.",
"",
"import type { ColumnType } from "kysely"",
"",
"export type Generated<T> = T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>",
"",
"export type Timestamp = ColumnType<Date, Date | string, Date | string>",
"",
"export type Numeric = ColumnType<string, number | string, number | string>",
"",
"export type Int8 = ColumnType<string, bigint | number | string, bigint | number | string>",
"",
"export type Json = JsonValue",
"",
"export type JsonArray = JsonValue[]",
"",
"export interface JsonObject {",
" [x: string]: JsonValue | undefined",
"}",
"",
"export type JsonPrimitive = boolean | number | string | null",
"",
"export type JsonValue = JsonArray | JsonObject | JsonPrimitive",
"",
"export type Currency = "EUR" | "GBP" | "USD"",
"",
"export type OrderStatus = "cancelled" | "paid" | "pending" | "shipped"",
"",
"export type UserRole = "admin" | "guest" | "member"",
"",
"export interface AllTypes {",
" colBit: string",
" colBool: boolean",
" colBoolDefault: Generated<boolean>",
" colBoolNullable: boolean | null",
" colBox: string",
" colBpchar: string",
" colBytea: Buffer",
" colCidr: string",
" colDate: Timestamp",
" colFloat4: number",
" colFloat8: number",
" colInet: string",
" colInt2: number",
" colInt4: number",
" colInt8: Int8",
" colJson: Json",
" colJsonb: Json",
" colJsonbDefault: Generated<Json>",
" colLine: string",
" colLseg: string",
" colMacaddr: string",
" colMoney: string",
" colNumeric: Numeric",
" colOid: number",
" colPath: string",
" colPoint: unknown",
" colPolygon: string",
" colText: string",
" colTextNullable: string | null",
" colTime: string",
" colTimestamp: Timestamp",
" colTimestampDefault: Generated<Timestamp>",
" colTimestamptz: Timestamp",
" colTimetz: string",
" colTsquery: string",
" colTsvector: string",
" colUuid: string",
" colVarbit: string",
" colVarchar: string",
" colXml: string",
" createdAt: Generated<Timestamp>",
" id: Generated<string>",
" updatedAt: Timestamp | null",
"}",
"",
"export interface Orders {",
" /**",
" * Total amount in cents.",
" *",
" * Excludes taxes.",
" */",
" amountCents: number",
" createdAt: Generated<Timestamp>",
" currency: Generated<Currency>",
" id: Generated<Int8>",
" /** Free text, may contain *\\/ characters. */",
" note: string | null",
" status: Generated<OrderStatus>",
" userId: string",
"}",
"",
"export interface Users {",
" createdAt: Generated<Timestamp>",
" /** Contact email address. */",
" email: string | null",
" id: Generated<string>",
" isActive: Generated<boolean>",
" role: Generated<UserRole>",
" username: string",
"}",
"",
"export interface DB {",
" AllTypes: AllTypes",
" Orders: Orders",
" Users: Users",
"}",
],
"tablesCount": 3,
}
`;
+54
View File
@@ -0,0 +1,54 @@
import type { StartedPostgreSqlContainer } from "@testcontainers/postgresql"
import { PostgreSqlContainer } from "@testcontainers/postgresql"
import Bun from "bun"
import { afterAll, beforeAll, describe, expect, it } from "bun:test"
import { Kysely } from "kysely"
import { PostgresJSDialect } from "kysely-postgres-js"
import { KyselyTypegenPostgresDialect } from "../../postgres.ts"
import { createSchema, POSTGRES_IMAGE } from "../_postgres.ts"
const CONTAINER_START_TIMEOUT_MILLISECONDS = 120_000
describe("typegen PostgreSQL (Bun.SQL)", () => {
let container: StartedPostgreSqlContainer
let database: Kysely<any>
beforeAll(async () => {
container = await new PostgreSqlContainer(POSTGRES_IMAGE).start()
database = new Kysely<any>({
dialect: new PostgresJSDialect({
postgres: new Bun.SQL({
adapter: "postgres",
database: container.getDatabase(),
hostname: container.getHost(),
port: container.getPort(),
username: container.getUsername(),
password: container.getPassword(),
}),
}),
})
await createSchema(database)
}, CONTAINER_START_TIMEOUT_MILLISECONDS)
afterAll(async () => {
await database.destroy()
await container.stop()
})
it("generate types matching snapshot", async () => {
// Arrange - Given
const databaseTypegen = new KyselyTypegenPostgresDialect({ database })
// Act - When
const result = await databaseTypegen.typegen()
// Assert - Then
expect({
lines: result.lines,
tablesCount: result.tables.length,
enumsCount: result.enums.length,
inlineEnumsCount: result.inlineEnums.size,
}).toMatchSnapshot()
})
})
+36 -199
View File
@@ -1,212 +1,16 @@
import { PGlite } from "@electric-sql/pglite"
import type { StartedPostgreSqlContainer } from "@testcontainers/postgresql"
import { PostgreSqlContainer } from "@testcontainers/postgresql"
import { Kysely, PostgresDialect, sql } from "kysely"
import { Kysely, PGliteDialect, PostgresDialect } from "kysely"
import { PostgresJSDialect } from "kysely-postgres-js"
import { after, before, describe, it } from "node:test"
import pg from "pg"
import postgres from "postgres"
import { KyselyTypegenPostgresDialect } from "../postgres.ts"
import { createSchema, POSTGRES_IMAGE } from "./_postgres.ts"
import "./_setup.ts"
const POSTGRES_IMAGE =
"docker.io/postgres:18.4@sha256:f7ce845ee6873dd84be93c9828fe0d1fab0f9707dc9ac569694657398b290bce"
const createSchema = async (database: Kysely<any>): Promise<void> => {
await database.schema.createType("Currency").asEnum(["EUR", "USD", "GBP"]).execute()
await database.schema.createType("UserRole").asEnum(["admin", "member", "guest"]).execute()
await database.schema
.createType("OrderStatus")
.asEnum(["pending", "paid", "shipped", "cancelled"])
.execute()
await database.schema
.createTable("AllTypes")
.addColumn("id", "uuid", (column) => {
return column
.notNull()
.primaryKey()
.defaultTo(sql`uuidv7()`)
})
.addColumn("colBool", "boolean", (column) => {
return column.notNull()
})
.addColumn("colBoolNullable", "boolean")
.addColumn("colBoolDefault", "boolean", (column) => {
return column.notNull().defaultTo(false)
})
.addColumn("colInt2", "smallint", (column) => {
return column.notNull()
})
.addColumn("colInt4", "integer", (column) => {
return column.notNull()
})
.addColumn("colInt8", "bigint", (column) => {
return column.notNull()
})
.addColumn("colFloat4", "real", (column) => {
return column.notNull()
})
.addColumn("colFloat8", "double precision", (column) => {
return column.notNull()
})
.addColumn("colNumeric", sql`numeric(12, 2)`, (column) => {
return column.notNull()
})
.addColumn("colMoney", sql`money`, (column) => {
return column.notNull()
})
.addColumn("colText", "text", (column) => {
return column.notNull()
})
.addColumn("colVarchar", "varchar(255)", (column) => {
return column.notNull()
})
.addColumn("colBpchar", sql`char(10)`, (column) => {
return column.notNull()
})
.addColumn("colBytea", "bytea", (column) => {
return column.notNull()
})
.addColumn("colDate", "date", (column) => {
return column.notNull()
})
.addColumn("colTime", "time", (column) => {
return column.notNull()
})
.addColumn("colTimetz", sql`timetz`, (column) => {
return column.notNull()
})
.addColumn("colTimestamp", "timestamp", (column) => {
return column.notNull()
})
.addColumn("colTimestamptz", "timestamptz", (column) => {
return column.notNull()
})
.addColumn("colJson", "json", (column) => {
return column.notNull()
})
.addColumn("colJsonb", "jsonb", (column) => {
return column.notNull()
})
.addColumn("colUuid", "uuid", (column) => {
return column.notNull()
})
.addColumn("colInet", sql`inet`, (column) => {
return column.notNull()
})
.addColumn("colCidr", sql`cidr`, (column) => {
return column.notNull()
})
.addColumn("colMacaddr", sql`macaddr`, (column) => {
return column.notNull()
})
.addColumn("colBit", sql`bit(8)`, (column) => {
return column.notNull()
})
.addColumn("colVarbit", sql`varbit(16)`, (column) => {
return column.notNull()
})
.addColumn("colXml", sql`xml`, (column) => {
return column.notNull()
})
.addColumn("colTsvector", sql`tsvector`, (column) => {
return column.notNull()
})
.addColumn("colTsquery", sql`tsquery`, (column) => {
return column.notNull()
})
.addColumn("colPoint", sql`point`, (column) => {
return column.notNull()
})
.addColumn("colLine", sql`line`, (column) => {
return column.notNull()
})
.addColumn("colLseg", sql`lseg`, (column) => {
return column.notNull()
})
.addColumn("colBox", sql`box`, (column) => {
return column.notNull()
})
.addColumn("colPath", sql`path`, (column) => {
return column.notNull()
})
.addColumn("colPolygon", sql`polygon`, (column) => {
return column.notNull()
})
.addColumn("colOid", sql`oid`, (column) => {
return column.notNull()
})
.addColumn("colTextNullable", "text")
.addColumn("colJsonbDefault", "jsonb", (column) => {
return column.notNull().defaultTo(sql`'{}'::jsonb`)
})
.addColumn("colTimestampDefault", "timestamp", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.addColumn("createdAt", "timestamptz", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.addColumn("updatedAt", "timestamptz")
.execute()
await database.schema
.createTable("Users")
.addColumn("id", "uuid", (column) => {
return column
.notNull()
.primaryKey()
.defaultTo(sql`uuidv7()`)
})
.addColumn("username", "varchar(50)", (column) => {
return column.notNull().unique()
})
.addColumn("email", "text")
.addColumn("role", sql`"UserRole"`, (column) => {
return column.notNull().defaultTo("member")
})
.addColumn("isActive", "boolean", (column) => {
return column.notNull().defaultTo(true)
})
.addColumn("createdAt", "timestamptz", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.execute()
await database.schema
.createTable("Orders")
.addColumn("id", "bigserial", (column) => {
return column.notNull().primaryKey()
})
.addColumn("userId", "uuid", (column) => {
return column.notNull().references("Users.id")
})
.addColumn("status", sql`"OrderStatus"`, (column) => {
return column.notNull().defaultTo("pending")
})
.addColumn("currency", sql`"Currency"`, (column) => {
return column.notNull().defaultTo("EUR")
})
.addColumn("amountCents", "integer", (column) => {
return column.notNull()
})
.addColumn("note", "text")
.addColumn("createdAt", "timestamptz", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.execute()
await sql`comment on column ${sql.ref("Users.email")} is ${sql.lit("Contact email address.")}`.execute(
database,
)
await sql`comment on column ${sql.ref("Orders.amountCents")} is ${sql.lit("Total amount in cents.\n\nExcludes taxes.")}`.execute(
database,
)
await sql`comment on column ${sql.ref("Orders.note")} is ${sql.lit("Free text, may contain */ characters.")}`.execute(
database,
)
}
describe("typegen PostgreSQL", () => {
let container: StartedPostgreSqlContainer
let database: Kysely<any>
@@ -276,3 +80,36 @@ describe("typegen PostgreSQL", () => {
})
})
})
describe("typegen PGlite", () => {
let database: Kysely<any>
before(async () => {
database = new Kysely<any>({
dialect: new PGliteDialect({
pglite: new PGlite(),
}),
})
await createSchema(database)
})
after(async () => {
await database.destroy()
})
it("generate types matching snapshot", async (testContext) => {
// Arrange - Given
const databaseTypegen = new KyselyTypegenPostgresDialect({ database })
// Act - When
const result = await databaseTypegen.typegen()
// Assert - Then
testContext.assert.snapshot({
lines: result.lines,
tablesCount: result.tables.length,
enumsCount: result.enums.length,
inlineEnumsCount: result.inlineEnums.size,
})
})
})
+1 -1
View File
@@ -29,6 +29,6 @@
"resolveJsonModule": true,
"lib": ["ESNext"],
"types": ["@types/node"]
"types": ["@types/node", "@types/bun"]
}
}