@react-native-firebase/database 24.1.1 → 25.0.1
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 +36 -0
- package/dist/module/DatabaseDataSnapshot.js +144 -0
- package/dist/module/DatabaseDataSnapshot.js.map +1 -0
- package/dist/module/DatabaseOnDisconnect.js +84 -0
- package/dist/module/DatabaseOnDisconnect.js.map +1 -0
- package/dist/module/DatabaseQuery.js +298 -0
- package/dist/module/DatabaseQuery.js.map +1 -0
- package/{lib → dist/module}/DatabaseQueryModifiers.js +49 -98
- package/dist/module/DatabaseQueryModifiers.js.map +1 -0
- package/dist/module/DatabaseReference.js +169 -0
- package/dist/module/DatabaseReference.js.map +1 -0
- package/dist/module/DatabaseStatics.js +35 -0
- package/dist/module/DatabaseStatics.js.map +1 -0
- package/{lib → dist/module}/DatabaseSyncTree.js +74 -156
- package/dist/module/DatabaseSyncTree.js.map +1 -0
- package/{lib → dist/module}/DatabaseThenableReference.js +12 -12
- package/dist/module/DatabaseThenableReference.js.map +1 -0
- package/{lib → dist/module}/DatabaseTransaction.js +26 -89
- package/dist/module/DatabaseTransaction.js.map +1 -0
- package/dist/module/index.js +27 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/modular/query.js +138 -0
- package/dist/module/modular/query.js.map +1 -0
- package/dist/module/modular/transaction.js +24 -0
- package/dist/module/modular/transaction.js.map +1 -0
- package/dist/module/modular.js +76 -0
- package/dist/module/modular.js.map +1 -0
- package/dist/module/namespaced.js +147 -0
- package/dist/module/namespaced.js.map +1 -0
- package/dist/module/package.json +1 -0
- package/dist/module/types/database.js +21 -0
- package/dist/module/types/database.js.map +1 -0
- package/dist/module/types/internal.js +4 -0
- package/dist/module/types/internal.js.map +1 -0
- package/dist/module/types/namespaced.js +67 -0
- package/dist/module/types/namespaced.js.map +1 -0
- package/dist/module/version.js +5 -0
- package/dist/module/version.js.map +1 -0
- package/dist/module/web/RNFBDatabaseModule.android.js +6 -0
- package/dist/module/web/RNFBDatabaseModule.android.js.map +1 -0
- package/dist/module/web/RNFBDatabaseModule.ios.js +6 -0
- package/dist/module/web/RNFBDatabaseModule.ios.js.map +1 -0
- package/dist/module/web/RNFBDatabaseModule.js +375 -0
- package/dist/module/web/RNFBDatabaseModule.js.map +1 -0
- package/{lib → dist/module}/web/query.js +19 -24
- package/dist/module/web/query.js.map +1 -0
- package/dist/typescript/lib/DatabaseDataSnapshot.d.ts +20 -0
- package/dist/typescript/lib/DatabaseDataSnapshot.d.ts.map +1 -0
- package/dist/typescript/lib/DatabaseOnDisconnect.d.ts +19 -0
- package/dist/typescript/lib/DatabaseOnDisconnect.d.ts.map +1 -0
- package/dist/typescript/lib/DatabaseQuery.d.ts +32 -0
- package/dist/typescript/lib/DatabaseQuery.d.ts.map +1 -0
- package/dist/typescript/lib/DatabaseQueryModifiers.d.ts +55 -0
- package/dist/typescript/lib/DatabaseQueryModifiers.d.ts.map +1 -0
- package/dist/typescript/lib/DatabaseReference.d.ts +21 -0
- package/dist/typescript/lib/DatabaseReference.d.ts.map +1 -0
- package/dist/typescript/lib/DatabaseStatics.d.ts +10 -0
- package/dist/typescript/lib/DatabaseStatics.d.ts.map +1 -0
- package/dist/typescript/lib/DatabaseSyncTree.d.ts +72 -0
- package/dist/typescript/lib/DatabaseSyncTree.d.ts.map +1 -0
- package/dist/typescript/lib/DatabaseThenableReference.d.ts +13 -0
- package/dist/typescript/lib/DatabaseThenableReference.d.ts.map +1 -0
- package/dist/typescript/lib/DatabaseTransaction.d.ts +15 -0
- package/dist/typescript/lib/DatabaseTransaction.d.ts.map +1 -0
- package/dist/typescript/lib/index.d.ts +6 -0
- package/dist/typescript/lib/index.d.ts.map +1 -0
- package/dist/typescript/lib/modular/query.d.ts +41 -0
- package/dist/typescript/lib/modular/query.d.ts.map +1 -0
- package/dist/typescript/lib/modular/transaction.d.ts +3 -0
- package/dist/typescript/lib/modular/transaction.d.ts.map +1 -0
- package/dist/typescript/lib/modular.d.ts +24 -0
- package/dist/typescript/lib/modular.d.ts.map +1 -0
- package/dist/typescript/lib/namespaced.d.ts +13 -0
- package/dist/typescript/lib/namespaced.d.ts.map +1 -0
- package/dist/typescript/lib/types/database.d.ts +93 -0
- package/dist/typescript/lib/types/database.d.ts.map +1 -0
- package/dist/typescript/lib/types/internal.d.ts +189 -0
- package/dist/typescript/lib/types/internal.d.ts.map +1 -0
- package/dist/typescript/lib/types/namespaced.d.ts +1230 -0
- package/dist/typescript/lib/types/namespaced.d.ts.map +1 -0
- package/dist/typescript/lib/version.d.ts +2 -0
- package/dist/typescript/lib/version.d.ts.map +1 -0
- package/dist/typescript/lib/web/RNFBDatabaseModule.android.d.ts +3 -0
- package/dist/typescript/lib/web/RNFBDatabaseModule.android.d.ts.map +1 -0
- package/dist/typescript/lib/web/RNFBDatabaseModule.d.ts +3 -0
- package/dist/typescript/lib/web/RNFBDatabaseModule.d.ts.map +1 -0
- package/dist/typescript/lib/web/RNFBDatabaseModule.ios.d.ts +3 -0
- package/dist/typescript/lib/web/RNFBDatabaseModule.ios.d.ts.map +1 -0
- package/dist/typescript/lib/web/query.d.ts +13 -0
- package/dist/typescript/lib/web/query.d.ts.map +1 -0
- package/dist/typescript/package.json +1 -0
- package/lib/{DatabaseDataSnapshot.js → DatabaseDataSnapshot.ts} +62 -88
- package/lib/{DatabaseOnDisconnect.js → DatabaseOnDisconnect.ts} +25 -23
- package/lib/{DatabaseQuery.js → DatabaseQuery.ts} +130 -144
- package/lib/DatabaseQueryModifiers.ts +283 -0
- package/lib/{DatabaseReference.js → DatabaseReference.ts} +103 -85
- package/lib/{DatabaseStatics.js → DatabaseStatics.ts} +4 -2
- package/lib/DatabaseSyncTree.ts +390 -0
- package/lib/DatabaseThenableReference.ts +73 -0
- package/lib/DatabaseTransaction.ts +172 -0
- package/lib/index.ts +25 -0
- package/lib/modular/query.ts +410 -0
- package/lib/modular/transaction.ts +34 -0
- package/lib/modular.ts +134 -0
- package/lib/{index.js → namespaced.ts} +91 -90
- package/lib/types/database.ts +144 -0
- package/lib/types/internal.ts +317 -0
- package/lib/{index.d.ts → types/namespaced.ts} +48 -9
- package/lib/version.ts +2 -0
- package/lib/web/RNFBDatabaseModule.android.ts +4 -0
- package/lib/web/RNFBDatabaseModule.ios.ts +4 -0
- package/lib/web/RNFBDatabaseModule.ts +534 -0
- package/lib/web/query.ts +97 -0
- package/package.json +42 -7
- package/tsconfig.json +21 -0
- package/typedoc.json +3 -3
- package/lib/modular/index.d.ts +0 -246
- package/lib/modular/index.js +0 -134
- package/lib/modular/query.d.ts +0 -972
- package/lib/modular/query.js +0 -305
- package/lib/modular/transaction.d.ts +0 -54
- package/lib/modular/transaction.js +0 -24
- package/lib/version.js +0 -2
- package/lib/web/RNFBDatabaseModule.android.js +0 -2
- package/lib/web/RNFBDatabaseModule.ios.js +0 -2
- package/lib/web/RNFBDatabaseModule.js +0 -558
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { createDeprecationProxy, isString } from '@react-native-firebase/app/dist/module/common';
|
|
19
|
+
import NativeError from '@react-native-firebase/app/dist/module/internal/NativeFirebaseError';
|
|
20
|
+
import SharedEventEmitter from '@react-native-firebase/app/dist/module/internal/SharedEventEmitter';
|
|
21
|
+
import { getReactNativeModule } from '@react-native-firebase/app/dist/module/internal/nativeModule';
|
|
22
|
+
import DatabaseDataSnapshot from './DatabaseDataSnapshot';
|
|
23
|
+
import type {
|
|
24
|
+
DatabaseChildSnapshotResultInternal,
|
|
25
|
+
DatabaseListenRegistrationInternal,
|
|
26
|
+
DatabaseSnapshotInternal,
|
|
27
|
+
RNFBDatabaseModule,
|
|
28
|
+
} from './types/internal';
|
|
29
|
+
import type { FirebaseDatabaseTypes } from './types/namespaced';
|
|
30
|
+
|
|
31
|
+
interface DatabaseSyncTreeRegistration {
|
|
32
|
+
eventType: string;
|
|
33
|
+
ref: FirebaseDatabaseTypes.Reference;
|
|
34
|
+
path: string;
|
|
35
|
+
key: string;
|
|
36
|
+
appName: string;
|
|
37
|
+
dbURL: string | null;
|
|
38
|
+
eventRegistrationKey: string;
|
|
39
|
+
listener: (...args: any[]) => void;
|
|
40
|
+
once?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface SharedEventSubscriptionInternal {
|
|
44
|
+
listener: (...args: any[]) => void;
|
|
45
|
+
remove(): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface DatabaseSyncEventBody {
|
|
49
|
+
error?: unknown;
|
|
50
|
+
eventType?: string;
|
|
51
|
+
data?: DatabaseSnapshotInternal | DatabaseChildSnapshotResultInternal;
|
|
52
|
+
registration: DatabaseListenRegistrationInternal;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface DatabaseSyncEventInternal {
|
|
56
|
+
body: DatabaseSyncEventBody;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type DatabaseSyncTreeListener = DatabaseSyncTreeRegistration['listener'];
|
|
60
|
+
type DatabaseSyncTreePathMap = Record<
|
|
61
|
+
string,
|
|
62
|
+
Record<string, Record<string, DatabaseSyncTreeListener>>
|
|
63
|
+
>;
|
|
64
|
+
type DatabaseSyncTreeRegistry = Record<string, Set<SharedEventSubscriptionInternal>>;
|
|
65
|
+
|
|
66
|
+
class DatabaseSyncTree {
|
|
67
|
+
private _tree: DatabaseSyncTreePathMap = {};
|
|
68
|
+
private _reverseLookup: Record<string, DatabaseSyncTreeRegistration> = {};
|
|
69
|
+
|
|
70
|
+
// We need to track individual emitter subscriptions so one callback can be removed
|
|
71
|
+
// without clearing every listener bound to the same registration key.
|
|
72
|
+
private _registry: DatabaseSyncTreeRegistry = {};
|
|
73
|
+
|
|
74
|
+
constructor() {
|
|
75
|
+
SharedEventEmitter.addListener(
|
|
76
|
+
'database_sync_event',
|
|
77
|
+
this._handleSyncEvent.bind(this) as (event: unknown) => void,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private get native(): RNFBDatabaseModule {
|
|
82
|
+
return getReactNativeModule('RNFBDatabaseQueryModule') as unknown as RNFBDatabaseModule;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private _allocate(
|
|
86
|
+
registry: DatabaseSyncTreeRegistry,
|
|
87
|
+
eventType: string,
|
|
88
|
+
): Set<SharedEventSubscriptionInternal> {
|
|
89
|
+
let registrations = registry[eventType];
|
|
90
|
+
if (registrations == null) {
|
|
91
|
+
registrations = new Set<SharedEventSubscriptionInternal>();
|
|
92
|
+
registry[eventType] = registrations;
|
|
93
|
+
}
|
|
94
|
+
return registrations;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Handles an incoming event from native.
|
|
99
|
+
*/
|
|
100
|
+
private _handleSyncEvent(event: DatabaseSyncEventInternal): void {
|
|
101
|
+
const { body } = event;
|
|
102
|
+
if (body.error) {
|
|
103
|
+
this._handleErrorEvent(body);
|
|
104
|
+
} else {
|
|
105
|
+
this._handleValueEvent(body);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Routes native database query listener cancellation events to their JS counterparts.
|
|
111
|
+
*/
|
|
112
|
+
private _handleErrorEvent(event: DatabaseSyncEventBody): void {
|
|
113
|
+
const { eventRegistrationKey, registrationCancellationKey } = event.registration;
|
|
114
|
+
|
|
115
|
+
if (!registrationCancellationKey) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const registration = this.getRegistration(registrationCancellationKey);
|
|
120
|
+
|
|
121
|
+
if (registration) {
|
|
122
|
+
const error = NativeError.fromEvent(
|
|
123
|
+
event.error as Parameters<typeof NativeError.fromEvent>[0],
|
|
124
|
+
'database',
|
|
125
|
+
) as Error;
|
|
126
|
+
|
|
127
|
+
SharedEventEmitter.emit(registrationCancellationKey, error);
|
|
128
|
+
|
|
129
|
+
// If native cancelled the listener, value events for the paired registration
|
|
130
|
+
// are finished as well, so clean up the JS-side bookkeeping.
|
|
131
|
+
this.removeRegistration(eventRegistrationKey);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Routes native database `on` events to their JS equivalents.
|
|
137
|
+
* If there are no listeners left for the event, native is told to unsubscribe.
|
|
138
|
+
*/
|
|
139
|
+
private _handleValueEvent(event: DatabaseSyncEventBody): void {
|
|
140
|
+
const { key, eventRegistrationKey } = event.registration;
|
|
141
|
+
const registration = this.getRegistration(eventRegistrationKey);
|
|
142
|
+
|
|
143
|
+
if (!registration) {
|
|
144
|
+
this.native.off(key, eventRegistrationKey);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let snapshot: FirebaseDatabaseTypes.DataSnapshot;
|
|
149
|
+
let previousChildName: string | null | undefined;
|
|
150
|
+
|
|
151
|
+
if (!event.data) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (event.eventType === 'value') {
|
|
156
|
+
snapshot = createDeprecationProxy(
|
|
157
|
+
new DatabaseDataSnapshot(registration.ref, event.data as DatabaseSnapshotInternal),
|
|
158
|
+
) as FirebaseDatabaseTypes.DataSnapshot;
|
|
159
|
+
} else {
|
|
160
|
+
const childData = event.data as DatabaseChildSnapshotResultInternal;
|
|
161
|
+
snapshot = createDeprecationProxy(
|
|
162
|
+
new DatabaseDataSnapshot(registration.ref, childData.snapshot),
|
|
163
|
+
) as FirebaseDatabaseTypes.DataSnapshot;
|
|
164
|
+
previousChildName = childData.previousChildName;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
SharedEventEmitter.emit(eventRegistrationKey, snapshot, previousChildName);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Returns registration information such as appName, ref, path and registration keys.
|
|
172
|
+
*/
|
|
173
|
+
getRegistration(registration: string): DatabaseSyncTreeRegistration | null {
|
|
174
|
+
return this._reverseLookup[registration]
|
|
175
|
+
? Object.assign({}, this._reverseLookup[registration])
|
|
176
|
+
: null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Removes all listeners for the specified registration keys.
|
|
181
|
+
*/
|
|
182
|
+
removeListenersForRegistrations(registrations: string[] | string): number {
|
|
183
|
+
if (isString(registrations)) {
|
|
184
|
+
this.removeRegistration(registrations);
|
|
185
|
+
SharedEventEmitter.removeAllListeners(registrations);
|
|
186
|
+
delete this._registry[registrations];
|
|
187
|
+
return 1;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!Array.isArray(registrations)) {
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
for (let i = 0, len = registrations.length; i < len; i++) {
|
|
195
|
+
this.removeRegistration(registrations[i] as string);
|
|
196
|
+
SharedEventEmitter.removeAllListeners(registrations[i] as string);
|
|
197
|
+
delete this._registry[registrations[i] as string];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return registrations.length;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Removes a specific listener from the specified registrations.
|
|
205
|
+
*/
|
|
206
|
+
removeListenerRegistrations(
|
|
207
|
+
listener: (...args: any[]) => void,
|
|
208
|
+
registrations: string[],
|
|
209
|
+
): string[] {
|
|
210
|
+
if (!Array.isArray(registrations)) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const removed: string[] = [];
|
|
215
|
+
|
|
216
|
+
for (let i = 0, len = registrations.length; i < len; i++) {
|
|
217
|
+
const registration = registrations[i] as string;
|
|
218
|
+
let subscriptions: SharedEventSubscriptionInternal[] | undefined;
|
|
219
|
+
|
|
220
|
+
const registrySubscriptionsSet = this._registry[registration];
|
|
221
|
+
if (registrySubscriptionsSet) {
|
|
222
|
+
subscriptions = Array.from(registrySubscriptionsSet);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (subscriptions) {
|
|
226
|
+
for (let j = 0, l = subscriptions.length; j < l; j++) {
|
|
227
|
+
const subscription = subscriptions[j];
|
|
228
|
+
if (subscription && subscription.listener === listener) {
|
|
229
|
+
this._registry[registration]?.delete(subscription);
|
|
230
|
+
subscription.remove();
|
|
231
|
+
removed.push(registration);
|
|
232
|
+
this.removeRegistration(registration);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return removed;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Returns all registration keys for the specified path.
|
|
243
|
+
*/
|
|
244
|
+
getRegistrationsByPath(path: string): string[] {
|
|
245
|
+
const out: string[] = [];
|
|
246
|
+
const eventKeys = Object.keys(this._tree[path] || {});
|
|
247
|
+
|
|
248
|
+
for (let i = 0, len = eventKeys.length; i < len; i++) {
|
|
249
|
+
Array.prototype.push.apply(
|
|
250
|
+
out,
|
|
251
|
+
Object.keys(this._tree[path]?.[eventKeys[i] as string] || {}),
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Returns all registration keys for the specified path and event type.
|
|
260
|
+
*/
|
|
261
|
+
getRegistrationsByPathEvent(path: string, eventType?: string): string[] {
|
|
262
|
+
if (!eventType) {
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (!this._tree[path]) {
|
|
267
|
+
return [];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (!this._tree[path][eventType]) {
|
|
271
|
+
return [];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return Object.keys(this._tree[path][eventType]);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Returns a single registration key for the specified path, event type, and listener.
|
|
279
|
+
*/
|
|
280
|
+
getOneByPathEventListener(
|
|
281
|
+
path: string,
|
|
282
|
+
eventType: string,
|
|
283
|
+
listener: (...args: any[]) => void,
|
|
284
|
+
): string | null {
|
|
285
|
+
if (!this._tree[path]) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (!this._tree[path][eventType]) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const registrationsForPathEvent = Object.entries(this._tree[path][eventType]);
|
|
294
|
+
|
|
295
|
+
for (let i = 0; i < registrationsForPathEvent.length; i++) {
|
|
296
|
+
const registration = registrationsForPathEvent[i];
|
|
297
|
+
if (registration?.[1] === listener) {
|
|
298
|
+
return registration[0];
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Register a new listener.
|
|
307
|
+
*/
|
|
308
|
+
addRegistration(registration: DatabaseSyncTreeRegistration): string {
|
|
309
|
+
const { eventRegistrationKey, eventType, listener, once, path } = registration;
|
|
310
|
+
|
|
311
|
+
if (!this._tree[path]) {
|
|
312
|
+
this._tree[path] = {};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (!this._tree[path][eventType]) {
|
|
316
|
+
this._tree[path][eventType] = {};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
this._tree[path][eventType][eventRegistrationKey] = listener;
|
|
320
|
+
this._reverseLookup[eventRegistrationKey] = registration;
|
|
321
|
+
|
|
322
|
+
if (once) {
|
|
323
|
+
const subscription = SharedEventEmitter.addListener(
|
|
324
|
+
eventRegistrationKey,
|
|
325
|
+
(event: unknown) => {
|
|
326
|
+
this._onOnceRemoveRegistration(eventRegistrationKey, listener)(event);
|
|
327
|
+
subscription.remove();
|
|
328
|
+
},
|
|
329
|
+
) as unknown as SharedEventSubscriptionInternal;
|
|
330
|
+
return eventRegistrationKey;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const emitterSubscription = SharedEventEmitter.addListener(
|
|
334
|
+
eventRegistrationKey,
|
|
335
|
+
listener,
|
|
336
|
+
) as unknown as SharedEventSubscriptionInternal;
|
|
337
|
+
|
|
338
|
+
const registrations = this._allocate(this._registry, eventRegistrationKey);
|
|
339
|
+
registrations.add(emitterSubscription);
|
|
340
|
+
|
|
341
|
+
return eventRegistrationKey;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Remove a registration and unsubscribe the paired native listener when needed.
|
|
346
|
+
*/
|
|
347
|
+
removeRegistration(registration: string): boolean {
|
|
348
|
+
if (!this._reverseLookup[registration]) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const registrationObj = this._reverseLookup[registration];
|
|
353
|
+
const { path, eventType, once } = registrationObj;
|
|
354
|
+
|
|
355
|
+
if (!this._tree[path]) {
|
|
356
|
+
delete this._reverseLookup[registration];
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!this._tree[path][eventType]) {
|
|
361
|
+
delete this._reverseLookup[registration];
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// `once` registrations are already unsubscribed by native after the first event.
|
|
366
|
+
if (!once) {
|
|
367
|
+
this.native.off(registrationObj.key, registration);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
delete this._tree[path][eventType][registration];
|
|
371
|
+
delete this._reverseLookup[registration];
|
|
372
|
+
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Wraps a `once` listener with a function that self-deregisters.
|
|
378
|
+
*/
|
|
379
|
+
private _onOnceRemoveRegistration(
|
|
380
|
+
registration: string,
|
|
381
|
+
listener: (...args: any[]) => void,
|
|
382
|
+
): (...args: any[]) => void {
|
|
383
|
+
return (...args: any[]) => {
|
|
384
|
+
this.removeRegistration(registration);
|
|
385
|
+
listener(...args);
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export default new DatabaseSyncTree();
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { createDeprecationProxy } from '@react-native-firebase/app/dist/module/common';
|
|
19
|
+
|
|
20
|
+
import type { DatabaseInternal } from './types/internal';
|
|
21
|
+
import type { FirebaseDatabaseTypes } from './types/namespaced';
|
|
22
|
+
|
|
23
|
+
type DatabaseReferenceConstructor = new (
|
|
24
|
+
database: DatabaseInternal,
|
|
25
|
+
path: string,
|
|
26
|
+
) => FirebaseDatabaseTypes.Reference;
|
|
27
|
+
|
|
28
|
+
let DatabaseReferenceClass: DatabaseReferenceConstructor | null = null;
|
|
29
|
+
|
|
30
|
+
export function provideReferenceClass(databaseReference: DatabaseReferenceConstructor): void {
|
|
31
|
+
DatabaseReferenceClass = databaseReference;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default class DatabaseThenableReference implements Pick<
|
|
35
|
+
Promise<FirebaseDatabaseTypes.Reference>,
|
|
36
|
+
'then' | 'catch'
|
|
37
|
+
> {
|
|
38
|
+
_ref: FirebaseDatabaseTypes.Reference;
|
|
39
|
+
_promise: Promise<FirebaseDatabaseTypes.Reference>;
|
|
40
|
+
|
|
41
|
+
constructor(
|
|
42
|
+
database: DatabaseInternal,
|
|
43
|
+
path: string,
|
|
44
|
+
promise: Promise<FirebaseDatabaseTypes.Reference>,
|
|
45
|
+
) {
|
|
46
|
+
if (!DatabaseReferenceClass) {
|
|
47
|
+
throw new Error('DatabaseReference class has not been provided.');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this._ref = createDeprecationProxy(
|
|
51
|
+
new DatabaseReferenceClass(database, path),
|
|
52
|
+
) as FirebaseDatabaseTypes.Reference;
|
|
53
|
+
this._promise = promise;
|
|
54
|
+
|
|
55
|
+
return new Proxy(this, {
|
|
56
|
+
get(target, prop) {
|
|
57
|
+
if (prop === 'then' || prop === 'catch') {
|
|
58
|
+
return Reflect.get(target, prop, target);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return Reflect.get(target._ref as object, prop);
|
|
62
|
+
},
|
|
63
|
+
}) as unknown as DatabaseThenableReference;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get then(): Promise<FirebaseDatabaseTypes.Reference>['then'] {
|
|
67
|
+
return this._promise.then.bind(this._promise);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get catch(): Promise<FirebaseDatabaseTypes.Reference>['catch'] {
|
|
71
|
+
return this._promise.catch.bind(this._promise);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { isOther } from '@react-native-firebase/app/dist/module/common';
|
|
19
|
+
import NativeError from '@react-native-firebase/app/dist/module/internal/NativeFirebaseError';
|
|
20
|
+
import type {
|
|
21
|
+
DatabaseModuleInternal,
|
|
22
|
+
DatabaseReferenceInternal,
|
|
23
|
+
DatabaseSnapshotInternal,
|
|
24
|
+
DatabaseTransactionEventInternal,
|
|
25
|
+
DatabaseTransactionUpdatesInternal,
|
|
26
|
+
} from './types/internal';
|
|
27
|
+
|
|
28
|
+
let transactionId = 0;
|
|
29
|
+
|
|
30
|
+
type DatabaseTransactionRecordInternal = {
|
|
31
|
+
id: number;
|
|
32
|
+
reference: DatabaseReferenceInternal;
|
|
33
|
+
transactionUpdater: (currentData: unknown) => unknown;
|
|
34
|
+
onComplete: (error: Error | null, committed: boolean, snapshot: unknown | null) => void;
|
|
35
|
+
applyLocally: boolean;
|
|
36
|
+
completed: boolean;
|
|
37
|
+
started: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const generateTransactionId = (): number => transactionId++;
|
|
41
|
+
|
|
42
|
+
export default class DatabaseTransaction {
|
|
43
|
+
private readonly _database: DatabaseModuleInternal;
|
|
44
|
+
private readonly _emitter: DatabaseModuleInternal['emitter'];
|
|
45
|
+
private readonly _transactions: Record<number, DatabaseTransactionRecordInternal>;
|
|
46
|
+
|
|
47
|
+
constructor(database: DatabaseModuleInternal) {
|
|
48
|
+
this._database = database;
|
|
49
|
+
this._emitter = database.emitter;
|
|
50
|
+
this._transactions = {};
|
|
51
|
+
|
|
52
|
+
this._emitter.addListener(
|
|
53
|
+
this._database.eventNameForApp('database_transaction_event'),
|
|
54
|
+
this._onTransactionEvent.bind(this),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
add(
|
|
59
|
+
reference: DatabaseReferenceInternal,
|
|
60
|
+
transactionUpdater: (currentData: unknown) => unknown,
|
|
61
|
+
onComplete: (error: Error | null, committed: boolean, snapshot: unknown | null) => void,
|
|
62
|
+
applyLocally = false,
|
|
63
|
+
): void {
|
|
64
|
+
const id = generateTransactionId();
|
|
65
|
+
|
|
66
|
+
this._transactions[id] = {
|
|
67
|
+
id,
|
|
68
|
+
reference,
|
|
69
|
+
transactionUpdater,
|
|
70
|
+
onComplete,
|
|
71
|
+
applyLocally,
|
|
72
|
+
completed: false,
|
|
73
|
+
started: true,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
if (isOther) {
|
|
77
|
+
this._database.native.transactionStart(reference.path, id, applyLocally, transactionUpdater);
|
|
78
|
+
} else {
|
|
79
|
+
this._database.native.transactionStart(reference.path, id, applyLocally);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private _getTransaction(id: number): DatabaseTransactionRecordInternal | undefined {
|
|
84
|
+
return this._transactions[id];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private _removeTransaction(id: number): void {
|
|
88
|
+
setImmediate(() => {
|
|
89
|
+
delete this._transactions[id];
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private _onTransactionEvent(event: DatabaseTransactionEventInternal): void {
|
|
94
|
+
switch (event.body.type) {
|
|
95
|
+
case 'update':
|
|
96
|
+
this._handleUpdate(event);
|
|
97
|
+
return;
|
|
98
|
+
case 'error':
|
|
99
|
+
this._handleError(event);
|
|
100
|
+
return;
|
|
101
|
+
case 'complete':
|
|
102
|
+
this._handleComplete(event);
|
|
103
|
+
return;
|
|
104
|
+
default:
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private _handleUpdate(event: DatabaseTransactionEventInternal): void {
|
|
110
|
+
let newValue: unknown;
|
|
111
|
+
|
|
112
|
+
const { id, body } = event;
|
|
113
|
+
const { value } = body as { value?: unknown };
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const transaction = this._getTransaction(id);
|
|
117
|
+
if (!transaction) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
newValue = transaction.transactionUpdater(value);
|
|
122
|
+
} finally {
|
|
123
|
+
const abort = newValue === undefined;
|
|
124
|
+
|
|
125
|
+
const updates: DatabaseTransactionUpdatesInternal = {
|
|
126
|
+
value: newValue,
|
|
127
|
+
abort,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
this._database.native.transactionTryCommit(id, updates);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private _handleError(event: DatabaseTransactionEventInternal): void {
|
|
135
|
+
const transaction = this._getTransaction(event.id);
|
|
136
|
+
|
|
137
|
+
if (transaction && !transaction.completed) {
|
|
138
|
+
transaction.completed = true;
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const error = NativeError.fromEvent(
|
|
142
|
+
(event.body as { error?: unknown }).error as Parameters<typeof NativeError.fromEvent>[0],
|
|
143
|
+
'database',
|
|
144
|
+
) as Error;
|
|
145
|
+
transaction.onComplete(error, false, null);
|
|
146
|
+
} finally {
|
|
147
|
+
this._removeTransaction(event.id);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private _handleComplete(event: DatabaseTransactionEventInternal): void {
|
|
153
|
+
const transaction = this._getTransaction(event.id);
|
|
154
|
+
|
|
155
|
+
if (transaction && !transaction.completed) {
|
|
156
|
+
transaction.completed = true;
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
transaction.onComplete(
|
|
160
|
+
null,
|
|
161
|
+
!!(event.body as { committed?: boolean }).committed,
|
|
162
|
+
Object.assign(
|
|
163
|
+
{},
|
|
164
|
+
(event.body as { snapshot?: DatabaseSnapshotInternal }).snapshot,
|
|
165
|
+
) as DatabaseSnapshotInternal,
|
|
166
|
+
);
|
|
167
|
+
} finally {
|
|
168
|
+
this._removeTransaction(event.id);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
package/lib/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Export modular API
|
|
19
|
+
export * from './modular';
|
|
20
|
+
export type * from './types/database';
|
|
21
|
+
|
|
22
|
+
// Export namespaced API
|
|
23
|
+
export type { FirebaseDatabaseTypes } from './types/namespaced';
|
|
24
|
+
export * from './namespaced';
|
|
25
|
+
export { default } from './namespaced';
|