@pikku/kysely-node-sqlite 0.12.3 → 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,39 @@
1
1
  # @pikku/kysely-node-sqlite
2
2
 
3
+ ## 0.12.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 9d62571: Make user-defined SQL functions an explicit, checked capability of the SQLite drivers.
8
+
9
+ `node:sqlite` can register scalar UDFs (`db.function()`); `bun:sqlite` cannot. Until now
10
+ neither driver mentioned that, so an app that registered a UDF by reaching for the raw
11
+ connection worked on Node and, on bun, failed as `no such function: …` from whichever
12
+ query used it — typically one endpoint breaking in production while everything else
13
+ looked healthy.
14
+
15
+ Both drivers now export `registerSqliteFunctions(db, functions)` and accept a
16
+ `functions` option on `createNodeSqliteKysely` / `createBunSqliteKysely`. The Node
17
+ implementation registers them as deterministic; the bun implementation throws
18
+ `SqliteFunctionsUnsupportedError` — exported from `@pikku/kysely-sqlite`, and naming
19
+ every function requested — so the incompatibility surfaces at wiring time with a message
20
+ saying what to do about it.
21
+
22
+ - Updated dependencies [32277d5]
23
+ - Updated dependencies [ea8aabf]
24
+ - Updated dependencies [33e96ab]
25
+ - Updated dependencies [fd72e58]
26
+ - Updated dependencies [cabd9dc]
27
+ - Updated dependencies [fd72e58]
28
+ - Updated dependencies [fd72e58]
29
+ - Updated dependencies [894b2f8]
30
+ - Updated dependencies [dd19aa7]
31
+ - Updated dependencies [50ec500]
32
+ - Updated dependencies [9d62571]
33
+ - @pikku/core@0.12.75
34
+ - @pikku/kysely@0.13.8
35
+ - @pikku/kysely-sqlite@0.12.10
36
+
3
37
  ## 0.12.3
4
38
 
5
39
  ### 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,59 @@
