@unvired/react-native-unvired-sdk 0.0.44 → 0.0.46
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/BuildNo.txt +1 -1
- package/dist/database/DatabaseManager.d.ts +10 -1
- package/dist/database/DatabaseManager.js +22 -2
- package/dist/database/DatabaseManager.web.d.ts +7 -0
- package/dist/database/DatabaseManager.web.js +8 -1
- package/dist/database/index.d.ts +3 -0
- package/dist/database/index.js +3 -0
- package/dist/database/services/DatabaseExpo.d.ts +14 -0
- package/dist/database/services/DatabaseExpo.js +284 -0
- package/dist/database/services/DatabaseNative.d.ts +2 -0
- package/dist/database/services/DatabaseNative.js +134 -41
- package/dist/database/services/DatabaseWeb.d.ts +1 -0
- package/dist/database/services/DatabaseWeb.js +138 -34
- package/dist/device-info/services/DeviceInfoNative.js +50 -3
- package/dist/file-system/FileSystem.d.ts +12 -3
- package/dist/file-system/FileSystem.js +34 -6
- package/dist/file-system/index.d.ts +4 -1
- package/dist/file-system/index.js +3 -0
- package/dist/file-system/services/FileSystemExpo.d.ts +22 -0
- package/dist/file-system/services/FileSystemExpo.js +336 -0
- package/dist/file-system/services/FileSystemNative.d.ts +24 -3
- package/dist/file-system/services/FileSystemNative.js +127 -36
- package/dist/file-system/services/FileSystemWeb.d.ts +1 -1
- package/dist/file-system/services/FileSystemWeb.js +86 -94
- package/dist/local-storage/services/StorageNative.d.ts +1 -0
- package/dist/local-storage/services/StorageNative.js +51 -12
- package/dist/logger/BaseLogger.js +4 -0
- package/dist/main.d.ts +3 -3
- package/dist/main.js +2 -2
- package/dist/push-notification/services/PushNotificationNative.js +182 -68
- package/package.json +21 -1
package/BuildNo.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
R-0.000.
|
|
1
|
+
R-0.000.0046
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { IDatabaseAdapter } from "./types";
|
|
2
2
|
/**
|
|
3
3
|
* DatabaseManager - Manages native database operations
|
|
4
|
-
* -
|
|
4
|
+
* - React Native CLI: Uses @op-engineering/op-sqlite
|
|
5
|
+
* - Expo: Uses expo-sqlite
|
|
5
6
|
*/
|
|
6
7
|
export declare class DatabaseManager {
|
|
7
8
|
/**
|
|
8
9
|
* Get the database adapter implementation
|
|
9
10
|
*/
|
|
10
11
|
static getDatabaseAdapter(): IDatabaseAdapter;
|
|
12
|
+
/**
|
|
13
|
+
* Manually override the database adapter
|
|
14
|
+
*/
|
|
15
|
+
static setDatabaseAdapter(adapter: {
|
|
16
|
+
getAdapter(): IDatabaseAdapter;
|
|
17
|
+
}): void;
|
|
11
18
|
}
|
|
19
|
+
export { DatabaseExpo } from "./services/DatabaseExpo";
|
|
20
|
+
export { DatabaseNative } from "./services/DatabaseNative";
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { DatabaseNative } from "./services/DatabaseNative";
|
|
2
|
-
|
|
2
|
+
import { DatabaseExpo } from "./services/DatabaseExpo";
|
|
3
|
+
function resolveAdapter() {
|
|
4
|
+
if (DatabaseNative.isAvailable()) {
|
|
5
|
+
return new DatabaseNative();
|
|
6
|
+
}
|
|
7
|
+
if (DatabaseExpo.isAvailable()) {
|
|
8
|
+
return new DatabaseExpo();
|
|
9
|
+
}
|
|
10
|
+
// Default fallback
|
|
11
|
+
return new DatabaseNative();
|
|
12
|
+
}
|
|
13
|
+
let dbImpl = resolveAdapter();
|
|
3
14
|
/**
|
|
4
15
|
* DatabaseManager - Manages native database operations
|
|
5
|
-
* -
|
|
16
|
+
* - React Native CLI: Uses @op-engineering/op-sqlite
|
|
17
|
+
* - Expo: Uses expo-sqlite
|
|
6
18
|
*/
|
|
7
19
|
export class DatabaseManager {
|
|
8
20
|
/**
|
|
@@ -11,4 +23,12 @@ export class DatabaseManager {
|
|
|
11
23
|
static getDatabaseAdapter() {
|
|
12
24
|
return dbImpl.getAdapter();
|
|
13
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Manually override the database adapter
|
|
28
|
+
*/
|
|
29
|
+
static setDatabaseAdapter(adapter) {
|
|
30
|
+
dbImpl = adapter;
|
|
31
|
+
}
|
|
14
32
|
}
|
|
33
|
+
export { DatabaseExpo } from "./services/DatabaseExpo";
|
|
34
|
+
export { DatabaseNative } from "./services/DatabaseNative";
|
|
@@ -8,4 +8,11 @@ export declare class DatabaseManager {
|
|
|
8
8
|
* Get the database adapter implementation
|
|
9
9
|
*/
|
|
10
10
|
static getDatabaseAdapter(): IDatabaseAdapter;
|
|
11
|
+
/**
|
|
12
|
+
* Manually override the database adapter
|
|
13
|
+
*/
|
|
14
|
+
static setDatabaseAdapter(adapter: {
|
|
15
|
+
getAdapter(): IDatabaseAdapter;
|
|
16
|
+
}): void;
|
|
11
17
|
}
|
|
18
|
+
export { DatabaseWeb } from "./services/DatabaseWeb";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DatabaseWeb } from "./services/DatabaseWeb";
|
|
2
|
-
|
|
2
|
+
let dbImpl = new DatabaseWeb();
|
|
3
3
|
/**
|
|
4
4
|
* DatabaseManager - Manages web database operations
|
|
5
5
|
* - Web: Uses Browser/WebSQL/IndexedDB/UnviredDB via sql.js
|
|
@@ -11,4 +11,11 @@ export class DatabaseManager {
|
|
|
11
11
|
static getDatabaseAdapter() {
|
|
12
12
|
return dbImpl.getAdapter();
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Manually override the database adapter
|
|
16
|
+
*/
|
|
17
|
+
static setDatabaseAdapter(adapter) {
|
|
18
|
+
dbImpl = adapter;
|
|
19
|
+
}
|
|
14
20
|
}
|
|
21
|
+
export { DatabaseWeb } from "./services/DatabaseWeb";
|
package/dist/database/index.d.ts
CHANGED
|
@@ -3,4 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { DatabaseManager } from './DatabaseManager';
|
|
5
5
|
export { Database, createDatabase } from './Database';
|
|
6
|
+
export { DatabaseNative } from './services/DatabaseNative';
|
|
7
|
+
export { DatabaseExpo } from './services/DatabaseExpo';
|
|
8
|
+
export { DatabaseWeb } from './services/DatabaseWeb';
|
|
6
9
|
export type { IDatabaseAdapter, DatabaseOptions, ExecuteOptions, SaveWebDBOptions, ExportWebDBOptions, DeleteUserDataOptions, SuccessCallback, ErrorCallback, ResultCallback, SQLResultSet, SQLTransaction, SQLiteDatabase } from './types';
|
package/dist/database/index.js
CHANGED
|
@@ -3,3 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { DatabaseManager } from './DatabaseManager';
|
|
5
5
|
export { Database, createDatabase } from './Database';
|
|
6
|
+
export { DatabaseNative } from './services/DatabaseNative';
|
|
7
|
+
export { DatabaseExpo } from './services/DatabaseExpo';
|
|
8
|
+
export { DatabaseWeb } from './services/DatabaseWeb';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { IDatabaseAdapter } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* DatabaseExpo - SQLite adapter for Expo environments using expo-sqlite
|
|
4
|
+
* Supports Expo SDK 50+ (openDatabaseSync / openDatabaseAsync) and legacy versions
|
|
5
|
+
*/
|
|
6
|
+
export declare class DatabaseExpo {
|
|
7
|
+
private databases;
|
|
8
|
+
private txDepths;
|
|
9
|
+
static isAvailable(): boolean;
|
|
10
|
+
private normalizeDbName;
|
|
11
|
+
private getDatabase;
|
|
12
|
+
private executeRaw;
|
|
13
|
+
getAdapter(): IDatabaseAdapter;
|
|
14
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
let expoSqlite = null;
|
|
2
|
+
try {
|
|
3
|
+
expoSqlite = require("expo-sqlite");
|
|
4
|
+
}
|
|
5
|
+
catch (e) {
|
|
6
|
+
expoSqlite = null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* DatabaseExpo - SQLite adapter for Expo environments using expo-sqlite
|
|
10
|
+
* Supports Expo SDK 50+ (openDatabaseSync / openDatabaseAsync) and legacy versions
|
|
11
|
+
*/
|
|
12
|
+
export class DatabaseExpo {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.databases = new Map();
|
|
15
|
+
this.txDepths = new Map();
|
|
16
|
+
}
|
|
17
|
+
static isAvailable() {
|
|
18
|
+
return (expoSqlite != null &&
|
|
19
|
+
(typeof expoSqlite.openDatabaseSync === "function" ||
|
|
20
|
+
typeof expoSqlite.openDatabaseAsync === "function" ||
|
|
21
|
+
typeof expoSqlite.openDatabase === "function"));
|
|
22
|
+
}
|
|
23
|
+
normalizeDbName(name) {
|
|
24
|
+
if (!name)
|
|
25
|
+
return "default.db";
|
|
26
|
+
return name.endsWith(".db") ? name : `${name}.db`;
|
|
27
|
+
}
|
|
28
|
+
async getDatabase(name) {
|
|
29
|
+
const dbKey = this.normalizeDbName(name);
|
|
30
|
+
if (this.databases.has(dbKey)) {
|
|
31
|
+
return this.databases.get(dbKey);
|
|
32
|
+
}
|
|
33
|
+
if (!expoSqlite) {
|
|
34
|
+
throw new Error("expo-sqlite is not installed or available");
|
|
35
|
+
}
|
|
36
|
+
let db = null;
|
|
37
|
+
if (typeof expoSqlite.openDatabaseSync === "function") {
|
|
38
|
+
db = expoSqlite.openDatabaseSync(dbKey);
|
|
39
|
+
}
|
|
40
|
+
else if (typeof expoSqlite.openDatabaseAsync === "function") {
|
|
41
|
+
db = await expoSqlite.openDatabaseAsync(dbKey);
|
|
42
|
+
}
|
|
43
|
+
else if (typeof expoSqlite.openDatabase === "function") {
|
|
44
|
+
db = expoSqlite.openDatabase(dbKey);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
throw new Error("Compatible expo-sqlite openDatabase method not found");
|
|
48
|
+
}
|
|
49
|
+
this.databases.set(dbKey, db);
|
|
50
|
+
return db;
|
|
51
|
+
}
|
|
52
|
+
async executeRaw(db, query, params = []) {
|
|
53
|
+
const trimmed = query.trim();
|
|
54
|
+
const isSelect = /^SELECT\b/i.test(trimmed);
|
|
55
|
+
if (typeof db.getAllAsync === "function" && isSelect) {
|
|
56
|
+
return await db.getAllAsync(query, params);
|
|
57
|
+
}
|
|
58
|
+
else if (typeof db.runAsync === "function") {
|
|
59
|
+
return await db.runAsync(query, params);
|
|
60
|
+
}
|
|
61
|
+
else if (typeof db.execAsync === "function") {
|
|
62
|
+
return await db.execAsync(query);
|
|
63
|
+
}
|
|
64
|
+
else if (typeof db.transaction === "function") {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
db.transaction((tx) => {
|
|
67
|
+
tx.executeSql(query, params, (_, res) => resolve(res), (_, err) => reject(err));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
throw new Error("No compatible query execution method on expo-sqlite database");
|
|
72
|
+
}
|
|
73
|
+
getAdapter() {
|
|
74
|
+
return {
|
|
75
|
+
create: async (options, successCallback, errorCallback) => {
|
|
76
|
+
try {
|
|
77
|
+
const dbName = options.name || options.userId || "default";
|
|
78
|
+
await this.getDatabase(dbName);
|
|
79
|
+
successCallback({ success: true, database: dbName });
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
errorCallback(error instanceof Error ? error.message : String(error));
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
execute: async (options, successCallback, errorCallback) => {
|
|
86
|
+
var _a, _b;
|
|
87
|
+
try {
|
|
88
|
+
const dbName = options.dbName || options.userId || "default";
|
|
89
|
+
const params = options.params || options.args || [];
|
|
90
|
+
const db = await this.getDatabase(dbName);
|
|
91
|
+
const rawQuery = options.query || "";
|
|
92
|
+
const trimmedQuery = rawQuery.trim().replace(/;+$/, "").toUpperCase().replace(/\s+/g, " ");
|
|
93
|
+
// Intercept transaction commands to prevent "cannot start a transaction within a transaction"
|
|
94
|
+
if (trimmedQuery === "BEGIN" || trimmedQuery === "BEGIN TRANSACTION") {
|
|
95
|
+
const currentDepth = this.txDepths.get(dbName) || 0;
|
|
96
|
+
if (currentDepth === 0) {
|
|
97
|
+
this.txDepths.set(dbName, 1);
|
|
98
|
+
try {
|
|
99
|
+
await this.executeRaw(db, "BEGIN TRANSACTION;");
|
|
100
|
+
}
|
|
101
|
+
catch (txErr) {
|
|
102
|
+
const msg = (txErr && (txErr.message || String(txErr))) || "";
|
|
103
|
+
if (msg.includes("within a transaction") || msg.includes("already in a transaction")) {
|
|
104
|
+
this.txDepths.set(dbName, currentDepth + 1);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
throw txErr;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// Nested transaction on the same SQLite connection: translate to SAVEPOINT
|
|
113
|
+
this.txDepths.set(dbName, currentDepth + 1);
|
|
114
|
+
try {
|
|
115
|
+
await this.executeRaw(db, `SAVEPOINT trans_sp_${currentDepth};`);
|
|
116
|
+
}
|
|
117
|
+
catch (spErr) { }
|
|
118
|
+
}
|
|
119
|
+
successCallback([]);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (trimmedQuery === "COMMIT" || trimmedQuery === "END" || trimmedQuery === "END TRANSACTION" || trimmedQuery === "COMMIT TRANSACTION") {
|
|
123
|
+
const currentDepth = this.txDepths.get(dbName) || 0;
|
|
124
|
+
if (currentDepth > 1) {
|
|
125
|
+
const newDepth = currentDepth - 1;
|
|
126
|
+
this.txDepths.set(dbName, newDepth);
|
|
127
|
+
try {
|
|
128
|
+
await this.executeRaw(db, `RELEASE SAVEPOINT trans_sp_${newDepth};`);
|
|
129
|
+
}
|
|
130
|
+
catch (spErr) { }
|
|
131
|
+
}
|
|
132
|
+
else if (currentDepth === 1) {
|
|
133
|
+
this.txDepths.set(dbName, 0);
|
|
134
|
+
try {
|
|
135
|
+
await this.executeRaw(db, "COMMIT;");
|
|
136
|
+
}
|
|
137
|
+
catch (txErr) {
|
|
138
|
+
const msg = (txErr && (txErr.message || String(txErr))) || "";
|
|
139
|
+
if (!msg.includes("no transaction is active") && !msg.includes("cannot commit")) {
|
|
140
|
+
throw txErr;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
successCallback([]);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (trimmedQuery === "ROLLBACK" || trimmedQuery === "ROLLBACK TRANSACTION") {
|
|
148
|
+
const currentDepth = this.txDepths.get(dbName) || 0;
|
|
149
|
+
if (currentDepth > 1) {
|
|
150
|
+
const newDepth = currentDepth - 1;
|
|
151
|
+
this.txDepths.set(dbName, newDepth);
|
|
152
|
+
try {
|
|
153
|
+
await this.executeRaw(db, `ROLLBACK TO SAVEPOINT trans_sp_${newDepth};`);
|
|
154
|
+
}
|
|
155
|
+
catch (spErr) { }
|
|
156
|
+
try {
|
|
157
|
+
await this.executeRaw(db, `RELEASE SAVEPOINT trans_sp_${newDepth};`);
|
|
158
|
+
}
|
|
159
|
+
catch (spErr) { }
|
|
160
|
+
}
|
|
161
|
+
else if (currentDepth === 1) {
|
|
162
|
+
this.txDepths.set(dbName, 0);
|
|
163
|
+
try {
|
|
164
|
+
await this.executeRaw(db, "ROLLBACK;");
|
|
165
|
+
}
|
|
166
|
+
catch (txErr) {
|
|
167
|
+
const msg = (txErr && (txErr.message || String(txErr))) || "";
|
|
168
|
+
if (!msg.includes("no transaction is active") && !msg.includes("cannot rollback")) {
|
|
169
|
+
throw txErr;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
this.txDepths.set(dbName, 0);
|
|
175
|
+
}
|
|
176
|
+
successCallback([]);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const res = await this.executeRaw(db, options.query, params);
|
|
180
|
+
let resultToReturn = [];
|
|
181
|
+
if (Array.isArray(res)) {
|
|
182
|
+
resultToReturn = res;
|
|
183
|
+
}
|
|
184
|
+
else if (res && typeof res === "object") {
|
|
185
|
+
if (res.rows && typeof res.rows.length === "number") {
|
|
186
|
+
for (let i = 0; i < res.rows.length; i++) {
|
|
187
|
+
resultToReturn.push(res.rows.item ? res.rows.item(i) : res.rows[i]);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (res.lastInsertRowId !== undefined || res.insertId !== undefined) {
|
|
191
|
+
resultToReturn.insertId = (_a = res.lastInsertRowId) !== null && _a !== void 0 ? _a : res.insertId;
|
|
192
|
+
resultToReturn.rowsAffected = (_b = res.changes) !== null && _b !== void 0 ? _b : res.rowsAffected;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
successCallback(resultToReturn);
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
const msg = (error && (error.message || String(error))) || "";
|
|
199
|
+
if (msg.includes("cannot start a transaction within a transaction")) {
|
|
200
|
+
const dbName = options.dbName || options.userId || "default";
|
|
201
|
+
const currentDepth = this.txDepths.get(dbName) || 0;
|
|
202
|
+
this.txDepths.set(dbName, currentDepth + 1);
|
|
203
|
+
successCallback([]);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (msg.includes("no transaction is active") || msg.includes("cannot rollback") || msg.includes("cannot commit")) {
|
|
207
|
+
const dbName = options.dbName || options.userId || "default";
|
|
208
|
+
this.txDepths.set(dbName, 0);
|
|
209
|
+
successCallback([]);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
errorCallback(error instanceof Error ? error.message : String(error));
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
executeStatementOnPath: async (dbPath, sqlQuery, callback) => {
|
|
216
|
+
var _a;
|
|
217
|
+
try {
|
|
218
|
+
const dbName = ((_a = dbPath.split("/").pop()) === null || _a === void 0 ? void 0 : _a.replace(".db", "")) || "default";
|
|
219
|
+
const db = await this.getDatabase(dbName);
|
|
220
|
+
const res = await this.executeRaw(db, sqlQuery);
|
|
221
|
+
callback(Array.isArray(res) ? res : []);
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
callback(error instanceof Error ? error.message : String(error));
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
selectFromPath: async (dbPath, sqlQuery, callback) => {
|
|
228
|
+
var _a;
|
|
229
|
+
try {
|
|
230
|
+
const dbName = ((_a = dbPath.split("/").pop()) === null || _a === void 0 ? void 0 : _a.replace(".db", "")) || "default";
|
|
231
|
+
const db = await this.getDatabase(dbName);
|
|
232
|
+
const res = await this.executeRaw(db, sqlQuery);
|
|
233
|
+
callback(Array.isArray(res) ? res : []);
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
callback(error instanceof Error ? error.message : String(error));
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
createDatabase: async (dbPath, callback) => {
|
|
240
|
+
var _a;
|
|
241
|
+
try {
|
|
242
|
+
const dbName = ((_a = dbPath.split("/").pop()) === null || _a === void 0 ? void 0 : _a.replace(".db", "")) || "default";
|
|
243
|
+
await this.getDatabase(dbName);
|
|
244
|
+
callback(true);
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
callback(error instanceof Error ? error.message : String(error));
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
getDBFilePath: (options, callback) => {
|
|
251
|
+
callback(options.name);
|
|
252
|
+
},
|
|
253
|
+
saveWebDB: (options, callback, errorCallback) => {
|
|
254
|
+
errorCallback("saveWebDB is only available on web platform");
|
|
255
|
+
},
|
|
256
|
+
exportWebDB: (options, callback, errorCallback) => {
|
|
257
|
+
errorCallback("exportWebDB is only available on web platform");
|
|
258
|
+
},
|
|
259
|
+
deleteUserData: async (options, callback, errorCallback) => {
|
|
260
|
+
try {
|
|
261
|
+
const dbName = options.dbName || options.userId || "default";
|
|
262
|
+
const db = await this.getDatabase(dbName);
|
|
263
|
+
const query = options.userId
|
|
264
|
+
? "DELETE FROM user_data WHERE user_id = ?"
|
|
265
|
+
: "DELETE FROM user_data";
|
|
266
|
+
const params = options.userId ? [options.userId] : [];
|
|
267
|
+
await this.executeRaw(db, query, params);
|
|
268
|
+
callback();
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
errorCallback(error instanceof Error ? error.message : String(error));
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
getEncryptionKey: (options, successCallback, errorCallback) => {
|
|
275
|
+
try {
|
|
276
|
+
successCallback("unvired_default_key");
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
errorCallback();
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
let opSqlite = null;
|
|
2
|
+
try {
|
|
3
|
+
opSqlite = require('@op-engineering/op-sqlite');
|
|
4
|
+
}
|
|
5
|
+
catch (e) {
|
|
6
|
+
opSqlite = null;
|
|
7
|
+
}
|
|
2
8
|
export class DatabaseNative {
|
|
3
9
|
constructor() {
|
|
4
10
|
this.databases = new Map();
|
|
11
|
+
this.txDepths = new Map();
|
|
5
12
|
this.defaultLocation = "default";
|
|
6
13
|
}
|
|
14
|
+
static isAvailable() {
|
|
15
|
+
return opSqlite != null && typeof opSqlite.open === 'function';
|
|
16
|
+
}
|
|
7
17
|
getDatabase(name, location) {
|
|
8
18
|
const dbKey = `${name}_${location || this.defaultLocation}`;
|
|
9
19
|
if (this.databases.has(dbKey)) {
|
|
@@ -12,7 +22,10 @@ export class DatabaseNative {
|
|
|
12
22
|
if (!name) {
|
|
13
23
|
throw new Error("Database name is required");
|
|
14
24
|
}
|
|
15
|
-
|
|
25
|
+
if (!DatabaseNative.isAvailable()) {
|
|
26
|
+
throw new Error("@op-engineering/op-sqlite is not installed or available");
|
|
27
|
+
}
|
|
28
|
+
const db = opSqlite.open({ name });
|
|
16
29
|
this.databases.set(dbKey, db);
|
|
17
30
|
return db;
|
|
18
31
|
}
|
|
@@ -33,7 +46,95 @@ export class DatabaseNative {
|
|
|
33
46
|
const dbName = options.dbName || options.userId;
|
|
34
47
|
const params = options.params || options.args || [];
|
|
35
48
|
const db = this.getDatabase(dbName);
|
|
36
|
-
|
|
49
|
+
const rawQuery = options.query || "";
|
|
50
|
+
const trimmedQuery = rawQuery.trim().replace(/;+$/, "").toUpperCase().replace(/\s+/g, " ");
|
|
51
|
+
// Intercept transaction commands to prevent "cannot start a transaction within a transaction"
|
|
52
|
+
if (trimmedQuery === "BEGIN" || trimmedQuery === "BEGIN TRANSACTION") {
|
|
53
|
+
const currentDepth = this.txDepths.get(dbName) || 0;
|
|
54
|
+
if (currentDepth === 0) {
|
|
55
|
+
this.txDepths.set(dbName, 1);
|
|
56
|
+
try {
|
|
57
|
+
await db.execute("BEGIN TRANSACTION;");
|
|
58
|
+
}
|
|
59
|
+
catch (txErr) {
|
|
60
|
+
const msg = (txErr && (txErr.message || String(txErr))) || "";
|
|
61
|
+
if (msg.includes("within a transaction") || msg.includes("already in a transaction")) {
|
|
62
|
+
this.txDepths.set(dbName, currentDepth + 1);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
throw txErr;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Nested transaction on the same SQLite connection: translate to SAVEPOINT
|
|
71
|
+
this.txDepths.set(dbName, currentDepth + 1);
|
|
72
|
+
try {
|
|
73
|
+
await db.execute(`SAVEPOINT trans_sp_${currentDepth};`);
|
|
74
|
+
}
|
|
75
|
+
catch (spErr) { }
|
|
76
|
+
}
|
|
77
|
+
successCallback([]);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (trimmedQuery === "COMMIT" || trimmedQuery === "END" || trimmedQuery === "END TRANSACTION" || trimmedQuery === "COMMIT TRANSACTION") {
|
|
81
|
+
const currentDepth = this.txDepths.get(dbName) || 0;
|
|
82
|
+
if (currentDepth > 1) {
|
|
83
|
+
const newDepth = currentDepth - 1;
|
|
84
|
+
this.txDepths.set(dbName, newDepth);
|
|
85
|
+
try {
|
|
86
|
+
await db.execute(`RELEASE SAVEPOINT trans_sp_${newDepth};`);
|
|
87
|
+
}
|
|
88
|
+
catch (spErr) { }
|
|
89
|
+
}
|
|
90
|
+
else if (currentDepth === 1) {
|
|
91
|
+
this.txDepths.set(dbName, 0);
|
|
92
|
+
try {
|
|
93
|
+
await db.execute("COMMIT;");
|
|
94
|
+
}
|
|
95
|
+
catch (txErr) {
|
|
96
|
+
const msg = (txErr && (txErr.message || String(txErr))) || "";
|
|
97
|
+
if (!msg.includes("no transaction is active") && !msg.includes("cannot commit")) {
|
|
98
|
+
throw txErr;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
successCallback([]);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (trimmedQuery === "ROLLBACK" || trimmedQuery === "ROLLBACK TRANSACTION") {
|
|
106
|
+
const currentDepth = this.txDepths.get(dbName) || 0;
|
|
107
|
+
if (currentDepth > 1) {
|
|
108
|
+
const newDepth = currentDepth - 1;
|
|
109
|
+
this.txDepths.set(dbName, newDepth);
|
|
110
|
+
try {
|
|
111
|
+
await db.execute(`ROLLBACK TO SAVEPOINT trans_sp_${newDepth};`);
|
|
112
|
+
}
|
|
113
|
+
catch (spErr) { }
|
|
114
|
+
try {
|
|
115
|
+
await db.execute(`RELEASE SAVEPOINT trans_sp_${newDepth};`);
|
|
116
|
+
}
|
|
117
|
+
catch (spErr) { }
|
|
118
|
+
}
|
|
119
|
+
else if (currentDepth === 1) {
|
|
120
|
+
this.txDepths.set(dbName, 0);
|
|
121
|
+
try {
|
|
122
|
+
await db.execute("ROLLBACK;");
|
|
123
|
+
}
|
|
124
|
+
catch (txErr) {
|
|
125
|
+
const msg = (txErr && (txErr.message || String(txErr))) || "";
|
|
126
|
+
if (!msg.includes("no transaction is active") && !msg.includes("cannot rollback")) {
|
|
127
|
+
throw txErr;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
this.txDepths.set(dbName, 0);
|
|
133
|
+
}
|
|
134
|
+
successCallback([]);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
// Await the result properly
|
|
37
138
|
const res = await db.execute(options.query, params);
|
|
38
139
|
let resultToReturn = [];
|
|
39
140
|
// Cast to any to avoid TS error "Property '_array' does not exist on type 'never'"
|
|
@@ -69,6 +170,20 @@ export class DatabaseNative {
|
|
|
69
170
|
successCallback(resultToReturn);
|
|
70
171
|
}
|
|
71
172
|
catch (error) {
|
|
173
|
+
const msg = (error && (error.message || String(error))) || "";
|
|
174
|
+
if (msg.includes("cannot start a transaction within a transaction")) {
|
|
175
|
+
const dbName = options.dbName || options.userId || "default";
|
|
176
|
+
const currentDepth = this.txDepths.get(dbName) || 0;
|
|
177
|
+
this.txDepths.set(dbName, currentDepth + 1);
|
|
178
|
+
successCallback([]);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (msg.includes("no transaction is active") || msg.includes("cannot rollback") || msg.includes("cannot commit")) {
|
|
182
|
+
const dbName = options.dbName || options.userId || "default";
|
|
183
|
+
this.txDepths.set(dbName, 0);
|
|
184
|
+
successCallback([]);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
72
187
|
errorCallback(error instanceof Error ? error.message : String(error));
|
|
73
188
|
}
|
|
74
189
|
},
|
|
@@ -88,22 +203,11 @@ export class DatabaseNative {
|
|
|
88
203
|
resultToReturn.push(r);
|
|
89
204
|
}
|
|
90
205
|
}
|
|
91
|
-
else if (Array.isArray(rawRows)) {
|
|
92
|
-
resultToReturn = rawRows;
|
|
93
|
-
}
|
|
94
|
-
else if (rawRows._array) {
|
|
95
|
-
resultToReturn = rawRows._array;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// Attach insertId if present
|
|
99
|
-
if (rawRes && rawRes.insertId !== undefined) {
|
|
100
|
-
resultToReturn.insertId = rawRes.insertId;
|
|
101
|
-
resultToReturn.rowsAffected = rawRes.rowsAffected;
|
|
102
206
|
}
|
|
103
207
|
callback(resultToReturn);
|
|
104
208
|
}
|
|
105
209
|
catch (error) {
|
|
106
|
-
callback(
|
|
210
|
+
callback(error instanceof Error ? error.message : String(error));
|
|
107
211
|
}
|
|
108
212
|
},
|
|
109
213
|
selectFromPath: async (dbPath, sqlQuery, callback) => {
|
|
@@ -122,17 +226,11 @@ export class DatabaseNative {
|
|
|
122
226
|
resultToReturn.push(r);
|
|
123
227
|
}
|
|
124
228
|
}
|
|
125
|
-
else if (Array.isArray(rawRows)) {
|
|
126
|
-
resultToReturn = rawRows;
|
|
127
|
-
}
|
|
128
|
-
else if (rawRows._array) {
|
|
129
|
-
resultToReturn = rawRows._array;
|
|
130
|
-
}
|
|
131
229
|
}
|
|
132
230
|
callback(resultToReturn);
|
|
133
231
|
}
|
|
134
232
|
catch (error) {
|
|
135
|
-
callback(
|
|
233
|
+
callback(error instanceof Error ? error.message : String(error));
|
|
136
234
|
}
|
|
137
235
|
},
|
|
138
236
|
createDatabase: async (dbPath, callback) => {
|
|
@@ -140,28 +238,20 @@ export class DatabaseNative {
|
|
|
140
238
|
try {
|
|
141
239
|
const dbName = ((_a = dbPath.split("/").pop()) === null || _a === void 0 ? void 0 : _a.replace(".db", "")) || "default";
|
|
142
240
|
this.getDatabase(dbName);
|
|
143
|
-
callback();
|
|
241
|
+
callback(true);
|
|
144
242
|
}
|
|
145
243
|
catch (error) {
|
|
146
|
-
callback();
|
|
244
|
+
callback(error instanceof Error ? error.message : String(error));
|
|
147
245
|
}
|
|
148
246
|
},
|
|
149
|
-
getDBFilePath:
|
|
150
|
-
callback(
|
|
247
|
+
getDBFilePath: (options, callback) => {
|
|
248
|
+
callback(options.name);
|
|
151
249
|
},
|
|
152
|
-
saveWebDB:
|
|
153
|
-
|
|
154
|
-
callback({
|
|
155
|
-
success: true,
|
|
156
|
-
message: "Not applicable for native",
|
|
157
|
-
});
|
|
250
|
+
saveWebDB: (options, callback, errorCallback) => {
|
|
251
|
+
errorCallback("saveWebDB is only available on web platform");
|
|
158
252
|
},
|
|
159
|
-
exportWebDB:
|
|
160
|
-
|
|
161
|
-
callback({
|
|
162
|
-
success: true,
|
|
163
|
-
message: "Not applicable for native",
|
|
164
|
-
});
|
|
253
|
+
exportWebDB: (options, callback, errorCallback) => {
|
|
254
|
+
errorCallback("exportWebDB is only available on web platform");
|
|
165
255
|
},
|
|
166
256
|
deleteUserData: async (options, callback, errorCallback) => {
|
|
167
257
|
try {
|
|
@@ -179,9 +269,12 @@ export class DatabaseNative {
|
|
|
179
269
|
}
|
|
180
270
|
},
|
|
181
271
|
getEncryptionKey: (options, successCallback, errorCallback) => {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
272
|
+
try {
|
|
273
|
+
successCallback("unvired_default_key");
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
errorCallback();
|
|
277
|
+
}
|
|
185
278
|
},
|
|
186
279
|
};
|
|
187
280
|
}
|