Migrate SQLite from react-native-quick-sqlite to op-sqlite

Replaces react-native-quick-sqlite (8.2.7) with @op-engineering/op-sqlite
(16.2.0). The motivating bug: quick-sqlite's JSI binding silently dropped
non-bindable JS objects (e.g. a cashu-ts Amount instance), shifting the
parameter array and corrupting proof rows with no exception.

A new connection adapter (services/db/connection.ts) is the single seam to
the native library and reconciles op-sqlite's API differences so sqlite.ts
is nearly untouched:
  - op-sqlite execute() is async; sync is executeSync() -> our execute maps
    to executeSync, executeAsync maps to execute
  - executeBatch() is async-only with no insertId -> synchronous atomic batch
    emulated with BEGIN/COMMIT/ROLLBACK over executeSync
  - rows is a plain array -> re-wrapped into the WebSQL { _array, length,
    item() } shape the existing query code expects

The adapter also centralizes parameter sanitization (sanitizeParams): every
bind is coerced deliberately (Date -> ISO, numeric-like objects -> Number) or
rejected loudly, so the silent-skip footgun is structurally impossible.

Also collapses 33 repetitive try/catch AppError blocks into a dbError()
helper, which uniformly passes deliberate AppErrors (e.g. NOTFOUND_ERROR)
through instead of flattening them to DATABASE_ERROR (-109 net lines).

