@op-engineering/op-sqlite 15.0.0 → 15.0.2

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/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { NativeModules, Platform } from 'react-native';
1
+ import { NativeModules } from 'react-native';
2
+
2
3
  export { Storage } from './Storage';
4
+ export * from './functions';
3
5
 
4
6
  export type Scalar =
5
7
  | string
@@ -55,7 +57,8 @@ export type ColumnMetadata = {
55
57
  */
56
58
  export type SQLBatchTuple =
57
59
  | [string]
58
- | [string, Array<Scalar> | Array<Array<Scalar>>];
60
+ | [string, Scalar[]]
61
+ | [string, Scalar[][]];
59
62
 
60
63
  export type UpdateHookOperation = 'INSERT' | 'DELETE' | 'UPDATE';
61
64
 
@@ -82,7 +85,7 @@ export type Transaction = {
82
85
  rollback: () => QueryResult;
83
86
  };
84
87
 
85
- type PendingTransaction = {
88
+ export type _PendingTransaction = {
86
89
  /*
87
90
  * The start function should not throw or return a promise because the
88
91
  * queue just calls it and does not monitor for failures or completions.
@@ -101,7 +104,7 @@ export type PreparedStatement = {
101
104
  execute: () => Promise<QueryResult>;
102
105
  };
103
106
 
104
- type InternalDB = {
107
+ export type _InternalDB = {
105
108
  close: () => void;
106
109
  delete: (location?: string) => void;
107
110
  attach: (params: {
@@ -307,44 +310,14 @@ export type OPSQLiteProxy = {
307
310
  name: string;
308
311
  location?: string;
309
312
  encryptionKey?: string;
310
- }) => InternalDB;
311
- openRemote: (options: { url: string; authToken: string }) => InternalDB;
312
- openSync: (options: DBParams) => InternalDB;
313
+ }) => _InternalDB;
314
+ openRemote: (options: { url: string; authToken: string }) => _InternalDB;
315
+ openSync: (options: DBParams) => _InternalDB;
313
316
  isSQLCipher: () => boolean;
314
317
  isLibsql: () => boolean;
315
318
  isIOSEmbedded: () => boolean;
316
319
  };
317
320
 
318
- declare global {
319
- var __OPSQLiteProxy: object | undefined;
320
- }
321
-
322
- if (global.__OPSQLiteProxy == null) {
323
- if (NativeModules.OPSQLite == null) {
324
- throw new Error(
325
- 'Base module not found. Did you do a pod install/clear the gradle cache?'
326
- );
327
- }
328
-
329
- // Call the synchronous blocking install() function
330
- const installed = NativeModules.OPSQLite.install();
331
- if (!installed) {
332
- throw new Error(
333
- `Failed to install op-sqlite: The native OPSQLite Module could not be installed! Looks like something went wrong when installing JSI bindings, check the native logs for more info`
334
- );
335
- }
336
-
337
- // Check again if the constructor now exists. If not, throw an error.
338
- if (global.__OPSQLiteProxy == null) {
339
- throw new Error(
340
- 'OPSqlite native object is not available. Something is wrong. Check the native logs for more information.'
341
- );
342
- }
343
- }
344
-
345
- const proxy = global.__OPSQLiteProxy;
346
- const OPSQLite = proxy as OPSQLiteProxy;
347
-
348
321
  export const {
349
322
  IOS_DOCUMENT_PATH,
350
323
  IOS_LIBRARY_PATH,
@@ -354,417 +327,3 @@ export const {
354
327
  } = !!NativeModules.OPSQLite.getConstants
355
328
  ? NativeModules.OPSQLite.getConstants()
356
329
  : NativeModules.OPSQLite;
357
-
358
- function enhanceDB(db: InternalDB, options: DBParams): DB {
359
- const lock = {
360
- queue: [] as PendingTransaction[],
361
- inProgress: false,
362
- };
363
-
364
- const startNextTransaction = () => {
365
- if (lock.inProgress) {
366
- // Transaction is already in process bail out
367
- return;
368
- }
369
-
370
- if (lock.queue.length) {
371
- lock.inProgress = true;
372
- const tx = lock.queue.shift();
373
-
374
- if (!tx) {
375
- throw new Error('Could not get a operation on database');
376
- }
377
-
378
- setImmediate(() => {
379
- tx.start();
380
- });
381
- }
382
- };
383
-
384
- function sanitizeArrayBuffersInArray(
385
- params?: any[] | any[][]
386
- ): any[] | undefined {
387
- if (!params) {
388
- return params;
389
- }
390
-
391
- return params.map((p) => {
392
- if (Array.isArray(p)) {
393
- return sanitizeArrayBuffersInArray(p);
394
- }
395
-
396
- if (ArrayBuffer.isView(p)) {
397
- return p.buffer;
398
- }
399
-
400
- return p;
401
- });
402
- }
403
-
404
- // spreading the object does not work with HostObjects (db)
405
- // We need to manually assign the fields
406
- let enhancedDb = {
407
- delete: db.delete,
408
- attach: db.attach,
409
- detach: db.detach,
410
- executeBatch: async (
411
- commands: SQLBatchTuple[]
412
- ): Promise<BatchQueryResult> => {
413
- const sanitizedCommands = commands.map(([query, params]) => {
414
- if (params) {
415
- return [query, sanitizeArrayBuffersInArray(params)];
416
- }
417
-
418
- return [query];
419
- });
420
-
421
- async function run() {
422
- try {
423
- enhancedDb.executeSync('BEGIN TRANSACTION;');
424
-
425
- let res = await db.executeBatch(sanitizedCommands as any[]);
426
-
427
- enhancedDb.executeSync('COMMIT;');
428
-
429
- return res;
430
- } catch (executionError) {
431
- try {
432
- enhancedDb.executeSync('ROLLBACK;');
433
- } catch (rollbackError) {
434
- throw rollbackError;
435
- }
436
-
437
- throw executionError;
438
- } finally {
439
- lock.inProgress = false;
440
- startNextTransaction();
441
- }
442
- }
443
-
444
- return await new Promise((resolve, reject) => {
445
- const tx: PendingTransaction = {
446
- start: () => {
447
- run().then(resolve).catch(reject);
448
- },
449
- };
450
-
451
- lock.queue.push(tx);
452
- startNextTransaction();
453
- });
454
- },
455
- loadFile: db.loadFile,
456
- updateHook: db.updateHook,
457
- commitHook: db.commitHook,
458
- rollbackHook: db.rollbackHook,
459
- loadExtension: db.loadExtension,
460
- getDbPath: db.getDbPath,
461
- reactiveExecute: db.reactiveExecute,
462
- sync: db.sync,
463
- close: db.close,
464
- executeWithHostObjects: async (
465
- query: string,
466
- params?: Scalar[]
467
- ): Promise<QueryResult> => {
468
- const sanitizedParams = sanitizeArrayBuffersInArray(params);
469
-
470
- return sanitizedParams
471
- ? await db.executeWithHostObjects(query, sanitizedParams as Scalar[])
472
- : await db.executeWithHostObjects(query);
473
- },
474
- executeRaw: async (query: string, params?: Scalar[]) => {
475
- const sanitizedParams = sanitizeArrayBuffersInArray(params);
476
-
477
- return db.executeRaw(query, sanitizedParams as Scalar[]);
478
- },
479
- // Wrapper for executeRaw, drizzleORM uses this function
480
- // at some point I changed the API but they did not pin their dependency to a specific version
481
- // so re-inserting this so it starts working again
482
- executeRawAsync: async (query: string, params?: Scalar[]) => {
483
- const sanitizedParams = sanitizeArrayBuffersInArray(params);
484
-
485
- return db.executeRaw(query, sanitizedParams as Scalar[]);
486
- },
487
- executeRawSync: (query: string, params?: Scalar[]) => {
488
- const sanitizedParams = sanitizeArrayBuffersInArray(params);
489
- return db.executeRawSync(query, sanitizedParams as Scalar[]);
490
- },
491
- executeSync: (query: string, params?: Scalar[]): QueryResult => {
492
- const sanitizedParams = sanitizeArrayBuffersInArray(params);
493
-
494
- let intermediateResult = sanitizedParams
495
- ? db.executeSync(query, sanitizedParams as Scalar[])
496
- : db.executeSync(query);
497
-
498
- let rows: Record<string, Scalar>[] = [];
499
- for (let i = 0; i < (intermediateResult.rawRows?.length ?? 0); i++) {
500
- let row: Record<string, Scalar> = {};
501
- let rawRow = intermediateResult.rawRows![i]!;
502
- for (let j = 0; j < intermediateResult.columnNames!.length; j++) {
503
- let columnName = intermediateResult.columnNames![j]!;
504
- let value = rawRow[j]!;
505
-
506
- row[columnName] = value;
507
- }
508
- rows.push(row);
509
- }
510
-
511
- let res = {
512
- ...intermediateResult,
513
- rows,
514
- };
515
-
516
- delete res.rawRows;
517
-
518
- return res;
519
- },
520
- executeAsync: async (
521
- query: string,
522
- params?: Scalar[] | undefined
523
- ): Promise<QueryResult> => {
524
- return db.execute(query, params);
525
- },
526
- execute: async (
527
- query: string,
528
- params?: Scalar[] | undefined
529
- ): Promise<QueryResult> => {
530
- const sanitizedParams = sanitizeArrayBuffersInArray(params);
531
-
532
- let intermediateResult = await db.execute(
533
- query,
534
- sanitizedParams as Scalar[]
535
- );
536
-
537
- let rows: Record<string, Scalar>[] = [];
538
- for (let i = 0; i < (intermediateResult.rawRows?.length ?? 0); i++) {
539
- let row: Record<string, Scalar> = {};
540
- let rawRow = intermediateResult.rawRows![i]!;
541
- for (let j = 0; j < intermediateResult.columnNames!.length; j++) {
542
- let columnName = intermediateResult.columnNames![j]!;
543
- let value = rawRow[j]!;
544
-
545
- row[columnName] = value;
546
- }
547
- rows.push(row);
548
- }
549
-
550
- let res = {
551
- ...intermediateResult,
552
- rows,
553
- };
554
-
555
- delete res.rawRows;
556
-
557
- return res;
558
- },
559
- prepareStatement: (query: string) => {
560
- const stmt = db.prepareStatement(query);
561
-
562
- return {
563
- bindSync: (params: Scalar[]) => {
564
- const sanitizedParams = sanitizeArrayBuffersInArray(params);
565
-
566
- stmt.bindSync(sanitizedParams!);
567
- },
568
- bind: async (params: Scalar[]) => {
569
- const sanitizedParams = sanitizeArrayBuffersInArray(params);
570
-
571
- await stmt.bind(sanitizedParams!);
572
- },
573
- execute: stmt.execute,
574
- };
575
- },
576
- transaction: async (
577
- fn: (tx: Transaction) => Promise<void>
578
- ): Promise<void> => {
579
- let isFinalized = false;
580
-
581
- const execute = async (query: string, params?: Scalar[]) => {
582
- if (isFinalized) {
583
- throw Error(
584
- `OP-Sqlite Error: Database: ${
585
- options.name || options.url
586
- }. Cannot execute query on finalized transaction`
587
- );
588
- }
589
- return await enhancedDb.execute(query, params);
590
- };
591
-
592
- const commit = async (): Promise<QueryResult> => {
593
- if (isFinalized) {
594
- throw Error(
595
- `OP-Sqlite Error: Database: ${
596
- options.name || options.url
597
- }. Cannot execute query on finalized transaction`
598
- );
599
- }
600
- const result = enhancedDb.executeSync('COMMIT;');
601
-
602
- await db.flushPendingReactiveQueries();
603
-
604
- isFinalized = true;
605
- return result;
606
- };
607
-
608
- const rollback = (): QueryResult => {
609
- if (isFinalized) {
610
- throw Error(
611
- `OP-Sqlite Error: Database: ${
612
- options.name || options.url
613
- }. Cannot execute query on finalized transaction`
614
- );
615
- }
616
- const result = enhancedDb.executeSync('ROLLBACK;');
617
- isFinalized = true;
618
- return result;
619
- };
620
-
621
- async function run() {
622
- try {
623
- enhancedDb.executeSync('BEGIN TRANSACTION;');
624
-
625
- await fn({
626
- commit,
627
- execute,
628
- rollback,
629
- });
630
-
631
- if (!isFinalized) {
632
- commit();
633
- }
634
- } catch (executionError) {
635
- if (!isFinalized) {
636
- try {
637
- rollback();
638
- } catch (rollbackError) {
639
- throw rollbackError;
640
- }
641
- }
642
-
643
- throw executionError;
644
- } finally {
645
- lock.inProgress = false;
646
- isFinalized = false;
647
- startNextTransaction();
648
- }
649
- }
650
-
651
- return await new Promise((resolve, reject) => {
652
- const tx: PendingTransaction = {
653
- start: () => {
654
- run().then(resolve).catch(reject);
655
- },
656
- };
657
-
658
- lock.queue.push(tx);
659
- startNextTransaction();
660
- });
661
- },
662
- };
663
-
664
- return enhancedDb;
665
- }
666
-
667
- /**
668
- * Open a replicating connection via libsql to a turso db
669
- * libsql needs to be enabled on your package.json
670
- */
671
- export const openSync = (params: {
672
- url: string;
673
- authToken: string;
674
- name: string;
675
- location?: string;
676
- libsqlSyncInterval?: number;
677
- libsqlOffline?: boolean;
678
- encryptionKey?: string;
679
- remoteEncryptionKey?: string;
680
- }): DB => {
681
- if (!isLibsql()) {
682
- throw new Error('This function is only available for libsql');
683
- }
684
-
685
- const db = OPSQLite.openSync(params);
686
- const enhancedDb = enhanceDB(db, params);
687
-
688
- return enhancedDb;
689
- };
690
-
691
- /**
692
- * Open a remote connection via libsql to a turso db
693
- * libsql needs to be enabled on your package.json
694
- */
695
- export const openRemote = (params: { url: string; authToken: string }): DB => {
696
- if (!isLibsql()) {
697
- throw new Error('This function is only available for libsql');
698
- }
699
-
700
- const db = OPSQLite.openRemote(params);
701
- const enhancedDb = enhanceDB(db, params);
702
-
703
- return enhancedDb;
704
- };
705
-
706
- /**
707
- * Open a connection to a local sqlite, sqlcipher or libsql database.
708
- *
709
- * If you want libsql remote or sync connections, use openSync or openRemote.
710
- */
711
- export const open = (params: {
712
- name: string;
713
- location?: string;
714
- encryptionKey?: string;
715
- }): DB => {
716
- if (params.location?.startsWith('file://')) {
717
- console.warn(
718
- "[op-sqlite] You are passing a path with 'file://' prefix, it's automatically removed"
719
- );
720
- params.location = params.location.substring(7);
721
- }
722
-
723
- const db = OPSQLite.open(params);
724
- const enhancedDb = enhanceDB(db, params);
725
-
726
- return enhancedDb;
727
- };
728
-
729
- /**
730
- * Moves the database from the assets folder to the default path (check the docs) or to a custom path
731
- * It DOES NOT OVERWRITE the database if it already exists in the destination path
732
- * if you want to overwrite the database, you need to pass the overwrite flag as true
733
- * @param args object with the parameters for the operaiton
734
- * @returns promise, rejects if failed to move the database, resolves if the operation was successful
735
- */
736
- export const moveAssetsDatabase = async (args: {
737
- filename: string;
738
- path?: string;
739
- overwrite?: boolean;
740
- }): Promise<boolean> => {
741
- return NativeModules.OPSQLite.moveAssetsDatabase(args);
742
- };
743
-
744
- /**
745
- * Used to load a dylib file that contains a sqlite 3 extension/plugin
746
- * It returns the raw path to the actual file which then needs to be passed to the loadExtension function
747
- * Check the docs for more information
748
- * @param bundle the iOS bundle identifier of the .framework
749
- * @param name the file name of the dylib file
750
- * @returns
751
- */
752
- export const getDylibPath = (bundle: string, name: string): string => {
753
- return NativeModules.OPSQLite.getDylibPath(bundle, name);
754
- };
755
-
756
- export const isSQLCipher = (): boolean => {
757
- return OPSQLite.isSQLCipher();
758
- };
759
-
760
- export const isLibsql = (): boolean => {
761
- return OPSQLite.isLibsql();
762
- };
763
-
764
- export const isIOSEmbeeded = (): boolean => {
765
- if (Platform.OS !== 'ios') {
766
- return false;
767
- }
768
-
769
- return OPSQLite.isIOSEmbedded();
770
- };