@ccheever/exact-ibex-runtime 0.1.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.
Files changed (161) hide show
  1. package/package.json +63 -0
  2. package/src/abort/AbortController.ts +23 -0
  3. package/src/abort/AbortSignal.ts +152 -0
  4. package/src/abort/index.ts +2 -0
  5. package/src/accessibility.ts +12 -0
  6. package/src/arraybuffer-detach.ts +109 -0
  7. package/src/base64/base64.ts +168 -0
  8. package/src/base64/index.ts +1 -0
  9. package/src/blob/Blob.ts +259 -0
  10. package/src/blob/File.ts +59 -0
  11. package/src/blob/FormData.ts +323 -0
  12. package/src/blob/index.ts +3 -0
  13. package/src/bootstrap.ts +1946 -0
  14. package/src/broadcast/BroadcastChannel.ts +280 -0
  15. package/src/broadcast/index.ts +5 -0
  16. package/src/cache/Cache.ts +349 -0
  17. package/src/cache/CacheStorage.ts +89 -0
  18. package/src/cache/index.ts +27 -0
  19. package/src/camera/index.ts +6202 -0
  20. package/src/camera/processor.worker.ts +194 -0
  21. package/src/camera/scene.ts +195 -0
  22. package/src/clipboard/Clipboard.ts +129 -0
  23. package/src/clipboard/ClipboardItem.ts +97 -0
  24. package/src/clipboard/index.ts +6 -0
  25. package/src/clone/index.ts +1 -0
  26. package/src/clone/structuredClone.ts +389 -0
  27. package/src/clone/transferableSymbols.ts +2 -0
  28. package/src/compression/CompressionStream.ts +146 -0
  29. package/src/compression/DecompressionStream.ts +342 -0
  30. package/src/compression/index.ts +4 -0
  31. package/src/console/Console.ts +341 -0
  32. package/src/console/index.ts +2 -0
  33. package/src/core/accessibility-state.ts +263 -0
  34. package/src/core/accessibility.ts +184 -0
  35. package/src/core/agent-state.ts +37 -0
  36. package/src/core/diagnostics-logs.ts +144 -0
  37. package/src/core/host-call-bridge.ts +16 -0
  38. package/src/core/i18n-helpers.ts +189 -0
  39. package/src/core/locale-state.ts +253 -0
  40. package/src/core/locale.ts +95 -0
  41. package/src/crypto/Crypto.ts +2743 -0
  42. package/src/crypto/index.ts +1 -0
  43. package/src/diagnostics/logs.ts +7 -0
  44. package/src/encoding/TextDecoder.ts +1181 -0
  45. package/src/encoding/TextDecoderStream.ts +58 -0
  46. package/src/encoding/TextEncoder.ts +180 -0
  47. package/src/encoding/TextEncoderStream.ts +39 -0
  48. package/src/encoding/index.ts +8 -0
  49. package/src/events/CloseEvent.ts +91 -0
  50. package/src/events/DOMException.ts +409 -0
  51. package/src/events/ErrorEvent.ts +39 -0
  52. package/src/events/Event.ts +151 -0
  53. package/src/events/EventTarget.ts +280 -0
  54. package/src/events/FocusEvent.ts +27 -0
  55. package/src/events/KeyboardEvent.ts +46 -0
  56. package/src/events/MessageEvent.ts +61 -0
  57. package/src/events/ProgressEvent.ts +33 -0
  58. package/src/events/PromiseRejectionEvent.ts +31 -0
  59. package/src/events/index.ts +52 -0
  60. package/src/eventsource/EventSource.ts +371 -0
  61. package/src/eventsource/index.ts +2 -0
  62. package/src/fetch/Headers.ts +642 -0
  63. package/src/fetch/Request.ts +760 -0
  64. package/src/fetch/Response.ts +543 -0
  65. package/src/fetch/body.ts +1256 -0
  66. package/src/fetch/cookie-jar.ts +566 -0
  67. package/src/fetch/demo.ts +207 -0
  68. package/src/fetch/errors.ts +101 -0
  69. package/src/fetch/fetch.ts +2610 -0
  70. package/src/fetch/index.ts +101 -0
  71. package/src/fetch/native-bridge.ts +65 -0
  72. package/src/fetch/types.ts +258 -0
  73. package/src/filereader/FileReader.ts +236 -0
  74. package/src/filereader/index.ts +1 -0
  75. package/src/fs/Dirent.ts +39 -0
  76. package/src/fs/ExactFile.ts +450 -0
  77. package/src/fs/Stats.ts +80 -0
  78. package/src/fs/index.ts +944 -0
  79. package/src/fs/promises.ts +386 -0
  80. package/src/fs/shared.ts +328 -0
  81. package/src/http-server/index.js +697 -0
  82. package/src/http-server/index.ts +27 -0
  83. package/src/identity.generated.ts +14 -0
  84. package/src/index.ts +283 -0
  85. package/src/indexeddb/IDBCursor.ts +188 -0
  86. package/src/indexeddb/IDBDatabase.ts +343 -0
  87. package/src/indexeddb/IDBFactory.ts +269 -0
  88. package/src/indexeddb/IDBIndex.ts +194 -0
  89. package/src/indexeddb/IDBKeyRange.ts +109 -0
  90. package/src/indexeddb/IDBObjectStore.ts +468 -0
  91. package/src/indexeddb/IDBRequest.ts +163 -0
  92. package/src/indexeddb/IDBTransaction.ts +207 -0
  93. package/src/indexeddb/index.ts +34 -0
  94. package/src/indexeddb/utils.ts +52 -0
  95. package/src/inspect/index.ts +1 -0
  96. package/src/inspect/inspect.ts +465 -0
  97. package/src/internal/detect.ts +104 -0
  98. package/src/locale.ts +10 -0
  99. package/src/location/index.ts +1059 -0
  100. package/src/locks/LockManager.ts +460 -0
  101. package/src/locks/index.ts +12 -0
  102. package/src/media/VideoFrame.ts +58 -0
  103. package/src/messaging/MessageChannel.ts +31 -0
  104. package/src/messaging/MessagePort.ts +180 -0
  105. package/src/messaging/index.ts +2 -0
  106. package/src/messaging.ts +247 -0
  107. package/src/native/NativeModules.ts +354 -0
  108. package/src/native/index.ts +1 -0
  109. package/src/navigator/Navigator.ts +351 -0
  110. package/src/navigator/index.ts +1 -0
  111. package/src/node/Buffer.ts +1786 -0
  112. package/src/node/index.ts +4 -0
  113. package/src/node/path.ts +495 -0
  114. package/src/node/process.ts +2528 -0
  115. package/src/performance/Performance.ts +532 -0
  116. package/src/performance/index.ts +21 -0
  117. package/src/polyfills/array.ts +236 -0
  118. package/src/polyfills/arraybuffer.ts +172 -0
  119. package/src/polyfills/groupby.ts +85 -0
  120. package/src/polyfills/index.ts +85 -0
  121. package/src/polyfills/intl.ts +1956 -0
  122. package/src/polyfills/iterator.ts +479 -0
  123. package/src/polyfills/promise.ts +37 -0
  124. package/src/polyfills/set.ts +245 -0
  125. package/src/polyfills/string.ts +85 -0
  126. package/src/polyfills/typedarray.ts +110 -0
  127. package/src/promise-rejection-tracking.ts +464 -0
  128. package/src/react-native/index.ts +388 -0
  129. package/src/runtime-entry.ts +55 -0
  130. package/src/scheduling/AnimationFrame.ts +105 -0
  131. package/src/scheduling/IdleCallback.ts +167 -0
  132. package/src/scheduling/index.ts +13 -0
  133. package/src/security/Capabilities.ts +1146 -0
  134. package/src/security/Permissions.ts +392 -0
  135. package/src/security/capability-bits.generated.ts +63 -0
  136. package/src/security/index.ts +16 -0
  137. package/src/sqlite/Database.ts +456 -0
  138. package/src/sqlite/Statement.ts +206 -0
  139. package/src/sqlite/constants.ts +79 -0
  140. package/src/sqlite/errors.ts +25 -0
  141. package/src/sqlite/index.ts +34 -0
  142. package/src/sqlite/module.js +438 -0
  143. package/src/storage/Storage.ts +291 -0
  144. package/src/storage/StorageManager.ts +91 -0
  145. package/src/storage/index.ts +3 -0
  146. package/src/stream-compat.ts +47 -0
  147. package/src/streams/ReadableStream.ts +4131 -0
  148. package/src/streams/TransformStream.ts +375 -0
  149. package/src/streams/WritableStream.ts +866 -0
  150. package/src/streams/index.ts +41 -0
  151. package/src/timers/Timers.ts +296 -0
  152. package/src/timers/index.ts +11 -0
  153. package/src/url/URL.ts +656 -0
  154. package/src/url/URLPattern.ts +850 -0
  155. package/src/url/URLSearchParams.ts +244 -0
  156. package/src/url/index.ts +9 -0
  157. package/src/websocket/WebSocket.ts +770 -0
  158. package/src/websocket/WebSocketError.ts +52 -0
  159. package/src/websocket/WebSocketStream.ts +628 -0
  160. package/src/websocket/index.ts +7 -0
  161. package/src/window/index.ts +872 -0
