@massalabs/gossip-sdk 0.0.2-dev.20260128094509 → 0.0.2-dev.20260128111120

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/dist/api/messageProtocol/index.d.ts +19 -0
  2. package/dist/api/messageProtocol/index.js +26 -0
  3. package/dist/api/messageProtocol/mock.d.ts +12 -0
  4. package/{src/api/messageProtocol/mock.ts → dist/api/messageProtocol/mock.js} +2 -3
  5. package/dist/api/messageProtocol/rest.d.ts +22 -0
  6. package/dist/api/messageProtocol/rest.js +161 -0
  7. package/dist/api/messageProtocol/types.d.ts +61 -0
  8. package/dist/api/messageProtocol/types.js +6 -0
  9. package/dist/assets/generated/wasm/README.md +281 -0
  10. package/dist/assets/generated/wasm/gossip_wasm.d.ts +498 -0
  11. package/dist/assets/generated/wasm/gossip_wasm.js +1399 -0
  12. package/dist/assets/generated/wasm/gossip_wasm_bg.wasm +0 -0
  13. package/dist/assets/generated/wasm/gossip_wasm_bg.wasm.d.ts +68 -0
  14. package/dist/assets/generated/wasm/package.json +15 -0
  15. package/dist/config/protocol.d.ts +36 -0
  16. package/dist/config/protocol.js +77 -0
  17. package/dist/config/sdk.d.ts +82 -0
  18. package/dist/config/sdk.js +55 -0
  19. package/{src/contacts.ts → dist/contacts.d.ts} +10 -94
  20. package/dist/contacts.js +166 -0
  21. package/dist/core/SdkEventEmitter.d.ts +36 -0
  22. package/dist/core/SdkEventEmitter.js +59 -0
  23. package/dist/core/SdkPolling.d.ts +35 -0
  24. package/dist/core/SdkPolling.js +100 -0
  25. package/{src/core/index.ts → dist/core/index.d.ts} +0 -2
  26. package/dist/core/index.js +5 -0
  27. package/dist/crypto/bip39.d.ts +34 -0
  28. package/dist/crypto/bip39.js +62 -0
  29. package/dist/crypto/encryption.d.ts +37 -0
  30. package/dist/crypto/encryption.js +46 -0
  31. package/dist/db.d.ts +190 -0
  32. package/dist/db.js +311 -0
  33. package/dist/gossipSdk.d.ts +274 -0
  34. package/dist/gossipSdk.js +690 -0
  35. package/dist/index.d.ts +73 -0
  36. package/dist/index.js +77 -0
  37. package/dist/services/announcement.d.ts +43 -0
  38. package/dist/services/announcement.js +491 -0
  39. package/dist/services/auth.d.ts +37 -0
  40. package/dist/services/auth.js +76 -0
  41. package/dist/services/discussion.d.ts +63 -0
  42. package/dist/services/discussion.js +297 -0
  43. package/dist/services/message.d.ts +74 -0
  44. package/dist/services/message.js +826 -0
  45. package/dist/services/refresh.d.ts +41 -0
  46. package/dist/services/refresh.js +205 -0
  47. package/{src/sw.ts → dist/sw.d.ts} +1 -8
  48. package/dist/sw.js +10 -0
  49. package/dist/types/events.d.ts +80 -0
  50. package/dist/types/events.js +7 -0
  51. package/dist/types.d.ts +32 -0
  52. package/dist/types.js +7 -0
  53. package/dist/utils/base64.d.ts +10 -0
  54. package/dist/utils/base64.js +30 -0
  55. package/dist/utils/contacts.d.ts +42 -0
  56. package/dist/utils/contacts.js +113 -0
  57. package/dist/utils/discussions.d.ts +24 -0
  58. package/dist/utils/discussions.js +38 -0
  59. package/dist/utils/logs.d.ts +19 -0
  60. package/dist/utils/logs.js +89 -0
  61. package/dist/utils/messageSerialization.d.ts +64 -0
  62. package/dist/utils/messageSerialization.js +184 -0
  63. package/dist/utils/queue.d.ts +50 -0
  64. package/dist/utils/queue.js +110 -0
  65. package/dist/utils/type.d.ts +10 -0
  66. package/dist/utils/type.js +4 -0
  67. package/dist/utils/userId.d.ts +40 -0
  68. package/dist/utils/userId.js +90 -0
  69. package/dist/utils/validation.d.ts +50 -0
  70. package/dist/utils/validation.js +112 -0
  71. package/dist/utils.d.ts +30 -0
  72. package/{src/utils.ts → dist/utils.js} +9 -19
  73. package/dist/wasm/encryption.d.ts +56 -0
  74. package/{src/wasm/encryption.ts → dist/wasm/encryption.js} +22 -51
  75. package/dist/wasm/index.d.ts +10 -0
  76. package/{src/wasm/index.ts → dist/wasm/index.js} +1 -8
  77. package/dist/wasm/loader.d.ts +21 -0
  78. package/dist/wasm/loader.js +103 -0
  79. package/dist/wasm/session.d.ts +85 -0
  80. package/dist/wasm/session.js +226 -0
  81. package/dist/wasm/userKeys.d.ts +17 -0
  82. package/{src/wasm/userKeys.ts → dist/wasm/userKeys.js} +6 -13
  83. package/package.json +5 -1
  84. package/src/api/messageProtocol/index.ts +0 -53
  85. package/src/api/messageProtocol/rest.ts +0 -209
  86. package/src/api/messageProtocol/types.ts +0 -70
  87. package/src/config/protocol.ts +0 -97
  88. package/src/config/sdk.ts +0 -131
  89. package/src/core/SdkEventEmitter.ts +0 -91
  90. package/src/core/SdkPolling.ts +0 -134
  91. package/src/crypto/bip39.ts +0 -84
  92. package/src/crypto/encryption.ts +0 -77
  93. package/src/db.ts +0 -465
  94. package/src/gossipSdk.ts +0 -994
  95. package/src/index.ts +0 -211
  96. package/src/services/announcement.ts +0 -653
  97. package/src/services/auth.ts +0 -95
  98. package/src/services/discussion.ts +0 -380
  99. package/src/services/message.ts +0 -1055
  100. package/src/services/refresh.ts +0 -234
  101. package/src/types/events.ts +0 -108
  102. package/src/types.ts +0 -70
  103. package/src/utils/base64.ts +0 -39
  104. package/src/utils/contacts.ts +0 -161
  105. package/src/utils/discussions.ts +0 -55
  106. package/src/utils/logs.ts +0 -86
  107. package/src/utils/messageSerialization.ts +0 -257
  108. package/src/utils/queue.ts +0 -106
  109. package/src/utils/type.ts +0 -7
  110. package/src/utils/userId.ts +0 -114
  111. package/src/utils/validation.ts +0 -144
  112. package/src/wasm/loader.ts +0 -123
  113. package/src/wasm/session.ts +0 -276
  114. package/test/config/protocol.spec.ts +0 -31
  115. package/test/config/sdk.spec.ts +0 -163
  116. package/test/db/helpers.spec.ts +0 -142
  117. package/test/db/operations.spec.ts +0 -128
  118. package/test/db/states.spec.ts +0 -535
  119. package/test/integration/discussion-flow.spec.ts +0 -422
  120. package/test/integration/messaging-flow.spec.ts +0 -708
  121. package/test/integration/sdk-lifecycle.spec.ts +0 -325
  122. package/test/mocks/index.ts +0 -9
  123. package/test/mocks/mockMessageProtocol.ts +0 -100
  124. package/test/services/auth.spec.ts +0 -311
  125. package/test/services/discussion.spec.ts +0 -279
  126. package/test/services/message-deduplication.spec.ts +0 -299
  127. package/test/services/message-startup.spec.ts +0 -331
  128. package/test/services/message.spec.ts +0 -817
  129. package/test/services/refresh.spec.ts +0 -199
  130. package/test/services/session-status.spec.ts +0 -349
  131. package/test/session/wasm.spec.ts +0 -227
  132. package/test/setup.ts +0 -52
  133. package/test/utils/contacts.spec.ts +0 -156
  134. package/test/utils/discussions.spec.ts +0 -66
  135. package/test/utils/queue.spec.ts +0 -52
  136. package/test/utils/serialization.spec.ts +0 -120
  137. package/test/utils/userId.spec.ts +0 -120
  138. package/test/utils/validation.spec.ts +0 -223
  139. package/test/utils.ts +0 -212
  140. package/tsconfig.json +0 -26
  141. package/tsconfig.tsbuildinfo +0 -1
  142. package/vitest.config.ts +0 -28
