mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-10-05 19:28:26 +00:00
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:
co-authored by
Claude Opus 4.8
parent
c61e73a786
commit
686b21a5bb
+1
-1
@@ -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",
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user