@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.
- package/package.json +63 -0
- package/src/abort/AbortController.ts +23 -0
- package/src/abort/AbortSignal.ts +152 -0
- package/src/abort/index.ts +2 -0
- package/src/accessibility.ts +12 -0
- package/src/arraybuffer-detach.ts +109 -0
- package/src/base64/base64.ts +168 -0
- package/src/base64/index.ts +1 -0
- package/src/blob/Blob.ts +259 -0
- package/src/blob/File.ts +59 -0
- package/src/blob/FormData.ts +323 -0
- package/src/blob/index.ts +3 -0
- package/src/bootstrap.ts +1946 -0
- package/src/broadcast/BroadcastChannel.ts +280 -0
- package/src/broadcast/index.ts +5 -0
- package/src/cache/Cache.ts +349 -0
- package/src/cache/CacheStorage.ts +89 -0
- package/src/cache/index.ts +27 -0
- package/src/camera/index.ts +6202 -0
- package/src/camera/processor.worker.ts +194 -0
- package/src/camera/scene.ts +195 -0
- package/src/clipboard/Clipboard.ts +129 -0
- package/src/clipboard/ClipboardItem.ts +97 -0
- package/src/clipboard/index.ts +6 -0
- package/src/clone/index.ts +1 -0
- package/src/clone/structuredClone.ts +389 -0
- package/src/clone/transferableSymbols.ts +2 -0
- package/src/compression/CompressionStream.ts +146 -0
- package/src/compression/DecompressionStream.ts +342 -0
- package/src/compression/index.ts +4 -0
- package/src/console/Console.ts +341 -0
- package/src/console/index.ts +2 -0
- package/src/core/accessibility-state.ts +263 -0
- package/src/core/accessibility.ts +184 -0
- package/src/core/agent-state.ts +37 -0
- package/src/core/diagnostics-logs.ts +144 -0
- package/src/core/host-call-bridge.ts +16 -0
- package/src/core/i18n-helpers.ts +189 -0
- package/src/core/locale-state.ts +253 -0
- package/src/core/locale.ts +95 -0
- package/src/crypto/Crypto.ts +2743 -0
- package/src/crypto/index.ts +1 -0
- package/src/diagnostics/logs.ts +7 -0
- package/src/encoding/TextDecoder.ts +1181 -0
- package/src/encoding/TextDecoderStream.ts +58 -0
- package/src/encoding/TextEncoder.ts +180 -0
- package/src/encoding/TextEncoderStream.ts +39 -0
- package/src/encoding/index.ts +8 -0
- package/src/events/CloseEvent.ts +91 -0
- package/src/events/DOMException.ts +409 -0
- package/src/events/ErrorEvent.ts +39 -0
- package/src/events/Event.ts +151 -0
- package/src/events/EventTarget.ts +280 -0
- package/src/events/FocusEvent.ts +27 -0
- package/src/events/KeyboardEvent.ts +46 -0
- package/src/events/MessageEvent.ts +61 -0
- package/src/events/ProgressEvent.ts +33 -0
- package/src/events/PromiseRejectionEvent.ts +31 -0
- package/src/events/index.ts +52 -0
- package/src/eventsource/EventSource.ts +371 -0
- package/src/eventsource/index.ts +2 -0
- package/src/fetch/Headers.ts +642 -0
- package/src/fetch/Request.ts +760 -0
- package/src/fetch/Response.ts +543 -0
- package/src/fetch/body.ts +1256 -0
- package/src/fetch/cookie-jar.ts +566 -0
- package/src/fetch/demo.ts +207 -0
- package/src/fetch/errors.ts +101 -0
- package/src/fetch/fetch.ts +2610 -0
- package/src/fetch/index.ts +101 -0
- package/src/fetch/native-bridge.ts +65 -0
- package/src/fetch/types.ts +258 -0
- package/src/filereader/FileReader.ts +236 -0
- package/src/filereader/index.ts +1 -0
- package/src/fs/Dirent.ts +39 -0
- package/src/fs/ExactFile.ts +450 -0
- package/src/fs/Stats.ts +80 -0
- package/src/fs/index.ts +944 -0
- package/src/fs/promises.ts +386 -0
- package/src/fs/shared.ts +328 -0
- package/src/http-server/index.js +697 -0
- package/src/http-server/index.ts +27 -0
- package/src/identity.generated.ts +14 -0
- package/src/index.ts +283 -0
- package/src/indexeddb/IDBCursor.ts +188 -0
- package/src/indexeddb/IDBDatabase.ts +343 -0
- package/src/indexeddb/IDBFactory.ts +269 -0
- package/src/indexeddb/IDBIndex.ts +194 -0
- package/src/indexeddb/IDBKeyRange.ts +109 -0
- package/src/indexeddb/IDBObjectStore.ts +468 -0
- package/src/indexeddb/IDBRequest.ts +163 -0
- package/src/indexeddb/IDBTransaction.ts +207 -0
- package/src/indexeddb/index.ts +34 -0
- package/src/indexeddb/utils.ts +52 -0
- package/src/inspect/index.ts +1 -0
- package/src/inspect/inspect.ts +465 -0
- package/src/internal/detect.ts +104 -0
- package/src/locale.ts +10 -0
- package/src/location/index.ts +1059 -0
- package/src/locks/LockManager.ts +460 -0
- package/src/locks/index.ts +12 -0
- package/src/media/VideoFrame.ts +58 -0
- package/src/messaging/MessageChannel.ts +31 -0
- package/src/messaging/MessagePort.ts +180 -0
- package/src/messaging/index.ts +2 -0
- package/src/messaging.ts +247 -0
- package/src/native/NativeModules.ts +354 -0
- package/src/native/index.ts +1 -0
- package/src/navigator/Navigator.ts +351 -0
- package/src/navigator/index.ts +1 -0
- package/src/node/Buffer.ts +1786 -0
- package/src/node/index.ts +4 -0
- package/src/node/path.ts +495 -0
- package/src/node/process.ts +2528 -0
- package/src/performance/Performance.ts +532 -0
- package/src/performance/index.ts +21 -0
- package/src/polyfills/array.ts +236 -0
- package/src/polyfills/arraybuffer.ts +172 -0
- package/src/polyfills/groupby.ts +85 -0
- package/src/polyfills/index.ts +85 -0
- package/src/polyfills/intl.ts +1956 -0
- package/src/polyfills/iterator.ts +479 -0
- package/src/polyfills/promise.ts +37 -0
- package/src/polyfills/set.ts +245 -0
- package/src/polyfills/string.ts +85 -0
- package/src/polyfills/typedarray.ts +110 -0
- package/src/promise-rejection-tracking.ts +464 -0
- package/src/react-native/index.ts +388 -0
- package/src/runtime-entry.ts +55 -0
- package/src/scheduling/AnimationFrame.ts +105 -0
- package/src/scheduling/IdleCallback.ts +167 -0
- package/src/scheduling/index.ts +13 -0
- package/src/security/Capabilities.ts +1146 -0
- package/src/security/Permissions.ts +392 -0
- package/src/security/capability-bits.generated.ts +63 -0
- package/src/security/index.ts +16 -0
- package/src/sqlite/Database.ts +456 -0
- package/src/sqlite/Statement.ts +206 -0
- package/src/sqlite/constants.ts +79 -0
- package/src/sqlite/errors.ts +25 -0
- package/src/sqlite/index.ts +34 -0
- package/src/sqlite/module.js +438 -0
- package/src/storage/Storage.ts +291 -0
- package/src/storage/StorageManager.ts +91 -0
- package/src/storage/index.ts +3 -0
- package/src/stream-compat.ts +47 -0
- package/src/streams/ReadableStream.ts +4131 -0
- package/src/streams/TransformStream.ts +375 -0
- package/src/streams/WritableStream.ts +866 -0
- package/src/streams/index.ts +41 -0
- package/src/timers/Timers.ts +296 -0
- package/src/timers/index.ts +11 -0
- package/src/url/URL.ts +656 -0
- package/src/url/URLPattern.ts +850 -0
- package/src/url/URLSearchParams.ts +244 -0
- package/src/url/index.ts +9 -0
- package/src/websocket/WebSocket.ts +770 -0
- package/src/websocket/WebSocketError.ts +52 -0
- package/src/websocket/WebSocketStream.ts +628 -0
- package/src/websocket/index.ts +7 -0
- package/src/window/index.ts +872 -0
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDBObjectStore - IndexedDB Object Store
|
|
3
|
+
*
|
|
4
|
+
* Represents an object store in the database. Backed by a SQLite table.
|
|
5
|
+
*
|
|
6
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { IDBRequest } from './IDBRequest';
|
|
10
|
+
import { IDBIndex, type IDBIndexParameters, extractKeyPath } from './IDBIndex';
|
|
11
|
+
import { IDBKeyRange, compareKeys } from './IDBKeyRange';
|
|
12
|
+
import { IDBCursorWithValue, type IDBCursorDirection } from './IDBCursor';
|
|
13
|
+
import { DOMException, sanitizeName } from './utils';
|
|
14
|
+
|
|
15
|
+
export interface IDBObjectStoreParameters {
|
|
16
|
+
keyPath?: string | string[] | null;
|
|
17
|
+
autoIncrement?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class IDBObjectStore {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly keyPath: string | string[] | null;
|
|
23
|
+
readonly autoIncrement: boolean;
|
|
24
|
+
|
|
25
|
+
/** @internal */
|
|
26
|
+
_transaction: any;
|
|
27
|
+
/** @internal */
|
|
28
|
+
_db: any; // The SQLite-backed database reference
|
|
29
|
+
/** @internal */
|
|
30
|
+
_indexes: Map<string, IDBIndex> = new Map();
|
|
31
|
+
/** @internal */
|
|
32
|
+
_autoIncrementValue: number = 0;
|
|
33
|
+
/** @internal */
|
|
34
|
+
_tableName: string;
|
|
35
|
+
|
|
36
|
+
constructor(
|
|
37
|
+
name: string,
|
|
38
|
+
options: IDBObjectStoreParameters,
|
|
39
|
+
transaction: any,
|
|
40
|
+
db: any,
|
|
41
|
+
) {
|
|
42
|
+
this.name = name;
|
|
43
|
+
this.keyPath = options.keyPath ?? null;
|
|
44
|
+
this.autoIncrement = options.autoIncrement ?? false;
|
|
45
|
+
this._transaction = transaction;
|
|
46
|
+
this._db = db;
|
|
47
|
+
this._tableName = `idb_store_${sanitizeName(name)}`;
|
|
48
|
+
|
|
49
|
+
// Create the SQLite table if it doesn't exist
|
|
50
|
+
this._ensureTable();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get indexNames(): string[] {
|
|
54
|
+
return Array.from(this._indexes.keys()).sort();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get transaction(): any {
|
|
58
|
+
return this._transaction;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Add a record to the store. Fails if a record with the same key exists.
|
|
63
|
+
*/
|
|
64
|
+
add(value: any, key?: any): IDBRequest {
|
|
65
|
+
const request = new IDBRequest();
|
|
66
|
+
request.source = this;
|
|
67
|
+
request.transaction = this._transaction;
|
|
68
|
+
try {
|
|
69
|
+
const resolvedKey = this._resolveKey(value, key);
|
|
70
|
+
if (this._hasRecord(resolvedKey)) {
|
|
71
|
+
throw new DOMException(
|
|
72
|
+
`A record with key ${JSON.stringify(resolvedKey)} already exists`,
|
|
73
|
+
'ConstraintError',
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
this._putRecord(resolvedKey, value);
|
|
77
|
+
request._resolve(resolvedKey);
|
|
78
|
+
} catch (e: any) {
|
|
79
|
+
request._reject(e instanceof DOMException ? e : new DOMException(e.message, 'DataError'));
|
|
80
|
+
}
|
|
81
|
+
return request;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Add or update a record in the store.
|
|
86
|
+
*/
|
|
87
|
+
put(value: any, key?: any): IDBRequest {
|
|
88
|
+
const request = new IDBRequest();
|
|
89
|
+
request.source = this;
|
|
90
|
+
request.transaction = this._transaction;
|
|
91
|
+
try {
|
|
92
|
+
const resolvedKey = this._resolveKey(value, key);
|
|
93
|
+
this._putRecord(resolvedKey, value);
|
|
94
|
+
request._resolve(resolvedKey);
|
|
95
|
+
} catch (e: any) {
|
|
96
|
+
request._reject(e instanceof DOMException ? e : new DOMException(e.message, 'DataError'));
|
|
97
|
+
}
|
|
98
|
+
return request;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Retrieve a record by key.
|
|
103
|
+
*/
|
|
104
|
+
get(query: any): IDBRequest {
|
|
105
|
+
const request = new IDBRequest();
|
|
106
|
+
request.source = this;
|
|
107
|
+
request.transaction = this._transaction;
|
|
108
|
+
try {
|
|
109
|
+
if (query instanceof IDBKeyRange) {
|
|
110
|
+
const records = this._getAllRecords();
|
|
111
|
+
const match = records.find((r: any) => query.includes(r.key));
|
|
112
|
+
request._resolve(match ? match.value : undefined);
|
|
113
|
+
} else {
|
|
114
|
+
const value = this._getRecord(query);
|
|
115
|
+
request._resolve(value);
|
|
116
|
+
}
|
|
117
|
+
} catch (e: any) {
|
|
118
|
+
request._reject(e);
|
|
119
|
+
}
|
|
120
|
+
return request;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Retrieve the key of the first record matching.
|
|
125
|
+
*/
|
|
126
|
+
getKey(query: any): IDBRequest {
|
|
127
|
+
const request = new IDBRequest();
|
|
128
|
+
request.source = this;
|
|
129
|
+
request.transaction = this._transaction;
|
|
130
|
+
try {
|
|
131
|
+
if (query instanceof IDBKeyRange) {
|
|
132
|
+
const records = this._getAllRecords();
|
|
133
|
+
const match = records.find((r: any) => query.includes(r.key));
|
|
134
|
+
request._resolve(match ? match.key : undefined);
|
|
135
|
+
} else {
|
|
136
|
+
const value = this._getRecord(query);
|
|
137
|
+
request._resolve(value !== undefined ? query : undefined);
|
|
138
|
+
}
|
|
139
|
+
} catch (e: any) {
|
|
140
|
+
request._reject(e);
|
|
141
|
+
}
|
|
142
|
+
return request;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Retrieve all records, optionally filtered by key range.
|
|
147
|
+
*/
|
|
148
|
+
getAll(query?: any, count?: number): IDBRequest {
|
|
149
|
+
const request = new IDBRequest();
|
|
150
|
+
request.source = this;
|
|
151
|
+
request.transaction = this._transaction;
|
|
152
|
+
try {
|
|
153
|
+
let records = this._getAllRecords();
|
|
154
|
+
if (query !== undefined && query !== null) {
|
|
155
|
+
const range = query instanceof IDBKeyRange ? query : IDBKeyRange.only(query);
|
|
156
|
+
records = records.filter((r: any) => range.includes(r.key));
|
|
157
|
+
}
|
|
158
|
+
if (count !== undefined && count >= 0) {
|
|
159
|
+
records = records.slice(0, count);
|
|
160
|
+
}
|
|
161
|
+
request._resolve(records.map((r: any) => r.value));
|
|
162
|
+
} catch (e: any) {
|
|
163
|
+
request._reject(e);
|
|
164
|
+
}
|
|
165
|
+
return request;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Retrieve all keys, optionally filtered.
|
|
170
|
+
*/
|
|
171
|
+
getAllKeys(query?: any, count?: number): IDBRequest {
|
|
172
|
+
const request = new IDBRequest();
|
|
173
|
+
request.source = this;
|
|
174
|
+
request.transaction = this._transaction;
|
|
175
|
+
try {
|
|
176
|
+
let records = this._getAllRecords();
|
|
177
|
+
if (query !== undefined && query !== null) {
|
|
178
|
+
const range = query instanceof IDBKeyRange ? query : IDBKeyRange.only(query);
|
|
179
|
+
records = records.filter((r: any) => range.includes(r.key));
|
|
180
|
+
}
|
|
181
|
+
if (count !== undefined && count >= 0) {
|
|
182
|
+
records = records.slice(0, count);
|
|
183
|
+
}
|
|
184
|
+
request._resolve(records.map((r: any) => r.key));
|
|
185
|
+
} catch (e: any) {
|
|
186
|
+
request._reject(e);
|
|
187
|
+
}
|
|
188
|
+
return request;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Delete a record by key or key range.
|
|
193
|
+
*/
|
|
194
|
+
delete(query: any): IDBRequest {
|
|
195
|
+
const request = new IDBRequest();
|
|
196
|
+
request.source = this;
|
|
197
|
+
request.transaction = this._transaction;
|
|
198
|
+
try {
|
|
199
|
+
if (query instanceof IDBKeyRange) {
|
|
200
|
+
const records = this._getAllRecords();
|
|
201
|
+
for (const r of records) {
|
|
202
|
+
if (query.includes(r.key)) {
|
|
203
|
+
this._deleteRecord(r.key);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
this._deleteRecord(query);
|
|
208
|
+
}
|
|
209
|
+
request._resolve(undefined);
|
|
210
|
+
} catch (e: any) {
|
|
211
|
+
request._reject(e);
|
|
212
|
+
}
|
|
213
|
+
return request;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Delete all records in the store.
|
|
218
|
+
*/
|
|
219
|
+
clear(): IDBRequest {
|
|
220
|
+
const request = new IDBRequest();
|
|
221
|
+
request.source = this;
|
|
222
|
+
request.transaction = this._transaction;
|
|
223
|
+
try {
|
|
224
|
+
this._clearRecords();
|
|
225
|
+
request._resolve(undefined);
|
|
226
|
+
} catch (e: any) {
|
|
227
|
+
request._reject(e);
|
|
228
|
+
}
|
|
229
|
+
return request;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Count records, optionally filtered.
|
|
234
|
+
*/
|
|
235
|
+
count(query?: any): IDBRequest {
|
|
236
|
+
const request = new IDBRequest();
|
|
237
|
+
request.source = this;
|
|
238
|
+
request.transaction = this._transaction;
|
|
239
|
+
try {
|
|
240
|
+
if (query === undefined || query === null) {
|
|
241
|
+
request._resolve(this._countRecords());
|
|
242
|
+
} else {
|
|
243
|
+
const range = query instanceof IDBKeyRange ? query : IDBKeyRange.only(query);
|
|
244
|
+
const records = this._getAllRecords();
|
|
245
|
+
request._resolve(records.filter((r: any) => range.includes(r.key)).length);
|
|
246
|
+
}
|
|
247
|
+
} catch (e: any) {
|
|
248
|
+
request._reject(e);
|
|
249
|
+
}
|
|
250
|
+
return request;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Create an index on this object store. Only valid during upgradeneeded.
|
|
255
|
+
*/
|
|
256
|
+
createIndex(name: string, keyPath: string | string[], options?: IDBIndexParameters): IDBIndex {
|
|
257
|
+
if (this._indexes.has(name)) {
|
|
258
|
+
throw new DOMException(
|
|
259
|
+
`Index "${name}" already exists`,
|
|
260
|
+
'ConstraintError',
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
const index = new IDBIndex(name, keyPath, options ?? {}, this);
|
|
264
|
+
this._indexes.set(name, index);
|
|
265
|
+
|
|
266
|
+
// Persist index metadata
|
|
267
|
+
this._db._saveIndexMeta(this.name, name, keyPath, options ?? {});
|
|
268
|
+
|
|
269
|
+
return index;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Delete an index from this object store. Only valid during upgradeneeded.
|
|
274
|
+
*/
|
|
275
|
+
deleteIndex(name: string): void {
|
|
276
|
+
if (!this._indexes.has(name)) {
|
|
277
|
+
throw new DOMException(
|
|
278
|
+
`Index "${name}" does not exist`,
|
|
279
|
+
'NotFoundError',
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
this._indexes.delete(name);
|
|
283
|
+
this._db._deleteIndexMeta(this.name, name);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Get a reference to an index by name.
|
|
288
|
+
*/
|
|
289
|
+
index(name: string): IDBIndex {
|
|
290
|
+
const idx = this._indexes.get(name);
|
|
291
|
+
if (!idx) {
|
|
292
|
+
throw new DOMException(
|
|
293
|
+
`Index "${name}" does not exist on object store "${this.name}"`,
|
|
294
|
+
'NotFoundError',
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
return idx;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Open a cursor over the object store.
|
|
302
|
+
*/
|
|
303
|
+
openCursor(query?: any, direction?: IDBCursorDirection): IDBRequest {
|
|
304
|
+
const request = new IDBRequest();
|
|
305
|
+
request.source = this;
|
|
306
|
+
request.transaction = this._transaction;
|
|
307
|
+
try {
|
|
308
|
+
let records = this._getAllRecords();
|
|
309
|
+
if (query !== undefined && query !== null) {
|
|
310
|
+
const range = query instanceof IDBKeyRange ? query : IDBKeyRange.only(query);
|
|
311
|
+
records = records.filter((r: any) => range.includes(r.key));
|
|
312
|
+
}
|
|
313
|
+
if (records.length === 0) {
|
|
314
|
+
request._resolve(null);
|
|
315
|
+
} else {
|
|
316
|
+
const cursorRecords = records.map((r: any) => ({
|
|
317
|
+
key: r.key,
|
|
318
|
+
primaryKey: r.key,
|
|
319
|
+
value: r.value,
|
|
320
|
+
}));
|
|
321
|
+
const cursor = new IDBCursorWithValue(this, direction ?? 'next', cursorRecords, request);
|
|
322
|
+
request._resolve(cursor);
|
|
323
|
+
}
|
|
324
|
+
} catch (e: any) {
|
|
325
|
+
request._reject(e);
|
|
326
|
+
}
|
|
327
|
+
return request;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Open a key cursor over the object store.
|
|
332
|
+
*/
|
|
333
|
+
openKeyCursor(query?: any, direction?: IDBCursorDirection): IDBRequest {
|
|
334
|
+
return this.openCursor(query, direction);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ================================================================
|
|
338
|
+
// Internal SQLite-backed operations
|
|
339
|
+
// ================================================================
|
|
340
|
+
|
|
341
|
+
/** @internal */
|
|
342
|
+
_ensureTable(): void {
|
|
343
|
+
this._db._exec(
|
|
344
|
+
`CREATE TABLE IF NOT EXISTS "${this._tableName}" (key TEXT PRIMARY KEY, value TEXT)`
|
|
345
|
+
);
|
|
346
|
+
// Load auto-increment counter by scanning all keys in JS.
|
|
347
|
+
// We cannot rely on SQLite's typeof() or CAST() on JSON-encoded text keys,
|
|
348
|
+
// so we parse them in JS and find the max numeric key.
|
|
349
|
+
if (this.autoIncrement) {
|
|
350
|
+
const rows = this._db._all(`SELECT key FROM "${this._tableName}"`);
|
|
351
|
+
let maxKey = 0;
|
|
352
|
+
for (const row of rows) {
|
|
353
|
+
try {
|
|
354
|
+
const parsed = JSON.parse(row.key);
|
|
355
|
+
if (typeof parsed === 'number' && !isNaN(parsed) && parsed > maxKey) {
|
|
356
|
+
maxKey = parsed;
|
|
357
|
+
}
|
|
358
|
+
} catch (_) {
|
|
359
|
+
// Skip non-parseable keys
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
this._autoIncrementValue = maxKey;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** @internal */
|
|
367
|
+
_resolveKey(value: any, explicitKey?: any): any {
|
|
368
|
+
if (explicitKey !== undefined) {
|
|
369
|
+
return explicitKey;
|
|
370
|
+
}
|
|
371
|
+
if (this.keyPath !== null) {
|
|
372
|
+
const key = extractKeyPath(value, this.keyPath);
|
|
373
|
+
if (key !== undefined) return key;
|
|
374
|
+
}
|
|
375
|
+
if (this.autoIncrement) {
|
|
376
|
+
this._autoIncrementValue++;
|
|
377
|
+
const newKey = this._autoIncrementValue;
|
|
378
|
+
// If keyPath exists, set the key on the value
|
|
379
|
+
if (this.keyPath !== null && typeof this.keyPath === 'string' && typeof value === 'object' && value !== null) {
|
|
380
|
+
setKeyPath(value, this.keyPath, newKey);
|
|
381
|
+
}
|
|
382
|
+
return newKey;
|
|
383
|
+
}
|
|
384
|
+
throw new DOMException(
|
|
385
|
+
'No key provided and no keyPath or autoIncrement configured',
|
|
386
|
+
'DataError',
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** @internal */
|
|
391
|
+
_hasRecord(key: any): boolean {
|
|
392
|
+
const row = this._db._get(
|
|
393
|
+
`SELECT 1 FROM "${this._tableName}" WHERE key = ?`,
|
|
394
|
+
[JSON.stringify(key)]
|
|
395
|
+
);
|
|
396
|
+
return !!row;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** @internal */
|
|
400
|
+
_putRecord(key: any, value: any): void {
|
|
401
|
+
const serializedKey = JSON.stringify(key);
|
|
402
|
+
const serializedValue = JSON.stringify(value);
|
|
403
|
+
this._db._exec(
|
|
404
|
+
`INSERT OR REPLACE INTO "${this._tableName}" (key, value) VALUES (?, ?)`,
|
|
405
|
+
[serializedKey, serializedValue]
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** @internal */
|
|
410
|
+
_getRecord(key: any): any {
|
|
411
|
+
const row = this._db._get(
|
|
412
|
+
`SELECT value FROM "${this._tableName}" WHERE key = ?`,
|
|
413
|
+
[JSON.stringify(key)]
|
|
414
|
+
);
|
|
415
|
+
return row ? JSON.parse(row.value) : undefined;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** @internal */
|
|
419
|
+
_deleteRecord(key: any): void {
|
|
420
|
+
this._db._exec(
|
|
421
|
+
`DELETE FROM "${this._tableName}" WHERE key = ?`,
|
|
422
|
+
[JSON.stringify(key)]
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** @internal */
|
|
427
|
+
_clearRecords(): void {
|
|
428
|
+
this._db._exec(`DELETE FROM "${this._tableName}"`);
|
|
429
|
+
if (this.autoIncrement) {
|
|
430
|
+
this._autoIncrementValue = 0;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** @internal */
|
|
435
|
+
_countRecords(): number {
|
|
436
|
+
const row = this._db._get(`SELECT COUNT(*) as cnt FROM "${this._tableName}"`);
|
|
437
|
+
return row ? row.cnt : 0;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** @internal */
|
|
441
|
+
_getAllRecords(): Array<{ key: any; value: any }> {
|
|
442
|
+
const rows = this._db._all(`SELECT key, value FROM "${this._tableName}"`);
|
|
443
|
+
const records = rows.map((r: any) => ({
|
|
444
|
+
key: JSON.parse(r.key),
|
|
445
|
+
value: JSON.parse(r.value),
|
|
446
|
+
}));
|
|
447
|
+
// Sort in JS using IndexedDB key comparison (number < string < Date < Array)
|
|
448
|
+
// to avoid lexicographic issues with SQLite text-based ORDER BY.
|
|
449
|
+
records.sort((a: any, b: any) => compareKeys(a.key, b.key));
|
|
450
|
+
return records;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Set a value at a key path on an object.
|
|
456
|
+
*/
|
|
457
|
+
function setKeyPath(obj: any, keyPath: string, value: any): void {
|
|
458
|
+
const parts = keyPath.split('.');
|
|
459
|
+
let current = obj;
|
|
460
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
461
|
+
if (current[parts[i]] === undefined) {
|
|
462
|
+
current[parts[i]] = {};
|
|
463
|
+
}
|
|
464
|
+
current = current[parts[i]];
|
|
465
|
+
}
|
|
466
|
+
current[parts[parts.length - 1]] = value;
|
|
467
|
+
}
|
|
468
|
+
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* IDBRequest - IndexedDB Request
|
|
4
|
+
*
|
|
5
|
+
* Represents an asynchronous request to the database. IndexedDB's async model
|
|
6
|
+
* uses onsuccess/onerror callbacks instead of Promises.
|
|
7
|
+
*
|
|
8
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/IDBRequest
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { DOMException } from './utils';
|
|
12
|
+
|
|
13
|
+
export type IDBRequestReadyState = 'pending' | 'done';
|
|
14
|
+
|
|
15
|
+
export class IDBRequest<T = any> {
|
|
16
|
+
private _result: T | undefined = undefined;
|
|
17
|
+
private _error: DOMException | null = null;
|
|
18
|
+
private _readyState: IDBRequestReadyState = 'pending';
|
|
19
|
+
private _source: any = null;
|
|
20
|
+
private _transaction: any = null;
|
|
21
|
+
/** @internal - EventTarget listeners keyed by event type */
|
|
22
|
+
private _listeners: Record<string, Function[]> = {};
|
|
23
|
+
|
|
24
|
+
onsuccess: ((event: any) => void) | null = null;
|
|
25
|
+
onerror: ((event: any) => void) | null = null;
|
|
26
|
+
|
|
27
|
+
get result(): T {
|
|
28
|
+
if (this._readyState === 'pending') {
|
|
29
|
+
throw new DOMException(
|
|
30
|
+
'The request has not finished.',
|
|
31
|
+
'InvalidStateError'
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return this._result as T;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get error(): DOMException | null {
|
|
38
|
+
if (this._readyState === 'pending') {
|
|
39
|
+
throw new DOMException(
|
|
40
|
+
'The request has not finished.',
|
|
41
|
+
'InvalidStateError'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return this._error;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get readyState(): IDBRequestReadyState {
|
|
48
|
+
return this._readyState;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get source(): any {
|
|
52
|
+
return this._source;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
set source(s: any) {
|
|
56
|
+
this._source = s;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get transaction(): any {
|
|
60
|
+
return this._transaction;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
set transaction(t: any) {
|
|
64
|
+
this._transaction = t;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
addEventListener(type: string, fn: Function): void {
|
|
68
|
+
if (!this._listeners[type]) this._listeners[type] = [];
|
|
69
|
+
this._listeners[type].push(fn);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
removeEventListener(type: string, fn: Function): void {
|
|
73
|
+
const list = this._listeners[type];
|
|
74
|
+
if (list) this._listeners[type] = list.filter(f => f !== fn);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** @internal - Fire listeners for a given event type */
|
|
78
|
+
private _fireListeners(type: string, event: any): void {
|
|
79
|
+
const list = this._listeners[type];
|
|
80
|
+
if (list) {
|
|
81
|
+
for (const fn of list) {
|
|
82
|
+
try {
|
|
83
|
+
fn(event);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
if (this._transaction && this._transaction._handleError) {
|
|
86
|
+
this._transaction._handleError(e);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** @internal - Resolve the request with a result */
|
|
94
|
+
_resolve(value: T): void {
|
|
95
|
+
this._readyState = 'done';
|
|
96
|
+
this._result = value;
|
|
97
|
+
this._error = null;
|
|
98
|
+
// Fire onsuccess in a microtask to match async behavior.
|
|
99
|
+
// Check onsuccess inside the microtask so handlers set after
|
|
100
|
+
// _resolve() is called (but before the microtask runs) still fire.
|
|
101
|
+
const event = { type: 'success', target: this };
|
|
102
|
+
queueMicrotask(() => {
|
|
103
|
+
if (this.onsuccess) {
|
|
104
|
+
try {
|
|
105
|
+
this.onsuccess(event);
|
|
106
|
+
} catch (e) {
|
|
107
|
+
// Errors in handlers propagate to transaction
|
|
108
|
+
if (this._transaction && this._transaction._handleError) {
|
|
109
|
+
this._transaction._handleError(e);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
this._fireListeners('success', event);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** @internal - Reject the request with an error */
|
|
118
|
+
_reject(error: DOMException | Error): void {
|
|
119
|
+
this._readyState = 'done';
|
|
120
|
+
this._error = error instanceof DOMException
|
|
121
|
+
? error
|
|
122
|
+
: new DOMException(error.message, 'UnknownError');
|
|
123
|
+
this._result = undefined as any;
|
|
124
|
+
const event = { type: 'error', target: this };
|
|
125
|
+
queueMicrotask(() => {
|
|
126
|
+
if (this.onerror) {
|
|
127
|
+
this.onerror(event);
|
|
128
|
+
}
|
|
129
|
+
this._fireListeners('error', event);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** @internal - Resolve synchronously (used during upgradeneeded) */
|
|
134
|
+
_resolveSync(value: T): void {
|
|
135
|
+
this._readyState = 'done';
|
|
136
|
+
this._result = value;
|
|
137
|
+
this._error = null;
|
|
138
|
+
const event = { type: 'success', target: this };
|
|
139
|
+
if (this.onsuccess) {
|
|
140
|
+
this.onsuccess(event);
|
|
141
|
+
}
|
|
142
|
+
this._fireListeners('success', event);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @internal - Set the result value without changing readyState or firing events.
|
|
147
|
+
* Used by IDBFactory.open() to make the result accessible to onupgradeneeded
|
|
148
|
+
* handlers before the request formally completes.
|
|
149
|
+
*/
|
|
150
|
+
_setResult(value: T): void {
|
|
151
|
+
this._readyState = 'done';
|
|
152
|
+
this._result = value;
|
|
153
|
+
this._error = null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* IDBOpenDBRequest extends IDBRequest with onupgradeneeded and onblocked.
|
|
159
|
+
*/
|
|
160
|
+
export class IDBOpenDBRequest<T = any> extends IDBRequest<T> {
|
|
161
|
+
onupgradeneeded: ((event: any) => void) | null = null;
|
|
162
|
+
onblocked: ((event: any) => void) | null = null;
|
|
163
|
+
}
|