diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e73269569..4a0a17600 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -82,17 +82,20 @@ jobs: node-version: 22.x cache: 'npm' - - name: Install dependencies - run: npm ci - - name: Use Deno ${{ matrix.deno-version }} uses: denoland/setup-deno@v2 with: deno-version: ${{ matrix.deno-version }} + - name: Install dependencies + run: npm ci + - name: Run docker compose run: docker compose up -d + - name: Build + run: npm run build + - name: Run deno tests run: npm run test:deno @@ -210,3 +213,30 @@ jobs: - name: Run tests with older TypeScript run: npm run test:typings && npm run test:node:build + + jsdocs: + name: JSDocs + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.x + cache: 'npm' + + - name: Use Deno + uses: denoland/setup-deno@v2 + with: + deno-version: 2.0.x + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Type-check JSDocs code blocks + run: npm run test:jsdocs diff --git a/deno.check.d.ts b/deno.check.d.ts new file mode 100644 index 000000000..97139b6cd --- /dev/null +++ b/deno.check.d.ts @@ -0,0 +1,71 @@ +import type { + ColumnType, + Generated, + GeneratedAlways, + Insertable, + Kysely, + Selectable, + Updateable, +} from './dist/esm' + +interface Database { + person: PersonTable + pet: PetTable + wine: WineTable + wine_stock_change: WineStockChangeTable +} + +interface PersonTable { + id: Generated + address: { city: string } | null + age: number | null + birthdate: ColumnType + created_at: GeneratedAlways + deleted_at: ColumnType + experience: { role: string }[] | null + first_name: string + gender: 'male' | 'female' | 'other' | null + has_pets: Generated<'Y' | 'N'> + last_name: string | null + middle_name: string | null + nicknames: string[] | null + nullable_column: string | null + profile: { + addresses: { city: string }[] + website: { url: string } + } | null + updated_at: ColumnType +} + +interface PetTable { + id: Generated + is_favorite: Generated + name: string + owner_id: number + species: Species +} + +interface WineTable { + name: string + stock: number +} + +interface WineStockChangeTable { + stock_delta: number + wine_name: string +} + +export type Person = Selectable +export type NewPerson = Insertable +export type PersonUpdate = Updateable +export type Pet = Selectable +export type NewPet = Insertable +export type PetUpdate = Updateable +export type Species = 'dog' | 'cat' + +declare global { + export const db: Kysely + export function functionThatExpectsPersonWithNonNullValue( + person: Person & { nullable_column: string }, + ): void +} diff --git a/deno.check.json b/deno.check.json new file mode 100644 index 000000000..7c3e3bf10 --- /dev/null +++ b/deno.check.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://deno.land/x/deno/cli/schemas/config-file.v1.json", + "compilerOptions": { + "types": ["./deno.check.d.ts"] + }, + "imports": { + "better-sqlite3": "npm:better-sqlite3", + "kysely": "./dist/esm", + "kysely/helpers/postgres": "./dist/esm/helpers/postgres.js", + "mysql2": "npm:mysql2", + "pg": "npm:pg", + "pg-cursor": "npm:pg-cursor", + "tarn": "npm:tarn", + "tedious": "npm:tedious", + "type-editor": "./deno.check.d.ts" + } +} diff --git a/package.json b/package.json index b63d9d3a4..493d394d1 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,12 @@ "test:browser": "npm run build && npm run test:browser:build && node test/browser/test.js", "test:bun": "npm run build && bun link && cd test/bun && bun install && bun run test", "test:cloudflare-workers": "npm run build && cd test/cloudflare-workers && npm ci && npm test", - "test:deno": "npm run build && deno run --allow-env --allow-read --allow-net test/deno/local.test.ts && deno run --allow-env --allow-read --allow-net test/deno/cdn.test.ts", + "test:deno": "deno run --allow-env --allow-read --allow-net test/deno/local.test.ts && deno run --allow-env --allow-read --allow-net test/deno/cdn.test.ts", "test:typings": "tsd test/typings", "test:esmimports": "node scripts/check-esm-imports.js", "test:esbuild": "esbuild --bundle --platform=node --external:pg-native dist/esm/index.js --outfile=/dev/null", "test:exports": "attw --pack . && node scripts/check-exports.js", + "test:jsdocs": "deno check --doc-only --no-lock --unstable-sloppy-imports --config=\"deno.check.json\" ./src", "prettier": "prettier --write 'src/**/*.ts' 'test/**/*.ts'", "build": "npm run clean && (npm run build:esm & npm run build:cjs) && npm run script:module-fixup && npm run script:copy-interface-doc", "build:esm": "tsc -p tsconfig.json && npm run script:add-deno-type-references", diff --git a/src/dialect/dialect-adapter.ts b/src/dialect/dialect-adapter.ts index d4020278b..95c71531b 100644 --- a/src/dialect/dialect-adapter.ts +++ b/src/dialect/dialect-adapter.ts @@ -57,8 +57,10 @@ export interface DialectAdapter { * have explicit locks but supports `FOR UPDATE` row locks and transactional DDL: * * ```ts - * { - * async acquireMigrationLock(db, options): Promise { + * import { DialectAdapterBase, MigrationLockOptions, Kysely } from 'kysely' + * + * export class MyAdapter extends DialectAdapterBase { + * async override acquireMigrationLock(db: Kysely, options: MigrationLockOptions): Promise { * const queryDb = options.lockTableSchema * ? db.withSchema(options.lockTableSchema) * : db diff --git a/src/dialect/mssql/mssql-dialect-config.ts b/src/dialect/mssql/mssql-dialect-config.ts index efac14bd1..197610ba3 100644 --- a/src/dialect/mssql/mssql-dialect-config.ts +++ b/src/dialect/mssql/mssql-dialect-config.ts @@ -41,7 +41,9 @@ export interface MssqlDialectConfig { * // ... * tedious: { * ...Tedious, - * connectionFactory: () => new Tedious.Connection({ ... }), + * connectionFactory: () => new Tedious.Connection({ + * // ... + * }), * }, * }) * ``` diff --git a/src/expression/expression-builder.ts b/src/expression/expression-builder.ts index b22277545..c3faa4c57 100644 --- a/src/expression/expression-builder.ts +++ b/src/expression/expression-builder.ts @@ -995,7 +995,7 @@ export interface ExpressionBuilder { * ```ts * db.selectFrom('person') * .selectAll('person') - * .where((eb) => eb(eb.parens('age', '+', 1), '/', 100), '<', 0.1)) + * .where((eb) => eb(eb.parens('age', '+', 1), '/', 100), '<', 0.1) * ``` * * The generated SQL (PostgreSQL): @@ -1015,7 +1015,7 @@ export interface ExpressionBuilder { * eb('age', '=', 1).or('age', '=', 2)) * ).and( * eb('first_name', '=', 'Jennifer').or('first_name', '=', 'Arnold') - * )) + * ) * ``` * * The generated SQL (PostgreSQL): diff --git a/src/expression/expression-wrapper.ts b/src/expression/expression-wrapper.ts index 33d0cdc47..ce0d8638a 100644 --- a/src/expression/expression-wrapper.ts +++ b/src/expression/expression-wrapper.ts @@ -54,7 +54,7 @@ export class ExpressionWrapper * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * select "first_name" = $1 as "is_jennifer" * from "person" * ``` @@ -85,7 +85,7 @@ export class ExpressionWrapper * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * select * * from "person" * where ( @@ -107,13 +107,13 @@ export class ExpressionWrapper * eb.selectFrom('pet') * .select('id') * .whereRef('pet.owner_id', '=', 'person.id') - * ) + * )) * ) * ``` * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * select * * from "person" * where ( @@ -168,7 +168,7 @@ export class ExpressionWrapper * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * select * * from "person" * where ( @@ -190,13 +190,13 @@ export class ExpressionWrapper * eb.selectFrom('pet') * .select('id') * .whereRef('pet.owner_id', '=', 'person.id') - * ) + * )) * ) * ``` * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * select * * from "person" * where ( @@ -328,7 +328,7 @@ export class OrWrapper * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * select "first_name" = $1 or "first_name" = $2 as "is_jennifer_or_sylvester" * from "person" * ``` @@ -410,7 +410,7 @@ export class AndWrapper * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * select "first_name" = $1 and "first_name" = $2 as "is_jennifer_aniston" * from "person" * ``` diff --git a/src/expression/expression.ts b/src/expression/expression.ts index f4964e5ee..a1b1f8f16 100644 --- a/src/expression/expression.ts +++ b/src/expression/expression.ts @@ -70,7 +70,7 @@ export interface AliasableExpression extends Expression { * .selectFrom('person') * .select((eb) => * // `eb.fn` returns an AliasableExpression - * eb.fn('concat', ['first_name' eb.val(' '), 'last_name']).as('full_name') + * eb.fn('concat', ['first_name', eb.val(' '), 'last_name']).as('full_name') * ) * .executeTakeFirstOrThrow() * @@ -80,7 +80,7 @@ export interface AliasableExpression extends Expression { * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * select * concat("first_name", $1, "last_name") as "full_name" * from @@ -109,7 +109,7 @@ export interface AliasableExpression extends Expression { * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * insert into "person" ("first_name", "last_name") * from (values (1, 'foo')) as t(a, b) * select "t"."a", "t"."b" diff --git a/src/migration/file-migration-provider.ts b/src/migration/file-migration-provider.ts index 00e3eebac..382d08b4f 100644 --- a/src/migration/file-migration-provider.ts +++ b/src/migration/file-migration-provider.ts @@ -7,8 +7,8 @@ import { Migration, MigrationProvider } from './migrator.js' * ### Examples * * ```ts - * import { promises as fs } from 'fs' - * import path from 'path' + * import { promises as fs } from 'node:fs' + * import path from 'node:path' * * new FileMigrationProvider({ * fs, diff --git a/src/migration/migrator.ts b/src/migration/migrator.ts index 200599245..d98bc7841 100644 --- a/src/migration/migrator.ts +++ b/src/migration/migrator.ts @@ -36,8 +36,8 @@ export interface Migration { * other way. * * ```ts - * import { promises as fs } from 'fs' - * import path from 'path' + * import { promises as fs } from 'node:fs' + * import path from 'node:path' * * const migrator = new Migrator({ * db, diff --git a/src/operation-node/operation-node-transformer.ts b/src/operation-node/operation-node-transformer.ts index 418b504e0..ec7a01971 100644 --- a/src/operation-node/operation-node-transformer.ts +++ b/src/operation-node/operation-node-transformer.ts @@ -111,7 +111,7 @@ import { OutputNode } from './output-node.js' * ```ts * class CamelCaseTransformer extends OperationNodeTransformer { * transformIdentifier(node: IdentifierNode): IdentifierNode { - * node = super.transformIdentifier(node), + * node = super.transformIdentifier(node) * * return { * ...node, diff --git a/src/plugin/parse-json-results/parse-json-results-plugin.ts b/src/plugin/parse-json-results/parse-json-results-plugin.ts index 59c612d29..4bad81553 100644 --- a/src/plugin/parse-json-results/parse-json-results-plugin.ts +++ b/src/plugin/parse-json-results/parse-json-results-plugin.ts @@ -32,8 +32,9 @@ type ObjectStrategy = 'in-place' | 'create' * * ```ts * const db = new Kysely({ - * ... + * // ... * plugins: [new ParseJSONResultsPlugin()] + * // ... * }) * ``` */ diff --git a/src/query-builder/delete-query-builder.ts b/src/query-builder/delete-query-builder.ts index 3e3de6a77..0dde970a9 100644 --- a/src/query-builder/delete-query-builder.ts +++ b/src/query-builder/delete-query-builder.ts @@ -906,11 +906,11 @@ export class DeleteQueryBuilder * the code. In the example above the return type of the `deletePerson` function is: * * ```ts - * { + * Promise<{ * id: number * first_name: string * last_name?: string - * } + * }> * ``` */ $if( diff --git a/src/query-builder/insert-query-builder.ts b/src/query-builder/insert-query-builder.ts index cef3d46ad..6307a6a5b 100644 --- a/src/query-builder/insert-query-builder.ts +++ b/src/query-builder/insert-query-builder.ts @@ -561,7 +561,7 @@ export class InsertQueryBuilder * .onConflict((oc) => oc * .column('name') * .doUpdateSet({ species: 'hamster' }) - * .where('excluded.name', '!=', 'Catto'') + * .where('excluded.name', '!=', 'Catto') * ) * .execute() * ``` @@ -829,11 +829,11 @@ export class InsertQueryBuilder * the code. In the example above the return type of the `insertPerson` function is: * * ```ts - * { + * Promise<{ * id: number * first_name: string * last_name?: string - * } + * }> * ``` */ $if( diff --git a/src/query-builder/json-path-builder.ts b/src/query-builder/json-path-builder.ts index aa964075a..1ace2b6d8 100644 --- a/src/query-builder/json-path-builder.ts +++ b/src/query-builder/json-path-builder.ts @@ -35,9 +35,12 @@ export class JSONPathBuilder { * ### Examples * * ```ts - * db.selectFrom('person').select(eb => - * eb.ref('nicknames', '->').at(0).as('primary_nickname') - * ) + * await db.selectFrom('person') + * .select(eb => + * eb.ref('nicknames', '->').at(0).as('primary_nickname') + * ) + * .execute() + * ``` * * The generated SQL (PostgreSQL): * @@ -229,7 +232,7 @@ export class TraversedJSONPathBuilder * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * select "first_name" = $1 as "is_jennifer" * from "person" * ``` diff --git a/src/query-builder/merge-query-builder.ts b/src/query-builder/merge-query-builder.ts index 141d585bc..c6de45ed6 100644 --- a/src/query-builder/merge-query-builder.ts +++ b/src/query-builder/merge-query-builder.ts @@ -735,11 +735,11 @@ export class WheneableMergeQueryBuilder< * the code. In the example above the return type of the `updatePerson` function is: * * ```ts - * { + * Promise<{ * id: number * first_name: string * last_name?: string - * } + * }> * ``` */ $if( diff --git a/src/query-builder/output-interface.ts b/src/query-builder/output-interface.ts index 8a44502fe..75743a59e 100644 --- a/src/query-builder/output-interface.ts +++ b/src/query-builder/output-interface.ts @@ -58,9 +58,9 @@ export interface OutputInterface< * * const { id, full_name } = await db * .deleteFrom('person') - * .output((eb) => sql`concat(${eb.ref('deleted.first_name')}, ' ', ${eb.ref('deleted.last_name')})`.as('full_name') + * .output((eb) => sql`concat(${eb.ref('deleted.first_name')}, ' ', ${eb.ref('deleted.last_name')})`.as('full_name')) * .where('created_at', '<', new Date()) - * .executeTakeFirst() + * .executeTakeFirstOrThrow() * ``` * * Return the action performed on the row: diff --git a/src/query-builder/select-query-builder.ts b/src/query-builder/select-query-builder.ts index 80de9abac..588214904 100644 --- a/src/query-builder/select-query-builder.ts +++ b/src/query-builder/select-query-builder.ts @@ -341,10 +341,10 @@ export interface SelectQueryBuilder * // You can use `keyof Person` if any column of an interface is allowed. * type PossibleColumns = 'last_name' | 'first_name' | 'birth_date' * - * const spersons = await db + * const persons = await db * .selectFrom('person') * .select([ - * ref(columnFromUserInput) + * ref(columnFromUserInput), * 'id' * ]) * .execute() @@ -1590,11 +1590,11 @@ export interface SelectQueryBuilder * the code. In the example above the return type of the `getPerson` function is: * * ```ts - * { + * Promise<{ * id: number * first_name: string * last_name?: string - * } + * }> * ``` * * You can also call any other methods inside the callback: diff --git a/src/query-builder/update-query-builder.ts b/src/query-builder/update-query-builder.ts index b68704198..d900676fa 100644 --- a/src/query-builder/update-query-builder.ts +++ b/src/query-builder/update-query-builder.ts @@ -571,7 +571,7 @@ export class UpdateQueryBuilder * .set('first_name', 'Foo') * // As always, both arguments can be arbitrary expressions or * // callbacks that give you access to an expression builder: - * .set(sql`address['postalCode']`, (eb) => eb.val('61710)) + * .set(sql`address['postalCode']`, (eb) => eb.val('61710')) * .where('id', '=', '1') * .executeTakeFirst() * ``` @@ -864,11 +864,11 @@ export class UpdateQueryBuilder * the code. In the example above the return type of the `updatePerson` function is: * * ```ts - * { + * Promise<{ * id: number * first_name: string * last_name?: string - * } + * }> * ``` */ $if( diff --git a/src/raw-builder/raw-builder.ts b/src/raw-builder/raw-builder.ts index aeb73467d..491eff6b3 100644 --- a/src/raw-builder/raw-builder.ts +++ b/src/raw-builder/raw-builder.ts @@ -46,7 +46,7 @@ export interface RawBuilder extends AliasableExpression { * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * select concat(first_name, ' ', last_name) as "full_name" * from "person" * ``` @@ -73,7 +73,7 @@ export interface RawBuilder extends AliasableExpression { * * The generated SQL (PostgreSQL): * - * ```ts + * ```sql * insert into "person" ("first_name", "last_name") * from (values (1, 'foo')) as t(a, b) * select "t"."a", "t"."b" diff --git a/src/schema/create-table-builder.ts b/src/schema/create-table-builder.ts index 9aefddaa1..09de9fefd 100644 --- a/src/schema/create-table-builder.ts +++ b/src/schema/create-table-builder.ts @@ -95,7 +95,7 @@ export class CreateTableBuilder * * await db.schema * .createTable('person') - * .addColumn('id', 'integer', (col) => col.autoIncrement().primaryKey()), + * .addColumn('id', 'integer', (col) => col.autoIncrement().primaryKey()) * .addColumn('first_name', 'varchar(50)', (col) => col.notNull()) * .addColumn('last_name', 'varchar(255)') * .addColumn('bank_balance', 'numeric(8, 2)') @@ -103,7 +103,7 @@ export class CreateTableBuilder * // don't include it. * .addColumn('data', sql`any_type_here`) * .addColumn('parent_id', 'integer', (col) => - * col.references('person.id').onDelete('cascade')) + * col.references('person.id').onDelete('cascade') * ) * ``` * @@ -114,11 +114,14 @@ export class CreateTableBuilder * `create table` query. See the next example: * * ```ts + * await db.schema + * .createTable('person') * .addColumn('parent_id', 'integer') * .addForeignKeyConstraint( * 'person_parent_id_fk', ['parent_id'], 'person', ['id'], * (cb) => cb.onDelete('cascade') * ) + * .execute() * ``` * * Another good example is that PostgreSQL doesn't support the `auto_increment` @@ -128,7 +131,8 @@ export class CreateTableBuilder * ```ts * await db.schema * .createTable('person') - * .addColumn('id', 'serial', (col) => col.primaryKey()), + * .addColumn('id', 'serial', (col) => col.primaryKey()) + * .execute() * ``` */ addColumn( @@ -310,11 +314,11 @@ export class CreateTableBuilder * ### Examples * * ```ts - * db.schema.createTable('person') + * await db.schema.createTable('person') * .modifyFront(sql`global temporary`) * .addColumn('id', 'integer', col => col.primaryKey()) * .addColumn('first_name', 'varchar(64)', col => col.notNull()) - * .addColumn('last_name', 'varchar(64), col => col.notNull()) + * .addColumn('last_name', 'varchar(64)', col => col.notNull()) * .execute() * ``` * @@ -346,10 +350,10 @@ export class CreateTableBuilder * ### Examples * * ```ts - * db.schema.createTable('person') + * await db.schema.createTable('person') * .addColumn('id', 'integer', col => col => primaryKey()) * .addColumn('first_name', 'varchar(64)', col => col.notNull()) - * .addColumn('last_name', 'varchar(64), col => col.notNull()) + * .addColumn('last_name', 'varchar(64)', col => col.notNull()) * .modifyEnd(sql`collate utf8_unicode_ci`) * .execute() * ``` diff --git a/src/util/streamable.ts b/src/util/streamable.ts index d9c73358f..3c88d1e9f 100644 --- a/src/util/streamable.ts +++ b/src/util/streamable.ts @@ -8,7 +8,7 @@ export interface Streamable { * ### Examples * * ```ts - * const stream = db. + * const stream = db * .selectFrom('person') * .select(['first_name', 'last_name']) * .where('gender', '=', 'other')