@pikku/kysely-bun-sqlite 0.12.2 → 0.12.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @pikku/kysely-bun-sqlite
2
2
 
3
+ ## 0.12.4
4
+
5
+ ### Patch Changes
6
+
7
+ - fd9d834: Stop publishing internals that only their own package or file used. The declarations stay; only the entrypoint re-export is removed, so nothing that imported a name from where it is declared is affected.
8
+ - Updated dependencies [fd9d834]
9
+ - @pikku/kysely@0.13.15
10
+ - @pikku/kysely-sqlite@0.12.11
11
+
12
+ ## 0.12.3
13
+
14
+ ### Patch Changes
15
+
16
+ - 9d62571: Make user-defined SQL functions an explicit, checked capability of the SQLite drivers.
17
+
18
+ `node:sqlite` can register scalar UDFs (`db.function()`); `bun:sqlite` cannot. Until now
19
+ neither driver mentioned that, so an app that registered a UDF by reaching for the raw
20
+ connection worked on Node and, on bun, failed as `no such function: …` from whichever
21
+ query used it — typically one endpoint breaking in production while everything else
22
+ looked healthy.
23
+
24
+ Both drivers now export `registerSqliteFunctions(db, functions)` and accept a
25
+ `functions` option on `createNodeSqliteKysely` / `createBunSqliteKysely`. The Node
26
+ implementation registers them as deterministic; the bun implementation throws
27
+ `SqliteFunctionsUnsupportedError` — exported from `@pikku/kysely-sqlite`, and naming
28
+ every function requested — so the incompatibility surfaces at wiring time with a message
29
+ saying what to do about it.
30
+
31
+ - Updated dependencies [cabd9dc]
32
+ - Updated dependencies [9d62571]
33
+ - @pikku/kysely@0.13.8
34
+ - @pikku/kysely-sqlite@0.12.10
35
+
3
36
  ## 0.12.2
4
37
 
