From c988be9c7006c120fac30f7c7813d38a24b63188 Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Mon, 8 Apr 2024 19:12:40 +0100 Subject: [PATCH 01/15] feat: :construction: add boilerplate folder, tests, copied from sqljs --- src/libsql/README.md | 252 ++++++++++++++++++ src/libsql/build.ts | 7 + src/libsql/bun.lockb | Bin 0 -> 15424 bytes src/libsql/index.ts | 103 ++++++++ src/libsql/package.json | 45 ++++ src/libsql/tests/all.test.ts | 186 ++++++++++++++ src/libsql/tests/create.test.ts | 58 +++++ src/libsql/tests/delete.test.ts | 84 ++++++ src/libsql/tests/select.test.ts | 436 ++++++++++++++++++++++++++++++++ src/libsql/tests/update.test.ts | 76 ++++++ 10 files changed, 1247 insertions(+) create mode 100644 src/libsql/README.md create mode 100644 src/libsql/build.ts create mode 100755 src/libsql/bun.lockb create mode 100644 src/libsql/index.ts create mode 100644 src/libsql/package.json create mode 100644 src/libsql/tests/all.test.ts create mode 100644 src/libsql/tests/create.test.ts create mode 100644 src/libsql/tests/delete.test.ts create mode 100644 src/libsql/tests/select.test.ts create mode 100644 src/libsql/tests/update.test.ts diff --git a/src/libsql/README.md b/src/libsql/README.md new file mode 100644 index 0000000..e14e23d --- /dev/null +++ b/src/libsql/README.md @@ -0,0 +1,252 @@ +# Sibyl + +Sibyl is a lightweight SQLite query builder for SQL.js and Bun's sqlite3 driver, providing a Prisma-like query builder. Sibyl is in early development, +so expect breaking changes and rapid development. + +## Getting Started + +### Installation + +Dependant on your chosen SQLite driver, you'll want to follow one +of the following installation methods: + +#### SQL.js Installation + +If you choose to use Sibyl with `sql.js`, `sql.js` will provide the lower-level API to interact with your +embedded SQLite database. You'll also need to install the `.wasm` file that `sql.js` +provides; Please see their documentation at https://sql.js.org. + +With the `.wasm` file now available, you can install Sibyl with the following command: + +```bash +bun install sql.js @types/sql.js @crbroughton/sibyl +``` + +#### Bun Installation + +If you are using Sibyl with Bun, you should already have access to the driver, and can refer to the +Bun documentation. The Bun implemenation of Sibyl can be installed +with the following command: + +```bash +bun install @crbroughton/sibyl_bun +``` +Sibyl will then accept the native Bun SQLite `Database`, again, see the +Bun documentation. + +#### Getting Started + +To start off with Sibyl, you'll first have to ensure Sibyl is able to be run inside +of a top-level async/await file, alongside your `sql.js` database connection. As +referenced from the `sql.js` documentation, you can provide Sibyl a database instance +like so: + +```typescript +interface tableRowType { + id: number + name: string + sex: string + job: string + hasReadTheReadme: boolean +} + +interface secondRowType { + id: number +} + +interface Tables { + firstTable: tableRowType + secondTable: secondRowType +} + +const SQL = await sql({ // sql.js implementation + locateFile: () => { + return '/sql-wasm.wasm' + } +}) +const db = new SQL.Database() + +const { createTable, Insert, Select, All, Create } = await Sibyl(db) +``` + +With top-level async/await enabled, you can then use Sibyl. Sibyl provides the following +functions: + +- `createTable` - Allows you to create a table +- `Create` - Creates and returns a new entry into your selected table +- `Insert` - Allows you to provide an array of insertable entries into your selected table +- `Select` - Returns a type-safe array of entries from the selected table +- `All` - Returns all entries from the selected table +- `Update` Updates and returns a single entry from the selected table +- `Delete` - Deletes an entry from a selected table + +### Creating the table + +To create a new table, use the `createTable` command: + +```typescript +createTable('firstTable', { // inferred table name and entry + id: { + autoincrement: true, + type: 'INTEGER', // only allows for known data types ('int', 'char', 'blob') + nullable: false, + primary: true, + unique: true, + }, + job: { + type: 'char', + }, + name: { + type: 'char', + }, + sex: { + type: 'char', + }, + hasReadTheReadme: { + type: 'bool', + }, +}) +``` + +`createTable` takes two arguments, the first is the name of the table you wish to select, This +is based off the generic interface you first supplied to Sibyl. +The second argument will create the specified columns for your database. Sibyl will handle the order and creation of each column you have specified, and only allow known data types. + +### Inserting a single entry into the DB + +To create a new entry, you can use the `Create` function: + +```typescript +const result = Create('firstTable', { // returns the resulting entry + id: faker.number.int(), + name: 'Craig', + sex: 'male', + job: 'Software Engineer', + hasReadTheReadme: true, +}) +``` + +### Inserting mutiple entries into the DB + +To insert new entries into the database, you can use the `Insert` function: + +```typescript +let insertions: SibylResponse[] = [] +for (let index = 0; index < 1000; index++) { + insertions.push({ + id: faker.number.int(), + name: faker.person.firstName(), + sex: faker.person.sex(), + job: faker.person.jobTitle(), + hasReadTheReadme: true, + }) +} +// execute the provided instruction - Data will now be in the DB +const test = Insert('firstTable', insertions) +``` + +### Selecting entries from the DB + +When selecting entries from the database, you can utilise the `Select` function +to retrieve an array of type-safe entries, based from the generic interface +you have supplied to Sybil main function (see above `tableRowType`). + +```typescript +selection.value = Select('firstTable', { + where: { + id: 1, + name: "Craig", // can combine multiple where clauses + }, + limit: 20, // limit the response from Sibyl + offset: 10, // offset the response, useful for pagination +}) +``` + +#### OR Selection + +When selecting entries from the database, the `Select` function, by +default, uses an AND statement to build you query. You can however, +include an optional OR array to select entries: + +```typescript +const response = Select('firstTable', { // Returns all entries where name is Craig OR Bob + where: { + OR: [ + { + name: 'Craig' + }, + { + name: 'Bob' + } + ] + } +}) +``` + +You can also combine multiple OR statements as part of a single object, +if the keys do no clash: + +```typescript +const response = Select('firstTable', { // Returns all entries where name is Craig OR Bob OR hasReadTheReadme is false + where: { + OR: [ + { + name: 'Craig', + hasReadTheReadme: 0, // boolean values need to be selected + // based on their database values + // and will be returned as such + }, + { + name: 'Bob' + } + ] + } +}) +``` +When using the optional OR array to build a query, you can still use +the optional `offset` and `limit` keys. + +### Updating an entry in the DB + +To update a single entry in the database, you can use the `Update` function: + +```typescript +const updatedEntry = Update('firstTable', { // infers the table and response type + where: { // Can combine multiple where clauses + id: 1, + name: 'Craig', + }, + updates: { + name: 'Bob', // Can update multiple values at once + job: 'Engineer', + } +}) +``` + +### Sibyl Responses + +Sibyl also offers a custom type the `SibylResponse` type; This type can be helpful +when wanting to convert data types to TypeScript types; At the moment the custom type +only support boolean conversions from `boolean` to `0 | 1`. It's recommended to use +this type as a wrapper, if you're ever using boolean values. + +## Development + +To install dependencies: + +```bash +bun install +``` + +You can then try Sibyl in the playground, first install the dependencies: + +```bash +cd playground && bun install +``` + +and then run the playground: +```bash +bun run dev +``` + +This project was created using `bun init` in bun v1.0.29. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/src/libsql/build.ts b/src/libsql/build.ts new file mode 100644 index 0000000..6553d77 --- /dev/null +++ b/src/libsql/build.ts @@ -0,0 +1,7 @@ +import dts from 'bun-plugin-dts' + +await Bun.build({ + entrypoints: ['./index.ts'], + outdir: 'dist', + plugins: [dts()], +}) diff --git a/src/libsql/bun.lockb b/src/libsql/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..a2ef18506bc65312afea3f31a2c7b8e5046b1c3a GIT binary patch literal 15424 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 ZC0{bT=@uT4$^^xU31XFR3IBbc{|{L-5>(db: Database) { +type TableKeys = keyof T +type AccessTable = T[I] +function createTable(table: T, tableRow: MappedTable>) { + const statement = convertCreateTableStatement(tableRow) + db.run(`CREATE TABLE ${String(table)} (${statement});`) +} + +function Insert(table: K, rows: AccessTable[]) { + const statement = formatInsertStatement(String(table), rows) + db.run(statement) +} + +function Select(table: T, args: SelectArgs>>) { + const query = buildSelectQuery(String(table), args) + const record = db.exec(query) + + if (record[0]) { + return convertBooleanValues(convertToObjects>({ + columns: record[0].columns, + values: record[0].values, + })) + } + + return undefined +} + +function Create(table: T, entry: AccessTable) { + const statement = formatInsertStatement(String(table), [entry]) + db.run(statement) + const result = Select(table, { + where: entry, + }) + + if (result !== undefined) + return result[0] + + return undefined +} + +function All(table: K, args?: { sort: Sort>> }) { + let query = `SELECT * from ${String(table)}` + + if (args !== undefined && args.sort) { + const orders: string[] = [] + query += ' ORDER BY ' + for (const [key, value] of Object.entries(args.sort)) + orders.push(`${key} ${value}`) + query += orders.join(', ') + } + + const record = db.exec(query) + + if (record[0]) { + return convertBooleanValues(convertToObjects>({ + columns: record[0].columns, + values: record[0].values, + })) + } + + return undefined +} + +function Update(table: K, args: UpdateArgs>) { + const query = buildUpdateQuery(table, args) + db.exec(query) + + const result = Select(table, { + where: args.where, + }) + + if (result !== undefined) + return result[0] + + return undefined +} + +function Delete(table: K, args: DeleteArgs>) { + db.run(`DELETE FROM ${String(table)} WHERE ${objectToWhereClause(args.where)}`) +} + +return { + createTable, + Select, + All, + Insert, + Create, + Update, + Delete, +} +} diff --git a/src/libsql/package.json b/src/libsql/package.json new file mode 100644 index 0000000..640406c --- /dev/null +++ b/src/libsql/package.json @@ -0,0 +1,45 @@ +{ + "name": "@crbroughton/sibyl_libsql", + "type": "module", + "version": "2.1.2", + "description": "A lightweight query builder for libsql", + "author": "Craig R Broughton", + "license": "MIT", + "homepage": "https://github.com/crbroughton/sibyl", + "repository": { + "type": "git", + "url": "git+https://github.com/crbroughton/sibyl.git" + }, + "keywords": [ + "query builder", + "sqlite", + "wasm", + "embedded", + "typescript", + "libsql" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "bun run build.ts", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "changeset": "npx changeset", + "changeset:status": "npx changeset status --verbose", + "changeset:version": "npx changeset version", + "publish": "bun run build && npm publish --access=public" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "devDependencies": { + "@faker-js/faker": "^8.4.1", + "@types/bun": "latest", + "@types/sql.js": "^1.4.9", + "bun-plugin-dts": "^0.2.1", + "libsql": "^0.3.10" + } +} diff --git a/src/libsql/tests/all.test.ts b/src/libsql/tests/all.test.ts new file mode 100644 index 0000000..8314b7b --- /dev/null +++ b/src/libsql/tests/all.test.ts @@ -0,0 +1,186 @@ +import { describe, expect, it } from 'bun:test' +import sql from 'sql.js' +import Sibyl from '../index' + +interface TableRow { + id: number + location: string + name: string +} + +interface Tables { + first: TableRow +} + +describe('all tests', () => { + it('returns all data available in the given table', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + const { createTable, Insert, All } = await Sibyl(db) + + createTable('first', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + }) + + Insert('first', [ + { + id: 1, + name: 'Craig', + location: 'Brighton', + }, + { + id: 2, + name: 'Bob', + location: 'Cornwall', + }, + ]) + + const actual = All('first') + + const expectation = [ + { + id: 1, + location: 'Brighton', + name: 'Craig', + }, + { + id: 2, + location: 'Cornwall', + name: 'Bob', + }, + ] + + expect(actual).toStrictEqual(expectation) + }) + it('returns all data available in the given table and sorts then in ascending order by ID', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + const { createTable, Insert, All } = await Sibyl(db) + + createTable('first', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + }) + + Insert('first', [ + { + id: 1, + name: 'Craig', + location: 'Brighton', + }, + { + id: 2, + name: 'Bob', + location: 'Cornwall', + }, + ]) + + const actual = All('first', { + sort: { + id: 'ASC', + }, + }) + + const expectation = [ + { + id: 1, + location: 'Brighton', + name: 'Craig', + }, + { + id: 2, + location: 'Cornwall', + name: 'Bob', + }, + ] + + expect(actual).toStrictEqual(expectation) + }) + it('returns all data available in the given table and sorts then in descending order by ID', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + const { createTable, Insert, All } = await Sibyl(db) + + createTable('first', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + }) + + Insert('first', [ + { + id: 1, + name: 'Craig', + location: 'Brighton', + }, + { + id: 2, + name: 'Bob', + location: 'Cornwall', + }, + ]) + + const actual = All('first', { + sort: { + id: 'DESC', + }, + }) + + const expectation = [ + { + id: 2, + location: 'Cornwall', + name: 'Bob', + }, + { + id: 1, + location: 'Brighton', + name: 'Craig', + }, + ] + + expect(actual).toStrictEqual(expectation) + }) +}) diff --git a/src/libsql/tests/create.test.ts b/src/libsql/tests/create.test.ts new file mode 100644 index 0000000..ea95017 --- /dev/null +++ b/src/libsql/tests/create.test.ts @@ -0,0 +1,58 @@ +import { describe, expect, it } from 'bun:test' +import sql from 'sql.js' +import Sibyl from '../index' +import type { SibylResponse } from '../../types' + +interface TableRow { + id: number + location: string + name: string + booleanTest: boolean +} + +interface Tables { + first: TableRow +} + +describe('create tests', () => { + it('creates a new entry in the DB', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + const { createTable, Create } = await Sibyl(db) + + createTable('first', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, + }) + const actual = Create('first', { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: true, + }) + const expectation: SibylResponse = { + id: 2344, + location: 'Brighton', + name: 'Craig', + booleanTest: 1, + } + expect(actual).toStrictEqual(expectation) + }) +}) diff --git a/src/libsql/tests/delete.test.ts b/src/libsql/tests/delete.test.ts new file mode 100644 index 0000000..a0c38ce --- /dev/null +++ b/src/libsql/tests/delete.test.ts @@ -0,0 +1,84 @@ +import { describe, expect, it } from 'bun:test' +import sql from 'sql.js' +import Sibyl from '../index' + +interface TableRow { + id: number + location: string + name: string +} + +interface Tables { + first: TableRow +} + +describe('delete tests', () => { + it('deletes an entry in the DB', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + const { createTable, Insert, All, Delete } = await Sibyl(db) + + createTable('first', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + }) + Insert('first', [ + { + name: 'Craig', + id: 2344, + location: 'Brighton', + }, + { + id: 1, + name: 'Bob', + location: 'Cornwall', + }, + ]) + + let actual = All('first') + + let expectation = [ + { + id: 1, + name: 'Bob', + location: 'Cornwall', + }, + { + name: 'Craig', + id: 2344, + location: 'Brighton', + }, + ] + expect(actual).toStrictEqual(expectation) + + Delete('first', { + where: { + id: 2344, + name: 'Craig', + }, + }) + actual = All('first') + expectation = [ + { + id: 1, + name: 'Bob', + location: 'Cornwall', + }, + ] + expect(actual).toStrictEqual(expectation) + }) +}) diff --git a/src/libsql/tests/select.test.ts b/src/libsql/tests/select.test.ts new file mode 100644 index 0000000..c648724 --- /dev/null +++ b/src/libsql/tests/select.test.ts @@ -0,0 +1,436 @@ +import { describe, expect, it } from 'bun:test' +import sql from 'sql.js' +import Sibyl from '../index' +import type { SibylResponse } from '../../types' + +interface TableRow { + id: number + location: string + name: string + booleanTest: boolean +} + +interface Tables { + first: TableRow +} + +describe('select tests', () => { + it('select an entry in the DB', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + const { createTable, Create, Select } = await Sibyl(db) + + createTable('first', { + id: { + primary: true, + autoincrement: true, + type: 'INTEGER', + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, + }) + Create('first', { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: true, + }) + + const actual = Select('first', { + where: { + id: 2344, + }, + }) + + const expectation: SibylResponse[] = [{ + id: 2344, + location: 'Brighton', + name: 'Craig', + booleanTest: 1, + }] + expect(actual).toStrictEqual(expectation) + }) + it('selects multiple entries in the DB', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + const { createTable, Insert, Select } = await Sibyl(db) + + createTable('first', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, + }) + Insert('first', [ + { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: true, + }, + { + name: 'Bob', + id: 1, + location: 'Brighton', + booleanTest: false, + }, + ]) + + const actual = Select('first', { + where: { + location: 'Brighton', + }, + }) + + const expectation: SibylResponse[] = [ + { + name: 'Bob', + id: 1, + location: 'Brighton', + booleanTest: 0, + }, + { + id: 2344, + location: 'Brighton', + name: 'Craig', + booleanTest: 1, + }, + ] + expect(actual).toStrictEqual(expectation) + }) + it('selects multiple entries with the OR statement', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + const { createTable, Insert, Select } = await Sibyl(db) + + createTable('first', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, + }) + Insert('first', [ + { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: true, + }, + { + name: 'Bob', + id: 1, + location: 'Leeds', + booleanTest: false, + }, + { + name: 'Chris', + id: 2, + location: 'Cornwall', + booleanTest: false, + }, + ]) + + const actual = Select('first', { + where: { + OR: [ + { + location: 'Cornwall', + }, + { + location: 'Brighton', + }, + ], + }, + }) + + const expectation: SibylResponse[] = [ + { + name: 'Chris', + id: 2, + location: 'Cornwall', + booleanTest: 0, + }, + { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: 1, + }, + ] + expect(actual).toStrictEqual(expectation) + }) + it('selects multiple entries with the OR statement (mixed object)', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + const { createTable, Insert, Select } = await Sibyl(db) + + createTable('first', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, + }) + Insert('first', [ + { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: true, + }, + { + name: 'Bob', + id: 1, + location: 'Leeds', + booleanTest: false, + }, + { + name: 'Chris', + id: 2, + location: 'Cornwall', + booleanTest: false, + }, + ]) + + const actual = Select('first', { + where: { + OR: [ + { + location: 'Cornwall', + id: 2344, + }, + ], + }, + }) + + const expectation: SibylResponse[] = [ + { + name: 'Chris', + id: 2, + location: 'Cornwall', + booleanTest: 0, + }, + { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: 1, + }, + ] + expect(actual).toStrictEqual(expectation) + }) + it('selects multiple entries with the OR statement and sorts them in ascending order by id', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + const { createTable, Insert, Select } = await Sibyl(db) + + createTable('first', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, + }) + Insert('first', [ + { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: true, + }, + { + name: 'Bob', + id: 1, + location: 'Brighton', + booleanTest: false, + }, + { + name: 'Chris', + id: 2, + location: 'Cornwall', + booleanTest: false, + }, + ]) + + const actual = Select('first', { + where: { + location: 'Brighton', + }, + sort: { + id: 'ASC', + }, + }) + + const expectation: SibylResponse[] = [ + { + id: 1, + location: 'Brighton', + name: 'Bob', + booleanTest: 0, + }, + { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: 1, + }, + ] + expect(actual).toStrictEqual(expectation) + }) + it('selects using boolean value', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + // Create table schema + interface firstTable { + id: number + name: string + location: string + hasReadTheReadme: boolean + } + interface Tables { + firstTable: firstTable + } + const { createTable, Insert, Select } = await Sibyl(db) + + createTable('firstTable', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + name: { + type: 'char', + }, + hasReadTheReadme: { + type: 'bool', + }, + location: { + type: 'char', + }, + }) + + Insert('firstTable', [ + { + id: 1, + hasReadTheReadme: true, + location: 'Brighton', + name: 'Craig', + }, + { + id: 2, + hasReadTheReadme: false, + location: 'Leeds', + name: 'Bob', + }, + { + id: 3, + hasReadTheReadme: true, + location: 'Brighton', + name: 'David', + }, + ]) + + const actual = Select('firstTable', { + where: { + OR: [ + { + name: 'Craig', + }, + { + hasReadTheReadme: 1, + }, + ], + }, + }) + const expectation: SibylResponse[] = [ + { + id: 1, + hasReadTheReadme: 1, + location: 'Brighton', + name: 'Craig', + }, + { + id: 3, + hasReadTheReadme: 1, + location: 'Brighton', + name: 'David', + }, + ] + expect(actual).toStrictEqual(expectation) + }) +}) diff --git a/src/libsql/tests/update.test.ts b/src/libsql/tests/update.test.ts new file mode 100644 index 0000000..c0f2d81 --- /dev/null +++ b/src/libsql/tests/update.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, it } from 'bun:test' +import sql from 'sql.js' +import Sibyl from '../index' +import type { SibylResponse } from '../../types' + +interface TableRow { + id: number + location: string + name: string + booleanTest: boolean +} + +interface Tables { + first: TableRow +} + +describe('update tests', () => { + it('updates an entry in the DB', async () => { + const SQL = await sql({ + locateFile: () => { + return 'playground/public/sql-wasm.wasm' + }, + }) + const db = new SQL.Database() + const { createTable, Insert, Update } = await Sibyl(db) + + createTable('first', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, + }) + Insert('first', [ + { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: true, + }, + { + name: 'Bob', + id: 1, + location: 'Cornwall', + booleanTest: false, + }, + ]) + + const actual = Update('first', { + where: { + id: 2344, + }, + updates: { + name: 'Richard', + booleanTest: false, + }, + }) + const expectation: SibylResponse = { + id: 2344, + location: 'Brighton', + name: 'Richard', + booleanTest: 0, + } + expect(actual).toStrictEqual(expectation) + }) +}) From 89a10714d40bc0fb35fc4d6f4a502e98beb010d2 Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Mon, 8 Apr 2024 19:18:48 +0100 Subject: [PATCH 02/15] test: :test_tube: update tests to use libsql Database type, tests now failing --- src/libsql/index.ts | 10 ++++---- src/libsql/tests/all.test.ts | 23 +++-------------- src/libsql/tests/delete.test.ts | 9 ++----- src/libsql/tests/select.test.ts | 44 ++++++--------------------------- src/libsql/tests/update.test.ts | 9 ++----- 5 files changed, 20 insertions(+), 75 deletions(-) diff --git a/src/libsql/index.ts b/src/libsql/index.ts index 1a881db..90ec6a7 100644 --- a/src/libsql/index.ts +++ b/src/libsql/index.ts @@ -1,4 +1,4 @@ -import type { Database } from 'sql.js' +import type { Database } from 'libsql' import type { DeleteArgs, MappedTable, SelectArgs, SibylResponse, Sort, UpdateArgs } from '../types' import { buildSelectQuery, @@ -15,12 +15,12 @@ type TableKeys = keyof T type AccessTable = T[I] function createTable(table: T, tableRow: MappedTable>) { const statement = convertCreateTableStatement(tableRow) - db.run(`CREATE TABLE ${String(table)} (${statement});`) + db.exec(`CREATE TABLE ${String(table)} (${statement});`) } function Insert(table: K, rows: AccessTable[]) { const statement = formatInsertStatement(String(table), rows) - db.run(statement) + db.exec(statement) } function Select(table: T, args: SelectArgs>>) { @@ -39,7 +39,7 @@ function Select(table: T, args: SelectArgs(table: T, entry: AccessTable) { const statement = formatInsertStatement(String(table), [entry]) - db.run(statement) + db.exec(statement) const result = Select(table, { where: entry, }) @@ -88,7 +88,7 @@ function Update(table: K, args: UpdateArgs>) } function Delete(table: K, args: DeleteArgs>) { - db.run(`DELETE FROM ${String(table)} WHERE ${objectToWhereClause(args.where)}`) + db.exec(`DELETE FROM ${String(table)} WHERE ${objectToWhereClause(args.where)}`) } return { diff --git a/src/libsql/tests/all.test.ts b/src/libsql/tests/all.test.ts index 8314b7b..e718b12 100644 --- a/src/libsql/tests/all.test.ts +++ b/src/libsql/tests/all.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'bun:test' -import sql from 'sql.js' +import Database from 'libsql' import Sibyl from '../index' interface TableRow { @@ -14,12 +14,7 @@ interface Tables { describe('all tests', () => { it('returns all data available in the given table', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') const { createTable, Insert, All } = await Sibyl(db) createTable('first', { @@ -68,12 +63,7 @@ describe('all tests', () => { expect(actual).toStrictEqual(expectation) }) it('returns all data available in the given table and sorts then in ascending order by ID', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') const { createTable, Insert, All } = await Sibyl(db) createTable('first', { @@ -126,12 +116,7 @@ describe('all tests', () => { expect(actual).toStrictEqual(expectation) }) it('returns all data available in the given table and sorts then in descending order by ID', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') const { createTable, Insert, All } = await Sibyl(db) createTable('first', { diff --git a/src/libsql/tests/delete.test.ts b/src/libsql/tests/delete.test.ts index a0c38ce..e3e7d66 100644 --- a/src/libsql/tests/delete.test.ts +++ b/src/libsql/tests/delete.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'bun:test' -import sql from 'sql.js' +import Database from 'libsql' import Sibyl from '../index' interface TableRow { @@ -14,12 +14,7 @@ interface Tables { describe('delete tests', () => { it('deletes an entry in the DB', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') const { createTable, Insert, All, Delete } = await Sibyl(db) createTable('first', { diff --git a/src/libsql/tests/select.test.ts b/src/libsql/tests/select.test.ts index c648724..bd8188d 100644 --- a/src/libsql/tests/select.test.ts +++ b/src/libsql/tests/select.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'bun:test' -import sql from 'sql.js' +import Database from 'libsql' import Sibyl from '../index' import type { SibylResponse } from '../../types' @@ -16,12 +16,7 @@ interface Tables { describe('select tests', () => { it('select an entry in the DB', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') const { createTable, Create, Select } = await Sibyl(db) createTable('first', { @@ -63,12 +58,7 @@ describe('select tests', () => { expect(actual).toStrictEqual(expectation) }) it('selects multiple entries in the DB', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') const { createTable, Insert, Select } = await Sibyl(db) createTable('first', { @@ -126,12 +116,7 @@ describe('select tests', () => { expect(actual).toStrictEqual(expectation) }) it('selects multiple entries with the OR statement', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') const { createTable, Insert, Select } = await Sibyl(db) createTable('first', { @@ -202,12 +187,7 @@ describe('select tests', () => { expect(actual).toStrictEqual(expectation) }) it('selects multiple entries with the OR statement (mixed object)', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') const { createTable, Insert, Select } = await Sibyl(db) createTable('first', { @@ -276,12 +256,7 @@ describe('select tests', () => { expect(actual).toStrictEqual(expectation) }) it('selects multiple entries with the OR statement and sorts them in ascending order by id', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') const { createTable, Insert, Select } = await Sibyl(db) createTable('first', { @@ -348,12 +323,7 @@ describe('select tests', () => { expect(actual).toStrictEqual(expectation) }) it('selects using boolean value', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') // Create table schema interface firstTable { id: number diff --git a/src/libsql/tests/update.test.ts b/src/libsql/tests/update.test.ts index c0f2d81..a7c1371 100644 --- a/src/libsql/tests/update.test.ts +++ b/src/libsql/tests/update.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'bun:test' -import sql from 'sql.js' +import Database from 'libsql' import Sibyl from '../index' import type { SibylResponse } from '../../types' @@ -16,12 +16,7 @@ interface Tables { describe('update tests', () => { it('updates an entry in the DB', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') const { createTable, Insert, Update } = await Sibyl(db) createTable('first', { From d5ad27e16cdcee175b4fb43c59fc17b24a215d8b Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Mon, 8 Apr 2024 19:19:36 +0100 Subject: [PATCH 03/15] test: :test_tube: update tests to use libsql Database type, tests now failing --- src/libsql/tests/create.test.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/libsql/tests/create.test.ts b/src/libsql/tests/create.test.ts index ea95017..c24ee5b 100644 --- a/src/libsql/tests/create.test.ts +++ b/src/libsql/tests/create.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'bun:test' -import sql from 'sql.js' +import Database from 'libsql' import Sibyl from '../index' import type { SibylResponse } from '../../types' @@ -16,12 +16,7 @@ interface Tables { describe('create tests', () => { it('creates a new entry in the DB', async () => { - const SQL = await sql({ - locateFile: () => { - return 'playground/public/sql-wasm.wasm' - }, - }) - const db = new SQL.Database() + const db = new Database(':memory:') const { createTable, Create } = await Sibyl(db) createTable('first', { From 9186e1a054baf2e9799ba4def8aabf3096656081 Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Tue, 9 Apr 2024 08:20:39 +0100 Subject: [PATCH 04/15] feat: :construction: add formatInsertStatement helper for libsql --- src/sibylLib.ts | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/sibylLib.ts b/src/sibylLib.ts index cba3ddf..033f457 100644 --- a/src/sibylLib.ts +++ b/src/sibylLib.ts @@ -190,3 +190,38 @@ function processNonPrimaryType(columnType: DBEntry>) { result += ' UNIQUE' return result } + +export function formatInsertStatementLibSQL>(table: string, structs: T[]) { + const sortedStructs = sortKeys(structs) + const flattenedInsert = sortedStructs.map(obj => Object.values(obj)) + const flattenedKeys = sortedStructs.map(obj => Object.keys(obj))[0] + let insertions: string = '' + insertions += `INSERT INTO ${table} ` + + let tableKeys = '' + for (const key of flattenedKeys) + tableKeys += `${key}, ` + + tableKeys = tableKeys.slice(0, -2) + tableKeys.trim() + tableKeys = `(${tableKeys}) ` + + insertions += tableKeys + insertions += 'VALUES ' + + for (const insert of flattenedInsert) { + let row: T | string[] = [] + for (const cell of insert) { + if (typeof cell !== 'string') + row = [...row, cell] + + if (typeof cell === 'string') + row = [...row, `\'${cell}\'`] + } + insertions += `(${row}), ` + } + insertions = insertions.slice(0, -2) + insertions.trim() + insertions += ';' + return insertions +} From 1272feb669d5cf74d271def92b2e6acbb7d08c7a Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Tue, 9 Apr 2024 08:21:06 +0100 Subject: [PATCH 05/15] test: :white_check_mark: add passing tests for formatInsertStatementLibSQL helper --- .../tests/formatInsertStatementLibSQL.test.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/libsql/tests/formatInsertStatementLibSQL.test.ts diff --git a/src/libsql/tests/formatInsertStatementLibSQL.test.ts b/src/libsql/tests/formatInsertStatementLibSQL.test.ts new file mode 100644 index 0000000..30fd750 --- /dev/null +++ b/src/libsql/tests/formatInsertStatementLibSQL.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, it } from 'bun:test' +import { formatInsertStatementLibSQL } from '../../sibylLib' + +interface TableRow { + id: number + location: string + name: string +} + +describe('formatInsertStatmentLibSQL tests', () => { + it('correctly formats a single INSERT statement for the DB', async () => { + const actual = formatInsertStatementLibSQL('test', [ + { + id: 1, + location: 'Brighton', + name: 'Craig', + }, + ]) + + expect(actual).toStrictEqual('INSERT INTO test (id, location, name) VALUES (1,\'Brighton\',\'Craig\');') + }) + it('correctly formats several INSERT statments for the DB', async () => { + const actual = formatInsertStatementLibSQL('test', [ + { + id: 1, + location: 'Brighton', + name: 'Craig', + }, + { + id: 2, + location: 'Cornwall', + name: 'Bob', + }, + ]) + + expect(actual).toStrictEqual('INSERT INTO test (id, location, name) VALUES (1,\'Brighton\',\'Craig\'), (2,\'Cornwall\',\'Bob\');') + }) + it('catches incorrect insert keys being the wrong way around and fixes itself', async () => { + const actual = formatInsertStatementLibSQL('test', [ + { + id: 1, + name: 'Craig', + location: 'Brighton', + }, + { + location: 'Cornwall', + id: 2, + name: 'Bob', + }, + ]) + + expect(actual).toStrictEqual('INSERT INTO test (id, location, name) VALUES (1,\'Brighton\',\'Craig\'), (2,\'Cornwall\',\'Bob\');') + }) +}) From 25f375efb251c853f96e9f5f9a5936be10e73e56 Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Tue, 9 Apr 2024 08:21:37 +0100 Subject: [PATCH 06/15] feat: :construction: passing test for create function --- src/libsql/index.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/libsql/index.ts b/src/libsql/index.ts index 90ec6a7..11ccfcf 100644 --- a/src/libsql/index.ts +++ b/src/libsql/index.ts @@ -6,7 +6,7 @@ import { convertBooleanValues, convertCreateTableStatement, convertToObjects, - formatInsertStatement, + formatInsertStatementLibSQL, objectToWhereClause, } from '../sibylLib' @@ -19,33 +19,30 @@ function createTable(table: T, tableRow: MappedTable(table: K, rows: AccessTable[]) { - const statement = formatInsertStatement(String(table), rows) + const statement = formatInsertStatementLibSQL(String(table), rows) db.exec(statement) } function Select(table: T, args: SelectArgs>>) { const query = buildSelectQuery(String(table), args) - const record = db.exec(query) + const record = db.prepare(query).get() - if (record[0]) { - return convertBooleanValues(convertToObjects>({ - columns: record[0].columns, - values: record[0].values, - })) - } + if (record !== undefined) + return record return undefined } function Create(table: T, entry: AccessTable) { - const statement = formatInsertStatement(String(table), [entry]) + const statement = formatInsertStatementLibSQL(String(table), [entry]) db.exec(statement) + const result = Select(table, { where: entry, }) if (result !== undefined) - return result[0] + return result return undefined } From bbb00886b7f33f3d6435505ef59e5bbbd0f75d96 Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Wed, 10 Apr 2024 08:16:28 +0100 Subject: [PATCH 07/15] feat: :construction: cast record response to SibylResponse> --- src/libsql/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsql/index.ts b/src/libsql/index.ts index 11ccfcf..4b67cf6 100644 --- a/src/libsql/index.ts +++ b/src/libsql/index.ts @@ -25,7 +25,7 @@ function Insert(table: K, rows: AccessTable[]) { function Select(table: T, args: SelectArgs>>) { const query = buildSelectQuery(String(table), args) - const record = db.prepare(query).get() + const record = db.prepare(query).get() as SibylResponse> if (record !== undefined) return record From 9921b8f03245174aaa4bb1ea77ef24ce2f1e1311 Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Wed, 10 Apr 2024 08:24:17 +0100 Subject: [PATCH 08/15] feat: :construction: fix return type for libsql Select function --- src/libsql/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsql/index.ts b/src/libsql/index.ts index 4b67cf6..3d2dec1 100644 --- a/src/libsql/index.ts +++ b/src/libsql/index.ts @@ -25,7 +25,7 @@ function Insert(table: K, rows: AccessTable[]) { function Select(table: T, args: SelectArgs>>) { const query = buildSelectQuery(String(table), args) - const record = db.prepare(query).get() as SibylResponse> + const record = db.prepare(query).all() as SibylResponse>[] if (record !== undefined) return record From c9c605ff9312f23a26cada7246bb24c662aeb7a6 Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Wed, 10 Apr 2024 08:26:03 +0100 Subject: [PATCH 09/15] feat: :construction: ensure Create only returns the first item in the array --- src/libsql/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsql/index.ts b/src/libsql/index.ts index 3d2dec1..f5a44c6 100644 --- a/src/libsql/index.ts +++ b/src/libsql/index.ts @@ -42,7 +42,7 @@ function Create(table: T, entry: AccessTable) { }) if (result !== undefined) - return result + return result[0] return undefined } From 006040734fa30d4cf07eec30c0c096d42c3372f5 Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Wed, 10 Apr 2024 08:30:42 +0100 Subject: [PATCH 10/15] feat: :sparkles: Add libSQL support - Sibyl now supports the libSQL implementation of SQLite --- .changeset/sweet-eagles-drop.md | 5 +++++ src/libsql/index.ts | 10 +++------- 2 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 .changeset/sweet-eagles-drop.md diff --git a/.changeset/sweet-eagles-drop.md b/.changeset/sweet-eagles-drop.md new file mode 100644 index 0000000..972e0c7 --- /dev/null +++ b/.changeset/sweet-eagles-drop.md @@ -0,0 +1,5 @@ +--- +"@crbroughton/sibyl": minor +--- + +Add libSQL support - Sibyl now supports the libSQL implementation of SQLite diff --git a/src/libsql/index.ts b/src/libsql/index.ts index f5a44c6..75ffa5c 100644 --- a/src/libsql/index.ts +++ b/src/libsql/index.ts @@ -58,14 +58,10 @@ function All(table: K, args?: { sort: Sort>[] - if (record[0]) { - return convertBooleanValues(convertToObjects>({ - columns: record[0].columns, - values: record[0].values, - })) - } + if (record !== undefined) + return record return undefined } From 5af2c8b8b97c9bfac9586f4cd81dcfb61fb7333a Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Wed, 10 Apr 2024 08:34:21 +0100 Subject: [PATCH 11/15] feat: :green_heart: update CI for libSQL dependencies --- .github/workflows/{vitest.yml => bun.yml} | 2 ++ 1 file changed, 2 insertions(+) rename .github/workflows/{vitest.yml => bun.yml} (91%) diff --git a/.github/workflows/vitest.yml b/.github/workflows/bun.yml similarity index 91% rename from .github/workflows/vitest.yml rename to .github/workflows/bun.yml index baa2ac9..8ec5df1 100644 --- a/.github/workflows/vitest.yml +++ b/.github/workflows/bun.yml @@ -26,6 +26,8 @@ jobs: run: cd src/sqljs && bun install - name: Install dependencies for Bun run: cd src/bun && bun install + - name: Install dependencies for libSQL + run: cd src/libsql && bun install - name: Run unit tests run: bun test - uses: actions/upload-artifact@v3 From f26089add6c6fc4eb9f86d220a2382591b843956 Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Thu, 11 Apr 2024 08:11:26 +0100 Subject: [PATCH 12/15] docs: :memo: sync readmes across implementations, add libsql information --- README.md | 15 ++++++++++++-- src/bun/README.md | 47 +++++++++++++++++++++++++++++++++++++++++--- src/libsql/README.md | 47 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 101 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3a68fba..48baef0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Sibyl -Sibyl is a lightweight SQLite query builder for SQL.js and Bun's sqlite3 driver, providing a Prisma-like query builder. Sibyl is in early development, +Sibyl is a lightweight SQLite query builder for libSQL, Bun's sqlite3 driver, and libSQL, providing a Prisma-like query builder. Sibyl is in early development, so expect breaking changes and rapid development. ## Getting Started @@ -29,11 +29,22 @@ Bun documentation. The Bun implemenation of Sibyl can be installed with the following command: ```bash -bun install @crbroughton/sibyl:bun +bun install @crbroughton/sibyl_bun ``` Sibyl will then accept the native Bun SQLite `Database`, again, see the Bun documentation. +#### libSQL Installation + +The libSQL implemenation of Sibyl can be installed +with the following command: + +```bash +bun install @crbroughton/sibyl_libsql libsql +``` +Sibyl will then accept libSQL `Database`, then see the +libSQL Getting Started Guide. + #### Getting Started To start off with Sibyl, you'll first have to ensure Sibyl is able to be run inside diff --git a/src/bun/README.md b/src/bun/README.md index e14e23d..48baef0 100644 --- a/src/bun/README.md +++ b/src/bun/README.md @@ -1,6 +1,6 @@ # Sibyl -Sibyl is a lightweight SQLite query builder for SQL.js and Bun's sqlite3 driver, providing a Prisma-like query builder. Sibyl is in early development, +Sibyl is a lightweight SQLite query builder for libSQL, Bun's sqlite3 driver, and libSQL, providing a Prisma-like query builder. Sibyl is in early development, so expect breaking changes and rapid development. ## Getting Started @@ -34,6 +34,17 @@ bun install @crbroughton/sibyl_bun Sibyl will then accept the native Bun SQLite `Database`, again, see the Bun documentation. +#### libSQL Installation + +The libSQL implemenation of Sibyl can be installed +with the following command: + +```bash +bun install @crbroughton/sibyl_libsql libsql +``` +Sibyl will then accept libSQL `Database`, then see the +libSQL Getting Started Guide. + #### Getting Started To start off with Sibyl, you'll first have to ensure Sibyl is able to be run inside @@ -89,12 +100,13 @@ createTable('firstTable', { // inferred table name and entry id: { autoincrement: true, type: 'INTEGER', // only allows for known data types ('int', 'char', 'blob') - nullable: false, primary: true, unique: true, }, job: { - type: 'char', + type: 'varchar', + size: 100, // specify the size of the varchar + nullable: true }, name: { type: 'char', @@ -222,6 +234,14 @@ const updatedEntry = Update('firstTable', { // infers the table and response typ } }) ``` +### Primary type + +Sibyl offers a custom type, called the 'primary' type. When using +this type, Sibyl will automatically set the entry to a primary key, +not nullable and unique. Sibyl will also ensure that the underlying +type changes, so your editor gives feedback about no longer requiring +you to manually set these keys. Currently the primary type is only +available as an integer type. ### Sibyl Responses @@ -230,6 +250,27 @@ when wanting to convert data types to TypeScript types; At the moment the custom only support boolean conversions from `boolean` to `0 | 1`. It's recommended to use this type as a wrapper, if you're ever using boolean values. +### Working With Reactivity + +When working with any front-end framework, you'll want to combine +Sibyl with your frameworks reactivity engine. I've provided some +examples in the playground, in this case using Vue, but in general +you should follow the following rules: + +- Sibyl is not responsive by default; You should aim for Sibyls +responses to end up in a reactive object (see ref for Vue). +- When working with your reactive state, it's good practice to ensure +that the states type is the same of that of the response type from +Sibyl +- Sibyl provides the `SibylResponse` type; You can use this type +as a 'wrapper' type like so: + +```typescript +const results = ref[]>([]) +``` +This ensures that when you work with the `results` array, it conforms +to the shape and type Sibyl will return. + ## Development To install dependencies: diff --git a/src/libsql/README.md b/src/libsql/README.md index e14e23d..48baef0 100644 --- a/src/libsql/README.md +++ b/src/libsql/README.md @@ -1,6 +1,6 @@ # Sibyl -Sibyl is a lightweight SQLite query builder for SQL.js and Bun's sqlite3 driver, providing a Prisma-like query builder. Sibyl is in early development, +Sibyl is a lightweight SQLite query builder for libSQL, Bun's sqlite3 driver, and libSQL, providing a Prisma-like query builder. Sibyl is in early development, so expect breaking changes and rapid development. ## Getting Started @@ -34,6 +34,17 @@ bun install @crbroughton/sibyl_bun Sibyl will then accept the native Bun SQLite `Database`, again, see the Bun documentation. +#### libSQL Installation + +The libSQL implemenation of Sibyl can be installed +with the following command: + +```bash +bun install @crbroughton/sibyl_libsql libsql +``` +Sibyl will then accept libSQL `Database`, then see the +libSQL Getting Started Guide. + #### Getting Started To start off with Sibyl, you'll first have to ensure Sibyl is able to be run inside @@ -89,12 +100,13 @@ createTable('firstTable', { // inferred table name and entry id: { autoincrement: true, type: 'INTEGER', // only allows for known data types ('int', 'char', 'blob') - nullable: false, primary: true, unique: true, }, job: { - type: 'char', + type: 'varchar', + size: 100, // specify the size of the varchar + nullable: true }, name: { type: 'char', @@ -222,6 +234,14 @@ const updatedEntry = Update('firstTable', { // infers the table and response typ } }) ``` +### Primary type + +Sibyl offers a custom type, called the 'primary' type. When using +this type, Sibyl will automatically set the entry to a primary key, +not nullable and unique. Sibyl will also ensure that the underlying +type changes, so your editor gives feedback about no longer requiring +you to manually set these keys. Currently the primary type is only +available as an integer type. ### Sibyl Responses @@ -230,6 +250,27 @@ when wanting to convert data types to TypeScript types; At the moment the custom only support boolean conversions from `boolean` to `0 | 1`. It's recommended to use this type as a wrapper, if you're ever using boolean values. +### Working With Reactivity + +When working with any front-end framework, you'll want to combine +Sibyl with your frameworks reactivity engine. I've provided some +examples in the playground, in this case using Vue, but in general +you should follow the following rules: + +- Sibyl is not responsive by default; You should aim for Sibyls +responses to end up in a reactive object (see ref for Vue). +- When working with your reactive state, it's good practice to ensure +that the states type is the same of that of the response type from +Sibyl +- Sibyl provides the `SibylResponse` type; You can use this type +as a 'wrapper' type like so: + +```typescript +const results = ref[]>([]) +``` +This ensures that when you work with the `results` array, it conforms +to the shape and type Sibyl will return. + ## Development To install dependencies: From ef7865abb6f9144444a035a9b4d1a08cddcf1ae1 Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Thu, 11 Apr 2024 08:18:08 +0100 Subject: [PATCH 13/15] feat: :construction: setup playground folder and dependencies --- libsql-playground/.gitignore | 175 ++++++++++++++++++++++++++++++++ libsql-playground/README.md | 15 +++ libsql-playground/bun.lockb | Bin 0 -> 6891 bytes libsql-playground/index.ts | 1 + libsql-playground/package.json | 14 +++ libsql-playground/tsconfig.json | 27 +++++ src/libsql/index.ts | 2 - 7 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 libsql-playground/.gitignore create mode 100644 libsql-playground/README.md create mode 100755 libsql-playground/bun.lockb create mode 100644 libsql-playground/index.ts create mode 100644 libsql-playground/package.json create mode 100644 libsql-playground/tsconfig.json diff --git a/libsql-playground/.gitignore b/libsql-playground/.gitignore new file mode 100644 index 0000000..9b1ee42 --- /dev/null +++ b/libsql-playground/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/libsql-playground/README.md b/libsql-playground/README.md new file mode 100644 index 0000000..56b6ca7 --- /dev/null +++ b/libsql-playground/README.md @@ -0,0 +1,15 @@ +# libsql-playground + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.1.0. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/libsql-playground/bun.lockb b/libsql-playground/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..69d3dbf6c88bd698c257846abcdab01250121a05 GIT binary patch literal 6891 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 Date: Thu, 11 Apr 2024 18:06:49 +0100 Subject: [PATCH 14/15] feat: :construction: setup initial playground for libsql --- libsql-playground/index.ts | 87 +++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/libsql-playground/index.ts b/libsql-playground/index.ts index f67b2c6..5da6268 100644 --- a/libsql-playground/index.ts +++ b/libsql-playground/index.ts @@ -1 +1,86 @@ -console.log("Hello via Bun!"); \ No newline at end of file +import Sibyl from '@crbroughton/sibyl_libsql' +import Database from 'libsql' + +const db = new Database(':memory:') + +// Create table schema +interface Tables { + firstTable: { + id: number + name: string + location: string + hasReadTheReadme: boolean + } +} +const { createTable, Insert, Select, All } = await Sibyl(db) + +createTable('firstTable', { + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + name: { + type: 'char', + }, + hasReadTheReadme: { + type: 'bool', + }, + location: { + type: 'char', + }, +}) + +Insert('firstTable', [ + { + id: 1, + hasReadTheReadme: true, + location: 'Brighton', + name: 'Craig', + }, + { + id: 2, + hasReadTheReadme: false, + location: 'Leeds', + name: 'Bob', + }, + { + id: 3, + hasReadTheReadme: true, + location: 'Brighton', + name: 'David', + }, +]) + +const allResponse = All('firstTable') +console.log(allResponse) + +const selectedResponse = Select('firstTable', { + where: { + id: 1, + }, +}) +console.log(selectedResponse) + +const selectedResponseWithMultiple = Select('firstTable', { + where: { + id: 1, + location: 'Brighton', + }, +}) +console.log(selectedResponseWithMultiple) + +const selectedREsponseWithORStatement = Select('firstTable', { + where: { + OR: [ + { + name: 'Craig', + }, + { + hasReadTheReadme: 1, + }, + ], + }, +}) +console.log('here', selectedREsponseWithORStatement) From 95b7786017d16581856bf8ad0f8a2ce6c83a1edc Mon Sep 17 00:00:00 2001 From: CRBroughton Date: Thu, 11 Apr 2024 18:30:09 +0100 Subject: [PATCH 15/15] feat: :heavy_plus_sign: add required dependencies to the playground --- libsql-playground/bun.lockb | Bin 6891 -> 7305 bytes libsql-playground/package.json | 1 + 2 files changed, 1 insertion(+) diff --git a/libsql-playground/bun.lockb b/libsql-playground/bun.lockb index 69d3dbf6c88bd698c257846abcdab01250121a05..ba1c3bdd85fec35459ef7572f508f8abf264185b 100755 GIT binary patch delta 1397 zcmcgsYitZr6ux(MrtGubS9Nxaw4PNRyJg!&?XDmxMKykGQc|=YtyW8mbVJ%rXb_K7 zk4P116ZJ?0X%`W1rHE&dkj5hfKd1x|)DPv{*&Pjuf8r!}zVFOA_sqFx&beK#O=D}N zq;ux2Z|>Gr4!CycMzJ;OSU~;q!HlY}?zuIF*1+QFCkB7B6Io2#{R69~nZio=X(S|A zQj}k@qJ$7FB_tO0&uC)!9MOt+9Wf5E1+gDuBO(R~Zbq~q1`)N06^MGoA&4R(#wHp> z_H~AaXub$c@J03Lj&)jcc7E^a-0A*!NX+=kSnA?6XmP%EQBpQ z7^{LT{{mT7hDbbyOpnIJGG-NKI{uAnnV3;gLVPQpM)$E9PSj=$LC<)jd!~idhgKRov ztn4wkn2#?~>MhJMQDA00Of;q2n1kZTB3_I*9<{)|WA zi@)t2TiR=XjvrzZy^N|KZrD-K)NA<+=geZZas%v4t7vVHzZ&Ybvy-qHRt7VM=D9F4 z2O;uQqh-yzCAT-4x)M3+M8oJbLQaF$BHEQ<-*>Y<(75z|5l5@sl1p+r(C)Q}gXQ7J z{pzxet8a4}&Y;~TrKaJbyD?QJ?pRoB@xeul!`6*I1WO%U?{?jym4==|yr-Caw1dW) zCHe}=^UKRtEh$`CRywMpC_hj#FMPQH744zr)>2i<-#kX8{I($_RdPzH(8o3%YGUJ| PNsJ1;iW^2@wypjr>5wU+ delta 1098 zcmc(e-%Aux6vywKAGo?R`^%lxoi!{>QOjvvTYpsUE}3L3B9gMCC23SjR;&`(57I!B z2tzwz;!7eu^aD~_1fdrrGam{psF#EciqP=ICnK=Vot@QxAn3sG`JQ_Z_nvd+o?B%< z{exPb_M2qY=eFMHY7Si~?f-r|dZw;7f8Ww_jqTC>zRaBUMwRGhABtplSKH%`u|!Bu zds|Cqw4IPOTymkmXz&eEL4N|7jvPUzA#Wlb$PT0vc@}9#HXvoB2bqGL=LtzhrU`LM zNV5$W?x2v6ERz(FX(M#eT+@T>p543pPPf1qt)khmOoO6{5>gGTbiY`?sm4)-^|e)0 zwYmy$gw*J209B=~KA<|n6!pL|AEd`1PY7~%c~}tw+l z5sP7on&=OBER?_`6}qtm!al5nrL5b(Ag0cGMKDQX#gF;e8l8zBgzUzpxc#3*lH!-! zV`WL-cz9eo)vb;H83M7aLHT|OSOh5oU|N@nXGm`WzeYS zp&jZRmCGn#h{V!&gU^i?zpM1cmUuG3#YPIn)M~sU@2Mtbfx+ zK!+2CQ=7ozRQ-wPSeh3({%Uqx1xK5_npfLPNaDe^{<_v91g8#j)Q1~BJf;IqINhGa z1Fv-yT(&Np3kcM!6={Wqgp9zD(@iJfDMls6<2+6N)PL{P*jCgcd~v!n*6=(wPQ%5W z&tGTE#kTh6dC-(}k3J6=Ftn#(AZMz)jHB0mUc5#tN`q#_O(nRhxOXOoCHdlkxs}?{ z7Z~N&e8pJD3=W@e3QQ{@oGC(JUGb`)jq&O&^}nTEmJKfsF|(s89CU?Y(&LG}bY0+M OvuXkUCbbHp^2lGd>%{#4 diff --git a/libsql-playground/package.json b/libsql-playground/package.json index a565df2..a10a74d 100644 --- a/libsql-playground/package.json +++ b/libsql-playground/package.json @@ -6,6 +6,7 @@ "typescript": "^5.0.0" }, "dependencies": { + "@crbroughton/sibyl_libsql": "^2.1.2", "libsql": "^0.3.11" }, "devDependencies": {