@react-native-firebase/app 20.1.0 → 20.2.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +20 -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 +8 -7
@@ -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 {};
|