@rozenite/sqlite-plugin 2.1.0 → 2.3.0
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/CHANGELOG.md +22 -0
- package/dist/devtools/assets/{panel-BsdCagVv.js → panel-CLyprPq_.js} +41 -41
- package/dist/devtools/panel.html +1 -1
- package/dist/react-native/cjs/package.json +3 -0
- package/dist/react-native/cjs/react-native.js +37 -0
- package/dist/react-native/cjs/src/react-native/adapters/expo-sqlite.js +121 -0
- package/dist/react-native/cjs/src/react-native/adapters/generic.js +33 -0
- package/dist/react-native/cjs/src/react-native/adapters/index.js +7 -0
- package/dist/react-native/cjs/src/react-native/sqlite-view.js +11 -0
- package/dist/react-native/cjs/src/react-native/useRozeniteSqlitePlugin.js +178 -0
- package/dist/react-native/cjs/src/react-native/useSqliteAgentTools.js +115 -0
- package/dist/react-native/cjs/src/shared/bridge-values.js +126 -0
- package/dist/react-native/cjs/src/shared/protocol.js +4 -0
- package/dist/react-native/cjs/src/shared/sql.js +339 -0
- package/dist/react-native/cjs/src/shared/types.js +2 -0
- package/dist/react-native/package.json +3 -0
- package/dist/react-native/react-native.d.ts +15 -0
- package/dist/react-native/react-native.js +43 -0
- package/dist/react-native/src/react-native/adapters/expo-sqlite.d.ts +28 -0
- package/dist/react-native/src/react-native/adapters/expo-sqlite.js +117 -0
- package/dist/react-native/src/react-native/adapters/generic.d.ts +19 -0
- package/dist/react-native/src/react-native/adapters/generic.js +29 -0
- package/dist/react-native/src/react-native/adapters/index.d.ts +2 -0
- package/dist/react-native/src/react-native/adapters/index.js +2 -0
- package/dist/react-native/src/react-native/sqlite-view.d.ts +5 -0
- package/dist/react-native/src/react-native/sqlite-view.js +7 -0
- package/dist/react-native/src/react-native/useRozeniteSqlitePlugin.d.ts +6 -0
- package/dist/react-native/src/react-native/useRozeniteSqlitePlugin.js +174 -0
- package/dist/react-native/src/react-native/useSqliteAgentTools.d.ts +2 -0
- package/dist/react-native/src/react-native/useSqliteAgentTools.js +111 -0
- package/dist/react-native/src/shared/bridge-values.d.ts +3 -0
- package/dist/react-native/src/shared/bridge-values.js +120 -0
- package/dist/react-native/src/shared/protocol.d.ts +38 -0
- package/dist/react-native/src/shared/protocol.js +1 -0
- package/dist/react-native/src/shared/sql.d.ts +14 -0
- package/dist/react-native/src/shared/sql.js +328 -0
- package/dist/react-native/src/shared/types.d.ts +56 -0
- package/dist/react-native/src/shared/types.js +1 -0
- package/dist/rozenite.json +1 -1
- package/package.json +12 -11
- package/react-native.ts +8 -1
- package/rozenite.config.ts +1 -0
- package/src/__tests__/release-bundle.test.ts +32 -0
- package/tsconfig.json +4 -4
- package/dist/react-native/chunks/bridge-values.require.cjs +0 -2
- package/dist/react-native/chunks/bridge-values.require.js +0 -61
- package/dist/react-native/chunks/index.require.cjs +0 -1
- package/dist/react-native/chunks/index.require.js +0 -107
- package/dist/react-native/chunks/sql.require.cjs +0 -4
- package/dist/react-native/chunks/sql.require.js +0 -219
- package/dist/react-native/chunks/useRozeniteSqlitePlugin.require.cjs +0 -1
- package/dist/react-native/chunks/useRozeniteSqlitePlugin.require.js +0 -283
- package/dist/react-native/index.cjs +0 -1
- package/dist/react-native/index.d.ts +0 -208
- package/dist/react-native/index.js +0 -22
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.escapeSqlString = exports.quoteSqlIdentifier = exports.statementReturnsRows = exports.classifySqlStatement = exports.normalizeSingleStatementSql = exports.getStatementAtCursor = exports.splitSqlStatements = exports.countSqlStatements = void 0;
|
|
4
|
+
const isWhitespace = (char) => /\s/.test(char);
|
|
5
|
+
const countSqlStatements = (sql) => {
|
|
6
|
+
let count = 0;
|
|
7
|
+
let hasToken = false;
|
|
8
|
+
let i = 0;
|
|
9
|
+
let mode = null;
|
|
10
|
+
while (i < sql.length) {
|
|
11
|
+
const char = sql[i];
|
|
12
|
+
const next = sql[i + 1];
|
|
13
|
+
if (mode === 'line-comment') {
|
|
14
|
+
if (char === '\n') {
|
|
15
|
+
mode = null;
|
|
16
|
+
}
|
|
17
|
+
i += 1;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (mode === 'block-comment') {
|
|
21
|
+
if (char === '*' && next === '/') {
|
|
22
|
+
mode = null;
|
|
23
|
+
i += 2;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
i += 1;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (mode === 'single-quote') {
|
|
30
|
+
if (char === "'" && next === "'") {
|
|
31
|
+
i += 2;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (char === "'") {
|
|
35
|
+
mode = null;
|
|
36
|
+
}
|
|
37
|
+
i += 1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (mode === 'double-quote') {
|
|
41
|
+
if (char === '"' && next === '"') {
|
|
42
|
+
i += 2;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (char === '"') {
|
|
46
|
+
mode = null;
|
|
47
|
+
}
|
|
48
|
+
i += 1;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (mode === 'backtick') {
|
|
52
|
+
if (char === '`' && next === '`') {
|
|
53
|
+
i += 2;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (char === '`') {
|
|
57
|
+
mode = null;
|
|
58
|
+
}
|
|
59
|
+
i += 1;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (mode === 'bracket') {
|
|
63
|
+
if (char === ']' && next === ']') {
|
|
64
|
+
i += 2;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (char === ']') {
|
|
68
|
+
mode = null;
|
|
69
|
+
}
|
|
70
|
+
i += 1;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (char === '-' && next === '-') {
|
|
74
|
+
mode = 'line-comment';
|
|
75
|
+
i += 2;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (char === '/' && next === '*') {
|
|
79
|
+
mode = 'block-comment';
|
|
80
|
+
i += 2;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (char === "'") {
|
|
84
|
+
mode = 'single-quote';
|
|
85
|
+
hasToken = true;
|
|
86
|
+
i += 1;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (char === '"') {
|
|
90
|
+
mode = 'double-quote';
|
|
91
|
+
hasToken = true;
|
|
92
|
+
i += 1;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (char === '`') {
|
|
96
|
+
mode = 'backtick';
|
|
97
|
+
hasToken = true;
|
|
98
|
+
i += 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (char === '[') {
|
|
102
|
+
mode = 'bracket';
|
|
103
|
+
hasToken = true;
|
|
104
|
+
i += 1;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (char === ';') {
|
|
108
|
+
if (hasToken) {
|
|
109
|
+
count += 1;
|
|
110
|
+
hasToken = false;
|
|
111
|
+
}
|
|
112
|
+
i += 1;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (!isWhitespace(char)) {
|
|
116
|
+
hasToken = true;
|
|
117
|
+
}
|
|
118
|
+
i += 1;
|
|
119
|
+
}
|
|
120
|
+
if (hasToken) {
|
|
121
|
+
count += 1;
|
|
122
|
+
}
|
|
123
|
+
return count;
|
|
124
|
+
};
|
|
125
|
+
exports.countSqlStatements = countSqlStatements;
|
|
126
|
+
const splitSqlStatements = (sql) => {
|
|
127
|
+
const segments = [];
|
|
128
|
+
let hasToken = false;
|
|
129
|
+
let segmentStart = 0;
|
|
130
|
+
let i = 0;
|
|
131
|
+
let mode = null;
|
|
132
|
+
const pushSegment = (end) => {
|
|
133
|
+
const text = sql.slice(segmentStart, end).trim();
|
|
134
|
+
if (text) {
|
|
135
|
+
segments.push({
|
|
136
|
+
text,
|
|
137
|
+
start: segmentStart,
|
|
138
|
+
end,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
segmentStart = end + 1;
|
|
142
|
+
hasToken = false;
|
|
143
|
+
};
|
|
144
|
+
while (i < sql.length) {
|
|
145
|
+
const char = sql[i];
|
|
146
|
+
const next = sql[i + 1];
|
|
147
|
+
if (mode === 'line-comment') {
|
|
148
|
+
if (char === '\n') {
|
|
149
|
+
mode = null;
|
|
150
|
+
}
|
|
151
|
+
i += 1;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (mode === 'block-comment') {
|
|
155
|
+
if (char === '*' && next === '/') {
|
|
156
|
+
mode = null;
|
|
157
|
+
i += 2;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
i += 1;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (mode === 'single-quote') {
|
|
164
|
+
if (char === "'" && next === "'") {
|
|
165
|
+
i += 2;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (char === "'") {
|
|
169
|
+
mode = null;
|
|
170
|
+
}
|
|
171
|
+
i += 1;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (mode === 'double-quote') {
|
|
175
|
+
if (char === '"' && next === '"') {
|
|
176
|
+
i += 2;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (char === '"') {
|
|
180
|
+
mode = null;
|
|
181
|
+
}
|
|
182
|
+
i += 1;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (mode === 'backtick') {
|
|
186
|
+
if (char === '`' && next === '`') {
|
|
187
|
+
i += 2;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (char === '`') {
|
|
191
|
+
mode = null;
|
|
192
|
+
}
|
|
193
|
+
i += 1;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (mode === 'bracket') {
|
|
197
|
+
if (char === ']' && next === ']') {
|
|
198
|
+
i += 2;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (char === ']') {
|
|
202
|
+
mode = null;
|
|
203
|
+
}
|
|
204
|
+
i += 1;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (char === '-' && next === '-') {
|
|
208
|
+
mode = 'line-comment';
|
|
209
|
+
i += 2;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (char === '/' && next === '*') {
|
|
213
|
+
mode = 'block-comment';
|
|
214
|
+
i += 2;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (char === "'") {
|
|
218
|
+
mode = 'single-quote';
|
|
219
|
+
hasToken = true;
|
|
220
|
+
i += 1;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (char === '"') {
|
|
224
|
+
mode = 'double-quote';
|
|
225
|
+
hasToken = true;
|
|
226
|
+
i += 1;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (char === '`') {
|
|
230
|
+
mode = 'backtick';
|
|
231
|
+
hasToken = true;
|
|
232
|
+
i += 1;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (char === '[') {
|
|
236
|
+
mode = 'bracket';
|
|
237
|
+
hasToken = true;
|
|
238
|
+
i += 1;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (char === ';') {
|
|
242
|
+
if (hasToken) {
|
|
243
|
+
pushSegment(i);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
segmentStart = i + 1;
|
|
247
|
+
}
|
|
248
|
+
i += 1;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (!isWhitespace(char)) {
|
|
252
|
+
hasToken = true;
|
|
253
|
+
}
|
|
254
|
+
i += 1;
|
|
255
|
+
}
|
|
256
|
+
if (hasToken) {
|
|
257
|
+
pushSegment(sql.length);
|
|
258
|
+
}
|
|
259
|
+
return segments;
|
|
260
|
+
};
|
|
261
|
+
exports.splitSqlStatements = splitSqlStatements;
|
|
262
|
+
const getStatementAtCursor = (sql, cursor) => {
|
|
263
|
+
const segments = (0, exports.splitSqlStatements)(sql);
|
|
264
|
+
if (segments.length === 0) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const match = segments.find((segment) => cursor >= segment.start && cursor <= segment.end + 1);
|
|
268
|
+
return match ?? segments[0];
|
|
269
|
+
};
|
|
270
|
+
exports.getStatementAtCursor = getStatementAtCursor;
|
|
271
|
+
const normalizeSingleStatementSql = (sql) => {
|
|
272
|
+
const statementCount = (0, exports.countSqlStatements)(sql);
|
|
273
|
+
if (statementCount === 0) {
|
|
274
|
+
throw new Error('Query cannot be empty.');
|
|
275
|
+
}
|
|
276
|
+
if (statementCount > 1) {
|
|
277
|
+
throw new Error('Only a single SQL statement is supported in v1.');
|
|
278
|
+
}
|
|
279
|
+
return sql.trim().replace(/;\s*$/, '').trim();
|
|
280
|
+
};
|
|
281
|
+
exports.normalizeSingleStatementSql = normalizeSingleStatementSql;
|
|
282
|
+
const readLeadingKeyword = (sql) => {
|
|
283
|
+
let i = 0;
|
|
284
|
+
while (i < sql.length) {
|
|
285
|
+
const char = sql[i];
|
|
286
|
+
const next = sql[i + 1];
|
|
287
|
+
if (isWhitespace(char)) {
|
|
288
|
+
i += 1;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (char === '-' && next === '-') {
|
|
292
|
+
i += 2;
|
|
293
|
+
while (i < sql.length && sql[i] !== '\n') {
|
|
294
|
+
i += 1;
|
|
295
|
+
}
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (char === '/' && next === '*') {
|
|
299
|
+
i += 2;
|
|
300
|
+
while (i < sql.length && !(sql[i] === '*' && sql[i + 1] === '/')) {
|
|
301
|
+
i += 1;
|
|
302
|
+
}
|
|
303
|
+
i += 2;
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
const start = i;
|
|
309
|
+
while (i < sql.length && /[A-Za-z]/.test(sql[i])) {
|
|
310
|
+
i += 1;
|
|
311
|
+
}
|
|
312
|
+
return sql.slice(start, i).toLowerCase();
|
|
313
|
+
};
|
|
314
|
+
const classifySqlStatement = (sql) => {
|
|
315
|
+
const keyword = readLeadingKeyword(sql);
|
|
316
|
+
if (keyword === 'select' ||
|
|
317
|
+
keyword === 'insert' ||
|
|
318
|
+
keyword === 'update' ||
|
|
319
|
+
keyword === 'delete' ||
|
|
320
|
+
keyword === 'pragma' ||
|
|
321
|
+
keyword === 'create' ||
|
|
322
|
+
keyword === 'alter' ||
|
|
323
|
+
keyword === 'drop' ||
|
|
324
|
+
keyword === 'explain' ||
|
|
325
|
+
keyword === 'with') {
|
|
326
|
+
return keyword;
|
|
327
|
+
}
|
|
328
|
+
return 'other';
|
|
329
|
+
};
|
|
330
|
+
exports.classifySqlStatement = classifySqlStatement;
|
|
331
|
+
const statementReturnsRows = (statementType) => statementType === 'select' ||
|
|
332
|
+
statementType === 'pragma' ||
|
|
333
|
+
statementType === 'explain' ||
|
|
334
|
+
statementType === 'with';
|
|
335
|
+
exports.statementReturnsRows = statementReturnsRows;
|
|
336
|
+
const quoteSqlIdentifier = (identifier) => `"${identifier.replace(/"/g, '""')}"`;
|
|
337
|
+
exports.quoteSqlIdentifier = quoteSqlIdentifier;
|
|
338
|
+
const escapeSqlString = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
339
|
+
exports.escapeSqlString = escapeSqlString;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type { SqliteAdapter, SqliteDatabaseInfo, SqliteDatabaseNode, SqliteExecuteStatementsError, SqliteExecuteStatementsRunner, SqliteQueryMetadata, SqliteQueryParams, SqliteQueryResult, SqliteScriptResult, SqliteScriptStatementResult, SqliteStatementExecutionResult, SqliteStatementInput, SqliteStatementType, } from './src/shared/types';
|
|
2
|
+
export type { CreateSqliteAdapterOptions } from './src/react-native/adapters/generic';
|
|
3
|
+
export type { CreateExpoSqliteAdapterOptions, ExpoSqliteLike, } from './src/react-native/adapters/expo-sqlite';
|
|
4
|
+
export type { SqlStatementSegment } from './src/shared/sql';
|
|
5
|
+
type CreateSqliteAdapter = typeof import('./src/react-native/adapters').createSqliteAdapter;
|
|
6
|
+
type CreateExpoSqliteAdapter = typeof import('./src/react-native/adapters').createExpoSqliteAdapter;
|
|
7
|
+
export declare let createSqliteAdapter: CreateSqliteAdapter;
|
|
8
|
+
export declare let createExpoSqliteAdapter: CreateExpoSqliteAdapter;
|
|
9
|
+
export declare let useRozeniteSqlitePlugin: typeof import('./src/react-native/useRozeniteSqlitePlugin').useRozeniteSqlitePlugin;
|
|
10
|
+
export declare let classifySqlStatement: typeof import('./src/shared/sql').classifySqlStatement;
|
|
11
|
+
export declare let normalizeSingleStatementSql: typeof import('./src/shared/sql').normalizeSingleStatementSql;
|
|
12
|
+
export declare let splitSqlStatements: typeof import('./src/shared/sql').splitSqlStatements;
|
|
13
|
+
export declare let statementReturnsRows: typeof import('./src/shared/sql').statementReturnsRows;
|
|
14
|
+
export declare let decodeSqliteBridgeValue: typeof import('./src/shared/bridge-values').decodeSqliteBridgeValue;
|
|
15
|
+
export declare let formatSqliteError: typeof import('./src/shared/bridge-values').formatSqliteError;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export let createSqliteAdapter;
|
|
2
|
+
export let createExpoSqliteAdapter;
|
|
3
|
+
export let useRozeniteSqlitePlugin;
|
|
4
|
+
export let classifySqlStatement;
|
|
5
|
+
export let normalizeSingleStatementSql;
|
|
6
|
+
export let splitSqlStatements;
|
|
7
|
+
export let statementReturnsRows;
|
|
8
|
+
export let decodeSqliteBridgeValue;
|
|
9
|
+
export let formatSqliteError;
|
|
10
|
+
const isDev = process.env.NODE_ENV !== 'production';
|
|
11
|
+
const isWeb = typeof window !== 'undefined' && window.navigator.product !== 'ReactNative';
|
|
12
|
+
const isServer = typeof window === 'undefined' && typeof lynx === 'undefined';
|
|
13
|
+
if (isDev && !isWeb && !isServer) {
|
|
14
|
+
createSqliteAdapter = require('./src/react-native/adapters').createSqliteAdapter;
|
|
15
|
+
createExpoSqliteAdapter = require('./src/react-native/adapters').createExpoSqliteAdapter;
|
|
16
|
+
useRozeniteSqlitePlugin =
|
|
17
|
+
require('./src/react-native/useRozeniteSqlitePlugin').useRozeniteSqlitePlugin;
|
|
18
|
+
classifySqlStatement = require('./src/shared/sql').classifySqlStatement;
|
|
19
|
+
normalizeSingleStatementSql = require('./src/shared/sql').normalizeSingleStatementSql;
|
|
20
|
+
splitSqlStatements = require('./src/shared/sql').splitSqlStatements;
|
|
21
|
+
statementReturnsRows = require('./src/shared/sql').statementReturnsRows;
|
|
22
|
+
decodeSqliteBridgeValue = require('./src/shared/bridge-values').decodeSqliteBridgeValue;
|
|
23
|
+
formatSqliteError = require('./src/shared/bridge-values').formatSqliteError;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
createSqliteAdapter = (options) => ({
|
|
27
|
+
id: options.adapterId ?? 'sqlite',
|
|
28
|
+
name: options.adapterName ?? 'SQLite',
|
|
29
|
+
databases: [],
|
|
30
|
+
});
|
|
31
|
+
createExpoSqliteAdapter = (options) => ({
|
|
32
|
+
id: options.adapterId ?? 'expo-sqlite',
|
|
33
|
+
name: options.adapterName ?? 'Expo SQLite',
|
|
34
|
+
databases: [],
|
|
35
|
+
});
|
|
36
|
+
useRozeniteSqlitePlugin = () => null;
|
|
37
|
+
classifySqlStatement = () => 'other';
|
|
38
|
+
normalizeSingleStatementSql = (sql) => sql;
|
|
39
|
+
splitSqlStatements = () => [];
|
|
40
|
+
statementReturnsRows = (_type) => false;
|
|
41
|
+
decodeSqliteBridgeValue = (value) => value;
|
|
42
|
+
formatSqliteError = () => 'Unknown SQLite error.';
|
|
43
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { SqliteAdapter } from '../../shared/types';
|
|
2
|
+
export type ExpoSqliteLike = {
|
|
3
|
+
getAllAsync: (...args: any[]) => Promise<Record<string, unknown>[]>;
|
|
4
|
+
runAsync: (...args: any[]) => Promise<{
|
|
5
|
+
changes: number;
|
|
6
|
+
lastInsertRowId: number;
|
|
7
|
+
}>;
|
|
8
|
+
};
|
|
9
|
+
type SingleDatabaseOptions = {
|
|
10
|
+
database: ExpoSqliteLike | {
|
|
11
|
+
database: ExpoSqliteLike;
|
|
12
|
+
name?: string;
|
|
13
|
+
};
|
|
14
|
+
adapterId?: string;
|
|
15
|
+
adapterName?: string;
|
|
16
|
+
databaseName?: string;
|
|
17
|
+
};
|
|
18
|
+
type MultiDatabaseOptions = {
|
|
19
|
+
databases: Record<string, ExpoSqliteLike | {
|
|
20
|
+
database: ExpoSqliteLike;
|
|
21
|
+
name?: string;
|
|
22
|
+
}>;
|
|
23
|
+
adapterId?: string;
|
|
24
|
+
adapterName?: string;
|
|
25
|
+
};
|
|
26
|
+
export type CreateExpoSqliteAdapterOptions = SingleDatabaseOptions | MultiDatabaseOptions;
|
|
27
|
+
export declare const createExpoSqliteAdapter: (options: CreateExpoSqliteAdapterOptions) => SqliteAdapter;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { classifySqlStatement, normalizeSingleStatementSql, statementReturnsRows, } from '../../shared/sql';
|
|
2
|
+
import { decodeSqliteBridgeValue, formatSqliteError } from '../../shared/bridge-values';
|
|
3
|
+
import { createSqliteAdapter } from './generic';
|
|
4
|
+
const now = () => typeof performance !== 'undefined' && typeof performance.now === 'function'
|
|
5
|
+
? performance.now()
|
|
6
|
+
: Date.now();
|
|
7
|
+
const safeError = (error) => formatSqliteError(error);
|
|
8
|
+
const createExecuteStatementsError = (message, options = {}) => Object.assign(new Error(message), options);
|
|
9
|
+
const toBridgeSafeValue = (value) => {
|
|
10
|
+
if (value == null ||
|
|
11
|
+
typeof value === 'string' ||
|
|
12
|
+
typeof value === 'number' ||
|
|
13
|
+
typeof value === 'boolean') {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
if (value instanceof Uint8Array) {
|
|
17
|
+
return Array.from(value);
|
|
18
|
+
}
|
|
19
|
+
if (value instanceof ArrayBuffer) {
|
|
20
|
+
return Array.from(new Uint8Array(value));
|
|
21
|
+
}
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
return value.map(toBridgeSafeValue);
|
|
24
|
+
}
|
|
25
|
+
if (typeof value === 'object') {
|
|
26
|
+
return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => [key, toBridgeSafeValue(nestedValue)]));
|
|
27
|
+
}
|
|
28
|
+
return String(value);
|
|
29
|
+
};
|
|
30
|
+
const normalizeRows = (rows) => rows.map((row) => Object.fromEntries(Object.entries(row).map(([key, value]) => [key, toBridgeSafeValue(value)])));
|
|
31
|
+
const executeSingleStatement = async (database, { sql, params }) => {
|
|
32
|
+
const normalizedSql = normalizeSingleStatementSql(sql);
|
|
33
|
+
const statementType = classifySqlStatement(normalizedSql);
|
|
34
|
+
const startedAt = now();
|
|
35
|
+
const decodedParams = params === undefined ? undefined : decodeSqliteBridgeValue(params);
|
|
36
|
+
if (statementReturnsRows(statementType)) {
|
|
37
|
+
const rows = normalizeRows(decodedParams === undefined
|
|
38
|
+
? await database.getAllAsync(normalizedSql)
|
|
39
|
+
: await database.getAllAsync(normalizedSql, decodedParams));
|
|
40
|
+
const durationMs = now() - startedAt;
|
|
41
|
+
return {
|
|
42
|
+
rows,
|
|
43
|
+
columns: Object.keys(rows[0] ?? {}),
|
|
44
|
+
metadata: {
|
|
45
|
+
statementType,
|
|
46
|
+
rowCount: rows.length,
|
|
47
|
+
changes: null,
|
|
48
|
+
lastInsertRowId: null,
|
|
49
|
+
durationMs,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const result = decodedParams === undefined
|
|
54
|
+
? await database.runAsync(normalizedSql)
|
|
55
|
+
: await database.runAsync(normalizedSql, decodedParams);
|
|
56
|
+
const durationMs = now() - startedAt;
|
|
57
|
+
return {
|
|
58
|
+
rows: [],
|
|
59
|
+
columns: [],
|
|
60
|
+
metadata: {
|
|
61
|
+
statementType,
|
|
62
|
+
rowCount: 0,
|
|
63
|
+
changes: typeof result.changes === 'number' ? result.changes : null,
|
|
64
|
+
lastInsertRowId: typeof result.lastInsertRowId === 'number' ? result.lastInsertRowId : null,
|
|
65
|
+
durationMs,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const createExpoExecuteStatementsRunner = (database) => {
|
|
70
|
+
return async (statements) => {
|
|
71
|
+
const results = [];
|
|
72
|
+
for (let index = 0; index < statements.length; index += 1) {
|
|
73
|
+
try {
|
|
74
|
+
results.push(await executeSingleStatement(database, statements[index]));
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
throw createExecuteStatementsError(safeError(error), {
|
|
78
|
+
completedResults: results,
|
|
79
|
+
failedStatementIndex: index,
|
|
80
|
+
cause: error,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return results;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
const resolveDatabaseConfig = (config) => ('database' in config ? config : { database: config });
|
|
88
|
+
export const createExpoSqliteAdapter = (options) => {
|
|
89
|
+
const genericOptions = 'databases' in options
|
|
90
|
+
? {
|
|
91
|
+
adapterId: options.adapterId ?? 'expo-sqlite',
|
|
92
|
+
adapterName: options.adapterName ?? 'Expo SQLite',
|
|
93
|
+
databases: Object.fromEntries(Object.entries(options.databases).map(([key, config]) => {
|
|
94
|
+
const resolved = resolveDatabaseConfig(config);
|
|
95
|
+
return [
|
|
96
|
+
key,
|
|
97
|
+
{
|
|
98
|
+
name: resolved.name ?? key,
|
|
99
|
+
executeStatements: createExpoExecuteStatementsRunner(resolved.database),
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
})),
|
|
103
|
+
}
|
|
104
|
+
: {
|
|
105
|
+
adapterId: options.adapterId ?? 'expo-sqlite',
|
|
106
|
+
adapterName: options.adapterName ?? 'Expo SQLite',
|
|
107
|
+
databaseName: options.databaseName,
|
|
108
|
+
database: (() => {
|
|
109
|
+
const resolved = resolveDatabaseConfig(options.database);
|
|
110
|
+
return {
|
|
111
|
+
name: resolved.name ?? options.databaseName,
|
|
112
|
+
executeStatements: createExpoExecuteStatementsRunner(resolved.database),
|
|
113
|
+
};
|
|
114
|
+
})(),
|
|
115
|
+
};
|
|
116
|
+
return createSqliteAdapter(genericOptions);
|
|
117
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SqliteAdapter, SqliteExecuteStatementsRunner } from '../../shared/types';
|
|
2
|
+
type SqliteDatabaseConfig = {
|
|
3
|
+
name?: string;
|
|
4
|
+
executeStatements: SqliteExecuteStatementsRunner;
|
|
5
|
+
};
|
|
6
|
+
type SingleDatabaseOptions = {
|
|
7
|
+
database: SqliteExecuteStatementsRunner | SqliteDatabaseConfig;
|
|
8
|
+
adapterId?: string;
|
|
9
|
+
adapterName?: string;
|
|
10
|
+
databaseName?: string;
|
|
11
|
+
};
|
|
12
|
+
type MultiDatabaseOptions = {
|
|
13
|
+
databases: Record<string, SqliteExecuteStatementsRunner | SqliteDatabaseConfig>;
|
|
14
|
+
adapterId?: string;
|
|
15
|
+
adapterName?: string;
|
|
16
|
+
};
|
|
17
|
+
export type CreateSqliteAdapterOptions = SingleDatabaseOptions | MultiDatabaseOptions;
|
|
18
|
+
export declare const createSqliteAdapter: (options: CreateSqliteAdapterOptions) => SqliteAdapter;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const slugify = (value) => value
|
|
2
|
+
.trim()
|
|
3
|
+
.toLowerCase()
|
|
4
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
5
|
+
.replace(/^-+|-+$/g, '') || 'database';
|
|
6
|
+
const createDatabaseId = (adapterId, seed, index) => `${adapterId}__${slugify(seed)}__${index.toString(36)}`;
|
|
7
|
+
const resolveDatabaseConfig = (config) => typeof config === 'function' ? { executeStatements: config } : config;
|
|
8
|
+
const toDatabaseNode = (adapterId, databaseKey, config, index, fallbackName) => {
|
|
9
|
+
const resolved = resolveDatabaseConfig(config);
|
|
10
|
+
const name = resolved.name ?? fallbackName ?? databaseKey;
|
|
11
|
+
return {
|
|
12
|
+
id: createDatabaseId(adapterId, `${databaseKey}-${name}`, index),
|
|
13
|
+
name,
|
|
14
|
+
executeStatements: resolved.executeStatements,
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export const createSqliteAdapter = (options) => {
|
|
18
|
+
const { adapterId = 'sqlite', adapterName = 'SQLite' } = options;
|
|
19
|
+
const databases = 'databases' in options
|
|
20
|
+
? Object.entries(options.databases).map(([key, config], index) => toDatabaseNode(adapterId, key, config, index))
|
|
21
|
+
: [
|
|
22
|
+
toDatabaseNode(adapterId, options.databaseName ?? 'default', options.database, 0, options.databaseName ?? 'Default Database'),
|
|
23
|
+
];
|
|
24
|
+
return {
|
|
25
|
+
id: adapterId,
|
|
26
|
+
name: adapterName,
|
|
27
|
+
databases,
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SqliteAdapter, SqliteDatabaseInfo, SqliteStatementInput } from '../shared/types';
|
|
2
|
+
export type SqliteDatabaseView = SqliteDatabaseInfo & {
|
|
3
|
+
executeStatements: (statements: SqliteStatementInput[]) => ReturnType<SqliteAdapter['databases'][number]['executeStatements']>;
|
|
4
|
+
};
|
|
5
|
+
export declare const createSqliteDatabaseViews: (adapters: SqliteAdapter[]) => SqliteDatabaseView[];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const createSqliteDatabaseViews = (adapters) => adapters.flatMap((adapter) => adapter.databases.map((database) => ({
|
|
2
|
+
id: database.id,
|
|
3
|
+
name: database.name,
|
|
4
|
+
adapterId: adapter.id,
|
|
5
|
+
adapterName: adapter.name,
|
|
6
|
+
executeStatements: database.executeStatements,
|
|
7
|
+
})));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type SqliteEventMap } from '../shared/protocol';
|
|
2
|
+
import type { SqliteAdapter } from '../shared/types';
|
|
3
|
+
export type RozeniteSqlitePluginOptions = {
|
|
4
|
+
adapters: SqliteAdapter[];
|
|
5
|
+
};
|
|
6
|
+
export declare const useRozeniteSqlitePlugin: ({ adapters }: RozeniteSqlitePluginOptions) => import("@rozenite/plugin-bridge").RozeniteDevToolsRequestClient<SqliteEventMap> | null;
|