5
38
  ### Patch Changes
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 - present Yasser Fadl and Pikku contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # @pikku/kysely-bun-sqlite
2
+
3
+ Kysely driver for Pikku backed by Bun's built-in `bun:sqlite` module — no
4
+ native dependency to install or compile.
5
+
6
+ Ships a coercion plugin that maps SQLite's storage classes back to the types
7
+ your schema declares (booleans, dates, JSON).
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ bun add @pikku/kysely-bun-sqlite
13
+ ```
14
+
15
+ Bun only — for Node use `@pikku/kysely-node-sqlite`.
16
+
17
+ ## Usage
18
+
19
+ ```typescript
20
+ import { createBunSqliteKysely } from '@pikku/kysely-bun-sqlite'
21
+ import type { KyselyPikkuDB } from '@pikku/kysely'
22
+
23
+ const db = createBunSqliteKysely<KyselyPikkuDB>({
24
+ filename: 'app.db',
25
+ })
26
+ ```
27
+
28
+ `camelCase` (default `true`) applies Kysely's `CamelCasePlugin`; pass extra
29
+ `plugins` to layer more on top. Use `':memory:'` for an in-memory database.
30
+
31
+ ## User-defined SQL functions are not supported
32
+
33
+ `bun:sqlite` has no equivalent of `node:sqlite`'s `db.function()`, so scalar
34
+ UDFs cannot be registered on this driver. Passing `functions` — or calling
35
+ `registerSqliteFunctions` — throws `SqliteFunctionsUnsupportedError` immediately,
36
+ naming every function requested.
37
+
38
+ That is deliberate. The alternative is code that registers its functions on Node
39
+ and silently does not on bun, where the difference only appears as
40
+ `no such function: …` from whichever query calls one.
41
+
42
+ If you hit this, either run on Node with `@pikku/kysely-node-sqlite`, or move the
43
+ computation out of SQL: precompute what the function was matching on into an
44
+ indexed table, use that index to narrow to a candidate set, and run the real
45
+ logic over those rows in TypeScript.
46
+
47
+ ## Docs
48
+
49
+ https://pikku.dev/docs
@@ -1,4 +1,5 @@
1
1
  import { Kysely, type KyselyPlugin } from 'kysely';
2
+ import type { SqliteFunctionMap } from '@pikku/kysely-sqlite';
2
3
  export interface CreateBunSqliteKyselyOptions {
3
4
  /** Path to the SQLite file. Use ':memory:' for an in-memory DB. */
4
5
  filename: string;
@@ -6,5 +7,16 @@ export interface CreateBunSqliteKyselyOptions {
6
7
  camelCase?: boolean;
7
8
  /** Extra plugins to layer on top. */
8
9
  plugins?: KyselyPlugin[];
10
+ /**
11
+ * Accepted only so that setting it fails loudly. bun:sqlite cannot register
12
+ * user-defined SQL functions, so passing any throws
13
+ * SqliteFunctionsUnsupportedError here rather than leaving the queries that
14
+ * call them to fail with "no such function" later.
15
+ *
16
+ * The option is declared — instead of simply absent — so that code shared
17
+ * with `createNodeSqliteKysely` still type-checks and the incompatibility
18
+ * shows up as an error that explains itself.
19
+ */
20
+ functions?: SqliteFunctionMap;
9
21
  }
10
22
  export declare function createBunSqliteKysely<DB>(options: CreateBunSqliteKyselyOptions): Kysely<DB>;
@@ -1,8 +1,11 @@
1
1
  import { Database } from 'bun:sqlite';
2
2
  import { Kysely, SqliteDialect, CamelCasePlugin, } from 'kysely';
3
3
  import { BunSqliteDatabase } from './bun-sqlite-adapter.js';
4
+ import { registerSqliteFunctions } from './register-functions.js';
4
5
  export function createBunSqliteKysely(options) {
5
6
  const db = new Database(options.filename);
7
+ if (options.functions)
8
+ registerSqliteFunctions(db, options.functions);
6
9
  const plugins = [];
7
10
  if (options.camelCase ?? true)
8
11
  plugins.push(new CamelCasePlugin());
@@ -1,3 +1,5 @@
1
1
  export { BunSqliteDatabase } from './bun-sqlite-adapter.js';
2
2
  export { createBunSqliteKysely, type CreateBunSqliteKyselyOptions, } from './create-bun-sqlite-kysely.js';
3
- export { createCoercionPlugin, type CoercionMap, type ColumnKind, type CreateCoercionPluginOptions, } from './coercion-plugin.js';
3
+ export { registerSqliteFunctions } from './register-functions.js';
4
+ export { SqliteFunctionsUnsupportedError, type SqliteFunctionMap, } from '@pikku/kysely-sqlite';
5
+ export { createCoercionPlugin, type ColumnKind, type CreateCoercionPluginOptions, } from './coercion-plugin.js';
package/dist/src/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  export { BunSqliteDatabase } from './bun-sqlite-adapter.js';
2
2
  export { createBunSqliteKysely, } from './create-bun-sqlite-kysely.js';
3
+ export { registerSqliteFunctions } from './register-functions.js';
4
+ export { SqliteFunctionsUnsupportedError, } from '@pikku/kysely-sqlite';
3
5
  export { createCoercionPlugin, } from './coercion-plugin.js';
@@ -0,0 +1,18 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import { type SqliteFunctionMap } from '@pikku/kysely-sqlite';
3
+ /**
4
+ * The bun counterpart to `@pikku/kysely-node-sqlite`'s function of the same
5
+ * name. It always throws: bun:sqlite exposes `loadExtension` but nothing
6
+ * equivalent to node:sqlite's `db.function()`, so scalar UDFs cannot be
7
+ * registered at all.
8
+ *
9
+ * It exists precisely so that the failure happens here. Without it the two
10
+ * drivers differ silently — code written against node registers its functions,
11
+ * the same code on bun does not, and the difference only surfaces as
12
+ * `no such function: …` from whichever query calls one, which in practice means
13
+ * one endpoint failing in production while the rest of the app looks healthy.
14
+ *
15
+ * Throwing at wiring time turns that into a startup error naming every function
16
+ * involved.
17
+ */
18
+ export declare function registerSqliteFunctions(_db: Database, functions: SqliteFunctionMap): never;
@@ -0,0 +1,19 @@
1
+ import { SqliteFunctionsUnsupportedError, } from '@pikku/kysely-sqlite';
2
+ /**
3
+ * The bun counterpart to `@pikku/kysely-node-sqlite`'s function of the same
4
+ * name. It always throws: bun:sqlite exposes `loadExtension` but nothing
5
+ * equivalent to node:sqlite's `db.function()`, so scalar UDFs cannot be
6
+ * registered at all.
7
+ *
8
+ * It exists precisely so that the failure happens here. Without it the two
9
+ * drivers differ silently — code written against node registers its functions,
10
+ * the same code on bun does not, and the difference only surfaces as
11
+ * `no such function: …` from whichever query calls one, which in practice means
12
+ * one endpoint failing in production while the rest of the app looks healthy.
13
+ *
14
+ * Throwing at wiring time turns that into a startup error naming every function
15
+ * involved.
16
+ */
17
+ export function registerSqliteFunctions(_db, functions) {
18
+ throw new SqliteFunctionsUnsupportedError('bun:sqlite', Object.keys(functions));
19
+ }
@@ -1 +1 @@
1
- {"root":["../src/bun-sqlite-adapter.ts","../src/coercion-plugin.ts","../src/create-bun-sqlite-kysely.ts","../src/index.ts"],"version":"6.0.3"}
1
+ {"root":["../src/bun-sqlite-adapter.ts","../src/coercion-plugin.ts","../src/create-bun-sqlite-kysely.ts","../src/index.ts","../src/register-functions.ts"],"version":"6.0.3"}
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@pikku/kysely-bun-sqlite",
3
- "version": "0.12.2",
3
+ "version": "0.12.4",
4
+ "description": "Kysely driver for Pikku backed by the built-in bun:sqlite module",
4
5
  "author": "yasser.fadl@gmail.com",
5
6
  "license": "MIT",
6
7
  "module": "dist/src/index.js",
@@ -18,8 +19,8 @@
18
19
  "bun": ">=1.0.0"
19
20
  },
20
21
  "dependencies": {
21
- "@pikku/kysely": "^0.13.0",
22
- "@pikku/kysely-sqlite": "^0.12.7",
22
+ "@pikku/kysely": "^0.13.15",
23
+ "@pikku/kysely-sqlite": "^0.12.11",
23
24
  "kysely": "^0.29.0"
24
25
  },
25
26
  "devDependencies": {
@@ -27,10 +27,9 @@ describe('createCoercionPlugin', () => {
27
27
  })
28
28
 
29
29
  test('coerces date columns from ISO strings to Date', async () => {
30
- const [row] = await runTransform(
31
- { users: { created_at: 'date' } },
32
- [{ created_at: '2026-06-26T00:00:00.000Z' }]
33
- )
30
+ const [row] = await runTransform({ users: { created_at: 'date' } }, [
31
+ { created_at: '2026-06-26T00:00:00.000Z' },
32
+ ])
34
33
  assert.ok(row.created_at instanceof Date)
35
34
  assert.equal(
36
35
  (row.created_at as Date).toISOString(),
@@ -39,10 +38,9 @@ describe('createCoercionPlugin', () => {
39
38
  })
40
39
 
41
40
  test('leaves unparseable date strings untouched', async () => {
42
- const [row] = await runTransform(
43
- { users: { created_at: 'date' } },
44
- [{ created_at: 'not-a-date' }]
45
- )
41
+ const [row] = await runTransform({ users: { created_at: 'date' } }, [
42
+ { created_at: 'not-a-date' },
43
+ ])
46
44
  assert.equal(row.created_at, 'not-a-date')
47
45
  })
48
46
 
@@ -65,19 +63,17 @@ describe('createCoercionPlugin', () => {
65
63
  })
66
64
 
67
65
  test('passes through null values and unmapped columns', async () => {
68
- const rows = await runTransform(
69
- { users: { created_at: 'date' } },
70
- [{ created_at: null, name: 'leave me' }]
71
- )
66
+ const rows = await runTransform({ users: { created_at: 'date' } }, [
67
+ { created_at: null, name: 'leave me' },
68
+ ])
72
69
  assert.equal(rows[0].created_at, null)
73
70
  assert.equal(rows[0].name, 'leave me')
74
71
  })
75
72
 
76
73
  test('matches both snake_case and camelCase column names', async () => {
77
- const rows = await runTransform(
78
- { users: { created_at: 'date' } },
79
- [{ createdAt: '2026-06-26T00:00:00.000Z' }]
80
- )
74
+ const rows = await runTransform({ users: { created_at: 'date' } }, [
75
+ { createdAt: '2026-06-26T00:00:00.000Z' },
76
+ ])
81
77
  assert.ok(rows[0].createdAt instanceof Date)
82
78
  })
83
79
 
@@ -5,7 +5,9 @@ import {
5
5
  CamelCasePlugin,
6
6
  type KyselyPlugin,
7
7
  } from 'kysely'
8
+ import type { SqliteFunctionMap } from '@pikku/kysely-sqlite'
8
9
  import { BunSqliteDatabase } from './bun-sqlite-adapter.js'
10
+ import { registerSqliteFunctions } from './register-functions.js'
9
11
 
10
12
  export interface CreateBunSqliteKyselyOptions {
11
13
  /** Path to the SQLite file. Use ':memory:' for an in-memory DB. */
@@ -14,12 +16,24 @@ export interface CreateBunSqliteKyselyOptions {
14
16
  camelCase?: boolean
15
17
  /** Extra plugins to layer on top. */
16
18
  plugins?: KyselyPlugin[]
19
+ /**
20
+ * Accepted only so that setting it fails loudly. bun:sqlite cannot register
21
+ * user-defined SQL functions, so passing any throws
22
+ * SqliteFunctionsUnsupportedError here rather than leaving the queries that
23
+ * call them to fail with "no such function" later.
24
+ *
25
+ * The option is declared — instead of simply absent — so that code shared
26
+ * with `createNodeSqliteKysely` still type-checks and the incompatibility
27
+ * shows up as an error that explains itself.
28
+ */
29
+ functions?: SqliteFunctionMap
17
30
  }
18
31
 
19
32
  export function createBunSqliteKysely<DB>(
20
33
  options: CreateBunSqliteKyselyOptions
21
34
  ): Kysely<DB> {
22
35
  const db = new Database(options.filename)
36
+ if (options.functions) registerSqliteFunctions(db, options.functions)
23
37
  const plugins: KyselyPlugin[] = []
24
38
  if (options.camelCase ?? true) plugins.push(new CamelCasePlugin())
25
39
  if (options.plugins) plugins.push(...options.plugins)
package/src/index.ts CHANGED
@@ -3,9 +3,13 @@ export {
3
3
  createBunSqliteKysely,
4
4
  type CreateBunSqliteKyselyOptions,
5
5
  } from './create-bun-sqlite-kysely.js'
6
+ export { registerSqliteFunctions } from './register-functions.js'
7
+ export {
8
+ SqliteFunctionsUnsupportedError,
9
+ type SqliteFunctionMap,
10
+ } from '@pikku/kysely-sqlite'
6
11
  export {
7
12
  createCoercionPlugin,
8
- type CoercionMap,
9
13
  type ColumnKind,
10
14
  type CreateCoercionPluginOptions,
11
15
  } from './coercion-plugin.js'
@@ -0,0 +1,54 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { Database } from 'bun:sqlite'
4
+ import { SqliteFunctionsUnsupportedError } from '@pikku/kysely-sqlite'
5
+ import { registerSqliteFunctions } from './register-functions.js'
6
+ import { createBunSqliteKysely } from './create-bun-sqlite-kysely.js'
7
+
8
+ describe('registerSqliteFunctions', () => {
9
+ test('throws rather than silently skipping registration', () => {
10
+ const db = new Database(':memory:')
11
+ assert.throws(
12
+ () => registerSqliteFunctions(db, { similarity: () => 0 }),
13
+ SqliteFunctionsUnsupportedError
14
+ )
15
+ db.close()
16
+ })
17
+
18
+ test('names every requested function, so the error says what to port', () => {
19
+ const db = new Database(':memory:')
20
+ try {
21
+ registerSqliteFunctions(db, {
22
+ similarity: () => 0,
23
+ levenshtein: () => 0,
24
+ })
25
+ assert.fail('expected a throw')
26
+ } catch (error) {
27
+ assert.ok(error instanceof SqliteFunctionsUnsupportedError)
28
+ assert.deepEqual(error.functionNames, ['similarity', 'levenshtein'])
29
+ assert.match(error.message, /similarity, levenshtein/)
30
+ assert.match(error.message, /kysely-node-sqlite/)
31
+ }
32
+ db.close()
33
+ })
34
+
35
+ // The point of the whole change: the failure has to happen while the app is
36
+ // wiring itself up, not on the first request that reaches a query using one.
37
+ test('createBunSqliteKysely rejects `functions` at construction', () => {
38
+ assert.throws(
39
+ () =>
40
+ createBunSqliteKysely({
41
+ filename: ':memory:',
42
+ functions: { similarity: () => 0 },
43
+ }),
44
+ SqliteFunctionsUnsupportedError
45
+ )
46
+ })
47
+
48
+ test('omitting `functions` is unaffected', async () => {
49
+ const db = createBunSqliteKysely<{ t: { id: number } }>({
50
+ filename: ':memory:',
51
+ })
52
+ await db.destroy()
53
+ })
54
+ })
@@ -0,0 +1,30 @@
1
+ import type { Database } from 'bun:sqlite'
2
+ import {
3
+ SqliteFunctionsUnsupportedError,
4
+ type SqliteFunctionMap,
5
+ } from '@pikku/kysely-sqlite'
6
+
7
+ /**
8
+ * The bun counterpart to `@pikku/kysely-node-sqlite`'s function of the same
9
+ * name. It always throws: bun:sqlite exposes `loadExtension` but nothing
10
+ * equivalent to node:sqlite's `db.function()`, so scalar UDFs cannot be
11
+ * registered at all.
12
+ *
13
+ * It exists precisely so that the failure happens here. Without it the two
14
+ * drivers differ silently — code written against node registers its functions,
15
+ * the same code on bun does not, and the difference only surfaces as
16
+ * `no such function: …` from whichever query calls one, which in practice means
17
+ * one endpoint failing in production while the rest of the app looks healthy.
18
+ *
19
+ * Throwing at wiring time turns that into a startup error naming every function
20
+ * involved.
21
+ */
22
+ export function registerSqliteFunctions(
23
+ _db: Database,
24
+ functions: SqliteFunctionMap
25
+ ): never {
26
+ throw new SqliteFunctionsUnsupportedError(
27
+ 'bun:sqlite',
28
+ Object.keys(functions)
29
+ )
30
+ }