Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

@kanik/libsql-capacitor

Capacitor plugin for libSQL on Android and iOS. Designed to drop in under Drizzle ORM via the sqlite-proxy adapter, with NULL-safe parameter binding.

Adding this plugin to a project? Read INTEGRATION.md for the full step-by-step recipe.

Install

npx npm@10 install github:7999999166/kanik-libsql-capacitor#v0.3.4
npx cap sync

The plugin is published as a Git dependency. Use npx npm@10 — npm 11.12.0 ships a git-dep preparation bug (--prefer-online cannot be provided when using --prefer-offline).

Platforms

  • Android — Kotlin wrapper around tech.turso.libsql (minSdk 24).
  • iOS — Swift wrapper around libsql-swift (iOS 15+, SPM).
  • Web — not implemented; use a separate browser-side SQLite (e.g. wa-sqlite or Dexie).

API

import { Libsql } from '@kanik/libsql-capacitor';

const { connectionId } = await Libsql.connect({ url: 'file:/full/path/to.db' });

await Libsql.execute({ connectionId, statement: 'CREATE TABLE t (id INTEGER, name TEXT)' });
await Libsql.execute({ connectionId, statement: 'INSERT INTO t VALUES (?, ?)', values: [1, 'Kanik'] });

const { rows } = await Libsql.query({ connectionId, statement: 'SELECT * FROM t WHERE id = ?', values: [1] });

await Libsql.beginTransaction({ connectionId });
await Libsql.executeBatch({ connectionId, statements: [{ statement: 'INSERT INTO t VALUES (?, ?)', values: [2, 'A'] }] });
await Libsql.commitTransaction({ connectionId });

await Libsql.disconnect({ connectionId });

Methods

Method Purpose
connect Open a libSQL connection. Returns a connectionId.
disconnect Close a connection.
execute Run a statement that does not return rows.
executeBatch Run multiple statements in one call.
query Run a SELECT and return rows (array of arrays of values).
beginTransaction Begin a transaction on the connection.
commitTransaction Commit.
rollbackTransaction Rollback.
sync Sync (no-op for local-only setups).

query shape

rows is Array<Array<value>> — positional, not keyed by column. Each value is a JSON-safe scalar: number, string, null, or Array<number> (byte array for blobs).

iOS column iteration: libsql-swift does not expose a column-count API and returns a null Value (without throwing) for out-of-bounds indices. The plugin iterates each row up to a hard cap of 1024 columns and stops early after seeing 16 consecutive nulls, trimming them from the result. Realistic schemas are well within these bounds.

NULL parameters

The Capacitor JS↔native bridge marshals null JS values to a sentinel that tech.turso.libsql rejects as "null not supported". If your app passes null parameters, rewrite them to literal NULL in the SQL text before sending — example:

function inlineNulls(sql, params) {
  if (!params || !params.length) return { sql, params };
  let out = '', i = 0, inSingle = false, inDouble = false, kept = [];
  for (let c = 0; c < sql.length; c++) {
    const ch = sql[c];
    if (ch === "'" && !inDouble) inSingle = !inSingle;
    else if (ch === '"' && !inSingle) inDouble = !inDouble;
    if (ch === '?' && !inSingle && !inDouble) {
      const p = params[i++];
      if (p === null || p === undefined) { out += 'NULL'; continue; }
      kept.push(p);
    }
    out += ch;
  }
  return { sql: out, params: kept };
}

Drizzle integration

import { drizzle } from 'drizzle-orm/sqlite-proxy';
import { Libsql } from '@kanik/libsql-capacitor';

const { connectionId } = await Libsql.connect({ url: 'file:/data/app.db' });

const db = drizzle(async (sql, params, method) => {
  const safe = inlineNulls(sql, params);
  if (method === 'run') {
    await Libsql.execute({ connectionId, statement: safe.sql, values: safe.params });
    return { rows: [] };
  }
  const { rows } = await Libsql.query({ connectionId, statement: safe.sql, values: safe.params });
  return method === 'get' ? { rows: rows[0] ?? [] } : { rows };
});

Building from source

git clone https://github.com/7999999166/kanik-libsql-capacitor.git
cd kanik-libsql-capacitor
npm install
npm run build

dist/ is committed so consumers installing from a Git tag don't need a prepare step.

Changelog

v0.3.4

  • iOS: Coerce bridged NSNumber / NSNull parameters to ValueRepresentable before binding. Previously any non-string parameter (numbers, booleans) failed with invalidValues ("All values must be arrays.").

v0.3.3

  • iOS: Convert Libsql.Value enum cases to JSON-serializable scalars in QueryResult. Previously every query response failed Capacitor's bridge with Unable to serialize plugin response as JSON. and returned {}.

v0.3.2

  • iOS: Fix infinite loop in QueryResult.convertRowsToAnyArray that allocated multi-GB arrays and crashed the app. libsql-swift's Row.get(index) returns .null (without throwing) for out-of-bounds indices and exposes no column count, so the previous catch-based loop never terminated. Now capped at 1024 columns with a 16-consecutive-null early stop.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages