@react-native-firebase/app 20.0.0 → 20.2.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 +26 -0
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseVersion.java +1 -1
- package/ios/RNFBApp/RNFBVersion.m +1 -1
- package/lib/common/index.js +2 -0
- package/lib/index.d.ts +1 -0
- package/lib/internal/RNFBNativeEventEmitter.js +31 -6
- package/lib/internal/nativeModule.android.js +2 -0
- package/lib/internal/nativeModule.ios.js +2 -0
- package/lib/internal/nativeModule.js +4 -0
- package/lib/internal/nativeModuleAndroidIos.js +45 -0
- package/lib/internal/nativeModuleWeb.js +48 -0
- package/lib/internal/registry/app.js +2 -1
- package/lib/internal/registry/nativeModule.js +11 -15
- package/lib/internal/web/RNFBAppModule.js +266 -0
- package/lib/internal/web/firebaseApp.js +3 -0
- package/lib/internal/web/firebaseAppCheck.js +6 -0
- package/lib/internal/web/firebaseAuth.js +4 -0
- package/lib/internal/web/firebaseDatabase.js +4 -0
- package/lib/internal/web/firebaseFirestore.js +4 -0
- package/lib/internal/web/firebaseFunctions.js +4 -0
- package/lib/internal/web/firebaseInstallations.js +6 -0
- package/lib/internal/web/firebaseRemoteConfig.js +6 -0
- package/lib/internal/web/firebaseStorage.js +4 -0
- package/lib/internal/web/memidb/FDBCursor.js +503 -0
- package/lib/internal/web/memidb/FDBCursorWithValue.js +11 -0
- package/lib/internal/web/memidb/FDBDatabase.js +172 -0
- package/lib/internal/web/memidb/FDBFactory.js +256 -0
- package/lib/internal/web/memidb/FDBIndex.js +187 -0
- package/lib/internal/web/memidb/FDBKeyRange.js +71 -0
- package/lib/internal/web/memidb/FDBObjectStore.js +411 -0
- package/lib/internal/web/memidb/FDBOpenDBRequest.js +9 -0
- package/lib/internal/web/memidb/FDBRequest.js +33 -0
- package/lib/internal/web/memidb/FDBTransaction.js +216 -0
- package/lib/internal/web/memidb/FDBVersionChangeEvent.js +12 -0
- package/lib/internal/web/memidb/LICENSE +208 -0
- package/lib/internal/web/memidb/index.js +39 -0
- package/lib/internal/web/memidb/lib/Database.js +32 -0
- package/lib/internal/web/memidb/lib/FakeDOMStringList.js +72 -0
- package/lib/internal/web/memidb/lib/FakeEvent.js +38 -0
- package/lib/internal/web/memidb/lib/FakeEventTarget.js +110 -0
- package/lib/internal/web/memidb/lib/Index.js +157 -0
- package/lib/internal/web/memidb/lib/KeyGenerator.js +22 -0
- package/lib/internal/web/memidb/lib/ObjectStore.js +172 -0
- package/lib/internal/web/memidb/lib/RecordStore.js +141 -0
- package/lib/internal/web/memidb/lib/binarySearch.js +78 -0
- package/lib/internal/web/memidb/lib/canInjectKey.js +25 -0
- package/lib/internal/web/memidb/lib/cmp.js +77 -0
- package/lib/internal/web/memidb/lib/enforceRange.js +13 -0
- package/lib/internal/web/memidb/lib/errors.js +69 -0
- package/lib/internal/web/memidb/lib/extractKey.js +39 -0
- package/lib/internal/web/memidb/lib/scheduling.js +30 -0
- package/lib/internal/web/memidb/lib/types.js +1 -0
- package/lib/internal/web/memidb/lib/validateKeyPath.js +54 -0
- package/lib/internal/web/memidb/lib/valueToKey.js +62 -0
- package/lib/internal/web/memidb/lib/valueToKeyRange.js +19 -0
- package/lib/internal/web/structuredClone/index.js +222 -0
- package/lib/internal/web/utils.js +35 -0
- package/lib/utils/UtilsStatics.js +3 -2
- package/lib/version.js +1 -1
- package/package.json +9 -8
@@ -0,0 +1,172 @@
|
|
1
|
+
import { ConstraintError, DataError } from './errors.js';
|
2
|
+
import extractKey from './extractKey.js';
|
3
|
+
import KeyGenerator from './KeyGenerator.js';
|
4
|
+
import RecordStore from './RecordStore.js';
|
5
|
+
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-object-store
|
6
|
+
class ObjectStore {
|
7
|
+
deleted = false;
|
8
|
+
records = new RecordStore();
|
9
|
+
rawIndexes = new Map();
|
10
|
+
constructor(rawDatabase, name, keyPath, autoIncrement) {
|
11
|
+
this.rawDatabase = rawDatabase;
|
12
|
+
this.keyGenerator = autoIncrement === true ? new KeyGenerator() : null;
|
13
|
+
this.deleted = false;
|
14
|
+
this.name = name;
|
15
|
+
this.keyPath = keyPath;
|
16
|
+
this.autoIncrement = autoIncrement;
|
17
|
+
}
|
18
|
+
|
19
|
+
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-retrieving-a-value-from-an-object-store
|
20
|
+
getKey(key) {
|
21
|
+
const record = this.records.get(key);
|
22
|
+
return record !== undefined ? structuredClone(record.key) : undefined;
|
23
|
+
}
|
24
|
+
|
25
|
+
// http://w3c.github.io/IndexedDB/#retrieve-multiple-keys-from-an-object-store
|
26
|
+
getAllKeys(range, count) {
|
27
|
+
if (count === undefined || count === 0) {
|
28
|
+
count = Infinity;
|
29
|
+
}
|
30
|
+
const records = [];
|
31
|
+
for (const record of this.records.values(range)) {
|
32
|
+
records.push(structuredClone(record.key));
|
33
|
+
if (records.length >= count) {
|
34
|
+
break;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
return records;
|
38
|
+
}
|
39
|
+
|
40
|
+
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-retrieving-a-value-from-an-object-store
|
41
|
+
getValue(key) {
|
42
|
+
const record = this.records.get(key);
|
43
|
+
return record !== undefined ? structuredClone(record.value) : undefined;
|
44
|
+
}
|
45
|
+
|
46
|
+
// http://w3c.github.io/IndexedDB/#retrieve-multiple-values-from-an-object-store
|
47
|
+
getAllValues(range, count) {
|
48
|
+
if (count === undefined || count === 0) {
|
49
|
+
count = Infinity;
|
50
|
+
}
|
51
|
+
const records = [];
|
52
|
+
for (const record of this.records.values(range)) {
|
53
|
+
records.push(structuredClone(record.value));
|
54
|
+
if (records.length >= count) {
|
55
|
+
break;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
return records;
|
59
|
+
}
|
60
|
+
|
61
|
+
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-storing-a-record-into-an-object-store
|
62
|
+
storeRecord(newRecord, noOverwrite, rollbackLog) {
|
63
|
+
if (this.keyPath !== null) {
|
64
|
+
const key = extractKey(this.keyPath, newRecord.value);
|
65
|
+
if (key !== undefined) {
|
66
|
+
newRecord.key = key;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
if (this.keyGenerator !== null && newRecord.key === undefined) {
|
70
|
+
if (rollbackLog) {
|
71
|
+
const keyGeneratorBefore = this.keyGenerator.num;
|
72
|
+
rollbackLog.push(() => {
|
73
|
+
if (this.keyGenerator) {
|
74
|
+
this.keyGenerator.num = keyGeneratorBefore;
|
75
|
+
}
|
76
|
+
});
|
77
|
+
}
|
78
|
+
newRecord.key = this.keyGenerator.next();
|
79
|
+
|
80
|
+
// Set in value if keyPath defiend but led to no key
|
81
|
+
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-to-assign-a-key-to-a-value-using-a-key-path
|
82
|
+
if (this.keyPath !== null) {
|
83
|
+
if (Array.isArray(this.keyPath)) {
|
84
|
+
throw new Error('Cannot have an array key path in an object store with a key generator');
|
85
|
+
}
|
86
|
+
let remainingKeyPath = this.keyPath;
|
87
|
+
let object = newRecord.value;
|
88
|
+
let identifier;
|
89
|
+
let i = 0; // Just to run the loop at least once
|
90
|
+
while (i >= 0) {
|
91
|
+
if (typeof object !== 'object') {
|
92
|
+
throw new DataError();
|
93
|
+
}
|
94
|
+
i = remainingKeyPath.indexOf('.');
|
95
|
+
if (i >= 0) {
|
96
|
+
identifier = remainingKeyPath.slice(0, i);
|
97
|
+
remainingKeyPath = remainingKeyPath.slice(i + 1);
|
98
|
+
if (!Object.hasOwn(object, identifier)) {
|
99
|
+
object[identifier] = {};
|
100
|
+
}
|
101
|
+
object = object[identifier];
|
102
|
+
}
|
103
|
+
}
|
104
|
+
identifier = remainingKeyPath;
|
105
|
+
object[identifier] = newRecord.key;
|
106
|
+
}
|
107
|
+
} else if (this.keyGenerator !== null && typeof newRecord.key === 'number') {
|
108
|
+
this.keyGenerator.setIfLarger(newRecord.key);
|
109
|
+
}
|
110
|
+
const existingRecord = this.records.get(newRecord.key);
|
111
|
+
if (existingRecord) {
|
112
|
+
if (noOverwrite) {
|
113
|
+
throw new ConstraintError();
|
114
|
+
}
|
115
|
+
this.deleteRecord(newRecord.key, rollbackLog);
|
116
|
+
}
|
117
|
+
this.records.add(newRecord);
|
118
|
+
if (rollbackLog) {
|
119
|
+
rollbackLog.push(() => {
|
120
|
+
this.deleteRecord(newRecord.key);
|
121
|
+
});
|
122
|
+
}
|
123
|
+
|
124
|
+
// Update indexes
|
125
|
+
for (const rawIndex of this.rawIndexes.values()) {
|
126
|
+
if (rawIndex.initialized) {
|
127
|
+
rawIndex.storeRecord(newRecord);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
return newRecord.key;
|
131
|
+
}
|
132
|
+
|
133
|
+
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-deleting-records-from-an-object-store
|
134
|
+
deleteRecord(key, rollbackLog) {
|
135
|
+
const deletedRecords = this.records.delete(key);
|
136
|
+
if (rollbackLog) {
|
137
|
+
for (const record of deletedRecords) {
|
138
|
+
rollbackLog.push(() => {
|
139
|
+
this.storeRecord(record, true);
|
140
|
+
});
|
141
|
+
}
|
142
|
+
}
|
143
|
+
for (const rawIndex of this.rawIndexes.values()) {
|
144
|
+
rawIndex.records.deleteByValue(key);
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-clearing-an-object-store
|
149
|
+
clear(rollbackLog) {
|
150
|
+
const deletedRecords = this.records.clear();
|
151
|
+
if (rollbackLog) {
|
152
|
+
for (const record of deletedRecords) {
|
153
|
+
rollbackLog.push(() => {
|
154
|
+
this.storeRecord(record, true);
|
155
|
+
});
|
156
|
+
}
|
157
|
+
}
|
158
|
+
for (const rawIndex of this.rawIndexes.values()) {
|
159
|
+
rawIndex.records.clear();
|
160
|
+
}
|
161
|
+
}
|
162
|
+
count(range) {
|
163
|
+
let count = 0;
|
164
|
+
|
165
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
166
|
+
for (const record of this.records.values(range)) {
|
167
|
+
count += 1;
|
168
|
+
}
|
169
|
+
return count;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
export default ObjectStore;
|
@@ -0,0 +1,141 @@
|
|
1
|
+
import FDBKeyRange from '../FDBKeyRange.js';
|
2
|
+
import {
|
3
|
+
getByKey,
|
4
|
+
getByKeyRange,
|
5
|
+
getIndexByKey,
|
6
|
+
getIndexByKeyGTE,
|
7
|
+
getIndexByKeyRange,
|
8
|
+
} from './binarySearch.js';
|
9
|
+
import cmp from './cmp.js';
|
10
|
+
class RecordStore {
|
11
|
+
records = [];
|
12
|
+
get(key) {
|
13
|
+
if (key instanceof FDBKeyRange) {
|
14
|
+
return getByKeyRange(this.records, key);
|
15
|
+
}
|
16
|
+
return getByKey(this.records, key);
|
17
|
+
}
|
18
|
+
add(newRecord) {
|
19
|
+
// Find where to put it so it's sorted by key
|
20
|
+
let i;
|
21
|
+
if (this.records.length === 0) {
|
22
|
+
i = 0;
|
23
|
+
} else {
|
24
|
+
i = getIndexByKeyGTE(this.records, newRecord.key);
|
25
|
+
if (i === -1) {
|
26
|
+
// If no matching key, add to end
|
27
|
+
i = this.records.length;
|
28
|
+
} else {
|
29
|
+
// If matching key, advance to appropriate position based on value (used in indexes)
|
30
|
+
while (i < this.records.length && cmp(this.records[i].key, newRecord.key) === 0) {
|
31
|
+
if (cmp(this.records[i].value, newRecord.value) !== -1) {
|
32
|
+
// Record value >= newRecord value, so insert here
|
33
|
+
break;
|
34
|
+
}
|
35
|
+
i += 1; // Look at next record
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
this.records.splice(i, 0, newRecord);
|
40
|
+
}
|
41
|
+
delete(key) {
|
42
|
+
const deletedRecords = [];
|
43
|
+
const isRange = key instanceof FDBKeyRange;
|
44
|
+
while (true) {
|
45
|
+
const idx = isRange
|
46
|
+
? getIndexByKeyRange(this.records, key)
|
47
|
+
: getIndexByKey(this.records, key);
|
48
|
+
if (idx === -1) {
|
49
|
+
break;
|
50
|
+
}
|
51
|
+
deletedRecords.push(this.records[idx]);
|
52
|
+
this.records.splice(idx, 1);
|
53
|
+
}
|
54
|
+
return deletedRecords;
|
55
|
+
}
|
56
|
+
deleteByValue(key) {
|
57
|
+
const range = key instanceof FDBKeyRange ? key : FDBKeyRange.only(key);
|
58
|
+
const deletedRecords = [];
|
59
|
+
this.records = this.records.filter(record => {
|
60
|
+
const shouldDelete = range.includes(record.value);
|
61
|
+
if (shouldDelete) {
|
62
|
+
deletedRecords.push(record);
|
63
|
+
}
|
64
|
+
return !shouldDelete;
|
65
|
+
});
|
66
|
+
return deletedRecords;
|
67
|
+
}
|
68
|
+
clear() {
|
69
|
+
const deletedRecords = this.records.slice();
|
70
|
+
this.records = [];
|
71
|
+
return deletedRecords;
|
72
|
+
}
|
73
|
+
values(range, direction = 'next') {
|
74
|
+
return {
|
75
|
+
[Symbol.iterator]: () => {
|
76
|
+
let i;
|
77
|
+
if (direction === 'next') {
|
78
|
+
i = 0;
|
79
|
+
if (range !== undefined && range.lower !== undefined) {
|
80
|
+
while (this.records[i] !== undefined) {
|
81
|
+
const cmpResult = cmp(this.records[i].key, range.lower);
|
82
|
+
if (cmpResult === 1 || (cmpResult === 0 && !range.lowerOpen)) {
|
83
|
+
break;
|
84
|
+
}
|
85
|
+
i += 1;
|
86
|
+
}
|
87
|
+
}
|
88
|
+
} else {
|
89
|
+
i = this.records.length - 1;
|
90
|
+
if (range !== undefined && range.upper !== undefined) {
|
91
|
+
while (this.records[i] !== undefined) {
|
92
|
+
const cmpResult = cmp(this.records[i].key, range.upper);
|
93
|
+
if (cmpResult === -1 || (cmpResult === 0 && !range.upperOpen)) {
|
94
|
+
break;
|
95
|
+
}
|
96
|
+
i -= 1;
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
return {
|
101
|
+
next: () => {
|
102
|
+
let done;
|
103
|
+
let value;
|
104
|
+
if (direction === 'next') {
|
105
|
+
value = this.records[i];
|
106
|
+
done = i >= this.records.length;
|
107
|
+
i += 1;
|
108
|
+
if (!done && range !== undefined && range.upper !== undefined) {
|
109
|
+
const cmpResult = cmp(value.key, range.upper);
|
110
|
+
done = cmpResult === 1 || (cmpResult === 0 && range.upperOpen);
|
111
|
+
if (done) {
|
112
|
+
value = undefined;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
} else {
|
116
|
+
value = this.records[i];
|
117
|
+
done = i < 0;
|
118
|
+
i -= 1;
|
119
|
+
if (!done && range !== undefined && range.lower !== undefined) {
|
120
|
+
const cmpResult = cmp(value.key, range.lower);
|
121
|
+
done = cmpResult === -1 || (cmpResult === 0 && range.lowerOpen);
|
122
|
+
if (done) {
|
123
|
+
value = undefined;
|
124
|
+
}
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
// The weird "as IteratorResult<Record>" is needed because of
|
129
|
+
// https://github.com/Microsoft/TypeScript/issues/11375 and
|
130
|
+
// https://github.com/Microsoft/TypeScript/issues/2983
|
131
|
+
return {
|
132
|
+
done,
|
133
|
+
value,
|
134
|
+
};
|
135
|
+
},
|
136
|
+
};
|
137
|
+
},
|
138
|
+
};
|
139
|
+
}
|
140
|
+
}
|
141
|
+
export default RecordStore;
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import cmp from './cmp.js';
|
2
|
+
/**
|
3
|
+
* Classic binary search implementation. Returns the index where the key
|
4
|
+
* should be inserted, assuming the records list is ordered.
|
5
|
+
*/
|
6
|
+
function binarySearch(records, key) {
|
7
|
+
let low = 0;
|
8
|
+
let high = records.length;
|
9
|
+
let mid;
|
10
|
+
while (low < high) {
|
11
|
+
mid = (low + high) >>> 1; // like Math.floor((low + high) / 2) but fast
|
12
|
+
if (cmp(records[mid].key, key) < 0) {
|
13
|
+
low = mid + 1;
|
14
|
+
} else {
|
15
|
+
high = mid;
|
16
|
+
}
|
17
|
+
}
|
18
|
+
return low;
|
19
|
+
}
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Equivalent to `records.findIndex(record => cmp(record.key, key) === 0)`
|
23
|
+
*/
|
24
|
+
export function getIndexByKey(records, key) {
|
25
|
+
const idx = binarySearch(records, key);
|
26
|
+
const record = records[idx];
|
27
|
+
if (record && cmp(record.key, key) === 0) {
|
28
|
+
return idx;
|
29
|
+
}
|
30
|
+
return -1;
|
31
|
+
}
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Equivalent to `records.find(record => cmp(record.key, key) === 0)`
|
35
|
+
*/
|
36
|
+
export function getByKey(records, key) {
|
37
|
+
const idx = getIndexByKey(records, key);
|
38
|
+
return records[idx];
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Equivalent to `records.findIndex(record => key.includes(record.key))`
|
43
|
+
*/
|
44
|
+
export function getIndexByKeyRange(records, keyRange) {
|
45
|
+
const lowerIdx =
|
46
|
+
typeof keyRange.lower === 'undefined' ? 0 : binarySearch(records, keyRange.lower);
|
47
|
+
const upperIdx =
|
48
|
+
typeof keyRange.upper === 'undefined'
|
49
|
+
? records.length - 1
|
50
|
+
: binarySearch(records, keyRange.upper);
|
51
|
+
for (let i = lowerIdx; i <= upperIdx; i++) {
|
52
|
+
const record = records[i];
|
53
|
+
if (record && keyRange.includes(record.key)) {
|
54
|
+
return i;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
return -1;
|
58
|
+
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Equivalent to `records.find(record => key.includes(record.key))`
|
62
|
+
*/
|
63
|
+
export function getByKeyRange(records, keyRange) {
|
64
|
+
const idx = getIndexByKeyRange(records, keyRange);
|
65
|
+
return records[idx];
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Equivalent to `records.findIndex(record => cmp(record.key, key) >= 0)`
|
70
|
+
*/
|
71
|
+
export function getIndexByKeyGTE(records, key) {
|
72
|
+
const idx = binarySearch(records, key);
|
73
|
+
const record = records[idx];
|
74
|
+
if (record && cmp(record.key, key) >= 0) {
|
75
|
+
return idx;
|
76
|
+
}
|
77
|
+
return -1;
|
78
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
// http://w3c.github.io/IndexedDB/#check-that-a-key-could-be-injected-into-a-value
|
2
|
+
const canInjectKey = (keyPath, value) => {
|
3
|
+
if (Array.isArray(keyPath)) {
|
4
|
+
throw new Error(
|
5
|
+
'The key paths used in this section are always strings and never sequences, since it is not possible to create a object store which has a key generator and also has a key path that is a sequence.',
|
6
|
+
);
|
7
|
+
}
|
8
|
+
const identifiers = keyPath.split('.');
|
9
|
+
if (identifiers.length === 0) {
|
10
|
+
throw new Error('Assert: identifiers is not empty');
|
11
|
+
}
|
12
|
+
identifiers.pop();
|
13
|
+
for (const identifier of identifiers) {
|
14
|
+
if (typeof value !== 'object' && !Array.isArray(value)) {
|
15
|
+
return false;
|
16
|
+
}
|
17
|
+
const hop = Object.hasOwn(value, identifier);
|
18
|
+
if (!hop) {
|
19
|
+
return true;
|
20
|
+
}
|
21
|
+
value = value[identifier];
|
22
|
+
}
|
23
|
+
return typeof value === 'object' || Array.isArray(value);
|
24
|
+
};
|
25
|
+
export default canInjectKey;
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import { DataError } from './errors.js';
|
2
|
+
import valueToKey from './valueToKey.js';
|
3
|
+
const getType = x => {
|
4
|
+
if (typeof x === 'number') {
|
5
|
+
return 'Number';
|
6
|
+
}
|
7
|
+
if (Object.prototype.toString.call(x) === '[object Date]') {
|
8
|
+
return 'Date';
|
9
|
+
}
|
10
|
+
if (Array.isArray(x)) {
|
11
|
+
return 'Array';
|
12
|
+
}
|
13
|
+
if (typeof x === 'string') {
|
14
|
+
return 'String';
|
15
|
+
}
|
16
|
+
if (x instanceof ArrayBuffer) {
|
17
|
+
return 'Binary';
|
18
|
+
}
|
19
|
+
throw new DataError();
|
20
|
+
};
|
21
|
+
|
22
|
+
// https://w3c.github.io/IndexedDB/#compare-two-keys
|
23
|
+
const cmp = (first, second) => {
|
24
|
+
if (second === undefined) {
|
25
|
+
throw new TypeError();
|
26
|
+
}
|
27
|
+
first = valueToKey(first);
|
28
|
+
second = valueToKey(second);
|
29
|
+
const t1 = getType(first);
|
30
|
+
const t2 = getType(second);
|
31
|
+
if (t1 !== t2) {
|
32
|
+
if (t1 === 'Array') {
|
33
|
+
return 1;
|
34
|
+
}
|
35
|
+
if (t1 === 'Binary' && (t2 === 'String' || t2 === 'Date' || t2 === 'Number')) {
|
36
|
+
return 1;
|
37
|
+
}
|
38
|
+
if (t1 === 'String' && (t2 === 'Date' || t2 === 'Number')) {
|
39
|
+
return 1;
|
40
|
+
}
|
41
|
+
if (t1 === 'Date' && t2 === 'Number') {
|
42
|
+
return 1;
|
43
|
+
}
|
44
|
+
return -1;
|
45
|
+
}
|
46
|
+
if (t1 === 'Binary') {
|
47
|
+
first = new Uint8Array(first);
|
48
|
+
second = new Uint8Array(second);
|
49
|
+
}
|
50
|
+
if (t1 === 'Array' || t1 === 'Binary') {
|
51
|
+
const length = Math.min(first.length, second.length);
|
52
|
+
for (let i = 0; i < length; i++) {
|
53
|
+
const result = cmp(first[i], second[i]);
|
54
|
+
if (result !== 0) {
|
55
|
+
return result;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
if (first.length > second.length) {
|
59
|
+
return 1;
|
60
|
+
}
|
61
|
+
if (first.length < second.length) {
|
62
|
+
return -1;
|
63
|
+
}
|
64
|
+
return 0;
|
65
|
+
}
|
66
|
+
if (t1 === 'Date') {
|
67
|
+
if (first.getTime() === second.getTime()) {
|
68
|
+
return 0;
|
69
|
+
}
|
70
|
+
} else {
|
71
|
+
if (first === second) {
|
72
|
+
return 0;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
return first > second ? 1 : -1;
|
76
|
+
};
|
77
|
+
export default cmp;
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// https://heycam.github.io/webidl/#EnforceRange
|
2
|
+
|
3
|
+
const enforceRange = (num, type) => {
|
4
|
+
const min = 0;
|
5
|
+
const max = type === 'unsigned long' ? 4294967295 : 9007199254740991;
|
6
|
+
if (isNaN(num) || num < min || num > max) {
|
7
|
+
throw new TypeError();
|
8
|
+
}
|
9
|
+
if (num >= 0) {
|
10
|
+
return Math.floor(num);
|
11
|
+
}
|
12
|
+
};
|
13
|
+
export default enforceRange;
|
@@ -0,0 +1,69 @@
|
|
1
|
+
const messages = {
|
2
|
+
AbortError: 'A request was aborted, for example through a call to IDBTransaction.abort.',
|
3
|
+
ConstraintError:
|
4
|
+
'A mutation operation in the transaction failed because a constraint was not satisfied. For example, an object such as an object store or index already exists and a request attempted to create a new one.',
|
5
|
+
DataCloneError:
|
6
|
+
'The data being stored could not be cloned by the internal structured cloning algorithm.',
|
7
|
+
DataError: 'Data provided to an operation does not meet requirements.',
|
8
|
+
InvalidAccessError:
|
9
|
+
'An invalid operation was performed on an object. For example transaction creation attempt was made, but an empty scope was provided.',
|
10
|
+
InvalidStateError:
|
11
|
+
'An operation was called on an object on which it is not allowed or at a time when it is not allowed. Also occurs if a request is made on a source object that has been deleted or removed. Use TransactionInactiveError or ReadOnlyError when possible, as they are more specific variations of InvalidStateError.',
|
12
|
+
NotFoundError:
|
13
|
+
'The operation failed because the requested database object could not be found. For example, an object store did not exist but was being opened.',
|
14
|
+
ReadOnlyError: 'The mutating operation was attempted in a "readonly" transaction.',
|
15
|
+
TransactionInactiveError:
|
16
|
+
'A request was placed against a transaction which is currently not active, or which is finished.',
|
17
|
+
VersionError:
|
18
|
+
'An attempt was made to open a database using a lower version than the existing version.',
|
19
|
+
};
|
20
|
+
export class AbortError extends DOMException {
|
21
|
+
constructor(message = messages.AbortError) {
|
22
|
+
super(message, 'AbortError');
|
23
|
+
}
|
24
|
+
}
|
25
|
+
export class ConstraintError extends DOMException {
|
26
|
+
constructor(message = messages.ConstraintError) {
|
27
|
+
super(message, 'ConstraintError');
|
28
|
+
}
|
29
|
+
}
|
30
|
+
export class DataCloneError extends DOMException {
|
31
|
+
constructor(message = messages.DataCloneError) {
|
32
|
+
super(message, 'DataCloneError');
|
33
|
+
}
|
34
|
+
}
|
35
|
+
export class DataError extends DOMException {
|
36
|
+
constructor(message = messages.DataError) {
|
37
|
+
super(message, 'DataError');
|
38
|
+
}
|
39
|
+
}
|
40
|
+
export class InvalidAccessError extends DOMException {
|
41
|
+
constructor(message = messages.InvalidAccessError) {
|
42
|
+
super(message, 'InvalidAccessError');
|
43
|
+
}
|
44
|
+
}
|
45
|
+
export class InvalidStateError extends DOMException {
|
46
|
+
constructor(message = messages.InvalidStateError) {
|
47
|
+
super(message, 'InvalidStateError');
|
48
|
+
}
|
49
|
+
}
|
50
|
+
export class NotFoundError extends DOMException {
|
51
|
+
constructor(message = messages.NotFoundError) {
|
52
|
+
super(message, 'NotFoundError');
|
53
|
+
}
|
54
|
+
}
|
55
|
+
export class ReadOnlyError extends DOMException {
|
56
|
+
constructor(message = messages.ReadOnlyError) {
|
57
|
+
super(message, 'ReadOnlyError');
|
58
|
+
}
|
59
|
+
}
|
60
|
+
export class TransactionInactiveError extends DOMException {
|
61
|
+
constructor(message = messages.TransactionInactiveError) {
|
62
|
+
super(message, 'TransactionInactiveError');
|
63
|
+
}
|
64
|
+
}
|
65
|
+
export class VersionError extends DOMException {
|
66
|
+
constructor(message = messages.VersionError) {
|
67
|
+
super(message, 'VersionError');
|
68
|
+
}
|
69
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import valueToKey from './valueToKey.js';
|
2
|
+
|
3
|
+
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-extracting-a-key-from-a-value-using-a-key-path
|
4
|
+
const extractKey = (keyPath, value) => {
|
5
|
+
if (Array.isArray(keyPath)) {
|
6
|
+
const result = [];
|
7
|
+
for (let item of keyPath) {
|
8
|
+
// This doesn't make sense to me based on the spec, but it is needed to pass the W3C KeyPath tests (see same
|
9
|
+
// comment in validateKeyPath)
|
10
|
+
if (item !== undefined && item !== null && typeof item !== 'string' && item.toString) {
|
11
|
+
item = item.toString();
|
12
|
+
}
|
13
|
+
result.push(valueToKey(extractKey(item, value)));
|
14
|
+
}
|
15
|
+
return result;
|
16
|
+
}
|
17
|
+
if (keyPath === '') {
|
18
|
+
return value;
|
19
|
+
}
|
20
|
+
let remainingKeyPath = keyPath;
|
21
|
+
let object = value;
|
22
|
+
while (remainingKeyPath !== null) {
|
23
|
+
let identifier;
|
24
|
+
const i = remainingKeyPath.indexOf('.');
|
25
|
+
if (i >= 0) {
|
26
|
+
identifier = remainingKeyPath.slice(0, i);
|
27
|
+
remainingKeyPath = remainingKeyPath.slice(i + 1);
|
28
|
+
} else {
|
29
|
+
identifier = remainingKeyPath;
|
30
|
+
remainingKeyPath = null;
|
31
|
+
}
|
32
|
+
if (object === undefined || object === null || !Object.hasOwn(object, identifier)) {
|
33
|
+
return;
|
34
|
+
}
|
35
|
+
object = object[identifier];
|
36
|
+
}
|
37
|
+
return object;
|
38
|
+
};
|
39
|
+
export default extractKey;
|
@@ -0,0 +1,30 @@
|
|
1
|
+
// When running within Node.js (including jsdom), we want to use setImmediate
|
2
|
+
// (which runs immediately) rather than setTimeout (which enforces a minimum
|
3
|
+
// delay of 1ms, and on Windows only has a resolution of 15ms or so). jsdom
|
4
|
+
// doesn't provide setImmediate (to better match the browser environment) and
|
5
|
+
// sandboxes scripts, but its sandbox is by necessity imperfect, so we can break
|
6
|
+
// out of it:
|
7
|
+
//
|
8
|
+
// - https://github.com/jsdom/jsdom#executing-scripts
|
9
|
+
// - https://github.com/jsdom/jsdom/issues/2729
|
10
|
+
// - https://github.com/scala-js/scala-js-macrotask-executor/pull/17
|
11
|
+
function getSetImmediateFromJsdom() {
|
12
|
+
if (typeof navigator !== 'undefined' && /jsdom/.test(navigator.userAgent)) {
|
13
|
+
const outerRealmFunctionConstructor = Node.constructor;
|
14
|
+
return new outerRealmFunctionConstructor('return setImmediate')();
|
15
|
+
} else {
|
16
|
+
return undefined;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
// Schedules a task to run later. Use Node.js's setImmediate if available and
|
21
|
+
// setTimeout otherwise. Note that options like process.nextTick or
|
22
|
+
// queueMicrotask will likely not work: IndexedDB semantics require that
|
23
|
+
// transactions are marked as not active when the event loop runs. The next
|
24
|
+
// tick queue and microtask queue run within the current event loop macrotask,
|
25
|
+
// so they'd process database operations too quickly.
|
26
|
+
export const queueTask = fn => {
|
27
|
+
const setImmediate =
|
28
|
+
globalThis.setImmediate || getSetImmediateFromJsdom() || (fn => setTimeout(fn, 0));
|
29
|
+
setImmediate(fn);
|
30
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|