Tests: 121/121 pass, no regressions. Note: native rebuild and on-device
verification of the minibits.db file location are still required before ship.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
minibits-cash
2026-05-30 21:34:28 +02:00
co-authored by Claude Opus 4.8
parent c61e73a786
commit 686b21a5bb
4 changed files with 274 additions and 309 deletions
+1 -1
View File
@@ -33,6 +33,7 @@
"@gocodingnow/rn-size-matters": "^0.0.4",
"@hot-updater/react-native": "0.23.1",
"@notifee/react-native": "^9.1.8",
"@op-engineering/op-sqlite": "^16.2.0",
"@react-native-clipboard/clipboard": "^1.16.3",
"@react-native-community/netinfo": "^11.4.1",
"@react-native-firebase/app": "^23.5.0",
@@ -77,7 +78,6 @@
"react-native-pager-view": "^8.0.0",
"react-native-qrcode-svg": "^6.3.15",
"react-native-quick-crypto": "^0.7.17",
"react-native-quick-sqlite": "8.2.7",
"react-native-reanimated": "^4.2.2",
"react-native-safe-area-context": "^5.6.1",
"react-native-screens": "^4.16.0",
+201
View File
@@ -0,0 +1,201 @@
/**
* op-sqlite connection adapter.
*
* This module is the single seam between the rest of the app and the native
* SQLite library. It exposes a small, quick-sqlite-compatible surface so that
* `services/sqlite.ts` (and its `Database.*` facade, consumed across 9 files)
* keeps working with minimal changes after the migration from
* `react-native-quick-sqlite` to `@op-engineering/op-sqlite`.
*
* Three op-sqlite differences are reconciled here so callers never see them:
*
* 1. SYNC vs ASYNC. In op-sqlite `execute()` is ASYNC and the synchronous
* variant is `executeSync()`. quick-sqlite was the opposite: `execute()`
* was sync and `executeAsync()` async. We map:
* our `execute` -> op-sqlite `executeSync` (stays synchronous)
* our `executeAsync` -> op-sqlite `execute` (stays async)
*
* 2. NO SYNC BATCH. op-sqlite `executeBatch()` is async-only and returns no
* `insertId`. Our reservation / schema / migration code calls `executeBatch`
* synchronously and depends on its all-or-nothing atomicity. We emulate a
* synchronous atomic batch with an explicit BEGIN / COMMIT / ROLLBACK over
* `executeSync` (see `executeBatch` below).
*
* 3. RESULT SHAPE. op-sqlite returns `rows` as a plain array. quick-sqlite
* returned a WebSQL-style object with `item(i)`, `length`, and `_array`.
* We wrap every result back into that shape (see `adaptResult`) so the ~600
* lines of query code in `sqlite.ts` are untouched.
*
* It is ALSO the one place where parameters are sanitized before they reach the
* native binder (see `sanitizeParams`). This closes the silent-bind footgun that
* motivated the migration: in quick-sqlite a non-bindable JS object (e.g. a
* cashu-ts `Amount` instance) fell through the C++ binding silently, shifting
* every subsequent parameter and corrupting the row. Here, such a value is
* either coerced deliberately or rejected loudly — never silently dropped.
*/
import {open as opOpen, DB, Scalar, SQLBatchTuple as OpSQLBatchTuple} from '@op-engineering/op-sqlite'
import AppError, {Err} from '../../utils/AppError'
export type {Scalar}
/**
* A batch command: a SQL string, optionally with a single row of params.
*
* Intentionally looser than op-sqlite's `SQLBatchTuple` (whose `Scalar[][]`
* "param sets" variant makes TS reject the ordinary single-row tuples this
* codebase builds). Params are sanitized at bind time, so `any[]` here is safe.
*/
export type SQLBatchTuple = [string] | [string, any[]]
/** WebSQL-style row accessor, preserved for compatibility with existing code. */
export type AdaptedRows = {
_array: any[]
length: number
// Returns `any` (not `T | undefined`) to match quick-sqlite's original typing;
// callers already guard the empty-result case where it matters.
item: (index: number) => any
}
/** quick-sqlite-compatible result shape. */
export type QueryResult = {
insertId?: number
rowsAffected: number
rows: AdaptedRows
}
/**
* The connection surface the rest of the app sees. Intentionally a subset of
* the old `QuickSQLiteConnection` — only the members `sqlite.ts` actually uses.
*/
export type DbConnection = {
/** Synchronous single statement (maps to op-sqlite `executeSync`). */
execute: (query: string, params?: unknown[]) => QueryResult
/** Asynchronous single statement (maps to op-sqlite `execute`). */
executeAsync: (query: string, params?: unknown[]) => Promise<QueryResult>
/**
* Synchronous atomic batch, emulated with BEGIN/COMMIT/ROLLBACK so the
* historical synchronous semantics and all-or-nothing guarantee are kept.
*/
executeBatch: (commands: SQLBatchTuple[]) => {rowsAffected: number}
/** Native async batch (op-sqlite `executeBatch`), for non-atomic-sync paths. */
executeBatchAsync: (commands: SQLBatchTuple[]) => Promise<{rowsAffected: number}>
}
/**
* Coerce a single JS value to a SQLite-bindable `Scalar`, or throw.
*
* The whole point of routing every bind through here is that an unbindable
* value can NEVER be silently skipped (the quick-sqlite bug). The rules:
* - null / undefined -> null
* - string / boolean / buffers -> passed through unchanged
* - finite number -> passed through (NaN / Infinity rejected)
* - bigint -> number if safe, else string
* - Date -> ISO string (every date column here is TEXT)
* - other object (e.g. Amount) -> Number(v) if finite, else THROW
* - anything else -> THROW
*/
const sanitizeValue = (value: unknown, index: number): Scalar => {
if (value === null || value === undefined) return null
const t = typeof value
if (t === 'string' || t === 'boolean') return value as Scalar
if (t === 'number') {
if (!Number.isFinite(value as number)) {
throw new AppError(Err.DATABASE_ERROR, 'Cannot bind non-finite number', {index, value})
}
return value as number
}
if (t === 'bigint') {
const n = Number(value)
return Number.isSafeInteger(n) ? n : String(value)
}
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
return value as Scalar
}
if (value instanceof Date) {
return value.toISOString()
}
if (t === 'object') {
// cashu-ts `Amount` and other numeric-like wrappers coerce cleanly. Plain
// objects / arrays produce NaN and are rejected so the bug surfaces loudly.
const n = Number(value)
if (Number.isFinite(n)) return n
throw new AppError(Err.DATABASE_ERROR, 'Cannot bind non-numeric object parameter', {
index,
constructor: (value as object)?.constructor?.name,
})
}
throw new AppError(Err.DATABASE_ERROR, `Cannot bind parameter of type ${t}`, {index})
}
const sanitizeParams = (params?: unknown[]): Scalar[] | undefined => {
if (!params || params.length === 0) return undefined
return params.map(sanitizeValue)
}
const adaptResult = (r: {insertId?: number; rowsAffected?: number; rows?: any[]}): QueryResult => {
const rows = r.rows ?? []
return {
insertId: r.insertId,
rowsAffected: r.rowsAffected ?? 0,
rows: {
_array: rows,
length: rows.length,
item: (index: number) => rows[index],
},
}
}
/**
* Is the tuple's params slot a list of param SETS (Scalar[][]) rather than a
* single param row (Scalar[])? op-sqlite's `SQLBatchTuple` allows both; the old
* code only ever used the single-row form, but we handle both for safety.
*/
const isParamSets = (params: unknown[]): params is unknown[][] =>
params.length > 0 && Array.isArray(params[0])
const open = (params: {name: string; location?: string; encryptionKey?: string}): DbConnection => {
const db: DB = opOpen(params)
const execute = (query: string, p?: unknown[]): QueryResult =>
adaptResult(db.executeSync(query, sanitizeParams(p)))
const executeAsync = async (query: string, p?: unknown[]): Promise<QueryResult> =>
adaptResult(await db.execute(query, sanitizeParams(p)))
const executeBatch = (commands: SQLBatchTuple[]): {rowsAffected: number} => {
let rowsAffected = 0
db.executeSync('BEGIN')
try {
for (const [query, p] of commands as Array<[string, unknown[]?]>) {
if (p && isParamSets(p)) {
for (const set of p) {
rowsAffected += db.executeSync(query, sanitizeParams(set)).rowsAffected ?? 0
}
} else {
rowsAffected += db.executeSync(query, sanitizeParams(p)).rowsAffected ?? 0
}
}
db.executeSync('COMMIT')
} catch (e) {
// Best-effort rollback; surface the original error to the caller.
try {
db.executeSync('ROLLBACK')
} catch {
// ignore — nothing to roll back / already aborted
}
throw e
}
return {rowsAffected}
}
const executeBatchAsync = async (commands: SQLBatchTuple[]): Promise<{rowsAffected: number}> => {
const r = await db.executeBatch(commands as OpSQLBatchTuple[])
return {rowsAffected: r.rowsAffected ?? 0}
}
return {execute, executeAsync, executeBatch, executeBatchAsync}
}
export {open, sanitizeParams}
+54 -163
View File
@@ -1,8 +1,8 @@
import {
QuickSQLiteConnection,
DbConnection,
open,
SQLBatchTuple,
} from 'react-native-quick-sqlite'
} from './db/connection'
import {Proof, ProofState} from '../models/Proof'
import {CashuProof} from './cashu/cashuUtils'
import {
@@ -14,6 +14,16 @@ import {log} from './logService'
import {ProofRecord} from '../models/Proof'
import { isAlive } from 'mobx-state-tree'
/**
* Normalize an arbitrary caught error into an AppError for the DB layer.
*
* Existing AppErrors (e.g. a NOTFOUND_ERROR raised deliberately inside a query)
* pass through unchanged so their specific `name`/code isn't flattened into a
* generic DATABASE_ERROR. Everything else is wrapped with the supplied message.
*/
const dbError = (message: string, e: any): AppError =>
e instanceof AppError ? e : new AppError(Err.DATABASE_ERROR, message, e?.message)
// Helper functions to normalize transaction records with Date objects
const normalizeTransactionRecord = function (r: any) {
if (r.createdAt) r.createdAt = new Date(r.createdAt);
@@ -25,14 +35,14 @@ const normalizeTransactionRows = function(rows: any) {
return rows?._array.map(normalizeTransactionRecord) as Transaction[];
}
let _db: QuickSQLiteConnection
let _db: DbConnection
const _dbVersion = 26 // Update this if db changes require migrations
const getInstance = function () {
if (!_db) {
// 1. creates database
_db = _createDatabaseInstance() as QuickSQLiteConnection
_db = _createDatabaseInstance() as DbConnection
// 2. Runs possible migrations and sets version
_createOrUpdateSchema(_db)
@@ -44,17 +54,13 @@ const getInstance = function () {
const _createDatabaseInstance = function () {
try {
const instance = open({name: 'minibits.db'})
return instance as QuickSQLiteConnection
return instance as DbConnection
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not create or open database',
e.message,
)
throw dbError('Could not create or open database', e)
}
}
const _createOrUpdateSchema = function (db: QuickSQLiteConnection) {
const _createOrUpdateSchema = function (db: DbConnection) {
const creationQueries = [
[
`CREATE TABLE IF NOT EXISTS transactions (
@@ -141,17 +147,13 @@ const _createOrUpdateSchema = function (db: QuickSQLiteConnection) {
}
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not create or update database schema',
e.message,
)
throw dbError('Could not create or update database schema', e)
}
}
// Run database migrations in case on device version of schema is not yet set or outdated
const _runMigrations = function (db: QuickSQLiteConnection) {
const _runMigrations = function (db: DbConnection) {
const now = new Date()
const {version} = getDatabaseVersion(db)
@@ -322,16 +324,12 @@ const cleanAll = function () {
log.info('[cleanAll]', 'Database tables were deleted')
}
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not delete database schema',
e.message,
)
throw dbError('Could not delete database schema', e)
}
}
const getDatabaseVersion = function (db: QuickSQLiteConnection): {version: number} {
const getDatabaseVersion = function (db: DbConnection): {version: number} {
try {
const query = `
SELECT version FROM dbVersion
@@ -355,11 +353,7 @@ const getDatabaseVersion = function (db: QuickSQLiteConnection): {version: numbe
return rows?.item(0)
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not get database version',
e.message,
)
throw dbError('Could not get database version', e)
}
}
/*
@@ -406,11 +400,7 @@ const updateTransaction = function (id: number, fields: Partial<Transaction>): T
return updated as Transaction
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not update transaction in database',
e.message,
)
throw dbError('Could not update transaction in database', e)
}
}
@@ -447,11 +437,7 @@ const getTransactionsAsync = async function (limit: number, offset: number, only
return normalizeTransactionRows(rows)
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Transactions could not be retrieved from the database',
e.message,
)
throw dbError('Transactions could not be retrieved from the database', e)
}
}
@@ -524,11 +510,7 @@ const searchTransactionsAsync = async function (
return normalizeTransactionRows(rows)
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Transactions search failed',
e.message,
)
throw dbError('Transactions search failed', e)
}
}
@@ -543,11 +525,7 @@ const searchTransactionsCount = function (
const {rows} = db.execute(query, params)
return (rows?.item(0)?.total as number) || 0
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Transactions search count failed',
e.message,
)
throw dbError('Transactions search count failed', e)
}
}
@@ -569,11 +547,7 @@ const getPendingTopups = function () {
return normalizeTransactionRows(rows)
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Transactions could not be retrieved from the database',
e.message,
)
throw dbError('Transactions could not be retrieved from the database', e)
}
}
@@ -596,11 +570,7 @@ const getPendingTransfers = function () {
return normalizeTransactionRows(rows)
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Transactions could not be retrieved from the database',
e.message,
)
throw dbError('Transactions could not be retrieved from the database', e)
}
}
@@ -624,11 +594,7 @@ const getPendingTopupsCount = function () {
return rows?.item(0)['total'] as number
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Transactions could not be retrieved from the database',
e.message,
)
throw dbError('Transactions could not be retrieved from the database', e)
}
}
@@ -652,11 +618,7 @@ const getPendingTransfersCount = function () {
return rows?.item(0)['total'] as number
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Transactions could not be retrieved from the database',
e.message,
)
throw dbError('Transactions could not be retrieved from the database', e)
}
}
@@ -707,7 +669,7 @@ const getTransactionsCount = function (status?: TransactionStatus) {
}
} catch (e: any) {
throw new AppError(Err.DATABASE_ERROR, 'Transaction count error', e.message)
throw dbError('Transaction count error', e)
}
}
@@ -732,7 +694,7 @@ const getRecentTransactionsByUnitAsync = async (countRecent: number) => {
return normalizeTransactionRows(rows)
} catch (e: any) {
throw new AppError(Err.DATABASE_ERROR, 'Error retrieving last 3 transactions by unit', e.message)
throw dbError('Error retrieving last 3 transactions by unit', e)
}
}
@@ -753,7 +715,7 @@ const getPendingAmount = function () {
return rows?.item(0)['SUM(amount)']
} catch (e: any) {
throw new AppError(Err.DATABASE_ERROR, 'Transaction not found', e.message)
throw dbError('Transaction not found', e)
}
}
@@ -771,7 +733,7 @@ const getTransactionById = function (id: number) {
return normalizeTransactionRecord(rows?.item(0))
} catch (e: any) {
throw new AppError(Err.DATABASE_ERROR, 'Transaction not found', e.message)
throw dbError('Transaction not found', e)
}
}
@@ -842,16 +804,9 @@ const getLastTransactionBy = function (
return normalizeTransactionRecord(row)
} catch (e: any) {
if (e instanceof AppError) {
throw e // rethrow known app errors
}
log.error('[getLastTransactionBy] Database error', e)
throw new AppError(
Err.DATABASE_ERROR,
'Failed to fetch transaction',
e.message || String(e)
)
// dbError passes through the deliberate NOTFOUND/validation AppErrors above
// and wraps anything else as a DATABASE_ERROR.
throw dbError('Failed to fetch transaction', e)
}
}
@@ -875,11 +830,7 @@ const addTransactionAsync = async function (tx: Partial<Transaction>): Promise<T
return getTransactionById(result.insertId as number) // already normalized
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not store transaction in the database',
e.message,
)
throw dbError('Could not store transaction in the database', e)
}
}
@@ -929,11 +880,7 @@ const updateStatusesAsync = async function (
return result2
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not update transaction statuses in the database',
e.message,
)
throw dbError('Could not update transaction statuses in the database', e)
}
}
@@ -965,11 +912,7 @@ const deleteTransactionsByStatus = function (status: TransactionStatus) {
return rows
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not delete transactions.',
e.message,
)
throw dbError('Could not delete transactions.', e)
}
}
@@ -988,11 +931,7 @@ const getIncomingPendingCount = function () {
return rows?.item(0)['total'] as number
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not get incoming pending count.',
e.message,
)
throw dbError('Could not get incoming pending count.', e)
}
}
@@ -1012,11 +951,7 @@ const deleteIncomingPending = function () {
return rows
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not delete incoming pending transactions.',
e.message,
)
throw dbError('Could not delete incoming pending transactions.', e)
}
}
@@ -1038,11 +973,7 @@ const deleteTransactionById = function (id: number) {
return rows
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not delete transaction.',
e.message,
)
throw dbError('Could not delete transaction.', e)
}
}
@@ -1086,11 +1017,7 @@ const addOrUpdateProof = function (
return newProof as ProofRecord
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not store proof into the database',
e.message,
)
throw dbError('Could not store proof into the database', e)
}
}
@@ -1143,11 +1070,7 @@ const addOrUpdateProofs = function (
return rowsAffected
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not insert or update proofs into the database',
e.message,
)
throw dbError('Could not insert or update proofs into the database', e)
}
}
@@ -1168,11 +1091,7 @@ const updateProofsMintUrl = function (currentMintUrl: string, updatedMintUrl: st
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not update proof mintUrl in database',
e.message,
)
throw dbError('Could not update proof mintUrl in database', e)
}
}
@@ -1188,11 +1107,7 @@ const removeAllProofs = async function () {
return true
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not remove proofs from the database',
e.message,
)
throw dbError('Could not remove proofs from the database', e)
}
}
@@ -1208,7 +1123,7 @@ const getProofById = function (id: number) {
return rows?.item(0) as ProofRecord
} catch (e: any) {
throw new AppError(Err.DATABASE_ERROR, 'proof not found', e.message)
throw dbError('proof not found', e)
}
}
@@ -1238,11 +1153,7 @@ const getProofs = async (
const { rows } = await db.executeAsync(query)
return (rows?._array ?? []) as ProofRecord[]
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Proofs could not be retrieved from the database',
e.message,
)
throw dbError('Proofs could not be retrieved from the database', e)
}
}
@@ -1260,11 +1171,7 @@ const getProofsByTransaction = function (transactionId: number): ProofRecord[] {
return rows?._array as ProofRecord[]
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Proofs could not be retrieved from the database',
e.message,
)
throw dbError('Proofs could not be retrieved from the database', e)
}
}
@@ -1377,11 +1284,7 @@ const openReservation = function (
operationType: reservation.operationType,
})
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not open proof reservation',
e.message,
)
throw dbError('Could not open proof reservation', e)
}
}
@@ -1523,11 +1426,7 @@ const commitReservation = function (
txUpdate: changes.transactionUpdate ? changes.transactionUpdate.id : undefined,
})
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not commit proof reservation',
e.message,
)
throw dbError('Could not commit proof reservation', e)
}
}
@@ -1561,11 +1460,7 @@ const rollbackReservation = function (
restoredCount: lockedProofs.length,
})
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not rollback proof reservation',
e.message,
)
throw dbError('Could not rollback proof reservation', e)
}
}
@@ -1600,11 +1495,7 @@ const getOpenReservations = function (): ReservationRow[] {
}
return result
} catch (e: any) {
throw new AppError(
Err.DATABASE_ERROR,
'Could not read open reservations',
e.message,
)
throw dbError('Could not read open reservations', e)
}
}
+18 -145
View File
@@ -5567,6 +5567,20 @@ __metadata:
languageName: node
linkType: hard
"@op-engineering/op-sqlite@npm:^16.2.0":
version: 16.2.0
resolution: "@op-engineering/op-sqlite@npm:16.2.0"
peerDependencies:
"@sqlite.org/sqlite-wasm": "*"
react: "*"
react-native: "*"
peerDependenciesMeta:
"@sqlite.org/sqlite-wasm":
optional: true
checksum: 6201c72256f116d5ead337e50c55e4c7e583cf6a69983df9fef3e4d04f85b037900b734849c9eff85f4168bede6325a0f17f1a8e6d8348f9c45a05619b86d2b5
languageName: node
linkType: hard
"@opentelemetry/api@npm:^1.3.0, @opentelemetry/api@npm:~1.9.0":
version: 1.9.0
resolution: "@opentelemetry/api@npm:1.9.0"
@@ -6938,13 +6952,6 @@ __metadata:
languageName: node
linkType: hard
"@sqltools/formatter@npm:^1.2.5":
version: 1.2.5
resolution: "@sqltools/formatter@npm:1.2.5"
checksum: 9b8354e715467d660daa5afe044860b5686bbb1a5cb67a60866b932effafbf5e8b429f19a8ae67cd412065a4f067161f227e182f3664a0245339d5eb1e26e355
languageName: node
linkType: hard
"@tootallnate/once@npm:2":
version: 2.0.0
resolution: "@tootallnate/once@npm:2.0.0"
@@ -7791,13 +7798,6 @@ __metadata:
languageName: node
linkType: hard
"app-root-path@npm:^3.1.0":
version: 3.1.0
resolution: "app-root-path@npm:3.1.0"
checksum: e3db3957aee197143a0f6c75e39fe89b19e7244f28b4f2944f7276a9c526d2a7ab2d115b4b2d70a51a65a9a3ca17506690e5b36f75a068a7e5a13f8c092389ba
languageName: node
linkType: hard
"appdirsjs@npm:^1.2.4":
version: 1.2.7
resolution: "appdirsjs@npm:1.2.7"
@@ -9479,7 +9479,7 @@ __metadata:
languageName: node
linkType: hard
"dayjs@npm:^1.11.9, dayjs@npm:^1.8.15":
"dayjs@npm:^1.8.15":
version: 1.11.13
resolution: "dayjs@npm:1.11.13"
checksum: f388db88a6aa93956c1f6121644e783391c7b738b73dbc54485578736565c8931bdfba4bb94e9b1535c6e509c97d5deb918bbe1ae6b34358d994de735055cca9
@@ -9758,13 +9758,6 @@ __metadata:
languageName: node
linkType: hard
"dotenv@npm:^16.0.3":
version: 16.4.7
resolution: "dotenv@npm:16.4.7"
checksum: c27419b5875a44addcc56cc69b7dc5b0e6587826ca85d5b355da9303c6fc317fc9989f1f18366a16378c9fdd9532d14117a1abe6029cc719cdbbef6eaef2cea4
languageName: node
linkType: hard
"dotenv@npm:^16.4.5":
version: 16.6.1
resolution: "dotenv@npm:16.6.1"
@@ -12309,7 +12302,7 @@ __metadata:
languageName: node
linkType: hard
"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3, inherits@npm:~2.0.4":
"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3, inherits@npm:~2.0.4":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1
@@ -14634,6 +14627,7 @@ __metadata:
"@hot-updater/react-native": 0.23.1
"@hot-updater/sentry-plugin": ^0.23.1
"@notifee/react-native": ^9.1.8
"@op-engineering/op-sqlite": ^16.2.0
"@react-native-clipboard/clipboard": ^1.16.3
"@react-native-community/cli": 20.1.0
"@react-native-community/cli-platform-android": 20.1.0
@@ -14702,7 +14696,6 @@ __metadata:
react-native-pager-view: ^8.0.0
react-native-qrcode-svg: ^6.3.15
react-native-quick-crypto: ^0.7.17
react-native-quick-sqlite: 8.2.7
react-native-reanimated: ^4.2.2
react-native-safe-area-context: ^5.6.1
react-native-screens: ^4.16.0
@@ -14880,15 +14873,6 @@ __metadata:
languageName: node
linkType: hard
"mkdirp@npm:^2.1.3":
version: 2.1.6
resolution: "mkdirp@npm:2.1.6"
bin:
mkdirp: dist/cjs/src/bin.js
checksum: 8a1d09ffac585e55f41c54f445051f5bc33a7de99b952bb04c576cafdf1a67bb4bae8cb93736f7da6838771fbf75bc630430a3a59e1252047d2278690bd150ee
languageName: node
linkType: hard
"mkdirp@npm:^3.0.1":
version: 3.0.1
resolution: "mkdirp@npm:3.0.1"
@@ -16761,18 +16745,6 @@ __metadata:
languageName: node
linkType: hard
"react-native-quick-sqlite@npm:8.2.7":
version: 8.2.7
resolution: "react-native-quick-sqlite@npm:8.2.7"
dependencies:
typeorm: ^0.3.20
peerDependencies:
react: ">=17.0.0"
react-native: ">=0.74.0"
checksum: a2b9202025dae60896445674f65ca56c3b8fb8371346dd95dbbc2aa2c8637993729f92277d4f7d4d4ae0ea39e943aa3f8b611c2d430a19e7bffc0e6191c80211
languageName: node
linkType: hard
"react-native-reanimated@npm:^4.2.2":
version: 4.2.2
resolution: "react-native-reanimated@npm:4.2.2"
@@ -17006,13 +16978,6 @@ __metadata:
languageName: node
linkType: hard
"reflect-metadata@npm:^0.2.1":
version: 0.2.2
resolution: "reflect-metadata@npm:0.2.2"
checksum: a66c7b583e4efdd8f3c3124fbff33da2d0c86d8280617516308b32b2159af7a3698c961db3246387f56f6316b1d33a608f39bb2b49d813316dfc58f6d3bf3210
languageName: node
linkType: hard
"reflect.getprototypeof@npm:^1.0.6, reflect.getprototypeof@npm:^1.0.9":
version: 1.0.10
resolution: "reflect.getprototypeof@npm:1.0.10"
@@ -17644,18 +17609,6 @@ __metadata:
languageName: node
linkType: hard
"sha.js@npm:^2.4.11":
version: 2.4.11
resolution: "sha.js@npm:2.4.11"
dependencies:
inherits: ^2.0.1
safe-buffer: ^5.0.1
bin:
sha.js: ./bin.js
checksum: ebd3f59d4b799000699097dadb831c8e3da3eb579144fd7eb7a19484cbcbb7aca3c68ba2bb362242eb09e33217de3b4ea56e4678184c334323eca24a58e3ad07
languageName: node
linkType: hard
"shebang-command@npm:^2.0.0":
version: 2.0.0
resolution: "shebang-command@npm:2.0.0"
@@ -18598,7 +18551,7 @@ __metadata:
languageName: node
linkType: hard
"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0":
"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0":
version: 2.8.1
resolution: "tslib@npm:2.8.1"
checksum: e4aba30e632b8c8902b47587fd13345e2827fa639e7c3121074d5ee0880723282411a8838f830b55100cbe4517672f84a2472667d355b81e8af165a55dc6203a
@@ -18748,86 +18701,6 @@ __metadata:
languageName: node
linkType: hard
"typeorm@npm:^0.3.20":
version: 0.3.20
resolution: "typeorm@npm:0.3.20"
dependencies:
"@sqltools/formatter": ^1.2.5
app-root-path: ^3.1.0
buffer: ^6.0.3
chalk: ^4.1.2
cli-highlight: ^2.1.11
dayjs: ^1.11.9
debug: ^4.3.4
dotenv: ^16.0.3
glob: ^10.3.10
mkdirp: ^2.1.3
reflect-metadata: ^0.2.1
sha.js: ^2.4.11
tslib: ^2.5.0
uuid: ^9.0.0
yargs: ^17.6.2
peerDependencies:
"@google-cloud/spanner": ^5.18.0
"@sap/hana-client": ^2.12.25
better-sqlite3: ^7.1.2 || ^8.0.0 || ^9.0.0
hdb-pool: ^0.1.6
ioredis: ^5.0.4
mongodb: ^5.8.0
mssql: ^9.1.1 || ^10.0.1
mysql2: ^2.2.5 || ^3.0.1
oracledb: ^6.3.0
pg: ^8.5.1
pg-native: ^3.0.0
pg-query-stream: ^4.0.0
redis: ^3.1.1 || ^4.0.0
sql.js: ^1.4.0
sqlite3: ^5.0.3
ts-node: ^10.7.0
typeorm-aurora-data-api-driver: ^2.0.0
peerDependenciesMeta:
"@google-cloud/spanner":
optional: true
"@sap/hana-client":
optional: true
better-sqlite3:
optional: true
hdb-pool:
optional: true
ioredis:
optional: true
mongodb:
optional: true
mssql:
optional: true
mysql2:
optional: true
oracledb:
optional: true
pg:
optional: true
pg-native:
optional: true
pg-query-stream:
optional: true
redis:
optional: true
sql.js:
optional: true
sqlite3:
optional: true
ts-node:
optional: true
typeorm-aurora-data-api-driver:
optional: true
bin:
typeorm: cli.js
typeorm-ts-node-commonjs: cli-ts-node-commonjs.js
typeorm-ts-node-esm: cli-ts-node-esm.js
checksum: 9d6e5ecd0688eed5a151f33fb26b6d7c3e0af0eaf14ceaefe4c1c1863cdda6eabf2e865c9df2321f7dce26e40f320b76b811e2d1d58bf43f6ce19b36aa1ed1c4
languageName: node
linkType: hard
"typescript@npm:^5.8.3":
version: 5.9.3
resolution: "typescript@npm:5.9.3"