@@ -0,0 +1,343 @@
1
+ /**
2
+ * IDBDatabase - IndexedDB Database
3
+ *
4
+ * Represents an open connection to a database. Backed by a SQLite database.
5
+ *
6
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase
7
+ */
8
+
9
+ import { IDBTransaction, type IDBTransactionMode } from './IDBTransaction';
10
+ import { IDBObjectStore, type IDBObjectStoreParameters } from './IDBObjectStore';
11
+ import { IDBIndex } from './IDBIndex';
12
+ import { DOMException, makeDOMStringList, sanitizeName } from './utils';
13
+
14
+ export class IDBDatabase {
15
+ readonly name: string;
16
+ private _version: number;
17
+ private _objectStores: Map<string, { options: IDBObjectStoreParameters; indexes: Map<string, any> }> = new Map();
18
+ private _closed = false;
19
+ /** @internal - SQLite database wrapper */
20
+ _sqliteDb: any;
21
+ /** @internal - The active versionchange transaction during upgradeneeded, if any */
22
+ _upgradeTransaction: IDBTransaction | null = null;
23
+ /** @internal - EventTarget listeners keyed by event type */
24
+ private _listeners: Record<string, Function[]> = {};
25
+
26
+ onclose: ((event: any) => void) | null = null;
27
+ onversionchange: ((event: any) => void) | null = null;
28
+ onerror: ((event: any) => void) | null = null;
29
+ onabort: ((event: any) => void) | null = null;
30
+
31
+ constructor(name: string, version: number, sqliteDb: any) {
32
+ this.name = name;
33
+ this._version = version;
34
+ this._sqliteDb = sqliteDb;
35
+
36
+ // Initialize meta tables
37
+ this._initMeta();
38
+ // Load existing object store definitions
39
+ this._loadStoreDefinitions();
40
+ }
41
+
42
+ get version(): number {
43
+ return this._version;
44
+ }
45
+
46
+ get objectStoreNames(): any {
47
+ return makeDOMStringList(Array.from(this._objectStores.keys()).sort());
48
+ }
49
+
50
+ addEventListener(type: string, fn: Function): void {
51
+ if (!this._listeners[type]) this._listeners[type] = [];
52
+ this._listeners[type].push(fn);
53
+ }
54
+
55
+ removeEventListener(type: string, fn: Function): void {
56
+ const list = this._listeners[type];
57
+ if (list) this._listeners[type] = list.filter(f => f !== fn);
58
+ }
59
+
60
+ /**
61
+ * Create a new object store. Only valid during upgradeneeded.
62
+ */
63
+ createObjectStore(name: string, options?: IDBObjectStoreParameters): IDBObjectStore {
64
+ // Guard: only allowed during an active versionchange transaction
65
+ if (this._upgradeTransaction && this._upgradeTransaction._state !== 'active') {
66
+ throw new DOMException(
67
+ 'Can only be called during upgradeneeded',
68
+ 'InvalidStateError',
69
+ );
70
+ }
71
+
72
+ if (this._objectStores.has(name)) {
73
+ throw new DOMException(
74
+ `Object store "${name}" already exists`,
75
+ 'ConstraintError',
76
+ );
77
+ }
78
+
79
+ const opts: IDBObjectStoreParameters = {
80
+ keyPath: options?.keyPath ?? null,
81
+ autoIncrement: options?.autoIncrement ?? false,
82
+ };
83
+
84
+ this._objectStores.set(name, { options: opts, indexes: new Map() });
85
+
86
+ // Persist store definition to meta table
87
+ this._saveStoreMeta(name, opts);
88
+
89
+ // Create the store object with a versionchange transaction
90
+ const txn = this._upgradeTransaction ?? new IDBTransaction(this, [name], 'versionchange');
91
+ const store = new IDBObjectStore(name, opts, txn, this);
92
+ return store;
93
+ }
94
+
95
+ /**
96
+ * Delete an object store. Only valid during upgradeneeded.
97
+ */
98
+ deleteObjectStore(name: string): void {
99
+ // Guard: only allowed during an active versionchange transaction
100
+ if (this._upgradeTransaction && this._upgradeTransaction._state !== 'active') {
101
+ throw new DOMException(
102
+ 'Can only be called during upgradeneeded',
103
+ 'InvalidStateError',
104
+ );
105
+ }
106
+
107
+ if (!this._objectStores.has(name)) {
108
+ throw new DOMException(
109
+ `Object store "${name}" does not exist`,
110
+ 'NotFoundError',
111
+ );
112
+ }
113
+
114
+ // Drop the SQLite table
115
+ const tableName = `idb_store_${sanitizeName(name)}`;
116
+ this._exec(`DROP TABLE IF EXISTS "${tableName}"`);
117
+
118
+ // Remove from meta
119
+ this._exec(
120
+ `DELETE FROM _idb_stores WHERE store_name = ?`,
121
+ [name]
122
+ );
123
+ this._exec(
124
+ `DELETE FROM _idb_indexes WHERE store_name = ?`,
125
+ [name]
126
+ );
127
+
128
+ this._objectStores.delete(name);
129
+ }
130
+
131
+ /**
132
+ * Create a transaction for the given object stores.
133
+ */
134
+ transaction(storeNames: string | string[], mode?: IDBTransactionMode): IDBTransaction {
135
+ this._checkClosed();
136
+ const names = Array.isArray(storeNames) ? storeNames : [storeNames];
137
+
138
+ // Verify all stores exist
139
+ for (const name of names) {
140
+ if (!this._objectStores.has(name)) {
141
+ throw new DOMException(
142
+ `Object store "${name}" does not exist`,
143
+ 'NotFoundError',
144
+ );
145
+ }
146
+ }
147
+
148
+ const txn = new IDBTransaction(this, names, mode ?? 'readonly');
149
+
150
+ // Auto-commit after microtasks settle
151
+ queueMicrotask(() => {
152
+ queueMicrotask(() => {
153
+ txn._autoCommit();
154
+ });
155
+ });
156
+
157
+ return txn;
158
+ }
159
+
160
+ /**
161
+ * Close the database connection.
162
+ */
163
+ close(): void {
164
+ if (this._closed) return;
165
+ this._closed = true;
166
+ if (this._sqliteDb && this._sqliteDb.close) {
167
+ this._sqliteDb.close();
168
+ }
169
+ if (this.onclose) {
170
+ const event = { type: 'close', target: this };
171
+ this.onclose(event);
172
+ }
173
+ }
174
+
175
+ // ================================================================
176
+ // Internal SQLite operations
177
+ // ================================================================
178
+
179
+ /** @internal */
180
+ _initMeta(): void {
181
+ this._exec(`
182
+ CREATE TABLE IF NOT EXISTS _idb_meta (
183
+ key TEXT PRIMARY KEY,
184
+ value TEXT
185
+ )
186
+ `);
187
+ this._exec(`
188
+ CREATE TABLE IF NOT EXISTS _idb_stores (
189
+ store_name TEXT PRIMARY KEY,
190
+ key_path TEXT,
191
+ auto_increment INTEGER DEFAULT 0
192
+ )
193
+ `);
194
+ this._exec(`
195
+ CREATE TABLE IF NOT EXISTS _idb_indexes (
196
+ store_name TEXT,
197
+ index_name TEXT,
198
+ key_path TEXT,
199
+ unique_flag INTEGER DEFAULT 0,
200
+ multi_entry INTEGER DEFAULT 0,
201
+ PRIMARY KEY (store_name, index_name)
202
+ )
203
+ `);
204
+ }
205
+
206
+ /** @internal */
207
+ _loadStoreDefinitions(): void {
208
+ const stores = this._all('SELECT store_name, key_path, auto_increment FROM _idb_stores');
209
+ for (const row of stores) {
210
+ const keyPath = row.key_path ? JSON.parse(row.key_path) : null;
211
+ const opts: IDBObjectStoreParameters = {
212
+ keyPath,
213
+ autoIncrement: !!row.auto_increment,
214
+ };
215
+ const indexes = new Map<string, any>();
216
+
217
+ // Load indexes for this store
218
+ const idxRows = this._all(
219
+ 'SELECT index_name, key_path, unique_flag, multi_entry FROM _idb_indexes WHERE store_name = ?',
220
+ [row.store_name]
221
+ );
222
+ for (const idx of idxRows) {
223
+ indexes.set(idx.index_name, {
224
+ keyPath: JSON.parse(idx.key_path),
225
+ unique: !!idx.unique_flag,
226
+ multiEntry: !!idx.multi_entry,
227
+ });
228
+ }
229
+
230
+ this._objectStores.set(row.store_name, { options: opts, indexes });
231
+ }
232
+ }
233
+
234
+ /** @internal */
235
+ _saveStoreMeta(name: string, options: IDBObjectStoreParameters): void {
236
+ this._exec(
237
+ `INSERT OR REPLACE INTO _idb_stores (store_name, key_path, auto_increment) VALUES (?, ?, ?)`,
238
+ [name, JSON.stringify(options.keyPath), options.autoIncrement ? 1 : 0]
239
+ );
240
+ }
241
+
242
+ /** @internal */
243
+ _saveIndexMeta(storeName: string, indexName: string, keyPath: string | string[], options: any): void {
244
+ this._exec(
245
+ `INSERT OR REPLACE INTO _idb_indexes (store_name, index_name, key_path, unique_flag, multi_entry) VALUES (?, ?, ?, ?, ?)`,
246
+ [storeName, indexName, JSON.stringify(keyPath), options.unique ? 1 : 0, options.multiEntry ? 1 : 0]
247
+ );
248
+ // Update in-memory definition
249
+ const storeInfo = this._objectStores.get(storeName);
250
+ if (storeInfo) {
251
+ storeInfo.indexes.set(indexName, {
252
+ keyPath,
253
+ unique: options.unique ?? false,
254
+ multiEntry: options.multiEntry ?? false,
255
+ });
256
+ }
257
+ }
258
+
259
+ /** @internal */
260
+ _deleteIndexMeta(storeName: string, indexName: string): void {
261
+ this._exec(
262
+ `DELETE FROM _idb_indexes WHERE store_name = ? AND index_name = ?`,
263
+ [storeName, indexName]
264
+ );
265
+ const storeInfo = this._objectStores.get(storeName);
266
+ if (storeInfo) {
267
+ storeInfo.indexes.delete(indexName);
268
+ }
269
+ }
270
+
271
+ /** @internal - Get an object store instance for a transaction */
272
+ _getObjectStore(name: string, transaction: IDBTransaction): IDBObjectStore | null {
273
+ const storeInfo = this._objectStores.get(name);
274
+ if (!storeInfo) return null;
275
+
276
+ const store = new IDBObjectStore(name, storeInfo.options, transaction, this);
277
+
278
+ // Restore indexes
279
+ for (const [indexName, indexDef] of storeInfo.indexes) {
280
+ const idx = new IDBIndex(indexName, indexDef.keyPath, indexDef, store);
281
+ store._indexes.set(indexName, idx);
282
+ }
283
+
284
+ return store;
285
+ }
286
+
287
+ /** @internal */
288
+ _checkClosed(): void {
289
+ if (this._closed) {
290
+ throw new DOMException(
291
+ 'Database connection is closed',
292
+ 'InvalidStateError',
293
+ );
294
+ }
295
+ }
296
+
297
+ // ================================================================
298
+ // SQLite execution wrappers
299
+ // ================================================================
300
+
301
+ /** @internal - Execute a SQL statement */
302
+ _exec(sql: string, params?: any[]): void {
303
+ if (this._sqliteDb.exec) {
304
+ if (params && params.length > 0) {
305
+ this._sqliteDb.exec(sql, ...params);
306
+ } else {
307
+ this._sqliteDb.exec(sql);
308
+ }
309
+ } else if (this._sqliteDb.run) {
310
+ if (params && params.length > 0) {
311
+ this._sqliteDb.run(sql, ...params);
312
+ } else {
313
+ this._sqliteDb.run(sql);
314
+ }
315
+ }
316
+ }
317
+
318
+ /** @internal - Query a single row */
319
+ _get(sql: string, params?: any[]): any {
320
+ if (this._sqliteDb.query) {
321
+ const stmt = this._sqliteDb.query(sql);
322
+ return params ? stmt.get(...params) : stmt.get();
323
+ }
324
+ if (this._sqliteDb.prepare) {
325
+ const stmt = this._sqliteDb.prepare(sql);
326
+ return params ? stmt.get(...params) : stmt.get();
327
+ }
328
+ return null;
329
+ }
330
+
331
+ /** @internal - Query all rows */
332
+ _all(sql: string, params?: any[]): any[] {
333
+ if (this._sqliteDb.query) {
334
+ const stmt = this._sqliteDb.query(sql);
335
+ return params ? stmt.all(...params) : stmt.all();
336
+ }
337
+ if (this._sqliteDb.prepare) {
338
+ const stmt = this._sqliteDb.prepare(sql);
339
+ return params ? stmt.all(...params) : stmt.all();
340
+ }
341
+ return [];
342
+ }
343
+ }
@@ -0,0 +1,269 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * IDBFactory - IndexedDB Factory
4
+ *
5
+ * The main entry point for IndexedDB. Provides open() and deleteDatabase().
6
+ * Backed by SQLite via the exact:sqlite module.
7
+ *
8
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/IDBFactory
9
+ */
10
+
11
+ import { IDBDatabase } from './IDBDatabase';
12
+ import { IDBOpenDBRequest } from './IDBRequest';
13
+ import { IDBTransaction } from './IDBTransaction';
14
+ import { compareKeys } from './IDBKeyRange';
15
+ import { DOMException } from './utils';
16
+
17
+ /**
18
+ * Interface for creating SQLite database instances.
19
+ * This allows the factory to work with either the real Database class
20
+ * or a mock for testing.
21
+ */
22
+ export interface SQLiteDatabaseProvider {
23
+ create(name: string): any;
24
+ delete?(name: string): void;
25
+ }
26
+
27
+ /**
28
+ * Default provider that uses exact:sqlite Database.
29
+ */
30
+ class DefaultSQLiteProvider implements SQLiteDatabaseProvider {
31
+ create(name: string): any {
32
+ const g = globalThis as any;
33
+ if (typeof g.__exactSqliteOpen !== 'function' && typeof g.__exactEnsureSqlite === 'function') {
34
+ g.__exactEnsureSqlite();
35
+ }
36
+ if (g.__exactSqliteOpen) {
37
+ // Import is done lazily to avoid circular dependencies
38
+ const { Database } = require('../sqlite');
39
+ return new Database(indexedDbPath(name));
40
+ }
41
+ throw new Error(
42
+ 'IndexedDB requires SQLite support. The exact:sqlite native bridge is not available.'
43
+ );
44
+ }
45
+
46
+ delete(name: string): void {
47
+ const g = globalThis as any;
48
+ if (typeof g.__exactUnlink !== 'function' && typeof g.__exactEnsureFs === 'function') {
49
+ g.__exactEnsureFs();
50
+ }
51
+ if (typeof g.__exactUnlink !== 'function') return;
52
+ for (const path of [indexedDbPath(name), `${indexedDbPath(name)}-wal`, `${indexedDbPath(name)}-shm`]) {
53
+ try {
54
+ g.__exactUnlink(path);
55
+ } catch (_) {}
56
+ }
57
+ }
58
+ }
59
+
60
+ function trimTrailingSlash(path: string): string {
61
+ return path.replace(/\/+$/, '') || '/';
62
+ }
63
+
64
+ function indexedDbRoot(): string {
65
+ const g = globalThis as any;
66
+ const androidFilesDir = g.__exactAndroidStoragePaths?.filesDir;
67
+ if (typeof androidFilesDir === 'string' && androidFilesDir.length > 0) {
68
+ return trimTrailingSlash(androidFilesDir);
69
+ }
70
+
71
+ const env = g.process?.env;
72
+ const envFilesDir = env?.EXACT_ANDROID_FILES_DIR ?? env?.HOME;
73
+ if (typeof envFilesDir === 'string' && envFilesDir.length > 0) {
74
+ return trimTrailingSlash(envFilesDir);
75
+ }
76
+
77
+ return '/tmp';
78
+ }
79
+
80
+ function ensureIndexedDbDirectory(directory: string): void {
81
+ const g = globalThis as any;
82
+ try {
83
+ if (typeof g.__exactMkdir !== 'function' && typeof g.__exactEnsureFs === 'function') {
84
+ g.__exactEnsureFs();
85
+ }
86
+ if (typeof g.__exactMkdir === 'function') {
87
+ g.__exactMkdir(directory, true);
88
+ }
89
+ } catch (_) {}
90
+ }
91
+
92
+ function indexedDbPath(name: string): string {
93
+ // @ref LLP 0008#android-backend-matrix — IndexedDB persists under Android app filesDir.
94
+ const directory = `${indexedDbRoot()}/.ibex/indexeddb`;
95
+ ensureIndexedDbDirectory(directory);
96
+ const encodedName = encodeURIComponent(String(name)) || 'default';
97
+ return `${directory}/${encodedName}.sqlite`;
98
+ }
99
+
100
+ function readStoredVersion(sqliteDb: any): number {
101
+ try {
102
+ const row = sqliteDb.query('SELECT value FROM _idb_meta WHERE key = ?').get('version');
103
+ const version = Number(row?.value);
104
+ return Number.isFinite(version) && version > 0 ? Math.floor(version) : 0;
105
+ } catch (_) {
106
+ return 0;
107
+ }
108
+ }
109
+
110
+ function writeStoredVersion(sqliteDb: any, version: number): void {
111
+ try {
112
+ sqliteDb.run(`
113
+ CREATE TABLE IF NOT EXISTS _idb_meta (
114
+ key TEXT PRIMARY KEY,
115
+ value TEXT
116
+ )
117
+ `);
118
+ sqliteDb.run(
119
+ 'INSERT INTO _idb_meta(key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value',
120
+ 'version',
121
+ String(version),
122
+ );
123
+ } catch (_) {}
124
+ }
125
+
126
+ export class IDBFactory {
127
+ /** @internal - Map of database name -> { version, sqliteDb } */
128
+ private _databases: Map<string, { version: number; sqliteDb: any }> = new Map();
129
+ /** @internal - SQLite provider for creating database instances */
130
+ private _sqliteProvider: SQLiteDatabaseProvider;
131
+
132
+ constructor(provider?: SQLiteDatabaseProvider) {
133
+ this._sqliteProvider = provider ?? new DefaultSQLiteProvider();
134
+ }
135
+
136
+ /**
137
+ * Open (or create) a database with the given name and optional version.
138
+ *
139
+ * Returns an IDBOpenDBRequest. Set onupgradeneeded and onsuccess handlers
140
+ * on the request before the event loop yields.
141
+ */
142
+ open(name: string, version?: number): IDBOpenDBRequest<IDBDatabase> {
143
+ const request = new IDBOpenDBRequest<IDBDatabase>();
144
+
145
+ if (
146
+ version !== undefined &&
147
+ (version <= 0 || !Number.isFinite(version) || Math.floor(version) !== version)
148
+ ) {
149
+ queueMicrotask(() => {
150
+ request._reject(new TypeError(
151
+ `The version provided (${version}) is not a valid positive integer.`
152
+ ));
153
+ });
154
+ return request;
155
+ }
156
+
157
+ // Process in a microtask to allow handlers to be attached
158
+ queueMicrotask(() => {
159
+ try {
160
+ const existing = this._databases.get(name);
161
+
162
+ let sqliteDb: any;
163
+ let oldVersion = 0;
164
+ if (existing) {
165
+ sqliteDb = existing.sqliteDb;
166
+ oldVersion = existing.version;
167
+ } else {
168
+ sqliteDb = this._sqliteProvider.create(name);
169
+ oldVersion = readStoredVersion(sqliteDb);
170
+ }
171
+ const resolvedVersion = version ?? (oldVersion > 0 ? oldVersion : 1);
172
+ if (oldVersion > 0 && resolvedVersion < oldVersion) {
173
+ throw new DOMException(
174
+ `The requested version (${resolvedVersion}) is less than the existing version (${oldVersion}).`,
175
+ 'VersionError',
176
+ );
177
+ }
178
+
179
+ const db = new IDBDatabase(name, resolvedVersion, sqliteDb);
180
+
181
+ // Set the result on the request before upgradeneeded fires,
182
+ // because onupgradeneeded handlers access event.target.result
183
+ // to get a reference to the database.
184
+ request._setResult(db);
185
+
186
+ // Version upgrade needed?
187
+ if (resolvedVersion > oldVersion) {
188
+ // Create an upgrade transaction so createObjectStore/deleteObjectStore
189
+ // can detect that they are being called during upgradeneeded.
190
+ const storeNames = Array.from((db as any)._objectStores?.keys?.() ?? []);
191
+ const upgradeTxn = new IDBTransaction(db, storeNames, 'versionchange');
192
+ db._upgradeTransaction = upgradeTxn;
193
+
194
+ // Fire onupgradeneeded synchronously
195
+ if (request.onupgradeneeded) {
196
+ const event = {
197
+ type: 'upgradeneeded',
198
+ target: request,
199
+ oldVersion,
200
+ newVersion: resolvedVersion,
201
+ };
202
+ // During upgradeneeded, the database is in a special state
203
+ // where createObjectStore/deleteObjectStore can be called
204
+ request.onupgradeneeded(event);
205
+ }
206
+
207
+ // Clear the upgrade transaction after upgradeneeded completes
208
+ db._upgradeTransaction = null;
209
+ }
210
+
211
+ // Store the database reference
212
+ this._databases.set(name, { version: resolvedVersion, sqliteDb });
213
+ writeStoredVersion(sqliteDb, resolvedVersion);
214
+
215
+ // Fire onsuccess
216
+ request._resolveSync(db);
217
+ } catch (e: any) {
218
+ request._reject(e);
219
+ }
220
+ });
221
+
222
+ return request;
223
+ }
224
+
225
+ /**
226
+ * Delete a database.
227
+ */
228
+ deleteDatabase(name: string): IDBOpenDBRequest<undefined> {
229
+ const request = new IDBOpenDBRequest<undefined>();
230
+
231
+ queueMicrotask(() => {
232
+ try {
233
+ const existing = this._databases.get(name);
234
+ if (existing) {
235
+ // Close the SQLite database
236
+ if (existing.sqliteDb && existing.sqliteDb.close) {
237
+ existing.sqliteDb.close();
238
+ }
239
+ this._databases.delete(name);
240
+ }
241
+ this._sqliteProvider.delete?.(name);
242
+ request._resolveSync(undefined);
243
+ } catch (e: any) {
244
+ request._reject(e);
245
+ }
246
+ });
247
+
248
+ return request;
249
+ }
250
+
251
+ /**
252
+ * Compare two keys using IndexedDB key comparison rules.
253
+ */
254
+ cmp(first: any, second: any): number {
255
+ const result = compareKeys(first, second);
256
+ return result < 0 ? -1 : result > 0 ? 1 : 0;
257
+ }
258
+
259
+ /**
260
+ * List all databases. Returns a promise.
261
+ */
262
+ databases(): Promise<Array<{ name: string; version: number }>> {
263
+ const result = Array.from(this._databases.entries()).map(([name, info]) => ({
264
+ name,
265
+ version: info.version,
266
+ }));
267
+ return Promise.resolve(result);
268
+ }
269
+ }