mirror of
https://github.com/theoludwig/kysely-typegen.git
synced 2026-05-22 16:23:25 +02:00
Compare commits
4 Commits
c025a63c8c
...
v2.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
b3c01aee90
|
|||
|
9df8b6b64a
|
|||
|
e11a4d2722
|
|||
|
e9adb364e0
|
@@ -10,13 +10,13 @@ Thank you [kysely-codegen](https://npmx.dev/package/kysely-codegen) for inspirat
|
||||
|
||||
Why `kysely-typegen` if there is already `kysely-codegen`? Comparison:
|
||||
|
||||
| | `kysely-codegen@0.20.0` | `kysely-typegen` |
|
||||
| ----------------------------- | ---------------------------------------- | ------------------------------------------------------------ |
|
||||
| **Install Size** | 6.8 MB | 5 kB |
|
||||
| **Dependencies** | 35 total | 0 (no runtime dependencies) |
|
||||
| **Type** | CLI | Library/Programmatic Usage |
|
||||
| **Code Size/Maintainability** | Heavy | Less than 200 LOC/Simple and straightforward |
|
||||
| **Database Support** | PostgreSQL, MySQL, SQLite, MSSQL, LibSQL | PostgreSQL (but can **easily be extended to more dialects**) |
|
||||
| | `kysely-codegen@0.20.0` | `kysely-typegen` |
|
||||
| ----------------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------- |
|
||||
| **Install Size** | 6.8 MB | 22.1 kB |
|
||||
| **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**) |
|
||||
|
||||
`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.
|
||||
|
||||
@@ -40,48 +40,98 @@ Peer dependencies:
|
||||
npm install kysely
|
||||
```
|
||||
|
||||
Kysely dialect of your choice, for example: [kysely-postgres-js](https://github.com/kysely-org/kysely-postgres-js)
|
||||
|
||||
```sh
|
||||
npm install kysely-postgres-js postgres
|
||||
```
|
||||
Plus a Kysely dialect driver for your database (see [Setup Kysely database](#setup-kysely-database) below).
|
||||
|
||||
## Usage
|
||||
|
||||
### Setup Kysely database
|
||||
|
||||
Create your Kysely database instance (example with [kysely-postgres-js](https://github.com/kysely-org/kysely-postgres-js)):
|
||||
Create your Kysely database instance and export a `databaseTypegen` for the script in the next section. The rest of the guide is dialect-agnostic: only this file changes per database.
|
||||
|
||||
#### 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`.
|
||||
|
||||
```sh
|
||||
npm install kysely-postgres-js postgres
|
||||
```
|
||||
|
||||
```ts
|
||||
// database.ts
|
||||
import { Kysely } from "kysely"
|
||||
import { PostgresJSDialect } from "kysely-postgres-js"
|
||||
import { KyselyTypegenPostgresDialect } from "kysely-typegen/postgres"
|
||||
import postgres from "postgres"
|
||||
|
||||
import type { DB } from "./codegen.ts"
|
||||
|
||||
export const DATABASE_USER = process.env["DATABASE_USER"] ?? "user"
|
||||
export const DATABASE_PASSWORD = process.env["DATABASE_PASSWORD"] ?? "password"
|
||||
export const DATABASE_NAME = process.env["DATABASE_NAME"] ?? "database"
|
||||
export const DATABASE_HOST = process.env["DATABASE_HOST"] ?? "localhost"
|
||||
export const DATABASE_PORT = Number.parseInt(process.env["DATABASE_PORT"] ?? "5432", 10)
|
||||
|
||||
const dialect = new PostgresJSDialect({
|
||||
postgres: postgres({
|
||||
database: DATABASE_NAME,
|
||||
host: DATABASE_HOST,
|
||||
user: DATABASE_USER,
|
||||
password: DATABASE_PASSWORD,
|
||||
port: DATABASE_PORT,
|
||||
database: process.env["DATABASE_NAME"] ?? "database",
|
||||
host: process.env["DATABASE_HOST"] ?? "localhost",
|
||||
user: 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 })
|
||||
```
|
||||
|
||||
#### MySQL
|
||||
|
||||
```sh
|
||||
npm install mysql2
|
||||
```
|
||||
|
||||
```ts
|
||||
// database.ts
|
||||
import { Kysely, MysqlDialect } from "kysely"
|
||||
import { KyselyTypegenMySQLDialect } from "kysely-typegen/mysql"
|
||||
import { createPool } from "mysql2"
|
||||
|
||||
import type { DB } from "./codegen.ts"
|
||||
|
||||
const dialect = new MysqlDialect({
|
||||
pool: createPool({
|
||||
database: process.env["DATABASE_NAME"] ?? "database",
|
||||
host: process.env["DATABASE_HOST"] ?? "localhost",
|
||||
user: process.env["DATABASE_USER"] ?? "user",
|
||||
password: process.env["DATABASE_PASSWORD"] ?? "password",
|
||||
port: Number.parseInt(process.env["DATABASE_PORT"] ?? "3306", 10),
|
||||
}),
|
||||
})
|
||||
|
||||
export const database = new Kysely<DB>({ dialect })
|
||||
export const databaseTypegen = new KyselyTypegenMySQLDialect({ database })
|
||||
```
|
||||
|
||||
#### SQLite
|
||||
|
||||
```sh
|
||||
npm install better-sqlite3
|
||||
```
|
||||
|
||||
```ts
|
||||
// database.ts
|
||||
import Database from "better-sqlite3"
|
||||
import { Kysely, SqliteDialect } from "kysely"
|
||||
import { KyselyTypegenSQLiteDialect } from "kysely-typegen/sqlite"
|
||||
|
||||
import type { DB } from "./codegen.ts"
|
||||
|
||||
const dialect = new SqliteDialect({
|
||||
database: new Database(process.env["DATABASE_PATH"] ?? "database.sqlite"),
|
||||
})
|
||||
|
||||
export const database = new Kysely<DB>({ dialect })
|
||||
export const databaseTypegen = new KyselyTypegenSQLiteDialect({ database })
|
||||
```
|
||||
|
||||
### Generate the type definitions
|
||||
|
||||
Create a script that uses `kysely-typegen` to introspect your database and write the generated types to a file:
|
||||
Create a script that uses `databaseTypegen` to introspect your database and write the generated types to a file. This script is the same regardless of the underlying database:
|
||||
|
||||
```ts
|
||||
// scripts/typegen.ts
|
||||
@@ -90,11 +140,8 @@ Create a script that uses `kysely-typegen` to introspect your database and write
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
import { KyselyTypegenPostgresDialect } from "kysely-typegen"
|
||||
import { database, databaseTypegen } from "./database.ts"
|
||||
|
||||
import { database } from "./database.ts"
|
||||
|
||||
const databaseTypegen = new KyselyTypegenPostgresDialect({ database })
|
||||
const result = await databaseTypegen.typegen()
|
||||
const codegenContent = result.lines.join("\n")
|
||||
const codegenPath = path.join(process.cwd(), "codegen.ts")
|
||||
@@ -123,45 +170,49 @@ Fully type-safe queries derived from your actual database schema.
|
||||
|
||||
## Extending to other database dialects
|
||||
|
||||
`kysely-typegen` ships with `KyselyTypegenPostgresDialect`, but you can add support for any database by extending the abstract `KyselyTypegenDialect` class.
|
||||
`kysely-typegen` ships with `KyselyTypegenPostgresDialect`, `KyselyTypegenMySQLDialect`, and `KyselyTypegenSQLiteDialect`, but you can add support for any database by extending the abstract `KyselyTypegenDialect` class.
|
||||
|
||||
Only two things are required:
|
||||
Only one thing is required:
|
||||
|
||||
- `scalars`: a `Record<string, string>` mapping the database column types to TypeScript types.
|
||||
- `getEnumsMap()`: a method returning a `Map<string, string[]>` of enum names to their values (return an empty `Map` if your database doesn't support enums).
|
||||
|
||||
```ts
|
||||
import { KyselyTypegenDialect } from "kysely-typegen"
|
||||
import type { Kysely } from "kysely"
|
||||
|
||||
export class KyselyTypegenMySQLDialect extends KyselyTypegenDialect {
|
||||
public database: KyselyTypegenDialect["database"]
|
||||
|
||||
public readonly scalars: Record<string, string> = {
|
||||
bigint: "number",
|
||||
export class KyselyTypegenMSSQLDialect extends KyselyTypegenDialect {
|
||||
public override readonly scalars: Record<string, string> = {
|
||||
bigint: "Int8",
|
||||
bit: "boolean",
|
||||
char: "string",
|
||||
datetime: "Timestamp",
|
||||
decimal: "Numeric",
|
||||
int: "number",
|
||||
json: "Json",
|
||||
nvarchar: "string",
|
||||
smallint: "number",
|
||||
text: "string",
|
||||
tinyint: "number",
|
||||
varbinary: "Buffer",
|
||||
varchar: "string",
|
||||
// ...
|
||||
}
|
||||
|
||||
public constructor(input: { database: Kysely<any> }) {
|
||||
super()
|
||||
this.database = input.database
|
||||
}
|
||||
|
||||
protected async getEnumsMap(): Promise<Map<string, string[]>> {
|
||||
// Query your database's information schema for enum types.
|
||||
// Key: enum name, Value: array of enum values.
|
||||
return new Map()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If your database supports enums, override the optional `introspectEnums()` hook, which returns two maps:
|
||||
|
||||
- `named`: enum name → values (emitted as `export type Name = "a" | "b"`).
|
||||
- `inline`: `${tableName}.${columnName}` → values (emitted inline at the column site, for databases like MySQL where enums are anonymous per-column).
|
||||
|
||||
```ts
|
||||
import type { IntrospectedEnums } from "kysely-typegen"
|
||||
|
||||
protected override async introspectEnums(): Promise<IntrospectedEnums> {
|
||||
// Query your database's information schema...
|
||||
return { named: [], inline: new Map() }
|
||||
}
|
||||
```
|
||||
|
||||
If your database needs to normalize column type spellings before scalar lookup (e.g. strip `VARCHAR(255)` → `VARCHAR`, or uppercase keys), override the optional `normalizeDataType(dataType: string): string` hook.
|
||||
|
||||
The base class handles introspection (via Kysely's `database.introspection.getTables()`), sorting, and the final type generation. Contributions of new dialects are welcome!
|
||||
|
||||
## Contributing
|
||||
|
||||
Generated
+551
@@ -9,13 +9,19 @@
|
||||
"version": "0.0.0-development",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@testcontainers/mysql": "12.0.0",
|
||||
"@testcontainers/postgresql": "12.0.0",
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"@types/node": "25.9.1",
|
||||
"@types/pg": "8.20.0",
|
||||
"better-sqlite3": "12.10.0",
|
||||
"kysely": "0.29.2",
|
||||
"kysely-postgres-js": "3.0.0",
|
||||
"mysql2": "3.22.3",
|
||||
"oxfmt": "0.51.0",
|
||||
"oxlint": "1.66.0",
|
||||
"oxlint-tsgolint": "0.23.0",
|
||||
"pg": "8.21.0",
|
||||
"postgres": "3.4.9",
|
||||
"semantic-release": "25.0.3",
|
||||
"tsdown": "0.22.0",
|
||||
@@ -2253,6 +2259,16 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@testcontainers/mysql": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@testcontainers/mysql/-/mysql-12.0.0.tgz",
|
||||
"integrity": "sha512-ofK3bPWIkqEhwQq5hv31QO3B6AlBGF10wI2QaJDjvDiYr7jQ1WGh1n4S1Eu3xwIWyO8FYSxKjgHgkoaNPPy4Ww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"testcontainers": "^12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@testcontainers/postgresql": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-12.0.0.tgz",
|
||||
@@ -2274,6 +2290,16 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/better-sqlite3": {
|
||||
"version": "7.6.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
|
||||
"integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/docker-modem": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz",
|
||||
@@ -2328,6 +2354,18 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/pg": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz",
|
||||
"integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"pg-protocol": "*",
|
||||
"pg-types": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ssh2": {
|
||||
"version": "1.15.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz",
|
||||
@@ -2674,6 +2712,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-ssl-profiles": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
|
||||
"integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/b4a": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
|
||||
@@ -2831,6 +2879,31 @@
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/better-sqlite3": {
|
||||
"version": "12.10.0",
|
||||
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.10.0.tgz",
|
||||
"integrity": "sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bindings": "^1.5.0",
|
||||
"prebuild-install": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20.x || 22.x || 23.x || 24.x || 25.x || 26.x"
|
||||
}
|
||||
},
|
||||
"node_modules/bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/birpc": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/birpc/-/birpc-4.0.0.tgz",
|
||||
@@ -3623,6 +3696,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/decompress-response": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mimic-response": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/deep-extend": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
@@ -3640,6 +3729,26 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dir-glob": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||
@@ -4101,6 +4210,16 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/expand-template": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
|
||||
"dev": true,
|
||||
"license": "(MIT OR WTFPL)",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-content-type-parse": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz",
|
||||
@@ -4141,6 +4260,13 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
@@ -4249,6 +4375,16 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/generate-function": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
|
||||
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-property": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
@@ -4329,6 +4465,13 @@
|
||||
"traverse": "0.6.8"
|
||||
}
|
||||
},
|
||||
"node_modules/github-from-package": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||
@@ -4471,6 +4614,23 @@
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
|
||||
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
@@ -4647,6 +4807,13 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-property": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
||||
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
|
||||
@@ -4964,6 +5131,22 @@
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/lru.min": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz",
|
||||
"integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"bun": ">=1.0.0",
|
||||
"deno": ">=1.30.0",
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wellwelwel"
|
||||
}
|
||||
},
|
||||
"node_modules/make-asynchronous": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-asynchronous/-/make-asynchronous-1.1.0.tgz",
|
||||
@@ -5093,6 +5276,19 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-response": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.9",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
||||
@@ -5159,6 +5355,29 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mysql2": {
|
||||
"version": "3.22.3",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.22.3.tgz",
|
||||
"integrity": "sha512-uWWxvZSRvRhtBdh2CdcuK83YcOfPdmEeEYB069bAmPnV93QApDGVPuvCQOLjlh7tYHEWdgQPrn6kosDxHBVLkA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"aws-ssl-profiles": "^1.1.2",
|
||||
"denque": "^2.1.0",
|
||||
"generate-function": "^2.3.1",
|
||||
"iconv-lite": "^0.7.2",
|
||||
"long": "^5.3.2",
|
||||
"lru.min": "^1.1.4",
|
||||
"named-placeholders": "^1.1.6",
|
||||
"sql-escaper": "^1.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/mz": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||
@@ -5171,6 +5390,19 @@
|
||||
"thenify-all": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/named-placeholders": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz",
|
||||
"integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lru.min": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.27.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz",
|
||||
@@ -5179,6 +5411,13 @@
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/napi-build-utils": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/neo-async": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||
@@ -5193,6 +5432,19 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-abi": {
|
||||
"version": "3.92.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz",
|
||||
"integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/node-emoji": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz",
|
||||
@@ -7599,6 +7851,113 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.21.0",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz",
|
||||
"integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pg-connection-string": "^2.13.0",
|
||||
"pg-pool": "^3.14.0",
|
||||
"pg-protocol": "^1.14.0",
|
||||
"pg-types": "2.2.0",
|
||||
"pgpass": "1.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-cloudflare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz",
|
||||
"integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.13.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.13.0.tgz",
|
||||
"integrity": "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.14.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz",
|
||||
"integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.14.0.tgz",
|
||||
"integrity": "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"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"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pgpass/node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -7657,6 +8016,122 @@
|
||||
"url": "https://github.com/sponsors/porsager"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-bytea": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
|
||||
"integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prebuild-install": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
|
||||
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
|
||||
"deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.0",
|
||||
"expand-template": "^2.0.3",
|
||||
"github-from-package": "0.0.0",
|
||||
"minimist": "^1.2.3",
|
||||
"mkdirp-classic": "^0.5.3",
|
||||
"napi-build-utils": "^2.0.0",
|
||||
"node-abi": "^3.3.0",
|
||||
"pump": "^3.0.0",
|
||||
"rc": "^1.2.7",
|
||||
"simple-get": "^4.0.0",
|
||||
"tar-fs": "^2.0.0",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
},
|
||||
"bin": {
|
||||
"prebuild-install": "bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/prebuild-install/node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/prebuild-install/node_modules/tar-fs": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
|
||||
"integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/prebuild-install/node_modules/tar-stream": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pretty-ms": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz",
|
||||
@@ -8290,6 +8765,53 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-concat": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/simple-get": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
|
||||
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decompress-response": "^6.0.0",
|
||||
"once": "^1.3.1",
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/skin-tone": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz",
|
||||
@@ -8373,6 +8895,22 @@
|
||||
"through2": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sql-escaper": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.3.3.tgz",
|
||||
"integrity": "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"bun": ">=1.0.0",
|
||||
"deno": ">=2.0.0",
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/mysqljs/sql-escaper?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ssh-remote-port-forward": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz",
|
||||
@@ -9013,6 +9551,19 @@
|
||||
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
"types": "./dist/index.d.mts",
|
||||
"exports": {
|
||||
".": "./dist/index.mjs",
|
||||
"./mysql": "./dist/mysql.mjs",
|
||||
"./postgres": "./dist/postgres.mjs",
|
||||
"./sqlite": "./dist/sqlite.mjs",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"publishConfig": {
|
||||
@@ -38,13 +41,19 @@
|
||||
"release": "semantic-release"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testcontainers/mysql": "12.0.0",
|
||||
"@testcontainers/postgresql": "12.0.0",
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"@types/node": "25.9.1",
|
||||
"@types/pg": "8.20.0",
|
||||
"better-sqlite3": "12.10.0",
|
||||
"kysely": "0.29.2",
|
||||
"kysely-postgres-js": "3.0.0",
|
||||
"mysql2": "3.22.3",
|
||||
"oxfmt": "0.51.0",
|
||||
"oxlint": "1.66.0",
|
||||
"oxlint-tsgolint": "0.23.0",
|
||||
"pg": "8.21.0",
|
||||
"postgres": "3.4.9",
|
||||
"semantic-release": "25.0.3",
|
||||
"tsdown": "0.22.0",
|
||||
|
||||
+32
-48
@@ -1,4 +1,4 @@
|
||||
exports[`typegen > generate types matching snapshot 1`] = `
|
||||
exports[`typegen MySQL > generate types matching snapshot 1`] = `
|
||||
{
|
||||
"lines": [
|
||||
"// This file was automatically generated by \`kysely-typegen\`.",
|
||||
@@ -26,74 +26,57 @@ exports[`typegen > generate types matching snapshot 1`] = `
|
||||
"",
|
||||
"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 {",
|
||||
" colBigint: Int8",
|
||||
" colBinary: Buffer",
|
||||
" colBit: string",
|
||||
" colBool: boolean",
|
||||
" colBoolDefault: Generated<boolean>",
|
||||
" colBoolNullable: boolean | null",
|
||||
" colBox: string",
|
||||
" colBpchar: string",
|
||||
" colBytea: Buffer",
|
||||
" colCidr: string",
|
||||
" colBlob: Buffer",
|
||||
" colBool: number",
|
||||
" colChar: string",
|
||||
" colDate: Timestamp",
|
||||
" colFloat4: number",
|
||||
" colFloat8: number",
|
||||
" colInet: string",
|
||||
" colInt2: number",
|
||||
" colInt4: number",
|
||||
" colInt8: Int8",
|
||||
" colDatetime: Timestamp",
|
||||
" colDecimal: Numeric",
|
||||
" colDouble: number",
|
||||
" colFloat: number",
|
||||
" colInt: number",
|
||||
" colJson: Json",
|
||||
" colJsonb: Json",
|
||||
" colJsonbDefault: Generated<Json>",
|
||||
" colLine: string",
|
||||
" colLseg: string",
|
||||
" colMacaddr: string",
|
||||
" colMoney: string",
|
||||
" colLongblob: Buffer",
|
||||
" colLongtext: string",
|
||||
" colMediumblob: Buffer",
|
||||
" colMediumint: number",
|
||||
" colMediumtext: string",
|
||||
" colNumeric: Numeric",
|
||||
" colOid: number",
|
||||
" colPath: string",
|
||||
" colPoint: unknown",
|
||||
" colPolygon: string",
|
||||
" colSmallint: number",
|
||||
" colText: string",
|
||||
" colTextNullable: string | null",
|
||||
" colTime: string",
|
||||
" colTimestamp: Timestamp",
|
||||
" colTimestampDefault: Generated<Timestamp>",
|
||||
" colTimestamptz: Timestamp",
|
||||
" colTimetz: string",
|
||||
" colTsquery: string",
|
||||
" colTsvector: string",
|
||||
" colUuid: string",
|
||||
" colVarbit: string",
|
||||
" colTimestamp: Generated<Timestamp>",
|
||||
" colTinyblob: Buffer",
|
||||
" colTinyint: number",
|
||||
" colTinytext: string",
|
||||
" colVarbinary: Buffer",
|
||||
" colVarchar: string",
|
||||
" colXml: string",
|
||||
" colYear: number",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" id: Generated<string>",
|
||||
" updatedAt: Timestamp | null",
|
||||
" id: Generated<number>",
|
||||
"}",
|
||||
"",
|
||||
"export interface Orders {",
|
||||
" amountCents: number",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" currency: Generated<Currency>",
|
||||
" currency: Generated<\\"EUR\\" | \\"GBP\\" | \\"USD\\">",
|
||||
" id: Generated<Int8>",
|
||||
" note: string | null",
|
||||
" status: Generated<OrderStatus>",
|
||||
" userId: string",
|
||||
" status: Generated<\\"cancelled\\" | \\"paid\\" | \\"pending\\" | \\"shipped\\">",
|
||||
" userId: number",
|
||||
"}",
|
||||
"",
|
||||
"export interface Users {",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" email: string | null",
|
||||
" id: Generated<string>",
|
||||
" isActive: Generated<boolean>",
|
||||
" role: Generated<UserRole>",
|
||||
" id: Generated<number>",
|
||||
" isActive: Generated<number>",
|
||||
" role: Generated<\\"admin\\" | \\"guest\\" | \\"member\\">",
|
||||
" username: string",
|
||||
"}",
|
||||
"",
|
||||
@@ -104,6 +87,7 @@ exports[`typegen > generate types matching snapshot 1`] = `
|
||||
"}"
|
||||
],
|
||||
"tablesCount": 3,
|
||||
"enumsCount": 3
|
||||
"enumsCount": 0,
|
||||
"inlineEnumsCount": 3
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,221 @@
|
||||
exports[`typegen PostgreSQL > generate types matching snapshot (kysely-postgres-js) 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 {",
|
||||
" amountCents: number",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" currency: Generated<Currency>",
|
||||
" id: Generated<Int8>",
|
||||
" note: string | null",
|
||||
" status: Generated<OrderStatus>",
|
||||
" userId: string",
|
||||
"}",
|
||||
"",
|
||||
"export interface Users {",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" 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 (pg) 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 {",
|
||||
" amountCents: number",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" currency: Generated<Currency>",
|
||||
" id: Generated<Int8>",
|
||||
" note: string | null",
|
||||
" status: Generated<OrderStatus>",
|
||||
" userId: string",
|
||||
"}",
|
||||
"",
|
||||
"export interface Users {",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" 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
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,69 @@
|
||||
exports[`typegen SQLite > 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 interface AllTypes {",
|
||||
" colBlob: Buffer",
|
||||
" colBoolean: Generated<number>",
|
||||
" colDatetime: string",
|
||||
" colInteger: number",
|
||||
" colJson: Json",
|
||||
" colNumeric: Numeric",
|
||||
" colReal: number",
|
||||
" colText: string",
|
||||
" colTextNullable: string | null",
|
||||
" colVarchar: string",
|
||||
" id: Generated<number>",
|
||||
"}",
|
||||
"",
|
||||
"export interface Orders {",
|
||||
" amountCents: number",
|
||||
" createdAt: Generated<string>",
|
||||
" id: Generated<number>",
|
||||
" note: string | null",
|
||||
" userId: number",
|
||||
"}",
|
||||
"",
|
||||
"export interface Users {",
|
||||
" createdAt: Generated<string>",
|
||||
" email: string | null",
|
||||
" id: Generated<number>",
|
||||
" isActive: Generated<number>",
|
||||
" username: string",
|
||||
"}",
|
||||
"",
|
||||
"export interface DB {",
|
||||
" AllTypes: AllTypes",
|
||||
" Orders: Orders",
|
||||
" Users: Users",
|
||||
"}"
|
||||
],
|
||||
"tablesCount": 3,
|
||||
"enumsCount": 0,
|
||||
"inlineEnumsCount": 0
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import path from "node:path"
|
||||
import { snapshot } from "node:test"
|
||||
|
||||
snapshot.setResolveSnapshotPath((testFilePath) => {
|
||||
if (testFilePath == null) {
|
||||
throw new Error('"testFilePath" is null.')
|
||||
}
|
||||
const dir = path.dirname(testFilePath)
|
||||
const base = path.basename(testFilePath)
|
||||
return path.join(dir, "__snapshots__", `${base}.snapshot`)
|
||||
})
|
||||
@@ -0,0 +1,196 @@
|
||||
import type { StartedMySqlContainer } from "@testcontainers/mysql"
|
||||
import { MySqlContainer } from "@testcontainers/mysql"
|
||||
import { Kysely, MysqlDialect, sql } from "kysely"
|
||||
import { createPool } from "mysql2"
|
||||
import { after, before, describe, it } from "node:test"
|
||||
|
||||
import { KyselyTypegenMySQLDialect } from "../mysql.ts"
|
||||
import "./_setup.ts"
|
||||
|
||||
const MYSQL_IMAGE =
|
||||
"docker.io/mysql:8.4@sha256:c36050afdca850f23cef85703f84c7531a5ae155a11b5ee1c60acb09937c4084"
|
||||
|
||||
const createSchema = async (database: Kysely<any>): Promise<void> => {
|
||||
await database.schema
|
||||
.createTable("AllTypes")
|
||||
.addColumn("id", "integer", (column) => {
|
||||
return column.notNull().autoIncrement().primaryKey()
|
||||
})
|
||||
.addColumn("colBigint", "bigint", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colSmallint", "smallint", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colMediumint", sql`mediumint`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTinyint", sql`tinyint`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colInt", "integer", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colYear", sql`year`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colFloat", sql`float`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colDouble", "double precision", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colDecimal", sql`decimal(12, 2)`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colNumeric", sql`numeric(10, 4)`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colBool", "boolean", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colChar", sql`char(10)`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colVarchar", "varchar(255)", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colText", "text", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTinytext", sql`tinytext`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colMediumtext", sql`mediumtext`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colLongtext", sql`longtext`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTime", "time", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colBit", sql`bit(8)`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colBinary", sql`binary(16)`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colVarbinary", sql`varbinary(64)`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colBlob", sql`blob`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTinyblob", sql`tinyblob`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colMediumblob", sql`mediumblob`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colLongblob", sql`longblob`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colDate", "date", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colDatetime", "datetime", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTimestamp", "timestamp", (column) => {
|
||||
return column.notNull().defaultTo(sql`current_timestamp`)
|
||||
})
|
||||
.addColumn("colJson", "json", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTextNullable", "text")
|
||||
.addColumn("createdAt", "timestamp", (column) => {
|
||||
return column.notNull().defaultTo(sql`current_timestamp`)
|
||||
})
|
||||
.execute()
|
||||
|
||||
await database.schema
|
||||
.createTable("Users")
|
||||
.addColumn("id", "integer", (column) => {
|
||||
return column.notNull().autoIncrement().primaryKey()
|
||||
})
|
||||
.addColumn("username", "varchar(50)", (column) => {
|
||||
return column.notNull().unique()
|
||||
})
|
||||
.addColumn("email", "text")
|
||||
.addColumn("role", sql`enum('admin','member','guest')`, (column) => {
|
||||
return column.notNull().defaultTo("member")
|
||||
})
|
||||
.addColumn("isActive", sql`tinyint(1)`, (column) => {
|
||||
return column.notNull().defaultTo(1)
|
||||
})
|
||||
.addColumn("createdAt", "timestamp", (column) => {
|
||||
return column.notNull().defaultTo(sql`current_timestamp`)
|
||||
})
|
||||
.execute()
|
||||
|
||||
await database.schema
|
||||
.createTable("Orders")
|
||||
.addColumn("id", "bigint", (column) => {
|
||||
return column.notNull().autoIncrement().primaryKey()
|
||||
})
|
||||
.addColumn("userId", "integer", (column) => {
|
||||
return column.notNull().references("Users.id")
|
||||
})
|
||||
.addColumn("status", sql`enum('pending','paid','shipped','cancelled')`, (column) => {
|
||||
return column.notNull().defaultTo("pending")
|
||||
})
|
||||
.addColumn("currency", sql`enum('EUR','USD','GBP')`, (column) => {
|
||||
return column.notNull().defaultTo("EUR")
|
||||
})
|
||||
.addColumn("amountCents", "integer", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("note", "text")
|
||||
.addColumn("createdAt", "timestamp", (column) => {
|
||||
return column.notNull().defaultTo(sql`current_timestamp`)
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
describe("typegen MySQL", () => {
|
||||
let container: StartedMySqlContainer
|
||||
let database: Kysely<any>
|
||||
|
||||
before(async () => {
|
||||
container = await new MySqlContainer(MYSQL_IMAGE).start()
|
||||
database = new Kysely<any>({
|
||||
dialect: new MysqlDialect({
|
||||
pool: createPool({
|
||||
host: container.getHost(),
|
||||
port: container.getPort(),
|
||||
user: container.getUsername(),
|
||||
password: container.getUserPassword(),
|
||||
database: container.getDatabase(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
await createSchema(database)
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await database.destroy()
|
||||
await container.stop()
|
||||
})
|
||||
|
||||
it("generate types matching snapshot", async (testContext) => {
|
||||
// Arrange - Given
|
||||
const databaseTypegen = new KyselyTypegenMySQLDialect({ 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,20 +1,13 @@
|
||||
import type { StartedPostgreSqlContainer } from "@testcontainers/postgresql"
|
||||
import { PostgreSqlContainer } from "@testcontainers/postgresql"
|
||||
import { Kysely, sql } from "kysely"
|
||||
import { Kysely, PostgresDialect, sql } from "kysely"
|
||||
import { PostgresJSDialect } from "kysely-postgres-js"
|
||||
import path from "node:path"
|
||||
import { after, before, describe, it, snapshot } from "node:test"
|
||||
import { after, before, describe, it } from "node:test"
|
||||
import pg from "pg"
|
||||
import postgres from "postgres"
|
||||
import { KyselyTypegenPostgresDialect } from "../index.ts"
|
||||
|
||||
snapshot.setResolveSnapshotPath((testFilePath) => {
|
||||
if (testFilePath == null) {
|
||||
throw new Error('"testFilePath" is null.')
|
||||
}
|
||||
const dir = path.dirname(testFilePath)
|
||||
const base = path.basename(testFilePath)
|
||||
return path.join(dir, "__snapshots__", `${base}.snapshot`)
|
||||
})
|
||||
import { KyselyTypegenPostgresDialect } from "../postgres.ts"
|
||||
import "./_setup.ts"
|
||||
|
||||
const POSTGRES_IMAGE =
|
||||
"docker.io/postgres:18.4@sha256:f7ce845ee6873dd84be93c9828fe0d1fab0f9707dc9ac569694657398b290bce"
|
||||
@@ -204,7 +197,7 @@ const createSchema = async (database: Kysely<any>): Promise<void> => {
|
||||
.execute()
|
||||
}
|
||||
|
||||
describe("typegen", () => {
|
||||
describe("typegen PostgreSQL", () => {
|
||||
let container: StartedPostgreSqlContainer
|
||||
let database: Kysely<any>
|
||||
|
||||
@@ -229,7 +222,7 @@ describe("typegen", () => {
|
||||
await container.stop()
|
||||
})
|
||||
|
||||
it("generate types matching snapshot", async (testContext) => {
|
||||
it("generate types matching snapshot (kysely-postgres-js)", async (testContext) => {
|
||||
// Arrange - Given
|
||||
const databaseTypegen = new KyselyTypegenPostgresDialect({ database })
|
||||
|
||||
@@ -241,6 +234,35 @@ describe("typegen", () => {
|
||||
lines: result.lines,
|
||||
tablesCount: result.tables.length,
|
||||
enumsCount: result.enums.length,
|
||||
inlineEnumsCount: result.inlineEnums.size,
|
||||
})
|
||||
})
|
||||
|
||||
it("generate types matching snapshot (pg)", async (testContext) => {
|
||||
// Arrange - Given
|
||||
const databasePg = new Kysely<any>({
|
||||
dialect: new PostgresDialect({
|
||||
pool: new pg.Pool({
|
||||
database: container.getDatabase(),
|
||||
host: container.getHost(),
|
||||
port: container.getPort(),
|
||||
user: container.getUsername(),
|
||||
password: container.getPassword(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
const databaseTypegen = new KyselyTypegenPostgresDialect({ database: databasePg })
|
||||
|
||||
// Act - When
|
||||
const result = await databaseTypegen.typegen()
|
||||
await databasePg.destroy()
|
||||
|
||||
// Assert - Then
|
||||
testContext.assert.snapshot({
|
||||
lines: result.lines,
|
||||
tablesCount: result.tables.length,
|
||||
enumsCount: result.enums.length,
|
||||
inlineEnumsCount: result.inlineEnums.size,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,110 @@
|
||||
import Database from "better-sqlite3"
|
||||
import { Kysely, sql, SqliteDialect } from "kysely"
|
||||
import { after, before, describe, it } from "node:test"
|
||||
|
||||
import { KyselyTypegenSQLiteDialect } from "../sqlite.ts"
|
||||
import "./_setup.ts"
|
||||
|
||||
const createSchema = async (database: Kysely<any>): Promise<void> => {
|
||||
await database.schema
|
||||
.createTable("AllTypes")
|
||||
.addColumn("id", "integer", (column) => {
|
||||
return column.notNull().primaryKey()
|
||||
})
|
||||
.addColumn("colInteger", "integer", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colReal", "real", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colText", "text", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colBlob", "blob", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colNumeric", sql`NUMERIC`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colBoolean", sql`BOOLEAN`, (column) => {
|
||||
return column.notNull().defaultTo(0)
|
||||
})
|
||||
.addColumn("colDatetime", sql`DATETIME`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colVarchar", sql`VARCHAR(255)`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colJson", sql`JSON`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTextNullable", "text")
|
||||
.execute()
|
||||
|
||||
await database.schema
|
||||
.createTable("Users")
|
||||
.addColumn("id", "integer", (column) => {
|
||||
return column.notNull().primaryKey()
|
||||
})
|
||||
.addColumn("username", sql`VARCHAR(50)`, (column) => {
|
||||
return column.notNull().unique()
|
||||
})
|
||||
.addColumn("email", "text")
|
||||
.addColumn("isActive", sql`BOOLEAN`, (column) => {
|
||||
return column.notNull().defaultTo(1)
|
||||
})
|
||||
.addColumn("createdAt", sql`DATETIME`, (column) => {
|
||||
return column.notNull().defaultTo(sql`CURRENT_TIMESTAMP`)
|
||||
})
|
||||
.execute()
|
||||
|
||||
await database.schema
|
||||
.createTable("Orders")
|
||||
.addColumn("id", "integer", (column) => {
|
||||
return column.notNull().primaryKey()
|
||||
})
|
||||
.addColumn("userId", "integer", (column) => {
|
||||
return column.notNull().references("Users.id")
|
||||
})
|
||||
.addColumn("amountCents", "integer", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("note", "text")
|
||||
.addColumn("createdAt", sql`DATETIME`, (column) => {
|
||||
return column.notNull().defaultTo(sql`CURRENT_TIMESTAMP`)
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
describe("typegen SQLite", () => {
|
||||
let database: Kysely<any>
|
||||
|
||||
before(async () => {
|
||||
database = new Kysely<any>({
|
||||
dialect: new SqliteDialect({
|
||||
database: new Database(":memory:"),
|
||||
}),
|
||||
})
|
||||
await createSchema(database)
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await database.destroy()
|
||||
})
|
||||
|
||||
it("generate types matching snapshot", async (testContext) => {
|
||||
// Arrange - Given
|
||||
const databaseTypegen = new KyselyTypegenSQLiteDialect({ 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,
|
||||
})
|
||||
})
|
||||
})
|
||||
+99
-125
@@ -1,37 +1,95 @@
|
||||
import type { Kysely, TableMetadata as KyselyTableMetadata } from "kysely"
|
||||
|
||||
export type TableMetadata = KyselyTableMetadata
|
||||
export type ColumnMetadata = TableMetadata["columns"][number]
|
||||
export interface EnumMetadata {
|
||||
name: string
|
||||
values: string[]
|
||||
}
|
||||
|
||||
export interface IntrospectedEnums {
|
||||
named: EnumMetadata[]
|
||||
inline: Map<string, string[]>
|
||||
}
|
||||
|
||||
const PREAMBLE_LINES: string[] = [
|
||||
`// 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 abstract class KyselyTypegenDialect {
|
||||
public abstract readonly database: Kysely<any>
|
||||
public readonly database: Kysely<any>
|
||||
public abstract readonly scalars: Record<string, string>
|
||||
|
||||
public constructor(input: { database: Kysely<any> }) {
|
||||
this.database = input.database
|
||||
}
|
||||
|
||||
public async getTables(): Promise<TableMetadata[]> {
|
||||
const tables = await this.database.introspection.getTables()
|
||||
return tables.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name)
|
||||
})
|
||||
}
|
||||
public getTablesTypegen(tables: TableMetadata[], enums: EnumMetadata[]): string[] {
|
||||
const enumNames = new Set(
|
||||
enums.map((enumMetadata) => {
|
||||
return enumMetadata.name
|
||||
}),
|
||||
)
|
||||
|
||||
protected normalizeDataType(dataType: string): string {
|
||||
return dataType
|
||||
}
|
||||
|
||||
protected resolveColumnType(
|
||||
table: TableMetadata,
|
||||
column: ColumnMetadata,
|
||||
scalars: Record<string, string>,
|
||||
inlineEnums: Map<string, string>,
|
||||
): string {
|
||||
const inlineUnion = inlineEnums.get(`${table.name}.${column.name}`)
|
||||
if (inlineUnion != null) {
|
||||
return inlineUnion
|
||||
}
|
||||
const dataType = this.normalizeDataType(column.dataType)
|
||||
return scalars[dataType] ?? "unknown"
|
||||
}
|
||||
|
||||
public getTablesTypegen(
|
||||
tables: TableMetadata[],
|
||||
enums: EnumMetadata[],
|
||||
inlineEnums: Map<string, string>,
|
||||
): string[] {
|
||||
const scalars: Record<string, string> = { ...this.scalars }
|
||||
for (const enumMetadata of enums) {
|
||||
scalars[enumMetadata.name] = enumMetadata.name
|
||||
}
|
||||
const result: string[] = []
|
||||
for (const table of tables) {
|
||||
const interfaceName = table.name
|
||||
result.push(`export interface ${interfaceName} {`)
|
||||
result.push(`export interface ${table.name} {`)
|
||||
const columns = [...table.columns].sort((a, b) => {
|
||||
return a.name.localeCompare(b.name)
|
||||
})
|
||||
for (const column of columns) {
|
||||
const isEnum = enumNames.has(column.dataType)
|
||||
const baseType = isEnum ? column.dataType : (this.scalars[column.dataType] ?? "unknown")
|
||||
const baseType = this.resolveColumnType(table, column, scalars, inlineEnums)
|
||||
let columnType = column.isNullable ? `${baseType} | null` : baseType
|
||||
if (column.hasDefaultValue || column.isAutoIncrementing) {
|
||||
columnType = `Generated<${columnType}>`
|
||||
@@ -43,30 +101,38 @@ export abstract class KyselyTypegenDialect {
|
||||
return result
|
||||
}
|
||||
|
||||
protected abstract getEnumsMap(): Promise<Map<string, string[]>>
|
||||
public async getEnums(): Promise<EnumMetadata[]> {
|
||||
const enumsMap = await this.getEnumsMap()
|
||||
const enums: EnumMetadata[] = []
|
||||
for (const [name, values] of enumsMap) {
|
||||
enums.push({ name, values })
|
||||
}
|
||||
return enums.sort((a, b) => {
|
||||
protected async introspectEnums(): Promise<IntrospectedEnums> {
|
||||
return { named: [], inline: new Map() }
|
||||
}
|
||||
|
||||
public async getEnums(): Promise<{ enums: EnumMetadata[]; inlineEnums: Map<string, string> }> {
|
||||
const { named, inline } = await this.introspectEnums()
|
||||
const enums = [...named].sort((a, b) => {
|
||||
return a.name.localeCompare(b.name)
|
||||
})
|
||||
const inlineEnums = new Map<string, string>()
|
||||
for (const [key, values] of inline) {
|
||||
inlineEnums.set(key, this.formatEnumUnion(values))
|
||||
}
|
||||
return { enums, inlineEnums }
|
||||
}
|
||||
|
||||
protected formatEnumUnion(values: string[]): string {
|
||||
return [...values]
|
||||
.sort((a, b) => {
|
||||
return a.localeCompare(b)
|
||||
})
|
||||
.map((value) => {
|
||||
return `"${value}"`
|
||||
})
|
||||
.join(" | ")
|
||||
}
|
||||
|
||||
public getEnumsTypegen(enums: EnumMetadata[]): string[] {
|
||||
const result: string[] = []
|
||||
for (const enumMetadata of enums) {
|
||||
const enumName = enumMetadata.name
|
||||
result.push(
|
||||
`export type ${enumName} = ${enumMetadata.values
|
||||
.sort((a, b) => {
|
||||
return a.localeCompare(b)
|
||||
})
|
||||
.map((value) => {
|
||||
return `"${value}"`
|
||||
})
|
||||
.join(" | ")}`,
|
||||
`export type ${enumMetadata.name} = ${this.formatEnumUnion(enumMetadata.values)}`,
|
||||
"",
|
||||
)
|
||||
}
|
||||
@@ -76,110 +142,18 @@ export abstract class KyselyTypegenDialect {
|
||||
public async typegen(): Promise<{
|
||||
lines: string[]
|
||||
enums: EnumMetadata[]
|
||||
inlineEnums: Map<string, string>
|
||||
tables: TableMetadata[]
|
||||
}> {
|
||||
const [tables, enums] = await Promise.all([this.getTables(), this.getEnums()])
|
||||
const lines: string[] = [
|
||||
`// 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",
|
||||
"",
|
||||
]
|
||||
const [tables, { enums, inlineEnums }] = await Promise.all([this.getTables(), this.getEnums()])
|
||||
const lines: string[] = [...PREAMBLE_LINES]
|
||||
lines.push(...this.getEnumsTypegen(enums))
|
||||
lines.push(...this.getTablesTypegen(tables, enums))
|
||||
lines.push(...this.getTablesTypegen(tables, enums, inlineEnums))
|
||||
lines.push("export interface DB {")
|
||||
for (const table of tables) {
|
||||
lines.push(` ${table.name}: ${table.name}`)
|
||||
}
|
||||
lines.push("}")
|
||||
return { lines, enums, tables }
|
||||
}
|
||||
}
|
||||
export class KyselyTypegenPostgresDialect extends KyselyTypegenDialect {
|
||||
public database: KyselyTypegenDialect["database"]
|
||||
|
||||
// These types have been found through experimentation in Adminer and in the 'pg' source code.
|
||||
public readonly scalars: Record<string, string> = {
|
||||
bit: "string",
|
||||
bool: "boolean",
|
||||
box: "string",
|
||||
bpchar: "string",
|
||||
bytea: "Buffer",
|
||||
cidr: "string",
|
||||
date: "Timestamp",
|
||||
float4: "number",
|
||||
float8: "number",
|
||||
inet: "string",
|
||||
int2: "number",
|
||||
int4: "number",
|
||||
int8: "Int8",
|
||||
json: "Json",
|
||||
jsonb: "Json",
|
||||
line: "string",
|
||||
lseg: "string",
|
||||
macaddr: "string",
|
||||
money: "string",
|
||||
numeric: "Numeric",
|
||||
oid: "number",
|
||||
path: "string",
|
||||
polygon: "string",
|
||||
text: "string",
|
||||
time: "string",
|
||||
timestamp: "Timestamp",
|
||||
timestamptz: "Timestamp",
|
||||
timetz: "string",
|
||||
tsquery: "string",
|
||||
tsvector: "string",
|
||||
uuid: "string",
|
||||
varbit: "string",
|
||||
varchar: "string",
|
||||
xml: "string",
|
||||
}
|
||||
|
||||
public constructor(input: { database: KyselyTypegenDialect["database"] }) {
|
||||
super()
|
||||
this.database = input.database
|
||||
}
|
||||
|
||||
protected async getEnumsMap(): Promise<Map<string, string[]>> {
|
||||
const rows = await this.database
|
||||
.withoutPlugins()
|
||||
.selectFrom("pg_type as type")
|
||||
.innerJoin("pg_enum as enum", "type.oid", "enum.enumtypid")
|
||||
.innerJoin("pg_catalog.pg_namespace as namespace", "namespace.oid", "type.typnamespace")
|
||||
.select(["type.typname as enumName", "enum.enumlabel as enumValue"])
|
||||
.execute()
|
||||
const enums = new Map<string, string[]>()
|
||||
for (const row of rows) {
|
||||
const data = row as { enumName: string; enumValue: string }
|
||||
const existing = enums.get(data.enumName)
|
||||
if (existing == null) {
|
||||
enums.set(data.enumName, [data.enumValue])
|
||||
} else {
|
||||
existing.push(data.enumValue)
|
||||
}
|
||||
}
|
||||
return enums
|
||||
return { lines, enums, inlineEnums, tables }
|
||||
}
|
||||
}
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
import { sql } from "kysely"
|
||||
|
||||
import type { IntrospectedEnums } from "./index.ts"
|
||||
import { KyselyTypegenDialect } from "./index.ts"
|
||||
|
||||
export class KyselyTypegenMySQLDialect extends KyselyTypegenDialect {
|
||||
// Keys are lowercase values returned by information_schema.COLUMNS.DATA_TYPE,
|
||||
// which is what Kysely's MysqlIntrospector forwards as column.dataType.
|
||||
public override readonly scalars: Record<string, string> = {
|
||||
bigint: "Int8",
|
||||
binary: "Buffer",
|
||||
bit: "string",
|
||||
blob: "Buffer",
|
||||
bool: "boolean",
|
||||
boolean: "boolean",
|
||||
char: "string",
|
||||
date: "Timestamp",
|
||||
datetime: "Timestamp",
|
||||
decimal: "Numeric",
|
||||
double: "number",
|
||||
enum: "string",
|
||||
float: "number",
|
||||
int: "number",
|
||||
json: "Json",
|
||||
longblob: "Buffer",
|
||||
longtext: "string",
|
||||
mediumblob: "Buffer",
|
||||
mediumint: "number",
|
||||
mediumtext: "string",
|
||||
numeric: "Numeric",
|
||||
set: "string",
|
||||
smallint: "number",
|
||||
text: "string",
|
||||
time: "string",
|
||||
timestamp: "Timestamp",
|
||||
tinyblob: "Buffer",
|
||||
tinyint: "number",
|
||||
tinytext: "string",
|
||||
varbinary: "Buffer",
|
||||
varchar: "string",
|
||||
year: "number",
|
||||
}
|
||||
|
||||
protected override async introspectEnums(): Promise<IntrospectedEnums> {
|
||||
const rows = await this.database
|
||||
.withoutPlugins()
|
||||
.selectFrom("information_schema.columns as columns")
|
||||
.select([
|
||||
"columns.TABLE_NAME as tableName",
|
||||
"columns.COLUMN_NAME as columnName",
|
||||
"columns.COLUMN_TYPE as columnType",
|
||||
])
|
||||
.where("columns.TABLE_SCHEMA", "=", sql`database()`)
|
||||
.where("columns.DATA_TYPE", "in", ["enum", "set"])
|
||||
.execute()
|
||||
|
||||
const inline = new Map<string, string[]>()
|
||||
for (const row of rows) {
|
||||
const data = row as { tableName: string; columnName: string; columnType: string }
|
||||
inline.set(`${data.tableName}.${data.columnName}`, parseMysqlEnumColumnType(data.columnType))
|
||||
}
|
||||
return { named: [], inline }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses MySQL `enum(...)` / `set(...)` column type definitions into the list of declared values. MySQL doubles single quotes inside string literals.
|
||||
*
|
||||
* @example
|
||||
* parseMysqlEnumColumnType("enum('a','b','c''d')") // ["a", "b", "c'd"]
|
||||
*/
|
||||
const parseMysqlEnumColumnType = (columnType: string): string[] => {
|
||||
const open = columnType.indexOf("(")
|
||||
const close = columnType.lastIndexOf(")")
|
||||
if (open === -1 || close === -1 || close <= open) {
|
||||
return []
|
||||
}
|
||||
const inner = columnType.slice(open + 1, close)
|
||||
const values: string[] = []
|
||||
let current = ""
|
||||
let insideString = false
|
||||
for (let index = 0; index < inner.length; index++) {
|
||||
const character = inner[index]
|
||||
if (insideString) {
|
||||
if (character === "'" && inner[index + 1] === "'") {
|
||||
current += "'"
|
||||
index++
|
||||
continue
|
||||
}
|
||||
if (character === "'") {
|
||||
insideString = false
|
||||
values.push(current)
|
||||
current = ""
|
||||
continue
|
||||
}
|
||||
current += character
|
||||
} else if (character === "'") {
|
||||
insideString = true
|
||||
}
|
||||
}
|
||||
return values
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import type { EnumMetadata, IntrospectedEnums } from "./index.ts"
|
||||
import { KyselyTypegenDialect } from "./index.ts"
|
||||
|
||||
export class KyselyTypegenPostgresDialect extends KyselyTypegenDialect {
|
||||
// These types have been found through experimentation in Adminer and in the 'pg' source code.
|
||||
public override readonly scalars: Record<string, string> = {
|
||||
bit: "string",
|
||||
bool: "boolean",
|
||||
box: "string",
|
||||
bpchar: "string",
|
||||
bytea: "Buffer",
|
||||
cidr: "string",
|
||||
date: "Timestamp",
|
||||
float4: "number",
|
||||
float8: "number",
|
||||
inet: "string",
|
||||
int2: "number",
|
||||
int4: "number",
|
||||
int8: "Int8",
|
||||
json: "Json",
|
||||
jsonb: "Json",
|
||||
line: "string",
|
||||
lseg: "string",
|
||||
macaddr: "string",
|
||||
money: "string",
|
||||
numeric: "Numeric",
|
||||
oid: "number",
|
||||
path: "string",
|
||||
polygon: "string",
|
||||
text: "string",
|
||||
time: "string",
|
||||
timestamp: "Timestamp",
|
||||
timestamptz: "Timestamp",
|
||||
timetz: "string",
|
||||
tsquery: "string",
|
||||
tsvector: "string",
|
||||
uuid: "string",
|
||||
varbit: "string",
|
||||
varchar: "string",
|
||||
xml: "string",
|
||||
}
|
||||
|
||||
protected override async introspectEnums(): Promise<IntrospectedEnums> {
|
||||
const rows = (await this.database
|
||||
.withoutPlugins()
|
||||
.selectFrom("pg_type as type")
|
||||
.innerJoin("pg_enum as enum", "type.oid", "enum.enumtypid")
|
||||
.select(["type.typname as name", "enum.enumlabel as value"])
|
||||
.execute()) as Array<{ name: string; value: string }>
|
||||
const grouped = new Map<string, string[]>()
|
||||
for (const { name, value } of rows) {
|
||||
const existing = grouped.get(name)
|
||||
if (existing == null) {
|
||||
grouped.set(name, [value])
|
||||
} else {
|
||||
existing.push(value)
|
||||
}
|
||||
}
|
||||
const named: EnumMetadata[] = []
|
||||
for (const [name, values] of grouped) {
|
||||
named.push({ name, values })
|
||||
}
|
||||
return { named, inline: new Map() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { KyselyTypegenDialect } from "./index.ts"
|
||||
|
||||
export class KyselyTypegenSQLiteDialect extends KyselyTypegenDialect {
|
||||
// SQLite is dynamically typed and pragma_table_info preserves the declared
|
||||
// type spelling verbatim. Lookups are normalized to uppercase, so keys here
|
||||
// must be uppercase. Covers the five storage classes plus popular aliases.
|
||||
public override readonly scalars: Record<string, string> = {
|
||||
BIGINT: "Int8",
|
||||
BLOB: "Buffer",
|
||||
BOOLEAN: "number",
|
||||
CHAR: "string",
|
||||
DATE: "string",
|
||||
DATETIME: "string",
|
||||
DECIMAL: "Numeric",
|
||||
DOUBLE: "number",
|
||||
FLOAT: "number",
|
||||
INTEGER: "number",
|
||||
JSON: "Json",
|
||||
NUMERIC: "Numeric",
|
||||
REAL: "number",
|
||||
TEXT: "string",
|
||||
TIMESTAMP: "string",
|
||||
VARCHAR: "string",
|
||||
}
|
||||
|
||||
protected override normalizeDataType(dataType: string): string {
|
||||
const parenIndex = dataType.indexOf("(")
|
||||
const stripped = parenIndex === -1 ? dataType : dataType.slice(0, parenIndex)
|
||||
return stripped.toUpperCase()
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { defineConfig } from "tsdown"
|
||||
|
||||
export default defineConfig({
|
||||
entry: "./src/index.ts",
|
||||
entry: ["./src/index.ts", "./src/mysql.ts", "./src/postgres.ts", "./src/sqlite.ts"],
|
||||
dts: true,
|
||||
exports: true,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user