-
-
Notifications
You must be signed in to change notification settings - Fork 12
feat: add sql.js dialect #134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Forked from https://github.com/betarixm/kysely-sql-js |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| import config from '@zenstackhq/eslint-config/base.js'; | ||
|
|
||
| /** @type {import("eslint").Linter.Config} */ | ||
| export default config; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| { | ||
| "name": "@zenstackhq/kysely-sql-js", | ||
| "version": "3.0.0-alpha.16", | ||
| "description": "Kysely dialect for sql.js", | ||
| "type": "module", | ||
| "scripts": { | ||
| "build": "tsup-node", | ||
| "watch": "tsup-node --watch", | ||
| "lint": "eslint src --ext ts", | ||
| "pack": "pnpm pack" | ||
| }, | ||
| "keywords": [], | ||
| "author": "ZenStack Team", | ||
| "license": "MIT", | ||
| "files": [ | ||
| "dist" | ||
| ], | ||
| "exports": { | ||
| ".": { | ||
| "import": { | ||
| "types": "./dist/index.d.ts", | ||
| "default": "./dist/index.js" | ||
| }, | ||
| "require": { | ||
| "types": "./dist/index.d.cts", | ||
| "default": "./dist/index.cjs" | ||
| } | ||
| } | ||
| }, | ||
| "devDependencies": { | ||
| "@types/sql.js": "^1.4.9", | ||
| "@zenstackhq/eslint-config": "workspace:*", | ||
| "@zenstackhq/typescript-config": "workspace:*", | ||
| "@zenstackhq/vitest-config": "workspace:*", | ||
| "sql.js": "^1.13.0", | ||
| "kysely": "catalog:" | ||
| }, | ||
| "peerDependencies": { | ||
| "sql.js": "^1.13.0", | ||
| "kysely": "catalog:" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import type { DatabaseConnection, QueryResult } from 'kysely'; | ||
| import type { BindParams, Database } from 'sql.js'; | ||
|
|
||
| import { CompiledQuery } from 'kysely'; | ||
|
|
||
| export class SqlJsConnection implements DatabaseConnection { | ||
| private database: Database; | ||
|
|
||
| constructor(database: Database) { | ||
| this.database = database; | ||
| } | ||
|
|
||
| async executeQuery<R>(compiledQuery: CompiledQuery<unknown>): Promise<QueryResult<R>> { | ||
| const executeResult = this.database.exec(compiledQuery.sql, compiledQuery.parameters as BindParams); | ||
| const rowsModified = this.database.getRowsModified(); | ||
| return { | ||
| numAffectedRows: BigInt(rowsModified), | ||
| rows: executeResult | ||
| .map(({ columns, values }) => | ||
| values.map((row) => columns.reduce((acc, column, i) => ({ ...acc, [column]: row[i] }), {}) as R), | ||
| ) | ||
| .flat(), | ||
| }; | ||
| } | ||
|
|
||
| // eslint-disable-next-line require-yield | ||
| async *streamQuery() { | ||
| throw new Error('Not supported with SQLite'); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import type { Dialect } from 'kysely'; | ||
|
|
||
| import type { SqlJsDialectConfig } from './types'; | ||
|
|
||
| import { Kysely, SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler } from 'kysely'; | ||
|
|
||
| import { SqlJsDriver } from './driver'; | ||
|
|
||
| /** | ||
| * The SqlJsDialect is for testing purposes only and should not be used in production. | ||
| */ | ||
| export class SqlJsDialect implements Dialect { | ||
| private config: SqlJsDialectConfig; | ||
|
|
||
| constructor(config: SqlJsDialectConfig) { | ||
| this.config = config; | ||
| } | ||
|
|
||
| createAdapter = () => new SqliteAdapter(); | ||
|
|
||
| createDriver = () => new SqlJsDriver(this.config); | ||
|
|
||
| createIntrospector = (db: Kysely<any>) => new SqliteIntrospector(db); | ||
|
|
||
| createQueryCompiler = () => new SqliteQueryCompiler(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import type { DatabaseConnection, Driver } from 'kysely'; | ||
|
|
||
| import { CompiledQuery } from 'kysely'; | ||
|
|
||
| import { SqlJsConnection } from './connection'; | ||
| import type { SqlJsDialectConfig } from './types'; | ||
|
|
||
| export class SqlJsDriver implements Driver { | ||
| private config: SqlJsDialectConfig; | ||
|
|
||
| constructor(config: SqlJsDialectConfig) { | ||
| this.config = config; | ||
| } | ||
|
|
||
| async acquireConnection(): Promise<DatabaseConnection> { | ||
| return new SqlJsConnection(this.config.sqlJs); | ||
| } | ||
|
|
||
| async beginTransaction(connection: DatabaseConnection): Promise<void> { | ||
| await connection.executeQuery(CompiledQuery.raw('BEGIN')); | ||
| } | ||
|
|
||
| async commitTransaction(connection: DatabaseConnection): Promise<void> { | ||
| await connection.executeQuery(CompiledQuery.raw('COMMIT')); | ||
| } | ||
|
|
||
| async rollbackTransaction(connection: DatabaseConnection): Promise<void> { | ||
| await connection.executeQuery(CompiledQuery.raw('ROLLBACK')); | ||
| } | ||
|
|
||
| async destroy(): Promise<void> { | ||
| this.config.sqlJs.close(); | ||
| } | ||
|
|
||
| async init() {} | ||
|
|
||
| async releaseConnection(_connection: DatabaseConnection): Promise<void> {} | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| export * from './connection'; | ||
| export * from './dialect'; | ||
| export * from './driver'; | ||
| export * from './types'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import type { Database } from 'sql.js'; | ||
|
|
||
| export interface SqlJsDialectConfig { | ||
| sqlJs: Database; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import type { Database } from './types'; | ||
|
|
||
| import { Kysely } from 'kysely'; | ||
| import initSqlJs from 'sql.js'; | ||
|
|
||
| import { SqlJsDialect } from '../../src'; | ||
|
|
||
| const SqlJsStatic = await initSqlJs(); | ||
|
|
||
| export const db = new Kysely<Database>({ | ||
| dialect: new SqlJsDialect({ sqlJs: new SqlJsStatic.Database() }), | ||
| }); |
96 changes: 96 additions & 0 deletions
96
packages/dialects/sql.js/test/getting-started/person-repository.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| import { sql } from 'kysely'; | ||
| import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'; | ||
| import { db } from './database'; | ||
| import * as PersonRepository from './person-repository'; | ||
|
|
||
| describe('person-repository', () => { | ||
| beforeEach(async () => { | ||
| await db | ||
| .insertInto('person') | ||
| .values({ | ||
| id: 123, | ||
| first_name: 'Arnold', | ||
| last_name: 'Schwarzenegger', | ||
| gender: 'other', | ||
| }) | ||
| .executeTakeFirstOrThrow(); | ||
| }); | ||
|
|
||
| beforeAll(async () => { | ||
| await db.schema | ||
| .createTable('person') | ||
| .addColumn('id', 'integer', (cb) => cb.primaryKey().autoIncrement().notNull()) | ||
| .addColumn('first_name', 'varchar(255)', (cb) => cb.notNull()) | ||
| .addColumn('last_name', 'varchar(255)') | ||
| .addColumn('gender', 'varchar(50)', (cb) => cb.notNull()) | ||
| .addColumn('created_at', 'timestamp', (cb) => cb.notNull().defaultTo(sql`current_timestamp`)) | ||
| .execute(); | ||
| }); | ||
|
|
||
| afterEach(async () => { | ||
| await sql`delete from ${sql.table('person')}`.execute(db); | ||
| }); | ||
|
|
||
| afterAll(async () => { | ||
| await db.schema.dropTable('person').execute(); | ||
| }); | ||
|
|
||
| it('should find a person with a given id', async () => { | ||
| expect(await PersonRepository.findPersonById(123)).toMatchObject({ | ||
| id: 123, | ||
| first_name: 'Arnold', | ||
| last_name: 'Schwarzenegger', | ||
| gender: 'other', | ||
| }); | ||
| }); | ||
|
|
||
| it('should find all people named Arnold', async () => { | ||
| const people = await PersonRepository.findPeople({ first_name: 'Arnold' }); | ||
|
|
||
| expect(people).toHaveLength(1); | ||
| expect(people[0]).toMatchObject({ | ||
| id: 123, | ||
| first_name: 'Arnold', | ||
| last_name: 'Schwarzenegger', | ||
| gender: 'other', | ||
| }); | ||
| }); | ||
|
|
||
| it('should update gender of a person with a given id', async () => { | ||
| await PersonRepository.updatePerson(123, { gender: 'woman' }); | ||
|
|
||
| expect(await PersonRepository.findPersonById(123)).toMatchObject({ | ||
| id: 123, | ||
| first_name: 'Arnold', | ||
| last_name: 'Schwarzenegger', | ||
| gender: 'woman', | ||
| }); | ||
| }); | ||
|
|
||
| it('should create a person', async () => { | ||
| await PersonRepository.createPerson({ | ||
| first_name: 'Jennifer', | ||
| last_name: 'Aniston', | ||
| gender: 'woman', | ||
| }); | ||
|
|
||
| expect(await PersonRepository.findPeople({ first_name: 'Jennifer' })).toHaveLength(1); | ||
| }); | ||
|
|
||
| it('should create multiple persons', async () => { | ||
| const created = await PersonRepository.createPersons([ | ||
| { first_name: 'Brad', last_name: 'Pitt', gender: 'man' }, | ||
| { first_name: 'Angelina', last_name: 'Jolie', gender: 'woman' }, | ||
| ]); | ||
| console.log(created); | ||
ymc9 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| await expect(PersonRepository.findPeople({ first_name: 'Brad' })).resolves.toBeTruthy(); | ||
| await expect(PersonRepository.findPeople({ first_name: 'Angelina' })).resolves.toBeTruthy(); | ||
| }); | ||
|
|
||
| it('should delete a person with a given id', async () => { | ||
| await PersonRepository.deletePerson(123); | ||
|
|
||
| expect(await PersonRepository.findPersonById(123)).toBeUndefined(); | ||
| }); | ||
| }); | ||
48 changes: 48 additions & 0 deletions
48
packages/dialects/sql.js/test/getting-started/person-repository.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| import type { PersonUpdate, Person, NewPerson } from './types'; | ||
|
|
||
| import { db } from './database'; | ||
|
|
||
| export async function findPersonById(id: number) { | ||
| return await db.selectFrom('person').where('id', '=', id).selectAll().executeTakeFirst(); | ||
| } | ||
|
|
||
| export async function findPeople(criteria: Partial<Person>) { | ||
| let query = db.selectFrom('person'); | ||
|
|
||
| if (criteria.id) { | ||
| query = query.where('id', '=', criteria.id); // Kysely is immutable, you must re-assign! | ||
| } | ||
|
|
||
| if (criteria.first_name) { | ||
| query = query.where('first_name', '=', criteria.first_name); | ||
| } | ||
|
|
||
| if (criteria.last_name !== undefined) { | ||
| query = query.where('last_name', criteria.last_name === null ? 'is' : '=', criteria.last_name); | ||
| } | ||
|
|
||
| if (criteria.gender) { | ||
| query = query.where('gender', '=', criteria.gender); | ||
| } | ||
|
|
||
| if (criteria.created_at) { | ||
| query = query.where('created_at', '=', criteria.created_at); | ||
| } | ||
|
|
||
| return await query.selectAll().execute(); | ||
| } | ||
|
|
||
| export async function updatePerson(id: number, updateWith: PersonUpdate) { | ||
| await db.updateTable('person').set(updateWith).where('id', '=', id).execute(); | ||
| } | ||
|
|
||
| export async function createPerson(person: NewPerson) { | ||
| return await db.insertInto('person').values(person).returningAll().executeTakeFirstOrThrow(); | ||
| } | ||
|
|
||
| export async function createPersons(persons: NewPerson[]) { | ||
| return await db.insertInto('person').values(persons).executeTakeFirstOrThrow(); | ||
| } | ||
| export async function deletePerson(id: number) { | ||
| return await db.deleteFrom('person').where('id', '=', id).returningAll().executeTakeFirst(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import type { ColumnType, Generated, Insertable, Selectable, Updateable } from 'kysely'; | ||
|
|
||
| export interface Database { | ||
| person: PersonTable; | ||
| pet: PetTable; | ||
| } | ||
|
|
||
| // This interface describes the `person` table to Kysely. Table | ||
| // interfaces should only be used in the `Database` type above | ||
| // and never as a result type of a query!. See the `Person`, | ||
| // `NewPerson` and `PersonUpdate` types below. | ||
| export interface PersonTable { | ||
| // Columns that are generated by the database should be marked | ||
| // using the `Generated` type. This way they are automatically | ||
| // made optional in inserts and updates. | ||
| id: Generated<number>; | ||
|
|
||
| first_name: string; | ||
| gender: 'man' | 'woman' | 'other'; | ||
|
|
||
| // If the column is nullable in the database, make its type nullable. | ||
| // Don't use optional properties. Optionality is always determined | ||
| // automatically by Kysely. | ||
| last_name: string | null; | ||
|
|
||
| // You can specify a different type for each operation (select, insert and | ||
| // update) using the `ColumnType<SelectType, InsertType, UpdateType>` | ||
| // wrapper. Here we define a column `created_at` that is selected as | ||
| // a `Date`, can optionally be provided as a `string` in inserts and | ||
| // can never be updated: | ||
| created_at: ColumnType<Date, string | undefined, never>; | ||
| } | ||
|
|
||
| // You should not use the table schema interfaces directly. Instead, you should | ||
| // use the `Selectable`, `Insertable` and `Updateable` wrappers. These wrappers | ||
| // make sure that the correct types are used in each operation. | ||
| // | ||
| // Most of the time you should trust the type inference and not use explicit | ||
| // types at all. These types can be useful when typing function arguments. | ||
| export type Person = Selectable<PersonTable>; | ||
| export type NewPerson = Insertable<PersonTable>; | ||
| export type PersonUpdate = Updateable<PersonTable>; | ||
|
|
||
| export interface PetTable { | ||
| id: Generated<number>; | ||
| name: string; | ||
| owner_id: number; | ||
| species: 'dog' | 'cat'; | ||
| } | ||
|
|
||
| export type Pet = Selectable<PetTable>; | ||
| export type NewPet = Insertable<PetTable>; | ||
| export type PetUpdate = Updateable<PetTable>; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "extends": "@zenstackhq/typescript-config/base.json", | ||
| "compilerOptions": { | ||
| "outDir": "dist" | ||
| }, | ||
| "include": ["src/**/*", "test/**/*"] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { defineConfig } from 'tsup'; | ||
|
|
||
| export default defineConfig({ | ||
| entry: { | ||
| index: 'src/index.ts', | ||
| }, | ||
| outDir: 'dist', | ||
| splitting: false, | ||
| sourcemap: true, | ||
| clean: true, | ||
| dts: true, | ||
| format: ['cjs', 'esm'], | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| import { defineConfig, mergeConfig } from 'vitest/config'; | ||
| import base from '@zenstackhq/vitest-config/base'; | ||
|
|
||
| export default mergeConfig(base, defineConfig({})); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.