1
+ # @pikku/kysely-node-sqlite
2
+
3
+ Kysely driver for Pikku backed by Node's built-in `node: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
+ npm install @pikku/kysely-node-sqlite
13
+ ```
14
+
15
+ Requires a Node version with `node:sqlite` available.
16
+
17
+ ## Usage
18
+
19
+ ```typescript
20
+ import { createNodeSqliteKysely } from '@pikku/kysely-node-sqlite'
21
+ import type { KyselyPikkuDB } from '@pikku/kysely'
22
+
23
+ const db = createNodeSqliteKysely<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
32
+
33
+ `node:sqlite` can register scalar functions, so SQL can call your own code:
34
+
35
+ ```typescript
36
+ const db = createNodeSqliteKysely<KyselyPikkuDB>({
37
+ filename: 'app.db',
38
+ functions: {
39
+ similarity: (a, b) => trigramSimilarity(String(a), String(b)),
40
+ },
41
+ })
42
+ ```
43
+
44
+ They are registered as **deterministic**, which is what allows SQLite to use
45
+ them in an index and to cache repeated calls. Do not register a function whose
46
+ result depends on the clock, a random source, or anything mutable.
47
+
48
+ If you build the connection yourself — as you must when you need pragmas like
49
+ WAL or `busy_timeout` — call `registerSqliteFunctions(db, { … })` on it directly.
50
+
51
+ **This option has no bun equivalent.** `bun:sqlite` cannot register functions at
52
+ all, so `@pikku/kysely-bun-sqlite` throws `SqliteFunctionsUnsupportedError`
53
+ rather than accepting them. Using this is a decision to stay on Node; if you may
54
+ want to move, do the computation outside SQL over an indexed candidate set
55
+ instead.
56
+
57
+ ## Docs
58
+
59
+ 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 CreateNodeSqliteKyselyOptions {
3
4
  /** Path to the SQLite file. Use ':memory:' for an in-memory DB. */
4
5
  filename: string;
@@ -6,5 +7,14 @@ export interface CreateNodeSqliteKyselyOptions {
6
7
  camelCase?: boolean;
7
8
  /** Extra plugins to layer on top. */
8
9
  plugins?: KyselyPlugin[];
10
+ /**
11
+ * Scalar user-defined SQL functions to register, keyed by the name SQL calls
12
+ * them by. Registered as deterministic.
13
+ *
14
+ * Note that this is the one option with no bun equivalent:
15
+ * `createBunSqliteKysely` throws if it is set, because bun:sqlite cannot
16
+ * register functions. Using it is a decision to stay on Node.
17
+ */
18
+ functions?: SqliteFunctionMap;
9
19
  }
10
20
  export declare function createNodeSqliteKysely<DB>(options: CreateNodeSqliteKyselyOptions): Kysely<DB>;
@@ -1,8 +1,11 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
2
  import { Kysely, SqliteDialect, CamelCasePlugin, } from 'kysely';
3
3
  import { NodeSqliteDatabase } from './node-sqlite-adapter.js';
4
+ import { registerSqliteFunctions } from './register-functions.js';
4
5
  export function createNodeSqliteKysely(options) {
5
6
  const db = new DatabaseSync(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 { NodeSqliteDatabase } from './node-sqlite-adapter.js';
2
2
  export { createNodeSqliteKysely, type CreateNodeSqliteKyselyOptions, } from './create-node-sqlite-kysely.js';
3
+ export { registerSqliteFunctions } from './register-functions.js';
4
+ export { SqliteFunctionsUnsupportedError, type SqliteFunction, type SqliteFunctionMap, } from '@pikku/kysely-sqlite';
3
5
  export { createCoercionPlugin, type CoercionMap, type ColumnKind, type CreateCoercionPluginOptions, } from './coercion-plugin.js';
package/dist/src/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  export { NodeSqliteDatabase } from './node-sqlite-adapter.js';
2
2
  export { createNodeSqliteKysely, } from './create-node-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,19 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ import type { SqliteFunctionMap } from '@pikku/kysely-sqlite';
3
+ /**
4
+ * Registers scalar user-defined SQL functions on a node:sqlite connection.
5
+ *
6
+ * Exported separately from `createNodeSqliteKysely` because an app that needs
7
+ * pragmas — WAL, `foreign_keys`, `busy_timeout` — has to build the connection
8
+ * itself, and would otherwise have no supported way to add UDFs to it.
9
+ *
10
+ * Registered as `deterministic`, which is what makes them usable in indexes and
11
+ * lets SQLite cache repeated calls with the same arguments. That is a promise
12
+ * about the function, not a hint: one whose output depends on the clock, a
13
+ * random source or anything mutable must not go through here.
14
+ *
15
+ * There is no bun counterpart. `@pikku/kysely-bun-sqlite` exports the same name
16
+ * so the import resolves either way, but it throws — see
17
+ * SqliteFunctionsUnsupportedError.
18
+ */
19
+ export declare function registerSqliteFunctions(db: DatabaseSync, functions: SqliteFunctionMap): void;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Registers scalar user-defined SQL functions on a node:sqlite connection.
3
+ *
4
+ * Exported separately from `createNodeSqliteKysely` because an app that needs
5
+ * pragmas — WAL, `foreign_keys`, `busy_timeout` — has to build the connection
6
+ * itself, and would otherwise have no supported way to add UDFs to it.
7
+ *
8
+ * Registered as `deterministic`, which is what makes them usable in indexes and
9
+ * lets SQLite cache repeated calls with the same arguments. That is a promise
10
+ * about the function, not a hint: one whose output depends on the clock, a
11
+ * random source or anything mutable must not go through here.
12
+ *
13
+ * There is no bun counterpart. `@pikku/kysely-bun-sqlite` exports the same name
14
+ * so the import resolves either way, but it throws — see
15
+ * SqliteFunctionsUnsupportedError.
16
+ */
17
+ export function registerSqliteFunctions(db, functions) {
18
+ for (const [name, fn] of Object.entries(functions)) {
19
+ db.function(name, { deterministic: true }, fn);
20
+ }
21
+ }
@@ -1 +1 @@
1
- {"root":["../src/coercion-plugin.ts","../src/create-node-sqlite-kysely.ts","../src/index.ts","../src/node-sqlite-adapter.ts"],"version":"6.0.3"}
1
+ {"root":["../src/coercion-plugin.ts","../src/create-node-sqlite-kysely.ts","../src/index.ts","../src/node-sqlite-adapter.ts","../src/register-functions.ts"],"version":"6.0.3"}
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@pikku/kysely-node-sqlite",
3
- "version": "0.12.3",
3
+ "version": "0.12.4",
4
+ "description": "Kysely driver for Pikku backed by the built-in node:sqlite module",
4
5
  "author": "yasser.fadl@gmail.com",
5
6
  "license": "MIT",
6
7
  "module": "dist/src/index.js",
@@ -10,22 +11,26 @@
10
11
  "tsc": "tsc",
11
12
  "ncu": "npx npm-check-updates",
12
13
  "build": "tsc -b",
14
+ "test": "bash run-tests.sh",
15
+ "test:watch": "bash run-tests.sh --watch",
16
+ "test:coverage": "bash run-tests.sh --coverage",
13
17
  "prepublishOnly": "yarn build"
14
18
  },
15
19
  "engines": {
16
20
  "node": ">=22.10"
17
21
  },
18
22
  "dependencies": {
19
- "@pikku/kysely": "^0.13.0",
20
- "@pikku/kysely-sqlite": "^0.12.7",
23
+ "@pikku/kysely": "^0.13.8",
24
+ "@pikku/kysely-sqlite": "^0.12.10",
21
25
  "kysely": "^0.29.0"
22
26
  },
23
27
  "peerDependencies": {
24
- "@pikku/core": "^0.12.44"
28
+ "@pikku/core": "^0.12.75"
25
29
  },
26
30
  "devDependencies": {
27
- "@pikku/core": "^0.12.44",
31
+ "@pikku/core": "^0.12.75",
28
32
  "@types/node": "^22",
33
+ "tsx": "^4.19.2",
29
34
  "typescript": "^6.0.3"
30
35
  },
31
36
  "exports": {
package/run-tests.sh ADDED
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+
3
+ # Parse command line arguments
4
+ WATCH_MODE=false
5
+ COVERAGE_MODE=false
6
+
7
+ for arg in "$@"
8
+ do
9
+ case $arg in
10
+ --watch)
11
+ WATCH_MODE=true
12
+ shift
13
+ ;;
14
+ --coverage)
15
+ COVERAGE_MODE=true
16
+ shift
17
+ ;;
18
+ esac
19
+ done
20
+
21
+ # Build the command
22
+ CMD="node --import tsx --test --test-force-exit src/**/*.test.ts"
23
+
24
+ if [ "$WATCH_MODE" = true ]; then
25
+ CMD="$CMD --watch"
26
+ fi
27
+
28
+ if [ "$COVERAGE_MODE" = true ]; then
29
+ CMD="$CMD --experimental-test-coverage"
30
+ fi
31
+
32
+ # Execute the command
33
+ eval $CMD
@@ -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 { NodeSqliteDatabase } from './node-sqlite-adapter.js'
10
+ import { registerSqliteFunctions } from './register-functions.js'
9
11
 
10
12
  export interface CreateNodeSqliteKyselyOptions {
11
13
  /** Path to the SQLite file. Use ':memory:' for an in-memory DB. */
@@ -14,12 +16,22 @@ export interface CreateNodeSqliteKyselyOptions {
14
16
  camelCase?: boolean
15
17
  /** Extra plugins to layer on top. */
16
18
  plugins?: KyselyPlugin[]
19
+ /**
20
+ * Scalar user-defined SQL functions to register, keyed by the name SQL calls
21
+ * them by. Registered as deterministic.
22
+ *
23
+ * Note that this is the one option with no bun equivalent:
24
+ * `createBunSqliteKysely` throws if it is set, because bun:sqlite cannot
25
+ * register functions. Using it is a decision to stay on Node.
26
+ */
27
+ functions?: SqliteFunctionMap
17
28
  }
18
29
 
19
30
  export function createNodeSqliteKysely<DB>(
20
31
  options: CreateNodeSqliteKyselyOptions
21
32
  ): Kysely<DB> {
22
33
  const db = new DatabaseSync(options.filename)
34
+ if (options.functions) registerSqliteFunctions(db, options.functions)
23
35
  const plugins: KyselyPlugin[] = []
24
36
  if (options.camelCase ?? true) plugins.push(new CamelCasePlugin())
25
37
  if (options.plugins) plugins.push(...options.plugins)
package/src/index.ts CHANGED
@@ -3,6 +3,12 @@ export {
3
3
  createNodeSqliteKysely,
4
4
  type CreateNodeSqliteKyselyOptions,
5
5
  } from './create-node-sqlite-kysely.js'
6
+ export { registerSqliteFunctions } from './register-functions.js'
7
+ export {
8
+ SqliteFunctionsUnsupportedError,
9
+ type SqliteFunction,
10
+ type SqliteFunctionMap,
11
+ } from '@pikku/kysely-sqlite'
6
12
  export {
7
13
  createCoercionPlugin,
8
14
  type CoercionMap,
@@ -0,0 +1,52 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { DatabaseSync } from 'node:sqlite'
4
+ import { sql } from 'kysely'
5
+ import { registerSqliteFunctions } from './register-functions.js'
6
+ import { createNodeSqliteKysely } from './create-node-sqlite-kysely.js'
7
+
8
+ describe('registerSqliteFunctions', () => {
9
+ test('makes the function callable from SQL', () => {
10
+ const db = new DatabaseSync(':memory:')
11
+ registerSqliteFunctions(db, {
12
+ shout: (value) => `${String(value).toUpperCase()}!`,
13
+ })
14
+
15
+ const row = db.prepare(`SELECT shout('hello') AS v`).get() as { v: string }
16
+ assert.equal(row.v, 'HELLO!')
17
+ db.close()
18
+ })
19
+
20
+ test('registers as deterministic, so it is usable in an index', () => {
21
+ const db = new DatabaseSync(':memory:')
22
+ registerSqliteFunctions(db, { half: (n) => Number(n) / 2 })
23
+
24
+ db.exec('CREATE TABLE t (n INTEGER)')
25
+ // SQLite rejects non-deterministic functions in an index outright, so this
26
+ // statement succeeding IS the assertion about how they were registered.
27
+ db.exec('CREATE INDEX t_half ON t (half(n))')
28
+ db.close()
29
+ })
30
+
31
+ test('registers every entry in the map', () => {
32
+ const db = new DatabaseSync(':memory:')
33
+ registerSqliteFunctions(db, { a: () => 1, b: () => 2 })
34
+
35
+ const row = db.prepare('SELECT a() + b() AS v').get() as { v: number }
36
+ assert.equal(row.v, 3)
37
+ db.close()
38
+ })
39
+
40
+ test('createNodeSqliteKysely registers them on the connection it builds', async () => {
41
+ const db = createNodeSqliteKysely<{ t: { id: number } }>({
42
+ filename: ':memory:',
43
+ functions: { double: (n) => Number(n) * 2 },
44
+ })
45
+
46
+ const result = await sql<{
47
+ v: number
48
+ }>`SELECT double(21) AS v`.execute(db)
49
+ assert.equal(result.rows[0]!.v, 42)
50
+ await db.destroy()
51
+ })
52
+ })
@@ -0,0 +1,27 @@
1
+ import type { DatabaseSync } from 'node:sqlite'
2
+ import type { SqliteFunctionMap } from '@pikku/kysely-sqlite'
3
+
4
+ /**
5
+ * Registers scalar user-defined SQL functions on a node:sqlite connection.
6
+ *
7
+ * Exported separately from `createNodeSqliteKysely` because an app that needs
8
+ * pragmas — WAL, `foreign_keys`, `busy_timeout` — has to build the connection
9
+ * itself, and would otherwise have no supported way to add UDFs to it.
10
+ *
11
+ * Registered as `deterministic`, which is what makes them usable in indexes and
12
+ * lets SQLite cache repeated calls with the same arguments. That is a promise
13
+ * about the function, not a hint: one whose output depends on the clock, a
14
+ * random source or anything mutable must not go through here.
15
+ *
16
+ * There is no bun counterpart. `@pikku/kysely-bun-sqlite` exports the same name
17
+ * so the import resolves either way, but it throws — see
18
+ * SqliteFunctionsUnsupportedError.
19
+ */
20
+ export function registerSqliteFunctions(
21
+ db: DatabaseSync,
22
+ functions: SqliteFunctionMap
23
+ ): void {
24
+ for (const [name, fn] of Object.entries(functions)) {
25
+ db.function(name, { deterministic: true }, fn as never)
26
+ }
27
+ }