package/src/db.ts DELETED
@@ -1,465 +0,0 @@
1
- /**
2
- * Gossip Database
3
- *
4
- * IndexedDB database implementation using Dexie for the Gossip messenger.
5
- * Provides tables for contacts, messages, discussions, user profiles, and more.
6
- */
7
-
8
- import Dexie, { Table } from 'dexie';
9
-
10
- // Define authentication method type
11
- export type AuthMethod = 'capacitor' | 'webauthn' | 'password';
12
-
13
- // Define interfaces for data models
14
- export interface Contact {
15
- id?: number;
16
- ownerUserId: string; // The current user's userId owning this contact
17
- userId: string; // 32-byte user ID (gossip Bech32 encoded) - primary key
18
- name: string;
19
- avatar?: string;
20
- publicKeys: Uint8Array; // Serialized UserPublicKeys bytes (from UserPublicKeys.to_bytes())
21
- isOnline: boolean;
22
- lastSeen: Date;
23
- createdAt: Date;
24
- }
25
-
26
- export interface Message {
27
- id?: number;
28
- ownerUserId: string; // The current user's userId owning this message
29
- contactUserId: string; // Reference to Contact.userId
30
- content: string;
31
- serializedContent?: Uint8Array; // Serialized message content
32
- type: MessageType;
33
- direction: MessageDirection;
34
- status: MessageStatus;
35
- timestamp: Date;
36
- metadata?: Record<string, unknown>;
37
- seeker?: Uint8Array; // Seeker for this message (stored when sending or receiving)
38
- replyTo?: {
39
- originalContent?: string;
40
- originalSeeker: Uint8Array; // Seeker of the original message (required for replies)
41
- };
42
- forwardOf?: {
43
- originalContent?: string;
44
- originalSeeker: Uint8Array;
45
- };
46
- encryptedMessage?: Uint8Array; // Ciphertext of the message
47
- }
48
-
49
- export interface UserProfile {
50
- userId: string; // 32-byte user ID (gossip Bech32 encoded) - primary key
51
- username: string;
52
- avatar?: string;
53
- security: {
54
- encKeySalt: Uint8Array;
55
-
56
- // Authentication method used to create the account
57
- authMethod: AuthMethod;
58
-
59
- // WebAuthn/FIDO2 (biometric) details when used
60
- webauthn?: {
61
- credentialId?: string;
62
- };
63
-
64
- // iCloud Keychain sync preference (iOS only)
65
- iCloudSync?: boolean;
66
-
67
- // Mnemonic backup details
68
- mnemonicBackup: {
69
- encryptedMnemonic: Uint8Array;
70
- createdAt: Date;
71
- backedUp: boolean;
72
- };
73
- };
74
- session: Uint8Array;
75
- bio?: string;
76
- status: 'online' | 'away' | 'busy' | 'offline';
77
- lastSeen: Date;
78
- createdAt: Date;
79
- updatedAt: Date;
80
- lastPublicKeyPush?: Date;
81
- lastBulletinCounter?: string;
82
- }
83
-
84
- // Unified discussion interface combining protocol state and UI metadata
85
-
86
- export enum DiscussionStatus {
87
- PENDING = 'pending',
88
- ACTIVE = 'active',
89
- CLOSED = 'closed', // closed by the user
90
- BROKEN = 'broken', // The session is killed. Need to be reinitiated
91
- SEND_FAILED = 'sendFailed', // The discussion was initiated by the session manager but could not be broadcasted on network
92
- RECONNECTING = 'reconnecting', // Session recovery in progress, waiting for peer's response
93
- }
94
-
95
- export enum MessageDirection {
96
- INCOMING = 'incoming',
97
- OUTGOING = 'outgoing',
98
- }
99
-
100
- export enum MessageStatus {
101
- WAITING_SESSION = 'waiting_session', // Waiting for active session with peer
102
- SENDING = 'sending',
103
- SENT = 'sent',
104
- DELIVERED = 'delivered',
105
- READ = 'read',
106
- FAILED = 'failed', // Only for unrecoverable errors (network down, blocked, etc.)
107
- }
108
-
109
- export enum DiscussionDirection {
110
- INITIATED = 'initiated',
111
- RECEIVED = 'received',
112
- }
113
-
114
- export enum MessageType {
115
- TEXT = 'text',
116
- KEEP_ALIVE = 'keep_alive',
117
- IMAGE = 'image',
118
- FILE = 'file',
119
- AUDIO = 'audio',
120
- VIDEO = 'video',
121
- }
122
-
123
- export interface Discussion {
124
- id?: number;
125
- ownerUserId: string; // The current user's userId owning this discussion
126
- contactUserId: string; // Reference to Contact.userId - unique per contact
127
-
128
- // Protocol/Encryption fields
129
- direction: DiscussionDirection; // Whether this user initiated or received the discussion
130
- status: DiscussionStatus;
131
- nextSeeker?: Uint8Array; // The next seeker for sending messages (from SendMessageOutput)
132
- initiationAnnouncement?: Uint8Array; // Outgoing announcement bytes when we initiate
133
- announcementMessage?: string; // Optional message from incoming announcement (user_data)
134
- lastSyncTimestamp?: Date; // Last time messages were synced from protocol
135
-
136
- // UI/Display fields
137
- customName?: string; // Optional custom name for the discussion (overrides contact name)
138
- lastMessageId?: number;
139
- lastMessageContent?: string;
140
- lastMessageTimestamp?: Date;
141
- unreadCount: number;
142
-
143
- // Timestamps
144
- createdAt: Date;
145
- updatedAt: Date;
146
- }
147
-
148
- export interface PendingEncryptedMessage {
149
- id?: number;
150
- seeker: Uint8Array;
151
- ciphertext: Uint8Array;
152
- fetchedAt: Date;
153
- }
154
-
155
- export interface PendingAnnouncement {
156
- id?: number;
157
- announcement: Uint8Array;
158
- fetchedAt: Date;
159
- counter?: string;
160
- }
161
-
162
- export interface ActiveSeeker {
163
- id?: number;
164
- seeker: Uint8Array;
165
- }
166
-
167
- // Define the database class
168
- export class GossipDatabase extends Dexie {
169
- // Define tables
170
- contacts!: Table<Contact>;
171
- messages!: Table<Message>;
172
- userProfile!: Table<UserProfile>;
173
- discussions!: Table<Discussion>;
174
- pendingEncryptedMessages!: Table<PendingEncryptedMessage>;
175
- pendingAnnouncements!: Table<PendingAnnouncement>;
176
- activeSeekers!: Table<ActiveSeeker>;
177
-
178
- constructor() {
179
- super('GossipDatabase');
180
-
181
- this.version(13).stores({
182
- contacts:
183
- '++id, ownerUserId, userId, name, isOnline, lastSeen, createdAt, [ownerUserId+userId] , [ownerUserId+name]',
184
- messages:
185
- '++id, ownerUserId, contactUserId, type, direction, status, timestamp, seeker, [ownerUserId+contactUserId], [ownerUserId+status], [ownerUserId+contactUserId+status], [ownerUserId+seeker], [ownerUserId+contactUserId+direction], [ownerUserId+direction+status]',
186
- userProfile: 'userId, username, status, lastSeen',
187
- discussions:
188
- '++id, ownerUserId, &[ownerUserId+contactUserId], status, [ownerUserId+status], lastSyncTimestamp, unreadCount, lastMessageTimestamp, createdAt, updatedAt',
189
- pendingEncryptedMessages: '++id, fetchedAt, seeker',
190
- pendingAnnouncements: '++id, fetchedAt, &announcement',
191
- activeSeekers: '++id, seeker',
192
- });
193
-
194
- // Add hooks for automatic timestamps
195
- this.contacts.hook('creating', function (_primKey, obj, _trans) {
196
- obj.createdAt = new Date();
197
- });
198
-
199
- this.userProfile.hook('creating', function (_primKey, obj, _trans) {
200
- obj.createdAt = new Date();
201
- obj.updatedAt = new Date();
202
- });
203
-
204
- this.userProfile.hook(
205
- 'updating',
206
- function (modifications, _primKey, _obj, _trans) {
207
- (modifications as Record<string, unknown>).updatedAt = new Date();
208
- }
209
- );
210
-
211
- this.discussions.hook('creating', function (_primKey, obj, _trans) {
212
- obj.createdAt = new Date();
213
- obj.updatedAt = new Date();
214
- });
215
-
216
- this.discussions.hook(
217
- 'updating',
218
- function (modifications, _primKey, _obj, _trans) {
219
- (modifications as Record<string, unknown>).updatedAt = new Date();
220
- }
221
- );
222
- }
223
-
224
- // Helper methods for common operations
225
-
226
- /** CONTACTS */
227
- async getContactsByOwner(ownerUserId: string): Promise<Contact[]> {
228
- return await this.contacts
229
- .where('ownerUserId')
230
- .equals(ownerUserId)
231
- .toArray();
232
- }
233
-
234
- async getContactByOwnerAndUserId(
235
- ownerUserId: string,
236
- userId: string
237
- ): Promise<Contact | undefined> {
238
- return await this.contacts
239
- .where('[ownerUserId+userId]')
240
- .equals([ownerUserId, userId])
241
- .first();
242
- }
243
-
244
- /** DISCUSSIONS */
245
- async getDiscussionsByOwner(ownerUserId: string): Promise<Discussion[]> {
246
- const all = await this.discussions
247
- .where('ownerUserId')
248
- .equals(ownerUserId)
249
- .toArray();
250
- return all.sort((a, b) => {
251
- if (a.lastMessageTimestamp && b.lastMessageTimestamp) {
252
- return (
253
- b.lastMessageTimestamp.getTime() - a.lastMessageTimestamp.getTime()
254
- );
255
- }
256
- if (a.lastMessageTimestamp) return -1;
257
- if (b.lastMessageTimestamp) return 1;
258
- return b.createdAt.getTime() - a.createdAt.getTime();
259
- });
260
- }
261
-
262
- async getUnreadCountByOwner(ownerUserId: string): Promise<number> {
263
- const discussions = await this.discussions
264
- .where('ownerUserId')
265
- .equals(ownerUserId)
266
- .toArray();
267
- return discussions.reduce((total, d) => total + d.unreadCount, 0);
268
- }
269
-
270
- async getDiscussionByOwnerAndContact(
271
- ownerUserId: string,
272
- contactUserId: string
273
- ): Promise<Discussion | undefined> {
274
- if (!ownerUserId || !contactUserId) {
275
- return undefined;
276
- }
277
- return await this.discussions
278
- .where('[ownerUserId+contactUserId]')
279
- .equals([ownerUserId, contactUserId])
280
- .first();
281
- }
282
-
283
- /**
284
- * Get all active discussions with their sync status
285
- * @returns Array of active discussions
286
- */
287
- async getActiveDiscussionsByOwner(
288
- ownerUserId: string
289
- ): Promise<Discussion[]> {
290
- return await this.discussions
291
- .where('[ownerUserId+status]')
292
- .equals([ownerUserId, DiscussionStatus.ACTIVE])
293
- .toArray();
294
- }
295
-
296
- async markMessagesAsRead(
297
- ownerUserId: string,
298
- contactUserId: string
299
- ): Promise<void> {
300
- await this.messages
301
- .where('[ownerUserId+contactUserId+status]')
302
- .equals([ownerUserId, contactUserId, MessageStatus.DELIVERED])
303
- .and(msg => msg.direction === MessageDirection.INCOMING)
304
- .modify({ status: MessageStatus.READ });
305
-
306
- await this.discussions
307
- .where('[ownerUserId+contactUserId]')
308
- .equals([ownerUserId, contactUserId])
309
- .modify({ unreadCount: 0 });
310
- }
311
-
312
- async getMessagesForContactByOwner(
313
- ownerUserId: string,
314
- contactUserId: string,
315
- limit = 50
316
- ): Promise<Message[]> {
317
- return await this.messages
318
- .where('[ownerUserId+contactUserId]')
319
- .equals([ownerUserId, contactUserId])
320
- .reverse()
321
- .limit(limit)
322
- .toArray();
323
- }
324
-
325
- async addMessage(message: Omit<Message, 'id'>): Promise<number> {
326
- const messageId = await this.messages.add(message);
327
-
328
- // Get existing discussion
329
- const discussion = await this.getDiscussionByOwnerAndContact(
330
- message.ownerUserId,
331
- message.contactUserId
332
- );
333
-
334
- if (discussion) {
335
- await this.discussions.update(discussion.id!, {
336
- lastMessageId: messageId,
337
- lastMessageContent: message.content,
338
- lastMessageTimestamp: message.timestamp,
339
- unreadCount:
340
- message.direction === MessageDirection.INCOMING
341
- ? discussion.unreadCount + 1
342
- : discussion.unreadCount,
343
- updatedAt: new Date(),
344
- });
345
- } else {
346
- // Note: For new messages, a discussion should already exist from the protocol
347
- // If not, we'll create a minimal one (this shouldn't normally happen)
348
- console.log(
349
- 'Warning: Creating discussion for contact without protocol setup:',
350
- message.contactUserId
351
- );
352
- await this.discussions.put({
353
- ownerUserId: message.ownerUserId,
354
- contactUserId: message.contactUserId,
355
- direction:
356
- message.direction === MessageDirection.INCOMING
357
- ? DiscussionDirection.RECEIVED
358
- : DiscussionDirection.INITIATED,
359
- status: DiscussionStatus.PENDING,
360
- nextSeeker: undefined,
361
- lastMessageId: messageId,
362
- lastMessageContent: message.content,
363
- lastMessageTimestamp: message.timestamp,
364
- unreadCount: message.direction === MessageDirection.INCOMING ? 1 : 0,
365
- createdAt: new Date(),
366
- updatedAt: new Date(),
367
- });
368
- }
369
-
370
- return messageId;
371
- }
372
-
373
- /**
374
- * Update the last sync timestamp for a discussion
375
- * @param discussionId - The discussion ID
376
- * @param timestamp - The sync timestamp
377
- */
378
- async updateLastSyncTimestamp(
379
- discussionId: number,
380
- timestamp: Date
381
- ): Promise<void> {
382
- await this.discussions.update(discussionId, {
383
- lastSyncTimestamp: timestamp,
384
- updatedAt: new Date(),
385
- });
386
- }
387
-
388
- async deleteDb(): Promise<void> {
389
- await this.close();
390
- await this.delete();
391
- }
392
-
393
- /**
394
- * Set all active seekers, replacing any existing ones
395
- * @param seekers - Array of seeker Uint8Arrays to store
396
- */
397
- async setActiveSeekers(seekers: Uint8Array[]): Promise<void> {
398
- await this.transaction('rw', this.activeSeekers, async () => {
399
- // Clear all existing seekers
400
- await this.activeSeekers.clear();
401
-
402
- // Bulk add all new seekers
403
- if (seekers.length > 0) {
404
- await this.activeSeekers.bulkAdd(
405
- seekers.map(seeker => ({
406
- seeker,
407
- }))
408
- );
409
- }
410
- });
411
- }
412
-
413
- /**
414
- * Get all active seekers from the database
415
- * @returns Array of seeker Uint8Arrays
416
- */
417
- async getActiveSeekers(): Promise<Uint8Array[]> {
418
- const activeSeekers = await this.activeSeekers.toArray();
419
- return activeSeekers.map(item => item.seeker);
420
- }
421
- }
422
-
423
- // Database instance - initialized lazily or via setDb()
424
- let _db: GossipDatabase | null = null;
425
- let _warnedGlobalDbAccess = false;
426
-
427
- /**
428
- * Get the database instance.
429
- * Creates a default instance if none was set via setDb().
430
- */
431
- export function getDb(): GossipDatabase {
432
- if (!_db) {
433
- _db = new GossipDatabase();
434
- }
435
- return _db;
436
- }
437
-
438
- /**
439
- * Set the database instance.
440
- * Call this before using any SDK functions if you need a custom db instance.
441
- */
442
- export function setDb(database: GossipDatabase): void {
443
- _db = database;
444
- }
445
-
446
- /**
447
- * Get the database instance.
448
- * Creates a default instance if none was set via setDb().
449
- */
450
- export const db: GossipDatabase = new Proxy({} as GossipDatabase, {
451
- get(_target, prop) {
452
- if (!_warnedGlobalDbAccess) {
453
- _warnedGlobalDbAccess = true;
454
- console.warn(
455
- '[GossipSdk] Global db access is deprecated. Use createGossipSdk() or setDb().'
456
- );
457
- }
458
- const target = getDb();
459
- const value = Reflect.get(target, prop);
460
- if (typeof value === 'function') {
461
- return value.bind(target);
462
- }
463
- return value;
464
- },
465
- });